efb88ea367
- increase UART DMA/ring buffer sizes for mux traffic - switch SPI1 to Mode0 with prescaler /2 and align CubeMX settings - refactor CH390 memory read/write path with chunked SPI read and HAL bulk write - fallback to hardware MAC when configured MAC is invalid (all-zero) - add mux frame RTT logs and remove redundant UART1 polling - update Keil post-build viewer integration and include build viewer artifacts - update AT manual with all-zero MAC behavior
380 lines
10 KiB
C
380 lines
10 KiB
C
#include "ch390_runtime.h"
|
|
|
|
#include "CH390.h"
|
|
#include "CH390_Interface.h"
|
|
#include "SEGGER_RTT.h"
|
|
#include "ethernetif.h"
|
|
#include "stm32f1xx_hal.h"
|
|
#include "lwip/etharp.h"
|
|
#include "lwip/pbuf.h"
|
|
#include "lwip/stats.h"
|
|
|
|
#include <string.h>
|
|
|
|
static void ch390_runtime_dispatch_frame(struct netif *netif, struct pbuf *p)
|
|
{
|
|
if ((p != NULL) && (netif->input(p, netif) != ERR_OK)) {
|
|
pbuf_free(p);
|
|
}
|
|
}
|
|
|
|
static uint8_t ch390_runtime_drain_rx(struct netif *netif, uint8_t max_frames)
|
|
{
|
|
struct pbuf *p;
|
|
uint8_t drained = 0u;
|
|
|
|
while (drained < max_frames) {
|
|
p = ch390_runtime_input_frame(netif);
|
|
if (p == NULL) {
|
|
break;
|
|
}
|
|
ch390_runtime_dispatch_frame(netif, p);
|
|
drained++;
|
|
}
|
|
|
|
return drained;
|
|
}
|
|
|
|
static volatile uint8_t g_ch390_irq_pending;
|
|
static uint8_t g_ch390_ready;
|
|
static ch390_diag_t g_diag;
|
|
|
|
static uint8_t ch390_runtime_probe_identity(void)
|
|
{
|
|
g_diag.vendor_id = ch390_get_vendor_id();
|
|
g_diag.product_id = ch390_get_product_id();
|
|
g_diag.revision = ch390_get_revision();
|
|
g_diag.phy_bmcr = ch390_read_phy(CH390_PHY_BMCR);
|
|
g_diag.phy_bmsr = ch390_read_phy(CH390_PHY_BMSR);
|
|
g_diag.phy_id1 = ch390_read_phy(CH390_PHY_PHYID1);
|
|
g_diag.phy_id2 = ch390_read_phy(CH390_PHY_PHYID2);
|
|
g_diag.phy_anar = ch390_read_phy(CH390_PHY_ANAR);
|
|
g_diag.phy_anlpar = ch390_read_phy(CH390_PHY_ANLPAR);
|
|
g_diag.phy_aner = ch390_read_phy(CH390_PHY_ANER);
|
|
g_diag.nsr = ch390_read_reg(CH390_NSR);
|
|
g_diag.ncr = ch390_read_reg(CH390_NCR);
|
|
g_diag.rcr = ch390_read_reg(CH390_RCR);
|
|
g_diag.imr = ch390_read_reg(CH390_IMR);
|
|
g_diag.intcr = ch390_read_reg(CH390_INTCR);
|
|
g_diag.gpr = ch390_read_reg(CH390_GPR);
|
|
g_diag.isr = ch390_read_reg(CH390_ISR);
|
|
g_diag.phy_speed_10m = 0u;
|
|
g_diag.phy_full_duplex = 0u;
|
|
g_diag.link_up = (uint8_t)0u;
|
|
g_diag.id_valid = (uint8_t)((g_diag.vendor_id != 0x0000u) &&
|
|
(g_diag.vendor_id != 0xFFFFu) &&
|
|
(g_diag.product_id != 0x0000u) &&
|
|
(g_diag.product_id != 0xFFFFu));
|
|
return g_diag.id_valid;
|
|
}
|
|
|
|
static void ch390_runtime_refresh_diag(void)
|
|
{
|
|
uint8_t id_valid = ch390_runtime_probe_identity();
|
|
g_diag.int_pin = (uint8_t)ch390_get_int_pin();
|
|
|
|
if (id_valid != 0u) {
|
|
g_diag.phy_speed_10m = (uint8_t)ch390_get_phy_speed();
|
|
g_diag.phy_full_duplex = (uint8_t)ch390_get_duplex_mode();
|
|
g_diag.link_up = (uint8_t)ch390_get_link_status();
|
|
}
|
|
}
|
|
|
|
struct pbuf *ch390_runtime_input_frame(struct netif *netif)
|
|
{
|
|
struct ethernetif *ethernetif = (struct ethernetif *)netif->state;
|
|
struct pbuf *p = NULL;
|
|
struct pbuf *q;
|
|
uint16_t len;
|
|
uint16_t frame_len;
|
|
uint8_t rcr;
|
|
uint8_t rx_ready;
|
|
uint8_t rx_header[4];
|
|
ch390_read_reg(CH390_MRCMDX);
|
|
rx_ready = ch390_read_reg(CH390_MRCMDX);
|
|
|
|
if (rx_ready & CH390_PKT_ERR) {
|
|
rcr = ch390_read_reg(CH390_RCR);
|
|
ch390_write_reg(CH390_RCR, (uint8_t)(rcr & (uint8_t)(~RCR_RXEN)));
|
|
ch390_write_reg(CH390_MPTRCR, 0x01u);
|
|
ch390_write_reg(CH390_MRRH, 0x0Cu);
|
|
ch390_delay_us(1000u);
|
|
ch390_write_reg(CH390_RCR, rcr);
|
|
ethernetif->rx_len = 0u;
|
|
LINK_STATS_INC(link.drop);
|
|
g_diag.rx_packets_drop++;
|
|
return NULL;
|
|
}
|
|
|
|
if ((rx_ready & CH390_PKT_RDY) == 0u) {
|
|
ethernetif->rx_len = 0u;
|
|
return NULL;
|
|
}
|
|
|
|
g_diag.rx_ready_hits++;
|
|
|
|
ch390_read_mem(rx_header, 4);
|
|
ethernetif->rx_status = rx_header[1];
|
|
frame_len = (uint16_t)((uint16_t)rx_header[2] | ((uint16_t)rx_header[3] << 8));
|
|
|
|
if ((ethernetif->rx_status & 0x3Fu) != 0u || frame_len == 0u || frame_len > CH390_PKT_MAX) {
|
|
ethernetif->rx_len = 0u;
|
|
ch390_drop_packet(frame_len);
|
|
LINK_STATS_INC(link.drop);
|
|
g_diag.rx_packets_drop++;
|
|
return NULL;
|
|
}
|
|
|
|
ethernetif->rx_len = frame_len;
|
|
len = ethernetif->rx_len;
|
|
#if ETH_PAD_SIZE
|
|
len += ETH_PAD_SIZE;
|
|
#endif
|
|
|
|
p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
|
|
if (p != NULL) {
|
|
#if ETH_PAD_SIZE
|
|
pbuf_remove_header(p, ETH_PAD_SIZE);
|
|
#endif
|
|
for (q = p; q != NULL; q = q->next) {
|
|
ch390_read_mem((uint8_t *)q->payload, q->len);
|
|
}
|
|
#if ETH_PAD_SIZE
|
|
pbuf_add_header(p, ETH_PAD_SIZE);
|
|
#endif
|
|
LINK_STATS_INC(link.recv);
|
|
g_diag.rx_packets_ok++;
|
|
|
|
g_diag.last_frame_len = frame_len;
|
|
g_diag.last_payload_len = p->tot_len;
|
|
} else {
|
|
ch390_drop_packet(ethernetif->rx_len);
|
|
LINK_STATS_INC(link.memerr);
|
|
LINK_STATS_INC(link.drop);
|
|
g_diag.rx_packets_drop++;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
bool ch390_mac_address_valid(const uint8_t *mac)
|
|
{
|
|
if (mac == NULL) {
|
|
return false;
|
|
}
|
|
for (uint8_t i = 0; i < ETHARP_HWADDR_LEN; ++i) {
|
|
if (mac[i] == 0u) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ch390_runtime_init(struct netif *netif, const uint8_t *mac)
|
|
{
|
|
struct ethernetif *ethernetif = (struct ethernetif *)netif->state;
|
|
|
|
SEGGER_RTT_WriteString(0, "ETH init: gpio\r\n");
|
|
ch390_gpio_init();
|
|
SEGGER_RTT_WriteString(0, "ETH init: spi\r\n");
|
|
ch390_spi_init();
|
|
SEGGER_RTT_WriteString(0, "ETH init: reset\r\n");
|
|
ch390_hardware_reset();
|
|
|
|
SEGGER_RTT_WriteString(0, "ETH init: probe\r\n");
|
|
g_ch390_ready = ch390_runtime_probe_identity();
|
|
if (g_ch390_ready == 0u) {
|
|
netif->hwaddr_len = ETHARP_HWADDR_LEN;
|
|
netif->mtu = 1500;
|
|
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
|
|
|
ethernetif->rx_len = 0u;
|
|
ethernetif->rx_status = 0u;
|
|
|
|
netif_set_link_down(netif);
|
|
SEGGER_RTT_WriteString(0, "ETH init: invalid chip id\r\n");
|
|
return;
|
|
}
|
|
|
|
SEGGER_RTT_WriteString(0, "ETH init: default\r\n");
|
|
ch390_default_config();
|
|
SEGGER_RTT_WriteString(0, "ETH init: mac\r\n");
|
|
if (ch390_mac_address_valid(mac)) {
|
|
ch390_set_mac_address((uint8_t *)mac);
|
|
}
|
|
else {
|
|
if (mac != NULL) {
|
|
ch390_get_mac((uint8_t *)mac);
|
|
SEGGER_RTT_printf(0, "ETH init: invalid MAC in config, using hardware MAC: %02X:%02X:%02X:%02X:%02X:%02X\r\n",
|
|
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|
}
|
|
else {
|
|
SEGGER_RTT_WriteString(0, "ETH init: no MAC in config\r\n");
|
|
}
|
|
}
|
|
|
|
netif->hwaddr_len = ETHARP_HWADDR_LEN;
|
|
SEGGER_RTT_WriteString(0, "ETH init: getmac\r\n");
|
|
ch390_get_mac(netif->hwaddr);
|
|
netif->mtu = 1500;
|
|
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET;
|
|
|
|
ethernetif->rx_len = 0u;
|
|
ethernetif->rx_status = 0u;
|
|
|
|
ch390_runtime_refresh_diag();
|
|
g_ch390_ready = g_diag.id_valid;
|
|
|
|
SEGGER_RTT_WriteString(0, "ETH init: irq\r\n");
|
|
ch390_interrupt_init();
|
|
SEGGER_RTT_WriteString(0, "ETH init: done\r\n");
|
|
}
|
|
|
|
void ch390_runtime_set_irq_pending(void)
|
|
{
|
|
g_ch390_irq_pending = 1u;
|
|
}
|
|
|
|
uint8_t ch390_runtime_is_irq_pending(void)
|
|
{
|
|
return g_ch390_irq_pending;
|
|
}
|
|
|
|
void ch390_runtime_poll(struct netif *netif)
|
|
{
|
|
uint8_t int_status;
|
|
uint8_t rx_ready;
|
|
uint8_t rx_budget;
|
|
uint8_t rx_hint;
|
|
|
|
if (!g_ch390_ready) {
|
|
return;
|
|
}
|
|
|
|
g_diag.rx_poll_calls++;
|
|
|
|
rx_budget = 1u;
|
|
rx_hint = 0u;
|
|
|
|
if ((g_ch390_irq_pending != 0u) || (ch390_get_int_pin() == GPIO_PIN_RESET)) {
|
|
g_ch390_irq_pending = 0u;
|
|
int_status = ch390_get_int_status();
|
|
|
|
if ((int_status & ISR_LNKCHG) != 0u) {
|
|
ch390_runtime_check_link(netif);
|
|
}
|
|
|
|
if ((int_status & ISR_ROS) != 0u) {
|
|
LINK_STATS_INC(link.err);
|
|
}
|
|
|
|
if ((int_status & (ISR_PR | ISR_ROS | ISR_ROO)) != 0u) {
|
|
rx_hint = 1u;
|
|
rx_budget = 8u;
|
|
}
|
|
}
|
|
|
|
ch390_read_reg(CH390_MRCMDX);
|
|
rx_ready = ch390_read_reg(CH390_MRCMDX);
|
|
if ((rx_ready & CH390_PKT_RDY) != 0u) {
|
|
rx_hint = 1u;
|
|
if (rx_budget < 4u) {
|
|
rx_budget = 4u;
|
|
}
|
|
}
|
|
|
|
if (rx_hint != 0u) {
|
|
(void)ch390_runtime_drain_rx(netif, rx_budget);
|
|
}
|
|
}
|
|
|
|
void ch390_runtime_check_link(struct netif *netif)
|
|
{
|
|
uint8_t link_up;
|
|
static uint8_t s_last_reported = 0xFFu;
|
|
|
|
if (!g_ch390_ready) {
|
|
netif_set_link_down(netif);
|
|
return;
|
|
}
|
|
|
|
ch390_runtime_refresh_diag();
|
|
link_up = (uint8_t)ch390_get_link_status();
|
|
|
|
if (link_up != s_last_reported) {
|
|
SEGGER_RTT_printf(0,
|
|
"ETH link %s nsr=0x%02X bmsr=0x%04X anlpar=0x%04X\r\n",
|
|
link_up ? "up" : "down",
|
|
g_diag.nsr,
|
|
g_diag.phy_bmsr,
|
|
g_diag.phy_anlpar);
|
|
s_last_reported = link_up;
|
|
}
|
|
|
|
if (link_up) {
|
|
if (!netif_is_link_up(netif)) {
|
|
netif_set_link_up(netif);
|
|
}
|
|
} else if (netif_is_link_up(netif)) {
|
|
netif_set_link_down(netif);
|
|
}
|
|
}
|
|
|
|
err_t ch390_runtime_output(struct netif *netif, struct pbuf *p)
|
|
{
|
|
struct pbuf *q;
|
|
uint32_t start_tick;
|
|
|
|
LWIP_UNUSED_ARG(netif);
|
|
|
|
if (!g_ch390_ready) {
|
|
LINK_STATS_INC(link.drop);
|
|
return ERR_IF;
|
|
}
|
|
|
|
#if ETH_PAD_SIZE
|
|
pbuf_remove_header(p, ETH_PAD_SIZE);
|
|
#endif
|
|
|
|
start_tick = HAL_GetTick();
|
|
while (ch390_read_reg(CH390_TCR) & TCR_TXREQ) {
|
|
if ((HAL_GetTick() - start_tick) > 10u) {
|
|
#if ETH_PAD_SIZE
|
|
pbuf_add_header(p, ETH_PAD_SIZE);
|
|
#endif
|
|
LINK_STATS_INC(link.drop);
|
|
g_diag.tx_packets_timeout++;
|
|
return ERR_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
for (q = p; q != NULL; q = q->next) {
|
|
ch390_write_mem((uint8_t *)q->payload, q->len);
|
|
}
|
|
|
|
ch390_write_reg(CH390_TXPLL, p->tot_len & 0xFFu);
|
|
ch390_write_reg(CH390_TXPLH, (p->tot_len >> 8) & 0xFFu);
|
|
ch390_send_request();
|
|
|
|
#if ETH_PAD_SIZE
|
|
pbuf_add_header(p, ETH_PAD_SIZE);
|
|
#endif
|
|
|
|
LINK_STATS_INC(link.xmit);
|
|
g_diag.tx_packets_ok++;
|
|
return ERR_OK;
|
|
}
|
|
|
|
void ch390_runtime_get_diag(ch390_diag_t *diag)
|
|
{
|
|
if (diag != NULL) {
|
|
ch390_runtime_refresh_diag();
|
|
*diag = g_diag;
|
|
}
|
|
}
|
|
|
|
bool ch390_runtime_is_ready(void)
|
|
{
|
|
return g_ch390_ready != 0u;
|
|
}
|