diff --git a/components/esp_eth/test_apps/conftest.py b/components/esp_eth/test_apps/conftest.py new file mode 100644 index 00000000000..41488ecb3b2 --- /dev/null +++ b/components/esp_eth/test_apps/conftest.py @@ -0,0 +1,46 @@ +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Apache-2.0 +import sys +from collections.abc import Callable +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest + +if TYPE_CHECKING: + from eth_test_runner import EthTestRunner + + +def _setup_eth_test_runner_sys_path() -> Path | None: + root = Path(__file__).resolve().parent + for name in ('espressif__eth_test_app', 'eth_test_app'): + candidate = root / 'managed_components' / name + if (candidate / 'eth_test_runner.py').exists(): + candidate_str = str(candidate) + if candidate_str not in sys.path: + sys.path.insert(0, candidate_str) + return candidate + + return None + + +# Support local runs when managed_components is already present after build. +_setup_eth_test_runner_sys_path() + + +@pytest.fixture +def eth_test_runner( + app_path: str, + download_app_extra: Callable[[str], None], +) -> 'EthTestRunner': + runner_dir = _setup_eth_test_runner_sys_path() + if runner_dir is None: + download_app_extra(app_path) + runner_dir = _setup_eth_test_runner_sys_path() + + if runner_dir is None: + raise RuntimeError('eth_test_runner.py not found. Build the test app locally or download CI artifacts first.') + + from eth_test_runner import EthTestRunner + + return EthTestRunner() diff --git a/components/esp_eth/test_apps/main/CMakeLists.txt b/components/esp_eth/test_apps/main/CMakeLists.txt index e59653346d7..580b9e2f326 100644 --- a/components/esp_eth/test_apps/main/CMakeLists.txt +++ b/components/esp_eth/test_apps/main/CMakeLists.txt @@ -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) diff --git a/components/esp_eth/test_apps/main/Kconfig.projbuild b/components/esp_eth/test_apps/main/Kconfig.projbuild deleted file mode 100644 index 9c6ffa81a7d..00000000000 --- a/components/esp_eth/test_apps/main/Kconfig.projbuild +++ /dev/null @@ -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 diff --git a/components/esp_eth/test_apps/main/dl_espressif_com_root_cert.pem b/components/esp_eth/test_apps/main/dl_espressif_com_root_cert.pem deleted file mode 100644 index 798e0027513..00000000000 --- a/components/esp_eth/test_apps/main/dl_espressif_com_root_cert.pem +++ /dev/null @@ -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----- diff --git a/components/esp_eth/test_apps/main/esp_eth_test_apps.c b/components/esp_eth/test_apps/main/esp_eth_test_apps.c deleted file mode 100644 index 0ee28599179..00000000000 --- a/components/esp_eth/test_apps/main/esp_eth_test_apps.c +++ /dev/null @@ -1,554 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ -#include -#include -#include -#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); -} diff --git a/components/esp_eth/test_apps/main/esp_eth_test_l2.c b/components/esp_eth/test_apps/main/esp_eth_test_l2.c deleted file mode 100644 index 464e1de224c..00000000000 --- a/components/esp_eth/test_apps/main/esp_eth_test_l2.c +++ /dev/null @@ -1,547 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ -#include -#include -#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); -} diff --git a/components/esp_eth/test_apps/main/esp_eth_test_utils.c b/components/esp_eth/test_apps/main/esp_eth_test_utils.c deleted file mode 100644 index da86f8e8b13..00000000000 --- a/components/esp_eth/test_apps/main/esp_eth_test_utils.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ -#include -#include -#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); -} diff --git a/components/esp_eth/test_apps/main/esp_eth_test_utils.h b/components/esp_eth/test_apps/main/esp_eth_test_utils.h deleted file mode 100644 index a94454634a0..00000000000 --- a/components/esp_eth/test_apps/main/esp_eth_test_utils.h +++ /dev/null @@ -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); diff --git a/components/esp_eth/test_apps/main/idf_component.yml b/components/esp_eth/test_apps/main/idf_component.yml index b187568955c..4efc3f9b2bf 100644 --- a/components/esp_eth/test_apps/main/idf_component.yml +++ b/components/esp_eth/test_apps/main/idf_component.yml @@ -2,3 +2,5 @@ dependencies: espressif/ethernet_init: version: "^1.3.0" + espressif/eth_test_app: + version: "*" diff --git a/components/esp_eth/test_apps/pytest_esp_eth.py b/components/esp_eth/test_apps/pytest_esp_eth.py index 513d19a9c66..42d930dfc97 100644 --- a/components/esp_eth/test_apps/pytest_esp_eth.py +++ b/components/esp_eth/test_apps/pytest_esp_eth.py @@ -1,254 +1,16 @@ # SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: CC0-1.0 -import contextlib -import logging -import os -import socket -from collections.abc import Iterator -from multiprocessing import Pipe -from multiprocessing import Process -from multiprocessing import connection + +from typing import TYPE_CHECKING import pytest from pytest_embedded_idf import IdfDut from pytest_embedded_idf.utils import idf_parametrize -from scapy.all import Ether -from scapy.all import raw -ETH_TYPE = 0x3300 - - -class EthTestIntf: - def __init__(self, eth_type: int, my_if: str = ''): - self.target_if = '' - self.eth_type = eth_type - self.find_target_if(my_if) - - def find_target_if(self, my_if: str = '') -> None: - # try to determine which interface to use - netifs = os.listdir('/sys/class/net/') - # order matters - ETH NIC with the highest number is connected to DUT on CI runner - netifs.sort(reverse=True) - logging.info('detected interfaces: %s', str(netifs)) - - if my_if == '': - if 'dut_p1' in netifs: - self.target_if = 'dut_p1' - else: - for netif in netifs: - # if no interface defined, try to find it automatically - if netif.find('eth') == 0 or netif.find('enp') == 0 or netif.find('eno') == 0: - self.target_if = netif - break - elif my_if in netifs: - self.target_if = my_if - - if self.target_if == '': - raise RuntimeError('network interface not found') - logging.info('Use %s for testing', self.target_if) - - @contextlib.contextmanager - def configure_eth_if(self, eth_type: int = 0) -> Iterator[socket.socket]: - if eth_type == 0: - eth_type = self.eth_type - so = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(eth_type)) # type: ignore - so.bind((self.target_if, 0)) - try: - yield so - finally: - so.close() - - def send_eth_packet(self, mac: str) -> None: - with self.configure_eth_if() as so: - so.settimeout(10) - payload = bytearray(1010) - for i, _ in enumerate(payload): - payload[i] = i & 0xFF - eth_frame = Ether(dst=mac, src=so.getsockname()[4], type=self.eth_type) / raw(payload) - try: - so.send(raw(eth_frame)) - except Exception as e: - raise e - - def recv_resp_poke(self, mac: str, i: int = 0) -> None: - eth_type_ctrl = self.eth_type + 1 - with self.configure_eth_if(eth_type_ctrl) as so: - so.settimeout(30) - for _ in range(10): - try: - eth_frame = Ether(so.recv(60)) - except Exception as e: - raise e - if mac == eth_frame.src and eth_frame.load[0] == 0xFA: - if eth_frame.load[1] != i: - raise RuntimeError('Missed Poke Packet') - logging.info('Poke Packet received...') - eth_frame.dst = eth_frame.src - eth_frame.src = so.getsockname()[4] - eth_frame.load = bytes.fromhex('fb') # POKE_RESP code - so.send(raw(eth_frame)) - break - else: - logging.warning('Unexpected Control packet') - logging.warning('Expected Ctrl command: 0xfa, actual: 0x%x', eth_frame.load[0]) - logging.warning('Source MAC %s', eth_frame.src) - else: - raise RuntimeError('No Poke Packet!') - - def traffic_gen(self, mac: str, pipe_rcv: connection.Connection) -> None: - with self.configure_eth_if() as so: - payload = bytes.fromhex('ff') # DUMMY_TRAFFIC code - payload += bytes(1485) - eth_frame = Ether(dst=mac, src=so.getsockname()[4], type=self.eth_type) / raw(payload) - try: - while pipe_rcv.poll() is not True: - so.send(raw(eth_frame)) - except Exception as e: - raise e - - def eth_loopback(self, mac: str, pipe_rcv: connection.Connection) -> None: - with self.configure_eth_if(self.eth_type) as so: - so.settimeout(30) - try: - while pipe_rcv.poll() is not True: - try: - eth_frame = Ether(so.recv(1522)) - except Exception as e: - raise e - if mac == eth_frame.src: - eth_frame.dst = eth_frame.src - eth_frame.src = so.getsockname()[4] - so.send(raw(eth_frame)) - else: - logging.warning('Received frame from unexpected source') - logging.warning('Source MAC %s', eth_frame.src) - except Exception as e: - raise e - - -def ethernet_test(dut: IdfDut) -> None: - dut.run_all_single_board_cases(group='ethernet', timeout=980) - - -def ethernet_int_emac_test(dut: IdfDut) -> None: - dut.run_all_single_board_cases(group='esp_emac', timeout=240) - - -def ethernet_l2_test(dut: IdfDut, test_if: str = '') -> None: - target_if = EthTestIntf(ETH_TYPE, test_if) - - dut.expect_exact('Press ENTER to see the list of tests') - dut.write('\n') - dut.expect_exact('Enter test for running.') - - with target_if.configure_eth_if() as so: - so.settimeout(30) - dut.write('"ethernet broadcast transmit"') - res = dut.expect( - r'DUT MAC: ([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})' - ) - - # wait for POKE msg to be sure the switch already started forwarding the port's traffic - # (there might be slight delay due to the RSTP execution) - dut_mac = res.group(1).decode('utf-8') - target_if.recv_resp_poke(mac=dut_mac) - - for _ in range(10): - eth_frame = Ether(so.recv(1024)) - if dut_mac == eth_frame.src: - break - else: - raise RuntimeError('No broadcast received from expected DUT MAC addr') - - for i in range(0, 1010): - if eth_frame.load[i] != i & 0xFF: - raise RuntimeError('Packet content mismatch') - dut.expect_unity_test_output() - - dut.expect_exact("Enter next test, or 'enter' to see menu") - dut.write('"ethernet recv_pkt"') - res = dut.expect( - r'DUT MAC: ([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})' - ) - dut_mac = res.group(1).decode('utf-8') - target_if.recv_resp_poke(mac=dut_mac) - for _ in range(5): - # wait to be sure the DUT reconfigured the filter - dut.expect_exact('Filter configured') - target_if.send_eth_packet('ff:ff:ff:ff:ff:ff') # broadcast frame - target_if.send_eth_packet('01:00:5e:00:00:00') # IPv4 multicast frame - target_if.send_eth_packet('33:33:00:00:00:00') # IPv6 multicast frame - target_if.send_eth_packet(mac=dut_mac) # unicast frame - - dut.expect_unity_test_output(extra_before=res.group(1)) - - dut.expect_exact("Enter next test, or 'enter' to see menu") - dut.write('"ethernet start/stop stress test under heavy traffic"') - res = dut.expect( - r'DUT MAC: ([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})' - ) - dut_mac = res.group(1).decode('utf-8') - # Start/stop under heavy Tx traffic - for tx_i in range(10): - target_if.recv_resp_poke(dut_mac, tx_i) - dut.expect_exact('Ethernet Stopped') - - for rx_i in range(10): - target_if.recv_resp_poke(dut_mac, rx_i) - # Start/stop under heavy Rx traffic - pipe_rcv, pipe_send = Pipe(False) - tx_proc = Process( - target=target_if.traffic_gen, - args=( - dut_mac, - pipe_rcv, - ), - ) - tx_proc.start() - try: - dut.expect_exact('Ethernet Stopped') - finally: - pipe_send.send(0) # just send some dummy data to stop traffic generation - tx_proc.join(5) - if tx_proc.exitcode is None: - tx_proc.terminate() - - dut.expect_unity_test_output(extra_before=res.group(1)) - - -def ethernet_heap_alloc_test(dut: IdfDut, test_if: str = '') -> None: - target_if = EthTestIntf(ETH_TYPE, test_if) - dut.expect_exact('Press ENTER to see the list of tests') - dut.write('\n') - dut.expect_exact('Enter test for running.') - dut.write('"heap utilization"') - res = dut.expect(r'PHY loopback is (enabled|disabled)') - phy_loopback = res.group(1).decode('utf-8') - # Some chips do not support internal loopback, we need to loopback at test PC side - if phy_loopback == 'disabled': - logging.info('Starting loopback server...') - res = dut.expect( - r'DUT MAC: ([0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2}:[0-9A-Fa-f]{2})' - ) - dut_mac = res.group(1).decode('utf-8') - pipe_rcv, pipe_send = Pipe(False) - loopback_proc = Process( - target=target_if.eth_loopback, - args=( - dut_mac, - pipe_rcv, - ), - ) - loopback_proc.start() - - target_if.recv_resp_poke(mac=dut_mac) - dut.expect_exact('Ethernet Stopped') - pipe_send.send(0) # just send some dummy data - loopback_proc.join(5) - if loopback_proc.exitcode is None: - loopback_proc.terminate() - - dut.expect_unity_test_output() +# Import for type-checking only — at runtime eth_test_runner pulls in dependencies that +# are not available during pytest collection. The real import is deferred to the fixture body. +if TYPE_CHECKING: + from conftest import EthTestRunner # ----------- IP101 ----------- @@ -256,8 +18,8 @@ def ethernet_heap_alloc_test(dut: IdfDut, test_if: str = '') -> None: @pytest.mark.parametrize('config', ['default_generic', 'release_generic', 'single_core_generic'], indirect=True) @pytest.mark.flaky(reruns=3, reruns_delay=5) @idf_parametrize('target', ['esp32'], indirect=['target']) -def test_esp_ethernet(dut: IdfDut) -> None: - ethernet_test(dut) +def test_esp_ethernet(dut: IdfDut, eth_test_runner: 'EthTestRunner') -> None: + eth_test_runner.run_ethernet_test_apps(dut) @pytest.mark.eth_ip101 @@ -269,10 +31,10 @@ def test_esp_ethernet(dut: IdfDut) -> None: indirect=True, ) @idf_parametrize('target', ['esp32'], indirect=['target']) -def test_esp_emac(dut: IdfDut) -> None: - ethernet_int_emac_test(dut) +def test_esp_emac(dut: IdfDut, eth_test_runner: 'EthTestRunner') -> None: + dut.run_all_single_board_cases(group='esp_emac', timeout=240) dut.serial.hard_reset() - ethernet_heap_alloc_test(dut) + eth_test_runner.run_ethernet_heap_alloc_test(dut) @pytest.mark.eth_ip101 @@ -284,8 +46,8 @@ def test_esp_emac(dut: IdfDut) -> None: indirect=True, ) @idf_parametrize('target', ['esp32'], indirect=['target']) -def test_esp_eth_ip101(dut: IdfDut) -> None: - ethernet_l2_test(dut) +def test_esp_eth_ip101(dut: IdfDut, eth_test_runner: 'EthTestRunner') -> None: + eth_test_runner.run_ethernet_l2_test(dut) @pytest.mark.eth_ip101 @@ -310,10 +72,10 @@ def test_esp32_emac_clko(dut: IdfDut) -> None: ], indirect=['target'], ) -def test_esp32p4_ethernet(dut: IdfDut) -> None: - ethernet_test(dut) +def test_esp32p4_ethernet(dut: IdfDut, eth_test_runner: 'EthTestRunner') -> None: + eth_test_runner.run_ethernet_test_apps(dut) dut.serial.hard_reset() - ethernet_l2_test(dut) + eth_test_runner.run_ethernet_l2_test(dut) @pytest.mark.parametrize( @@ -324,10 +86,10 @@ def test_esp32p4_ethernet(dut: IdfDut) -> None: ], indirect=['target'], ) -def test_esp32p4_emac(dut: IdfDut) -> None: - ethernet_int_emac_test(dut) +def test_esp32p4_emac(dut: IdfDut, eth_test_runner: 'EthTestRunner') -> None: + dut.run_all_single_board_cases(group='esp_emac', timeout=240) dut.serial.hard_reset() - ethernet_heap_alloc_test(dut) + eth_test_runner.run_ethernet_heap_alloc_test(dut) @pytest.mark.parametrize( @@ -352,10 +114,10 @@ def test_esp32p4_emac_clko(dut: IdfDut) -> None: indirect=True, ) @idf_parametrize('target', ['esp32'], indirect=['target']) -def test_esp_eth_lan8720(dut: IdfDut) -> None: - ethernet_test(dut) +def test_esp_eth_lan8720(dut: IdfDut, eth_test_runner: 'EthTestRunner') -> None: + eth_test_runner.run_ethernet_test_apps(dut) dut.serial.hard_reset() - ethernet_l2_test(dut) + eth_test_runner.run_ethernet_l2_test(dut) # ----------- RTL8201 ----------- @@ -368,10 +130,10 @@ def test_esp_eth_lan8720(dut: IdfDut) -> None: indirect=True, ) @idf_parametrize('target', ['esp32'], indirect=['target']) -def test_esp_eth_rtl8201(dut: IdfDut) -> None: - ethernet_test(dut) +def test_esp_eth_rtl8201(dut: IdfDut, eth_test_runner: 'EthTestRunner') -> None: + eth_test_runner.run_ethernet_test_apps(dut) dut.serial.hard_reset() - ethernet_l2_test(dut) + eth_test_runner.run_ethernet_l2_test(dut) # ----------- KSZ8041 ----------- @@ -384,10 +146,10 @@ def test_esp_eth_rtl8201(dut: IdfDut) -> None: indirect=True, ) @idf_parametrize('target', ['esp32'], indirect=['target']) -def test_esp_eth_ksz8041(dut: IdfDut) -> None: - ethernet_test(dut) +def test_esp_eth_ksz8041(dut: IdfDut, eth_test_runner: 'EthTestRunner') -> None: + eth_test_runner.run_ethernet_test_apps(dut) dut.serial.hard_reset() - ethernet_l2_test(dut) + eth_test_runner.run_ethernet_l2_test(dut) # ----------- DP83848 ----------- @@ -400,10 +162,10 @@ def test_esp_eth_ksz8041(dut: IdfDut) -> None: indirect=True, ) @idf_parametrize('target', ['esp32'], indirect=['target']) -def test_esp_eth_dp83848(dut: IdfDut) -> None: - ethernet_test(dut) +def test_esp_eth_dp83848(dut: IdfDut, eth_test_runner: 'EthTestRunner') -> None: + eth_test_runner.run_ethernet_test_apps(dut) dut.serial.hard_reset() - ethernet_l2_test(dut) + eth_test_runner.run_ethernet_l2_test(dut) # ----------- W5500 ----------- @@ -416,12 +178,12 @@ def test_esp_eth_dp83848(dut: IdfDut) -> None: indirect=True, ) @idf_parametrize('target', ['esp32'], indirect=['target']) -def test_esp_eth_w5500(dut: IdfDut) -> None: - ethernet_test(dut) +def test_esp_eth_w5500(dut: IdfDut, eth_test_runner: 'EthTestRunner') -> None: + eth_test_runner.run_ethernet_test_apps(dut) dut.serial.hard_reset() - ethernet_l2_test(dut) + eth_test_runner.run_ethernet_l2_test(dut) dut.serial.hard_reset() - ethernet_heap_alloc_test(dut) + eth_test_runner.run_ethernet_heap_alloc_test(dut) # ----------- KSZ8851SNL ----------- @@ -434,12 +196,12 @@ def test_esp_eth_w5500(dut: IdfDut) -> None: indirect=True, ) @idf_parametrize('target', ['esp32'], indirect=['target']) -def test_esp_eth_ksz8851snl(dut: IdfDut) -> None: - ethernet_test(dut) +def test_esp_eth_ksz8851snl(dut: IdfDut, eth_test_runner: 'EthTestRunner') -> None: + eth_test_runner.run_ethernet_test_apps(dut) dut.serial.hard_reset() - ethernet_l2_test(dut) + eth_test_runner.run_ethernet_l2_test(dut) dut.serial.hard_reset() - ethernet_heap_alloc_test(dut) + eth_test_runner.run_ethernet_heap_alloc_test(dut) # ----------- DM9051 ----------- @@ -452,12 +214,12 @@ def test_esp_eth_ksz8851snl(dut: IdfDut) -> None: indirect=True, ) @idf_parametrize('target', ['esp32'], indirect=['target']) -def test_esp_eth_dm9051(dut: IdfDut) -> None: - ethernet_test(dut) +def test_esp_eth_dm9051(dut: IdfDut, eth_test_runner: 'EthTestRunner') -> None: + eth_test_runner.run_ethernet_test_apps(dut) dut.serial.hard_reset() - ethernet_l2_test(dut) + eth_test_runner.run_ethernet_l2_test(dut) dut.serial.hard_reset() - ethernet_heap_alloc_test(dut) + eth_test_runner.run_ethernet_heap_alloc_test(dut) # ----------- EMAC Sleep Retention -----------