mirror of
https://github.com/espressif/esp-idf.git
synced 2026-07-13 06:23:04 +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:
46
components/esp_eth/test_apps/conftest.py
Normal file
46
components/esp_eth/test_apps/conftest.py
Normal file
@@ -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()
|
||||
@@ -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: "*"
|
||||
|
||||
@@ -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 -----------
|
||||
|
||||
Reference in New Issue
Block a user