#include "stm32f4x7_phy.h"
#include "dwt.h"
#include "lwip/api.h"
#include "lwip/sockets.h"
#include "stm32f4x7_eth.h"

ETH_InitTypeDef     ETH_InitStructure;
__IO uint32_t       EthStatus = 0;
extern struct netif gnetif;
static void         phy_gpio_config(void);
static void         phy_mac_dma_config(void);
static void         nvic_configuration(void);

/**
 * @brief  Inserts a delay time.
 * @param  nCount: number of 10ms periods to wait for.
 * @retval None
 */
void ETH_Delay10ms(uint32_t nCount)
{
    // OSTimeDly(10 * nCount);
    us_delay(10 * nCount);
}

/*!
    \brief      setup ethernet system(GPIOs, clocks, MAC, DMA, systick)
    \param[in]  none
    \param[out] none
    \retval     none
*/
void eth_init(void)
{
    nvic_configuration();

    /* configure the GPIO ports for ethernet pins */
    phy_gpio_config();

    /* configure the ethernet MAC/DMA */
    phy_mac_dma_config();

    /* Get Ethernet link status*/
    if (ETH_ReadPHYRegister(ETHERNET_PHY_ADDRESS, PHY_SR) & 1)
    {
        EthStatus |= ETH_LINK_FLAG;
    }

    // /* Configure the PHY to generate an interrupt on change of link status */
    // Eth_Link_PHYITConfig(ETHERNET_PHY_ADDRESS);

    ETH_DMAITConfig(ETH_DMA_IT_R | ETH_DMA_IT_NIS, ENABLE);
}

/*!
    \brief      configures the ethernet interface
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void phy_mac_dma_config(void)
{

    /* Reset ETHERNET on AHB Bus */
    ETH_DeInit();
    /* Software reset */
    ETH_SoftwareReset();
    /* Wait for software reset */
    while (ETH_GetSoftwareResetStatus() == SET)
        ;

    /* ETHERNET Configuration ------------------------------*/
    /* 缺省配置ETH_InitStructure */
    ETH_StructInit(&ETH_InitStructure);

    /* Fill ETH_InitStructure parametrs */
    /*--------------------   MAC   ----------------------------*/
    /* 开启网络自适应功能，速度和工作模式无需配置 */
    ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable;
    //  ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Disable;
    //  ETH_InitStructure.ETH_Speed = ETH_Speed_10M;
    //  ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex;
    /* 关闭反馈 */
    ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable;
    /* 关闭重传功能 */
    ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable;
    /* 关闭自动去除PDA/CRC功能  */
    ETH_InitStructure.ETH_AutomaticPadCRCStrip =
        ETH_AutomaticPadCRCStrip_Disable;
    /* 关闭接收所有的帧 */
    ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable;
    /* 允许接收所有广播帧 */
    ETH_InitStructure.ETH_BroadcastFramesReception =
        ETH_BroadcastFramesReception_Enable;
    /* 关闭混合模式的地址过滤  */
    ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable;
    /* 对于组播地址使用完美地址过滤    */
    ETH_InitStructure.ETH_MulticastFramesFilter =
        ETH_MulticastFramesFilter_Perfect;
    /* 对单播地址使用完美地址过滤  */
    ETH_InitStructure.ETH_UnicastFramesFilter =
        ETH_UnicastFramesFilter_Perfect;
#ifdef CHECKSUM_BY_HARDWARE
    /* 开启ipv4和TCP/UDP/ICMP的帧校验和卸载   */
    ETH_InitStructure.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable;
