#include "ethernetif.h" #include #include #include "ch32fun.h" #include "ch32v20xhw.h" #include "lwip/def.h" #include "lwip/etharp.h" #include "lwip/ethip6.h" #include "lwip/mem.h" #include "lwip/opt.h" #include "lwip/pbuf.h" #include "lwip/snmp.h" #include "lwip/stats.h" #include "netif/ethernet.h" #include "systick.h" #define IFNAME0 'e' #define IFNAME1 'n' #define ETH_RXBUFNB 4 #define ETH_TXBUFNB 1 #define ETH_MAX_PACKET_SIZE 1520 #define ETH_RX_BUF_SZE ETH_MAX_PACKET_SIZE #define ETH_TX_BUF_SZE ETH_MAX_PACKET_SIZE struct ethernetif { ETH_DMADESCTypeDef* DMARxDescToGet; ETH_DMADESCTypeDef* DMATxDescToSet; }; __attribute__((aligned(4))) ETH_DMADESCTypeDef DMARxDscrTab[ETH_RXBUFNB]; __attribute__((aligned(4))) ETH_DMADESCTypeDef DMATxDscrTab[ETH_TXBUFNB]; __attribute__((aligned(4))) uint8_t MACRxBuf[ETH_RXBUFNB * ETH_RX_BUF_SZE]; __attribute__((aligned(4))) uint8_t MACTxBuf[ETH_TXBUFNB * ETH_TX_BUF_SZE]; static volatile uint8_t g_rx_error_cnt = 0; volatile uint32_t g_isr_call_count = 0; static err_t low_level_output(struct netif* netif, struct pbuf* p); static struct pbuf* low_level_input(struct netif* netif); static void low_level_init(struct netif* netif); void eth_dma_tx_desc_chain_init(struct ethernetif* ethernetif, ETH_DMADESCTypeDef* DMATxDescTab, uint8_t* TxBuff, uint32_t TxBuffCount) { ethernetif->DMATxDescToSet = DMATxDescTab; DMATxDescTab->Status = 0; DMATxDescTab->Buffer1Addr = (uint32_t)TxBuff; DMATxDescTab->Buffer2NextDescAddr = (uint32_t)DMATxDescTab; // ring of 1 } void eth_dma_rx_desc_chain_init(struct ethernetif* ethernetif, ETH_DMADESCTypeDef* DMARxDescTab, uint8_t* RxBuff, uint32_t RxBuffCount) { ethernetif->DMARxDescToGet = DMARxDescTab; for (uint32_t i = 0; i < RxBuffCount; i++) { DMARxDescTab[i].Status = ETH_DMARxDesc_OWN; // give descriptor to DMA DMARxDescTab[i].ControlBufferSize = ETH_RX_BUF_SZE; DMARxDescTab[i].Buffer1Addr = (uint32_t)(&RxBuff[i * ETH_RX_BUF_SZE]); if (i < (RxBuffCount - 1)) { DMARxDescTab[i].Buffer2NextDescAddr = (uint32_t)(DMARxDescTab + i + 1); } else { DMARxDescTab[i].Buffer2NextDescAddr = (uint32_t)(DMARxDescTab); } } } void ETH_IRQHandler(void) __attribute__((interrupt)); void ETH_IRQHandler(void) { g_isr_call_count++; uint8_t flags = ETH10M->EIR; if (flags & RB_ETH_EIR_RXIF) { printf("<<< RX Interrupt Fired. EIR=0x%02X >>>>>\n", flags); } // tx complete/error if (flags & (RB_ETH_EIR_TXIF | RB_ETH_EIR_TXERIF)) { // release DMA descriptor back to cpu if (DMATxDscrTab[0].Status & ETH_DMATxDesc_OWN) { DMATxDscrTab[0].Status &= ~ETH_DMATxDesc_OWN; } } if (flags & RB_ETH_EIR_RXERIF) { if (g_rx_error_cnt < 255) { g_rx_error_cnt++; } } ETH10M->EIR = flags; } static void low_level_init(struct netif* netif) { struct ethernetif* ethernetif = netif->state; uint8_t i; netif->hwaddr_len = ETH_HWADDR_LEN; netif->hwaddr[0] = 0x00; netif->hwaddr[1] = 0x80; netif->hwaddr[2] = 0xE1; netif->hwaddr[3] = 0x00; netif->hwaddr[4] = 0x00; netif->hwaddr[5] = 0x01; netif->mtu = 1500; netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; // clock RCC->APB2PCENR |= RCC_APB2Periph_AFIO; RCC->CFGR0 &= ~((uint32_t)1 << 28); RCC->CFGR0 |= (RCC_ETHCLK_Div2 << 28); // interrupts ETH10M->EIE = 0; // clear ETH10M->EIE = RB_ETH_EIE_INTIE | RB_ETH_EIE_LINKIE | RB_ETH_EIE_TXIE | RB_ETH_EIE_TXERIE | RB_ETH_EIE_RXERIE; ETH10M->EIE |= RB_ETH_EIE_R_EN50; // 50 ohm pull-up ETH10M->EIR = 0xFF; ETH10M->ESTAT |= RB_ETH_ESTAT_INT | RB_ETH_ESTAT_BUFER; // reset mac ETH10M->ECON1 |= (RB_ETH_ECON1_TXRST | RB_ETH_ECON1_RXRST); ETH10M->ECON1 &= ~(RB_ETH_ECON1_TXRST | RB_ETH_ECON1_RXRST); // // mac regs ETH10M->ERXFON = 0; // accept unicast, multicast, broadcast R8_ETH_MAADRL1 = netif->hwaddr[5]; R8_ETH_MAADRL2 = netif->hwaddr[4]; R8_ETH_MAADRL3 = netif->hwaddr[3]; R8_ETH_MAADRL4 = netif->hwaddr[2]; R8_ETH_MAADRL5 = netif->hwaddr[1]; R8_ETH_MAADRL6 = netif->hwaddr[0]; ETH10M->MACON1 = RB_ETH_MACON1_MARXEN; ETH10M->MACON2 &= ~RB_ETH_MACON2_PADCFG; ETH10M->MACON2 |= PADCFG_AUTO_3 | RB_ETH_MACON2_TXCRCEN; ETH10M->MACON2 &= ~RB_ETH_MACON2_HFRMEN; // disable huge frames ETH10M->MACON2 |= RB_ETH_MACON2_FULDPX; ETH10M->MAMXFL = ETH_MAX_PACKET_SIZE; // PHY analog block ETH10M->ECON2 &= ~(0x07 << 1); ETH10M->ECON2 |= (5 << 1); // en PHY block EXTEN->EXTEN_CTR |= EXTEN_ETH_10M_EN; // tx desc eth_dma_tx_desc_chain_init(ethernetif, DMATxDscrTab, MACTxBuf, ETH_TXBUFNB); // rx desc eth_dma_rx_desc_chain_init(ethernetif, DMARxDscrTab, MACRxBuf, ETH_RXBUFNB); printf("set PHY to 10Mbps Full-Duplex mode\n"); WritePHYReg(PHY_BMCR, PHY_BMCR_FORCE_10BASE_T_FD); // init phy and auto neg // WritePHYReg(PHY_BMCR, PHY_BMCR_RESET); // Delay_Ms(200); // WritePHYReg(PHY_BMCR, PHY_BMCR_FORCE_10BASE_T_FD | PHY_BMCR_AN_ENABLE | // PHY_BMCR_AN_RESTART); // Delay_Ms(1000); NVIC_EnableIRQ(ETH_IRQn); printf("low_level_init : done\n"); } static err_t low_level_output(struct netif* netif, struct pbuf* p) { struct ethernetif* ethernetif = netif->state; struct pbuf* q; uint32_t len = 0; uint8_t* tx_buf_ptr = (uint8_t*)ethernetif->DMATxDescToSet->Buffer1Addr; if (ethernetif->DMATxDescToSet->Status & ETH_DMATxDesc_OWN) { return ERR_BUF; } for (q = p; q != NULL; q = q->next) { memcpy(&tx_buf_ptr[len], q->payload, q->len); len += q->len; } ethernetif->DMATxDescToSet->Status |= ETH_DMATxDesc_OWN; ETH10M->ETXLN = len; ETH10M->ETXST = (uint32_t)tx_buf_ptr; ETH10M->ECON1 |= RB_ETH_ECON1_TXRTS; ethernetif->DMATxDescToSet = (ETH_DMADESCTypeDef*)ethernetif->DMATxDescToSet->Buffer2NextDescAddr; MIB2_STATS_NETIF_ADD(netif, ifoutoctets, len); return ERR_OK; } struct pbuf* low_level_input(struct netif* netif) { struct ethernetif* ethernetif = netif->state; struct pbuf *p = NULL, *q; u16_t len; ETH_DMADESCTypeDef* dmarxdesc; if (ETH10M->EIR & RB_ETH_EIR_RXIF) { dmarxdesc = ethernetif->DMARxDescToGet; if (ETH10M->ESTAT & (RB_ETH_ESTAT_BUFER | RB_ETH_ESTAT_RXCRCER)) { len = 0; printf("HW RX Error ESTAT: 0x%02X\n", (unsigned int)ETH10M->ESTAT); ETH10M->ESTAT |= (RB_ETH_ESTAT_BUFER | RB_ETH_ESTAT_RXCRCER); } else { len = ETH10M->ERXLN; } if (len > 0) { p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); if (p != NULL) { uint8_t* rx_buffer = (uint8_t*)dmarxdesc->Buffer1Addr; uint32_t bytes_copied = 0; for (q = p; q != NULL; q = q->next) { memcpy(q->payload, rx_buffer + bytes_copied, q->len); bytes_copied += q->len; } printf( "\n>>> Packet Received (len=%d, MAC len=%d). Copied to LwIP. " "<<<\n\n", len, ETH10M->ERXLN); } else { printf("pbuf_alloc failed. Dropping packet.\n"); } } dmarxdesc->Status = ETH_DMARxDesc_OWN; ethernetif->DMARxDescToGet = (ETH_DMADESCTypeDef*)dmarxdesc->Buffer2NextDescAddr; ETH10M->ERXST = (uint32_t)ethernetif->DMARxDescToGet->Buffer1Addr; ETH10M->EIR = RB_ETH_EIR_RXIF; return p; // return pbuf to LwIP } return NULL; // No packet was available. } void ethernetif_input(struct netif* netif) { struct pbuf* p; p = low_level_input(netif); if (p != NULL) { if (netif->input(p, netif) != ERR_OK) { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); pbuf_free(p); } } } err_t ethernetif_init(struct netif* netif) { struct ethernetif* ethernetif; LWIP_ASSERT("netif != NULL", (netif != NULL)); ethernetif = mem_malloc(sizeof(struct ethernetif)); if (ethernetif == NULL) { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n")); return ERR_MEM; } #if LWIP_NETIF_HOSTNAME netif->hostname = "lwip-wch"; #endif MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 10000000); // 10 Mbps netif->state = ethernetif; netif->name[0] = IFNAME0; netif->name[1] = IFNAME1; #if LWIP_IPV4 netif->output = etharp_output; #endif #if LWIP_IPV6 netif->output_ip6 = ethip6_output; #endif netif->linkoutput = low_level_output; low_level_init(netif); return ERR_OK; } void ethernetif_link_poll(struct netif* netif) { struct ethernetif* ethernetif = netif->state; static uint32_t last_poll_time = 0; uint32_t now = millis(); // every 500ms if (now - last_poll_time < 500) { return; } last_poll_time = now; uint16_t bmsr = ReadPHYReg(PHY_BMSR); if (bmsr & PHY_Linked_Status) { if (!netif_is_link_up(netif)) { printf("Link is UP (10M-FD Mode)\n"); ETH10M->ERXST = (uint32_t)ethernetif->DMARxDescToGet->Buffer1Addr; ETH10M->ECON1 |= RB_ETH_ECON1_RXEN; netif_set_link_up(netif); g_rx_error_cnt = 0; } // polarity check // https://github.com/openwch/ch32v20x/blob/main/EVT/EXAM/ETH/NetLib/eth_driver.c#L262 if (g_rx_error_cnt > 5) { printf("RX error count: %d. Flipping PHY polarity\n", g_rx_error_cnt); uint16_t mdix_val = ReadPHYReg(PHY_MDIX); if ((mdix_val >> 2) & 0x01) { mdix_val &= ~(3 << 2); // normal } else { mdix_val |= (1 << 2); // reverse } WritePHYReg(PHY_MDIX, mdix_val); g_rx_error_cnt = 0; } } else { if (netif_is_link_up(netif)) { printf("Link is DOWN\n"); netif_set_link_down(netif); ETH10M->ECON1 &= ~RB_ETH_ECON1_RXEN; } } } void WritePHYReg(uint8_t reg_add, uint16_t reg_val) { R32_ETH_MIWR = (reg_add & RB_ETH_MIREGADR_MIRDL) | (1 << 8) | (reg_val << 16); } uint16_t ReadPHYReg(uint8_t reg_add) { ETH10M->MIERGADR = reg_add; return ETH10M->MIRD; }