diff --git a/components/esp_eth/src/esp_eth_mac_dm9051.c b/components/esp_eth/src/esp_eth_mac_dm9051.c index 9c71d7c3cf6..4f9a61180a2 100644 --- a/components/esp_eth/src/esp_eth_mac_dm9051.c +++ b/components/esp_eth/src/esp_eth_mac_dm9051.c @@ -30,6 +30,15 @@ static const char *TAG = "dm9051.mac"; #define DM9051_SPI_LOCK_TIMEOUT_MS (50) #define DM9051_PHY_OPERATION_TIMEOUT_US (1000) +#define DM9051_RX_MEM_START_ADDR (3072) +#define DM9051_RX_MEM_MAX_SIZE (16384) +#define DM9051_RX_HDR_SIZE (4) +#define DM9051_ETH_MAC_RX_BUF_SIZE_AUTO (0) + +typedef struct { + uint32_t copy_len; + uint32_t byte_cnt; +}__attribute__((packed)) dm9051_auto_buf_info_t; typedef struct { uint8_t flag; @@ -49,6 +58,7 @@ typedef struct { uint8_t addr[6]; bool packets_remain; bool flow_ctrl_enabled; + uint8_t *rx_buffer; } emac_dm9051_t; static inline bool dm9051_lock(emac_dm9051_t *emac) @@ -383,44 +393,6 @@ IRAM_ATTR static void dm9051_isr_handler(void *arg) } } -static void emac_dm9051_task(void *arg) -{ - emac_dm9051_t *emac = (emac_dm9051_t *)arg; - uint8_t status = 0; - uint8_t *buffer = NULL; - uint32_t length = 0; - while (1) { - // check if the task receives any notification - if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ... - gpio_get_level(emac->int_gpio_num) == 0) { // ...and no interrupt asserted - continue; // -> just continue to check again - } - /* clear interrupt status */ - dm9051_register_read(emac, DM9051_ISR, &status); - dm9051_register_write(emac, DM9051_ISR, status); - /* packet received */ - if (status & ISR_PR) { - do { - length = ETH_MAX_PACKET_SIZE; - buffer = heap_caps_malloc(length, MALLOC_CAP_DMA); - if (!buffer) { - ESP_LOGE(TAG, "no mem for receive buffer"); - } else if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) { - /* pass the buffer to stack (e.g. TCP/IP layer) */ - if (length) { - emac->eth->stack_input(emac->eth, buffer, length); - } else { - free(buffer); - } - } else { - free(buffer); - } - } while (emac->packets_remain); - } - } - vTaskDelete(NULL); -} - static esp_err_t emac_dm9051_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth) { esp_err_t ret = ESP_OK; @@ -613,6 +585,9 @@ static esp_err_t emac_dm9051_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t /* Check if last transmit complete */ uint8_t tcr = 0; + ESP_GOTO_ON_FALSE(length <= ETH_MAX_PACKET_SIZE, ESP_ERR_INVALID_ARG, err, + TAG, "frame size is too big (actual %u, maximum %u)", length, ETH_MAX_PACKET_SIZE); + int64_t wait_time = esp_timer_get_time(); do { ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_TCR, &tcr), err, TAG, "read TCR failed"); @@ -635,47 +610,137 @@ static esp_err_t emac_dm9051_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t return ret; } -static esp_err_t emac_dm9051_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length) +static esp_err_t dm9051_skip_recv_frame(emac_dm9051_t *emac, uint16_t rx_length) +{ + esp_err_t ret = ESP_OK; + uint8_t mrrh, mrrl; + ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRRH, &mrrh), err, TAG, "read MDRAH failed"); + ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRRL, &mrrl), err, TAG, "read MDRAL failed"); + uint16_t addr = mrrh << 8 | mrrl; + /* include 4B for header */ + addr += rx_length + DM9051_RX_HDR_SIZE; + if (addr > DM9051_RX_MEM_MAX_SIZE) { + addr = addr - DM9051_RX_MEM_MAX_SIZE + DM9051_RX_MEM_START_ADDR; + } + ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MRRH, addr >> 8), err, TAG, "write MDRAH failed"); + ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MRRL, addr & 0xFF), err, TAG, "write MDRAL failed"); +err: + return ret; +} + +static esp_err_t dm9051_get_recv_byte_count(emac_dm9051_t *emac, uint16_t *size) { esp_err_t ret = ESP_OK; - emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); uint8_t rxbyte = 0; - uint16_t rx_len = 0; __attribute__((aligned(4))) dm9051_rx_header_t header; // SPI driver needs the rx buffer 4 byte align - emac->packets_remain = false; + + *size = 0; /* dummy read, get the most updated data */ ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte), err, TAG, "read MRCMDX failed"); ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte), err, TAG, "read MRCMDX failed"); /* rxbyte must be 0xFF, 0 or 1 */ if (rxbyte > 1) { - ESP_GOTO_ON_ERROR(mac->stop(mac), err, TAG, "stop dm9051 failed"); + ESP_GOTO_ON_ERROR(emac->parent.stop(&emac->parent), err, TAG, "stop dm9051 failed"); /* reset rx fifo pointer */ ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MPTRCR, MPTRCR_RST_RX), err, TAG, "write MPTRCR failed"); esp_rom_delay_us(10); - ESP_GOTO_ON_ERROR(mac->start(mac), err, TAG, "start dm9051 failed"); + ESP_GOTO_ON_ERROR(emac->parent.start(&emac->parent), err, TAG, "start dm9051 failed"); ESP_GOTO_ON_FALSE(false, ESP_FAIL, err, TAG, "reset rx fifo pointer"); } else if (rxbyte) { ESP_GOTO_ON_ERROR(dm9051_memory_peek(emac, (uint8_t *)&header, sizeof(header)), err, TAG, "peek rx header failed"); - rx_len = header.length_low + (header.length_high << 8); - /* check if the buffer can hold all the incoming data */ - if (*length < rx_len - 4) { - ESP_LOGE(TAG, "buffer size too small, needs %d", rx_len - 4); - /* tell upper layer the size we need */ - *length = rx_len - 4; - ret = ESP_ERR_INVALID_SIZE; + uint16_t rx_len = header.length_low + (header.length_high << 8); + if (header.status & 0xBF) { + /* erroneous frames should not be forwarded by DM9051, however, if it happens, just skip it */ + dm9051_skip_recv_frame(emac, rx_len); + ESP_GOTO_ON_FALSE(false, ESP_FAIL, err, TAG, "receive status error: %xH", header.status); + } + *size = rx_len; + } +err: + return ret; +} + +static esp_err_t dm9051_flush_recv_frame(emac_dm9051_t *emac) +{ + esp_err_t ret = ESP_OK; + uint16_t rx_len; + ESP_GOTO_ON_ERROR(dm9051_get_recv_byte_count(emac, &rx_len), err, TAG, "get rx frame length failed"); + ESP_GOTO_ON_ERROR(dm9051_skip_recv_frame(emac, rx_len), err, TAG, "skipping frame in RX memory failed"); +err: + return ret; +} + +static esp_err_t dm9051_alloc_recv_buf(emac_dm9051_t *emac, uint8_t **buf, uint32_t *length) +{ + esp_err_t ret = ESP_OK; + uint16_t rx_len = 0; + uint16_t byte_count; + *buf = NULL; + + ESP_GOTO_ON_ERROR(dm9051_get_recv_byte_count(emac, &byte_count), err, TAG, "get rx frame length failed"); + // silently return when no frame is waiting + if (!byte_count) { + goto err; + } + // do not include 4 bytes CRC at the end + rx_len = byte_count - ETH_CRC_LEN; + // frames larger than expected will be truncated + uint16_t copy_len = rx_len > *length ? *length : rx_len; + // runt frames are not forwarded, but check the length anyway since it could be corrupted at SPI bus + ESP_GOTO_ON_FALSE(copy_len >= ETH_MIN_PACKET_SIZE - ETH_CRC_LEN, ESP_ERR_INVALID_SIZE, err, TAG, "invalid frame length %u", copy_len); + *buf = malloc(copy_len); + if (*buf != NULL) { + dm9051_auto_buf_info_t *buff_info = (dm9051_auto_buf_info_t *)*buf; + buff_info->copy_len = copy_len; + buff_info->byte_cnt = byte_count; + } else { + ret = ESP_ERR_NO_MEM; + goto err; + } +err: + *length = rx_len; + return ret; +} + +static esp_err_t emac_dm9051_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length) +{ + esp_err_t ret = ESP_OK; + emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); + uint16_t rx_len = 0; + uint8_t rxbyte; + uint16_t copy_len = 0; + uint16_t byte_count = 0; + emac->packets_remain = false; + + if (*length != DM9051_ETH_MAC_RX_BUF_SIZE_AUTO) { + ESP_GOTO_ON_ERROR(dm9051_get_recv_byte_count(emac, &byte_count), err, TAG, "get rx frame length failed"); + /* silently return when no frame is waiting */ + if (!byte_count) { goto err; } - ESP_GOTO_ON_ERROR(dm9051_memory_read(emac, (uint8_t *)&header, sizeof(header)), err, TAG, "read rx header failed"); - ESP_GOTO_ON_ERROR(dm9051_memory_read(emac, buf, rx_len), err, TAG, "read rx data failed"); - ESP_GOTO_ON_FALSE(!(header.status & 0xBF), ESP_FAIL, err, TAG, "receive status error: %xH", header.status); - *length = rx_len - 4; // substract the CRC length (4Bytes) - /* dummy read, get the most updated data */ - ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte), err, TAG, "read MRCMDX failed"); - ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte), err, TAG, "read MRCMDX failed"); - emac->packets_remain = rxbyte > 0; + /* do not include 4 bytes CRC at the end */ + rx_len = byte_count - ETH_CRC_LEN; + /* frames larger than expected will be truncated */ + copy_len = rx_len > *length ? *length : rx_len; + } else { + dm9051_auto_buf_info_t *buff_info = (dm9051_auto_buf_info_t *)buf; + copy_len = buff_info->copy_len; + byte_count = buff_info->byte_cnt; } + + byte_count += DM9051_RX_HDR_SIZE; + ESP_GOTO_ON_ERROR(dm9051_memory_read(emac, emac->rx_buffer, byte_count), err, TAG, "read rx data failed"); + memcpy(buf, emac->rx_buffer + DM9051_RX_HDR_SIZE, copy_len); + *length = copy_len; + + /* dummy read, get the most updated data */ + ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte), err, TAG, "read MRCMDX failed"); + /* check for remaing packets */ + ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte), err, TAG, "read MRCMDX failed"); + emac->packets_remain = rxbyte > 0; return ESP_OK; err: + *length = 0; return ret; } @@ -720,11 +785,68 @@ static esp_err_t emac_dm9051_deinit(esp_eth_mac_t *mac) return ESP_OK; } +static void emac_dm9051_task(void *arg) +{ + emac_dm9051_t *emac = (emac_dm9051_t *)arg; + uint8_t status = 0; + esp_err_t ret; + while (1) { + // check if the task receives any notification + if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ... + gpio_get_level(emac->int_gpio_num) == 0) { // ...and no interrupt asserted + continue; // -> just continue to check again + } + /* clear interrupt status */ + dm9051_register_read(emac, DM9051_ISR, &status); + dm9051_register_write(emac, DM9051_ISR, status); + /* packet received */ + if (status & ISR_PR) { + do { + /* define max expected frame len */ + uint32_t frame_len = ETH_MAX_PACKET_SIZE; + uint8_t *buffer; + if ((ret = dm9051_alloc_recv_buf(emac, &buffer, &frame_len)) == ESP_OK) { + if (buffer != NULL) { + /* we have memory to receive the frame of maximal size previously defined */ + uint32_t buf_len = DM9051_ETH_MAC_RX_BUF_SIZE_AUTO; + if (emac->parent.receive(&emac->parent, buffer, &buf_len) == ESP_OK) { + if (buf_len == 0) { + dm9051_flush_recv_frame(emac); + free(buffer); + } else if (frame_len > buf_len) { + ESP_LOGE(TAG, "received frame was truncated"); + free(buffer); + } else { + ESP_LOGD(TAG, "receive len=%u", buf_len); + /* pass the buffer to stack (e.g. TCP/IP layer) */ + emac->eth->stack_input(emac->eth, buffer, buf_len); + } + } else { + ESP_LOGE(TAG, "frame read from module failed"); + dm9051_flush_recv_frame(emac); + free(buffer); + } + } else if (frame_len) { + ESP_LOGE(TAG, "invalid combination of frame_len(%u) and buffer pointer(%p)", frame_len, buffer); + } + } else if (ret == ESP_ERR_NO_MEM) { + ESP_LOGE(TAG, "no mem for receive buffer"); + dm9051_flush_recv_frame(emac); + } else { + ESP_LOGE(TAG, "unexpected error 0x%x", ret); + } + } while (emac->packets_remain); + } + } + vTaskDelete(NULL); +} + static esp_err_t emac_dm9051_del(esp_eth_mac_t *mac) { emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); vTaskDelete(emac->rx_task_hdl); vSemaphoreDelete(emac->spi_lock); + heap_caps_free(emac->rx_buffer); free(emac); return ESP_OK; } @@ -772,6 +894,10 @@ esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config, BaseType_t xReturned = xTaskCreatePinnedToCore(emac_dm9051_task, "dm9051_tsk", mac_config->rx_task_stack_size, emac, mac_config->rx_task_prio, &emac->rx_task_hdl, core_num); ESP_GOTO_ON_FALSE(xReturned == pdPASS, NULL, err, TAG, "create dm9051 task failed"); + + emac->rx_buffer = heap_caps_malloc(ETH_MAX_PACKET_SIZE + DM9051_RX_HDR_SIZE, MALLOC_CAP_DMA); + ESP_GOTO_ON_FALSE(emac->rx_buffer, NULL, err, TAG, "RX buffer allocation failed"); + return &(emac->parent); err: @@ -782,6 +908,7 @@ esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config, if (emac->spi_lock) { vSemaphoreDelete(emac->spi_lock); } + heap_caps_free(emac->rx_buffer); free(emac); } return ret; diff --git a/components/esp_eth/src/esp_eth_mac_esp.c b/components/esp_eth/src/esp_eth_mac_esp.c index 03248ff8a84..9d430933138 100644 --- a/components/esp_eth/src/esp_eth_mac_esp.c +++ b/components/esp_eth/src/esp_eth_mac_esp.c @@ -252,24 +252,33 @@ static void emac_esp32_rx_task(void *arg) { emac_esp32_t *emac = (emac_esp32_t *)arg; uint8_t *buffer = NULL; - uint32_t length = 0; while (1) { // block indefinitely until got notification from underlay event ulTaskNotifyTake(pdTRUE, portMAX_DELAY); do { - length = ETH_MAX_PACKET_SIZE; - buffer = malloc(length); - if (!buffer) { - ESP_LOGE(TAG, "no mem for receive buffer"); - } else if (emac_esp32_receive(&emac->parent, buffer, &length) == ESP_OK) { - /* pass the buffer to stack (e.g. TCP/IP layer) */ - if (length) { - emac->eth->stack_input(emac->eth, buffer, length); - } else { + /* set max expected frame len */ + uint32_t frame_len = ETH_MAX_PACKET_SIZE; + buffer = emac_hal_alloc_recv_buf(&emac->hal, &frame_len); + /* we have memory to receive the frame of maximal size previously defined */ + if (buffer != NULL) { + uint32_t recv_len = emac_hal_receive_frame(&emac->hal, buffer, EMAC_HAL_BUF_SIZE_AUTO, &emac->frames_remain, &emac->free_rx_descriptor); + if (recv_len == 0) { + ESP_LOGE(TAG, "frame copy error"); + free(buffer); + /* ensure that interface to EMAC does not get stuck with unprocessed frames */ + emac_hal_flush_recv_frame(&emac->hal, &emac->frames_remain, &emac->free_rx_descriptor); + } else if (frame_len > recv_len) { + ESP_LOGE(TAG, "received frame was truncated"); free(buffer); + } else { + ESP_LOGD(TAG, "receive len= %d", recv_len); + emac->eth->stack_input(emac->eth, buffer, recv_len); } - } else { - free(buffer); + /* if allocation failed and there is a waiting frame */ + } else if (frame_len) { + ESP_LOGE(TAG, "no mem for receive buffer"); + /* ensure that interface to EMAC does not get stuck with unprocessed frames */ + emac_hal_flush_recv_frame(&emac->hal, &emac->frames_remain, &emac->free_rx_descriptor); } #if CONFIG_ETH_SOFT_FLOW_CONTROL // we need to do extra checking of remained frames in case there are no unhandled frames left, but pause frame is still undergoing diff --git a/components/esp_eth/src/esp_eth_mac_ksz8851snl.c b/components/esp_eth/src/esp_eth_mac_ksz8851snl.c index a7ebc7861f8..ff83de2748b 100644 --- a/components/esp_eth/src/esp_eth_mac_ksz8851snl.c +++ b/components/esp_eth/src/esp_eth_mac_ksz8851snl.c @@ -19,6 +19,8 @@ #include "ksz8851.h" +#define KSZ8851_ETH_MAC_RX_BUF_SIZE_AUTO (0) + typedef struct { esp_eth_mac_t parent; esp_eth_mediator_t *eth; @@ -31,6 +33,11 @@ typedef struct { uint8_t *tx_buffer; } emac_ksz8851snl_t; +typedef struct { + uint32_t copy_len; + uint32_t byte_cnt; +}__attribute__((packed)) ksz8851_auto_buf_info_t; + typedef enum { KSZ8851_SPI_COMMAND_READ_REG = 0x0U, KSZ8851_SPI_COMMAND_WRITE_REG = 0x1U, @@ -43,7 +50,6 @@ typedef enum { KSZ8851_QMU_PACKET_PADDING = 16U, } ksz8851_qmu_packet_size_t; - static const char *TAG = "ksz8851snl-mac"; static const unsigned KSZ8851_SPI_COMMAND_BITS = 2U; @@ -222,7 +228,7 @@ static esp_err_t init_set_defaults(emac_ksz8851snl_t *emac) RXCR1_RXUDPFCC | RXCR1_RXTCPFCC | RXCR1_RXIPFCC | RXCR1_RXPAFMA | RXCR1_RXFCE | RXCR1_RXBE | RXCR1_RXUE | RXCR1_RXME), err, TAG, "RXCR1 write failed"); ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXCR2, (4 << RXCR2_SRDBL_SHIFT) | RXCR2_IUFFP | RXCR2_RXIUFCEZ | RXCR2_UDPLFE | RXCR2_RXICMPFCC), err, TAG, "RXCR2 write failed"); - ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXQCR, RXQCR_RXIPHTOE | RXQCR_RXFCTE | RXQCR_ADRFE), err, TAG, "RXQCR write failed"); + ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXQCR, RXQCR_RXFCTE | RXQCR_ADRFE), err, TAG, "RXQCR write failed"); ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_P1CR, P1CR_FORCE_DUPLEX), err, TAG, "P1CR write failed"); ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_P1CR, P1CR_RESTART_AN), err, TAG, "P1CR write failed"); ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_ISR, ISR_ALL), err, TAG, "ISR write failed"); @@ -310,7 +316,8 @@ static esp_err_t emac_ksz8851snl_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint return ESP_ERR_TIMEOUT; } - ESP_GOTO_ON_FALSE(length <= KSZ8851_QMU_PACKET_LENGTH, ESP_ERR_INVALID_ARG, err, TAG, "packet is too big"); + ESP_GOTO_ON_FALSE(length <= KSZ8851_QMU_PACKET_LENGTH, ESP_ERR_INVALID_ARG, err, + TAG, "frame size is too big (actual %u, maximum %u)", length, ETH_MAX_PACKET_SIZE); // NOTE(v.chistyakov): 4 bytes header + length aligned to 4 bytes unsigned transmit_length = 4U + ((length + 3U) & ~0x3U); @@ -351,61 +358,121 @@ static esp_err_t emac_ksz8851snl_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint return ret; } -static esp_err_t emac_ksz8851_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length) +static esp_err_t emac_ksz8851_get_recv_byte_count(emac_ksz8851snl_t *emac, uint16_t *size) { - esp_err_t ret = ESP_OK; - emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent); - if (!ksz8851_mutex_lock(emac)) { - return ESP_ERR_TIMEOUT; - } - - ESP_GOTO_ON_FALSE(buf, ESP_ERR_INVALID_ARG, err, TAG, "receive buffer can not be null"); - ESP_GOTO_ON_FALSE(length, ESP_ERR_INVALID_ARG, err, TAG, "receive buffer length can not be null"); - ESP_GOTO_ON_FALSE(*length > 0U, ESP_ERR_INVALID_ARG, err, TAG, "receive buffer length must be greater than zero"); - + esp_err_t ret = ESP_OK; + *size = 0; uint16_t header_status; ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_RXFHSR, &header_status), err, TAG, "RXFHSR read failed"); - uint16_t byte_count; ESP_GOTO_ON_ERROR(ksz8851_read_reg(emac, KSZ8851_RXFHBCR, &byte_count), err, TAG, "RXFHBCR read failed"); - byte_count &= RXFHBCR_RXBC_MASK; - - // NOTE(v.chistyakov): do not include 2 bytes padding at the beginning and 4 bytes CRC at the end - const unsigned frame_size = byte_count - 6U; - ESP_GOTO_ON_FALSE(frame_size <= *length, ESP_FAIL, err, TAG, "frame size is greater than length"); - if (header_status & RXFHSR_RXFV) { - // NOTE(v.chistyakov): 4 dummy + 4 header + alignment - const unsigned receive_size = 8U + ((byte_count + 3U) & ~0x3U); - spi_transaction_ext_t trans = { - .base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY, - .base.cmd = KSZ8851_SPI_COMMAND_READ_FIFO, - .base.length = receive_size * 8U, // NOTE(v.chistyakov): bits - .base.rx_buffer = emac->rx_buffer, - .command_bits = 2U, - .address_bits = 6U, - }; - - ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_RXFDPR, RXFDPR_RXFP_MASK), err, TAG, "RXFDPR write failed"); - ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXQCR, RXQCR_SDA), err, TAG, "RXQCR write failed"); - if (spi_device_polling_transmit(emac->spi_hdl, &trans.base) != ESP_OK) { - ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); - ret = ESP_FAIL; - } - ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_RXQCR, RXQCR_SDA), err, TAG, "RXQCR write failed"); - - // NOTE(v.chistyakov): skip 4 dummy, 4 header, 2 padding - memcpy(buf, emac->rx_buffer + 10U, frame_size); - *length = frame_size; - ESP_LOGV(TAG, "received frame of size %u", frame_size); + *size = byte_count & RXFHBCR_RXBC_MASK; } else if (header_status & (RXFHSR_RXCE | RXFHSR_RXRF | RXFHSR_RXFTL | RXFHSR_RXMR | RXFHSR_RXUDPFCS | RXFHSR_RXTCPFCS | RXFHSR_RXIPFCS | RXFHSR_RXICMPFCS)) { // NOTE(v.chistyakov): RRXEF is a self-clearing bit ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXQCR, RXQCR_RRXEF), err, TAG, "RXQCR write failed"); - *length = 0U; } err: + return ret; +} + +static esp_err_t emac_ksz8851_alloc_recv_buf(emac_ksz8851snl_t *emac, uint8_t **buf, uint32_t *length) +{ + esp_err_t ret = ESP_OK; + uint16_t rx_len = 0; + uint16_t byte_count; + *buf = NULL; + + ESP_GOTO_ON_ERROR(emac_ksz8851_get_recv_byte_count(emac, &byte_count), err, TAG, "get receive frame byte count failed"); + // silently return when no frame is waiting + if (!byte_count) { + goto err; + } + // do not include 4 bytes CRC at the end + rx_len = byte_count - ETH_CRC_LEN; + // frames larger than expected will be truncated + uint16_t copy_len = rx_len > *length ? *length : rx_len; + // runt frames are not forwarded, but check the length anyway since it could be corrupted at SPI bus + ESP_GOTO_ON_FALSE(copy_len >= ETH_MIN_PACKET_SIZE - ETH_CRC_LEN, ESP_ERR_INVALID_SIZE, err, TAG, "invalid frame length %u", copy_len); + *buf = malloc(copy_len); + if (*buf != NULL) { + ksz8851_auto_buf_info_t *buff_info = (ksz8851_auto_buf_info_t *)*buf; + buff_info->copy_len = copy_len; + buff_info->byte_cnt = byte_count; + } else { + ret = ESP_ERR_NO_MEM; + goto err; + } +err: + *length = rx_len; + return ret; +} + +static esp_err_t emac_ksz8851_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length) +{ + esp_err_t ret = ESP_OK; + emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent); + uint16_t copy_len = 0; + uint16_t rx_len; + uint16_t byte_count; + + ESP_GOTO_ON_FALSE(buf, ESP_ERR_INVALID_ARG, err, TAG, "receive buffer can not be null"); + ESP_GOTO_ON_FALSE(length, ESP_ERR_INVALID_ARG, err, TAG, "receive buffer length can not be null"); + if (*length != KSZ8851_ETH_MAC_RX_BUF_SIZE_AUTO) { + ESP_GOTO_ON_ERROR(emac_ksz8851_get_recv_byte_count(emac, &byte_count), err, TAG, "get receive frame byte count failed"); + // silently return when no frame is waiting + if (!byte_count) { + goto err; + } + // do not include 4 bytes CRC at the end + rx_len = byte_count - ETH_CRC_LEN; + // frames larger than expected will be truncated + copy_len = rx_len > *length ? *length : rx_len; + } else { + ksz8851_auto_buf_info_t *buff_info = (ksz8851_auto_buf_info_t *)buf; + copy_len = buff_info->copy_len; + byte_count = buff_info->byte_cnt; + } + + // NOTE(v.chistyakov): 4 dummy + 4 header + alignment + const unsigned receive_size = 8U + ((byte_count + 3U) & ~0x3U); + spi_transaction_ext_t trans = { + .base.flags = SPI_TRANS_VARIABLE_CMD | SPI_TRANS_VARIABLE_ADDR | SPI_TRANS_VARIABLE_DUMMY, + .base.cmd = KSZ8851_SPI_COMMAND_READ_FIFO, + .base.length = receive_size * 8U, // NOTE(v.chistyakov): bits + .base.rx_buffer = emac->rx_buffer, + .command_bits = 2U, + .address_bits = 6U, + }; + if (!ksz8851_mutex_lock(emac)) { + return ESP_ERR_TIMEOUT; + } + ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_RXFDPR, RXFDPR_RXFP_MASK), err, TAG, "RXFDPR write failed"); + ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXQCR, RXQCR_SDA), err, TAG, "RXQCR write failed"); + if (spi_device_polling_transmit(emac->spi_hdl, &trans.base) != ESP_OK) { + ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__); + ret = ESP_FAIL; + } + ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_RXQCR, RXQCR_SDA), err, TAG, "RXQCR write failed"); ksz8851_mutex_unlock(emac); + // NOTE(v.chistyakov): skip 4 dummy, 4 header + memcpy(buf, emac->rx_buffer + 8U, copy_len); + *length = copy_len; + return ret; +err: + *length = 0U; + return ret; +} + +static esp_err_t emac_ksz8851_flush_recv_queue(emac_ksz8851snl_t *emac) +{ + esp_err_t ret = ESP_OK; + ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_RXCR1, RXCR1_RXE), err, TAG, "RXCR1 write failed"); + ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXCR1, RXCR1_FRXQ), err, TAG, "RXCR1 write failed"); + ESP_GOTO_ON_ERROR(ksz8851_clear_bits(emac, KSZ8851_RXCR1, RXCR1_FRXQ), err, TAG, "RXCR1 write failed"); + ESP_GOTO_ON_ERROR(ksz8851_set_bits(emac, KSZ8851_RXCR1, RXCR1_RXE), err, TAG, "RXCR1 write failed"); +err: return ret; } @@ -567,20 +634,10 @@ static esp_err_t emac_ksz8851_set_peer_pause_ability(esp_eth_mac_t *mac, uint32_ return ESP_ERR_NOT_SUPPORTED; } -static esp_err_t emac_ksz8851_del(esp_eth_mac_t *mac) -{ - emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent); - vTaskDelete(emac->rx_task_hdl); - vSemaphoreDelete(emac->spi_lock); - heap_caps_free(emac->rx_buffer); - heap_caps_free(emac->tx_buffer); - free(emac); - return ESP_OK; -} - static void emac_ksz8851snl_task(void *arg) { emac_ksz8851snl_t *emac = (emac_ksz8851snl_t *)arg; + esp_err_t ret; while (1) { ulTaskNotifyTake(pdTRUE, portMAX_DELAY); @@ -631,21 +688,38 @@ static void emac_ksz8851snl_task(void *arg) frame_count = (frame_count & RXFCTR_RXFC_MASK) >> RXFCTR_RXFC_SHIFT; while (frame_count--) { - uint32_t length = ETH_MAX_PACKET_SIZE; - uint8_t *packet = malloc(ETH_MAX_PACKET_SIZE); - if (!packet) { - continue; - } - - if (emac->parent.receive(&emac->parent, packet, &length) == ESP_OK && length) { - emac->eth->stack_input(emac->eth, packet, length); - // NOTE(v.chistyakov): the packet is freed in the upper layers + /* define max expected frame len */ + uint32_t frame_len = ETH_MAX_PACKET_SIZE; + uint8_t *buffer; + if ((ret = emac_ksz8851_alloc_recv_buf(emac, &buffer, &frame_len)) == ESP_OK) { + if (buffer != NULL) { + /* we have memory to receive the frame of maximal size previously defined */ + uint32_t buf_len = KSZ8851_ETH_MAC_RX_BUF_SIZE_AUTO; + if (emac->parent.receive(&emac->parent, buffer, &buf_len) == ESP_OK) { + if (buf_len == 0) { + emac_ksz8851_flush_recv_queue(emac); + free(buffer); + } else if (frame_len > buf_len) { + ESP_LOGE(TAG, "received frame was truncated"); + free(buffer); + } else { + ESP_LOGD(TAG, "receive len=%u", buf_len); + /* pass the buffer to stack (e.g. TCP/IP layer) */ + emac->eth->stack_input(emac->eth, buffer, buf_len); + } + } else { + ESP_LOGE(TAG, "frame read from module failed"); + emac_ksz8851_flush_recv_queue(emac); + free(buffer); + } + } else if (frame_len) { + ESP_LOGE(TAG, "invalid combination of frame_len(%u) and buffer pointer(%p)", frame_len, buffer); + } + } else if (ret == ESP_ERR_NO_MEM) { + ESP_LOGE(TAG, "no mem for receive buffer"); + emac_ksz8851_flush_recv_queue(emac); } else { - free(packet); - ksz8851_clear_bits(emac, KSZ8851_RXCR1, RXCR1_RXE); - ksz8851_set_bits(emac, KSZ8851_RXCR1, RXCR1_FRXQ); - ksz8851_clear_bits(emac, KSZ8851_RXCR1, RXCR1_FRXQ); - ksz8851_set_bits(emac, KSZ8851_RXCR1, RXCR1_RXE); + ESP_LOGE(TAG, "unexpected error 0x%x", ret); } } ksz8851_write_reg(emac, KSZ8851_IER, ier); @@ -654,6 +728,18 @@ static void emac_ksz8851snl_task(void *arg) vTaskDelete(NULL); } +static esp_err_t emac_ksz8851_del(esp_eth_mac_t *mac) +{ + emac_ksz8851snl_t *emac = __containerof(mac, emac_ksz8851snl_t, parent); + vTaskDelete(emac->rx_task_hdl); + spi_bus_remove_device(emac->spi_hdl); + vSemaphoreDelete(emac->spi_lock); + heap_caps_free(emac->rx_buffer); + heap_caps_free(emac->tx_buffer); + free(emac); + return ESP_OK; +} + esp_eth_mac_t *esp_eth_mac_new_ksz8851snl(const eth_ksz8851snl_config_t *ksz8851snl_config, const eth_mac_config_t *mac_config) { diff --git a/components/esp_eth/src/esp_eth_mac_w5500.c b/components/esp_eth/src/esp_eth_mac_w5500.c index bb356529881..212590df0c6 100644 --- a/components/esp_eth/src/esp_eth_mac_w5500.c +++ b/components/esp_eth/src/esp_eth_mac_w5500.c @@ -36,6 +36,14 @@ static const char *TAG = "w5500.mac"; #define W5500_SPI_LOCK_TIMEOUT_MS (50) #define W5500_TX_MEM_SIZE (0x4000) #define W5500_RX_MEM_SIZE (0x4000) +#define W5500_ETH_MAC_RX_BUF_SIZE_AUTO (0) + +typedef struct { + uint32_t offset; + uint32_t copy_len; + uint32_t rx_len; + uint32_t remain; +}__attribute__((packed)) emac_w5500_auto_buf_info_t; typedef struct { esp_eth_mac_t parent; @@ -47,6 +55,7 @@ typedef struct { int int_gpio_num; uint8_t addr[6]; bool packets_remain; + uint8_t *rx_buffer; } emac_w5500_t; static inline bool w5500_lock(emac_w5500_t *emac) @@ -299,59 +308,6 @@ static esp_err_t emac_w5500_stop(esp_eth_mac_t *mac) return ret; } -IRAM_ATTR static void w5500_isr_handler(void *arg) -{ - emac_w5500_t *emac = (emac_w5500_t *)arg; - BaseType_t high_task_wakeup = pdFALSE; - /* notify w5500 task */ - vTaskNotifyGiveFromISR(emac->rx_task_hdl, &high_task_wakeup); - if (high_task_wakeup != pdFALSE) { - portYIELD_FROM_ISR(); - } -} - -static void emac_w5500_task(void *arg) -{ - emac_w5500_t *emac = (emac_w5500_t *)arg; - uint8_t status = 0; - uint8_t *buffer = NULL; - uint32_t length = 0; - while (1) { - // check if the task receives any notification - if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ... - gpio_get_level(emac->int_gpio_num) != 0) { // ...and no interrupt asserted - continue; // -> just continue to check again - } - - /* read interrupt status */ - w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status)); - /* packet received */ - if (status & W5500_SIR_RECV) { - status = W5500_SIR_RECV; - // clear interrupt status - w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status)); - do { - length = ETH_MAX_PACKET_SIZE; - buffer = heap_caps_malloc(length, MALLOC_CAP_DMA); - if (!buffer) { - ESP_LOGE(TAG, "no mem for receive buffer"); - break; - } else if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) { - /* pass the buffer to stack (e.g. TCP/IP layer) */ - if (length) { - emac->eth->stack_input(emac->eth, buffer, length); - } else { - free(buffer); - } - } else { - free(buffer); - } - } while (emac->packets_remain); - } - } - vTaskDelete(NULL); -} - static esp_err_t emac_w5500_set_mediator(esp_eth_mac_t *mac, esp_eth_mediator_t *eth) { esp_err_t ret = ESP_OK; @@ -517,6 +473,8 @@ static esp_err_t emac_w5500_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent); uint16_t offset = 0; + ESP_GOTO_ON_FALSE(length <= ETH_MAX_PACKET_SIZE, ESP_ERR_INVALID_ARG, err, + TAG, "frame size is too big (actual %u, maximum %u)", length, ETH_MAX_PACKET_SIZE); // check if there're free memory to store this packet uint16_t free_size = 0; ESP_GOTO_ON_ERROR(w5500_get_tx_free_size(emac, &free_size), err, TAG, "get free size failed"); @@ -550,12 +508,103 @@ static esp_err_t emac_w5500_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t return ret; } +static esp_err_t emac_w5500_alloc_recv_buf(emac_w5500_t *emac, uint8_t **buf, uint32_t *length) +{ + esp_err_t ret = ESP_OK; + uint16_t offset = 0; + uint16_t rx_len = 0; + uint32_t copy_len = 0; + uint16_t remain_bytes = 0; + *buf = NULL; + + w5500_get_rx_received_size(emac, &remain_bytes); + if (remain_bytes) { + // get current read pointer + ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset)), err, TAG, "read RX RD failed"); + offset = __builtin_bswap16(offset); + // read head + ESP_GOTO_ON_ERROR(w5500_read_buffer(emac, &rx_len, sizeof(rx_len), offset), err, TAG, "read frame header failed"); + rx_len = __builtin_bswap16(rx_len) - 2; // data size includes 2 bytes of header + // frames larger than expected will be truncated + copy_len = rx_len > *length ? *length : rx_len; + // runt frames are not forwarded by W5500 (tested on target), but check the length anyway since it could be corrupted at SPI bus + ESP_GOTO_ON_FALSE(copy_len >= ETH_MIN_PACKET_SIZE - ETH_CRC_LEN, ESP_ERR_INVALID_SIZE, err, TAG, "invalid frame length %u", copy_len); + *buf = malloc(copy_len); + if (*buf != NULL) { + emac_w5500_auto_buf_info_t *buff_info = (emac_w5500_auto_buf_info_t *)*buf; + buff_info->offset = offset; + buff_info->copy_len = copy_len; + buff_info->rx_len = rx_len; + buff_info->remain = remain_bytes; + } else { + ret = ESP_ERR_NO_MEM; + goto err; + } + } +err: + *length = rx_len; + return ret; +} + static esp_err_t emac_w5500_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length) { esp_err_t ret = ESP_OK; emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent); uint16_t offset = 0; uint16_t rx_len = 0; + uint16_t copy_len = 0; + uint16_t remain_bytes = 0; + emac->packets_remain = false; + + if (*length != W5500_ETH_MAC_RX_BUF_SIZE_AUTO) { + w5500_get_rx_received_size(emac, &remain_bytes); + if (remain_bytes) { + // get current read pointer + ESP_GOTO_ON_ERROR(w5500_read(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset)), err, TAG, "read RX RD failed"); + offset = __builtin_bswap16(offset); + // read head first + ESP_GOTO_ON_ERROR(w5500_read_buffer(emac, &rx_len, sizeof(rx_len), offset), err, TAG, "read frame header failed"); + rx_len = __builtin_bswap16(rx_len) - 2; // data size includes 2 bytes of header + // frames larger than expected will be truncated + copy_len = rx_len > *length ? *length : rx_len; + } else { + // silently return when no frame is waiting + goto err; + } + } else { + emac_w5500_auto_buf_info_t *buff_info = (emac_w5500_auto_buf_info_t *)buf; + offset = buff_info->offset; + copy_len = buff_info->copy_len; + rx_len = buff_info->rx_len; + remain_bytes = buff_info->remain; + } + // 2 bytes of header + offset += 2; + // read the payload + ESP_GOTO_ON_ERROR(w5500_read_buffer(emac, emac->rx_buffer, copy_len, offset), err, TAG, "read payload failed, len=%d, offset=%d", rx_len, offset); + memcpy(buf, emac->rx_buffer, copy_len); + offset += rx_len; + // update read pointer + offset = __builtin_bswap16(offset); + ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset)), err, TAG, "write RX RD failed"); + /* issue RECV command */ + ESP_GOTO_ON_ERROR(w5500_send_command(emac, W5500_SCR_RECV, 100), err, TAG, "issue RECV command failed"); + // check if there're more data need to process + remain_bytes -= rx_len + 2; + emac->packets_remain = remain_bytes > 0; + + *length = rx_len; + return ret; +err: + *length = 0; + return ret; +} + +static esp_err_t emac_w5500_flush_recv_frame(emac_w5500_t *emac) +{ + esp_err_t ret = ESP_OK; + uint16_t offset = 0; + uint16_t rx_len = 0; uint16_t remain_bytes = 0; emac->packets_remain = false; @@ -566,26 +615,90 @@ static esp_err_t emac_w5500_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t * offset = __builtin_bswap16(offset); // read head first ESP_GOTO_ON_ERROR(w5500_read_buffer(emac, &rx_len, sizeof(rx_len), offset), err, TAG, "read frame header failed"); - rx_len = __builtin_bswap16(rx_len) - 2; // data size includes 2 bytes of header - offset += 2; - // read the payload - ESP_GOTO_ON_ERROR(w5500_read_buffer(emac, buf, rx_len, offset), err, TAG, "read payload failed, len=%d, offset=%d", rx_len, offset); - offset += rx_len; // update read pointer + rx_len = __builtin_bswap16(rx_len); + offset += rx_len; offset = __builtin_bswap16(offset); ESP_GOTO_ON_ERROR(w5500_write(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset)), err, TAG, "write RX RD failed"); /* issue RECV command */ ESP_GOTO_ON_ERROR(w5500_send_command(emac, W5500_SCR_RECV, 100), err, TAG, "issue RECV command failed"); // check if there're more data need to process - remain_bytes -= rx_len + 2; + remain_bytes -= rx_len; emac->packets_remain = remain_bytes > 0; } - - *length = rx_len; err: return ret; } +IRAM_ATTR static void w5500_isr_handler(void *arg) +{ + emac_w5500_t *emac = (emac_w5500_t *)arg; + BaseType_t high_task_wakeup = pdFALSE; + /* notify w5500 task */ + vTaskNotifyGiveFromISR(emac->rx_task_hdl, &high_task_wakeup); + if (high_task_wakeup != pdFALSE) { + portYIELD_FROM_ISR(); + } +} + +static void emac_w5500_task(void *arg) +{ + emac_w5500_t *emac = (emac_w5500_t *)arg; + uint8_t status = 0; + uint8_t *buffer = NULL; + uint32_t frame_len = 0; + uint32_t buf_len = 0; + esp_err_t ret; + while (1) { + /* check if the task receives any notification */ + if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ... + gpio_get_level(emac->int_gpio_num) != 0) { // ...and no interrupt asserted + continue; // -> just continue to check again + } + /* read interrupt status */ + w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status)); + /* packet received */ + if (status & W5500_SIR_RECV) { + status = W5500_SIR_RECV; + /* clear interrupt status */ + w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status)); + do { + /* define max expected frame len */ + frame_len = ETH_MAX_PACKET_SIZE; + if ((ret = emac_w5500_alloc_recv_buf(emac, &buffer, &frame_len)) == ESP_OK) { + if (buffer != NULL) { + /* we have memory to receive the frame of maximal size previously defined */ + buf_len = W5500_ETH_MAC_RX_BUF_SIZE_AUTO; + if (emac->parent.receive(&emac->parent, buffer, &buf_len) == ESP_OK) { + if (buf_len == 0) { + free(buffer); + } else if (frame_len > buf_len) { + ESP_LOGE(TAG, "received frame was truncated"); + free(buffer); + } else { + ESP_LOGD(TAG, "receive len=%u", buf_len); + /* pass the buffer to stack (e.g. TCP/IP layer) */ + emac->eth->stack_input(emac->eth, buffer, buf_len); + } + } else { + ESP_LOGE(TAG, "frame read from module failed"); + free(buffer); + } + } else if (frame_len) { + ESP_LOGE(TAG, "invalid combination of frame_len(%u) and buffer pointer(%p)", frame_len, buffer); + } + } else if (ret == ESP_ERR_NO_MEM) { + ESP_LOGE(TAG, "no mem for receive buffer"); + emac_w5500_flush_recv_frame(emac); + } else { + ESP_LOGE(TAG, "unexpected error 0x%x", ret); + } + } while (emac->packets_remain); + } + } + vTaskDelete(NULL); +} + static esp_err_t emac_w5500_init(esp_eth_mac_t *mac) { esp_err_t ret = ESP_OK; @@ -628,6 +741,7 @@ static esp_err_t emac_w5500_del(esp_eth_mac_t *mac) emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent); vTaskDelete(emac->rx_task_hdl); vSemaphoreDelete(emac->spi_lock); + heap_caps_free(emac->rx_buffer); free(emac); return ESP_OK; } @@ -674,6 +788,10 @@ esp_eth_mac_t *esp_eth_mac_new_w5500(const eth_w5500_config_t *w5500_config, con BaseType_t xReturned = xTaskCreatePinnedToCore(emac_w5500_task, "w5500_tsk", mac_config->rx_task_stack_size, emac, mac_config->rx_task_prio, &emac->rx_task_hdl, core_num); ESP_GOTO_ON_FALSE(xReturned == pdPASS, NULL, err, TAG, "create w5500 task failed"); + + emac->rx_buffer = heap_caps_malloc(ETH_MAX_PACKET_SIZE, MALLOC_CAP_DMA); + ESP_GOTO_ON_FALSE(emac->rx_buffer, NULL, err, TAG, "RX buffer allocation failed"); + return &(emac->parent); err: @@ -684,6 +802,7 @@ esp_eth_mac_t *esp_eth_mac_new_w5500(const eth_w5500_config_t *w5500_config, con if (emac->spi_lock) { vSemaphoreDelete(emac->spi_lock); } + heap_caps_free(emac->rx_buffer); free(emac); } return ret; diff --git a/components/hal/emac_hal.c b/components/hal/emac_hal.c index 0af3fed37b4..cc727534d67 100644 --- a/components/hal/emac_hal.c +++ b/components/hal/emac_hal.c @@ -12,6 +12,18 @@ #define ETH_CRC_LENGTH (4) +#ifndef NDEBUG +#define EMAC_HAL_BUF_MAGIC_ID 0x1E1C8416 +#endif // NDEBUG + +typedef struct { +#ifndef NDEBUG + uint32_t magic_id; +#endif // NDEBUG + uint32_t copy_len; +}__attribute__((packed)) emac_hal_auto_buf_info_t; + + static esp_err_t emac_hal_flush_trans_fifo(emac_hal_context_t *hal) { emac_ll_flush_trans_fifo_enable(hal->dma_regs, true); @@ -299,7 +311,7 @@ void emac_hal_init_dma_default(emac_hal_context_t *hal) /* Receive Threshold Control */ emac_ll_set_recv_threshold(hal->dma_regs, EMAC_LL_RECEIVE_THRESHOLD_CONTROL_64); /* Allow the DMA to process a second frame of Transmit data even before obtaining the status for the first frame */ - emac_ll_opt_second_frame_enable(hal->dma_regs, true);; + emac_ll_opt_second_frame_enable(hal->dma_regs, true); /* DMABMR Configuration */ /* Enable Mixed Burst */ @@ -395,11 +407,6 @@ esp_err_t emac_hal_stop(emac_hal_context_t *hal) return ESP_OK; } -uint32_t emac_hal_get_tx_desc_owner(emac_hal_context_t *hal) -{ - return hal->tx_desc->TDES0.Own; -} - uint32_t emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t length) { /* Get the number of Tx buffers to use for the frame */ @@ -464,29 +471,139 @@ uint32_t emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t return 0; } +uint8_t *emac_hal_alloc_recv_buf(emac_hal_context_t *hal, uint32_t *size) +{ + eth_dma_rx_descriptor_t *desc_iter = hal->rx_desc; + uint32_t used_descs = 0; + uint32_t ret_len = 0; + uint32_t copy_len = 0; + uint8_t *buf = NULL; + + /* Traverse descriptors owned by CPU */ + while ((desc_iter->RDES0.Own != EMAC_LL_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM)) { + used_descs++; + /* Last segment in frame */ + if (desc_iter->RDES0.LastDescriptor) { + /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */ + ret_len = desc_iter->RDES0.FrameLength - ETH_CRC_LENGTH; + /* packets larger than expected will be truncated */ + copy_len = ret_len > *size ? *size : ret_len; + break; + } + /* point to next descriptor */ + desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr); + } + if (copy_len > 0) { + buf = malloc(copy_len); + if (buf != NULL) { + emac_hal_auto_buf_info_t *buff_info = (emac_hal_auto_buf_info_t *)buf; + /* no need to check allocated buffer min lenght prior writing since we know that EMAC DMA is configured to + not forward erroneous or undersized frames (less than 64B), see emac_hal_init_dma_default */ +#ifndef NDEBUG + buff_info->magic_id = EMAC_HAL_BUF_MAGIC_ID; +#endif // NDEBUG + buff_info->copy_len = copy_len; + } + } + /* indicate actual size of received frame */ + *size = ret_len; + return buf; +} + uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t size, uint32_t *frames_remain, uint32_t *free_desc) { - eth_dma_rx_descriptor_t *desc_iter = NULL; - eth_dma_rx_descriptor_t *first_desc = NULL; + eth_dma_rx_descriptor_t *desc_iter = hal->rx_desc; + eth_dma_rx_descriptor_t *first_desc = hal->rx_desc; uint32_t used_descs = 0; - uint32_t seg_count = 0; uint32_t ret_len = 0; uint32_t copy_len = 0; - uint32_t write_len = 0; uint32_t frame_count = 0; - first_desc = hal->rx_desc; - desc_iter = hal->rx_desc; + if (size != EMAC_HAL_BUF_SIZE_AUTO) { + /* Traverse descriptors owned by CPU */ + while ((desc_iter->RDES0.Own != EMAC_LL_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM) && !frame_count) { + used_descs++; + /* Last segment in frame */ + if (desc_iter->RDES0.LastDescriptor) { + /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */ + ret_len = desc_iter->RDES0.FrameLength - ETH_CRC_LENGTH; + /* packets larger than expected will be truncated */ + copy_len = ret_len > size ? size : ret_len; + /* update unhandled frame count */ + frame_count++; + } + /* First segment in frame */ + if (desc_iter->RDES0.FirstDescriptor) { + first_desc = desc_iter; + } + /* point to next descriptor */ + desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr); + } + } else { + emac_hal_auto_buf_info_t *buff_info = (emac_hal_auto_buf_info_t *)buf; +#ifndef NDEBUG + /* check that buffer was allocated by emac_hal_alloc_recv_buf */ + assert(buff_info->magic_id == EMAC_HAL_BUF_MAGIC_ID); +#endif // NDEBUG + copy_len = buff_info->copy_len; + ret_len = copy_len; + } + + if (copy_len) { + /* check how many frames left to handle */ + while ((desc_iter->RDES0.Own != EMAC_LL_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM)) { + used_descs++; + if (desc_iter->RDES0.LastDescriptor) { + frame_count++; + } + /* point to next descriptor */ + desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr); + } + desc_iter = first_desc; + while(copy_len > CONFIG_ETH_DMA_BUFFER_SIZE) { + used_descs--; + memcpy(buf, (void *)(desc_iter->Buffer1Addr), CONFIG_ETH_DMA_BUFFER_SIZE); + buf += CONFIG_ETH_DMA_BUFFER_SIZE; + copy_len -= CONFIG_ETH_DMA_BUFFER_SIZE; + /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA; + desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr); + } + memcpy(buf, (void *)(desc_iter->Buffer1Addr), copy_len); + desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA; + used_descs--; + /* `copy_len` does not include CRC, hence check if we reached the last descriptor */ + while (!desc_iter->RDES0.LastDescriptor) { + desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr); + desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA; + used_descs--; + } + /* update rxdesc */ + hal->rx_desc = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr); + /* poll rx demand */ + emac_ll_receive_poll_demand(hal->dma_regs, 0); + frame_count--; + } + *frames_remain = frame_count; + *free_desc = CONFIG_ETH_DMA_RX_BUFFER_NUM - used_descs; + return ret_len; +} + +uint32_t emac_hal_flush_recv_frame(emac_hal_context_t *hal, uint32_t *frames_remain, uint32_t *free_desc) +{ + eth_dma_rx_descriptor_t *desc_iter = hal->rx_desc; + eth_dma_rx_descriptor_t *first_desc = hal->rx_desc; + uint32_t used_descs = 0; + uint32_t frame_len = 0; + uint32_t frame_count = 0; + /* Traverse descriptors owned by CPU */ while ((desc_iter->RDES0.Own != EMAC_LL_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM) && !frame_count) { used_descs++; - seg_count++; /* Last segment in frame */ if (desc_iter->RDES0.LastDescriptor) { /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */ - ret_len = desc_iter->RDES0.FrameLength - ETH_CRC_LENGTH; - /* packets larger than expected will be truncated */ - copy_len = ret_len > size ? size : ret_len; + frame_len = desc_iter->RDES0.FrameLength - ETH_CRC_LENGTH; /* update unhandled frame count */ frame_count++; } @@ -497,8 +614,9 @@ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t /* point to next descriptor */ desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr); } - /* there's at least one frame to process */ - if (frame_count) { + + /* if there is at least one frame waiting */ + if (frame_len) { /* check how many frames left to handle */ while ((desc_iter->RDES0.Own != EMAC_LL_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM)) { used_descs++; @@ -509,27 +627,21 @@ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr); } desc_iter = first_desc; - for (size_t i = 0; i < seg_count - 1; i++) { - used_descs--; - write_len = copy_len < CONFIG_ETH_DMA_BUFFER_SIZE ? copy_len : CONFIG_ETH_DMA_BUFFER_SIZE; - /* copy data to buffer */ - memcpy(buf, (void *)(desc_iter->Buffer1Addr), write_len); - buf += write_len; - copy_len -= write_len; - /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + /* return descriptors to DMA */ + while (!desc_iter->RDES0.LastDescriptor) { desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA; desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr); + used_descs--; } - memcpy(buf, (void *)(desc_iter->Buffer1Addr), copy_len); desc_iter->RDES0.Own = EMAC_LL_DMADESC_OWNER_DMA; + used_descs--; /* update rxdesc */ hal->rx_desc = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr); /* poll rx demand */ emac_ll_receive_poll_demand(hal->dma_regs, 0); frame_count--; - used_descs--; } *frames_remain = frame_count; *free_desc = CONFIG_ETH_DMA_RX_BUFFER_NUM - used_descs; - return ret_len; + return frame_len; } diff --git a/components/hal/include/hal/emac_hal.h b/components/hal/include/hal/emac_hal.h index fbf0a8ebd66..32c46abe82c 100644 --- a/components/hal/include/hal/emac_hal.h +++ b/components/hal/include/hal/emac_hal.h @@ -19,6 +19,12 @@ extern "C" { #include "soc/emac_mac_struct.h" #include "soc/emac_ext_struct.h" +/** + * @brief Indicate to ::emac_hal_receive_frame that receive frame buffer was allocated by ::emac_hal_alloc_recv_buf + * + */ +#define EMAC_HAL_BUF_SIZE_AUTO 0 + /** * @brief Ethernet DMA TX Descriptor * @@ -231,12 +237,53 @@ void emac_hal_start(emac_hal_context_t *hal); */ esp_err_t emac_hal_stop(emac_hal_context_t *hal); -uint32_t emac_hal_get_tx_desc_owner(emac_hal_context_t *hal); - +/** + * @brief Transmit data from buffer over EMAC + * + * @param[in] hal EMAC HAL context infostructure + * @param[in] buf buffer to be transmitted + * @param[in] length length of the buffer + * @return number of transmitted bytes when success + */ uint32_t emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t length); +/** + * @brief Allocate buffer with size equal to actually received Ethernet frame size. + * + * @param[in] hal EMAC HAL context infostructure + * @param[in, out] size as an input defines maximum size of buffer to be allocated. As an output, indicates actual size of received + * Ethernet frame which is waiting to be processed. Returned size may be 0 when there is no waiting frame. + * + * @note If maximum allowed size of buffer to be allocated is less than actual size of received Ethernet frame, the buffer + * is allocated with that limit and the frame will be truncated by emac_hal_receive_frame. + * + * @return Pointer to allocated buffer + * NULL when allocation fails or when there is no waiting Ethernet frame + */ +uint8_t *emac_hal_alloc_recv_buf(emac_hal_context_t *hal, uint32_t *size); + +/** + * @brief Copy received Ethernet frame from EMAC DMA memory space to application. + * + * @param[in] hal EMAC HAL context infostructure + * @param[in] buf buffer into which the Ethernet frame is to be copied + * @param[in] size buffer size. When buffer was allocated by ::emac_hal_alloc_recv_buf, this parameter needs to be set + * to EMAC_HAL_BUF_SIZE_AUTO + * @param[out] frames_remain number of frames remaining to be processed + * @param[out] free_desc muber of free DMA Rx descriptors + * + * @return number of copied bytes when success + * 0 when there is no waiting Ethernet frame or on error + * + * @note FCS field is never copied + * @note If buffer size is less than actual size of received Ethernet frame, the frame will be truncated. + * @note When this function is called with EMAC_HAL_BUF_SIZE_AUTO size parameter, buffer needs to be allocated by + * ::emac_hal_alloc_recv_buf function at first. + */ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t size, uint32_t *frames_remain, uint32_t *free_desc); +uint32_t emac_hal_flush_recv_frame(emac_hal_context_t *hal, uint32_t *frames_remain, uint32_t *free_desc); + void emac_hal_enable_flow_ctrl(emac_hal_context_t *hal, bool enable); uint32_t emac_hal_get_intr_enable_status(emac_hal_context_t *hal);