From 197d5d73788b08166e6ac9038823da32503a1e9f Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 23 Dec 2019 17:06:02 +0800 Subject: [PATCH 1/3] ethernet: support phy addr auto detect --- components/esp_eth/CMakeLists.txt | 2 +- components/esp_eth/include/esp_eth_com.h | 13 ++++++ components/esp_eth/include/esp_eth_phy.h | 16 +++---- components/esp_eth/src/esp_eth_phy.c | 44 ++++++++++++++++++++ components/esp_eth/src/esp_eth_phy_dm9051.c | 5 ++- components/esp_eth/src/esp_eth_phy_dp83848.c | 15 +++---- components/esp_eth/src/esp_eth_phy_ip101.c | 13 +++--- components/esp_eth/src/esp_eth_phy_lan8720.c | 4 ++ components/esp_eth/src/esp_eth_phy_rtl8201.c | 4 ++ 9 files changed, 94 insertions(+), 22 deletions(-) create mode 100644 components/esp_eth/src/esp_eth_phy.c diff --git a/components/esp_eth/CMakeLists.txt b/components/esp_eth/CMakeLists.txt index 21b64cc7a9..90e69186ad 100644 --- a/components/esp_eth/CMakeLists.txt +++ b/components/esp_eth/CMakeLists.txt @@ -6,7 +6,7 @@ set(priv_requires) # If Ethernet disabled in Kconfig, this is a config-only component if(CONFIG_ETH_ENABLED) - set(srcs "src/esp_eth.c") + set(srcs "src/esp_eth.c" "src/esp_eth_phy.c") set(include "include") set(priv_requires "driver" "log") # require "driver" for using some GPIO APIs diff --git a/components/esp_eth/include/esp_eth_com.h b/components/esp_eth/include/esp_eth_com.h index 2f40c645b7..ad7aa185f8 100644 --- a/components/esp_eth/include/esp_eth_com.h +++ b/components/esp_eth/include/esp_eth_com.h @@ -206,6 +206,19 @@ typedef enum { */ ESP_EVENT_DECLARE_BASE(ETH_EVENT); +/** +* @brief Detect PHY address +* +* @param[in] eth: mediator of Ethernet driver +* @param[out] detected_addr: a valid address after detection +* @return +* - ESP_OK: detect phy address successfully +* - ESP_ERR_INVALID_ARG: invalid parameter +* - ESP_ERR_NOT_FOUND: can't detect any PHY device +* - ESP_FAIL: detect phy address failed because some error occurred +*/ +esp_err_t esp_eth_detect_phy_addr(esp_eth_mediator_t *eth, uint32_t *detected_addr); + #ifdef __cplusplus } #endif diff --git a/components/esp_eth/include/esp_eth_phy.h b/components/esp_eth/include/esp_eth_phy.h index 2e27350e3a..4aedff1112 100644 --- a/components/esp_eth/include/esp_eth_phy.h +++ b/components/esp_eth/include/esp_eth_phy.h @@ -21,6 +21,8 @@ extern "C" { #include "esp_eth_com.h" #include "sdkconfig.h" +#define ESP_ETH_PHY_ADDR_AUTO (-1) + /** * @brief Ethernet PHY * @@ -176,7 +178,7 @@ struct esp_eth_phy_s { * */ typedef struct { - uint32_t phy_addr; /*!< PHY address */ + int32_t phy_addr; /*!< PHY address, set -1 to enable PHY address detection at initialization stage */ uint32_t reset_timeout_ms; /*!< Reset timeout value (Unit: ms) */ uint32_t autonego_timeout_ms; /*!< Auto-negotiation timeout value (Unit: ms) */ int reset_gpio_num; /*!< Reset GPIO number, -1 means no hardware reset */ @@ -186,12 +188,12 @@ typedef struct { * @brief Default configuration for Ethernet PHY object * */ -#define ETH_PHY_DEFAULT_CONFIG() \ - { \ - .phy_addr = 1, \ - .reset_timeout_ms = 100, \ - .autonego_timeout_ms = 4000, \ - .reset_gpio_num = 5, \ +#define ETH_PHY_DEFAULT_CONFIG() \ + { \ + .phy_addr = ESP_ETH_PHY_ADDR_AUTO, \ + .reset_timeout_ms = 100, \ + .autonego_timeout_ms = 4000, \ + .reset_gpio_num = 5, \ } /** diff --git a/components/esp_eth/src/esp_eth_phy.c b/components/esp_eth/src/esp_eth_phy.c new file mode 100644 index 0000000000..d77812f907 --- /dev/null +++ b/components/esp_eth/src/esp_eth_phy.c @@ -0,0 +1,44 @@ +// Copyright 2019 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 +#include +#include "esp_log.h" +#include "esp_eth.h" +#include "eth_phy_regs_struct.h" + +static const char *TAG = "esp_eth.phy"; + +esp_err_t esp_eth_detect_phy_addr(esp_eth_mediator_t *eth, uint32_t *detected_addr) +{ + if (!eth || !detected_addr) { + ESP_LOGE(TAG, "eth and detected_addr can't be null"); + return ESP_ERR_INVALID_ARG; + } + uint32_t addr_try = 0; + uint32_t reg_value = 0; + for (; addr_try < 16; addr_try++) { + eth->phy_reg_read(eth, addr_try, ETH_PHY_IDR1_REG_ADDR, ®_value); + if (reg_value != 0xFFFF && reg_value != 0x00) { + *detected_addr = addr_try; + break; + } + } + if (addr_try < 16) { + ESP_LOGD(TAG, "Found PHY address: %d", addr_try); + return ESP_OK; + } else { + ESP_LOGE(TAG, "No PHY device detected"); + return ESP_ERR_NOT_FOUND; + } +} diff --git a/components/esp_eth/src/esp_eth_phy_dm9051.c b/components/esp_eth/src/esp_eth_phy_dm9051.c index c3775f2042..286a7baa56 100644 --- a/components/esp_eth/src/esp_eth_phy_dm9051.c +++ b/components/esp_eth/src/esp_eth_phy_dm9051.c @@ -285,6 +285,10 @@ static esp_err_t dm9051_init(esp_eth_phy_t *phy) { phy_dm9051_t *dm9051 = __containerof(phy, phy_dm9051_t, parent); esp_eth_mediator_t *eth = dm9051->eth; + // Detect PHY address + if (dm9051->addr == ESP_ETH_PHY_ADDR_AUTO) { + PHY_CHECK(esp_eth_detect_phy_addr(eth, &dm9051->addr) == ESP_OK, "Detect PHY address failed", err); + } /* Power on Ethernet PHY */ PHY_CHECK(dm9051_pwrctl(phy, true) == ESP_OK, "power control failed", err); /* Reset Ethernet PHY */ @@ -315,7 +319,6 @@ err: esp_eth_phy_t *esp_eth_phy_new_dm9051(const eth_phy_config_t *config) { PHY_CHECK(config, "can't set phy config to null", err); - PHY_CHECK(config->phy_addr == 1, "dm9051's phy address can only set to 1", err); phy_dm9051_t *dm9051 = calloc(1, sizeof(phy_dm9051_t)); PHY_CHECK(dm9051, "calloc dm9051 failed", err); dm9051->addr = config->phy_addr; diff --git a/components/esp_eth/src/esp_eth_phy_dp83848.c b/components/esp_eth/src/esp_eth_phy_dp83848.c index fbb41391a9..9bcfeeda18 100644 --- a/components/esp_eth/src/esp_eth_phy_dp83848.c +++ b/components/esp_eth/src/esp_eth_phy_dp83848.c @@ -41,7 +41,7 @@ static const char *TAG = "dp83848"; typedef union { struct { uint32_t link_status : 1; /* Link Status */ - uint32_t speed_status : 1; /* Link Status */ + uint32_t speed_status : 1; /* Speed Status */ uint32_t duplex_status : 1; /* Duplex Status */ uint32_t loopback_status : 1; /* MII Loopback */ uint32_t auto_nego_complete : 1; /* Auto-Negotiation Complete */ @@ -98,17 +98,14 @@ static esp_err_t dp83848_update_link_duplex_speed(phy_dp83848_t *dp83848) esp_eth_mediator_t *eth = dp83848->eth; eth_speed_t speed = ETH_SPEED_10M; eth_duplex_t duplex = ETH_DUPLEX_HALF; - bmsr_reg_t bmsr; physts_reg_t physts; - PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, - "read BMSR failed", err); - eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN; + PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_STS_REG_ADDR, &(physts.val)) == ESP_OK, + "read PHYSTS failed", err); + eth_link_t link = physts.link_status ? ETH_LINK_UP : ETH_LINK_DOWN; /* check if link status changed */ if (dp83848->link_status != link) { /* when link up, read negotiation result */ if (link == ETH_LINK_UP) { - PHY_CHECK(eth->phy_reg_read(eth, dp83848->addr, ETH_PHY_STS_REG_ADDR, &(physts.val)) == ESP_OK, - "read PHYSTS failed", err); if (physts.speed_status) { speed = ETH_SPEED_10M; } else { @@ -283,6 +280,10 @@ static esp_err_t dp83848_init(esp_eth_phy_t *phy) { phy_dp83848_t *dp83848 = __containerof(phy, phy_dp83848_t, parent); esp_eth_mediator_t *eth = dp83848->eth; + // Detect PHY address + if (dp83848->addr == ESP_ETH_PHY_ADDR_AUTO) { + PHY_CHECK(esp_eth_detect_phy_addr(eth, &dp83848->addr) == ESP_OK, "Detect PHY address failed", err); + } /* Power on Ethernet PHY */ PHY_CHECK(dp83848_pwrctl(phy, true) == ESP_OK, "power control failed", err); /* Reset Ethernet PHY */ diff --git a/components/esp_eth/src/esp_eth_phy_ip101.c b/components/esp_eth/src/esp_eth_phy_ip101.c index 6b16c2188a..c3ea8a1b01 100644 --- a/components/esp_eth/src/esp_eth_phy_ip101.c +++ b/components/esp_eth/src/esp_eth_phy_ip101.c @@ -128,17 +128,14 @@ static esp_err_t ip101_update_link_duplex_speed(phy_ip101_t *ip101) eth_speed_t speed = ETH_SPEED_10M; eth_duplex_t duplex = ETH_DUPLEX_HALF; cssr_reg_t cssr; - bmsr_reg_t bmsr; PHY_CHECK(ip101_page_select(ip101, 16) == ESP_OK, "select page 16 failed", err); - PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_BMSR_REG_ADDR, &(bmsr.val)) == ESP_OK, - "read BMSR failed", err); - eth_link_t link = bmsr.link_status ? ETH_LINK_UP : ETH_LINK_DOWN; + PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_CSSR_REG_ADDR, &(cssr.val)) == ESP_OK, + "read CSSR failed", err); + eth_link_t link = cssr.link_up ? ETH_LINK_UP : ETH_LINK_DOWN; /* check if link status changed */ if (ip101->link_status != link) { /* when link up, read negotiation result */ if (link == ETH_LINK_UP) { - PHY_CHECK(eth->phy_reg_read(eth, ip101->addr, ETH_PHY_CSSR_REG_ADDR, &(cssr.val)) == ESP_OK, - "read CSSR failed", err); switch (cssr.op_mode) { case 1: //10M Half speed = ETH_SPEED_10M; @@ -320,6 +317,10 @@ static esp_err_t ip101_init(esp_eth_phy_t *phy) { phy_ip101_t *ip101 = __containerof(phy, phy_ip101_t, parent); esp_eth_mediator_t *eth = ip101->eth; + // Detect PHY address + if (ip101->addr == ESP_ETH_PHY_ADDR_AUTO) { + PHY_CHECK(esp_eth_detect_phy_addr(eth, &ip101->addr) == ESP_OK, "Detect PHY address failed", err); + } /* Power on Ethernet PHY */ PHY_CHECK(ip101_pwrctl(phy, true) == ESP_OK, "power control failed", err); /* Reset Ethernet PHY */ diff --git a/components/esp_eth/src/esp_eth_phy_lan8720.c b/components/esp_eth/src/esp_eth_phy_lan8720.c index d7d62bb781..b6480a6649 100644 --- a/components/esp_eth/src/esp_eth_phy_lan8720.c +++ b/components/esp_eth/src/esp_eth_phy_lan8720.c @@ -364,6 +364,10 @@ static esp_err_t lan8720_init(esp_eth_phy_t *phy) { phy_lan8720_t *lan8720 = __containerof(phy, phy_lan8720_t, parent); esp_eth_mediator_t *eth = lan8720->eth; + // Detect PHY address + if (lan8720->addr == ESP_ETH_PHY_ADDR_AUTO) { + PHY_CHECK(esp_eth_detect_phy_addr(eth, &lan8720->addr) == ESP_OK, "Detect PHY address failed", err); + } /* Power on Ethernet PHY */ PHY_CHECK(lan8720_pwrctl(phy, true) == ESP_OK, "power control failed", err); /* Reset Ethernet PHY */ diff --git a/components/esp_eth/src/esp_eth_phy_rtl8201.c b/components/esp_eth/src/esp_eth_phy_rtl8201.c index 814563fdc1..2a5808a22c 100644 --- a/components/esp_eth/src/esp_eth_phy_rtl8201.c +++ b/components/esp_eth/src/esp_eth_phy_rtl8201.c @@ -271,6 +271,10 @@ static esp_err_t rtl8201_init(esp_eth_phy_t *phy) { phy_rtl8201_t *rtl8201 = __containerof(phy, phy_rtl8201_t, parent); esp_eth_mediator_t *eth = rtl8201->eth; + // Detect PHY address + if (rtl8201->addr == ESP_ETH_PHY_ADDR_AUTO) { + PHY_CHECK(esp_eth_detect_phy_addr(eth, &rtl8201->addr) == ESP_OK, "Detect PHY address failed", err); + } /* Power on Ethernet PHY */ PHY_CHECK(rtl8201_pwrctl(phy, true) == ESP_OK, "power control failed", err); /* Reset Ethernet PHY */ From fce6a9c735ff3b7bd476194bffe4283a7056240f Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 23 Dec 2019 17:08:19 +0800 Subject: [PATCH 2/3] ethernet: test ethernet download --- components/esp_eth/test/CMakeLists.txt | 2 +- components/esp_eth/test/test_emac.c | 120 +++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/components/esp_eth/test/CMakeLists.txt b/components/esp_eth/test/CMakeLists.txt index ea533991a3..ddc90df760 100644 --- a/components/esp_eth/test/CMakeLists.txt +++ b/components/esp_eth/test/CMakeLists.txt @@ -3,5 +3,5 @@ idf_build_get_property(target IDF_TARGET) if(${target} STREQUAL "esp32") idf_component_register(SRC_DIRS . INCLUDE_DIRS . - PRIV_REQUIRES unity test_utils esp_eth) + PRIV_REQUIRES unity test_utils esp_eth esp_http_client) endif() diff --git a/components/esp_eth/test/test_emac.c b/components/esp_eth/test/test_emac.c index e70fbb5580..c15c9dc633 100644 --- a/components/esp_eth/test/test_emac.c +++ b/components/esp_eth/test/test_emac.c @@ -8,10 +8,12 @@ #include "esp_event.h" #include "esp_eth.h" #include "esp_log.h" +#include "esp_http_client.h" #include "lwip/inet.h" #include "lwip/netdb.h" #include "lwip/sockets.h" #include "ping/ping_sock.h" +#include "esp32/rom/md5_hash.h" static const char *TAG = "esp32_eth_test"; @@ -20,14 +22,20 @@ static const char *TAG = "esp32_eth_test"; #define ETH_CONNECT_BIT BIT(2) #define ETH_GOT_IP_BIT BIT(3) #define ETH_PING_END_BIT BIT(4) +#define ETH_DOWNLOAD_END_BIT BIT(5) #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) +#define ETH_DOWNLOAD_END_TIMEOUT_MS (240000) #define ETH_PING_DURATION_MS (5000) #define ETH_PING_END_TIMEOUT_MS (ETH_PING_DURATION_MS * 2) +// compute md5 of download file +static struct MD5Context md5_context; +static uint8_t digest[16]; + /** 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) @@ -132,6 +140,8 @@ TEST_CASE("esp32 ethernet io test", "[ethernet][test_env=UT_T2_Ethernet]") eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + // auto detect PHY address + phy_config.phy_addr = ESP_ETH_PHY_ADDR_AUTO; esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); esp_eth_handle_t eth_handle = NULL; @@ -380,3 +390,113 @@ TEST_CASE("esp32 ethernet icmp test", "[ethernet][test_env=UT_T2_Ethernet]") TEST_ESP_OK(esp_event_loop_delete_default()); vEventGroupDelete(eth_event_group); } + +esp_err_t http_event_handle(esp_http_client_event_t *evt) +{ + switch (evt->event_id) { + case HTTP_EVENT_ERROR: + ESP_LOGE(TAG, "HTTP_EVENT_ERROR"); + break; + case HTTP_EVENT_ON_CONNECTED: + ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED"); + break; + case HTTP_EVENT_HEADER_SENT: + ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT"); + break; + case HTTP_EVENT_ON_HEADER: + ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER"); + break; + case HTTP_EVENT_ON_DATA: + MD5Update(&md5_context, evt->data, evt->data_len); + break; + case HTTP_EVENT_ON_FINISH: + ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH"); + break; + case HTTP_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED"); + break; + } + return ESP_OK; +} + +static void eth_download_task(void *param) +{ + EventGroupHandle_t eth_event_group = (EventGroupHandle_t)param; + MD5Init(&md5_context); + esp_http_client_config_t config = { + .url = "https://dl.espressif.com/dl/misc/2MB.bin", + .event_handler = http_event_handle, + .buffer_size = 5120 + }; + esp_http_client_handle_t client = esp_http_client_init(&config); + TEST_ASSERT_NOT_NULL(client); + TEST_ESP_OK(esp_http_client_perform(client)); + TEST_ESP_OK(esp_http_client_cleanup(client)); + MD5Final(digest, &md5_context); + xEventGroupSetBits(eth_event_group, ETH_DOWNLOAD_END_BIT); + vTaskDelete(NULL); +} + +TEST_CASE("esp32 ethernet download test", "[ethernet][test_env=UT_T2_Ethernet][timeout=240]") +{ + EventBits_t bits = 0; + EventGroupHandle_t eth_event_group = xEventGroupCreate(); + TEST_ASSERT(eth_event_group != NULL); + test_case_uses_tcpip(); + TEST_ESP_OK(esp_event_loop_create_default()); + // create TCP/IP netif + esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH(); + esp_netif_t *eth_netif = esp_netif_new(&netif_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, ð_event_handler, eth_event_group)); + TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, eth_event_group)); + eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG(); + esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config); + eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); + esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); + esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy); + esp_eth_handle_t eth_handle = NULL; + // install Ethernet driver + TEST_ESP_OK(esp_eth_driver_install(ð_config, ð_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 */ + bits = xEventGroupWaitBits(eth_event_group, ETH_GOT_IP_BIT, true, true, pdMS_TO_TICKS(ETH_GET_IP_TIMEOUT_MS)); + TEST_ASSERT((bits & ETH_GOT_IP_BIT) == ETH_GOT_IP_BIT); + + xTaskCreate(eth_download_task, "eth_dl", 4096, eth_event_group, tskIDLE_PRIORITY + 2, NULL); + /* wait for download end */ + bits = xEventGroupWaitBits(eth_event_group, ETH_DOWNLOAD_END_BIT, true, true, pdMS_TO_TICKS(ETH_DOWNLOAD_END_TIMEOUT_MS)); + TEST_ASSERT_EQUAL(ETH_DOWNLOAD_END_BIT, bits & ETH_DOWNLOAD_END_BIT); + // check MD5 digest + // MD5: df61db8564d145bbe67112aa8ecdccd8 + uint8_t expect_digest[16] = {223, 97, 219, 133, 100, 209, 69, 187, 230, 113, 18, 170, 142, 205, 204, 216}; + printf("MD5 Digest: "); + for (int i = 0; i < 16; i++) { + printf("%d ", digest[i]); + } + printf("\r\n"); + TEST_ASSERT(memcmp(expect_digest, digest, sizeof(digest)) == 0); + + // stop Ethernet driver + TEST_ESP_OK(esp_eth_stop(eth_handle)); + /* wait for connection stop */ + bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS)); + TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT); + TEST_ESP_OK(esp_eth_del_netif_glue(glue)); + /* driver should be uninstalled within 2 seconds */ + TEST_ESP_OK(test_uninstall_driver(eth_handle, 2000)); + 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()); + vEventGroupDelete(eth_event_group); +} From 7baf7ce2739dacac9e0803fcf1dc57991debafa9 Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 23 Dec 2019 17:08:38 +0800 Subject: [PATCH 3/3] ethernet: optimise tx and rx --- components/esp_eth/src/esp_eth.c | 4 +++ components/esp_eth/src/esp_eth_mac_dm9051.c | 9 +++-- components/esp_eth/src/esp_eth_mac_esp32.c | 31 +++++++---------- components/esp_eth/src/esp_eth_mac_openeth.c | 19 +++++------ components/soc/esp32/emac_hal.c | 36 ++++++++++++-------- components/soc/esp32/include/hal/emac.h | 6 +++- 6 files changed, 57 insertions(+), 48 deletions(-) diff --git a/components/esp_eth/src/esp_eth.c b/components/esp_eth/src/esp_eth.c index 1c87b8a60c..4e61078f66 100644 --- a/components/esp_eth/src/esp_eth.c +++ b/components/esp_eth/src/esp_eth.c @@ -299,6 +299,8 @@ esp_err_t esp_eth_transmit(esp_eth_handle_t hdl, void *buf, uint32_t length) { esp_err_t ret = ESP_OK; esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl; + ETH_CHECK(buf, "can't set buf to null", err, ESP_ERR_INVALID_ARG); + ETH_CHECK(length, "buf length can't be zero", err, ESP_ERR_INVALID_ARG); ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG); esp_eth_mac_t *mac = eth_driver->mac; return mac->transmit(mac, buf, length); @@ -310,6 +312,8 @@ esp_err_t esp_eth_receive(esp_eth_handle_t hdl, uint8_t *buf, uint32_t *length) { esp_err_t ret = ESP_OK; esp_eth_driver_t *eth_driver = (esp_eth_driver_t *)hdl; + ETH_CHECK(buf && length, "can't set buf and length to null", err, ESP_ERR_INVALID_ARG); + ETH_CHECK(*length > 60, "length can't be less than 60", err, ESP_ERR_INVALID_ARG); ETH_CHECK(eth_driver, "ethernet driver handle can't be null", err, ESP_ERR_INVALID_ARG); esp_eth_mac_t *mac = eth_driver->mac; return mac->receive(mac, buf, length); diff --git a/components/esp_eth/src/esp_eth_mac_dm9051.c b/components/esp_eth/src/esp_eth_mac_dm9051.c index 599f1b6558..79eb2aaad6 100644 --- a/components/esp_eth/src/esp_eth_mac_dm9051.c +++ b/components/esp_eth/src/esp_eth_mac_dm9051.c @@ -397,8 +397,10 @@ static void emac_dm9051_task(void *arg) if (status & ISR_PR) { do { length = ETH_MAX_PACKET_SIZE; - buffer = (uint8_t *)heap_caps_malloc(length, MALLOC_CAP_DMA); - if (emac->parent.receive(&emac->parent, buffer, &length) == ESP_OK) { + 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); @@ -597,8 +599,6 @@ static esp_err_t emac_dm9051_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t { esp_err_t ret = ESP_OK; emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); - MAC_CHECK(buf, "can't set buf to null", err, ESP_ERR_INVALID_ARG); - MAC_CHECK(length, "buf length can't be zero", err, ESP_ERR_INVALID_ARG); /* Check if last transmit complete */ uint8_t tcr = 0; MAC_CHECK(dm9051_register_read(emac, DM9051_TCR, &tcr) == ESP_OK, "read TCR failed", err, ESP_FAIL); @@ -620,7 +620,6 @@ static esp_err_t emac_dm9051_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t { esp_err_t ret = ESP_OK; emac_dm9051_t *emac = __containerof(mac, emac_dm9051_t, parent); - MAC_CHECK(buf && length, "can't set buf and length to null", err, ESP_ERR_INVALID_ARG); 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 diff --git a/components/esp_eth/src/esp_eth_mac_esp32.c b/components/esp_eth/src/esp_eth_mac_esp32.c index 7f0ffdd4bf..6f79413845 100644 --- a/components/esp_eth/src/esp_eth_mac_esp32.c +++ b/components/esp_eth/src/esp_eth_mac_esp32.c @@ -214,12 +214,8 @@ static esp_err_t emac_esp32_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint32_t { esp_err_t ret = ESP_OK; emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent); - MAC_CHECK(buf, "can't set buf to null", err, ESP_ERR_INVALID_ARG); - MAC_CHECK(length, "buf length can't be zero", err, ESP_ERR_INVALID_ARG); - /* Check if the descriptor is owned by the Ethernet DMA (when 1) or CPU (when 0) */ - MAC_CHECK(emac_hal_get_tx_desc_owner(&emac->hal) == EMAC_DMADESC_OWNER_CPU, - "CPU doesn't own the Tx Descriptor", err, ESP_ERR_INVALID_STATE); - emac_hal_transmit_frame(&emac->hal, buf, length); + uint32_t sent_len = emac_hal_transmit_frame(&emac->hal, buf, length); + MAC_CHECK(sent_len == length, "insufficient TX buffer size", err, ESP_ERR_INVALID_SIZE); return ESP_OK; err: return ret; @@ -228,20 +224,17 @@ err: static esp_err_t emac_esp32_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32_t *length) { esp_err_t ret = ESP_OK; + uint32_t expected_len = *length; emac_esp32_t *emac = __containerof(mac, emac_esp32_t, parent); MAC_CHECK(buf && length, "can't set buf and length to null", err, ESP_ERR_INVALID_ARG); - uint32_t receive_len = emac_hal_receive_frame(&emac->hal, buf, *length, &emac->frames_remain); + uint32_t receive_len = emac_hal_receive_frame(&emac->hal, buf, expected_len, &emac->frames_remain); /* we need to check the return value in case the buffer size is not enough */ - if (*length < receive_len) { - ESP_LOGE(TAG, "buffer size too small"); - /* tell upper layer the size we need */ - *length = receive_len; - ret = ESP_ERR_INVALID_SIZE; - goto err; - } + ESP_LOGD(TAG, "receive len= %d", receive_len); + MAC_CHECK(expected_len >= receive_len, "received buffer longer than expected", err, ESP_ERR_INVALID_SIZE); *length = receive_len; return ESP_OK; err: + *length = expected_len; return ret; } @@ -255,8 +248,10 @@ static void emac_esp32_rx_task(void *arg) ulTaskNotifyTake(pdFALSE, portMAX_DELAY); do { length = ETH_MAX_PACKET_SIZE; - buffer = (uint8_t *)malloc(length); - if (emac_esp32_receive(&emac->parent, buffer, &length) == ESP_OK) { + 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); @@ -291,9 +286,9 @@ static esp_err_t emac_esp32_init(esp_eth_mac_t *mac) esp_eth_mediator_t *eth = emac->eth; /* enable peripheral clock */ periph_module_enable(PERIPH_EMAC_MODULE); - /* enable clock, config gpio, etc */ + /* init clock, config gpio, etc */ emac_hal_lowlevel_init(&emac->hal); - /* init gpio used by gpio */ + /* init gpio used by smi interface */ emac_esp32_init_smi_gpio(emac); MAC_CHECK(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL) == ESP_OK, "lowlevel init failed", err, ESP_FAIL); /* software reset */ diff --git a/components/esp_eth/src/esp_eth_mac_openeth.c b/components/esp_eth/src/esp_eth_mac_openeth.c index e110e405bf..654195d707 100644 --- a/components/esp_eth/src/esp_eth_mac_openeth.c +++ b/components/esp_eth/src/esp_eth_mac_openeth.c @@ -66,7 +66,7 @@ static esp_err_t emac_opencores_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32 static IRAM_ATTR void emac_opencores_isr_handler(void *args) { - emac_opencores_t *emac = (emac_opencores_t*) args; + emac_opencores_t *emac = (emac_opencores_t *) args; BaseType_t high_task_wakeup; uint32_t status = REG_READ(OPENETH_INT_SOURCE_REG); @@ -94,10 +94,12 @@ static void emac_opencores_rx_task(void *arg) uint32_t length = 0; while (1) { if (ulTaskNotifyTake(pdFALSE, portMAX_DELAY)) { - while(true) { - buffer = (uint8_t *)malloc(ETH_MAX_PACKET_SIZE); + while (true) { length = ETH_MAX_PACKET_SIZE; - if (emac_opencores_receive(&emac->parent, buffer, &length) == ESP_OK) { + buffer = malloc(length); + if (!buffer) { + ESP_LOGE(TAG, "no mem for receive buffer"); + } else if (emac_opencores_receive(&emac->parent, buffer, &length) == ESP_OK) { // pass the buffer to the upper layer if (length) { emac->eth->stack_input(emac->eth, buffer, length); @@ -232,8 +234,6 @@ static esp_err_t emac_opencores_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint3 { esp_err_t ret = ESP_OK; emac_opencores_t *emac = __containerof(mac, emac_opencores_t, parent); - MAC_CHECK(buf, "can't set buf to null", err, ESP_ERR_INVALID_ARG); - MAC_CHECK(length, "buf length can't be zero", err, ESP_ERR_INVALID_ARG); MAC_CHECK(length < DMA_BUF_SIZE * TX_BUF_COUNT, "insufficient TX buffer size", err, ESP_ERR_INVALID_SIZE); uint32_t bytes_remaining = length; @@ -243,7 +243,7 @@ static esp_err_t emac_opencores_transmit(esp_eth_mac_t *mac, uint8_t *buf, uint3 while (bytes_remaining > 0) { uint32_t will_write = MIN(bytes_remaining, DMA_BUF_SIZE); memcpy(emac->tx_buf[emac->cur_tx_desc], buf, will_write); - openeth_tx_desc_t* desc_ptr = openeth_tx_desc(emac->cur_tx_desc); + openeth_tx_desc_t *desc_ptr = openeth_tx_desc(emac->cur_tx_desc); openeth_tx_desc_t desc_val = *desc_ptr; desc_val.wr = (emac->cur_tx_desc == TX_BUF_COUNT - 1); desc_val.len = will_write; @@ -265,7 +265,6 @@ static esp_err_t emac_opencores_receive(esp_eth_mac_t *mac, uint8_t *buf, uint32 { esp_err_t ret = ESP_OK; emac_opencores_t *emac = __containerof(mac, emac_opencores_t, parent); - MAC_CHECK(buf && length, "can't set buf and length to null", err, ESP_ERR_INVALID_ARG); openeth_rx_desc_t *desc_ptr = openeth_rx_desc(emac->cur_rx_desc); openeth_rx_desc_t desc_val = *desc_ptr; @@ -294,7 +293,7 @@ static esp_err_t emac_opencores_init(esp_eth_mac_t *mac) esp_eth_mediator_t *eth = emac->eth; MAC_CHECK(eth->on_state_changed(eth, ETH_STATE_LLINIT, NULL) == ESP_OK, "lowlevel init failed", err, ESP_FAIL); MAC_CHECK(esp_read_mac(emac->addr, ESP_MAC_ETH) == ESP_OK, "fetch ethernet mac address failed", err, ESP_FAIL); - + // Sanity check if (REG_READ(OPENETH_MODER_REG) != OPENETH_MODER_DEFAULT) { ESP_LOGE(TAG, "CONFIG_ETH_USE_OPENETH should only be used when running in QEMU."); @@ -378,7 +377,7 @@ esp_eth_mac_t *esp_eth_mac_new_openeth(const eth_mac_config_t *config) emac->parent.set_promiscuous = emac_opencores_set_promiscuous; emac->parent.transmit = emac_opencores_transmit; emac->parent.receive = emac_opencores_receive; - + // Initialize the interrupt MAC_CHECK(esp_intr_alloc(OPENETH_INTR_SOURCE, ESP_INTR_FLAG_IRAM, emac_opencores_isr_handler, emac, &(emac->intr_hdl)) == ESP_OK, diff --git a/components/soc/esp32/emac_hal.c b/components/soc/esp32/emac_hal.c index 9b0c52e24a..06640fa99f 100644 --- a/components/soc/esp32/emac_hal.c +++ b/components/soc/esp32/emac_hal.c @@ -438,11 +438,12 @@ uint32_t emac_hal_get_tx_desc_owner(emac_hal_context_t *hal) return hal->tx_desc->TDES0.Own; } -void emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t length) +uint32_t emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t length) { /* Get the number of Tx buffers to use for the frame */ uint32_t bufcount = 0; uint32_t lastlen = length; + uint32_t sentout = 0; while (lastlen > CONFIG_ETH_DMA_BUFFER_SIZE) { lastlen -= CONFIG_ETH_DMA_BUFFER_SIZE; bufcount++; @@ -452,6 +453,10 @@ void emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t len } /* A frame is transmitted in multiple descriptor */ for (uint32_t i = 0; i < bufcount; i++) { + /* Check if the descriptor is owned by the Ethernet DMA (when 1) or CPU (when 0) */ + if (hal->tx_desc->TDES0.Own != EMAC_DMADESC_OWNER_CPU) { + goto err; + } /* Clear FIRST and LAST segment bits */ hal->tx_desc->TDES0.FirstSegment = 0; hal->tx_desc->TDES0.LastSegment = 0; @@ -468,18 +473,22 @@ void emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t len hal->tx_desc->TDES1.TransmitBuffer1Size = lastlen; /* copy data from uplayer stack buffer */ memcpy((void *)(hal->tx_desc->Buffer1Addr), buf + i * CONFIG_ETH_DMA_BUFFER_SIZE, lastlen); + sentout += lastlen; } else { /* Program size */ hal->tx_desc->TDES1.TransmitBuffer1Size = CONFIG_ETH_DMA_BUFFER_SIZE; /* copy data from uplayer stack buffer */ memcpy((void *)(hal->tx_desc->Buffer1Addr), buf + i * CONFIG_ETH_DMA_BUFFER_SIZE, CONFIG_ETH_DMA_BUFFER_SIZE); + sentout += CONFIG_ETH_DMA_BUFFER_SIZE; } /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ hal->tx_desc->TDES0.Own = EMAC_DMADESC_OWNER_DMA; /* Point to next descriptor */ hal->tx_desc = (eth_dma_tx_descriptor_t *)(hal->tx_desc->Buffer2NextDescAddr); } +err: hal->dma_regs->dmatxpolldemand = 0; + return sentout; } uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t size, uint32_t *frames_remain) @@ -488,7 +497,9 @@ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t eth_dma_rx_descriptor_t *first_desc = NULL; uint32_t iter = 0; uint32_t seg_count = 0; - uint32_t len = 0; + uint32_t ret_len = 0; + uint32_t copy_len = 0; + uint32_t write_len = 0; uint32_t frame_count = 0; first_desc = hal->rx_desc; @@ -500,13 +511,9 @@ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t /* Last segment in frame */ if (desc_iter->RDES0.LastDescriptor) { /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */ - len = desc_iter->RDES0.FrameLength - ETH_CRC_LENGTH; - /* check if the buffer can store the whole frame */ - if (len > size) { - /* return the real size that we want */ - /* user need to compare the return value to the size they prepared when this function returned */ - return len; - } + 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++; } @@ -530,15 +537,16 @@ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t } desc_iter = first_desc; for (iter = 0; iter < seg_count - 1; iter++) { + write_len = copy_len < CONFIG_ETH_DMA_BUFFER_SIZE ? copy_len : CONFIG_ETH_DMA_BUFFER_SIZE; /* copy data to buffer */ - memcpy(buf + iter * CONFIG_ETH_DMA_BUFFER_SIZE, - (void *)(desc_iter->Buffer1Addr), CONFIG_ETH_DMA_BUFFER_SIZE); + memcpy(buf, (void *)(desc_iter->Buffer1Addr), write_len); + buf += write_len; + copy_len -= write_len; /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ desc_iter->RDES0.Own = EMAC_DMADESC_OWNER_DMA; desc_iter = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr); } - memcpy(buf + iter * CONFIG_ETH_DMA_BUFFER_SIZE, - (void *)(desc_iter->Buffer1Addr), len % CONFIG_ETH_DMA_BUFFER_SIZE); + memcpy(buf, (void *)(desc_iter->Buffer1Addr), copy_len); desc_iter->RDES0.Own = EMAC_DMADESC_OWNER_DMA; /* update rxdesc */ hal->rx_desc = (eth_dma_rx_descriptor_t *)(desc_iter->Buffer2NextDescAddr); @@ -547,7 +555,7 @@ uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t frame_count--; } *frames_remain = frame_count; - return len; + return ret_len; } IRAM_ATTR void emac_hal_isr(void *arg) diff --git a/components/soc/esp32/include/hal/emac.h b/components/soc/esp32/include/hal/emac.h index 824b896327..b55d88301e 100644 --- a/components/soc/esp32/include/hal/emac.h +++ b/components/soc/esp32/include/hal/emac.h @@ -242,6 +242,8 @@ typedef struct { #define EMAC_DMATXDESC_CHECKSUM_TCPUDPICMPSEGMENT 2 /*!< TCP/UDP/ICMP Checksum Insertion calculated over segment only */ #define EMAC_DMATXDESC_CHECKSUM_TCPUDPICMPFULL 3 /*!< TCP/UDP/ICMP Checksum Insertion fully calculated */ +_Static_assert(sizeof(eth_dma_tx_descriptor_t) == 32, "eth_dma_tx_descriptor_t should occupy 32 bytes in memory"); + /** * @brief Ethernet DMA RX Descriptor * @@ -328,6 +330,8 @@ typedef struct { #define EMAC_DMADESC_OWNER_CPU (0) #define EMAC_DMADESC_OWNER_DMA (1) +_Static_assert(sizeof(eth_dma_rx_descriptor_t) == 32, "eth_dma_rx_descriptor_t should occupy 32 bytes in memory"); + typedef struct { emac_mac_dev_t *mac_regs; emac_dma_dev_t *dma_regs; @@ -378,7 +382,7 @@ void emac_hal_stop(emac_hal_context_t *hal); uint32_t emac_hal_get_tx_desc_owner(emac_hal_context_t *hal); -void emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t length); +uint32_t emac_hal_transmit_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t length); uint32_t emac_hal_receive_frame(emac_hal_context_t *hal, uint8_t *buf, uint32_t size, uint32_t *frames_remain);