/* Includes ------------------------------------------------------------------*/
#include "ethernetif_dm9k.h"
#include "dm9k.h"
#include "lwip/tcpip.h"
#include "lwip/timeouts.h"
#include "lwip_dm9k.h"
#include "lwip_init.h"
#include "netif/etharp.h"
#include "netif/ethernet.h"
#include "string.h"
#include <stdio.h>

#define DM9K_RX_TASK_PRIO     4
#define DM9K_RX_TASK_STK_SIZE 2048
CPU_STK dm9k_rx_task_stk[DM9K_RX_TASK_STK_SIZE];

/* Define those to better describe your network interface. */
#define IFNAME0 'D'
#define IFNAME1 '2'

#define DM9K_RX_DESC_CNT 4
#define DM9K_RX_Lenth    1536UL

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static uint8_t            *Rx_Buff[DM9K_RX_DESC_CNT]     = {0}; /* Ethernet Rx Buffer */
uint8_t                    current_pbuf_idx              = 0;   /* Zero-copy RX PBUF pool */
static struct pbuf_custom *custom_pbuf[DM9K_RX_DESC_CNT] = {0};
/* Private function prototypes -----------------------------------------------*/
static void pbuf_free_custom(struct pbuf *p);
static void ethernetif_input(void *pvParameters);
// static void ethernet_link_check_state(struct netif *netif);

static OS_EVENT *g_dm9k_rx_sem = NULL;
static OS_EVENT *g_dm9k_tx_sem = NULL;
// static OS_EVENT     *dm9k_mutex    = NULL;
static struct netif *low_netif = NULL;

static void low_level_init(struct netif *netif)
{
    /* set MAC hardware address length */
    netif->hwaddr_len = ETHARP_HWADDR_LEN;

    /* 网卡的 MAC 修改2 */
    netif->hwaddr[0] = dm9kdev.mac[0];
    netif->hwaddr[1] = dm9kdev.mac[1];
    netif->hwaddr[2] = dm9kdev.mac[2];
    netif->hwaddr[3] = dm9kdev.mac[3];
    netif->hwaddr[4] = dm9kdev.mac[4];
    netif->hwaddr[5] = dm9kdev.mac[5];

    /* maximum transfer unit */
    netif->mtu = 1500;

    /* device capabilities */
    /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
    netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;

    low_netif = netif;

    /* create binary semaphore used for informing ethernetif of frame reception */
    if (NULL == g_dm9k_rx_sem)
    {
        g_dm9k_rx_sem = OSSemCreate(0);
    }

    OSTaskCreateExt((void (*)(void *))ethernetif_input,
                    (void *)0,
                    (OS_STK *)&dm9k_rx_task_stk[DM9K_RX_TASK_STK_SIZE - 1],
                    (INT8U)DM9K_RX_TASK_PRIO,
                    (INT16U)DM9K_RX_TASK_PRIO,
                    (OS_STK *)&dm9k_rx_task_stk[0],
                    (INT32U)DM9K_RX_TASK_STK_SIZE,
                    (void *)0,
                    (INT16U)OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR | OS_TASK_OPT_SAVE_FP);
}

static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
    INT8U        err;
    err_t        errval = ERR_OK;
    uint32_t     i = 0, framelen = 0, calc_len = 0;
    struct pbuf *q;

    SYS_ARCH_DECL_PROTECT(sr);

    if (NULL == g_dm9k_tx_sem)
    {
        g_dm9k_tx_sem = OSSemCreate(1);
    }

    OSSemPend(g_dm9k_tx_sem, 0, &err);

    SYS_ARCH_PROTECT(sr);

    // dm9k_WriteReg(DM9K_REG_IMR, DM9K_IMR_OFF); /* 关闭 dm9kA 中断 */

    NET_REG_ADDR = DM9K_REG_MWCMD;

    for (q = p; q != NULL; q = q->next)
    {
        framelen = framelen + q->len;
        calc_len = (q->len + 1) >> 1;

        for (i = 0; i < calc_len; i++)
        {
            NET_REG_DATA = ((uint16_t *)q->payload)[i];
        }
    }

    dm9k_WriteReg(DM9K_REG_TXPLH, (framelen >> 8) & 0xff); /* 设置传送封包的长度 */
    dm9k_WriteReg(DM9K_REG_TXPLL, framelen & 0xff);

    dm9k_WriteReg(DM9K_REG_TCR, DM9K_TCR_SET); /* 进行传送 */

    while ((dm9k_ReadReg(DM9K_REG_ISR) & 0x02) == 0)
        ; /* 等待发送完成	 */
    // dm9k_WriteReg(DM9K_REG_IMR, DM9K_IMR_SET); /* 开启 dm9kA 中断 */

    SYS_ARCH_UNPROTECT(sr);

    // OSSemPost(g_dm9k_tx_sem);
    return errval;
}

static struct pbuf *low_level_input(struct netif *netif)
{
    struct pbuf *p        = NULL;
    uint16_t     framelen = 0;

    // dm9k_WriteReg(DM9K_REG_IMR, DM9K_IMR_OFF); /* 关闭 DM9000A 中断 */
    framelen = etherdev_read(Rx_Buff[current_pbuf_idx]);
    // dm9k_WriteReg(DM9K_REG_IMR, DM9K_IMR_SET); /* 开启 DM9000A 中断 */
    if (framelen)
    {
        printf("REC LEM: %d\r\n", framelen);
        p = pbuf_alloc(PBUF_RAW, framelen, PBUF_POOL);
        // p = pbuf_alloced_custom(PBUF_RAW, framelen, PBUF_REF, custom_pbuf[current_pbuf_idx], Rx_Buff[current_pbuf_idx], Max_Ethernet_Lenth);
        // if (current_pbuf_idx < (DM9K_RX_DESC_CNT - 1))
        // {
        //     current_pbuf_idx++;
        // }
        // else
        // {
        //     current_pbuf_idx = 0;
        // }
    }
    return p;
}

