Merge branch 'feature/add_eth_download_test' into 'master'

ethernet: test download blob && phy addr auto detect

Closes IDF-1238

See merge request espressif/esp-idf!6957
This commit is contained in:
Angus Gratton
2019-12-30 11:52:40 +08:00
17 changed files with 272 additions and 71 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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, \
}
/**

View File

@@ -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);

View File

@@ -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

View File

@@ -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 */

View File

@@ -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,

View File

@@ -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 <string.h>
#include <stdlib.h>
#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, &reg_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;
}
}

View File

@@ -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;

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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 */

View File

@@ -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()

View File

@@ -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, &eth_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(&eth_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 */
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);
}

View File

@@ -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)

View File

@@ -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);