eth: support W5500 MAC RAW mode

W5500 features an Ethernet MAC+PHY, plus a HW SOCKET implementation.
But in IDF, we only use software TCP/IP stack, which means we have
to bypass the HW TCP/IP stack in W5500.
So we enables the MAC RAW mode in W5500.
This commit is contained in:
morris
2020-11-06 12:17:18 +08:00
parent c5fe158929
commit 1e905acacf
10 changed files with 1076 additions and 147 deletions

View File

@@ -32,6 +32,11 @@ if(CONFIG_ETH_ENABLED)
"src/esp_eth_phy_dm9051.c") "src/esp_eth_phy_dm9051.c")
endif() endif()
if(CONFIG_ETH_SPI_ETHERNET_W5500)
list(APPEND srcs "src/esp_eth_mac_w5500.c"
"src/esp_eth_phy_w5500.c")
endif()
if(CONFIG_ETH_USE_OPENETH) if(CONFIG_ETH_USE_OPENETH)
list(APPEND srcs "src/esp_eth_mac_openeth.c") list(APPEND srcs "src/esp_eth_mac_openeth.c")
endif() endif()

View File

@@ -46,7 +46,7 @@ menu "Ethernet"
This clock can be routed to the external PHY device. This clock can be routed to the external PHY device.
ESP32 supports to route the RMII clock to GPIO0/16/17. ESP32 supports to route the RMII clock to GPIO0/16/17.
endchoice endchoice
endif endif # ETH_PHY_INTERFACE_RMII
if ETH_RMII_CLK_INPUT if ETH_RMII_CLK_INPUT
config ETH_RMII_CLK_IN_GPIO config ETH_RMII_CLK_IN_GPIO
@@ -55,7 +55,7 @@ menu "Ethernet"
default 0 default 0
help help
ESP32 only supports GPIO0 to input the RMII clock. ESP32 only supports GPIO0 to input the RMII clock.
endif endif # ETH_RMII_CLK_INPUT
if ETH_RMII_CLK_OUTPUT if ETH_RMII_CLK_OUTPUT
config ETH_RMII_CLK_OUTPUT_GPIO0 config ETH_RMII_CLK_OUTPUT_GPIO0
@@ -77,8 +77,8 @@ menu "Ethernet"
default 17 default 17
help help
Set the GPIO number to output RMII Clock. Set the GPIO number to output RMII Clock.
endif endif # !ETH_RMII_CLK_OUTPUT_GPIO0
endif endif # ETH_RMII_CLK_OUTPUT
config ETH_DMA_BUFFER_SIZE config ETH_DMA_BUFFER_SIZE
int "Ethernet DMA buffer size (Byte)" int "Ethernet DMA buffer size (Byte)"
@@ -102,7 +102,7 @@ menu "Ethernet"
help help
Number of DMA transmit buffers. Each buffer's size is ETH_DMA_BUFFER_SIZE. Number of DMA transmit buffers. Each buffer's size is ETH_DMA_BUFFER_SIZE.
Larger number of buffers could increase throughput somehow. Larger number of buffers could increase throughput somehow.
endif endif # ETH_USE_ESP32_EMAC
menuconfig ETH_USE_SPI_ETHERNET menuconfig ETH_USE_SPI_ETHERNET
bool "Support SPI to Ethernet Module" bool "Support SPI to Ethernet Module"
@@ -117,8 +117,17 @@ menu "Ethernet"
help help
DM9051 is a fast Ethernet controller with an SPI interface. DM9051 is a fast Ethernet controller with an SPI interface.
It's also integrated with a 10/100M PHY and MAC. It's also integrated with a 10/100M PHY and MAC.
Select to enable DM9051 driver. Select this to enable DM9051 driver.
endif
config ETH_SPI_ETHERNET_W5500
bool "Use W5500 (MAC RAW)"
help
W5500 is a HW TCP/IP embedded Ethernet controller.
TCP/IP stack, 10/100 Ethernet MAC and PHY are embedded in a single chip.
However the driver in ESP-IDF only enables the RAW MAC mode,
making it compatible with the software TCP/IP stack.
Say yes to enable W5500 driver.
endif # ETH_USE_SPI_ETHERNET
menuconfig ETH_USE_OPENETH menuconfig ETH_USE_OPENETH
bool "Support OpenCores Ethernet MAC (for use with QEMU)" bool "Support OpenCores Ethernet MAC (for use with QEMU)"
@@ -143,5 +152,5 @@ menu "Ethernet"
default 1 default 1
help help
Number of DMA transmit buffers, each buffer is 1600 bytes. Number of DMA transmit buffers, each buffer is 1600 bytes.
endif endif # ETH_USE_OPENETH
endmenu endmenu

View File

@@ -12,6 +12,10 @@ ifndef CONFIG_ETH_SPI_ETHERNET_DM9051
COMPONENT_OBJEXCLUDE += src/esp_eth_mac_dm9051.o src/esp_eth_phy_dm9051.o COMPONENT_OBJEXCLUDE += src/esp_eth_mac_dm9051.o src/esp_eth_phy_dm9051.o
endif endif
ifndef CONFIG_ETH_SPI_ETHERNET_W5500
COMPONENT_OBJEXCLUDE += src/esp_eth_mac_w5500.o src/esp_eth_phy_w5500.o
endif
ifndef CONFIG_ETH_USE_OPENETH ifndef CONFIG_ETH_USE_OPENETH
COMPONENT_OBJEXCLUDE += src/esp_eth_mac_openeth.o COMPONENT_OBJEXCLUDE += src/esp_eth_mac_openeth.o
endif endif

View File

