351 lines
9.7 KiB
C
351 lines
9.7 KiB
C
#include "ethernetif.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#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;
|
|
} |