/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2020-05-19 Joe first version */ #include "at32f4xx_eth.h" #include "board.h" #include #include "lwipopts.h" #include "drv_eth.h" #include /* EMAC Interface */ #define PHY_ADDRESS 0x00 /* Relative to AT32F407-EVAL Board */ //#define MII_MODE /* MII mode for AT32F407-EVAL Board (MB784) (check jumpers setting) */ #define RMII_MODE /* RMII mode for AT32F407-EVAL Board (MB784) (check jumpers setting) */ //#define CRYSTAL_ON_PHY #define MII_RX_REMAP 1 /* debug option */ //#define ETH_RX_DUMP //#define ETH_TX_DUMP //#define DRV_DEBUG #define LOG_TAG "drv.emac" #define ETH_RXBUFNB 4 #define ETH_TXBUFNB 2 #define LINK_THREAD_STACK_SIZE 256 #define LINK_THREAD_PREORITY 21 extern ETH_DMADESCTypeDef *DMATxDescToSet; extern ETH_DMADESCTypeDef *DMARxDescToGet; extern ETH_DMADESCTypeDef *DMAPTPTxDescToSet; extern ETH_DMADESCTypeDef *DMAPTPRxDescToGet; static ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB]; static rt_uint8_t Rx_Buff[ETH_RXBUFNB][ETH_MAX_PACKET_SIZE], Tx_Buff[ETH_TXBUFNB][ETH_MAX_PACKET_SIZE]; static struct rt_thread eth_link_thread; static rt_uint8_t eth_link_stack[LINK_THREAD_STACK_SIZE]; #define MAX_ADDR_LEN 6 /* Gloable variables ---------------------------------------------------------*/ void static Reset_Phy(void); void NVIC_Configuration(void); void GPIO_Configuration(void); struct rt_at32_eth { /* inherit from ethernet device */ struct eth_device parent; #ifndef PHY_USING_INTERRUPT_MODE rt_timer_t poll_link_timer; #endif /* interface address info, hw address */ rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* ETH_Speed */ uint32_t ETH_Speed; /* ETH_Duplex_Mode */ uint32_t ETH_Mode; }; static ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB], DMATxDscrTab[ETH_TXBUFNB]; /* Ethernet Rx & Tx DMA Descriptors */ static uint8_t Rx_Buff[ETH_RXBUFNB][ETH_MAX_PACKET_SIZE], Tx_Buff[ETH_TXBUFNB][ETH_MAX_PACKET_SIZE]; /* Ethernet buffers */ //static ETH_HandleTypeDef EthHandle; static struct rt_at32_eth at32_eth_device; static struct rt_semaphore tx_wait; static rt_bool_t tx_is_waiting = RT_FALSE; #if defined(ETH_RX_DUMP) || defined(ETH_TX_DUMP) #define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') static void dump_hex(const rt_uint8_t *ptr, rt_size_t buflen) { unsigned char *buf = (unsigned char *)ptr; int i, j; for (i = 0; i < buflen; i += 16) { rt_kprintf("%08X: ", i); for (j = 0; j < 16; j++) if (i + j < buflen) rt_kprintf("%02X ", buf[i + j]); else rt_kprintf(" "); rt_kprintf(" "); for (j = 0; j < 16; j++) if (i + j < buflen) rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.'); rt_kprintf("\n"); } } #endif /** * @brief reset the phy * @param None * @retval None */ void static Reset_Phy(void) { GPIO_InitType GPIO_InitStructure; GPIO_InitStructure.GPIO_Pins = GPIO_Pins_8; GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT_PP; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_ResetBits(GPIOC, GPIO_Pins_8); rt_thread_mdelay(2); GPIO_SetBits(GPIOC, GPIO_Pins_8); rt_thread_mdelay(2000); } /** * @brief Configure NVIC for ISR * @param None * @retval None */ void NVIC_Configuration(void) { NVIC_InitType NVIC_InitStructure; /* Set the Vector Table base location at 0x08000000 */ NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); /* 2 bit for pre-emption priority, 2 bits for subpriority */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); /* Enable the Ethernet global Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } /** * @brief Configure GPIO for ethernet * @param None * @retval None */ void GPIO_Configuration(void) { GPIO_InitType GPIO_InitStructure; #if MII_RX_REMAP GPIO_PinsRemapConfig(GPIO_Remap_ETH, ENABLE); #endif RCC_APB2PeriphClockCmd(RCC_APB2PERIPH_GPIOA | RCC_APB2PERIPH_GPIOB | RCC_APB2PERIPH_GPIOC | RCC_APB2PERIPH_GPIOD | RCC_APB2PERIPH_GPIOE | RCC_APB2PERIPH_AFIO, ENABLE); /* ETHERNET pins configuration */ /* AF Output Push Pull: ETH_MII_MDIO / ETH_RMII_MDIO: PA2 ETH_MII_MDC / ETH_RMII_MDC: PC1 */ /* Configure PA2 as alternate function push-pull MDIO*/ GPIO_InitStructure.GPIO_Pins = GPIO_Pins_2; GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); /*PC1-->MDC*/ GPIO_InitStructure.GPIO_Pins = GPIO_Pins_1; GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOC, &GPIO_InitStructure); /*MII Mode GPIO configuration*/ #ifdef MII_MODE /**********************MII Tx Pin Define****************************/ /* ETH_MII_TX0-->PB12 AF-PP ETH_MII_TX1-->PB13 AF-PP ETH_MII_TX2-->PC2 AF-PP ETH_MII_TX3-->PB8 AF-PP ETH_MII_TX_EN-->PB11 AF-PP ETH_MII_TX_CLK-->PC3 */ GPIO_InitStructure.GPIO_Pins = GPIO_Pins_8 | GPIO_Pins_11 | GPIO_Pins_12 | GPIO_Pins_13; GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pins = GPIO_Pins_2 | GPIO_Pins_3; GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOC, &GPIO_InitStructure); /**********************MII Rx Pin Define****************************/ #if MII_RX_REMAP /*IO PIN remaped*/ /* ETH_MII_RX_DV-->PD8 ETH_MII_RXD0-->PD9 ETH_MII_RXD1-->PD10 ETH_MII_RXD2-->PD11 ETH_MII_RXD3-->PD12 ETH_MII_RXCLK-->PA1 ETH_MII_CRS-->PA0 ETH_MII_COL-->PA3 ETH_MII_RX_ER-->PB10 */ GPIO_InitStructure.GPIO_Pins = GPIO_Pins_8 | GPIO_Pins_9 | GPIO_Pins_10 | GPIO_Pins_11 | GPIO_Pins_12; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pins = GPIO_Pins_0 | GPIO_Pins_1 | GPIO_Pins_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pins = GPIO_Pins_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, &GPIO_InitStructure); #else /* ETH_MII_RX_DV-->PA7 ETH_MII_RXD0-->PC4 ETH_MII_RXD1-->PC5 ETH_MII_RXD2-->PB0 ETH_MII_RXD3-->PB1 ETH_MII_RXCLK-->PA1 ETH_MII_CRS-->PA0 ETH_MII_COL-->PA3 ETH_MII_RX_ER-->PB10 */ GPIO_InitStructure.GPIO_Pins = GPIO_Pins_0 | GPIO_Pins_1 | GPIO_Pins_3 | GPIO_Pins_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pins = GPIO_Pins_4 | GPIO_Pins_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pins = GPIO_Pins_0 | GPIO_Pins_1 | GPIO_Pins_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOB, &GPIO_InitStructure); #endif //End MII RX REMAP #endif //End MII mode #ifdef RMII_MODE /**********************RMII Tx Pin Define****************************/ /* ETH_RMII_TX0-->PB12 AF-PP ETH_RMII_TX1-->PB13 AF-PP ETH_RMII_TX_EN-->PB11 AF-PP */ GPIO_InitStructure.GPIO_Pins = GPIO_Pins_11 | GPIO_Pins_12 | GPIO_Pins_13; GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOB, &GPIO_InitStructure); /**********************RMII Rx Pin Define****************************/ #if MII_RX_REMAP /*IO PIN remaped*/ /* ETH_RMII_RX_DV-->PD8 ETH_RMII_RXD0-->PD9 ETH_RMII_RXD1-->PD10 ETH_RMII_REF_CLK-->PA1 */ GPIO_InitStructure.GPIO_Pins = GPIO_Pins_8 | GPIO_Pins_9 | GPIO_Pins_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOD, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pins = GPIO_Pins_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); #else /* ETH_RMII_RX_DV-->PA7 ETH_RMII_RXD0-->PC4 ETH_RMII_RXD1-->PC5 ETH_RMII_REF_CLK-->PA1 */ GPIO_InitStructure.GPIO_Pins = GPIO_Pins_1 | GPIO_Pins_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pins = GPIO_Pins_4 | GPIO_Pins_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOC, &GPIO_InitStructure); #endif //End RMII RX REMAP #endif //End RMII mode /* ADC Channel4 config --------------------------------------------------------*/ /* Configure PA4(ADC Channel4) as analog input -------------------------*/ #ifdef ADC_NECESSARY GPIO_InitStructure.GPIO_Pins = GPIO_Pins_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_ANALOG; GPIO_Init(GPIOA, &GPIO_InitStructure); #endif /* MCO pin configuration------------------------------------------------- */ /* Configure MCO (PA8) as alternate function push-pull */ #ifndef CRYSTAL_ON_PHY GPIO_InitStructure.GPIO_Pins = GPIO_Pins_8; GPIO_InitStructure.GPIO_MaxSpeed = GPIO_MaxSpeed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); #endif } /* EMAC initialization function */ static rt_err_t rt_at32_eth_init(rt_device_t dev) { ETH_InitType ETH_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPERIPH_ETHMAC | RCC_AHBPERIPH_ETHMACTX | RCC_AHBPERIPH_ETHMACRX, ENABLE); /* MII/RMII Media interface selection ------------------------------------------*/ #ifdef MII_MODE /* Mode MII with AT32F407-EVAL */ GPIO_ETH_MediaInterfaceConfig(GPIO_ETH_MediaInterface_MII); /* Get 25MHz from system clock 200MHz on PA8 pin (MCO) */ #ifndef CRYSTAL_ON_PHY RCC_CLKOUTConfig(RCC_CLKOUT_SYSCLK, RCC_MCOPRE_8); #endif #elif defined RMII_MODE /* Mode RMII with AT32F407-EVAL */ GPIO_ETH_MediaInterfaceConfig(GPIO_ETH_MediaInterface_RMII); #ifndef CRYSTAL_ON_PHY RCC_CLKOUTConfig(RCC_CLKOUT_SYSCLK, RCC_MCOPRE_8); /*25M to RMII Mode*/ #endif #endif /*Reset phy*/ Reset_Phy(); /* Reset ETHERNET on AHB Bus */ ETH_DeInit(); /* Software reset */ ETH_SoftwareReset(); /* Wait for software reset */ while (ETH_GetSoftwareResetStatus() == SET); /* ETHERNET Configuration ------------------------------------------------------*/ /* Call ETH_StructInit if you don't like to configure all ETH_InitStructure parameter */ ETH_StructInit(Ð_InitStructure); /* Fill ETH_InitStructure parametrs */ /*------------------------ MAC -----------------------------------*/ ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable;//ETH_AutoNegotiation_Enable ; ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable; ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Enable; 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_MulticastFramesFilter_Perfect; ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect; #ifdef CHECKSUM_BY_HARDWARE ETH_InitStructure.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable; #endif /*------------------------ DMA -----------------------------------*/ /* When we use the Checksum offload feature, we need to enable the Store and Forward mode: the store and forward guarantee that a whole frame is stored in the FIFO, so the MAC can insert/verify the checksum, if the checksum is OK the DMA can handle the frame otherwise the frame is dropped */ ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable;//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; ETH_InitStructure.ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable; ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable; ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat; ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat; ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1; /* Configure Ethernet, check error */ if(ETH_Init(Ð_InitStructure, PHY_ADDRESS) == ((uint32_t)0)) { return RT_ERROR; } /* Enable DMA Receive interrupt (need to enable in this case Normal interrupt) */ ETH_DMAITConfig(ETH_DMA_INT_NIS | ETH_DMA_INT_R, ENABLE); /* Initialize Tx Descriptors list: Chain Mode */ ETH_DMATxDescChainInit(DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); /* Initialize Rx Descriptors list: Chain Mode */ ETH_DMARxDescChainInit(DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); /* MAC address configuration */ ETH_MACAddressConfig(ETH_MAC_Address0, (u8*)&at32_eth_device.dev_addr[0]); /* Enable ETH transmition and recetion */ ETH_Start(); return RT_EOK; } static rt_err_t rt_at32_eth_open(rt_device_t dev, rt_uint16_t oflag) { LOG_D("emac open"); return RT_EOK; } static rt_err_t rt_at32_eth_close(rt_device_t dev) { LOG_D("emac close"); return RT_EOK; } static rt_size_t rt_at32_eth_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) { LOG_D("emac read"); rt_set_errno(-RT_ENOSYS); return 0; } static rt_size_t rt_at32_eth_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) { LOG_D("emac write"); rt_set_errno(-RT_ENOSYS); return 0; } static rt_err_t rt_at32_eth_control(rt_device_t dev, int cmd, void *args) { switch (cmd) { case NIOCTL_GADDR: /* get mac address */ if (args) rt_memcpy(args, at32_eth_device.dev_addr, 6); else return -RT_ERROR; break; default : break; } return RT_EOK; } /* ethernet device interface */ /* transmit data*/ rt_err_t rt_at32_eth_tx(rt_device_t dev, struct pbuf *p) { struct pbuf* q; rt_uint32_t offset; /* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */ while ((DMATxDescToSet->Status & ETH_DMATxDesc_OWN) != (uint32_t)RESET) { rt_err_t result; rt_uint32_t level; level = rt_hw_interrupt_disable(); tx_is_waiting = RT_TRUE; rt_hw_interrupt_enable(level); /* it's own bit set, wait it */ result = rt_sem_take(&tx_wait, RT_WAITING_FOREVER); if (result == RT_EOK) break; if (result == -RT_ERROR) return -RT_ERROR; } offset = 0; for (q = p; q != NULL; q = q->next) { uint8_t *to; /* Copy the frame to be sent into memory pointed by the current ETHERNET DMA Tx descriptor */ to = (uint8_t*)((DMATxDescToSet->Buffer1Addr) + offset); memcpy(to, q->payload, q->len); offset += q->len; } #ifdef ETH_TX_DUMP { rt_uint32_t i; rt_uint8_t *ptr = (rt_uint8_t*)(DMATxDescToSet->Buffer1Addr); AT32_ETH_PRINTF("tx_dump, len:%d\r\n", p->tot_len); for(i=0; itot_len; i++) { AT32_ETH_PRINTF("%02x ",*ptr); ptr++; if(((i+1)%8) == 0) { AT32_ETH_PRINTF(" "); } if(((i+1)%16) == 0) { AT32_ETH_PRINTF("\r\n"); } } AT32_ETH_PRINTF("\r\ndump done!\r\n"); } #endif /* Setting the Frame Length: bits[12:0] */ DMATxDescToSet->ControlBufferSize = (p->tot_len & ETH_DMATxDesc_TBS1); /* Setting the last segment and first segment bits (in this case a frame is transmitted in one descriptor) */ DMATxDescToSet->Status |= ETH_DMATxDesc_LS | ETH_DMATxDesc_FS; /* Enable TX Completion Interrupt */ DMATxDescToSet->Status |= ETH_DMATxDesc_IC; #ifdef CHECKSUM_BY_HARDWARE DMATxDescToSet->Status |= ETH_DMATxDesc_ChecksumTCPUDPICMPFull; /* clean ICMP checksum STM32F need */ { struct eth_hdr *ethhdr = (struct eth_hdr *)(DMATxDescToSet->Buffer1Addr); /* is IP ? */ if( ethhdr->type == htons(ETHTYPE_IP) ) { struct ip_hdr *iphdr = (struct ip_hdr *)(DMATxDescToSet->Buffer1Addr + SIZEOF_ETH_HDR); /* is ICMP ? */ if( IPH_PROTO(iphdr) == IP_PROTO_ICMP ) { struct icmp_echo_hdr *iecho = (struct icmp_echo_hdr *)(DMATxDescToSet->Buffer1Addr + SIZEOF_ETH_HDR + sizeof(struct ip_hdr) ); iecho->chksum = 0; } } } #endif /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ DMATxDescToSet->Status |= ETH_DMATxDesc_OWN; /* When Tx Buffer unavailable flag is set: clear it and resume transmission */ if ((ETH->DMASTS & ETH_DMA_FLAG_TBU) != (uint32_t)RESET) { /* Clear TBUS ETHERNET DMA flag */ ETH->DMASTS = ETH_DMA_FLAG_TBU; /* Transmit Poll Demand to resume DMA transmission*/ ETH->DMATPD = 0; } /* Update the ETHERNET DMA global Tx descriptor with next Tx decriptor */ /* Chained Mode */ /* Selects the next DMA Tx descriptor list for next buffer to send */ DMATxDescToSet = (ETH_DMADESCTypeDef*) (DMATxDescToSet->Buffer2NextDescAddr); /* Return SUCCESS */ return RT_EOK; } /* receive data*/ struct pbuf *rt_at32_eth_rx(rt_device_t dev) { struct pbuf* p; rt_uint32_t offset = 0, framelength = 0; /* init p pointer */ p = RT_NULL; /* Check if the descriptor is owned by the ETHERNET DMA (when set) or CPU (when reset) */ if(((DMARxDescToGet->Status & ETH_DMARxDesc_OWN) != (uint32_t)RESET)) return p; if (((DMARxDescToGet->Status & ETH_DMARxDesc_ES) == (uint32_t)RESET) && ((DMARxDescToGet->Status & ETH_DMARxDesc_LS) != (uint32_t)RESET) && ((DMARxDescToGet->Status & ETH_DMARxDesc_FS) != (uint32_t)RESET)) { /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */ framelength = ((DMARxDescToGet->Status & ETH_DMARxDesc_FL) >> 16) - 4; /* allocate buffer */ p = pbuf_alloc(PBUF_LINK, framelength, PBUF_RAM); if (p != RT_NULL) { struct pbuf* q; for (q = p; q != RT_NULL; q= q->next) { /* Copy the received frame into buffer from memory pointed by the current ETHERNET DMA Rx descriptor */ memcpy(q->payload, (uint8_t *)((DMARxDescToGet->Buffer1Addr) + offset), q->len); offset += q->len; } #ifdef ETH_RX_DUMP { rt_uint32_t i; rt_uint8_t *ptr = (rt_uint8_t*)(DMARxDescToGet->Buffer1Addr); AT32_ETH_PRINTF("rx_dump, len:%d\r\n", p->tot_len); for(i=0; itot_len; i++) { AT32_ETH_PRINTF("%02x ", *ptr); ptr++; if(((i+1)%8) == 0) { AT32_ETH_PRINTF(" "); } if(((i+1)%16) == 0) { AT32_ETH_PRINTF("\r\n"); } } AT32_ETH_PRINTF("\r\ndump done!\r\n"); } #endif } } /* Set Own bit of the Rx descriptor Status: gives the buffer back to ETHERNET DMA */ DMARxDescToGet->Status = ETH_DMARxDesc_OWN; /* When Rx Buffer unavailable flag is set: clear it and resume reception */ if ((ETH->DMASTS & ETH_DMA_FLAG_RBU) != (uint32_t)RESET) { /* Clear RBUS ETHERNET DMA flag */ ETH->DMASTS = ETH_DMA_FLAG_RBU; /* Resume DMA reception */ ETH->DMARPD = 0; } /* Update the ETHERNET DMA global Rx descriptor with next Rx decriptor */ /* Chained Mode */ if((DMARxDescToGet->ControlBufferSize & ETH_DMARxDesc_RCH) != (uint32_t)RESET) { /* Selects the next DMA Rx descriptor list for next buffer to read */ DMARxDescToGet = (ETH_DMADESCTypeDef*) (DMARxDescToGet->Buffer2NextDescAddr); } else /* Ring Mode */ { if((DMARxDescToGet->ControlBufferSize & ETH_DMARxDesc_RER) != (uint32_t)RESET) { /* Selects the first DMA Rx descriptor for next buffer to read: last Rx descriptor was used */ DMARxDescToGet = (ETH_DMADESCTypeDef*) (ETH->DMARDLADDR); } else { /* Selects the next DMA Rx descriptor list for next buffer to read */ DMARxDescToGet = (ETH_DMADESCTypeDef*) ((uint32_t)DMARxDescToGet + 0x10 + ((ETH->DMABM & 0x0000007C) >> 2)); } } return p; } static void eth_link_thread_entry(void *paramter) { uint8_t linked_down = 1; struct netif *pnetif = at32_eth_device.parent.netif; while(1){ if((ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BSR) & PHY_Linked_Status) && (linked_down == 1)) { /* link up */ linked_down = 0; #ifndef RT_LWIP_DHCP pnetif->ip_addr = inet_addr(RT_LWIP_IPADDR); pnetif->gw = inet_addr(RT_LWIP_GWADDR); pnetif->netmask = inet_addr(RT_LWIP_MSKADDR); #else IP4_ADDR(&(pnetif->ip_addr), 0, 0, 0, 0); IP4_ADDR(&(pnetif->netmask), 0, 0, 0, 0); IP4_ADDR(&(pnetif->gw), 0, 0, 0, 0); #endif eth_device_linkchange(&(at32_eth_device.parent), RT_TRUE); }else if(!(ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BSR) & PHY_Linked_Status) && (linked_down == 0)) { /* link down */ linked_down = 1; eth_device_linkchange(&(at32_eth_device.parent), RT_FALSE); } rt_thread_mdelay(500); } } /* interrupt service routine */ void ETH_IRQHandler(void) { rt_uint32_t status; status = ETH->DMASTS; /* Clear received IT */ if ((status & ETH_DMA_INT_NIS) != (u32)RESET) ETH->DMASTS = (u32)ETH_DMA_INT_NIS; if ((status & ETH_DMA_INT_AIS) != (u32)RESET) ETH->DMASTS = (u32)ETH_DMA_INT_AIS; if ((status & ETH_DMA_INT_RO) != (u32)RESET) ETH->DMASTS = (u32)ETH_DMA_INT_RO; if ((status & ETH_DMA_INT_RBU) != (u32)RESET) ETH->DMASTS = (u32)ETH_DMA_INT_RBU; if (ETH_GetDMAITStatus(ETH_DMA_INT_R) == SET) /* packet receiption */ { /* a frame has been received */ eth_device_ready(&(at32_eth_device.parent)); ETH_DMAClearITPendingBit(ETH_DMA_INT_R); } if (ETH_GetDMAITStatus(ETH_DMA_INT_T) == SET) /* packet transmission */ { if (tx_is_waiting == RT_TRUE) { tx_is_waiting = RT_FALSE; rt_sem_release(&tx_wait); } ETH_DMAClearITPendingBit(ETH_DMA_INT_T); } } enum { PHY_LINK = (1 << 0), PHY_100M = (1 << 1), PHY_FULL_DUPLEX = (1 << 2), }; /* Register the EMAC device */ static int rt_hw_at32_eth_init(void) { rt_err_t state = RT_EOK; Reset_Phy(); GPIO_Configuration(); NVIC_Configuration(); at32_eth_device.dev_addr[0] = 0x00; at32_eth_device.dev_addr[1] = 0x80; at32_eth_device.dev_addr[2] = 0xE1; at32_eth_device.dev_addr[3] = 0x94; at32_eth_device.dev_addr[4] = 0x87; at32_eth_device.dev_addr[5] = 0x55; at32_eth_device.parent.parent.init = rt_at32_eth_init; at32_eth_device.parent.parent.open = rt_at32_eth_open; at32_eth_device.parent.parent.close = rt_at32_eth_close; at32_eth_device.parent.parent.read = rt_at32_eth_read; at32_eth_device.parent.parent.write = rt_at32_eth_write; at32_eth_device.parent.parent.control = rt_at32_eth_control; at32_eth_device.parent.parent.user_data = RT_NULL; at32_eth_device.parent.eth_rx = rt_at32_eth_rx; at32_eth_device.parent.eth_tx = rt_at32_eth_tx; /* register eth device */ state = eth_device_init(&(at32_eth_device.parent), "e0"); if (RT_EOK == state) { LOG_D("emac device init success"); state = rt_thread_init(ð_link_thread, "eth_link_detect", eth_link_thread_entry, RT_NULL, ð_link_stack[0], LINK_THREAD_STACK_SIZE, LINK_THREAD_PREORITY, 20); if (state == RT_EOK) { rt_thread_startup(ð_link_thread); } } else { LOG_E("emac device init faild: %d", state); state = -RT_ERROR; } /* start phy monitor */ return state; } INIT_DEVICE_EXPORT(rt_hw_at32_eth_init);