mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-01 10:40:47 +03:00
feat(esp_eth): update test_apps to use Ethernet test component
Move test logic into the eth_test_app managed component and slim down the test_apps main target and pytest suite accordingly.
This commit is contained in:
@@ -1,12 +1,8 @@
|
||||
idf_component_register(SRCS "esp_eth_test_main.c"
|
||||
"esp_eth_test_apps.c"
|
||||
"esp_eth_test_l2.c"
|
||||
"esp_eth_test_utils.c"
|
||||
"esp_eth_test_esp_emac.c"
|
||||
"esp_eth_test_emac_sleep_retention.c"
|
||||
"esp_eth_test_emac_init.cpp"
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES unity esp_eth esp_netif esp_http_client esp_driver_gpio esp_driver_uart
|
||||
EMBED_TXTFILES dl_espressif_com_root_cert.pem
|
||||
PRIV_REQUIRES unity esp_eth esp_netif esp_event hal esp_hw_support
|
||||
esp_driver_gpio esp_driver_uart esp_pm
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
menu "esp_eth TEST_APPS Configuration"
|
||||
|
||||
config ETH_TEST_UNITY_TEST_TASK_PRIO
|
||||
int "Unity test task priority"
|
||||
range 0 25
|
||||
default 5
|
||||
help
|
||||
Unity test task priority.
|
||||
|
||||
config ETH_TEST_UNITY_TEST_TASK_STACK
|
||||
int "Unity test task stack size"
|
||||
default 4096
|
||||
help
|
||||
Unity test task stack size in B.
|
||||
|
||||
config ETH_TEST_MAC_ADDR_UI
|
||||
bool "Expect Universal & Individual MAC address"
|
||||
default y
|
||||
help
|
||||
Enable this option to expect Universal & Individual MAC address.
|
||||
This should be disabled for SPI Ethernet devices.
|
||||
|
||||
config ETH_TEST_PHY_ADDRESS_DISABLED
|
||||
bool "Disable PHY address test"
|
||||
default n
|
||||
help
|
||||
Disable PHY address testing.
|
||||
This should be disabled for SPI Ethernet devices.
|
||||
|
||||
config ETH_TEST_LOOPBACK_DISABLED
|
||||
bool "Disable loopback test"
|
||||
default n
|
||||
help
|
||||
Disable loopback testing as some chips may not support it.
|
||||
|
||||
config ETH_TEST_10MB_LOOPBACK_DISABLED
|
||||
depends on !ETH_TEST_LOOPBACK_DISABLED
|
||||
bool "Disable 10MB loopback test"
|
||||
default n
|
||||
help
|
||||
Disable 10MB loopback testing as some chips may not support it.
|
||||
|
||||
config ETH_TEST_10MB_LOOPBACK_IGNORE_FAILURES
|
||||
depends on !ETH_TEST_LOOPBACK_DISABLED
|
||||
bool "Ignore 10MB loopback test failures"
|
||||
default y
|
||||
help
|
||||
Ignore failures in 10MB loopback tests.
|
||||
|
||||
config ETH_TEST_LOOPBACK_WITH_AUTONEGOTIATION_DISABLED
|
||||
depends on !ETH_TEST_LOOPBACK_DISABLED
|
||||
bool "Disable loopback with autonegotiation test"
|
||||
default n
|
||||
help
|
||||
Disable loopback testing with autonegotiation.
|
||||
|
||||
config ETH_TEST_FILL_RX_BUFFER_ITERATIONS
|
||||
int "Number of iterations for fill RX buffer test"
|
||||
range 1 100
|
||||
default 10
|
||||
help
|
||||
Number of iterations to fill RX buffer of module under test.
|
||||
For internal EMAC, it should be set as ETH_DMA_RX_BUFFER_NUM.
|
||||
For SPI modules, it should be set as RX_MEM_SIZE / ETH_MAX_PACKET_SIZE, where RX_MEM_SIZE is
|
||||
module specific RX buffer size in bytes as defined in datasheet.
|
||||
|
||||
config ETH_TEST_LAN8720_ERRATA_ENABLED
|
||||
bool "Enable LAN8720 errata workaround"
|
||||
default n
|
||||
help
|
||||
Enable workaround for LAN8720 errata.
|
||||
Enable only when LAN8720 is under test!!
|
||||
|
||||
config ETH_TEST_W5500_IP6_MCAST_DEVIATION_ENABLED
|
||||
bool "Enable W5500 IPv6 multicast deviation test"
|
||||
default n
|
||||
help
|
||||
W5500 always receives IPv6 multicast packets, even if the filter is set to block multicast.
|
||||
Enable only when W5500 is under test!!
|
||||
|
||||
config ETH_TEST_STRESS_TEST_TASK_PRIO
|
||||
int "Start/stop stress test task priority"
|
||||
range -1 25
|
||||
default -1
|
||||
help
|
||||
The Rx Task may occupy all the resources under heavy Rx traffic and it would not be possible
|
||||
to access the Ethernet module to stop it. Therefore, the test task priority can be set higher than
|
||||
the Rx task to be able to preempt the Rx task.
|
||||
This option should be set for SPI Ethernet modules (>15), otherwise set -1 to use default priority.
|
||||
|
||||
endmenu
|
||||
@@ -1,22 +0,0 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh
|
||||
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
|
||||
d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH
|
||||
MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT
|
||||
MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j
|
||||
b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG
|
||||
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI
|
||||
2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx
|
||||
1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ
|
||||
q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz
|
||||
tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ
|
||||
vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP
|
||||
BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV
|
||||
5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY
|
||||
1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4
|
||||
NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG
|
||||
Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91
|
||||
8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe
|
||||
pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl
|
||||
MrY=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -1,554 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_http_client.h"
|
||||
#include "esp_rom_md5.h"
|
||||
#include "esp_eth_test_utils.h"
|
||||
#include "unity.h"
|
||||
|
||||
#define LOOPBACK_TEST_PACKET_SIZE 256
|
||||
|
||||
static const char *TAG = "esp32_eth_test";
|
||||
|
||||
extern const char dl_espressif_com_root_cert_pem_start[] asm("_binary_dl_espressif_com_root_cert_pem_start");
|
||||
extern const char dl_espressif_com_root_cert_pem_end[] asm("_binary_dl_espressif_com_root_cert_pem_end");
|
||||
|
||||
// compute md5 of download file
|
||||
static md5_context_t md5_context;
|
||||
static uint8_t digest[16];
|
||||
|
||||
// Basic test to verify that the Ethernet driver can be initialized and deinitialized
|
||||
TEST_CASE("ethernet init/deinit test", "[ethernet],[skip_setup_teardown]")
|
||||
{
|
||||
esp_eth_handle_t eth_handle = NULL;
|
||||
|
||||
TEST_ESP_OK(esp_eth_test_eth_init(ð_handle));
|
||||
TEST_ESP_OK(esp_eth_test_eth_deinit(eth_handle));
|
||||
}
|
||||
|
||||
TEST_CASE("ethernet io test", "[ethernet]")
|
||||
{
|
||||
// get handles from common module initialized by setUp()
|
||||
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
|
||||
|
||||
/* get default MAC address */
|
||||
uint8_t mac_addr[ETH_ADDR_LEN];
|
||||
memset(mac_addr, 0, sizeof(mac_addr));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr));
|
||||
ESP_LOGI(TAG, "Ethernet MAC Address: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
|
||||
TEST_ASSERT(mac_addr[0] != 0);
|
||||
// *** SPI Ethernet modules deviation ***
|
||||
// Rationale: The SPI Ethernet modules don't have a burned default factory MAC address hence local MAC is used
|
||||
#if CONFIG_ETH_TEST_MAC_ADDR_UI
|
||||
TEST_ASSERT_BITS(0b00000011, 0b00, mac_addr[0]); // Check UL&IG, should be UI
|
||||
#else
|
||||
TEST_ASSERT_BITS(0b00000011, 0b10, mac_addr[0]); // Check UL&IG, should be U
|
||||
#endif
|
||||
|
||||
/* set different MAC address */
|
||||
mac_addr[5] ^= mac_addr[4];
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, mac_addr));
|
||||
/* get new MAC address */
|
||||
uint8_t mac_addr_new[ETH_ADDR_LEN] = { 0 };
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr_new));
|
||||
ESP_LOGI(TAG, "Ethernet MAC Address: %02x:%02x:%02x:%02x:%02x:%02x",
|
||||
mac_addr_new[0], mac_addr_new[1], mac_addr_new[2], mac_addr_new[3], mac_addr_new[4], mac_addr_new[5]);
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY(mac_addr_new, mac_addr, ETH_ADDR_LEN);
|
||||
|
||||
// *** SPI Ethernet modules deviation ***
|
||||
// Rationale: SPI Ethernet modules PHYs and MACs are statically configured at one die, hence there is no need for PHY address
|
||||
// from user's point of view
|
||||
#if CONFIG_ETH_TEST_PHY_ADDRESS_DISABLED
|
||||
/* get PHY address */
|
||||
int phy_addr = -1;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_PHY_ADDR, &phy_addr));
|
||||
ESP_LOGI(TAG, "Ethernet PHY Address: %d", phy_addr);
|
||||
TEST_ASSERT(phy_addr >= 0 && phy_addr <= 31);
|
||||
#endif
|
||||
}
|
||||
|
||||
// This test expects autonegotiation to be enabled on the other node.
|
||||
TEST_CASE("ethernet io speed/duplex/autonegotiation", "[ethernet]")
|
||||
{
|
||||
// get handles from common module initialized by setUp()
|
||||
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
|
||||
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
|
||||
|
||||
EventBits_t bits = 0;
|
||||
// this test only test layer2, so don't need to register input callback (i.e. esp_eth_update_input_path)
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle));
|
||||
// wait for connection start
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, true, true, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
|
||||
// wait for connection establish
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
|
||||
eth_duplex_t exp_duplex;
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex);
|
||||
|
||||
eth_speed_t exp_speed;
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed);
|
||||
// verify autonegotiation result (expecting the best link configuration)
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_FULL, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_100M, exp_speed);
|
||||
|
||||
bool exp_autoneg_en;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &exp_autoneg_en));
|
||||
TEST_ASSERT_EQUAL(true, exp_autoneg_en);
|
||||
|
||||
ESP_LOGI(TAG, "try to change autonegotiation when driver is started...");
|
||||
bool auto_nego_en = false;
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &exp_autoneg_en));
|
||||
TEST_ASSERT_EQUAL(true, exp_autoneg_en);
|
||||
|
||||
ESP_LOGI(TAG, "stop the Ethernet driver and...");
|
||||
esp_eth_stop(eth_handle);
|
||||
|
||||
ESP_LOGI(TAG, "try to change speed/duplex prior disabling the autonegotiation...");
|
||||
eth_duplex_t duplex = ETH_DUPLEX_HALF;
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
|
||||
|
||||
eth_speed_t speed = ETH_SPEED_10M;
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &speed));
|
||||
|
||||
// Disable autonegotiation and change speed to 10 Mbps and duplex to half
|
||||
ESP_LOGI(TAG, "disable the autonegotiation and change the speed/duplex...");
|
||||
auto_nego_en = false;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &exp_autoneg_en));
|
||||
TEST_ASSERT_EQUAL(false, exp_autoneg_en);
|
||||
|
||||
// set new duplex mode
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
|
||||
|
||||
// set new speed
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &speed));
|
||||
|
||||
// *** LAN8720 deviation ***
|
||||
// Rationale: When the device is in manual 100BASE-TX or 10BASE-T modes with Auto-MDIX enabled, the PHY does not link to a
|
||||
// link partner that is configured for auto-negotiation. See LAN8720 errata for more details.
|
||||
#if CONFIG_ETH_TEST_LAN8720_ERRATA_ENABLED
|
||||
TEST_ESP_OK(eth_test_set_phy_reg_bits(eth_handle, 27, 0x8000, 3));
|
||||
#endif
|
||||
|
||||
// start the driver and wait for connection establish
|
||||
esp_eth_start(eth_handle);
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
|
||||
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_HALF, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_10M, exp_speed);
|
||||
|
||||
// Change speed back to 100 Mbps
|
||||
esp_eth_stop(eth_handle);
|
||||
ESP_LOGI(TAG, "change speed again...");
|
||||
speed = ETH_SPEED_100M;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &speed));
|
||||
|
||||
// start the driver and wait for connection establish
|
||||
esp_eth_start(eth_handle);
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_HALF, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_100M, exp_speed);
|
||||
|
||||
// Change duplex back to full
|
||||
esp_eth_stop(eth_handle);
|
||||
ESP_LOGI(TAG, "change duplex again...");
|
||||
duplex = ETH_DUPLEX_FULL;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
|
||||
|
||||
// start the driver and wait for connection establish
|
||||
esp_eth_start(eth_handle);
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
|
||||
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_FULL, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_100M, exp_speed);
|
||||
|
||||
ESP_LOGI(TAG, "try to change speed/duplex when driver is started and autonegotiation disabled...");
|
||||
speed = ETH_SPEED_10M;
|
||||
duplex = ETH_DUPLEX_HALF;
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &speed));
|
||||
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
|
||||
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_FULL, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_100M, exp_speed);
|
||||
|
||||
ESP_LOGI(TAG, "change the speed/duplex to 10 Mbps/half and then enable autonegotiation...");
|
||||
esp_eth_stop(eth_handle);
|
||||
speed = ETH_SPEED_10M;
|
||||
duplex = ETH_DUPLEX_HALF;
|
||||
|
||||
// set new duplex mode
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
|
||||
|
||||
// set new speed
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &speed));
|
||||
|
||||
// start the driver and wait for connection establish
|
||||
esp_eth_start(eth_handle);
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
|
||||
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_HALF, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_10M, exp_speed);
|
||||
|
||||
esp_eth_stop(eth_handle);
|
||||
auto_nego_en = true;
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en);
|
||||
|
||||
// *** LAN8720 deviation ***
|
||||
// Rationale: See above
|
||||
#ifdef CONFIG_TARGET_ETH_PHY_DEVICE_LAN8720
|
||||
TEST_ESP_OK(eth_test_clear_phy_reg_bits(eth_handle, 27, 0x8000, 3));
|
||||
#endif
|
||||
|
||||
esp_eth_start(eth_handle);
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &exp_autoneg_en));
|
||||
TEST_ASSERT_EQUAL(true, exp_autoneg_en);
|
||||
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
|
||||
|
||||
// verify autonegotiation result (expecting the best link configuration)
|
||||
TEST_ASSERT_EQUAL(ETH_DUPLEX_FULL, exp_duplex);
|
||||
TEST_ASSERT_EQUAL(ETH_SPEED_100M, exp_speed);
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// use static semaphore to avoid dynamic allocation and so need for de-allocation in case of test failure
|
||||
static SemaphoreHandle_t loopback_test_case_data_received;
|
||||
static StaticSemaphore_t loopback_test_case_data_received_buffer;
|
||||
static esp_err_t loopback_test_case_incoming_handler(esp_eth_handle_t eth_handle, uint8_t *buffer, uint32_t length, void *priv)
|
||||
{
|
||||
TEST_ASSERT(memcmp(priv, buffer, LOOPBACK_TEST_PACKET_SIZE) == 0);
|
||||
xSemaphoreGive(loopback_test_case_data_received);
|
||||
free(buffer);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
TEST_CASE("ethernet io loopback", "[ethernet]")
|
||||
{
|
||||
// get handles from common module initialized by setUp()
|
||||
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
|
||||
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
|
||||
|
||||
loopback_test_case_data_received = xSemaphoreCreateBinaryStatic(&loopback_test_case_data_received_buffer);
|
||||
EventBits_t bits = 0;
|
||||
// Disable autonegotiation to manually set speed and duplex mode
|
||||
bool auto_nego_en = false;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
bool loopback_en = true;
|
||||
// *** PHY loopback not supported deviation ***
|
||||
// Rationale: Some PHYs do not support loopback at all
|
||||
#if CONFIG_ETH_TEST_LOOPBACK_DISABLED
|
||||
TEST_ASSERT(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en) == ESP_ERR_NOT_SUPPORTED);
|
||||
return;
|
||||
#else
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
|
||||
#endif
|
||||
|
||||
eth_duplex_t duplex_modes[] = {ETH_DUPLEX_HALF, ETH_DUPLEX_FULL};
|
||||
eth_speed_t speeds[] = {ETH_SPEED_100M, ETH_SPEED_10M};
|
||||
emac_frame_t* test_packet = (emac_frame_t*)eth_test_alloc(LOOPBACK_TEST_PACKET_SIZE);
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, test_packet->src);
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, test_packet->dest);
|
||||
for(size_t i = 0; i < LOOPBACK_TEST_PACKET_SIZE-ETH_HEADER_LEN; i++){
|
||||
test_packet->data[i] = rand() & 0xff;
|
||||
}
|
||||
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, loopback_test_case_incoming_handler, test_packet));
|
||||
|
||||
for (int i = 0; i < sizeof(speeds) / sizeof(eth_speed_t); i++) {
|
||||
eth_speed_t expected_speed = speeds[i];
|
||||
for (int j = 0; j < sizeof(duplex_modes) / sizeof(eth_duplex_t); j++) {
|
||||
eth_duplex_t expected_duplex = duplex_modes[j];
|
||||
ESP_LOGI(TAG, "Test with %s Mbps %s duplex.", expected_speed == ETH_SPEED_10M ? "10" : "100", expected_duplex == ETH_DUPLEX_HALF ? "half" : "full");
|
||||
// *** 10 Mbps loopback disabled deviation ***
|
||||
// Rationale: Some PHYs do not support loopback at 10 Mbps
|
||||
#if CONFIG_ETH_TEST_10MB_LOOPBACK_DISABLED
|
||||
if ((expected_speed == ETH_SPEED_10M)) {
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &expected_speed));
|
||||
continue;
|
||||
} else if (expected_speed == ETH_SPEED_100M) {
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &expected_speed));
|
||||
}
|
||||
#else
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &expected_speed));
|
||||
#endif
|
||||
if ((expected_duplex == ETH_DUPLEX_HALF)) {
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &expected_duplex));
|
||||
continue;
|
||||
} else if (expected_duplex == ETH_DUPLEX_FULL) {
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &expected_duplex));
|
||||
}
|
||||
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle));
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
|
||||
eth_speed_t actual_speed = -1;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &actual_speed));
|
||||
TEST_ASSERT_EQUAL(expected_speed, actual_speed);
|
||||
|
||||
eth_duplex_t actual_duplex = -1;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &actual_duplex));
|
||||
TEST_ASSERT_EQUAL(expected_duplex, actual_duplex);
|
||||
|
||||
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_packet, LOOPBACK_TEST_PACKET_SIZE));
|
||||
/* 10Mbps RMII loopback may have timing issues due to clock division architecture. RMII CLK stays 50MHz while the data signal must be held still for 10 cycles to
|
||||
achieve the speed reduction. Everything, including control signals must be perfectly synchronized. This may be a challenge for some PHYs or PCB layouts.*/
|
||||
if (expected_speed == ETH_SPEED_10M) {
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if(xSemaphoreTake(loopback_test_case_data_received, pdMS_TO_TICKS(1000)) != pdTRUE) {
|
||||
ESP_LOGW(TAG, "Timeout waiting for data received for 10 Mbps mode, trying again...");
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
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_start(eth_handle));
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_packet, LOOPBACK_TEST_PACKET_SIZE));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// *** 10 Mbps loopback ignore failures deviation ***
|
||||
// Rationale: 10 Mbps loopback may be supported by PHY but the test is not reliable.
|
||||
#if !CONFIG_ETH_TEST_10MB_LOOPBACK_IGNORE_FAILURES
|
||||
TEST_ASSERT_LESS_THAN(3, i);
|
||||
#endif
|
||||
} else {
|
||||
TEST_ASSERT(xSemaphoreTake(loopback_test_case_data_received, pdMS_TO_TICKS(1000)) == pdTRUE);
|
||||
}
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
}
|
||||
}
|
||||
|
||||
// Test enabling autonegotiation when loopback is disabled
|
||||
ESP_LOGI(TAG, "Test enabling autonegotiation without loopback.");
|
||||
loopback_en = false;
|
||||
auto_nego_en = true;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
auto_nego_en = false;
|
||||
loopback_en = true;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
|
||||
// Test with enabled autonegotiaton
|
||||
ESP_LOGI(TAG, "Test with enabled autonegotiation.");
|
||||
auto_nego_en = true;
|
||||
// *** Loopback with autonegotiation deviation ***
|
||||
// Rationale: Some PHYs do not support autonegotiation with loopback enabled.
|
||||
#if CONFIG_ETH_TEST_LOOPBACK_WITH_AUTONEGOTIATION_DISABLED
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
// Test passes - these devices correctly report autonegotiation is not supported with loopback
|
||||
return;
|
||||
#endif
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle));
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
|
||||
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_packet, LOOPBACK_TEST_PACKET_SIZE));
|
||||
TEST_ASSERT(xSemaphoreTake(loopback_test_case_data_received, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS)) == pdTRUE);
|
||||
|
||||
loopback_en = false;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
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_CASE("ethernet event test", "[ethernet]")
|
||||
{
|
||||
// get handles from common module initialized by setUp()
|
||||
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
|
||||
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
|
||||
|
||||
EventBits_t bits = 0;
|
||||
// this test only test layer2 event, so don't need to register input callback (i.e. esp_eth_update_input_path)
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle));
|
||||
/* wait for connection start */
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, true, true, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
|
||||
/* wait for connection establish */
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
// 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_CASE("ethernet dhcp test", "[ethernet][esp-netif]")
|
||||
{
|
||||
// get handles from common module initialized by setUp()
|
||||
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
|
||||
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
|
||||
|
||||
EventBits_t bits = 0;
|
||||
// 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);
|
||||
// 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_CASE("ethernet start/stop stress test with IP stack", "[ethernet][esp-netif]")
|
||||
{
|
||||
// get handles from common module initialized by setUp()
|
||||
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
|
||||
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
|
||||
|
||||
EventBits_t bits = 0;
|
||||
|
||||
for(int j = 0; j < 2; j++) {
|
||||
// run the start/stop test with disabled auto-negotiation
|
||||
if (j > 0) {
|
||||
ESP_LOGI(TAG, "Run with auto-negotiation disabled...");
|
||||
bool auto_nego_en = false;
|
||||
bool exp_autoneg_en;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &exp_autoneg_en));
|
||||
TEST_ASSERT_EQUAL(false, exp_autoneg_en);
|
||||
// *** LAN8720 deviation ***
|
||||
// Rationale: When the device is in manual 100BASE-TX or 10BASE-T modes with Auto-MDIX enabled, the PHY does not link to a
|
||||
// link partner that is configured for auto-negotiation. See LAN8720 errata for more details.
|
||||
#if CONFIG_ETH_TEST_LAN8720_ERRATA_ENABLED
|
||||
TEST_ESP_OK(eth_test_set_phy_reg_bits(eth_handle, 27, 0x8000, 3));
|
||||
#endif
|
||||
}
|
||||
for (int i = 0; i < 10; i++) {
|
||||
// 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);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_HEADERS_COMPLETE:
|
||||
ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADERS_COMPLETE");
|
||||
break;
|
||||
case HTTP_EVENT_ON_DATA:
|
||||
esp_rom_md5_update(&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;
|
||||
case HTTP_EVENT_REDIRECT:
|
||||
ESP_LOGI(TAG, "HTTP_EVENT_REDIRECT");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void eth_start_download(void)
|
||||
{
|
||||
esp_rom_md5_init(&md5_context);
|
||||
esp_http_client_config_t config = {
|
||||
.url = "https://dl.espressif.com/dl/misc/2MB.bin",
|
||||
.cert_pem = dl_espressif_com_root_cert_pem_start,
|
||||
.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));
|
||||
esp_rom_md5_final(digest, &md5_context);
|
||||
}
|
||||
|
||||
TEST_CASE("ethernet download test", "[ethernet][esp-netif]")
|
||||
{
|
||||
// get handles from common module initialized by setUp()
|
||||
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
|
||||
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
|
||||
|
||||
EventBits_t bits = 0;
|
||||
// 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);
|
||||
|
||||
eth_start_download();
|
||||
// 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_EQUAL_UINT8_ARRAY(expect_digest, digest, sizeof(digest));
|
||||
|
||||
// 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);
|
||||
}
|
||||
@@ -1,547 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_eth_test_utils.h"
|
||||
#include "arpa/inet.h" // for ntohs, etc.
|
||||
#include "esp_log.h"
|
||||
|
||||
#define TEST_ETH_TYPE 0x3300
|
||||
#define TEST_CTRL_ETH_TYPE (TEST_ETH_TYPE + 1)
|
||||
|
||||
#define WAIT_FOR_CONN_TMO_MS (5000)
|
||||
#define WAIT_AFTER_CONN_MS (2500)
|
||||
#define WAIT_AFTER_CONN_TMO_MS (20000)
|
||||
|
||||
#define ETH_BROADCAST_RECV_BIT BIT(0)
|
||||
#define ETH_MULTICAST_RECV_BIT BIT(1)
|
||||
#define ETH_UNICAST_RECV_BIT BIT(2)
|
||||
#define ETH_POKE_RESP_RECV_BIT BIT(3)
|
||||
|
||||
#define POKE_REQ (0xFA)
|
||||
#define POKE_RESP (0xFB)
|
||||
#define DUMMY_TRAFFIC (0xFF)
|
||||
|
||||
static const char *TAG = "esp32_eth_test_l2";
|
||||
typedef struct
|
||||
{
|
||||
EventGroupHandle_t eth_event_group;
|
||||
uint8_t dst_mac_addr[ETH_ADDR_LEN];
|
||||
int unicast_rx_cnt;
|
||||
int multicast_rx_cnt;
|
||||
int brdcast_rx_cnt;
|
||||
|
||||
bool check_rx_data;
|
||||
} recv_info_t;
|
||||
|
||||
static recv_info_t s_recv_info;
|
||||
|
||||
static esp_err_t l2_packet_txrx_test_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv) {
|
||||
recv_info_t *recv_info = (recv_info_t*)priv;
|
||||
EventGroupHandle_t eth_event_group = recv_info->eth_event_group;
|
||||
emac_frame_t *pkt = (emac_frame_t *)buffer;
|
||||
// check header
|
||||
if (pkt->proto == ntohs(TEST_ETH_TYPE)) { // data packet
|
||||
uint8_t local_mac_addr[ETH_ADDR_LEN];
|
||||
esp_eth_ioctl(hdl, ETH_CMD_G_MAC_ADDR, local_mac_addr);
|
||||
// check data content
|
||||
if (recv_info->check_rx_data) {
|
||||
if (length == 1024) {
|
||||
for (int i = 0; i < (length - ETH_HEADER_LEN); ++i) {
|
||||
if (pkt->data[i] != (i & 0xff)) {
|
||||
printf("payload mismatch\n");
|
||||
free(buffer);
|
||||
return ESP_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (memcmp(pkt->dest, "\xff\xff\xff\xff\xff\xff", ETH_ADDR_LEN) == 0) {
|
||||
recv_info->brdcast_rx_cnt++;
|
||||
xEventGroupSetBits(eth_event_group, ETH_BROADCAST_RECV_BIT);
|
||||
} else if (pkt->dest[0] & 0x1) {
|
||||
recv_info->multicast_rx_cnt++;
|
||||
xEventGroupSetBits(eth_event_group, ETH_MULTICAST_RECV_BIT);
|
||||
} else if (memcmp(pkt->dest, local_mac_addr, ETH_ADDR_LEN) == 0) {
|
||||
recv_info->unicast_rx_cnt++;
|
||||
xEventGroupSetBits(eth_event_group, ETH_UNICAST_RECV_BIT);
|
||||
}
|
||||
} else if (ntohs(pkt->proto) == TEST_CTRL_ETH_TYPE) { // control packet
|
||||
if (pkt->data[0] == POKE_RESP) {
|
||||
memcpy(recv_info->dst_mac_addr, pkt->src, ETH_ADDR_LEN); // test PC source MAC addr is destination for us
|
||||
printf("Poke response received\n");
|
||||
xEventGroupSetBits(eth_event_group, ETH_POKE_RESP_RECV_BIT);
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The function sends a "POKE" request message over the Ethernet and waits until the test script sends a reply.
|
||||
* Multiple "POKE" attempts are issued when timeout for the reply expires.
|
||||
* This function is used to drive the test flow and to ensure that data path between the test points
|
||||
* has been established. I.e. if DUT is connected in network with a switch, even if link is indicated up,
|
||||
* it may take some time the switch starts forwarding the associated port (e.g. it runs RSTP at first).
|
||||
*/
|
||||
void poke_and_wait(esp_eth_handle_t eth_handle, void *data, uint16_t size, uint8_t *dst_mac_addr, EventGroupHandle_t eth_event_group)
|
||||
{
|
||||
// create a control frame to control test flow between the UT and the Python test script
|
||||
emac_frame_t *ctrl_pkt = calloc(1, 60);
|
||||
ctrl_pkt->proto = htons(TEST_CTRL_ETH_TYPE);
|
||||
memset(ctrl_pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr
|
||||
esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, ctrl_pkt->src);
|
||||
|
||||
ctrl_pkt->data[0] = POKE_REQ;
|
||||
if (data != NULL && size > 0) {
|
||||
memcpy(&ctrl_pkt->data[1], data, size);
|
||||
}
|
||||
|
||||
uint32_t tmo;
|
||||
uint32_t i;
|
||||
esp_err_t tx_err = ESP_OK;
|
||||
for(tmo = 0, i = 1; tmo < WAIT_AFTER_CONN_TMO_MS; tmo += WAIT_AFTER_CONN_MS, i++) {
|
||||
printf("Poke attempt #%" PRIu32 "\n", i);
|
||||
tx_err = esp_eth_transmit(eth_handle, ctrl_pkt, 60);
|
||||
if (tx_err != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
EventBits_t bits = xEventGroupWaitBits(eth_event_group, ETH_POKE_RESP_RECV_BIT,
|
||||
true, true, pdMS_TO_TICKS(WAIT_AFTER_CONN_MS));
|
||||
if ((bits & ETH_POKE_RESP_RECV_BIT) == ETH_POKE_RESP_RECV_BIT) {
|
||||
if (dst_mac_addr != NULL) {
|
||||
memcpy(dst_mac_addr, s_recv_info.dst_mac_addr, ETH_ADDR_LEN);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(ctrl_pkt);
|
||||
// assert only after the allocated memory is freed
|
||||
TEST_ASSERT(tmo < WAIT_AFTER_CONN_TMO_MS);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(ESP_OK, tx_err, "esp_eth_transmit failed");
|
||||
}
|
||||
|
||||
TEST_CASE("ethernet broadcast transmit", "[ethernet_l2]")
|
||||
{
|
||||
// assign values to variables from common module initialized by setUp()
|
||||
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
|
||||
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
|
||||
|
||||
// use static event group to avoid dynamic memory allocation
|
||||
StaticEventGroup_t eth_event_rx_group_buffer;
|
||||
EventGroupHandle_t eth_event_rx_group = xEventGroupCreateStatic(ð_event_rx_group_buffer);
|
||||
TEST_ASSERT(eth_event_rx_group != NULL);
|
||||
|
||||
s_recv_info.eth_event_group = eth_event_rx_group;
|
||||
s_recv_info.check_rx_data = false;
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
|
||||
uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, local_mac_addr));
|
||||
// test app will parse the DUT MAC from this line of log output
|
||||
printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
|
||||
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
|
||||
|
||||
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
|
||||
|
||||
EventBits_t bits = 0;
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
// if DUT is connected in network with switch: even if link is indicated up, it may take some time the switch
|
||||
// starts switching the associated port (e.g. it runs RSTP at first)
|
||||
poke_and_wait(eth_handle, NULL, 0, NULL, eth_event_rx_group);
|
||||
|
||||
emac_frame_t *pkt = (emac_frame_t *)eth_test_alloc(1024);
|
||||
pkt->proto = htons(TEST_ETH_TYPE);
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, pkt->src));
|
||||
memset(pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr
|
||||
for (int i = 0; i < (1024 - ETH_HEADER_LEN); ++i){
|
||||
pkt->data[i] = i & 0xff;
|
||||
}
|
||||
|
||||
TEST_ESP_OK(esp_eth_transmit(eth_handle, pkt, 1024));
|
||||
// give it some time to complete transmit
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
}
|
||||
|
||||
TEST_CASE("ethernet recv_pkt", "[ethernet_l2]")
|
||||
{
|
||||
// get handles from common module initialized by setUp()
|
||||
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
|
||||
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
|
||||
|
||||
// use static event group to avoid dynamic memory allocation
|
||||
StaticEventGroup_t eth_event_rx_group_buffer;
|
||||
EventGroupHandle_t eth_event_rx_group = xEventGroupCreateStatic(ð_event_rx_group_buffer);
|
||||
TEST_ASSERT(eth_event_rx_group != NULL);
|
||||
|
||||
s_recv_info.eth_event_group = eth_event_rx_group;
|
||||
s_recv_info.check_rx_data = true;
|
||||
|
||||
uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, local_mac_addr));
|
||||
// test app will parse the DUT MAC from this line of log output
|
||||
printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
|
||||
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
|
||||
|
||||
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
|
||||
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
|
||||
|
||||
EventBits_t bits = 0;
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
// if DUT is connected in network with switch: even if link is indicated up, it may take some time the switch
|
||||
// starts switching the associated port (e.g. it runs RSTP at first)
|
||||
poke_and_wait(eth_handle, NULL, 0, NULL, eth_event_rx_group);
|
||||
|
||||
// ---------------------------------------
|
||||
printf("Enable receive all multicast\n");
|
||||
// ---------------------------------------
|
||||
EventBits_t expected_bits = ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
int expected_multicast_rx_cnt = 2;
|
||||
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
bool all_multicast = true;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_ALL_MULTICAST, &all_multicast));
|
||||
bits = 0;
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT);
|
||||
printf("Filter configured\n");
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
|
||||
true, true, pdMS_TO_TICKS(1000));
|
||||
printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
|
||||
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) == expected_bits);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.unicast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.brdcast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(expected_multicast_rx_cnt, s_recv_info.multicast_rx_cnt);
|
||||
|
||||
// ---------------------------------------
|
||||
printf("Disable receive all multicast\n");
|
||||
// ---------------------------------------
|
||||
// *** W5500 deviation ***
|
||||
// Rationale: The W5500 always receives IPv6 multicast packets, even if the filter is set to block multicast.
|
||||
// It's not documented behavior, but it's observed on the real hardware.
|
||||
#if CONFIG_ETH_TEST_W5500_IP6_MCAST_DEVIATION_ENABLED
|
||||
expected_bits = ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
expected_multicast_rx_cnt = 1;
|
||||
#else
|
||||
expected_bits = ETH_BROADCAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
expected_multicast_rx_cnt = 0;
|
||||
#endif
|
||||
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
all_multicast = false;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_ALL_MULTICAST, &all_multicast));
|
||||
bits = 0;
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT);
|
||||
printf("Filter configured\n");
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
|
||||
true, true, pdMS_TO_TICKS(1000));
|
||||
printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
|
||||
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) == expected_bits);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.unicast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.brdcast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(expected_multicast_rx_cnt, s_recv_info.multicast_rx_cnt);
|
||||
|
||||
|
||||
// ---------------------------------------
|
||||
printf("Add multicast addresses to the filter\n");
|
||||
// ---------------------------------------
|
||||
expected_bits = ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
expected_multicast_rx_cnt = 2;
|
||||
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
uint8_t multicast_addr_ip4[ETH_ADDR_LEN] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x00};
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_ADD_MAC_FILTER, multicast_addr_ip4));
|
||||
uint8_t multicast_addr_ip6[ETH_ADDR_LEN] = {0x33, 0x33, 0x00, 0x00, 0x00, 0x00};
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_ADD_MAC_FILTER, multicast_addr_ip6));
|
||||
bits = 0;
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT);
|
||||
printf("Filter configured\n");
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
|
||||
true, true, pdMS_TO_TICKS(1000));
|
||||
printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
|
||||
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) == expected_bits);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.unicast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.brdcast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(expected_multicast_rx_cnt, s_recv_info.multicast_rx_cnt);
|
||||
|
||||
// --------------------------------------------
|
||||
printf("Remove one multicast address from the filter\n");
|
||||
// --------------------------------------------
|
||||
expected_bits = ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
expected_multicast_rx_cnt = 1;
|
||||
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_DEL_MAC_FILTER, multicast_addr_ip4));
|
||||
bits = 0;
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT);
|
||||
printf("Filter configured\n");
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
|
||||
true, true, pdMS_TO_TICKS(1000));
|
||||
printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
|
||||
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) == expected_bits);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.unicast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.brdcast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(expected_multicast_rx_cnt, s_recv_info.multicast_rx_cnt);
|
||||
|
||||
// ----------------------------------------------
|
||||
printf("Remove all multicast addresses from the filter\n");
|
||||
// ----------------------------------------------
|
||||
// *** W5500 deviation ***
|
||||
// Rationale: The W5500 always receives IPv6 multicast packets, even if the filter is set to block multicast.
|
||||
// It's not documented behavior, but it's observed on the real hardware.
|
||||
#if CONFIG_ETH_TEST_W5500_IP6_MCAST_DEVIATION_ENABLED
|
||||
expected_bits = ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
expected_multicast_rx_cnt = 1;
|
||||
#else
|
||||
expected_bits = ETH_BROADCAST_RECV_BIT | ETH_UNICAST_RECV_BIT;
|
||||
expected_multicast_rx_cnt = 0;
|
||||
#endif
|
||||
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
// *** W5500 deviation ***
|
||||
// Rationale: The W5500 always receives IPv6 multicast packets and hence filter delete fails.
|
||||
#if CONFIG_ETH_TEST_W5500_IP6_MCAST_DEVIATION_ENABLED
|
||||
TEST_ESP_ERR(ESP_FAIL, esp_eth_ioctl(eth_handle, ETH_CMD_DEL_MAC_FILTER, multicast_addr_ip6));
|
||||
#else
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_DEL_MAC_FILTER, multicast_addr_ip6));
|
||||
#endif
|
||||
bits = 0;
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT);
|
||||
printf("Filter configured\n");
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
|
||||
true, true, pdMS_TO_TICKS(1000));
|
||||
printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
|
||||
TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) == expected_bits);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.unicast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(1, s_recv_info.brdcast_rx_cnt);
|
||||
TEST_ASSERT_EQUAL(expected_multicast_rx_cnt, s_recv_info.multicast_rx_cnt);
|
||||
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]")
|
||||
{
|
||||
// *** SPI Ethernet modules deviation ***
|
||||
// Rationale: The SPI bus is bottleneck when reading received frames from the module. The Rx Task would
|
||||
// occupy all the resources under heavy Rx traffic and it would not be possible to access
|
||||
// the Ethernet module to stop it. Therefore, the test task priority is set higher than the Rx task
|
||||
// to be able to preempt the Rx task.
|
||||
#if CONFIG_ETH_TEST_STRESS_TEST_TASK_PRIO > -1
|
||||
printf("task priority: %d\n", uxTaskPriorityGet(NULL));
|
||||
vTaskPrioritySet(NULL, CONFIG_ETH_TEST_STRESS_TEST_TASK_PRIO);
|
||||
#endif // CONFIG_ETH_TEST_STRESS_TEST_TASK_PRIO > 0
|
||||
// get handles from common module initialized by setUp()
|
||||
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
|
||||
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
|
||||
|
||||
// use static event group to avoid dynamic memory allocation
|
||||
StaticEventGroup_t eth_event_rx_group_buffer;
|
||||
EventGroupHandle_t eth_event_rx_group = xEventGroupCreateStatic(ð_event_rx_group_buffer);
|
||||
TEST_ASSERT(eth_event_rx_group != NULL);
|
||||
|
||||
s_recv_info.eth_event_group = eth_event_rx_group;
|
||||
s_recv_info.check_rx_data = false;
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
|
||||
uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
|
||||
uint8_t dest_mac_addr[ETH_ADDR_LEN] = {};
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, local_mac_addr));
|
||||
// test app will parse the DUT MAC from this line of log output
|
||||
printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
|
||||
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
|
||||
|
||||
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
|
||||
|
||||
// create dummy data packet used for traffic generation
|
||||
emac_frame_t *pkt = (emac_frame_t *)eth_test_alloc(1500);
|
||||
pkt->proto = htons(TEST_ETH_TYPE);
|
||||
memcpy(pkt->src, local_mac_addr, ETH_ADDR_LEN);
|
||||
|
||||
EventBits_t bits = 0;
|
||||
printf("EMAC start/stop stress test under heavy Tx traffic\n");
|
||||
for (int tx_i = 0; tx_i < 10; tx_i++) {
|
||||
printf("Tx Test iteration %d\n", tx_i);
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
// at first, check that Tx/Rx path works as expected by poking the test script
|
||||
// this also serves as main PASS/FAIL criteria
|
||||
poke_and_wait(eth_handle, &tx_i, sizeof(tx_i), dest_mac_addr, eth_event_rx_group);
|
||||
memcpy(pkt->dest, dest_mac_addr, ETH_ADDR_LEN);
|
||||
|
||||
// generate heavy Tx traffic
|
||||
for (int j = 0; j < 150; j++) {
|
||||
// return value is not checked on purpose since it is expected that it may fail time to time because
|
||||
// we may try to queue more packets than hardware is able to handle
|
||||
pkt->data[2] = j & 0xFF; // sequence number
|
||||
esp_eth_transmit(eth_handle, pkt, 1500);
|
||||
}
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(3000));
|
||||
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
|
||||
}
|
||||
|
||||
printf("EMAC start/stop stress test under heavy Rx traffic\n");
|
||||
for (int rx_i = 0; rx_i < 10; rx_i++) {
|
||||
printf("Rx Test iteration %d\n", rx_i);
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(WAIT_FOR_CONN_TMO_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
poke_and_wait(eth_handle, &rx_i, sizeof(rx_i), NULL, eth_event_rx_group);
|
||||
|
||||
// wait for dummy traffic
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT);
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT, true, true, pdMS_TO_TICKS(3000));
|
||||
TEST_ASSERT((bits & ETH_UNICAST_RECV_BIT) == ETH_UNICAST_RECV_BIT);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(3000));
|
||||
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
|
||||
printf("Recv packets: %d\n", s_recv_info.unicast_rx_cnt);
|
||||
TEST_ASSERT_GREATER_THAN_INT32(0, s_recv_info.unicast_rx_cnt);
|
||||
}
|
||||
// Add an extra delay to be sure that there is no traffic generated by the test script during the driver un-installation.
|
||||
// It was observed unintended behavior of the switch used in test environment when link is set down under heavy load.
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
|
||||
TEST_CASE("heap utilization", "[ethernet_l2]")
|
||||
{
|
||||
// get handles from common module initialized by setUp()
|
||||
esp_eth_handle_t eth_handle = eth_test_get_eth_handle();
|
||||
EventGroupHandle_t eth_event_group = eth_test_get_default_event_group();
|
||||
|
||||
// use static event group to avoid dynamic memory allocation
|
||||
StaticEventGroup_t eth_event_rx_group_buffer;
|
||||
EventGroupHandle_t eth_event_rx_group = xEventGroupCreateStatic(ð_event_rx_group_buffer);
|
||||
TEST_ASSERT(eth_event_rx_group != NULL);
|
||||
|
||||
s_recv_info.eth_event_group = eth_event_rx_group;
|
||||
s_recv_info.check_rx_data = false;
|
||||
s_recv_info.unicast_rx_cnt = 0;
|
||||
s_recv_info.multicast_rx_cnt = 0;
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
|
||||
// *** PHY loopback not supported deviation ***
|
||||
// Rationale: Some Ethernet modules do not support internal loopback
|
||||
#if !CONFIG_ETH_TEST_LOOPBACK_DISABLED
|
||||
// ---------------------------------------
|
||||
// Loopback greatly simplifies the test !!
|
||||
// ---------------------------------------
|
||||
bool loopback_en = true;
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en));
|
||||
printf("PHY loopback is enabled\n");
|
||||
#else
|
||||
printf("PHY loopback is disabled\n");
|
||||
#endif
|
||||
|
||||
uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
|
||||
TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, local_mac_addr));
|
||||
// test app will parse the DUT MAC from this line of log output
|
||||
printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
|
||||
local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
|
||||
|
||||
TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &s_recv_info));
|
||||
|
||||
// start the driver
|
||||
TEST_ESP_OK(esp_eth_start(eth_handle));
|
||||
// wait for connection start
|
||||
EventBits_t bits = 0;
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, true, true, pdMS_TO_TICKS(ETH_START_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
|
||||
// wait for connection establish
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(ETH_CONNECT_TIMEOUT_MS));
|
||||
TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
|
||||
|
||||
// create test frame
|
||||
uint8_t test_pkt_buffer[ETH_MAX_PACKET_SIZE];
|
||||
emac_frame_t *test_pkt = (emac_frame_t *)test_pkt_buffer;
|
||||
test_pkt->proto = htons(TEST_ETH_TYPE);
|
||||
memcpy(test_pkt->dest, local_mac_addr, ETH_ADDR_LEN); // our addr so the frame is not filtered at loopback by MAC
|
||||
memcpy(test_pkt->src, local_mac_addr, ETH_ADDR_LEN);
|
||||
// fill with data
|
||||
for (int i = 0; i < ETH_MAX_PAYLOAD_LEN; i++) {
|
||||
test_pkt->data[i] = i & 0xFF;
|
||||
}
|
||||
|
||||
// *** PHY loopback not supported deviation ***
|
||||
// Rationale: Some Ethernet modules do not support internal loopback so we need to loop frames back at test PC side
|
||||
#if CONFIG_ETH_TEST_LOOPBACK_DISABLED
|
||||
uint8_t dest_mac_addr[ETH_ADDR_LEN] = {};
|
||||
poke_and_wait(eth_handle, NULL, 0, dest_mac_addr, eth_event_rx_group);
|
||||
memcpy(test_pkt->dest, dest_mac_addr, ETH_ADDR_LEN); // overwrite destination address with test PC addr
|
||||
#endif
|
||||
|
||||
uint16_t transmit_size;
|
||||
size_t free_heap = 0;
|
||||
uint8_t *p;
|
||||
ESP_LOGI(TAG, "Allocate all heap");
|
||||
do {
|
||||
free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
|
||||
ESP_LOGD(TAG, "free heap: %i B", free_heap);
|
||||
p = eth_test_alloc(free_heap);
|
||||
} while (p != NULL && free_heap > 1024);
|
||||
ESP_LOGI(TAG, "remaining free heap: %i B", free_heap);
|
||||
TEST_ASSERT_LESS_OR_EQUAL_INT(1024, free_heap);
|
||||
transmit_size = ETH_MAX_PAYLOAD_LEN;
|
||||
ESP_LOGI(TAG, "Verify that the driver is able to recover from `no mem` error");
|
||||
|
||||
// define number of iteration to fill device internal buffer (if driver's flush function didn't work as expected)
|
||||
int32_t max_i = CONFIG_ETH_TEST_FILL_RX_BUFFER_ITERATIONS + 2;
|
||||
|
||||
for (int32_t i = 0; i < max_i; i++) { // be sure to fill all the descriptors
|
||||
ESP_LOGI(TAG, "transmit frame size: %" PRIu16 ", i = %" PRIi32, transmit_size, i);
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT);
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
|
||||
// wait for dummy traffic
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT, true, true, pdMS_TO_TICKS(200));
|
||||
TEST_ASSERT(bits == 0); // we don't received the frame due to "no mem"
|
||||
}
|
||||
ESP_LOGI(TAG, "Free previously allocated heap");
|
||||
eth_test_free_all();
|
||||
free_heap = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT);
|
||||
ESP_LOGI(TAG, "free heap: %i B", free_heap);
|
||||
for (int32_t i = 0; i < max_i; i++) {
|
||||
ESP_LOGD(TAG, "transmit frame size: %" PRIu16 ", i = %" PRIi32, transmit_size, i);
|
||||
xEventGroupClearBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT);
|
||||
s_recv_info.brdcast_rx_cnt = 0;
|
||||
TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
|
||||
// wait for dummy traffic
|
||||
bits = xEventGroupWaitBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT, true, true, pdMS_TO_TICKS(200));
|
||||
TEST_ASSERT((bits & ETH_UNICAST_RECV_BIT) == ETH_UNICAST_RECV_BIT); // now, we should be able to receive frames again
|
||||
}
|
||||
TEST_ESP_OK(esp_eth_stop(eth_handle));
|
||||
bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(3000));
|
||||
TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
|
||||
}
|
||||
@@ -1,368 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_eth_driver.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_eth_test_utils.h"
|
||||
#include "esp_check.h"
|
||||
#include "ethernet_init.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
// Local override of TEST_ASSERT and TEST_ESP_OK to fix Unity file name reporting
|
||||
// when assertions are in a different file than the test. This temporarily sets Unity.
|
||||
// TestFile to __FILE__ so the correct file name is reported on assertion failure.
|
||||
#define ETH_TEST_ASSERT(condition) do { \
|
||||
const char* _unity_test_file_save = Unity.TestFile; \
|
||||
Unity.TestFile = __FILE__; \
|
||||
TEST_ASSERT(condition); \
|
||||
Unity.TestFile = _unity_test_file_save; \
|
||||
} while(0)
|
||||
|
||||
#define ETH_TEST_ESP_OK(rc) do { \
|
||||
const char* _unity_test_file_save = Unity.TestFile; \
|
||||
Unity.TestFile = __FILE__; \
|
||||
TEST_ASSERT_EQUAL_HEX32(ESP_OK, rc); \
|
||||
Unity.TestFile = _unity_test_file_save; \
|
||||
} while(0)
|
||||
|
||||
static const char *TAG = "esp32_eth_test_common";
|
||||
|
||||
static esp_eth_handle_t *s_eth_handles; // only because we use Ethernet Init component
|
||||
|
||||
static EventGroupHandle_t s_eth_event_group;
|
||||
static esp_eth_handle_t s_eth_handle;
|
||||
static esp_netif_t *s_eth_netif;
|
||||
static esp_eth_netif_glue_handle_t s_eth_glue;
|
||||
static void *s_memory_p[MAX_HEAP_ALLOCATION_POINTERS];
|
||||
|
||||
esp_err_t eth_test_set_phy_reg_bits(esp_eth_handle_t eth_handle, uint32_t reg_addr, uint32_t bitmask, uint32_t max_attempts)
|
||||
{
|
||||
|
||||
esp_eth_phy_reg_rw_data_t reg = {
|
||||
.reg_addr = reg_addr,
|
||||
.reg_value_p = NULL
|
||||
};
|
||||
uint32_t reg_value, reg_value_rb;
|
||||
|
||||
for (uint32_t i = 0; i < max_attempts; i++) {
|
||||
reg.reg_value_p = ®_value;
|
||||
esp_err_t ret = esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, ®);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
reg_value |= bitmask;
|
||||
ret = esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, ®);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
reg.reg_value_p = ®_value_rb;
|
||||
ret = esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, ®);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
// Check if the write was successful
|
||||
if ((reg_value_rb & bitmask) == bitmask) {
|
||||
return ESP_OK;
|
||||
}
|
||||
// Add delay only if not the last attempt
|
||||
if (i < max_attempts - 1) {
|
||||
ESP_LOGW(TAG, "Setting PHY register %04"PRIx32" failed, retrying... (attempt %"PRIu32" of %"PRIu32")", reg_addr, i + 1, max_attempts);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
esp_err_t eth_test_clear_phy_reg_bits(esp_eth_handle_t eth_handle, uint32_t reg_addr, uint32_t bitmask, uint32_t max_attempts)
|
||||
{
|
||||
esp_eth_phy_reg_rw_data_t reg = {
|
||||
.reg_addr = reg_addr,
|
||||
.reg_value_p = NULL
|
||||
};
|
||||
uint32_t reg_value, reg_value_rb;
|
||||
|
||||
for (uint32_t i = 0; i < max_attempts; i++) {
|
||||
reg.reg_value_p = ®_value;
|
||||
esp_err_t ret = esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, ®);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
reg_value &= ~bitmask;
|
||||
ret = esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, ®);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
reg.reg_value_p = ®_value_rb;
|
||||
ret = esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, ®);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
// Check if the write was successful
|
||||
if ((reg_value_rb & bitmask) == 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
// Add delay only if not the last attempt
|
||||
if (i < max_attempts - 1) {
|
||||
ESP_LOGW(TAG, "Clearing PHY register %04"PRIx32" failed, retrying... (attempt %"PRIu32" of %"PRIu32")", reg_addr, i + 1, max_attempts);
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
}
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
__attribute__((weak)) esp_err_t esp_eth_test_eth_init(esp_eth_handle_t *eth_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
uint8_t eth_port_cnt = 0;
|
||||
ESP_LOGI(TAG, "Default Ethernet Initialization...");
|
||||
ESP_GOTO_ON_ERROR(ethernet_init_all(&s_eth_handles, ð_port_cnt), err, TAG, "Failed to initialize Ethernet");
|
||||
ESP_GOTO_ON_FALSE(eth_port_cnt == 1, ESP_FAIL, err, TAG, "Multiple Ethernet devices detected, the test cannot continue...");
|
||||
*eth_handle = s_eth_handles[0];
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
__attribute__((weak)) esp_err_t esp_eth_test_eth_deinit(esp_eth_handle_t eth_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_LOGI(TAG, "Default Ethernet Deinitialization...");
|
||||
ESP_GOTO_ON_FALSE(s_eth_handles[0] == eth_handle, ESP_FAIL, err, TAG, "Ethernet handle does not match");
|
||||
ESP_GOTO_ON_ERROR(ethernet_deinit_all(s_eth_handles), err, TAG, "Failed to deinitialize Ethernet");
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
if (strstr(Unity.CurrentDetail1, "[skip_setup_teardown]") != NULL) {
|
||||
return;
|
||||
}
|
||||
// Use Unity to check if the initialization is successful, it's intended to fail the test if initialization fails
|
||||
s_eth_event_group = xEventGroupCreate();
|
||||
ETH_TEST_ASSERT(s_eth_event_group != NULL);
|
||||
ETH_TEST_ESP_OK(esp_event_loop_create_default());
|
||||
ETH_TEST_ESP_OK(esp_eth_test_eth_init(&s_eth_handle));
|
||||
eth_dev_info_t phy_info = ethernet_init_get_dev_info(s_eth_handle);
|
||||
ESP_LOGI(TAG, "DUT PHY: %s", phy_info.name);
|
||||
if (strstr(Unity.CurrentDetail1, "[esp-netif]") != NULL) {
|
||||
esp_netif_init();
|
||||
// create TCP/IP netif
|
||||
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
|
||||
s_eth_netif = esp_netif_new(&netif_cfg);
|
||||
// combine driver with netif
|
||||
s_eth_glue = esp_eth_new_netif_glue(s_eth_handle);
|
||||
ETH_TEST_ESP_OK(esp_netif_attach(s_eth_netif, s_eth_glue));
|
||||
// register user defined event handlers
|
||||
ETH_TEST_ESP_OK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, ð_test_got_ip_event_handler, s_eth_event_group));
|
||||
}
|
||||
ETH_TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_test_default_event_handler, s_eth_event_group));
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
eth_test_free_all();
|
||||
|
||||
if (strstr(Unity.CurrentDetail1, "[skip_setup_teardown]") != NULL) {
|
||||
return;
|
||||
}
|
||||
// Use regular error checking instead of ETH_TEST_ESP_OK to ensure cleanup always completes
|
||||
// even if errors occur (e.g., if test failed and resources are in invalid state)
|
||||
// This is critical because TEST_ASSERT failures use longjmp which can skip cleanup
|
||||
esp_err_t ret;
|
||||
|
||||
if (s_eth_glue != NULL) {
|
||||
ret = esp_eth_del_netif_glue(s_eth_glue);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "esp_eth_del_netif_glue failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
s_eth_glue = NULL;
|
||||
}
|
||||
if (s_eth_netif != NULL) {
|
||||
esp_netif_destroy(s_eth_netif);
|
||||
s_eth_netif = NULL;
|
||||
|
||||
ret = esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP, eth_test_got_ip_event_handler);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "esp_event_handler_unregister IP event failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
}
|
||||
|
||||
ret = esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_test_default_event_handler);
|
||||
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
|
||||
// ESP_ERR_INVALID_STATE is OK - handler might already be unregistered
|
||||
ESP_LOGW(TAG, "esp_event_handler_unregister failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
if (s_eth_handle != NULL) {
|
||||
ret = esp_eth_test_eth_deinit(s_eth_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "esp_eth_test_eth_deinit failed: %s", esp_err_to_name(ret));
|
||||
ESP_LOGI(TAG, "Trying to stop Ethernet driver and deinitialize it again...");
|
||||
ret = esp_eth_stop(s_eth_handle);
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "esp_eth_stop failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
// try to deinitialize Ethernet driver again
|
||||
ret = esp_eth_test_eth_deinit(s_eth_handle);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "esp_eth_test_eth_deinit failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
}
|
||||
s_eth_handle = NULL;
|
||||
}
|
||||
|
||||
ret = esp_event_loop_delete_default();
|
||||
if (ret != ESP_OK && ret != ESP_ERR_INVALID_STATE) {
|
||||
// ESP_ERR_INVALID_STATE is OK - loop might already be deleted
|
||||
ESP_LOGW(TAG, "esp_event_loop_delete_default failed: %s", esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
if (s_eth_event_group != NULL) {
|
||||
vEventGroupDelete(s_eth_event_group);
|
||||
s_eth_event_group = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
esp_eth_handle_t eth_test_get_eth_handle(void)
|
||||
{
|
||||
return s_eth_handle;
|
||||
}
|
||||
|
||||
EventGroupHandle_t eth_test_get_default_event_group(void)
|
||||
{
|
||||
return s_eth_event_group;
|
||||
}
|
||||
|
||||
esp_netif_t *eth_test_get_netif(void)
|
||||
{
|
||||
return s_eth_netif;
|
||||
}
|
||||
|
||||
void* eth_test_alloc(size_t size)
|
||||
{
|
||||
for (int i = 0; i < MAX_HEAP_ALLOCATION_POINTERS; i++) {
|
||||
if (s_memory_p[i] == NULL) {
|
||||
s_memory_p[i] = malloc(size);
|
||||
return s_memory_p[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void eth_test_free(void *ptr)
|
||||
{
|
||||
for (int i = 0; i < MAX_HEAP_ALLOCATION_POINTERS; i++) {
|
||||
if (s_memory_p[i] == ptr) {
|
||||
free(s_memory_p[i]);
|
||||
s_memory_p[i] = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
void eth_test_free_all(void)
|
||||
{
|
||||
for (int i = 0; i < MAX_HEAP_ALLOCATION_POINTERS; i++) {
|
||||
if (s_memory_p[i] != NULL) {
|
||||
free(s_memory_p[i]);
|
||||
s_memory_p[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Event handler for Ethernet events */
|
||||
void eth_test_default_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:
|
||||
ESP_LOGI(TAG, "Ethernet Link Up");
|
||||
xEventGroupSetBits(eth_event_group, ETH_CONNECT_BIT);
|
||||
break;
|
||||
case ETHERNET_EVENT_DISCONNECTED:
|
||||
ESP_LOGI(TAG, "Ethernet Link Down");
|
||||
break;
|
||||
case ETHERNET_EVENT_START:
|
||||
ESP_LOGI(TAG, "Ethernet Started");
|
||||
xEventGroupSetBits(eth_event_group, ETH_START_BIT);
|
||||
break;
|
||||
case ETHERNET_EVENT_STOP:
|
||||
ESP_LOGI(TAG, "Ethernet Stopped");
|
||||
xEventGroupSetBits(eth_event_group, ETH_STOP_BIT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set public DNS servers as backup and fallback
|
||||
*
|
||||
* Priority used by lwIP:
|
||||
* DNS_MAIN (0) - assigned by DHCP lease (kept as primary)
|
||||
* DNS_BACKUP (1) - Google Public DNS 8.8.8.8 (used if main fails)
|
||||
* DNS_FALLBACK (2) - Google Public DNS 8.8.4.4 (last resort)
|
||||
*/
|
||||
static void eth_test_set_dns_fallback(esp_netif_t *netif)
|
||||
{
|
||||
esp_netif_dns_info_t dns = {0};
|
||||
dns.ip.type = ESP_IPADDR_TYPE_V4;
|
||||
|
||||
/* Check what DNS the DHCP lease provided in slot 0 */
|
||||
esp_netif_dns_info_t dhcp_dns = {0};
|
||||
esp_err_t ret = esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dhcp_dns);
|
||||
bool has_dhcp_dns = (ret == ESP_OK && dhcp_dns.ip.u_addr.ip4.addr != 0);
|
||||
|
||||
if (has_dhcp_dns) {
|
||||
ESP_LOGI(TAG, "DHCP DNS: " IPSTR " (keeping as primary)", IP2STR(&dhcp_dns.ip.u_addr.ip4));
|
||||
/* Keep DHCP DNS as primary, add Google DNS as backup and fallback */
|
||||
dns.ip.u_addr.ip4.addr = ESP_IP4TOADDR(8, 8, 8, 8);
|
||||
esp_netif_set_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns);
|
||||
ESP_LOGI(TAG, "Backup DNS set: 8.8.8.8");
|
||||
|
||||
dns.ip.u_addr.ip4.addr = ESP_IP4TOADDR(8, 8, 4, 4);
|
||||
esp_netif_set_dns_info(netif, ESP_NETIF_DNS_FALLBACK, &dns);
|
||||
ESP_LOGI(TAG, "Fallback DNS set: 8.8.4.4");
|
||||
} else {
|
||||
/* No DNS from DHCP, set Google DNS as primary so lwIP uses it immediately */
|
||||
ESP_LOGW(TAG, "No DNS received from DHCP — setting 8.8.8.8 as primary DNS");
|
||||
dns.ip.u_addr.ip4.addr = ESP_IP4TOADDR(8, 8, 8, 8);
|
||||
esp_netif_set_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns);
|
||||
ESP_LOGI(TAG, "Primary DNS set: 8.8.8.8");
|
||||
|
||||
dns.ip.u_addr.ip4.addr = ESP_IP4TOADDR(8, 8, 4, 4);
|
||||
esp_netif_set_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns);
|
||||
ESP_LOGI(TAG, "Backup DNS set: 8.8.4.4");
|
||||
}
|
||||
}
|
||||
|
||||
/** Event handler for IP_EVENT_ETH_GOT_IP */
|
||||
void eth_test_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, "~~~~~~~~~~~");
|
||||
|
||||
/* Configure public DNS fallbacks */
|
||||
eth_test_set_dns_fallback(event->esp_netif);
|
||||
|
||||
xEventGroupSetBits(eth_event_group, ETH_GOT_IP_BIT);
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "unity.h"
|
||||
#include "esp_event.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_eth.h"
|
||||
|
||||
#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)
|
||||
#define ETH_DOWNLOAD_END_TIMEOUT_MS (240000)
|
||||
|
||||
#define MAX_HEAP_ALLOCATION_POINTERS 20
|
||||
|
||||
typedef struct {
|
||||
uint8_t dest[ETH_ADDR_LEN];
|
||||
uint8_t src[ETH_ADDR_LEN];
|
||||
uint16_t proto;
|
||||
uint8_t data[];
|
||||
} __attribute__((__packed__)) emac_frame_t;
|
||||
|
||||
/** @brief Get the Ethernet handle initialized by setUp()
|
||||
*
|
||||
* @return esp_eth_handle_t The Ethernet handle, if initialized successfully, otherwise NULL
|
||||
*/
|
||||
esp_eth_handle_t eth_test_get_eth_handle(void);
|
||||
|
||||
/** @brief Get the Event Group handle initialized by setUp()
|
||||
*
|
||||
* @return EventGroupHandle_t The Event Group handle, if initialized successfully, otherwise NULL
|
||||
*/
|
||||
EventGroupHandle_t eth_test_get_default_event_group(void);
|
||||
|
||||
/** @brief Get the ESP-NETIF handle initialized by setUp()
|
||||
*
|
||||
* @note Valid output only available if the test case is using [esp-netif] identifier
|
||||
*
|
||||
* @return esp_netif_t The Network Interface handle if initialized successfully, otherwise NULL
|
||||
*/
|
||||
esp_netif_t *eth_test_get_netif(void);
|
||||
|
||||
/** @brief Set the PHY register bits
|
||||
*
|
||||
* @param eth_handle The Ethernet handle
|
||||
* @param reg_addr The PHY register address
|
||||
* @param bitmask The bits to set
|
||||
* @param max_attempts The maximum number of set attempts
|
||||
* @return esp_err_t The error code
|
||||
*/
|
||||
esp_err_t eth_test_set_phy_reg_bits(esp_eth_handle_t eth_handle, uint32_t reg_addr, uint32_t bitmask, uint32_t max_attempts);
|
||||
|
||||
/** @brief Clear the PHY register bits
|
||||
*
|
||||
* @param eth_handle The Ethernet handle
|
||||
* @param reg_addr The PHY register address
|
||||
* @param bitmask The bits to clear
|
||||
* @param max_attempts The maximum number of clear attempts
|
||||
* @return esp_err_t The error code
|
||||
*/
|
||||
esp_err_t eth_test_clear_phy_reg_bits(esp_eth_handle_t eth_handle, uint32_t reg_addr, uint32_t bitmask, uint32_t max_attempts);
|
||||
|
||||
/** @brief Initialize Ethernet driver (default implementation uses ethernet_init component).
|
||||
*
|
||||
* @param[out] eth_handle Initialized Ethernet driver handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t esp_eth_test_eth_init(esp_eth_handle_t *eth_handle);
|
||||
|
||||
/** @brief Deinitialize Ethernet driver (default implementation uses ethernet_init component).
|
||||
*
|
||||
* @param eth_handle Ethernet driver handle to deinitialize
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t esp_eth_test_eth_deinit(esp_eth_handle_t eth_handle);
|
||||
|
||||
/** @brief Default event handler function header for Ethernet events to be registered by `esp_event_handler_register()` in the test
|
||||
* with event base `ETH_EVENT` and event ID `ESP_EVENT_ANY_ID`
|
||||
*
|
||||
* @note This function is intended to be registered as a callback function only when [skip_setup_teardown] identifier is used. Otherwise,
|
||||
* it's registered automatically by the Common test app during setUp() phase.
|
||||
*/
|
||||
void eth_test_default_event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data);
|
||||
|
||||
/** @brief Default event handler function header for IP_EVENT_ETH_GOT_IP events to be registered by `esp_event_handler_register()` in the test
|
||||
* with event base `IP_EVENT` and event ID `IP_EVENT_ETH_GOT_IP`
|
||||
*
|
||||
* @note This function is intended to be registered as a callback function only when [skip_setup_teardown] identifier is used. Otherwise,
|
||||
* it's registered automatically by the Common test app during setUp() phase.
|
||||
*/
|
||||
void eth_test_got_ip_event_handler(void *arg, esp_event_base_t event_base,
|
||||
int32_t event_id, void *event_data);
|
||||
|
||||
/** @brief Allocate memory from the heap
|
||||
*
|
||||
* @note The the Common test app keeps track of all allocated memory and frees it automatically during tearDown() phase.
|
||||
* The maximum number of allocations is limited to `MAX_HEAP_ALLOCATION_POINTERS`.
|
||||
*
|
||||
* @param size The size of the memory to allocate
|
||||
* @return void* The pointer to the allocated memory, NULL if allocation failed
|
||||
*/
|
||||
void* eth_test_alloc(size_t size);
|
||||
|
||||
/** @brief Free memory from the heap
|
||||
*
|
||||
* @note All allocated memory is freed automatically during tearDown() phase. Use this function only when required by the test
|
||||
* procedure.
|
||||
*
|
||||
* @param ptr The pointer to the memory to free
|
||||
* @return void
|
||||
*/
|
||||
void eth_test_free(void *ptr);
|
||||
|
||||
/** @brief Free all memory from the heap
|
||||
*
|
||||
* @note All allocated memory is freed automatically during tearDown() phase. Use this function only when required by the test
|
||||
* procedure.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
void eth_test_free_all(void);
|
||||
@@ -2,3 +2,5 @@
|
||||
dependencies:
|
||||
espressif/ethernet_init:
|
||||
version: "^1.3.0"
|
||||
espressif/eth_test_app:
|
||||
version: "*"
|
||||
|
||||
Reference in New Issue
Block a user