@@ -16,9 +16,6 @@
#include <stdbool.h> #include <stdbool.h>
#include "esp_eth_com.h" #include "esp_eth_com.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#if CONFIG_ETH_USE_SPI_ETHERNET
#include "driver/spi_master.h"
#endif
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -339,8 +336,8 @@ esp_eth_mac_t *esp_eth_mac_new_esp32(const eth_mac_config_t *config);
* *
*/ */
typedef struct { typedef struct {
spi_device_handle_t spi_hdl; /*!< Handle of SPI device driver */ void *spi_hdl; /*!< Handle of SPI device driver */
int int_gpio_num; /*!< Interrupt GPIO number */ int int_gpio_num; /*!< Interrupt GPIO number */
} eth_dm9051_config_t; } eth_dm9051_config_t;
/** /**
@@ -366,6 +363,39 @@ typedef struct {
esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config, const eth_mac_config_t *mac_config); esp_eth_mac_t *esp_eth_mac_new_dm9051(const eth_dm9051_config_t *dm9051_config, const eth_mac_config_t *mac_config);
#endif // CONFIG_ETH_SPI_ETHERNET_DM9051 #endif // CONFIG_ETH_SPI_ETHERNET_DM9051
#if CONFIG_ETH_SPI_ETHERNET_W5500
/**
* @brief W5500 specific configuration
*
*/
typedef struct {
void *spi_hdl; /*!< Handle of SPI device driver */
int int_gpio_num; /*!< Interrupt GPIO number */
} eth_w5500_config_t;
/**
* @brief Default W5500 specific configuration
*
*/
#define ETH_W5500_DEFAULT_CONFIG(spi_device) \
{ \
.spi_hdl = spi_device, \
.int_gpio_num = 4, \
}
/**
* @brief Create W5500 Ethernet MAC instance
*
* @param w5500_config: W5500 specific configuration
* @param mac_config: Ethernet MAC configuration
*
* @return
* - instance: create MAC instance successfully
* - NULL: create MAC instance failed because some error occurred
*/
esp_eth_mac_t *esp_eth_mac_new_w5500(const eth_w5500_config_t *w5500_config, const eth_mac_config_t *mac_config);
#endif // CONFIG_ETH_SPI_ETHERNET_W5500
#if CONFIG_ETH_USE_OPENETH #if CONFIG_ETH_USE_OPENETH
/** /**
* @brief Create OpenCores Ethernet MAC instance * @brief Create OpenCores Ethernet MAC instance

View File

@@ -277,6 +277,18 @@ esp_eth_phy_t *esp_eth_phy_new_ksz8041(const eth_phy_config_t *config);
esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config); esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config);
#endif #endif
#if CONFIG_ETH_SPI_ETHERNET_W5500
/**
* @brief Create a PHY instance of W5500
*
* @param[in] config: configuration of PHY
*
* @return
* - instance: create PHY instance successfully
* - NULL: create PHY instance failed because some error occurred
*/
esp_eth_phy_t *esp_eth_phy_new_w5500(const eth_phy_config_t *config);
#endif
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -408,7 +408,7 @@ static void emac_dm9051_task(void *arg)
uint32_t length = 0; uint32_t length = 0;
while (1) { while (1) {
// block indefinitely until some task notifies me // block indefinitely until some task notifies me
ulTaskNotifyTake(pdFALSE, portMAX_DELAY); ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
/* clear interrupt status */ /* clear interrupt status */
dm9051_register_read(emac, DM9051_ISR, &status); dm9051_register_read(emac, DM9051_ISR, &status);
dm9051_register_write(emac, DM9051_ISR, status); dm9051_register_write(emac, DM9051_ISR, status);
@@ -686,13 +686,12 @@ static esp_err_t emac_dm9051_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t
rx_len = header.length_low + (header.length_high << 8); rx_len = header.length_low + (header.length_high << 8);
/* check if the buffer can hold all the incoming data */ /* check if the buffer can hold all the incoming data */
if (*length < rx_len - 4) { if (*length < rx_len - 4) {
ESP_LOGE(TAG, "buffer size too small"); ESP_LOGE(TAG, "buffer size too small, needs %d", rx_len - 4);
/* tell upper layer the size we need */ /* tell upper layer the size we need */
*length = rx_len - 4; *length = rx_len - 4;
ret = ESP_ERR_INVALID_SIZE; ret = ESP_ERR_INVALID_SIZE;
goto err; goto err;
} }
MAC_CHECK(*length >= rx_len - 4, "buffer size too small", err, ESP_ERR_INVALID_SIZE);
MAC_CHECK(dm9051_memory_read(emac, (uint8_t *)&header, sizeof(header)) == ESP_OK, MAC_CHECK(dm9051_memory_read(emac, (uint8_t *)&header, sizeof(header)) == ESP_OK,
"read rx header failed", err, ESP_FAIL); "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(dm9051_memory_read(emac, buf, rx_len) == ESP_OK, "read rx data failed", err, ESP_FAIL);

View File

@@ -0,0 +1,673 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include <stdlib.h>
#include <sys/cdefs.h>
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_eth.h"
#include "esp_system.h"
#include "esp_intr_alloc.h"
#include "esp_heap_caps.h"
#include "esp_rom_gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "hal/cpu_hal.h"
#include "w5500.h"
#include "sdkconfig.h"
static const char *TAG = "w5500-mac";
#define MAC_CHECK(a, str, goto_tag, ret_value, ...) \
do { \
if (!(a)) { \
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
ret = ret_value; \
goto goto_tag; \
} \
} while (0)
#define W5500_SPI_LOCK_TIMEOUT_MS (50)
#define W5500_TX_MEM_SIZE (0x4000)
#define W5500_RX_MEM_SIZE (0x4000)
typedef struct {
esp_eth_mac_t parent;
esp_eth_mediator_t *eth;
spi_device_handle_t spi_hdl;
SemaphoreHandle_t spi_lock;
TaskHandle_t rx_task_hdl;
uint32_t sw_reset_timeout_ms;
int int_gpio_num;
uint8_t addr[6];
bool packets_remain;
} emac_w5500_t;
static inline bool w5500_lock(emac_w5500_t *emac)
{
return xSemaphoreTake(emac->spi_lock, pdMS_TO_TICKS(W5500_SPI_LOCK_TIMEOUT_MS)) == pdTRUE;
}
static inline bool w5500_unlock(emac_w5500_t *emac)
{
return xSemaphoreGive(emac->spi_lock) == pdTRUE;
}
static esp_err_t w5500_write(emac_w5500_t *emac, uint32_t address, const void *value, uint32_t len)
{
esp_err_t ret = ESP_OK;
spi_transaction_t trans = {
.cmd = (address >> W5500_ADDR_OFFSET),
.addr = ((address & 0xFFFF) | (W5500_ACCESS_MODE_WRITE << W5500_RWB_OFFSET) | W5500_SPI_OP_MODE_VDM),
.length = 8 * len,
.tx_buffer = value
};
if (w5500_lock(emac)) {
if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) {
ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__);
ret = ESP_FAIL;
}
w5500_unlock(emac);
} else {
ret = ESP_ERR_TIMEOUT;
}
return ret;
}
static esp_err_t w5500_read(emac_w5500_t *emac, uint32_t address, void *value, uint32_t len)
{
esp_err_t ret = ESP_OK;
spi_transaction_t trans = {
.cmd = (address >> W5500_ADDR_OFFSET),
.addr = ((address & 0xFFFF) | (W5500_ACCESS_MODE_READ << W5500_RWB_OFFSET) | W5500_SPI_OP_MODE_VDM),
.length = 8 * len,
.rx_buffer = value
};
if (w5500_lock(emac)) {
if (spi_device_polling_transmit(emac->spi_hdl, &trans) != ESP_OK) {
ESP_LOGE(TAG, "%s(%d): spi transmit failed", __FUNCTION__, __LINE__);
ret = ESP_FAIL;
}
w5500_unlock(emac);
} else {
ret = ESP_ERR_TIMEOUT;
}
return ret;
}
static esp_err_t w5500_send_command(emac_w5500_t *emac, uint8_t command, uint32_t timeout_ms)
{
esp_err_t ret = ESP_OK;
MAC_CHECK(w5500_write(emac, W5500_REG_SOCK_CR(0), &command, sizeof(command)) == ESP_OK, "write SCR failed", err, ESP_FAIL);
// after W5500 accepts the command, the command register will be cleared automatically
uint32_t to = 0;
for (to = 0; to < timeout_ms / 10; to++) {
MAC_CHECK(w5500_read(emac, W5500_REG_SOCK_CR(0), &command, sizeof(command)) == ESP_OK, "read SCR failed", err, ESP_FAIL);
if (!command) {
break;
}
vTaskDelay(pdMS_TO_TICKS(10));
}
MAC_CHECK(to < timeout_ms / 10, "send command timeout", err, ESP_ERR_TIMEOUT);
err:
return ret;
}
static esp_err_t w5500_get_tx_free_size(emac_w5500_t *emac, uint16_t *size)
{
esp_err_t ret = ESP_OK;
uint16_t free0, free1 = 0;
// read TX_FSR register more than once, until we get the same value
// this is a trick because we might be interrupted between reading the high/low part of the TX_FSR register (16 bits in length)
do {
MAC_CHECK(w5500_read(emac, W5500_REG_SOCK_TX_FSR(0), &free0, sizeof(free0)) == ESP_OK, "read TX FSR failed", err, ESP_FAIL);
MAC_CHECK(w5500_read(emac, W5500_REG_SOCK_TX_FSR(0), &free1, sizeof(free1)) == ESP_OK, "read TX FSR failed", err, ESP_FAIL);
} while (free0 != free1);
*size = __builtin_bswap16(free0);
err:
return ret;
}
static esp_err_t w5500_get_rx_received_size(emac_w5500_t *emac, uint16_t *size)
{
esp_err_t ret = ESP_OK;
uint16_t received0, received1 = 0;
do {
MAC_CHECK(w5500_read(emac, W5500_REG_SOCK_RX_RSR(0), &received0, sizeof(received0)) == ESP_OK, "read RX RSR failed", err, ESP_FAIL);
MAC_CHECK(w5500_read(emac, W5500_REG_SOCK_RX_RSR(0), &received1, sizeof(received1)) == ESP_OK, "read RX RSR failed", err, ESP_FAIL);
} while (received0 != received1);
*size = __builtin_bswap16(received0);
err:
return ret;
}
static esp_err_t w5500_write_buffer(emac_w5500_t *emac, const void *buffer, uint32_t len, uint16_t offset)
{
esp_err_t ret = ESP_OK;
uint32_t remain = len;
const uint8_t *buf = buffer;
offset %= W5500_TX_MEM_SIZE;
if (offset + len > W5500_TX_MEM_SIZE) {
remain = (offset + len) % W5500_TX_MEM_SIZE;
len = W5500_TX_MEM_SIZE - offset;
MAC_CHECK(w5500_write(emac, W5500_MEM_SOCK_TX(0, offset), buf, len) == ESP_OK, "write TX buffer failed", err, ESP_FAIL);
offset += len;
buf += len;
}
MAC_CHECK(w5500_write(emac, W5500_MEM_SOCK_TX(0, offset), buf, remain) == ESP_OK, "write TX buffer failed", err, ESP_FAIL);
err:
return ret;
}
static esp_err_t w5500_read_buffer(emac_w5500_t *emac, void *buffer, uint32_t len, uint16_t offset)
{
esp_err_t ret = ESP_OK;
uint32_t remain = len;
uint8_t *buf = buffer;
offset %= W5500_RX_MEM_SIZE;
if (offset + len > W5500_RX_MEM_SIZE) {
remain = (offset + len) % W5500_RX_MEM_SIZE;
len = W5500_RX_MEM_SIZE - offset;
MAC_CHECK(w5500_read(emac, W5500_MEM_SOCK_RX(0, offset), buf, len) == ESP_OK, "read RX buffer failed", err, ESP_FAIL);
offset += len;
buf += len;
}
MAC_CHECK(w5500_read(emac, W5500_MEM_SOCK_RX(0, offset), buf, remain) == ESP_OK, "read RX buffer failed", err, ESP_FAIL);
err:
return ret;
}
static esp_err_t w5500_set_mac_addr(emac_w5500_t *emac)
{
esp_err_t ret = ESP_OK;
MAC_CHECK(w5500_write(emac, W5500_REG_MAC, emac->addr, 6) == ESP_OK, "write MAC address register failed", err, ESP_FAIL);
err:
return ret;
}
static esp_err_t w5500_reset(emac_w5500_t *emac)
{
esp_err_t ret = ESP_OK;
/* software reset */
uint8_t mr = W5500_MR_RST; // Set RST bit (auto clear)
MAC_CHECK(w5500_write(emac, W5500_REG_MR, &mr, sizeof(mr)) == ESP_OK, "write MR failed", err, ESP_FAIL);
uint32_t to = 0;
for (to = 0; to < emac->sw_reset_timeout_ms / 10; to++) {
MAC_CHECK(w5500_read(emac, W5500_REG_MR, &mr, sizeof(mr)) == ESP_OK, "read MR failed", err, ESP_FAIL);
if (!(mr & W5500_MR_RST)) {
break;
}
vTaskDelay(pdMS_TO_TICKS(10));
}
MAC_CHECK(to < emac->sw_reset_timeout_ms / 10, "reset timeout", err, ESP_ERR_TIMEOUT);
err:
return ret;
}
static esp_err_t w5500_verify_id(emac_w5500_t *emac)
{
esp_err_t ret = ESP_OK;
uint8_t version = 0;
MAC_CHECK(w5500_read(emac, W5500_REG_VERSIONR, &version, sizeof(version)) == ESP_OK,
"read VERSIONR failed", err, ESP_FAIL);
// W5500 doesn't have chip ID, we just print the version number instead
ESP_LOGI(TAG, "version=%x", version);
err:
return ret;
}
static esp_err_t w5500_setup_default(emac_w5500_t *emac)
{
esp_err_t ret = ESP_OK;
uint8_t reg_value = 16;
// Only SOCK0 can be used as MAC RAW mode, so we give the whole buffer (16KB TX and 16KB RX) to SOCK0
MAC_CHECK(w5500_write(emac, W5500_REG_SOCK_RXBUF_SIZE(0), &reg_value, sizeof(reg_value)) == ESP_OK, "set rx buffer size failed", err, ESP_FAIL);
MAC_CHECK(w5500_write(emac, W5500_REG_SOCK_TXBUF_SIZE(0), &reg_value, sizeof(reg_value)) == ESP_OK, "set tx buffer size failed", err, ESP_FAIL);
reg_value = 0;
for (int i = 1; i < 8; i++) {
MAC_CHECK(w5500_write(emac, W5500_REG_SOCK_RXBUF_SIZE(i), &reg_value, sizeof(reg_value)) == ESP_OK, "set rx buffer size failed", err, ESP_FAIL);
MAC_CHECK(w5500_write(emac, W5500_REG_SOCK_TXBUF_SIZE(i), &reg_value, sizeof(reg_value)) == ESP_OK, "set tx buffer size failed", err, ESP_FAIL);
}
/* Enable ping block, disable PPPoE, WOL */
reg_value = W5500_MR_PB;
MAC_CHECK(w5500_write(emac, W5500_REG_MR, &reg_value, sizeof(reg_value)) == ESP_OK, "write MR failed", err, ESP_FAIL);
/* Disable interrupt for all sockets by default */
reg_value = 0;
MAC_CHECK(w5500_write(emac, W5500_REG_SIMR, &reg_value, sizeof(reg_value)) == ESP_OK, "write SIMR failed", err, ESP_FAIL);
/* Enable MAC RAW mode for SOCK0, enable MAC filter, no blocking broadcast and multicast */
reg_value = W5500_SMR_MAC_RAW | W5500_SMR_MAC_FILTER;
MAC_CHECK(w5500_write(emac, W5500_REG_SOCK_MR(0), &reg_value, sizeof(reg_value)) == ESP_OK, "write SMR failed", err, ESP_FAIL);
/* Enable receive and send event for SOCK0 */
reg_value = W5500_SIR_RECV | W5500_SIR_SEND;
MAC_CHECK(w5500_write(emac, W5500_REG_SOCK_IMR(0), &reg_value, sizeof(reg_value)) == ESP_OK, "write SOCK0 IMR failed", err, ESP_FAIL);
err:
return ret;
}
static esp_err_t emac_w5500_start(esp_eth_mac_t *mac)
{
esp_err_t ret = ESP_OK;
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
uint8_t reg_value = 0;
/* open SOCK0 */
MAC_CHECK(w5500_send_command(emac, W5500_SCR_OPEN, 100) == ESP_OK, "issue OPEN command failed", err, ESP_FAIL);
/* enable interrupt for SOCK0 */
reg_value = W5500_SIMR_SOCK0;
MAC_CHECK(w5500_write(emac, W5500_REG_SIMR, &reg_value, sizeof(reg_value)) == ESP_OK, "write SIMR failed", err, ESP_FAIL);
err:
return ret;
}
static esp_err_t emac_w5500_stop(esp_eth_mac_t *mac)
{
esp_err_t ret = ESP_OK;
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
uint8_t reg_value = 0;
/* disable interrupt */
MAC_CHECK(w5500_write(emac, W5500_REG_SIMR, &reg_value, sizeof(reg_value)) == ESP_OK, "write SIMR failed", err, ESP_FAIL);
/* close SOCK0 */
MAC_CHECK(w5500_send_command(emac, W5500_SCR_CLOSE, 100) == ESP_OK, "issue CLOSE command failed", err, ESP_FAIL);
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) {
// block indefinitely until some task notifies me
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
/* 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;
MAC_CHECK(eth, "can't set mac's mediator to null", err, ESP_ERR_INVALID_ARG);
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
emac->eth = eth;
return ESP_OK;
err:
return ret;
}
static esp_err_t emac_w5500_write_phy_reg(esp_eth_mac_t *mac, uint32_t phy_addr, uint32_t phy_reg, uint32_t reg_value)
{
esp_err_t ret = ESP_OK;
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
// PHY register and MAC registers are mixed together in W5500
// The only PHY register is PHYCFGR
MAC_CHECK(phy_reg == W5500_REG_PHYCFGR, "wrong PHY register", err, ESP_FAIL);
MAC_CHECK(w5500_write(emac, W5500_REG_PHYCFGR, &reg_value, sizeof(uint8_t)) == ESP_OK, "write PHY register failed", err, ESP_FAIL);
err:
return ret;
}
static esp_err_t emac_w5500_read_phy_reg(esp_eth_mac_t *mac, uint32_t phy_addr, uint32_t phy_reg, uint32_t *reg_value)
{
esp_err_t ret = ESP_OK;
MAC_CHECK(reg_value, "can't set reg_value to null", err, ESP_ERR_INVALID_ARG);
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
// PHY register and MAC registers are mixed together in W5500
// The only PHY register is PHYCFGR
MAC_CHECK(phy_reg == W5500_REG_PHYCFGR, "wrong PHY register", err, ESP_FAIL);
MAC_CHECK(w5500_read(emac, W5500_REG_PHYCFGR, reg_value, sizeof(uint8_t)) == ESP_OK, "read PHY register failed", err, ESP_FAIL);
err:
return ret;
}
static esp_err_t emac_w5500_set_addr(esp_eth_mac_t *mac, uint8_t *addr)
{
esp_err_t ret = ESP_OK;
MAC_CHECK(addr, "invalid argument", err, ESP_ERR_INVALID_ARG);
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
memcpy(emac->addr, addr, 6);
MAC_CHECK(w5500_set_mac_addr(emac) == ESP_OK, "set mac address failed", err, ESP_FAIL);
err:
return ret;
}
static esp_err_t emac_w5500_get_addr(esp_eth_mac_t *mac, uint8_t *addr)
{
esp_err_t ret = ESP_OK;
MAC_CHECK(addr, "invalid argument", err, ESP_ERR_INVALID_ARG);
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
memcpy(addr, emac->addr, 6);
err:
return ret;
}
static esp_err_t emac_w5500_set_link(esp_eth_mac_t *mac, eth_link_t link)
{
esp_err_t ret = ESP_OK;
switch (link) {
case ETH_LINK_UP:
ESP_LOGD(TAG, "link is up");
MAC_CHECK(mac->start(mac) == ESP_OK, "w5500 start failed", err, ESP_FAIL);
break;
case ETH_LINK_DOWN:
ESP_LOGD(TAG, "link is down");
MAC_CHECK(mac->stop(mac) == ESP_OK, "w5500 stop failed", err, ESP_FAIL);
break;
default:
MAC_CHECK(false, "unknown link status", err, ESP_ERR_INVALID_ARG);
break;
}
err:
return ret;
}
static esp_err_t emac_w5500_set_speed(esp_eth_mac_t *mac, eth_speed_t speed)
{
esp_err_t ret = ESP_OK;
switch (speed) {
case ETH_SPEED_10M:
ESP_LOGD(TAG, "working in 10Mbps");
break;
case ETH_SPEED_100M:
ESP_LOGD(TAG, "working in 100Mbps");
break;
default:
MAC_CHECK(false, "unknown speed", err, ESP_ERR_INVALID_ARG);
break;
}
err:
return ret;
}
static esp_err_t emac_w5500_set_duplex(esp_eth_mac_t *mac, eth_duplex_t duplex)
{
esp_err_t ret = ESP_OK;
switch (duplex) {
case ETH_DUPLEX_HALF:
ESP_LOGD(TAG, "working in half duplex");
break;
case ETH_DUPLEX_FULL:
ESP_LOGD(TAG, "working in full duplex");
break;
default:
MAC_CHECK(false, "unknown duplex", err, ESP_ERR_INVALID_ARG);
break;
}
err:
return ret;
}
static esp_err_t emac_w5500_set_promiscuous(esp_eth_mac_t *mac, bool enable)
{
esp_err_t ret = ESP_OK;
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
uint8_t smr = 0;
MAC_CHECK(w5500_read(emac, W5500_REG_SOCK_MR(0), &smr, sizeof(smr)) == ESP_OK, "read SMR failed", err, ESP_FAIL);
if (enable) {
smr &= ~W5500_SMR_MAC_FILTER;
} else {
smr |= W5500_SMR_MAC_FILTER;
}
MAC_CHECK(w5500_write(emac, W5500_REG_SOCK_MR(0), &smr, sizeof(smr)) == ESP_OK, "write SMR failed", err, ESP_FAIL);
err:
return ret;
}
static esp_err_t emac_w5500_enable_flow_ctrl(esp_eth_mac_t *mac, bool enable)
{
/* w5500 doesn't support flow control function, so accept any value */
return ESP_ERR_NOT_SUPPORTED;
}
static esp_err_t emac_w5500_set_peer_pause_ability(esp_eth_mac_t *mac, uint32_t ability)
{
/* w5500 doesn't suppport PAUSE function, so accept any value */
return ESP_ERR_NOT_SUPPORTED;
}
static esp_err_t emac_w5500_transmit(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;
// 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);
MAC_CHECK(length <= free_size, "free size (%d) < send length (%d)", err, ESP_FAIL, free_size, length);
// get current write pointer
MAC_CHECK(w5500_read(emac, W5500_REG_SOCK_TX_WR(0), &offset, sizeof(offset)) == ESP_OK, "read TX WR failed", err, ESP_FAIL);
offset = __builtin_bswap16(offset);
// copy data to tx memory
MAC_CHECK(w5500_write_buffer(emac, buf, length, offset) == ESP_OK, "write frame failed", err, ESP_FAIL);
// update write pointer
offset += length;
offset = __builtin_bswap16(offset);
MAC_CHECK(w5500_write(emac, W5500_REG_SOCK_TX_WR(0), &offset, sizeof(offset)) == ESP_OK, "write TX WR failed", err, ESP_FAIL);
// issue SEND command
MAC_CHECK(w5500_send_command(emac, W5500_SCR_SEND, 100) == ESP_OK, "issue SEND command failed", err, ESP_FAIL);
// pooling the TX done event
uint8_t status = 0;
do {
MAC_CHECK(w5500_read(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status)) == ESP_OK, "read SOCK0 IR failed", err, ESP_FAIL);
} while (!(status & W5500_SIR_SEND));
// clear the event bit
status = W5500_SIR_SEND;
MAC_CHECK(w5500_write(emac, W5500_REG_SOCK_IR(0), &status, sizeof(status)) == ESP_OK, "write SOCK0 IR failed", err, ESP_FAIL);
err:
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 remain_bytes = 0;
emac->packets_remain = false;
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 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
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 = rx_len;
err:
return ret;
}
static esp_err_t emac_w5500_init(esp_eth_mac_t *mac)
{
esp_err_t ret = ESP_OK;
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
esp_eth_mediator_t *eth = emac->eth;
esp_rom_gpio_pad_select_gpio(emac->int_gpio_num);
gpio_set_direction(emac->int_gpio_num, GPIO_MODE_INPUT);
gpio_set_pull_mode(emac->int_gpio_num, GPIO_PULLUP_ONLY);
gpio_set_intr_type(emac->int_gpio_num, GPIO_INTR_NEGEDGE); // active low
gpio_intr_enable(emac->int_gpio_num);
gpio_isr_handler_add(emac->int_gpio_num, w5500_isr_handler, emac);
MAC_CHECK(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL) == ESP_OK, "lowlevel init failed", err, ESP_FAIL);
/* reset w5500 */
MAC_CHECK(w5500_reset(emac) == ESP_OK, "reset w5500 failed", err, ESP_FAIL);
/* verify chip id */
MAC_CHECK(w5500_verify_id(emac) == ESP_OK, "vefiry chip ID failed", err, ESP_FAIL);
/* default setup of internal registers */
MAC_CHECK(w5500_setup_default(emac) == ESP_OK, "w5500 default setup failed", err, ESP_FAIL);
return ESP_OK;
err:
gpio_isr_handler_remove(emac->int_gpio_num);
gpio_reset_pin(emac->int_gpio_num);
eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
return ret;
}
static esp_err_t emac_w5500_deinit(esp_eth_mac_t *mac)
{
emac_w5500_t *emac = __containerof(mac, emac_w5500_t, parent);
esp_eth_mediator_t *eth = emac->eth;
mac->stop(mac);
gpio_isr_handler_remove(emac->int_gpio_num);
gpio_reset_pin(emac->int_gpio_num);
eth->on_state_changed(eth, ETH_STATE_DEINIT, NULL);
return ESP_OK;
}
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);
free(emac);
return ESP_OK;
}
esp_eth_mac_t *esp_eth_mac_new_w5500(const eth_w5500_config_t *w5500_config, const eth_mac_config_t *mac_config)
{
esp_eth_mac_t *ret = NULL;
emac_w5500_t *emac = NULL;
MAC_CHECK(w5500_config && mac_config, "invalid argument", err, NULL);
emac = calloc(1, sizeof(emac_w5500_t));
MAC_CHECK(emac, "no mem for MAC instance", err, NULL);
/* w5500 driver is interrupt driven */
MAC_CHECK(w5500_config->int_gpio_num >= 0, "invalid interrupt gpio number", err, NULL);
/* bind methods and attributes */
emac->sw_reset_timeout_ms = mac_config->sw_reset_timeout_ms;
emac->int_gpio_num = w5500_config->int_gpio_num;
emac->spi_hdl = w5500_config->spi_hdl;
emac->parent.set_mediator = emac_w5500_set_mediator;
emac->parent.init = emac_w5500_init;
emac->parent.deinit = emac_w5500_deinit;
emac->parent.start = emac_w5500_start;
emac->parent.stop = emac_w5500_stop;
emac->parent.del = emac_w5500_del;
emac->parent.write_phy_reg = emac_w5500_write_phy_reg;
emac->parent.read_phy_reg = emac_w5500_read_phy_reg;
emac->parent.set_addr = emac_w5500_set_addr;
emac->parent.get_addr = emac_w5500_get_addr;
emac->parent.set_speed = emac_w5500_set_speed;
emac->parent.set_duplex = emac_w5500_set_duplex;
emac->parent.set_link = emac_w5500_set_link;
emac->parent.set_promiscuous = emac_w5500_set_promiscuous;
emac->parent.set_peer_pause_ability = emac_w5500_set_peer_pause_ability;
emac->parent.enable_flow_ctrl = emac_w5500_enable_flow_ctrl;
emac->parent.transmit = emac_w5500_transmit;
emac->parent.receive = emac_w5500_receive;
/* create mutex */
emac->spi_lock = xSemaphoreCreateMutex();
MAC_CHECK(emac->spi_lock, "create lock failed", err, NULL);
/* create w5500 task */
BaseType_t core_num = tskNO_AFFINITY;
if (mac_config->flags & ETH_MAC_FLAG_PIN_TO_CORE) {
core_num = cpu_hal_get_core_id();
}
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);
return &(emac->parent);
err:
if (emac) {
if (emac->rx_task_hdl) {
vTaskDelete(emac->rx_task_hdl);
}
if (emac->spi_lock) {
vSemaphoreDelete(emac->spi_lock);
}
free(emac);
}
return ret;
}

