mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
fix(esp_eth): Fix DM9051 abnormal operation when SPI clock is too high
Simplified buffer allocation for DM9051
This commit is contained in:
parent
f477682938
commit
61e05bddc9
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2019-2023 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2019-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -149,6 +149,16 @@ extern "C" {
|
|||||||
#define IMR_PRI (1 << 0) // Enable Packet Received Interrupt
|
#define IMR_PRI (1 << 0) // Enable Packet Received Interrupt
|
||||||
#define IMR_ALL (IMR_PAR | IMR_LNKCHGI | IMR_ROOI | IMR_ROI | IMR_PTI | IMR_PRI)
|
#define IMR_ALL (IMR_PAR | IMR_LNKCHGI | IMR_ROOI | IMR_ROI | IMR_PTI | IMR_PRI)
|
||||||
|
|
||||||
|
#define MLEDCR_MOD3 (1 << 7) // New LED mode
|
||||||
|
#define MLEDCR_POL (1 << 2) // Reverse Polarity of LED Type
|
||||||
|
|
||||||
|
// | LED Type | LNKLED (pin 25) | SPDLED (pin 26) | FDXLED (pin 16) |
|
||||||
|
// |-----------------------------------|------------------|-----------------|-----------------|
|
||||||
|
#define MLEDCR_LED_TYPE_00 (0x00 << 0) // | Link | Traffic | Full-Duplex |
|
||||||
|
#define MLEDCR_LED_TYPE_01 (0x01 << 0) // | Link & Traffic | Speed100M | Full-Duplex |
|
||||||
|
#define MLEDCR_LED_TYPE_10 (0x02 << 0) // | Traffic | Speeed100M | Speed10M |
|
||||||
|
#define MLEDCR_LED_TYPE_11 (0x03 << 0) // | Link | Traffic100M | Traffic10M |
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -36,18 +36,13 @@ static const char *TAG = "dm9051.mac";
|
|||||||
#define DM9051_RX_MEM_START_ADDR (3072)
|
#define DM9051_RX_MEM_START_ADDR (3072)
|
||||||
#define DM9051_RX_MEM_MAX_SIZE (16384)
|
#define DM9051_RX_MEM_MAX_SIZE (16384)
|
||||||
#define DM9051_RX_HDR_SIZE (4)
|
#define DM9051_RX_HDR_SIZE (4)
|
||||||
#define DM9051_ETH_MAC_RX_BUF_SIZE_AUTO (0)
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t copy_len;
|
uint8_t flag; // 0 = no frame, 1 = frame received, others = possible memory pointer error or tcpip_checksum_offload status flag if enabled
|
||||||
uint32_t byte_cnt;
|
uint8_t status; // Events occurred between this and previous frame (the same format as RSR)
|
||||||
} __attribute__((packed)) dm9051_auto_buf_info_t;
|
uint8_t length_low; // Low byte of received frame length
|
||||||
|
uint8_t length_high; // High byte of received frame length
|
||||||
typedef struct {
|
|
||||||
uint8_t flag;
|
|
||||||
uint8_t status;
|
|
||||||
uint8_t length_low;
|
|
||||||
uint8_t length_high;
|
|
||||||
} dm9051_rx_header_t;
|
} dm9051_rx_header_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -230,14 +225,6 @@ static esp_err_t dm9051_memory_read(emac_dm9051_t *emac, uint8_t *buffer, uint32
|
|||||||
return emac->spi.read(emac->spi.ctx, DM9051_SPI_RD, DM9051_MRCMD, buffer, len);
|
return emac->spi.read(emac->spi.ctx, DM9051_SPI_RD, DM9051_MRCMD, buffer, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief peek buffer from dm9051 internal memory (without internal cursor moved)
|
|
||||||
*/
|
|
||||||
static esp_err_t dm9051_memory_peek(emac_dm9051_t *emac, uint8_t *buffer, uint32_t len)
|
|
||||||
{
|
|
||||||
return emac->spi.read(emac->spi.ctx, DM9051_SPI_RD, DM9051_MRCMDX1, buffer, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief read mac address from internal registers
|
* @brief read mac address from internal registers
|
||||||
*/
|
*/
|
||||||
@ -272,13 +259,10 @@ err:
|
|||||||
static esp_err_t dm9051_clear_multicast_table(emac_dm9051_t *emac)
|
static esp_err_t dm9051_clear_multicast_table(emac_dm9051_t *emac)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
/* rx broadcast packet control by bit7 of MAC register 1DH */
|
/* Keep multicast hash table empty and use DM9051_BCASTCR to accept broadcast for in better performance */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_BCASTCR, 0x00), err, TAG, "write BCASTCR failed");
|
for (int i = 0; i < 8; i++) {
|
||||||
for (int i = 0; i < 7; i++) {
|
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MAR + i, 0x00), err, TAG, "write MAR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MAR + i, 0x00), err, TAG, "write MAR failed");
|
||||||
}
|
}
|
||||||
/* enable receive broadcast paclets */
|
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MAR + 7, 0x80), err, TAG, "write MAR failed");
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
@ -292,7 +276,7 @@ static esp_err_t dm9051_reset(emac_dm9051_t *emac)
|
|||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
/* power on phy */
|
/* power on phy */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_GPR, 0x00), err, TAG, "write GPR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_GPR, 0x00), err, TAG, "write GPR failed");
|
||||||
/* mac and phy register won't be accesable within at least 1ms */
|
/* mac and phy register won't be accessible within at least 1ms */
|
||||||
vTaskDelay(pdMS_TO_TICKS(10));
|
vTaskDelay(pdMS_TO_TICKS(10));
|
||||||
/* software reset */
|
/* software reset */
|
||||||
uint8_t ncr = NCR_RST;
|
uint8_t ncr = NCR_RST;
|
||||||
@ -340,6 +324,8 @@ static esp_err_t dm9051_setup_default(emac_dm9051_t *emac)
|
|||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_WCR, 0x00), err, TAG, "write WCR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_WCR, 0x00), err, TAG, "write WCR failed");
|
||||||
/* stop transmitting, enable appending pad, crc for packets */
|
/* stop transmitting, enable appending pad, crc for packets */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TCR, 0x00), err, TAG, "write TCR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TCR, 0x00), err, TAG, "write TCR failed");
|
||||||
|
/* Before enabling the RCR feature, make sure to set IMR_PAR in IMR */
|
||||||
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_IMR, IMR_PAR), err, TAG, "write DM9051_IMR failed");
|
||||||
/* stop receiving, no promiscuous mode, no runt packet(size < 64bytes), receive all multicast packets */
|
/* stop receiving, no promiscuous mode, no runt packet(size < 64bytes), receive all multicast packets */
|
||||||
/* discard long packet(size > 1522bytes) and crc error packet, enable watchdog */
|
/* discard long packet(size > 1522bytes) and crc error packet, enable watchdog */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_ALL_MCAST), err, TAG, "write RCR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_ALL_MCAST), err, TAG, "write RCR failed");
|
||||||
@ -347,19 +333,25 @@ static esp_err_t dm9051_setup_default(emac_dm9051_t *emac)
|
|||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TCR2, TCR2_RLCP), err, TAG, "write TCR2 failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TCR2, TCR2_RLCP), err, TAG, "write TCR2 failed");
|
||||||
/* enable auto transmit */
|
/* enable auto transmit */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_ATCR, ATCR_AUTO_TX), err, TAG, "write ATCR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_ATCR, ATCR_AUTO_TX), err, TAG, "write ATCR failed");
|
||||||
/* generate checksum for UDP, TCP and IPv4 packets */
|
/* do not generate checksum for UDP, TCP and IPv4 packets */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TCSCR, TCSCR_IPCSE | TCSCR_TCPCSE | TCSCR_UDPCSE), err, TAG, "write TCSCR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TCSCR, 0x00), err, TAG, "write TCSCR failed");
|
||||||
/* disable check sum for receive packets */
|
/* disable check sum for receive packets */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_RCSCSR, 0x00), err, TAG, "write RCSCSR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_RCSCSR, 0x00), err, TAG, "write RCSCSR failed");
|
||||||
/* interrupt pin config: push-pull output, active high */
|
/* interrupt pin config: push-pull output, active high */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_INTCR, 0x00), err, TAG, "write INTCR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_INTCR, 0x00), err, TAG, "write INTCR failed");
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_INTCKCR, 0x00), err, TAG, "write INTCKCR failed");
|
/* Utilize DM9051_INTCKCR to enable edge-triggered interrupts and address level-triggered interrupt loss */
|
||||||
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_INTCKCR, 0x83), err, TAG, "write INTCKCR failed");
|
||||||
/* no length limitation for rx packets */
|
/* no length limitation for rx packets */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_RLENCR, 0x00), err, TAG, "write RLENCR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_RLENCR, 0x00), err, TAG, "write RLENCR failed");
|
||||||
/* 3K-byte for TX and 13K-byte for RX */
|
/* 3K-byte for TX and 13K-byte for RX */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MEMSCR, 0x00), err, TAG, "write MEMSCR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MEMSCR, 0x00), err, TAG, "write MEMSCR failed");
|
||||||
/* clear network status: wakeup event, tx complete */
|
/* clear network status: wakeup event, tx complete */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END), err, TAG, "write NSR failed");
|
/* Optimize transmission process using TX2END and TX1END */
|
||||||
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_NSR, NSR_WAKEST), err, TAG, "write NSR failed");
|
||||||
|
/* Set Link & Traffic LED mode */
|
||||||
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MLEDCR, MLEDCR_MOD3 | MLEDCR_LED_TYPE_01), err, TAG, "write DM9051_MLEDCR failed");
|
||||||
|
/* Enable packet length filter of broadcast packet */
|
||||||
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_BCASTCR, 0xc0), err, TAG, "write DM9051_BCASTCR failed");
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
@ -414,8 +406,8 @@ static esp_err_t emac_dm9051_stop(esp_eth_mac_t *mac)
|
|||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
|
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
|
||||||
/* disable interrupt */
|
/* IMR_PAR should not be cleared unless Wake-on-LAN (WOL) functionality is required */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_IMR, 0x00), err, TAG, "write IMR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_IMR, IMR_PAR), err, TAG, "write IMR failed");
|
||||||
/* disable rx */
|
/* disable rx */
|
||||||
uint8_t rcr = 0;
|
uint8_t rcr = 0;
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_RCR, &rcr), err, TAG, "read RCR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_RCR, &rcr), err, TAG, "read RCR failed");
|
||||||
@ -647,29 +639,31 @@ static esp_err_t emac_dm9051_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t
|
|||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
|
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
|
||||||
/* Check if last transmit complete */
|
|
||||||
uint8_t tcr = 0;
|
|
||||||
|
|
||||||
ESP_GOTO_ON_FALSE(length <= ETH_MAX_PACKET_SIZE, ESP_ERR_INVALID_ARG, err,
|
ESP_GOTO_ON_FALSE(length <= ETH_MAX_PACKET_SIZE, ESP_ERR_INVALID_ARG, err,
|
||||||
TAG, "frame size is too big (actual %" PRIu32 ", maximum %d)", length, ETH_MAX_PACKET_SIZE);
|
TAG, "frame size is too big (actual %" PRIu32 ", maximum %d)", length, ETH_MAX_PACKET_SIZE);
|
||||||
|
|
||||||
|
uint8_t reg_nsr = 0;
|
||||||
int64_t wait_time = esp_timer_get_time();
|
int64_t wait_time = esp_timer_get_time();
|
||||||
do {
|
do {
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_TCR, &tcr), err, TAG, "read TCR failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_NSR, ®_nsr), err, TAG, "read NSR failed");
|
||||||
} while ((tcr & TCR_TXREQ) && ((esp_timer_get_time() - wait_time) < 100));
|
reg_nsr &= (NSR_TX2END | NSR_TX1END);
|
||||||
|
} while((!reg_nsr) && ((esp_timer_get_time() - wait_time) < 100));
|
||||||
|
|
||||||
if (tcr & TCR_TXREQ) {
|
if (!reg_nsr) {
|
||||||
ESP_LOGE(TAG, "last transmit still in progress, cannot send.");
|
ESP_LOGE(TAG, "last transmit still in progress, cannot send.");
|
||||||
return ESP_ERR_INVALID_STATE;
|
return ESP_ERR_INVALID_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(reg_nsr == (NSR_TX2END | NSR_TX1END)) {
|
||||||
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MPTRCR, MPTRCR_RST_TX), err, TAG, "write MPTRCR failed");
|
||||||
|
}
|
||||||
|
|
||||||
/* set tx length */
|
/* set tx length */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TXPLL, length & 0xFF), err, TAG, "write TXPLL failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TXPLL, length & 0xFF), err, TAG, "write TXPLL failed");
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TXPLH, (length >> 8) & 0xFF), err, TAG, "write TXPLH failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TXPLH, (length >> 8) & 0xFF), err, TAG, "write TXPLH failed");
|
||||||
/* copy data to tx memory */
|
/* copy data to tx memory */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_memory_write(emac, buf, length), err, TAG, "write memory failed");
|
ESP_GOTO_ON_ERROR(dm9051_memory_write(emac, buf, length), err, TAG, "write memory failed");
|
||||||
/* issue tx polling command */
|
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_TCR, TCR_TXREQ), err, TAG, "write TCR failed");
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
@ -682,127 +676,102 @@ static esp_err_t dm9051_skip_recv_frame(emac_dm9051_t *emac, uint16_t rx_length)
|
|||||||
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_MRRH, &mrrh), err, TAG, "read MDRAH failed");
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRRL, &mrrl), err, TAG, "read MDRAL failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRRL, &mrrl), err, TAG, "read MDRAL failed");
|
||||||
uint16_t addr = mrrh << 8 | mrrl;
|
uint16_t addr = mrrh << 8 | mrrl;
|
||||||
/* include 4B for header */
|
addr += rx_length;
|
||||||
addr += rx_length + DM9051_RX_HDR_SIZE;
|
|
||||||
if (addr > DM9051_RX_MEM_MAX_SIZE) {
|
if (addr > DM9051_RX_MEM_MAX_SIZE) {
|
||||||
addr = addr - DM9051_RX_MEM_MAX_SIZE + DM9051_RX_MEM_START_ADDR;
|
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");
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MRRL, addr & 0xFF), err, TAG, "write MDRAL failed");
|
||||||
|
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MRRH, addr >> 8), err, TAG, "write MDRAH failed");
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t dm9051_get_recv_byte_count(emac_dm9051_t *emac, uint16_t *size)
|
static esp_err_t dm9051_flush_recv_queue(emac_dm9051_t *emac)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
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(emac->parent.start(&emac->parent), err, TAG, "start dm9051 failed");
|
||||||
|
err:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t dm9051_frame_to_rx_buffer(emac_dm9051_t *emac, uint16_t *size)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
uint8_t rxbyte = 0;
|
uint8_t rxbyte = 0;
|
||||||
__attribute__((aligned(4))) dm9051_rx_header_t header; // SPI driver needs the rx buffer 4 byte align
|
__attribute__((aligned(4))) dm9051_rx_header_t header; // SPI driver needs the rx buffer 4 byte align
|
||||||
|
bool try_again = false;
|
||||||
|
|
||||||
*size = 0;
|
do {
|
||||||
/* dummy read, get the most updated data */
|
*size = 0;
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte), err, TAG, "read MRCMDX failed");
|
uint8_t reg_nsr = 0;
|
||||||
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_NSR, ®_nsr), err, TAG, "read NSR failed");
|
||||||
/* rxbyte must be 0xFF, 0 or 1 */
|
if (reg_nsr & NSR_RXRDY) {
|
||||||
if (rxbyte > 1) {
|
/* dummy read, get the most updated data */
|
||||||
ESP_GOTO_ON_ERROR(emac->parent.stop(&emac->parent), err, TAG, "stop dm9051 failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRCMDX, &rxbyte), err, TAG, "read MRCMDX failed");
|
||||||
/* reset rx fifo pointer */
|
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_MRCMDX1, &rxbyte), err, TAG, "read MRCMDX failed");
|
||||||
ESP_GOTO_ON_ERROR(dm9051_register_write(emac, DM9051_MPTRCR, MPTRCR_RST_RX), err, TAG, "write MPTRCR failed");
|
if (0x01 != rxbyte) {
|
||||||
esp_rom_delay_us(10);
|
ESP_GOTO_ON_ERROR(dm9051_flush_recv_queue(emac), err, TAG, "flush rx queue failed");
|
||||||
ESP_GOTO_ON_ERROR(emac->parent.start(&emac->parent), err, TAG, "start dm9051 failed");
|
ESP_GOTO_ON_FALSE(false, ESP_FAIL, err, TAG, "unexpected rx flag (0x%" PRIx8 "), reset rx fifo pointer", rxbyte);
|
||||||
ESP_GOTO_ON_FALSE(false, ESP_FAIL, err, TAG, "reset rx fifo pointer");
|
}
|
||||||
} else if (rxbyte) {
|
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_peek(emac, (uint8_t *)&header, sizeof(header)), err, TAG, "peek rx header failed");
|
uint16_t rx_len = header.length_low + (header.length_high << 8);
|
||||||
uint16_t rx_len = header.length_low + (header.length_high << 8);
|
/* store the whole frame to preallocated memory */
|
||||||
if (header.status & 0xBF) {
|
if (rx_len <= ETH_MAX_PACKET_SIZE) {
|
||||||
/* erroneous frames should not be forwarded by DM9051, however, if it happens, just skip it */
|
ESP_GOTO_ON_ERROR(dm9051_memory_read(emac, emac->rx_buffer, rx_len), err, TAG, "read rx data failed");
|
||||||
dm9051_skip_recv_frame(emac, rx_len);
|
} else {
|
||||||
ESP_GOTO_ON_FALSE(false, ESP_FAIL, err, TAG, "receive status error: %" PRIx8 "H", header.status);
|
/* we are out of sync or data is corrupted, there is no way how to fix position in rx fifo => flush all */
|
||||||
|
ESP_GOTO_ON_ERROR(dm9051_flush_recv_queue(emac), err, TAG, "flush rx queue failed");
|
||||||
|
ESP_GOTO_ON_FALSE(false, ESP_FAIL, err, TAG, "invalid frame length, reset rx fifo pointer");
|
||||||
|
}
|
||||||
|
if (header.status & (RSR_RF | RSR_RWTO | RSR_CE | RSR_FOE)) {
|
||||||
|
/* erroneous frames should not be forwarded by DM9051, however, if it happens, just skip it */
|
||||||
|
dm9051_skip_recv_frame(emac, rx_len);
|
||||||
|
ESP_LOGE(TAG, "receive status error: %" PRIx8 "H", header.status);
|
||||||
|
/* try again to check if other frame is waiting */
|
||||||
|
try_again = true;
|
||||||
|
} else {
|
||||||
|
*size = rx_len;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
*size = rx_len;
|
} while (try_again);
|
||||||
}
|
|
||||||
err:
|
err:
|
||||||
return ret;
|
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 %" PRIu16, 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)
|
static esp_err_t emac_dm9051_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
esp_err_t ret = ESP_OK;
|
||||||
emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent);
|
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;
|
uint16_t byte_count = 0;
|
||||||
emac->packets_remain = false;
|
emac->packets_remain = false;
|
||||||
|
|
||||||
if (*length != DM9051_ETH_MAC_RX_BUF_SIZE_AUTO) {
|
/* always read the full frame to preallocated memory to simplify subsequent rx fifo pointer operations */
|
||||||
ESP_GOTO_ON_ERROR(dm9051_get_recv_byte_count(emac, &byte_count), err, TAG, "get rx frame length failed");
|
ESP_GOTO_ON_ERROR(dm9051_frame_to_rx_buffer(emac, &byte_count), err, TAG, "moving data to internal rx_buffer failed");
|
||||||
/* silently return when no frame is waiting */
|
/* silently return when no frame is waiting */
|
||||||
if (!byte_count) {
|
if (!byte_count) {
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
/* do not include 4 bytes CRC at the end */
|
/* do not include 4 bytes CRC at the end */
|
||||||
rx_len = byte_count - ETH_CRC_LEN;
|
uint16_t rx_len = byte_count - ETH_CRC_LEN;
|
||||||
/* frames larger than expected will be truncated */
|
if (buf == emac->rx_buffer) {
|
||||||
copy_len = rx_len > *length ? *length : rx_len;
|
/* if we use internal buffer, we are done */
|
||||||
|
*length = rx_len;
|
||||||
} else {
|
} else {
|
||||||
dm9051_auto_buf_info_t *buff_info = (dm9051_auto_buf_info_t *)buf;
|
/* if frame to be copied to external buffer allocated by user */
|
||||||
copy_len = buff_info->copy_len;
|
ESP_GOTO_ON_FALSE(buf, ESP_ERR_INVALID_ARG, err, TAG, "buffer can't be NULL");
|
||||||
byte_count = buff_info->byte_cnt;
|
/* frames larger than expected will be truncated */
|
||||||
|
uint16_t copy_len = rx_len > *length ? *length : rx_len;
|
||||||
|
memcpy(buf, emac->rx_buffer, copy_len);
|
||||||
|
*length = copy_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte_count += DM9051_RX_HDR_SIZE;
|
uint8_t reg_nsr = 0;
|
||||||
ESP_GOTO_ON_ERROR(dm9051_memory_read(emac, emac->rx_buffer, byte_count), err, TAG, "read rx data failed");
|
ESP_GOTO_ON_ERROR(dm9051_register_read(emac, DM9051_NSR, ®_nsr), err, TAG, "read NSR failed");
|
||||||
memcpy(buf, emac->rx_buffer + DM9051_RX_HDR_SIZE, copy_len);
|
emac->packets_remain = (reg_nsr & NSR_RXRDY);
|
||||||
*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 remaining 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;
|
return ESP_OK;
|
||||||
err:
|
err:
|
||||||
*length = 0;
|
*length = 0;
|
||||||
@ -863,12 +832,11 @@ static void emac_dm9051_task(void *arg)
|
|||||||
{
|
{
|
||||||
emac_dm9051_t *emac = (emac_dm9051_t *)arg;
|
emac_dm9051_t *emac = (emac_dm9051_t *)arg;
|
||||||
uint8_t status = 0;
|
uint8_t status = 0;
|
||||||
esp_err_t ret;
|
|
||||||
while (1) {
|
while (1) {
|
||||||
// check if the task receives any notification
|
// check if the task receives any notification
|
||||||
if (emac->int_gpio_num >= 0) { // if in interrupt mode
|
if (emac->int_gpio_num >= 0) { // if in interrupt mode
|
||||||
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ...
|
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000)) == 0 && // if no notification ...
|
||||||
gpio_get_level(emac->int_gpio_num) == 0) { // ...and no interrupt asserted
|
gpio_get_level(emac->int_gpio_num) == 0) { // ...and no interrupt asserted
|
||||||
continue; // -> just continue to check again
|
continue; // -> just continue to check again
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -880,38 +848,22 @@ static void emac_dm9051_task(void *arg)
|
|||||||
/* packet received */
|
/* packet received */
|
||||||
if (status & ISR_PR) {
|
if (status & ISR_PR) {
|
||||||
do {
|
do {
|
||||||
/* define max expected frame len */
|
uint32_t buf_len;
|
||||||
uint32_t frame_len = ETH_MAX_PACKET_SIZE;
|
if (emac->parent.receive(&emac->parent, emac->rx_buffer, &buf_len) == ESP_OK) {
|
||||||
uint8_t *buffer;
|
/* if there is waiting frame */
|
||||||
if ((ret = dm9051_alloc_recv_buf(emac, &buffer, &frame_len)) == ESP_OK) {
|
if (buf_len > 0) {
|
||||||
if (buffer != NULL) {
|
uint8_t *buffer = malloc(buf_len);
|
||||||
/* we have memory to receive the frame of maximal size previously defined */
|
if (buffer == NULL) {
|
||||||
uint32_t buf_len = DM9051_ETH_MAC_RX_BUF_SIZE_AUTO;
|
ESP_LOGE(TAG, "no mem for receive buffer");
|
||||||
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=%" PRIu32, buf_len);
|
|
||||||
/* pass the buffer to stack (e.g. TCP/IP layer) */
|
|
||||||
emac->eth->stack_input(emac->eth, buffer, buf_len);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGE(TAG, "frame read from module failed");
|
memcpy(buffer, emac->rx_buffer, buf_len);
|
||||||
dm9051_flush_recv_frame(emac);
|
ESP_LOGD(TAG, "receive len=%" PRIu32, buf_len);
|
||||||
free(buffer);
|
/* pass the buffer to stack (e.g. TCP/IP layer) */
|
||||||
|
emac->eth->stack_input(emac->eth, buffer, buf_len);
|
||||||
}
|
}
|
||||||
} else if (frame_len) {
|
|
||||||
ESP_LOGE(TAG, "invalid combination of frame_len(%" PRIu32 ") 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 {
|
} else {
|
||||||
ESP_LOGE(TAG, "unexpected error 0x%x", ret);
|
ESP_LOGE(TAG, "frame read from module failed");
|
||||||
}
|
}
|
||||||
} while (emac->packets_remain);
|
} while (emac->packets_remain);
|
||||||
}
|
}
|
||||||
@ -997,7 +949,7 @@ esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config,
|
|||||||
mac_config->rx_task_prio, &emac->rx_task_hdl, core_num);
|
mac_config->rx_task_prio, &emac->rx_task_hdl, core_num);
|
||||||
ESP_GOTO_ON_FALSE(xReturned == pdPASS, NULL, err, TAG, "create dm9051 task failed");
|
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);
|
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");
|
ESP_GOTO_ON_FALSE(emac->rx_buffer, NULL, err, TAG, "RX buffer allocation failed");
|
||||||
|
|
||||||
if (emac->int_gpio_num < 0) {
|
if (emac->int_gpio_num < 0) {
|
||||||
|
Loading…
Reference in New Issue
Block a user