Merge pull request #15783 from maribu/stm32_eth_fix_error_handling

cpu/stm32/periph_eth: fix error handling in send()
This commit is contained in:
benpicco 2021-01-22 20:25:25 +01:00 committed by GitHub
commit 46337efd93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -33,7 +33,8 @@
#include "net/netdev/eth.h" #include "net/netdev/eth.h"
#include "periph/gpio.h" #include "periph/gpio.h"
#define ENABLE_DEBUG 0 #define ENABLE_DEBUG 0
#define ENABLE_DEBUG_VERBOSE 0
#include "debug.h" #include "debug.h"
#if IS_USED(MODULE_STM32_ETH_LINK_UP) #if IS_USED(MODULE_STM32_ETH_LINK_UP)
@ -113,7 +114,7 @@ static uint8_t _link_state = LINK_STATE_DOWN;
static void _debug_tx_descriptor_info(unsigned line) static void _debug_tx_descriptor_info(unsigned line)
{ {
if (IS_ACTIVE(ENABLE_DEBUG)) { if (IS_ACTIVE(ENABLE_DEBUG) && IS_ACTIVE(ENABLE_DEBUG_VERBOSE)) {
DEBUG("[stm32_eth:%u] TX descriptors:\n", line); DEBUG("[stm32_eth:%u] TX descriptors:\n", line);
for (unsigned i = 0; i < ETH_TX_DESCRIPTOR_COUNT; i++) { for (unsigned i = 0; i < ETH_TX_DESCRIPTOR_COUNT; i++) {
uint32_t status = tx_desc[i].status; uint32_t status = tx_desc[i].status;
@ -140,7 +141,7 @@ static inline uint32_t _len_from_rx_desc_status(uint32_t status)
static void _debug_rx_descriptor_info(unsigned line) static void _debug_rx_descriptor_info(unsigned line)
{ {
if (IS_ACTIVE(ENABLE_DEBUG)) { if (IS_ACTIVE(ENABLE_DEBUG) && IS_ACTIVE(ENABLE_DEBUG_VERBOSE)) {
DEBUG("[stm32_eth:%u] RX descriptors:\n", line); DEBUG("[stm32_eth:%u] RX descriptors:\n", line);
for (unsigned i = 0; i < ETH_RX_DESCRIPTOR_COUNT; i++) { for (unsigned i = 0; i < ETH_RX_DESCRIPTOR_COUNT; i++) {
uint32_t status = rx_desc[i].status; uint32_t status = rx_desc[i].status;
@ -228,8 +229,7 @@ static void stm32_eth_set_addr(const uint8_t *addr)
ETH->MACA0LR = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0]; ETH->MACA0LR = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
} }
/** Initialization of the DMA descriptors to be used */ static void _init_dma_descriptors(void)
static void _init_buffer(void)
{ {
size_t i; size_t i;
for (i = 0; i < ETH_RX_DESCRIPTOR_COUNT; i++) { for (i = 0; i < ETH_RX_DESCRIPTOR_COUNT; i++) {
@ -254,6 +254,17 @@ static void _init_buffer(void)
ETH->DMATDLAR = (uintptr_t)tx_curr; ETH->DMATDLAR = (uintptr_t)tx_curr;
} }
static void _reset_eth_dma(void)
{
/* disable DMA TX and RX */
ETH->DMAOMR &= ~(ETH_DMAOMR_ST | ETH_DMAOMR_SR);
_init_dma_descriptors();
/* enable DMA TX and RX */
ETH->DMAOMR |= ETH_DMAOMR_ST | ETH_DMAOMR_SR;
}
static int stm32_eth_set(netdev_t *dev, netopt_t opt, static int stm32_eth_set(netdev_t *dev, netopt_t opt,
const void *value, size_t max_len) const void *value, size_t max_len)
{ {
@ -445,8 +456,6 @@ static int stm32_eth_init(netdev_t *netdev)
netdev_eui48_get(netdev, &hwaddr); netdev_eui48_get(netdev, &hwaddr);
stm32_eth_set_addr(hwaddr.uint8); stm32_eth_set_addr(hwaddr.uint8);
_init_buffer();
ETH->DMAIER |= ETH_DMAIER_NISE | ETH_DMAIER_TIE | ETH_DMAIER_RIE; ETH->DMAIER |= ETH_DMAIER_NISE | ETH_DMAIER_TIE | ETH_DMAIER_RIE;
/* enable transmitter and receiver */ /* enable transmitter and receiver */
@ -456,8 +465,7 @@ static int stm32_eth_init(netdev_t *netdev)
/* wait for FIFO flushing to complete */ /* wait for FIFO flushing to complete */
while (ETH->DMAOMR & ETH_DMAOMR_FTF) { } while (ETH->DMAOMR & ETH_DMAOMR_FTF) { }
/* enable DMA TX and RX */ _reset_eth_dma();
ETH->DMAOMR |= ETH_DMAOMR_ST | ETH_DMAOMR_SR;
_setup_phy(); _setup_phy();
@ -476,9 +484,10 @@ static int stm32_eth_send(netdev_t *netdev, const struct iolist *iolist)
/* We cannot send more chunks than allocated descriptors */ /* We cannot send more chunks than allocated descriptors */
assert(iolist_count(iolist) <= ETH_TX_DESCRIPTOR_COUNT); assert(iolist_count(iolist) <= ETH_TX_DESCRIPTOR_COUNT);
edma_desc_t *dma_iter = tx_curr;
for (unsigned i = 0; iolist; iolist = iolist->iol_next, i++) { for (unsigned i = 0; iolist; iolist = iolist->iol_next, i++) {
tx_curr->control = iolist->iol_len; dma_iter->control = iolist->iol_len;
tx_curr->buffer_addr = iolist->iol_base; dma_iter->buffer_addr = iolist->iol_base;
uint32_t status = TX_DESC_STAT_IC | TX_DESC_STAT_TCH | TX_DESC_STAT_CIC uint32_t status = TX_DESC_STAT_IC | TX_DESC_STAT_TCH | TX_DESC_STAT_CIC
| TX_DESC_STAT_OWN; | TX_DESC_STAT_OWN;
if (!i) { if (!i) {
@ -489,34 +498,56 @@ static int stm32_eth_send(netdev_t *netdev, const struct iolist *iolist)
/* last chunk */ /* last chunk */
status |= TX_DESC_STAT_LS; status |= TX_DESC_STAT_LS;
} }
tx_curr->status = status; dma_iter->status = status;
tx_curr = tx_curr->desc_next; dma_iter = dma_iter->desc_next;
} }
/* start TX */ /* start TX */
ETH->DMATPDR = 0; ETH->DMATPDR = 0;
/* await completion */ /* await completion */
DEBUG("[stm32_eth] Started to send %u B via DMA\n", bytes_to_send); if (IS_ACTIVE(ENABLE_DEBUG_VERBOSE)) {
DEBUG("[stm32_eth] Started to send %u B via DMA\n", bytes_to_send);
}
mutex_lock(&stm32_eth_tx_completed); mutex_lock(&stm32_eth_tx_completed);
DEBUG("[stm32_eth] TX completed\n"); if (IS_ACTIVE(ENABLE_DEBUG_VERBOSE)) {
DEBUG("[stm32_eth] TX completed\n");
}
/* Error check */ /* Error check */
unsigned i = 0;
_debug_tx_descriptor_info(__LINE__); _debug_tx_descriptor_info(__LINE__);
int error = 0;
while (1) { while (1) {
uint32_t status = tx_desc[i].status; uint32_t status = tx_curr->status;
/* The Error Summary (ES) bit is set, if any error during TX occurred */ /* The Error Summary (ES) bit is set, if any error during TX occurred */
if (status & TX_DESC_STAT_ES) { if (status & TX_DESC_STAT_ES) {
/* TODO: Report better event to reflect error */ if (status & TX_DESC_STAT_EC) {
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); DEBUG("[stm32_eth] collision in half duplex mode\n");
return -1; error = -EBUSY;
}
else if (status & TX_DESC_STAT_NC) {
DEBUG("[stm32_eth] no carrier detected during TX\n");
error = -ENETDOWN;
}
else {
/* don't detect underflow error here, as we trigger TX only
* after all descriptors have been handed over to the DMA.
* Hence, the DMA should never run out of desciprtors during
* TX. */
DEBUG("[stm32_eth] unhandled error during TX\n");
error = -EIO;
}
_reset_eth_dma();
} }
tx_curr = tx_curr->desc_next;
if (status & TX_DESC_STAT_LS) { if (status & TX_DESC_STAT_LS) {
break; break;
} }
i++;
} }
netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE); netdev->event_callback(netdev, NETDEV_EVENT_TX_COMPLETE);
if (error) {
return error;
}
return (int)bytes_to_send; return (int)bytes_to_send;
} }
@ -533,10 +564,12 @@ static int get_rx_frame_size(void)
} }
if (status & RX_DESC_STAT_DE) { if (status & RX_DESC_STAT_DE) {
DEBUG("[stm32_eth] Overflow during RX\n"); DEBUG("[stm32_eth] Overflow during RX\n");
_reset_eth_dma();
return -EOVERFLOW; return -EOVERFLOW;
} }
if (status & RX_DESC_STAT_ES) { if (status & RX_DESC_STAT_ES) {
DEBUG("[stm32_eth] Error during RX\n"); DEBUG("[stm32_eth] Error during RX\n");
_reset_eth_dma();
return -EIO; return -EIO;
} }
if (status & RX_DESC_STAT_LS) { if (status & RX_DESC_STAT_LS) {