View File

@@ -0,0 +1,252 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include <stdlib.h>
#include <sys/cdefs.h>
#include "esp_log.h"
#include "esp_eth.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_rom_gpio.h"
#include "esp_rom_sys.h"
#include "w5500.h"
static const char *TAG = "w5500-phy";
#define PHY_CHECK(a, str, goto_tag, ...) \
do \
{ \
if (!(a)) \
{ \
ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
goto goto_tag; \
} \
} while (0)
/***************Vendor Specific Register***************/
/**
* @brief PHYCFGR(PHY Configuration Register)
*
*/
typedef union {
struct {
uint8_t link: 1; /*!< Link status */
uint8_t speed: 1; /*!< Speed status */
uint8_t duplex: 1; /*!< Duplex status */
uint8_t opmode: 3; /*!< Operation mode */
uint8_t opsel: 1; /*!< Operation select */
uint8_t reset: 1; /*!< Reset, when this bit is '0', PHY will get reset */
};
uint8_t val;
} phycfg_reg_t;
typedef struct {
esp_eth_phy_t parent;
esp_eth_mediator_t *eth;
uint32_t addr;
uint32_t reset_timeout_ms;
uint32_t autonego_timeout_ms;
eth_link_t link_status;
int reset_gpio_num;
} phy_w5500_t;
static esp_err_t w5500_update_link_duplex_speed(phy_w5500_t *w5500)
{
esp_eth_mediator_t *eth = w5500->eth;
eth_speed_t speed = ETH_SPEED_10M;
eth_duplex_t duplex = ETH_DUPLEX_HALF;
phycfg_reg_t phycfg;
PHY_CHECK(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)) == ESP_OK, "read PHYCFG failed", err);
eth_link_t link = phycfg.link ? ETH_LINK_UP : ETH_LINK_DOWN;
/* check if link status changed */
if (w5500->link_status != link) {
/* when link up, read negotiation result */
if (link == ETH_LINK_UP) {
if (phycfg.speed) {
speed = ETH_SPEED_100M;
} else {
speed = ETH_SPEED_10M;
}
if (phycfg.duplex) {
duplex = ETH_DUPLEX_FULL;
} else {
duplex = ETH_DUPLEX_HALF;
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_SPEED, (void *)speed) == ESP_OK,
"change speed failed", err);
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_DUPLEX, (void *)duplex) == ESP_OK,
"change duplex failed", err);
}
PHY_CHECK(eth->on_state_changed(eth, ETH_STATE_LINK, (void *)link) == ESP_OK,
"change link failed", err);
w5500->link_status = link;
}
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t w5500_set_mediator(esp_eth_phy_t *phy, esp_eth_mediator_t *eth)
{
PHY_CHECK(eth, "can't set mediator to null", err);
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
w5500->eth = eth;
return ESP_OK;
err:
return ESP_ERR_INVALID_ARG;
}
static esp_err_t w5500_get_link(esp_eth_phy_t *phy)
{
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
/* Updata information about link, speed, duplex */
PHY_CHECK(w5500_update_link_duplex_speed(w5500) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t w5500_reset(esp_eth_phy_t *phy)
{
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
w5500->link_status = ETH_LINK_DOWN;
esp_eth_mediator_t *eth = w5500->eth;
phycfg_reg_t phycfg;
PHY_CHECK(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)) == ESP_OK, "read PHYCFG failed", err);
phycfg.reset = 0; // set to '0' will reset internal PHY
PHY_CHECK(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val) == ESP_OK, "write PHYCFG failed", err);
vTaskDelay(pdMS_TO_TICKS(10));
phycfg.reset = 1; // set to '1' after reset
PHY_CHECK(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val) == ESP_OK, "write PHYCFG failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t w5500_reset_hw(esp_eth_phy_t *phy)
{
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
// set reset_gpio_num to a negative value can skip hardware reset phy chip
if (w5500->reset_gpio_num >= 0) {
esp_rom_gpio_pad_select_gpio(w5500->reset_gpio_num);
gpio_set_direction(w5500->reset_gpio_num, GPIO_MODE_OUTPUT);
gpio_set_level(w5500->reset_gpio_num, 0);
esp_rom_delay_us(100); // insert min input assert time
gpio_set_level(w5500->reset_gpio_num, 1);
}
return ESP_OK;
}
static esp_err_t w5500_negotiate(esp_eth_phy_t *phy)
{
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
esp_eth_mediator_t *eth = w5500->eth;
phycfg_reg_t phycfg;
PHY_CHECK(eth->phy_reg_read(eth, w5500->addr, W5500_REG_PHYCFGR, (uint32_t *) & (phycfg.val)) == ESP_OK, "read PHYCFG failed", err);
phycfg.opsel = 1; // PHY working mode configured by register
phycfg.opmode = 7; // all capable, auto-negotiation enabled
PHY_CHECK(eth->phy_reg_write(eth, w5500->addr, W5500_REG_PHYCFGR, phycfg.val) == ESP_OK, "write PHYCFG failed", err);
/* Update information about link, speed, duplex */
PHY_CHECK(w5500_update_link_duplex_speed(w5500) == ESP_OK, "update link duplex speed failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t w5500_pwrctl(esp_eth_phy_t *phy, bool enable)
{
// power control is not supported for W5500 internal PHY
return ESP_OK;
}
static esp_err_t w5500_set_addr(esp_eth_phy_t *phy, uint32_t addr)
{
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
w5500->addr = addr;
return ESP_OK;
}
static esp_err_t w5500_get_addr(esp_eth_phy_t *phy, uint32_t *addr)
{
PHY_CHECK(addr, "addr can't be null", err);
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
*addr = w5500->addr;
return ESP_OK;
err:
return ESP_ERR_INVALID_ARG;
}
static esp_err_t w5500_del(esp_eth_phy_t *phy)
{
phy_w5500_t *w5500 = __containerof(phy, phy_w5500_t, parent);
free(w5500);
return ESP_OK;
}
static esp_err_t w5500_advertise_pause_ability(esp_eth_phy_t *phy, uint32_t ability)
{
// pause ability advertisement is not supported for W5500 internal PHY
return ESP_OK;
}
static esp_err_t w5500_init(esp_eth_phy_t *phy)
{
/* Power on Ethernet PHY */
PHY_CHECK(w5500_pwrctl(phy, true) == ESP_OK, "power control failed", err);
/* Reset Ethernet PHY */
PHY_CHECK(w5500_reset(phy) == ESP_OK, "reset failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
static esp_err_t w5500_deinit(esp_eth_phy_t *phy)
{
/* Power off Ethernet PHY */
PHY_CHECK(w5500_pwrctl(phy, false) == ESP_OK, "power control failed", err);
return ESP_OK;
err:
return ESP_FAIL;
}
esp_eth_phy_t *esp_eth_phy_new_w5500(const eth_phy_config_t *config)
{
PHY_CHECK(config, "invalid arguments", err);
phy_w5500_t *w5500 = calloc(1, sizeof(phy_w5500_t));
PHY_CHECK(w5500, "no mem for PHY instance", err);
w5500->addr = config->phy_addr;
w5500->reset_timeout_ms = config->reset_timeout_ms;
w5500->reset_gpio_num = config->reset_gpio_num;
w5500->link_status = ETH_LINK_DOWN;
w5500->autonego_timeout_ms = config->autonego_timeout_ms;
w5500->parent.reset = w5500_reset;
w5500->parent.reset_hw = w5500_reset_hw;
w5500->parent.init = w5500_init;
w5500->parent.deinit = w5500_deinit;
w5500->parent.set_mediator = w5500_set_mediator;
w5500->parent.negotiate = w5500_negotiate;
w5500->parent.get_link = w5500_get_link;
w5500->parent.pwrctl = w5500_pwrctl;
w5500->parent.get_addr = w5500_get_addr;
w5500->parent.set_addr = w5500_set_addr;
w5500->parent.advertise_pause_ability = w5500_advertise_pause_ability;
w5500->parent.del = w5500_del;
return &(w5500->parent);
err:
return NULL;
}

View File

@@ -0,0 +1,76 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#define W5500_ADDR_OFFSET (16) // Address length
#define W5500_BSB_OFFSET (3) // Block Select Bits offset
#define W5500_RWB_OFFSET (2) // Read Write Bits offset
#define W5500_BSB_COM_REG (0x00) // Common Register
#define W5500_BSB_SOCK_REG(s) ((s)*4+1) // Socket Register
#define W5500_BSB_SOCK_TX_BUF(s) ((s)*4+2) // Socket TX Buffer
#define W5500_BSB_SOCK_RX_BUF(s) ((s)*4+3) // Socket RX Buffer
#define W5500_ACCESS_MODE_READ (0) // Read Mode
#define W5500_ACCESS_MODE_WRITE (1) // Write Mode
#define W5500_SPI_OP_MODE_VDM (0x00) // Variable Data Length Mode (SPI frame is controlled by CS line)
#define W5500_SPI_OP_MODE_FDM_1 (0x01) // Fixed Data Length Mode, 1 Byte Length
#define W5500_SPI_OP_MODE_FDM_2 (0x02) // Fixed Data Length Mode, 2 Bytes Length
#define W5500_SPI_OP_MODE_FDM_4 (0x03) // Fixed Data Length Mode, 4 Bytes Length
#define W5500_MAKE_MAP(offset, bsb) ((offset) << W5500_ADDR_OFFSET | (bsb) << W5500_BSB_OFFSET)
#define W5500_REG_MR W5500_MAKE_MAP(0x0000, W5500_BSB_COM_REG) // Mode
#define W5500_REG_MAC W5500_MAKE_MAP(0x0009, W5500_BSB_COM_REG) // MAC Address
#define W5500_REG_IR W5500_MAKE_MAP(0x0015, W5500_BSB_COM_REG) // Interrupt
#define W5500_REG_IMR W5500_MAKE_MAP(0x0016, W5500_BSB_COM_REG) // Interrupt Mask
#define W5500_REG_SIR W5500_MAKE_MAP(0x0017, W5500_BSB_COM_REG) // Socket Interrupt
#define W5500_REG_SIMR W5500_MAKE_MAP(0x0018, W5500_BSB_COM_REG) // Socket Interrupt Mask
#define W5500_REG_RTR W5500_MAKE_MAP(0x0019, W5500_BSB_COM_REG) // Retry Time
#define W5500_REG_RCR W5500_MAKE_MAP(0x001B, W5500_BSB_COM_REG) // Retry Count
#define W5500_REG_PHYCFGR W5500_MAKE_MAP(0x002E, W5500_BSB_COM_REG) // PHY Configuration
#define W5500_REG_VERSIONR W5500_MAKE_MAP(0x0039, W5500_BSB_COM_REG) // Chip version
#define W5500_REG_SOCK_MR(s) W5500_MAKE_MAP(0x0000, W5500_BSB_SOCK_REG(s)) // Socket Mode
#define W5500_REG_SOCK_CR(s) W5500_MAKE_MAP(0x0001, W5500_BSB_SOCK_REG(s)) // Socket Command
#define W5500_REG_SOCK_IR(s) W5500_MAKE_MAP(0x0002, W5500_BSB_SOCK_REG(s)) // Socket Interrupt
#define W5500_REG_SOCK_SR(s) W5500_MAKE_MAP(0x0004, W5500_BSB_SOCK_REG(s)) // Socket Status
#define W5500_REG_SOCK_RXBUF_SIZE(s) W5500_MAKE_MAP(0x001E, W5500_BSB_SOCK_REG(s)) // Socket Receive Buffer Size
#define W5500_REG_SOCK_TXBUF_SIZE(s) W5500_MAKE_MAP(0x001F, W5500_BSB_SOCK_REG(s)) // Socket Transmit Buffer Size
#define W5500_REG_SOCK_TX_FSR(s) W5500_MAKE_MAP(0x0020, W5500_BSB_SOCK_REG(s)) // Socket TX Free Size
#define W5500_REG_SOCK_TX_RD(s) W5500_MAKE_MAP(0x0022, W5500_BSB_SOCK_REG(s)) // Socket TX Read Pointer
#define W5500_REG_SOCK_TX_WR(s) W5500_MAKE_MAP(0x0024, W5500_BSB_SOCK_REG(s)) // Socket TX Write Pointer
#define W5500_REG_SOCK_RX_RSR(s) W5500_MAKE_MAP(0x0026, W5500_BSB_SOCK_REG(s)) // Socket RX Received Size
#define W5500_REG_SOCK_RX_RD(s) W5500_MAKE_MAP(0x0028, W5500_BSB_SOCK_REG(s)) // Socket RX Read Pointer
#define W5500_REG_SOCK_RX_WR(s) W5500_MAKE_MAP(0x002A, W5500_BSB_SOCK_REG(s)) // Socket RX Write Pointer
#define W5500_REG_SOCK_IMR(s) W5500_MAKE_MAP(0x002C, W5500_BSB_SOCK_REG(s)) // Socket Interrupt Mask
#define W5500_MEM_SOCK_TX(s,addr) W5500_MAKE_MAP(addr, W5500_BSB_SOCK_TX_BUF(s)) // Socket TX buffer address
#define W5500_MEM_SOCK_RX(s,addr) W5500_MAKE_MAP(addr, W5500_BSB_SOCK_RX_BUF(s)) // Socket RX buffer address
#define W5500_MR_RST (1<<7) // Software reset
#define W5500_MR_PB (1<<4) // Ping block (block the response to a ping request)
#define W5500_SIMR_SOCK0 (1<<0) // Socket 0 interrupt
#define W5500_SMR_MAC_RAW (1<<2) // MAC RAW mode
#define W5500_SMR_MAC_FILTER (1<<7) // MAC filter
#define W5500_SCR_OPEN (0x01) // Open command
#define W5500_SCR_CLOSE (0x10) // Close command
#define W5500_SCR_SEND (0x20) // Send command
#define W5500_SCR_RECV (0x40) // Recv command
#define W5500_SIR_RECV (1<<2) // Receive done
#define W5500_SIR_SEND (1<<4) // Send done

View File

@@ -1,131 +0,0 @@
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "unity.h"
#include "test_utils.h"
#include "esp_event.h"
#include "esp_eth.h"
#include "esp_log.h"
#include "driver/gpio.h"
#if CONFIG_ETH_SPI_ETHERNET_DM9051
static const char *TAG = "dm9051_test";
#define ETH_START_BIT BIT(0)
#define ETH_STOP_BIT BIT(1)
#define ETH_CONNECT_BIT BIT(2)
#define ETH_GOT_IP_BIT BIT(3)
#define ETH_START_TIMEOUT_MS (10000)
#define ETH_CONNECT_TIMEOUT_MS (40000)
#define ETH_STOP_TIMEOUT_MS (10000)
#define ETH_GET_IP_TIMEOUT_MS (60000)
/** Event handler for Ethernet events */
static void eth_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
EventGroupHandle_t eth_event_group = (EventGroupHandle_t)arg;
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
xEventGroupSetBits(eth_event_group, ETH_CONNECT_BIT);
ESP_LOGI(TAG, "Ethernet Link Up");
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet Link Down");
break;
case ETHERNET_EVENT_START:
xEventGroupSetBits(eth_event_group, ETH_START_BIT);
ESP_LOGI(TAG, "Ethernet Started");
break;
case ETHERNET_EVENT_STOP:
xEventGroupSetBits(eth_event_group, ETH_STOP_BIT);
ESP_LOGI(TAG, "Ethernet Stopped");
break;
default:
break;
}
}
/** Event handler for IP_EVENT_ETH_GOT_IP */
static void got_ip_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
EventGroupHandle_t eth_event_group = (EventGroupHandle_t)arg;
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
const esp_netif_ip_info_t *ip_info = &event->ip_info;
ESP_LOGI(TAG, "Ethernet Got IP Address");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip));
ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask));
ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
xEventGroupSetBits(eth_event_group, ETH_GOT_IP_BIT);
}
TEST_CASE("dm9051 io test", "[ethernet][dm9051][ignore]")
{
spi_device_handle_t spi_handle = NULL;
spi_bus_config_t buscfg = {
.miso_io_num = 25,
.mosi_io_num = 23,
.sclk_io_num = 19,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
};
TEST_ESP_OK(spi_bus_initialize(HSPI_HOST, &buscfg, 1));
spi_device_interface_config_t devcfg = {
.command_bits = 1,
.address_bits = 7,
.mode = 0,
.clock_speed_hz = 20 * 1000 * 1000,
.spics_io_num = 22,
.queue_size = 20
};
TEST_ESP_OK(spi_bus_add_device(HSPI_HOST, &devcfg, &spi_handle));
gpio_install_isr_service(0);
test_case_uses_tcpip();
TEST_ESP_OK(esp_event_loop_create_default());
// create TCP/IP netif
esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH();
esp_netif_t *eth_netif = esp_netif_new(&cfg);
// set default handlers to do layer 3 (and up) stuffs
TEST_ESP_OK(esp_eth_set_default_handlers(eth_netif));
// register user defined event handers
TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));
TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL));
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(spi_handle);
esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&dm9051_config, &mac_config);
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config);
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
esp_eth_handle_t eth_handle = NULL;
// install Ethernet driver
TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle));
// combine driver with netif
void *glue = esp_eth_new_netif_glue(eth_handle);
TEST_ESP_OK(esp_netif_attach(eth_netif, glue));
// start Ethernet driver
TEST_ESP_OK(esp_eth_start(eth_handle));
/* wait for IP lease */
vTaskDelay(pdMS_TO_TICKS(portMAX_DELAY));
// stop Ethernet driver
TEST_ESP_OK(esp_eth_stop(eth_handle));
TEST_ESP_OK(esp_eth_del_netif_glue(glue));
TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
TEST_ESP_OK(phy->del(phy));
TEST_ESP_OK(mac->del(mac));
TEST_ESP_OK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, got_ip_event_handler));
TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
TEST_ESP_OK(esp_eth_clear_default_handlers(eth_netif));
esp_netif_destroy(eth_netif);
TEST_ESP_OK(esp_event_loop_delete_default());
TEST_ESP_OK(spi_bus_remove_device(spi_handle));
TEST_ESP_OK(spi_bus_free(HSPI_HOST));
}
#endif