mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
esp_eth: receive buffer allocation optimization
Receive buffers are allocated with a size equal to actual received frame size
This commit is contained in:
parent
85f2d53e00
commit
f104be7b93
@ -39,6 +39,15 @@ static const char *TAG = "emac_dm9051";
|
||||
|
||||
#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;
|
||||
@ -58,6 +67,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)
|
||||
@ -394,44 +404,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;
|
||||
@ -643,6 +615,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;
|
||||
|
||||
MAC_CHECK(length <= ETH_MAX_PACKET_SIZE,"frame size is too big (actual %u, maximum %u)", err, ESP_ERR_INVALID_ARG,
|
||||
length, ETH_MAX_PACKET_SIZE);
|
||||
|
||||
int64_t wait_time = esp_timer_get_time();
|
||||
do {
|
||||
MAC_CHECK(dm9051_register_read(emac, DM9051_TCR, &tcr) == ESP_OK, "read TCR failed", err, ESP_FAIL);
|
||||
@ -665,50 +640,138 @@ err:
|
||||
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;
|
||||
MAC_CHECK(dm9051_register_read(emac, DM9051_MRRH, &mrrh) == ESP_OK, "read MDRAH failed", err, ESP_FAIL);
|
||||
MAC_CHECK(dm9051_register_read(emac, DM9051_MRRL, &mrrl) == ESP_OK, "read MDRAL failed", err, ESP_FAIL);
|
||||
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;
|
||||
}
|
||||
MAC_CHECK(dm9051_register_write(emac, DM9051_MRRH, addr >> 8) == ESP_OK, "write MDRAH failed", err, ESP_FAIL);
|
||||
MAC_CHECK(dm9051_register_write(emac, DM9051_MRRL, addr & 0xFF) == ESP_OK, "write MDRAL failed", err, ESP_FAIL);
|
||||
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 */
|
||||
MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL);
|
||||
MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL);
|
||||
/* rxbyte must be 0xFF, 0 or 1 */
|
||||
if (rxbyte > 1) {
|
||||
MAC_CHECK(mac->stop(mac) == ESP_OK, "stop dm9051 failed", err, ESP_FAIL);
|
||||
MAC_CHECK(emac->parent.stop(&emac->parent) == ESP_OK, "stop dm9051 failed", err, ESP_FAIL);
|
||||
/* reset rx fifo pointer */
|
||||
MAC_CHECK(dm9051_register_write(emac, DM9051_MPTRCR, MPTRCR_RST_RX) == ESP_OK,
|
||||
"write MPTRCR failed", err, ESP_FAIL);
|
||||
esp_rom_delay_us(10);
|
||||
MAC_CHECK(mac->start(mac) == ESP_OK, "start dm9051 failed", err, ESP_FAIL);
|
||||
MAC_CHECK(emac->parent.start(&emac->parent) == ESP_OK, "start dm9051 failed", err, ESP_FAIL);
|
||||
MAC_CHECK(false, "reset rx fifo pointer", err, ESP_FAIL);
|
||||
} else if (rxbyte) {
|
||||
MAC_CHECK(dm9051_memory_peek(emac, (uint8_t *)&header, sizeof(header)) == ESP_OK,
|
||||
"peek rx header failed", err, ESP_FAIL);
|
||||
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;
|
||||
MAC_CHECK(dm9051_memory_peek(emac, (uint8_t *)&header, sizeof(header)) == ESP_OK, "peek rx header failed", err, ESP_FAIL);
|
||||
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);
|
||||
MAC_CHECK(false, "receive status error: %xH", err, ESP_FAIL, 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;
|
||||
MAC_CHECK(dm9051_get_recv_byte_count(emac, &rx_len) == ESP_OK, "get rx frame length failed", err, ESP_FAIL);
|
||||
MAC_CHECK(dm9051_skip_recv_frame(emac, rx_len) == ESP_OK, "skipping frame in RX memory failed", err, ESP_FAIL);
|
||||
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;
|
||||
|
||||
MAC_CHECK(dm9051_get_recv_byte_count(emac, &byte_count) == ESP_OK, "get rx frame length failed", err, ESP_FAIL);
|
||||
// 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
|
||||
MAC_CHECK(copy_len >= ETH_MIN_PACKET_SIZE - ETH_CRC_LEN, "invalid frame length %u", err, ESP_ERR_INVALID_SIZE, 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) {
|
||||
MAC_CHECK(dm9051_get_recv_byte_count(emac, &byte_count) == ESP_OK,"get rx frame length failed", err, ESP_FAIL);
|
||||
/* silently return when no frame is waiting */
|
||||
if (!byte_count) {
|
||||
goto err;
|
||||
}
|
||||
MAC_CHECK(dm9051_memory_read(emac, (uint8_t *)&header, sizeof(header)) == ESP_OK,
|
||||
"read rx header failed", err, ESP_FAIL);
|
||||
MAC_CHECK(dm9051_memory_read(emac, buf, rx_len) == ESP_OK, "read rx data failed", err, ESP_FAIL);
|
||||
MAC_CHECK(!(header.status & 0xBF), "receive status error: %xH", err, ESP_FAIL, header.status);
|
||||
*length = rx_len - 4; // substract the CRC length (4Bytes)
|
||||
/* dummy read, get the most updated data */
|
||||
MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL);
|
||||
MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL);
|
||||
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;
|
||||
MAC_CHECK(dm9051_memory_read(emac, emac->rx_buffer, byte_count) == ESP_OK, "read rx data failed", err, ESP_FAIL);
|
||||
memcpy(buf, emac->rx_buffer + DM9051_RX_HDR_SIZE, copy_len);
|
||||
*length = copy_len;
|
||||
|
||||
/* dummy read, get the most updated data */
|
||||
MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL);
|
||||
/* check for remaing packets */
|
||||
MAC_CHECK(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte) == ESP_OK, "read MRCMDX failed", err, ESP_FAIL);
|
||||
emac->packets_remain = rxbyte > 0;
|
||||
return ESP_OK;
|
||||
err:
|
||||
*length = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -753,11 +816,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;
|
||||
}
|
||||
@ -805,6 +925,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);
|
||||
MAC_CHECK(xReturned == pdPASS, "create dm9051 task failed", err, NULL);
|
||||
|
||||
emac->rx_buffer = heap_caps_malloc(ETH_MAX_PACKET_SIZE + DM9051_RX_HDR_SIZE, MALLOC_CAP_DMA);
|
||||
MAC_CHECK(emac->rx_buffer, "RX buffer allocation failed", err, NULL);
|
||||
|
||||
return &(emac->parent);
|
||||
|
||||
err:
|
||||
@ -815,6 +939,7 @@ err:
|
||||
if (emac->spi_lock) {
|
||||
vSemaphoreDelete(emac->spi_lock);
|
||||
}
|
||||
heap_caps_free(emac->rx_buffer);
|
||||
free(emac);
|
||||
}
|
||||
return ret;
|
||||
|
@ -286,24 +286,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);
|
||||
if (buffer != NULL) {
|
||||
/* we have memory to receive the frame of maximal size previously defined */
|
||||
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
|
||||
|
@ -43,6 +43,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;
|
||||
@ -54,6 +62,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)
|
||||
@ -307,59 +316,6 @@ 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 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;
|
||||
@ -525,6 +481,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;
|
||||
|
||||
MAC_CHECK(length <= ETH_MAX_PACKET_SIZE, "frame size is too big (actual %u, maximum %u)", err, ESP_ERR_INVALID_ARG,
|
||||
length, ETH_MAX_PACKET_SIZE);
|
||||
// check if there're free memory to store this packet
|
||||
uint16_t free_size = 0;
|
||||
MAC_CHECK(w5500_get_tx_free_size(emac, &free_size) == ESP_OK, "get free size failed", err, ESP_FAIL);
|
||||
@ -558,12 +516,103 @@ err:
|
||||
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
|
||||
MAC_CHECK(w5500_read(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset)) == ESP_OK, "read RX RD failed", err, ESP_FAIL);
|
||||
offset = __builtin_bswap16(offset);
|
||||
// read head
|
||||
MAC_CHECK(w5500_read_buffer(emac, &rx_len, sizeof(rx_len), offset) == ESP_OK, "read frame header failed", err, ESP_FAIL);
|
||||
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
|
||||
MAC_CHECK(copy_len >= ETH_MIN_PACKET_SIZE - ETH_CRC_LEN, "invalid frame length %u", err, ESP_ERR_INVALID_SIZE, 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
|
||||
MAC_CHECK(w5500_read(emac, W5500_REG_SOCK_RX_RD(0) == ESP_OK, &offset, sizeof(offset)), "read RX RD failed", err, ESP_FAIL);
|
||||
offset = __builtin_bswap16(offset);
|
||||
// read head first
|
||||
MAC_CHECK(w5500_read_buffer(emac, &rx_len, sizeof(rx_len), offset) == ESP_OK, "read frame header failed", err, ESP_FAIL);
|
||||
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
|
||||
MAC_CHECK(w5500_read_buffer(emac, emac->rx_buffer, copy_len, offset) == ESP_OK, "read payload failed, len=%d, offset=%d", err, ESP_FAIL, rx_len, offset);
|
||||
memcpy(buf, emac->rx_buffer, copy_len);
|
||||
offset += rx_len;
|
||||
// update read pointer
|
||||
offset = __builtin_bswap16(offset);
|
||||
MAC_CHECK(w5500_write(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset)) == ESP_OK, "write RX RD failed", err, ESP_FAIL);
|
||||
/* issue RECV command */
|
||||
MAC_CHECK(w5500_send_command(emac, W5500_SCR_RECV, 100) == ESP_OK, "issue RECV command failed", err, ESP_FAIL);
|
||||
// check if there're more data need to process
|
||||
remain_bytes -= rx_len + 2;
|
||||
emac->packets_remain = remain_bytes > 0;
|
||||
|
||||
*length = copy_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;
|
||||
|
||||
@ -574,26 +623,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
|
||||
MAC_CHECK(w5500_read_buffer(emac, &rx_len, sizeof(rx_len), offset) == ESP_OK, "read frame header failed", err, ESP_FAIL);
|
||||
rx_len = __builtin_bswap16(rx_len) - 2; // data size includes 2 bytes of header
|
||||
offset += 2;
|
||||
// read the payload
|
||||
MAC_CHECK(w5500_read_buffer(emac, buf, rx_len, offset) == ESP_OK, "read payload failed, len=%d, offset=%d", err, ESP_FAIL, rx_len, offset);
|
||||
offset += rx_len;
|
||||
// update read pointer
|
||||
rx_len = __builtin_bswap16(rx_len);
|
||||
offset += rx_len;
|
||||
offset = __builtin_bswap16(offset);
|
||||
MAC_CHECK(w5500_write(emac, W5500_REG_SOCK_RX_RD(0), &offset, sizeof(offset)) == ESP_OK, "write RX RD failed", err, ESP_FAIL);
|
||||
/* issue RECV command */
|
||||
MAC_CHECK(w5500_send_command(emac, W5500_SCR_RECV, 100) == ESP_OK, "issue RECV command failed", err, ESP_FAIL);
|
||||
// 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;
|
||||
@ -636,6 +749,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;
|
||||
}
|
||||
@ -682,6 +796,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);
|
||||
MAC_CHECK(xReturned == pdPASS, "create w5500 task failed", err, NULL);
|
||||
|
||||
emac->rx_buffer = heap_caps_malloc(ETH_MAX_PACKET_SIZE, MALLOC_CAP_DMA);
|
||||
MAC_CHECK(emac->rx_buffer, "RX buffer allocation failed", err, NULL);
|
||||
|
||||
return &(emac->parent);
|
||||
|
||||
err:
|
||||
@ -692,6 +810,7 @@ err:
|
||||
if (emac->spi_lock) {
|
||||
vSemaphoreDelete(emac->spi_lock);
|
||||
}
|
||||
heap_caps_free(emac->rx_buffer);
|
||||
free(emac);
|
||||
}
|
||||
return ret;
|
||||
|
@ -21,6 +21,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;
|
||||
|
||||
|
||||
#if CONFIG_ETH_RMII_CLK_OUTPUT
|
||||
static void emac_config_apll_clock(void)
|
||||
{
|
||||
@ -531,41 +543,85 @@ err:
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
uint8_t *emac_hal_alloc_recv_buf(emac_hal_context_t *hal, uint32_t *size)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
uint8_t *buf = NULL;
|
||||
|
||||
first_desc = hal->rx_desc;
|
||||
desc_iter = hal->rx_desc;
|
||||
/* Traverse descriptors owned by CPU */
|
||||
while ((desc_iter->RDES0.Own != EMAC_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM) && !frame_count) {
|
||||
while ((desc_iter->RDES0.Own != EMAC_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM)) {
|
||||
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;
|
||||
/* update unhandled frame count */
|
||||
frame_count++;
|
||||
}
|
||||
/* First segment in frame */
|
||||
if (desc_iter->RDES0.FirstDescriptor) {
|
||||
first_desc = desc_iter;
|
||||
copy_len = ret_len > *size ? *size : ret_len;
|
||||
break;
|
||||
}
|
||||
/* 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 (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 = hal->rx_desc;
|
||||
eth_dma_rx_descriptor_t *first_desc = hal->rx_desc;
|
||||
uint32_t used_descs = 0;
|
||||
uint32_t ret_len = 0;
|
||||
uint32_t copy_len = 0;
|
||||
uint32_t frame_count = 0;
|
||||
|
||||
if (size != EMAC_HAL_BUF_SIZE_AUTO) {
|
||||
/* Traverse descriptors owned by CPU */
|
||||
while ((desc_iter->RDES0.Own != EMAC_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_DMADESC_OWNER_DMA) && (used_descs < CONFIG_ETH_DMA_RX_BUFFER_NUM)) {
|
||||
used_descs++;
|
||||
@ -576,31 +632,92 @@ 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++) {
|
||||
while(copy_len > CONFIG_ETH_DMA_BUFFER_SIZE) {
|
||||
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;
|
||||
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_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_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_DMADESC_OWNER_DMA;
|
||||
used_descs--;
|
||||
}
|
||||
/* update rxdesc */
|
||||
hal->rx_desc = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
|
||||
/* poll rx demand */
|
||||
hal->dma_regs->dmarxpolldemand = 0;
|
||||
frame_count--;
|
||||
used_descs--;
|
||||
}
|
||||
*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_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 */
|
||||
frame_len = desc_iter->RDES0.FrameLength - ETH_CRC_LENGTH;
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* if there is at least one frame waiting */
|
||||
if (frame_len) {
|
||||
/* check how many frames left to handle */
|
||||
while ((desc_iter->RDES0.Own != EMAC_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;
|
||||
/* return descriptors to DMA */
|
||||
while (!desc_iter->RDES0.LastDescriptor) {
|
||||
desc_iter->RDES0.Own = EMAC_DMADESC_OWNER_DMA;
|
||||
desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
|
||||
used_descs--;
|
||||
}
|
||||
desc_iter->RDES0.Own = EMAC_DMADESC_OWNER_DMA;
|
||||
used_descs--;
|
||||
/* update rxdesc */
|
||||
hal->rx_desc = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr);
|
||||
/* poll rx demand */
|
||||
hal->dma_regs->dmarxpolldemand = 0;
|
||||
frame_count--;
|
||||
}
|
||||
*frames_remain = frame_count;
|
||||
*free_desc = CONFIG_ETH_DMA_RX_BUFFER_NUM - used_descs;
|
||||
return frame_len;
|
||||
}
|
||||
|
||||
IRAM_ATTR void emac_hal_isr(void *arg)
|
||||
{
|
||||
emac_hal_context_t *hal = (emac_hal_context_t *)arg;
|
||||
|
@ -176,6 +176,12 @@ extern "C" {
|
||||
#define EMAC_DMA_ARBITRATION_ROUNDROBIN_RXTX_3_1 (2)
|
||||
#define EMAC_DMA_ARBITRATION_ROUNDROBIN_RXTX_4_1 (3)
|
||||
|
||||
/**
|
||||
* @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
|
||||
*
|
||||
@ -394,10 +400,53 @@ 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);
|
||||
|
||||
void emac_hal_isr(void *arg);
|
||||
|
Loading…
Reference in New Issue
Block a user