void ethernetif_input(void *pvParameters)
{
    struct pbuf *p;
    INT8U        err;

    SYS_ARCH_DECL_PROTECT(sr);

    for (;;)
    {

        OSSemPend(g_dm9k_rx_sem, 100, &err);

TRY_GET_NEXT_FRAME:
        SYS_ARCH_PROTECT(sr);
        p = low_level_input(low_netif);
        SYS_ARCH_UNPROTECT(sr);

        if (p != NULL)
        {
            if (ERR_OK != low_netif->input(p, low_netif))
            {
                pbuf_free(p);
            }
            else
            {
                goto TRY_GET_NEXT_FRAME;
            }
        }
    }
}

err_t ethernetif_dm9k_init(struct netif *netif)
{
    LWIP_ASSERT("netif != NULL", (netif != NULL));

#if LWIP_NETIF_HOSTNAME
    /* Initialize interface hostname */
    netif->hostname = "DM9000";
#endif /* LWIP_NETIF_HOSTNAME */

    /*
     * Initialize the snmp variables and counters inside the struct netif.
     * The last argument should be replaced with your link speed, in units
     * of bits per second.
     */
    // MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
    netif->name[0] = IFNAME0;
    netif->name[1] = IFNAME1;

    /* We directly use etharp_output() here to save a function call.
     * You can instead declare your own function an call etharp_output()
     * from it if you have to do some checks before sending (e.g. if link
     * is available...) */
    netif->output     = etharp_output;
    netif->linkoutput = low_level_output;

    /* initialize the hardware */
    low_level_init(netif);

    return ERR_OK;
}

/**
 * @brief  Custom Rx pbuf free callback
 * @param  pbuf: pbuf to be freed
 * @retval None
 */
// static void pbuf_free_custom(struct pbuf *p)
// {
//     if (p != NULL)
//     {
//         memset(p, 0, sizeof(struct pbuf));
//     }
// }
void dm9k_interrupt(void)
{
    uint8_t  save_reg;
    uint16_t isr_status;

    save_reg = NET_REG_ADDR; /* 暂存所使用的位置 */

    // dm9k_WriteReg(DM9K_REG_IMR, DM9K_IMR_OFF); /* 关闭 DM9KA 中断 */
    isr_status = dm9k_ReadReg(DM9K_REG_ISR); /* 取得中断产生值 */
    // isr_status = isr_status & DM9K_RX_INTR;
    if (isr_status & DM9K_RX_INTR)
    { /* 检查是否为接收中断 */
        OSSemPost(g_dm9k_rx_sem);
        // dm9k_receive_packet();							/* 执行接收处理程序 */
    }

    dm9k_WriteReg(DM9K_REG_IMR, DM9K_IMR_SET); /* 开启 DM9KA 中断 */
    NET_REG_ADDR = save_reg;                   /* 回复所使用的位置 */
}

/**
 * @brief  Ethernet Rx Transfer completed callback
 * @param  heth: ETH handle
 * @retval None
 */
void BSP_GPIO15_EXTI_Callback(void)
{
    uint8_t save_reg;
    uint8_t isr_status;

    save_reg = NET_REG_ADDR; /* 暂存所使用的位置 */

    isr_status = dm9k_ReadReg(DM9K_REG_ISR); /* 取得中断产生值 */
    dm9k_WriteReg(DM9K_REG_ISR, isr_status); /* 清除中断产生值 */
    // isr_status = isr_status & DM9K_RX_INTR;

    if (isr_status & DM9K_RX_INTR) /* 检查是否为接收中断 */
    {
        OSSemPost(g_dm9k_rx_sem); /* 执行接收处理程序 */
    }
    if (isr_status & DM9K_TX_INTR) // 发送中断
    {
        OSSemPost(g_dm9k_tx_sem);
        // 发送完成中断，用户自己添加代码
    }

    NET_REG_ADDR = save_reg; /* 回复所使用的位置 */
}

// 中断处理函数，注意不是中断服函数，需要在中断服务函数中调用
void DMA9K_ISRHandler(void)
{
    u16 int_status;
    u16 last_io;
    last_io    = NET_REG_ADDR;
    int_status = dm9k_ReadReg(DM9K_REG_ISR);
    dm9k_WriteReg(DM9K_REG_ISR, int_status); // 清除中断标志位

    if (int_status & DM9K_RX_INTR) // 接收中断
    {
        OSSemPost(g_dm9k_rx_sem);
        // 接收完成中断，用户自己添加代码
    }
    if (int_status & DM9K_TX_INTR) // 发送中断
    {
        // OSSemPost(g_dm9k_tx_sem);
        // 发送完成中断，用户自己添加代码
    }
    NET_REG_ADDR = last_io;
}

void ethernet_link_check_state(struct netif *netif)
{
    uint8_t linkchanged = 0;

    linkchanged = dm9k_linkstat();

    if (netif_is_link_up(netif) && (linkchanged == 0))
    {
        printf("dm9k LwIP Error interface....\r\n");
        netif_set_down(netif);
        netif_set_link_down(netif);
    }
    else if (!netif_is_link_up(netif) && (linkchanged == 1))
    {
        printf("dm9k LwIP Finish interface....\r\n");
        netif_set_up(netif);
        netif_set_link_up(netif);
    }
}