#endif

    /*------------------------   DMA   -------------------------------*/
    /*当我们使用帧校验和卸载功能的时候，一定要使能存储转发模式,存储
    转发模式中要保证整个帧存储在FIFO中,  这样MAC能插入/识别出帧校验
    值,当真校验正确的时候DMA就可以处理帧,否则就丢弃掉该帧*/
    /* 开启丢弃TCP/IP错误帧 */
    ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame =
        ETH_DropTCPIPChecksumErrorFrame_Enable;
    /* 开启接收数据的存储转发模式  */
    ETH_InitStructure.ETH_ReceiveStoreForward =
        ETH_ReceiveStoreForward_Enable;
    /* 开启发送数据的存储转发模式   */
    ETH_InitStructure.ETH_TransmitStoreForward =
        ETH_TransmitStoreForward_Enable;

    /* 禁止转发错误帧 */
    ETH_InitStructure.ETH_ForwardErrorFrames =
        ETH_ForwardErrorFrames_Disable;
    /* 不转发过小的好帧 */
    ETH_InitStructure.ETH_ForwardUndersizedGoodFrames =
        ETH_ForwardUndersizedGoodFrames_Disable;
    /* 打开处理第二帧功能 */
    ETH_InitStructure.ETH_SecondFrameOperate =
        ETH_SecondFrameOperate_Enable;
    /* 开启DMA传输的地址对齐功能 */
    ETH_InitStructure.ETH_AddressAlignedBeats =
        ETH_AddressAlignedBeats_Enable;
    /* 开启固定突发功能 */
    ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable;
    /* DMA发送的最大突发长度为32个节拍 */
    ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat;
    /*DMA接收的最大突发长度为32个节拍 */
    ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat;
    ETH_InitStructure.ETH_DMAArbitration =
        ETH_DMAArbitration_RoundRobin_RxTx_2_1;

    /* 配置ETH */
    EthStatus = ETH_Init(&ETH_InitStructure, ETHERNET_PHY_ADDRESS);
}

/*!
    \brief      configures the nested vectored interrupt controller
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void nvic_configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel                   = ETH_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority        = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

/*!
    \brief      configures the different GPIO ports
    \param[in]  none
    \param[out] none
    \retval     none
*/
static void phy_gpio_config(void)
{
    /* Enable SYSCFG clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

#ifdef MII_MODE
#ifdef PHY_CLOCK_MCO
    /* output HXTAL clock (25MHz) on CKOUT0 pin(PA8) to clock the PHY */
    RCC_MCO1Config(RCC_MCO1Source_HSE, RCC_MCO1Div_1);
#endif /* PHY_CLOCK_MCO */
    SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_MII);
#elif defined RMII_MODE
    SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII);
#endif
}

/**
 * @brief  This function handles Ethernet link status.
 * @param  None
 * @retval None
 */
void ETH_CheckLinkStatus(uint16_t PHYAddress)
{
    static uint8_t status = 0;
    uint32_t       t      = ETH_ReadPHYRegister(ETHERNET_PHY_ADDRESS, PHY_SR) & 1;

    /* If we have link and previous check was not yet */
    if (t && !status)
    {
        /* Set link up */
        netif_set_link_up(&gnetif);

        status = 1;
    }
    /* If we don't have link and it was on previous check */
    if (!t && status)
    {
        // EthLinkStatus = 1;
        /* Set link down */
        netif_set_link_down(&gnetif);

        status = 0;
    }
}

/**
 * @brief  Configure the PHY to generate an interrupt on change of link status.
 * @param PHYAddress: external PHY address
 * @retval None
 */
uint32_t Eth_Link_PHYITConfig(uint16_t PHYAddress)
{
    uint16_t tmpreg = 0;

    /* Read MICR register */
    tmpreg = ETH_ReadPHYRegister(PHYAddress, PHY_MICR);

    /* Enable output interrupt events to signal via the INT pin */
    tmpreg |= (uint16_t)(PHY_MICR_INT_EN | PHY_MICR_INT_OE);
    if (!(ETH_WritePHYRegister(PHYAddress, PHY_MICR, tmpreg)))
    {
        /* Return ERROR in case of write timeout */
        return ETH_ERROR;
    }

    /* Read MISR register */
    tmpreg = ETH_ReadPHYRegister(PHYAddress, PHY_MISR);

    /* Enable Interrupt on change of link status */
    tmpreg |= (uint16_t)PHY_MISR_LINK_INT_EN;
    if (!(ETH_WritePHYRegister(PHYAddress, PHY_MISR, tmpreg)))
    {
        /* Return ERROR in case of write timeout */
        return ETH_ERROR;
    }
    /* Return SUCCESS */
    return ETH_SUCCESS;
}
