Browse Source

Ethernet related tests improvements

esp_eth: tests migrated to pytest and added support of multiple Ethernet devices

esp_netif: l2tap test migrated to pytest
Ondrej 3 years ago
parent
commit
2bf7255285
40 changed files with 1142 additions and 599 deletions
  1. 8 0
      .gitlab/ci/target-test.yml
  2. 0 5
      components/esp_eth/test/CMakeLists.txt
  3. 2 0
      components/esp_eth/test_apps/CMakeLists.txt
  4. 1 3
      components/esp_eth/test_apps/README.md
  5. 8 3
      components/esp_eth/test_apps/main/CMakeLists.txt
  6. 64 8
      components/esp_eth/test_apps/main/Kconfig.projbuild
  7. 0 0
      components/esp_eth/test_apps/main/dl_espressif_com_root_cert.pem
  8. 0 408
      components/esp_eth/test_apps/main/esp_eth_test.c
  9. 93 130
      components/esp_eth/test_apps/main/esp_eth_test_apps.c
  10. 173 0
      components/esp_eth/test_apps/main/esp_eth_test_common.c
  11. 42 0
      components/esp_eth/test_apps/main/esp_eth_test_common.h
  12. 255 0
      components/esp_eth/test_apps/main/esp_eth_test_hal.c
  13. 277 0
      components/esp_eth/test_apps/main/esp_eth_test_l2.c
  14. 11 0
      components/esp_eth/test_apps/main/esp_eth_test_main.c
  15. 61 22
      components/esp_eth/test_apps/pytest_esp_eth.py
  16. 8 0
      components/esp_eth/test_apps/sdkconfig.ci.default_dm9051
  17. 9 0
      components/esp_eth/test_apps/sdkconfig.ci.default_dp83848
  18. 2 0
      components/esp_eth/test_apps/sdkconfig.ci.default_ip101
  19. 11 0
      components/esp_eth/test_apps/sdkconfig.ci.default_ksz80xx
  20. 8 0
      components/esp_eth/test_apps/sdkconfig.ci.default_ksz8851snl
  21. 2 0
      components/esp_eth/test_apps/sdkconfig.ci.default_lan8720
  22. 9 0
      components/esp_eth/test_apps/sdkconfig.ci.default_rtl8201
  23. 8 0
      components/esp_eth/test_apps/sdkconfig.ci.default_w5500
  24. 12 0
      components/esp_eth/test_apps/sdkconfig.ci.release_ip101
  25. 13 0
      components/esp_eth/test_apps/sdkconfig.ci.single_core_ip101
  26. 7 1
      components/esp_netif/.build-test-rules.yml
  27. 0 0
      components/esp_netif/test_apps/test_app_esp_netif/CMakeLists.txt
  28. 0 0
      components/esp_netif/test_apps/test_app_esp_netif/README.md
  29. 0 0
      components/esp_netif/test_apps/test_app_esp_netif/main/CMakeLists.txt
  30. 5 0
      components/esp_netif/test_apps/test_app_esp_netif/main/esp_netif_test.c
  31. 0 0
      components/esp_netif/test_apps/test_app_esp_netif/pytest_esp_netif.py
  32. 0 0
      components/esp_netif/test_apps/test_app_esp_netif/sdkconfig.defaults
  33. 10 0
      components/esp_netif/test_apps/test_app_vfs_l2tap/CMakeLists.txt
  34. 2 0
      components/esp_netif/test_apps/test_app_vfs_l2tap/README.md
  35. 3 0
      components/esp_netif/test_apps/test_app_vfs_l2tap/main/CMakeLists.txt
  36. 17 14
      components/esp_netif/test_apps/test_app_vfs_l2tap/main/test_vfs_l2tap.c
  37. 15 0
      components/esp_netif/test_apps/test_app_vfs_l2tap/pytest_esp_vfs_l2tap.py
  38. 4 0
      components/esp_netif/test_apps/test_app_vfs_l2tap/sdkconfig.defaults
  39. 2 2
      examples/ethernet/basic/components/ethernet_init/ethernet_init.c
  40. 0 3
      tools/unit-test-app/configs/netif_l2tap

+ 8 - 0
.gitlab/ci/target-test.yml

@@ -346,6 +346,14 @@ component_ut_pytest_esp32_lan8720:
     - build_pytest_components_esp32
   tags: [ esp32, lan8720 ]
 
+component_ut_pytest_esp32_ethernet:
+  extends:
+    - .pytest_components_dir_template
+    - .rules:test:component_ut-esp32
+  needs:
+    - build_pytest_components_esp32
+  tags: [ esp32, ethernet ]
+
 component_ut_pytest_esp32_flash_encryption:
   extends:
     - .pytest_components_dir_template

+ 0 - 5
components/esp_eth/test/CMakeLists.txt

@@ -1,5 +0,0 @@
-idf_component_register(SRC_DIRS .
-                       PRIV_INCLUDE_DIRS .
-                       PRIV_REQUIRES cmock test_utils esp_eth esp_http_client esp_netif
-                       EMBED_TXTFILES dl_espressif_com_root_cert.pem)
-target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

+ 2 - 0
components/esp_eth/test_apps/CMakeLists.txt

@@ -1,6 +1,8 @@
 # This is the project CMakeLists.txt file for the test subproject
 cmake_minimum_required(VERSION 3.16)
 
+set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
+
 include($ENV{IDF_PATH}/tools/cmake/project.cmake)
 project(esp_eth_test)
 

+ 1 - 3
components/esp_eth/test_apps/README.md

@@ -2,10 +2,8 @@
 | Supported Targets | ESP32 |
 | ----------------- | ----- |
 
-This test app is used to test MAC layer behavior with different PHY chips:
+This test app is used to test Ethernet MAC behavior with different chips.
 
-- IP101
-- LAN8720
 
 ## Prerequisites
 Install third part Python packages:

+ 8 - 3
components/esp_eth/test_apps/main/CMakeLists.txt

@@ -1,5 +1,10 @@
-idf_component_register(SRCS "esp_eth_test.c"
+idf_component_register(SRCS "esp_eth_test_apps.c"
+                            "esp_eth_test_l2.c"
+                            "esp_eth_test_hal.c"
+                            "esp_eth_test_common.c"
+                            "esp_eth_test_main.c"
                        INCLUDE_DIRS "."
                        PRIV_INCLUDE_DIRS "."
-                       PRIV_REQUIRES unity esp_eth esp_netif)
-target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
+                       PRIV_REQUIRES unity test_utils esp_eth esp_netif esp_http_client
+                       EMBED_TXTFILES dl_espressif_com_root_cert.pem
+                       WHOLE_ARCHIVE)

+ 64 - 8
components/esp_eth/test_apps/main/Kconfig.projbuild

@@ -1,14 +1,70 @@
 menu "esp_eth TEST_APPS Configuration"
 
-    choice TARGET_ETH_PHY_DEVICE
+    choice TARGET_ETH_CONFIG
         prompt "Ethernet peripheral device"
-        default TARGET_ETH_PHY_DEVICE_IP101
+        default TARGET_USE_INTERNAL_ETHERNET
         help
-            Select one of the devices listed here
+            Select type of Ethernet interface.
+
+        config TARGET_USE_INTERNAL_ETHERNET
+            depends on SOC_EMAC_SUPPORTED
+            select ETH_USE_ESP32_EMAC
+            bool "Internal EMAC"
+            help
+                Use internal Ethernet MAC controller.
+
+        config TARGET_USE_SPI_ETHERNET
+            bool "SPI Ethernet"
+            select ETH_USE_SPI_ETHERNET
+            help
+                Use external SPI-Ethernet module(s).
+    endchoice # TARGET_ETH_CONFIG
+
+    if TARGET_USE_INTERNAL_ETHERNET
+        choice TARGET_ETH_PHY_DEVICE
+            prompt "Ethernet PHY"
+            default TARGET_ETH_PHY_DEVICE_IP101
+            help
+                Select one of the devices listed here
+
+            config TARGET_ETH_PHY_DEVICE_IP101
+                bool "IP101"
+            config TARGET_ETH_PHY_DEVICE_LAN87XX
+                bool "LAN8720"
+            config TARGET_ETH_PHY_DEVICE_KSZ80XX
+                bool "KSZ80xx"
+            config TARGET_ETH_PHY_DEVICE_RTL8201
+                bool "RTL8201"
+            config TARGET_ETH_PHY_DEVICE_DP83848
+                bool "DP83848"
+        endchoice # TARGET_ETH_PHY_DEVICE
+    endif # TARGET_USE_INTERNAL_ETHERNET
+
+    if TARGET_USE_SPI_ETHERNET
+        choice TARGET_ETH_SPI_DEVICE
+            prompt "Ethernet SPI Module"
+            default TARGET_ETH_PHY_DEVICE_W5500
+            help
+                Select one of the devices listed here
+
+            config TARGET_ETH_PHY_DEVICE_W5500
+                bool "W5500"
+                select ETH_SPI_ETHERNET_W5500
+            config TARGET_ETH_PHY_DEVICE_KSZ8851SNL
+                bool "KSZ8851SNL"
+                select ETH_SPI_ETHERNET_KSZ8851SNL
+            config TARGET_ETH_PHY_DEVICE_DM9051
+                bool "DM9051"
+                select ETH_SPI_ETHERNET_DM9051
+        endchoice # TARGET_ETH_SPI_DEVICE
+
+        config TARGET_SPI_CLOCK_MHZ
+            int "SPI clock speed (MHz)"
+            range 5 80
+            default 12
+            help
+                Set the clock speed (MHz) of SPI interface.
+
+    endif # TARGET_USE_SPI_ETHERNET
 
-        config TARGET_ETH_PHY_DEVICE_IP101
-            bool "IP101"
-        config TARGET_ETH_PHY_DEVICE_LAN87XX
-            bool "LAN8720"
-    endchoice
 endmenu

+ 0 - 0
components/esp_eth/test/dl_espressif_com_root_cert.pem → components/esp_eth/test_apps/main/dl_espressif_com_root_cert.pem


+ 0 - 408
components/esp_eth/test_apps/main/esp_eth_test.c

@@ -1,408 +0,0 @@
-#include <stdio.h>
-#include <string.h>
-#include "freertos/FreeRTOS.h"
-#include "freertos/event_groups.h"
-#include "esp_event.h"
-#include "unity.h"
-#include "esp_netif.h"
-#include "esp_eth.h"
-#include "sdkconfig.h"
-#include "lwip/sockets.h"
-
-#define ETH_START_BIT BIT(0)
-#define ETH_STOP_BIT BIT(1)
-#define ETH_CONNECT_BIT BIT(2)
-
-#define ETH_BROADCAST_RECV_BIT BIT(0)
-#define ETH_MULTICAST_RECV_BIT BIT(1)
-#define ETH_UNICAST_RECV_BIT BIT(2)
-
-#define POKE_REQ 0xFA
-#define POKE_RESP 0xFB
-#define DUMMY_TRAFFIC 0xFF
-
-typedef struct {
-    uint8_t dest[6];
-    uint8_t src[6];
-    uint16_t proto;
-    uint8_t data[];
-} __attribute__((__packed__)) emac_frame_t;
-
-static void eth_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:
-        xEventGroupSetBits(eth_event_group, ETH_CONNECT_BIT);
-        break;
-    case ETHERNET_EVENT_DISCONNECTED:
-        break;
-    case ETHERNET_EVENT_START:
-        xEventGroupSetBits(eth_event_group, ETH_START_BIT);
-    break;
-    case ETHERNET_EVENT_STOP:
-        xEventGroupSetBits(eth_event_group, ETH_STOP_BIT);
-        break;
-    default:
-        break;
-    }
-}
-
-TEST_CASE("start_and_stop", "[esp_eth]")
-{
-    EventGroupHandle_t eth_event_group = xEventGroupCreate();
-    TEST_ASSERT(eth_event_group != NULL);
-
-    eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();  // apply default MAC configuration
-    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
-    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); // create MAC instance
-    TEST_ASSERT_NOT_NULL(mac);
-    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // apply default PHY configuration
-#if defined(CONFIG_TARGET_ETH_PHY_DEVICE_IP101)
-    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); // create PHY instance
-#elif defined(CONFIG_TARGET_ETH_PHY_DEVICE_LAN87XX)
-    esp_eth_phy_t *phy = esp_eth_phy_new_lan87xx(&phy_config);
-#endif
-    TEST_ASSERT_NOT_NULL(phy);
-    esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
-    esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_driver_install(&config, &eth_handle)); // install driver
-    TEST_ASSERT_NOT_NULL(eth_handle);
-    TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
-    TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_start(eth_handle)); // start Ethernet driver state machine
-
-    EventBits_t bits = 0;
-    bits = xEventGroupWaitBits(eth_event_group, ETH_START_BIT, true, true, pdMS_TO_TICKS(3000));
-    TEST_ASSERT((bits & ETH_START_BIT) == ETH_START_BIT);
-
-    TEST_ASSERT_EQUAL(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);
-
-    TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_delete_default());
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_driver_uninstall(eth_handle));
-    phy->del(phy);
-    mac->del(mac);
-    vEventGroupDelete(eth_event_group);
-}
-
-TEST_CASE("get_set_mac", "[esp_eth]")
-{
-    EventGroupHandle_t eth_event_group = xEventGroupCreate();
-    TEST_ASSERT(eth_event_group != NULL);
-
-    eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();  // apply default MAC configuration
-    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
-    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); // create MAC instance
-    TEST_ASSERT_NOT_NULL(mac);
-    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // apply default PHY configuration
-#if defined(CONFIG_TARGET_ETH_PHY_DEVICE_IP101)
-    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); // create PHY instance
-#elif defined(CONFIG_TARGET_ETH_PHY_DEVICE_LAN87XX)
-    esp_eth_phy_t *phy = esp_eth_phy_new_lan87xx(&phy_config);
-#endif
-    TEST_ASSERT_NOT_NULL(phy);
-    esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
-    esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_driver_install(&config, &eth_handle)); // install driver
-    TEST_ASSERT_NOT_NULL(eth_handle);
-    TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
-    TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
-    TEST_ASSERT_EQUAL(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(3000));
-    TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
-
-    uint8_t mac_addr[6] = {};
-    TEST_ASSERT_EQUAL(ESP_OK, mac->get_addr(mac, mac_addr));
-    TEST_ASSERT_BITS(0b00000011, 0b00, mac_addr[0]);    // Check UL&IG, should be UI
-    mac_addr[5] ^= mac_addr[4];
-    TEST_ASSERT_EQUAL(ESP_OK, mac->set_addr(mac, mac_addr));
-    uint8_t new_mac_addr[6] = {};
-    TEST_ASSERT_EQUAL(ESP_OK, mac->get_addr(mac, new_mac_addr));
-    TEST_ASSERT_EQUAL(0, memcmp(mac_addr, new_mac_addr, 6));
-
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_stop(eth_handle));
-    TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_delete_default());
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_driver_uninstall(eth_handle));
-    phy->del(phy);
-    mac->del(mac);
-    vEventGroupDelete(eth_event_group);
-}
-
-TEST_CASE("ethernet_broadcast_transmit", "[esp_eth]")
-{
-    EventGroupHandle_t eth_event_group = xEventGroupCreate();
-    TEST_ASSERT(eth_event_group != NULL);
-
-    eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();  // apply default MAC configuration
-    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
-    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); // create MAC instance
-    TEST_ASSERT_NOT_NULL(mac);
-    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // apply default PHY configuration
-#if defined(CONFIG_TARGET_ETH_PHY_DEVICE_IP101)
-    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); // create PHY instance
-#elif defined(CONFIG_TARGET_ETH_PHY_DEVICE_LAN87XX)
-    esp_eth_phy_t *phy = esp_eth_phy_new_lan87xx(&phy_config);
-#endif
-    TEST_ASSERT_NOT_NULL(phy);
-    esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
-    esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_driver_install(&config, &eth_handle)); // install driver
-    TEST_ASSERT_NOT_NULL(eth_handle);
-    TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
-    TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
-    TEST_ASSERT_EQUAL(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(3000));
-    TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
-    // even if PHY (IP101) indicates autonegotiation done and link up, it sometimes may miss few packets after atonego reset, hence wait a bit
-    vTaskDelay(pdMS_TO_TICKS(100));
-
-    emac_frame_t *pkt = malloc(1024);
-    pkt->proto = 0x2222;
-    memset(pkt->dest, 0xff, 6);     // broadcast addr
-    for (int i = 0; i < (1024 - ETH_HEADER_LEN); ++i){
-        pkt->data[i] = i & 0xff;
-    }
-
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_transmit(eth_handle, pkt, 1024));
-    vTaskDelay(pdMS_TO_TICKS(100));
-    free(pkt);
-
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_stop(eth_handle));
-    TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_delete_default());
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_driver_uninstall(eth_handle));
-    phy->del(phy);
-    mac->del(mac);
-    vEventGroupDelete(eth_event_group);
-}
-
-static uint8_t local_mac_addr[6] = {};
-
-esp_err_t l2_packet_txrx_test_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv) {
-    EventGroupHandle_t eth_event_group = (EventGroupHandle_t)priv;
-    emac_frame_t *pkt = (emac_frame_t *) buffer;
-    // check header
-    if (pkt->proto == 0x2222 && length == 1024) {
-        // check content
-        for (int i = 0; i < (length - ETH_HEADER_LEN); ++i) {
-            if (pkt->data[i] != (i & 0xff)) {
-                printf("payload mismatch\n");
-                return ESP_OK;
-            }
-        }
-        if (memcmp(pkt->dest, "\xff\xff\xff\xff\xff\xff", 6) == 0) {
-            printf("broadcast received...\n");
-            xEventGroupSetBits(eth_event_group, ETH_BROADCAST_RECV_BIT);
-        } else if (pkt->dest[0] & 0x1) {
-            printf("multicast received...\n");
-            xEventGroupSetBits(eth_event_group, ETH_MULTICAST_RECV_BIT);
-        } else if (memcmp(pkt->dest, local_mac_addr, 6) == 0) {
-            printf("unicast received...\n");
-            xEventGroupSetBits(eth_event_group, ETH_UNICAST_RECV_BIT);
-        }
-    } else {
-        printf("unexpected frame (protocol: 0x%x, length: %u)\n", pkt->proto, length);
-    }
-    return ESP_OK;
-};
-
-TEST_CASE("recv_pkt", "[esp_eth]")
-{
-    EventGroupHandle_t eth_event_group = xEventGroupCreate();
-    TEST_ASSERT(eth_event_group != NULL);
-
-    eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();  // apply default MAC configuration
-    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
-    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); // create MAC instance
-    TEST_ASSERT_NOT_NULL(mac);
-    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // apply default PHY configuration
-#if defined(CONFIG_TARGET_ETH_PHY_DEVICE_IP101)
-    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); // create PHY instance
-#elif defined(CONFIG_TARGET_ETH_PHY_DEVICE_LAN87XX)
-    esp_eth_phy_t *phy = esp_eth_phy_new_lan87xx(&phy_config);
-#endif
-    TEST_ASSERT_NOT_NULL(phy);
-    esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
-    esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_driver_install(&config, &eth_handle)); // install driver
-    TEST_ASSERT_NOT_NULL(eth_handle);
-    TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_start(eth_handle)); // start Ethernet driver state machine
-
-    TEST_ASSERT_EQUAL(ESP_OK, mac->get_addr(mac, 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_ASSERT_EQUAL(ESP_OK, esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, eth_event_group));
-
-    EventBits_t bits = 0;
-    bits = xEventGroupWaitBits(eth_event_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
-                               true, true, pdMS_TO_TICKS(5000));
-    TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) ==
-                                 (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
-
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_stop(eth_handle));
-    TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_delete_default());
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_driver_uninstall(eth_handle));
-    phy->del(phy);
-    mac->del(mac);
-    vEventGroupDelete(eth_event_group);
-}
-
-typedef struct
-{
-    SemaphoreHandle_t mutex;
-    int rx_pkt_cnt;
-} recv_info_t;
-
-static esp_err_t eth_recv_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv)
-{
-    emac_frame_t *pkt = (emac_frame_t *)buffer;
-    recv_info_t *recv_info = (recv_info_t *)priv;
-
-    if (pkt->proto == 0x2222) {
-        switch (pkt->data[0])
-        {
-        case POKE_RESP:
-            xSemaphoreGive(recv_info->mutex);
-            break;
-
-        case DUMMY_TRAFFIC:
-            (recv_info->rx_pkt_cnt)++;
-            break;
-        default:
-            break;
-        }
-    }
-    free(buffer);
-    return ESP_OK;
-}
-
-TEST_CASE("start_stop_stress_test", "[esp_eth]")
-{
-    recv_info_t recv_info;
-    recv_info.mutex = xSemaphoreCreateBinary();
-    TEST_ASSERT_NOT_NULL(recv_info.mutex);
-    recv_info.rx_pkt_cnt = 0;
-
-    eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();  // apply default MAC configuration
-    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
-    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config); // create MAC instance
-    TEST_ASSERT_NOT_NULL(mac);
-    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG(); // apply default PHY configuration
-#if defined(CONFIG_TARGET_ETH_PHY_DEVICE_IP101)
-    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config); // create PHY instance
-#elif defined(CONFIG_TARGET_ETH_PHY_DEVICE_LAN87XX)
-    esp_eth_phy_t *phy = esp_eth_phy_new_lan87xx(&phy_config);
-#endif
-    TEST_ASSERT_NOT_NULL(phy);
-    esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
-    esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_driver_install(&config, &eth_handle)); // install driver
-    TEST_ASSERT_NOT_NULL(eth_handle);
-
-    TEST_ASSERT_EQUAL(ESP_OK, mac->get_addr(mac, 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, eth_recv_cb, &recv_info));
-
-    EventBits_t bits = 0;
-    EventGroupHandle_t eth_event_group = xEventGroupCreate();
-    TEST_ASSERT(eth_event_group != NULL);
-    TEST_ESP_OK(esp_event_loop_create_default());
-    TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, 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 = 0x2222;
-    memset(ctrl_pkt->dest, 0xff, 6); // broadcast addr
-    memcpy(ctrl_pkt->src, local_mac_addr, 6);
-
-    // create dummy data packet used for traffic generation
-    emac_frame_t *pkt = calloc(1, 1500);
-    pkt->proto = 0x2222;
-    // we don't care about dest MAC address much, however it is better to not be broadcast or multifcast to not flood
-    // other network nodes
-    memset(pkt->dest, 0xBA, 6);
-    memcpy(pkt->src, local_mac_addr, 6);
-
-    printf("EMAC start/stop stress test under heavy Tx traffic\n");
-    for (int tx_i = 0; tx_i < 10; tx_i++) {
-        TEST_ASSERT_EQUAL(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(3000));
-        TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
-        // even if PHY (IP101) indicates autonegotiation done and link up, it sometimes may miss few packets after atonego reset, hence wait a bit
-        vTaskDelay(pdMS_TO_TICKS(100));
-
-        // at first, check that Tx/Rx path works as expected by poking the test script
-        // this also serves as main PASS/FAIL criteria
-        ctrl_pkt->data[0] = POKE_REQ;
-        ctrl_pkt->data[1] = tx_i;
-        TEST_ASSERT_EQUAL(ESP_OK, esp_eth_transmit(eth_handle, ctrl_pkt, 60));
-        TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(3000)));
-        printf("Tx Test iteration %d\n", tx_i);
-
-        // generate heavy Tx traffic
-        printf("Note: transmit errors are expected...\n");
-        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[0] = j & 0xFF;
-            esp_eth_transmit(eth_handle, pkt, 1500);
-        }
-        TEST_ASSERT_EQUAL(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("Ethernet stopped\n");
-    }
-
-    printf("EMAC start/stop stress test under heavy Rx traffic\n");
-    for (int rx_i = 0; rx_i < 10; rx_i++) {
-        recv_info.rx_pkt_cnt = 0;
-        TEST_ASSERT_EQUAL(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(3000));
-        TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
-        // even if PHY (IP101) indicates autonegotiation done and link up, it sometimes may miss few packets after atonego reset, hence wait a bit
-        vTaskDelay(pdMS_TO_TICKS(100));
-
-        ctrl_pkt->data[0] = POKE_REQ;
-        ctrl_pkt->data[1] = rx_i;
-        TEST_ASSERT_EQUAL(ESP_OK, esp_eth_transmit(eth_handle, ctrl_pkt, 60));
-        TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(3000)));
-        printf("Rx Test iteration %d\n", rx_i);
-
-        vTaskDelay(pdMS_TO_TICKS(500));
-
-        TEST_ASSERT_EQUAL(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", recv_info.rx_pkt_cnt);
-        TEST_ASSERT_GREATER_THAN_INT32(0, recv_info.rx_pkt_cnt);
-        printf("Ethernet stopped\n");
-    }
-
-    free(ctrl_pkt);
-    free(pkt);
-
-    TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
-    TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_delete_default());
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_driver_uninstall(eth_handle));
-    phy->del(phy);
-    mac->del(mac);
-    vEventGroupDelete(eth_event_group);
-    vSemaphoreDelete(recv_info.mutex);
-}
-
-void app_main(void)
-{
-    unity_run_menu();
-}

+ 93 - 130
components/esp_eth/test/test_emac.c → components/esp_eth/test_apps/main/esp_eth_test_apps.c

@@ -5,34 +5,17 @@
  */
 #include <stdio.h>
 #include <string.h>
+#include <inttypes.h>
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
 #include "freertos/event_groups.h"
-#include "unity.h"
-#include "test_utils.h"
-#include "esp_event.h"
-#include "esp_netif.h"
-#include "esp_eth.h"
 #include "esp_log.h"
 #include "esp_http_client.h"
 #include "esp_rom_md5.h"
-#include "soc/soc_caps.h"
-
-#if SOC_EMAC_SUPPORTED
+#include "esp_eth_test_common.h"
 
 static const char *TAG = "esp32_eth_test";
 
-#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)
-
 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");
 
@@ -40,48 +23,6 @@ extern const char dl_espressif_com_root_cert_pem_end[]   asm("_binary_dl_espress
 static md5_context_t md5_context;
 static uint8_t digest[16];
 
-/** Event handler for Ethernet events */
-static void eth_event_handler(void *arg, esp_event_base_t event_base,
-                              int32_t event_id, void *event_data)
-{
-    EventGroupHandle_t eth_event_group = (EventGroupHandle_t)arg;
-    switch (event_id) {
-    case ETHERNET_EVENT_CONNECTED:
-        xEventGroupSetBits(eth_event_group, ETH_CONNECT_BIT);
-        ESP_LOGI(TAG, "Ethernet Link Up");
-        break;
-    case ETHERNET_EVENT_DISCONNECTED:
-        ESP_LOGI(TAG, "Ethernet Link Down");
-        break;
-    case ETHERNET_EVENT_START:
-        xEventGroupSetBits(eth_event_group, ETH_START_BIT);
-        ESP_LOGI(TAG, "Ethernet Started");
-        break;
-    case ETHERNET_EVENT_STOP:
-        xEventGroupSetBits(eth_event_group, ETH_STOP_BIT);
-        ESP_LOGI(TAG, "Ethernet Stopped");
-        break;
-    default:
-        break;
-    }
-}
-
-/** Event handler for IP_EVENT_ETH_GOT_IP */
-static void 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, "~~~~~~~~~~~");
-    xEventGroupSetBits(eth_event_group, ETH_GOT_IP_BIT);
-}
-
 static esp_err_t test_uninstall_driver(esp_eth_handle_t eth_hdl, uint32_t ms_to_wait)
 {
     int i = 0;
@@ -99,37 +40,59 @@ static esp_err_t test_uninstall_driver(esp_eth_handle_t eth_hdl, uint32_t ms_to_
     }
 }
 
-TEST_CASE("esp32 ethernet io test", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("ethernet io test", "[ethernet]")
 {
     eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
     mac_config.flags = ETH_MAC_FLAG_PIN_TO_CORE; // pin to core
-    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
-    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
-    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
-    // auto detect PHY address
-    phy_config.phy_addr = ESP_ETH_PHY_ADDR_AUTO;
-    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
+    esp_eth_mac_t *mac = mac_init(NULL, &mac_config);
+    TEST_ASSERT_NOT_NULL(mac);
+    esp_eth_phy_t *phy = phy_init(NULL);
+    TEST_ASSERT_NOT_NULL(phy);
     esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
     esp_eth_handle_t eth_handle = NULL;
     TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
-    /* get MAC address */
-    uint8_t mac_addr[6];
+    extra_eth_config(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_TARGET_USE_SPI_ETHERNET
+    TEST_ASSERT_BITS(0b00000011, 0b00, mac_addr[0]);    // Check UL&IG, should be UI
+#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_TARGET_USE_SPI_ETHERNET
     /* 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
     TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
     TEST_ESP_OK(phy->del(phy));
     TEST_ESP_OK(mac->del(mac));
+    extra_cleanup();
 }
 
-TEST_CASE("esp32 ethernet speed/duplex/autonegotiation", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("ethernet io speed/duplex/autonegotiation", "[ethernet]")
 {
     EventBits_t bits = 0;
     EventGroupHandle_t eth_event_group = xEventGroupCreate();
@@ -138,19 +101,19 @@ TEST_CASE("esp32 ethernet speed/duplex/autonegotiation", "[ethernet][test_env=UT
     TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
     eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
     mac_config.flags = ETH_MAC_FLAG_PIN_TO_CORE; // pin to core
-    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
-    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
-    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
-    // auto detect PHY address
-    phy_config.phy_addr = ESP_ETH_PHY_ADDR_AUTO;
-    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
+    esp_eth_mac_t *mac = mac_init(NULL, &mac_config);
+    TEST_ASSERT_NOT_NULL(mac);
+    esp_eth_phy_t *phy = phy_init(NULL);
+    TEST_ASSERT_NOT_NULL(phy);
     esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
     esp_eth_handle_t eth_handle = NULL;
     TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
+    extra_eth_config(eth_handle);
 
     // Set PHY to loopback mode so we do not have to take care about link configuration of the other node.
     // The reason behind is improbable, however, if the other node was configured to e.g. 100 Mbps and we
     // tried to change the speed at ESP node to 10 Mbps, we could get into trouble to establish a link.
+    // TODO: this test in this configuration may not work for all the chips (JIRA IDF-6186)
     bool loopback_en = true;
     esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en);
 
@@ -173,13 +136,13 @@ TEST_CASE("esp32 ethernet speed/duplex/autonegotiation", "[ethernet][test_env=UT
     TEST_ASSERT_EQUAL(ETH_SPEED_100M, exp_speed);
 
     bool exp_autoneg_en;
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &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_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &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, "stop the Ethernet driver and...");
@@ -195,22 +158,22 @@ TEST_CASE("esp32 ethernet speed/duplex/autonegotiation", "[ethernet][test_env=UT
     // 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_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_AUTONEGO, &auto_nego_en));
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &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);
 
     // set new duplex mode
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
+    TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
 
     // set new speed
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &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_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
-    TEST_ASSERT_EQUAL(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_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);
@@ -219,14 +182,14 @@ TEST_CASE("esp32 ethernet speed/duplex/autonegotiation", "[ethernet][test_env=UT
     esp_eth_stop(eth_handle);
     ESP_LOGI(TAG, "change speed again...");
     speed = ETH_SPEED_100M;
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &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_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_SPEED, &exp_speed));
-    TEST_ASSERT_EQUAL(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_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);
 
@@ -234,14 +197,14 @@ TEST_CASE("esp32 ethernet speed/duplex/autonegotiation", "[ethernet][test_env=UT
     esp_eth_stop(eth_handle);
     ESP_LOGI(TAG, "change duplex again...");
     duplex = ETH_DUPLEX_FULL;
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
+    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_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
-    TEST_ASSERT_EQUAL(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_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);
@@ -253,8 +216,8 @@ TEST_CASE("esp32 ethernet speed/duplex/autonegotiation", "[ethernet][test_env=UT
     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_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
-    TEST_ASSERT_EQUAL(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_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);
@@ -265,17 +228,17 @@ TEST_CASE("esp32 ethernet speed/duplex/autonegotiation", "[ethernet][test_env=UT
     duplex = ETH_DUPLEX_HALF;
 
     // set new duplex mode
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
+    TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_DUPLEX_MODE, &duplex));
 
     // set new speed
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_S_SPEED, &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_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
-    TEST_ASSERT_EQUAL(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_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);
@@ -287,11 +250,11 @@ TEST_CASE("esp32 ethernet speed/duplex/autonegotiation", "[ethernet][test_env=UT
     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_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_AUTONEGO, &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);
 
-    TEST_ASSERT_EQUAL(ESP_OK, esp_eth_ioctl(eth_handle, ETH_CMD_G_DUPLEX_MODE, &exp_duplex));
-    TEST_ASSERT_EQUAL(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_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);
@@ -307,24 +270,25 @@ TEST_CASE("esp32 ethernet speed/duplex/autonegotiation", "[ethernet][test_env=UT
     TEST_ESP_OK(mac->del(mac));
     TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
     TEST_ESP_OK(esp_event_loop_delete_default());
+    extra_cleanup();
     vEventGroupDelete(eth_event_group);
 }
 
-TEST_CASE("esp32 ethernet event test", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("ethernet event test", "[ethernet]")
 {
     EventBits_t bits = 0;
     EventGroupHandle_t eth_event_group = xEventGroupCreate();
     TEST_ASSERT(eth_event_group != NULL);
     TEST_ESP_OK(esp_event_loop_create_default());
     TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
-    eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
-    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
-    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
-    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
-    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
+    esp_eth_mac_t *mac = mac_init(NULL, NULL);
+    TEST_ASSERT_NOT_NULL(mac);
+    esp_eth_phy_t *phy = phy_init(NULL);
+    TEST_ASSERT_NOT_NULL(phy);
     esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
     esp_eth_handle_t eth_handle = NULL;
     TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
+    extra_eth_config(eth_handle);
     // 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 */
@@ -344,10 +308,11 @@ TEST_CASE("esp32 ethernet event test", "[ethernet][test_env=UT_T2_Ethernet]")
     TEST_ESP_OK(mac->del(mac));
     TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
     TEST_ESP_OK(esp_event_loop_delete_default());
+    extra_cleanup();
     vEventGroupDelete(eth_event_group);
 }
 
-TEST_CASE("esp32 ethernet dhcp test", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("ethernet dhcp test", "[ethernet]")
 {
     EventBits_t bits = 0;
     EventGroupHandle_t eth_event_group = xEventGroupCreate();
@@ -357,16 +322,15 @@ TEST_CASE("esp32 ethernet dhcp test", "[ethernet][test_env=UT_T2_Ethernet]")
     // create TCP/IP netif
     esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
     esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);
-
-    eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
-    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
-    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
-    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
-    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
+    esp_eth_mac_t *mac = mac_init(NULL, NULL);
+    TEST_ASSERT_NOT_NULL(mac);
+    esp_eth_phy_t *phy = phy_init(NULL);
+    TEST_ASSERT_NOT_NULL(phy);
     esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
     esp_eth_handle_t eth_handle = NULL;
     // install Ethernet driver
     TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
+    extra_eth_config(eth_handle);
     // combine driver with netif
     esp_eth_netif_glue_handle_t glue = esp_eth_new_netif_glue(eth_handle);
     TEST_ESP_OK(esp_netif_attach(eth_netif, glue));
@@ -392,10 +356,11 @@ TEST_CASE("esp32 ethernet dhcp test", "[ethernet][test_env=UT_T2_Ethernet]")
     TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
     esp_netif_destroy(eth_netif);
     TEST_ESP_OK(esp_event_loop_delete_default());
+    extra_cleanup();
     vEventGroupDelete(eth_event_group);
 }
 
-TEST_CASE("esp32 ethernet start/stop stress test", "[ethernet][test_env=UT_T2_Ethernet][timeout=240]")
+TEST_CASE("ethernet start/stop stress test with IP stack", "[ethernet]")
 {
     EventBits_t bits = 0;
     EventGroupHandle_t eth_event_group = xEventGroupCreate();
@@ -405,16 +370,15 @@ TEST_CASE("esp32 ethernet start/stop stress test", "[ethernet][test_env=UT_T2_Et
     // create TCP/IP netif
     esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
     esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);
-
-    eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
-    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
-    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
-    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
-    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
+    esp_eth_mac_t *mac = mac_init(NULL, NULL);
+    TEST_ASSERT_NOT_NULL(mac);
+    esp_eth_phy_t *phy = phy_init(NULL);
+    TEST_ASSERT_NOT_NULL(phy);
     esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
     esp_eth_handle_t eth_handle = NULL;
     // install Ethernet driver
     TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
+    extra_eth_config(eth_handle);
     // combine driver with netif
     esp_eth_netif_glue_handle_t glue = esp_eth_new_netif_glue(eth_handle);
     TEST_ESP_OK(esp_netif_attach(eth_netif, glue));
@@ -444,6 +408,7 @@ TEST_CASE("esp32 ethernet start/stop stress test", "[ethernet][test_env=UT_T2_Et
     TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
     esp_netif_destroy(eth_netif);
     TEST_ESP_OK(esp_event_loop_delete_default());
+    extra_cleanup();
     vEventGroupDelete(eth_event_group);
 }
 
@@ -494,7 +459,7 @@ static void eth_start_download(void)
     esp_rom_md5_final(digest, &md5_context);
 }
 
-TEST_CASE("esp32 ethernet download test", "[ethernet][test_env=UT_T2_Ethernet][timeout=240]")
+TEST_CASE("ethernet download test", "[ethernet]")
 {
     EventBits_t bits = 0;
     EventGroupHandle_t eth_event_group = xEventGroupCreate();
@@ -504,16 +469,15 @@ TEST_CASE("esp32 ethernet download test", "[ethernet][test_env=UT_T2_Ethernet][t
     // create TCP/IP netif
     esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
     esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);
-
-    eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
-    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
-    esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);
-    eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
-    esp_eth_phy_t *phy = esp_eth_phy_new_ip101(&phy_config);
+    esp_eth_mac_t *mac = mac_init(NULL, NULL);
+    TEST_ASSERT_NOT_NULL(mac);
+    esp_eth_phy_t *phy = phy_init(NULL);
+    TEST_ASSERT_NOT_NULL(phy);
     esp_eth_config_t eth_config = ETH_DEFAULT_CONFIG(mac, phy);
     esp_eth_handle_t eth_handle = NULL;
     // install Ethernet driver
     TEST_ESP_OK(esp_eth_driver_install(&eth_config, &eth_handle));
+    extra_eth_config(eth_handle);
     // combine driver with netif
     esp_eth_netif_glue_handle_t glue = esp_eth_new_netif_glue(eth_handle);
     TEST_ESP_OK(esp_netif_attach(eth_netif, glue));
@@ -535,7 +499,7 @@ TEST_CASE("esp32 ethernet download test", "[ethernet][test_env=UT_T2_Ethernet][t
         printf("%d ", digest[i]);
     }
     printf("\r\n");
-    TEST_ASSERT(memcmp(expect_digest, digest, sizeof(digest)) == 0);
+    TEST_ASSERT_EQUAL_UINT8_ARRAY(expect_digest, digest, sizeof(digest));
 
     // stop Ethernet driver
     TEST_ESP_OK(esp_eth_stop(eth_handle));
@@ -551,7 +515,6 @@ TEST_CASE("esp32 ethernet download test", "[ethernet][test_env=UT_T2_Ethernet][t
     TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
     esp_netif_destroy(eth_netif);
     TEST_ESP_OK(esp_event_loop_delete_default());
+    extra_cleanup();
     vEventGroupDelete(eth_event_group);
 }
-
-#endif // SOC_EMAC_SUPPORTED

+ 173 - 0
components/esp_eth/test_apps/main/esp_eth_test_common.c

@@ -0,0 +1,173 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include "freertos/FreeRTOS.h"
+#include "freertos/event_groups.h"
+#include "esp_event.h"
+#include "esp_log.h"
+#if CONFIG_TARGET_USE_SPI_ETHERNET
+#include "driver/spi_master.h"
+#include "driver/gpio.h"
+#include "esp_mac.h"
+#endif // CONFIG_TARGET_USE_SPI_ETHERNET
+#include "sdkconfig.h"
+#include "esp_eth_test_common.h"
+
+#if CONFIG_TARGET_USE_SPI_ETHERNET
+#define DEFAULT_TARGET_SPI_HOST 1
+#define DEFAULT_TARGET_SPI_MISO_GPIO 12
+#define DEFAULT_TARGET_SPI_MOSI_GPIO 13
+#define DEFAULT_TARGET_SPI_SCLK_GPIO 14
+#define DEFAULT_TARGET_SPI_CS_GPIO 15
+#endif // CONFIG_TARGET_USE_SPI_ETHERNET
+
+static const char *TAG = "esp32_eth_test_common";
+
+esp_eth_mac_t *mac_init(void *vendor_emac_config, eth_mac_config_t *mac_config)
+{
+    esp_eth_mac_t *mac = NULL;
+
+    eth_mac_config_t mac_config_default = ETH_MAC_DEFAULT_CONFIG();
+    if (mac_config == NULL) {
+        mac_config = &mac_config_default;
+    }
+#if CONFIG_TARGET_USE_INTERNAL_ETHERNET
+    eth_esp32_emac_config_t esp32_emac_config = ETH_ESP32_EMAC_DEFAULT_CONFIG();
+    if (vendor_emac_config == NULL) {
+        vendor_emac_config = &esp32_emac_config;
+    }
+    mac = esp_eth_mac_new_esp32(vendor_emac_config, mac_config);
+#elif CONFIG_TARGET_USE_SPI_ETHERNET
+    // Install GPIO ISR handler to be able to service SPI Eth modlues interrupts
+    gpio_install_isr_service(0);
+
+    spi_bus_config_t buscfg = {
+        .miso_io_num = DEFAULT_TARGET_SPI_MISO_GPIO,
+        .mosi_io_num = DEFAULT_TARGET_SPI_MOSI_GPIO,
+        .sclk_io_num = DEFAULT_TARGET_SPI_SCLK_GPIO,
+        .quadwp_io_num = -1,
+        .quadhd_io_num = -1,
+    };
+    TEST_ESP_OK(spi_bus_initialize(DEFAULT_TARGET_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO));
+    spi_device_interface_config_t devcfg = {
+        .mode = 0,
+        .spics_io_num = DEFAULT_TARGET_SPI_CS_GPIO,
+        .clock_speed_hz = CONFIG_TARGET_SPI_CLOCK_MHZ * 1000 * 1000,
+        .queue_size = 20
+    };
+#if CONFIG_TARGET_ETH_PHY_DEVICE_W5500
+    eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(DEFAULT_TARGET_SPI_HOST, &devcfg);
+    if (vendor_emac_config == NULL) {
+        vendor_emac_config = &w5500_config;
+    }
+    mac = esp_eth_mac_new_w5500(vendor_emac_config, mac_config);
+#elif CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8851SNL
+    eth_ksz8851snl_config_t ksz8851snl_config = ETH_KSZ8851SNL_DEFAULT_CONFIG(DEFAULT_TARGET_SPI_HOST, &devcfg);
+    ksz8851snl_config.int_gpio_num = 4;
+    if (vendor_emac_config == NULL) {
+        vendor_emac_config = &ksz8851snl_config;
+    }
+    mac = esp_eth_mac_new_ksz8851snl(vendor_emac_config, mac_config);
+#elif CONFIG_TARGET_ETH_PHY_DEVICE_DM9051
+    eth_dm9051_config_t dm9051_config = ETH_DM9051_DEFAULT_CONFIG(DEFAULT_TARGET_SPI_HOST, &devcfg);
+    if (vendor_emac_config == NULL) {
+        vendor_emac_config = &dm9051_config ;
+    }
+    mac = esp_eth_mac_new_dm9051(vendor_emac_config, mac_config);
+#endif // CONFIG_TARGET_ETH_PHY_DEVICE_W5500
+#endif // CONFIG_TARGET_USE_INTERNAL_ETHERNET
+    return mac;
+}
+
+esp_eth_phy_t *phy_init(eth_phy_config_t *phy_config)
+{
+    esp_eth_phy_t *phy = NULL;
+    eth_phy_config_t phy_config_default = ETH_PHY_DEFAULT_CONFIG();
+    if (phy_config == NULL) {
+        phy_config = &phy_config_default;
+    }
+#if CONFIG_TARGET_USE_INTERNAL_ETHERNET
+    phy_config->phy_addr = ESP_ETH_PHY_ADDR_AUTO;
+#if CONFIG_TARGET_ETH_PHY_DEVICE_IP101
+    phy = esp_eth_phy_new_ip101(phy_config);
+#elif CONFIG_TARGET_ETH_PHY_DEVICE_LAN87XX
+    phy = esp_eth_phy_new_lan87xx(phy_config);
+#elif CONFIG_TARGET_ETH_PHY_DEVICE_KSZ80XX
+    phy = esp_eth_phy_new_ksz80xx(phy_config);
+#elif CONFIG_TARGET_ETH_PHY_DEVICE_RTL8201
+    phy = esp_eth_phy_new_rtl8201(phy_config);
+#elif CONFIG_TARGET_ETH_PHY_DEVICE_DP83848
+    phy = esp_eth_phy_new_dp83848(phy_config);
+#endif // CONFIG_TARGET_ETH_PHY_DEVICE_IP101
+#elif CONFIG_TARGET_USE_SPI_ETHERNET
+    phy_config->reset_gpio_num = -1;
+#if CONFIG_TARGET_ETH_PHY_DEVICE_W5500
+    phy = esp_eth_phy_new_w5500(phy_config);
+#elif CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8851SNL
+    phy = esp_eth_phy_new_ksz8851snl(phy_config);
+#elif CONFIG_TARGET_ETH_PHY_DEVICE_DM9051
+    phy = esp_eth_phy_new_dm9051(phy_config);
+#endif // CONFIG_TARGET_ETH_PHY_DEVICE_W5500
+#endif // CONFIG_TARGET_USE_INTERNAL_ETHERNET
+    return phy;
+}
+
+void extra_eth_config(esp_eth_handle_t eth_handle)
+{
+// *** SPI Ethernet modules deviation ***
+// Rationale: The SPI Ethernet modules don't have a burned default factory MAC address
+#if CONFIG_TARGET_USE_SPI_ETHERNET
+    uint8_t base_mac_addr[ETH_ADDR_LEN];
+    TEST_ESP_OK(esp_efuse_mac_get_default(base_mac_addr));
+    uint8_t local_mac[ETH_ADDR_LEN];
+    TEST_ESP_OK(esp_derive_local_mac(local_mac, base_mac_addr));
+    TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_S_MAC_ADDR, local_mac));
+#endif
+}
+
+void extra_cleanup(void)
+{
+#if CONFIG_TARGET_USE_SPI_ETHERNET
+    TEST_ESP_OK(spi_bus_free(DEFAULT_TARGET_SPI_HOST));
+#endif
+}
+
+/** Event handler for Ethernet events */
+void eth_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:
+        xEventGroupSetBits(eth_event_group, ETH_CONNECT_BIT);
+        break;
+    case ETHERNET_EVENT_DISCONNECTED:
+        break;
+    case ETHERNET_EVENT_START:
+        xEventGroupSetBits(eth_event_group, ETH_START_BIT);
+    break;
+    case ETHERNET_EVENT_STOP:
+        xEventGroupSetBits(eth_event_group, ETH_STOP_BIT);
+        break;
+    default:
+        break;
+    }
+}
+
+/** Event handler for IP_EVENT_ETH_GOT_IP */
+void 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, "~~~~~~~~~~~");
+    xEventGroupSetBits(eth_event_group, ETH_GOT_IP_BIT);
+}

+ 42 - 0
components/esp_eth/test_apps/main/esp_eth_test_common.h

@@ -0,0 +1,42 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#pragma once
+
+#include "unity.h"
+#include "test_utils.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)
+
+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;
+
+esp_eth_mac_t *mac_init(void *vendor_emac_config, eth_mac_config_t *mac_config);
+esp_eth_phy_t *phy_init(eth_phy_config_t *phy_config);
+void extra_eth_config(esp_eth_handle_t eth_handle);
+void extra_cleanup(void);
+
+/** Event handler for Ethernet events */
+void eth_event_handler(void *arg, esp_event_base_t event_base,
+                                int32_t event_id, void *event_data);
+/** Event handler for IP_EVENT_ETH_GOT_IP */
+void got_ip_event_handler(void *arg, esp_event_base_t event_base,
+                                 int32_t event_id, void *event_data);

+ 255 - 0
components/esp_eth/test_apps/main/esp_eth_test_hal.c

@@ -0,0 +1,255 @@
+/*
+ * SPDX-FileCopyrightText: 2022 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/event_groups.h"
+#include "esp_log.h"
+#include "esp_eth_test_common.h"
+
+#define ETHERTYPE_TX_STD        0x2222  // frame transmitted via emac_hal_transmit_frame
+#define ETHERTYPE_TX_MULTI_2    0x2223  // frame transmitted via emac_hal_transmit_multiple_buf_frame (2 buffers)
+#define ETHERTYPE_TX_MULTI_3    0x2224  // frame transmitted via emac_hal_transmit_multiple_buf_frame (3 buffers)
+
+static const char *TAG = "esp32_eth_test_hal";
+
+typedef struct
+{
+    SemaphoreHandle_t mutex;
+    uint16_t expected_size;
+    uint16_t expected_size_2;
+    uint16_t expected_size_3;
+} recv_hal_check_info_t;
+
+static esp_err_t eth_recv_hal_check_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv)
+{
+    emac_frame_t *pkt = (emac_frame_t *)buffer;
+    recv_hal_check_info_t *recv_info = (recv_hal_check_info_t *)priv;
+    uint16_t expected_size = recv_info->expected_size + recv_info->expected_size_2 + recv_info->expected_size_3;
+
+    ESP_LOGI(TAG, "recv frame size: %" PRIu16, expected_size);
+    TEST_ASSERT_EQUAL(expected_size, length);
+    // frame transmitted via emac_hal_transmit_frame
+    if (pkt->proto == ETHERTYPE_TX_STD) {
+        for (int i = 0; i < recv_info->expected_size - ETH_HEADER_LEN; i++) {
+            TEST_ASSERT_EQUAL(pkt->data[i], i & 0xFF);
+        }
+    // frame transmitted via emac_hal_transmit_multiple_buf_frame (2 buffers)
+    } else if (pkt->proto == ETHERTYPE_TX_MULTI_2) {
+        uint8_t *data_p = pkt->data;
+        for (int i = 0; i < recv_info->expected_size - ETH_HEADER_LEN; i++) {
+            TEST_ASSERT_EQUAL(*(data_p++), i & 0xFF);
+        }
+        int j = ETH_MAX_PAYLOAD_LEN;
+        for (int i = 0; i < recv_info->expected_size_2; i++) {
+            TEST_ASSERT_EQUAL(*(data_p++), j & 0xFF);
+            j--;
+        }
+    // frame transmitted via emac_hal_transmit_multiple_buf_frame (3 buffers)
+    } else if (pkt->proto == ETHERTYPE_TX_MULTI_3) {
+        uint8_t *data_p = pkt->data;
+        for (int i = 0; i < recv_info->expected_size - ETH_HEADER_LEN; i++) {
+            TEST_ASSERT_EQUAL(*(data_p++), i & 0xFF);
+        }
+        int j = ETH_MAX_PAYLOAD_LEN;
+        for (int i = 0; i < recv_info->expected_size_2; i++) {
+            TEST_ASSERT_EQUAL(*(data_p++), j & 0xFF);
+            j--;
+        }
+        for (int i = 0; i < recv_info->expected_size_3; i++) {
+            TEST_ASSERT_EQUAL(*(data_p++), i & 0xFF);
+        }
+    } else {
+        TEST_FAIL();
+    }
+    memset(buffer, 0, length);
+    free(buffer);
+    xSemaphoreGive(recv_info->mutex);
+    return ESP_OK;
+}
+
+TEST_CASE("hal receive/transmit", "[emac_hal]")
+{
+    recv_hal_check_info_t recv_info;
+    recv_info.mutex = xSemaphoreCreateBinary();
+    TEST_ASSERT_NOT_NULL(recv_info.mutex);
+    recv_info.expected_size = 0;
+    recv_info.expected_size_2 = 0;
+    recv_info.expected_size_3 = 0;
+
+    EventBits_t bits = 0;
+    EventGroupHandle_t eth_event_group = xEventGroupCreate();
+    TEST_ASSERT(eth_event_group != NULL);
+    TEST_ESP_OK(esp_event_loop_create_default());
+    TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
+
+    esp_eth_mac_t *mac = mac_init(NULL, NULL);
+    TEST_ASSERT_NOT_NULL(mac);
+    esp_eth_phy_t *phy = phy_init(NULL);
+    TEST_ASSERT_NOT_NULL(phy);
+    esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
+    esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
+    TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
+    TEST_ASSERT_NOT_NULL(eth_handle);
+    extra_eth_config(eth_handle);
+
+    // loopback greatly simplifies the test
+    bool loopback_en = true;
+    esp_eth_ioctl(eth_handle, ETH_CMD_S_PHY_LOOPBACK, &loopback_en);
+
+    TEST_ESP_OK(esp_eth_update_input_path(eth_handle, eth_recv_hal_check_cb, &recv_info));
+
+    // start the driver
+    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);
+
+    // create test frame
+    emac_frame_t *test_pkt = calloc(1, ETH_MAX_PACKET_SIZE);
+    test_pkt->proto = ETHERTYPE_TX_STD;
+    memset(test_pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr
+    uint8_t local_mac_addr[ETH_ADDR_LEN] = { 0 };
+    TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, local_mac_addr));
+    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;
+    }
+
+    // verify that HAL driver correctly processes frame from EMAC descriptors
+    uint16_t transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE;
+    ESP_LOGI(TAG, "transmit frame size: %" PRIu16, transmit_size);
+    recv_info.expected_size = transmit_size;
+    TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
+    TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
+
+    transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE - 1;
+    ESP_LOGI(TAG, "transmit frame size: %" PRIu16, transmit_size);
+    recv_info.expected_size = transmit_size;
+    TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
+    TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
+
+    transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE + 1;
+    ESP_LOGI(TAG, "transmit frame size: %" PRIu16, transmit_size);
+    recv_info.expected_size = transmit_size;
+    TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
+    TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
+
+    transmit_size = 2 * CONFIG_ETH_DMA_BUFFER_SIZE;
+    ESP_LOGI(TAG, "transmit frame size: %" PRIu16, transmit_size);
+    recv_info.expected_size = transmit_size;
+    TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
+    TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
+
+    transmit_size = 2 * CONFIG_ETH_DMA_BUFFER_SIZE - 1;
+    ESP_LOGI(TAG, "transmit frame size: %" PRIu16, transmit_size);
+    recv_info.expected_size = transmit_size;
+    TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
+    TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
+
+    transmit_size = 2 * CONFIG_ETH_DMA_BUFFER_SIZE + 1;
+    ESP_LOGI(TAG, "transmit frame size: %" PRIu16, transmit_size);
+    recv_info.expected_size = transmit_size;
+    TEST_ESP_OK(esp_eth_transmit(eth_handle, test_pkt, transmit_size));
+    TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
+
+    // verify transmission of multiple buffers
+    uint16_t transmit_size_2;
+    // allocated the second buffer
+    uint8_t *pkt_data_2 = malloc(ETH_MAX_PAYLOAD_LEN);
+    // fill with data (reverse order to differentiate the buffers)
+    int j = ETH_MAX_PAYLOAD_LEN;
+    for (int i = 0; i < ETH_MAX_PAYLOAD_LEN; i++) {
+        pkt_data_2[i] = j & 0xFF;
+        j--;
+    }
+    // change protocol number so the cb function is aware that frame was joint from two buffers
+    test_pkt->proto = ETHERTYPE_TX_MULTI_2;
+    transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE;
+    transmit_size_2 = CONFIG_ETH_DMA_BUFFER_SIZE;
+    recv_info.expected_size = transmit_size;
+    recv_info.expected_size_2 = transmit_size_2;
+    ESP_LOGI(TAG, "transmit joint frame size: %" PRIu16, transmit_size + transmit_size_2);
+    TEST_ESP_OK(esp_eth_transmit_vargs(eth_handle, 2, test_pkt, transmit_size, pkt_data_2, transmit_size_2));
+    TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
+
+    transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE - 1;
+    transmit_size_2 = CONFIG_ETH_DMA_BUFFER_SIZE;
+    recv_info.expected_size = transmit_size;
+    recv_info.expected_size_2 = transmit_size_2;
+    ESP_LOGI(TAG, "transmit joint frame size: %" PRIu16, transmit_size + transmit_size_2);
+    TEST_ESP_OK(esp_eth_transmit_vargs(eth_handle, 2, test_pkt, transmit_size, pkt_data_2, transmit_size_2));
+    TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
+
+    transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE + 1;
+    transmit_size_2 = CONFIG_ETH_DMA_BUFFER_SIZE;
+    recv_info.expected_size = transmit_size;
+    recv_info.expected_size_2 = transmit_size_2;
+    ESP_LOGI(TAG, "transmit joint frame size: %" PRIu16, transmit_size + transmit_size_2);
+    TEST_ESP_OK(esp_eth_transmit_vargs(eth_handle, 2, test_pkt, transmit_size, pkt_data_2, transmit_size_2));
+    TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
+
+    uint16_t transmit_size_3 = 256;
+    // allocated the third buffer
+    uint8_t *pkt_data_3 = malloc(256);
+    // fill with data
+    for (int i = 0; i < 256; i++) {
+        pkt_data_3[i] = i & 0xFF;
+    }
+    // change protocol number so the cb function is aware that frame was joint from three buffers
+    test_pkt->proto = ETHERTYPE_TX_MULTI_3;
+    transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE;
+    transmit_size_2 = CONFIG_ETH_DMA_BUFFER_SIZE;
+    transmit_size_3 = 256;
+    recv_info.expected_size = transmit_size;
+    recv_info.expected_size_2 = transmit_size_2;
+    recv_info.expected_size_3 = transmit_size_3;
+    ESP_LOGI(TAG, "transmit joint frame size (3 buffs): %" PRIu16, transmit_size + transmit_size_2 + transmit_size_3);
+    TEST_ESP_OK(esp_eth_transmit_vargs(eth_handle, 3, test_pkt, transmit_size, pkt_data_2, transmit_size_2, pkt_data_3, transmit_size_3));
+    TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
+
+    transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE - 1;
+    transmit_size_2 = CONFIG_ETH_DMA_BUFFER_SIZE;
+    transmit_size_3 = 256;
+    recv_info.expected_size = transmit_size;
+    recv_info.expected_size_2 = transmit_size_2;
+    recv_info.expected_size_3 = transmit_size_3;
+    ESP_LOGI(TAG, "transmit joint frame size (3 buffs): %" PRIu16, transmit_size + transmit_size_2 + transmit_size_3);
+    TEST_ESP_OK(esp_eth_transmit_vargs(eth_handle, 3, test_pkt, transmit_size, pkt_data_2, transmit_size_2, pkt_data_3, transmit_size_3));
+    TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
+
+    transmit_size = CONFIG_ETH_DMA_BUFFER_SIZE + 1;
+    transmit_size_2 = CONFIG_ETH_DMA_BUFFER_SIZE;
+    transmit_size_3 = 256;
+    recv_info.expected_size = transmit_size;
+    recv_info.expected_size_2 = transmit_size_2;
+    recv_info.expected_size_3 = transmit_size_3;
+    ESP_LOGI(TAG, "transmit joint frame size (3 buffs): %" PRIu16, transmit_size + transmit_size_2 + transmit_size_3);
+    TEST_ESP_OK(esp_eth_transmit_vargs(eth_handle, 3, test_pkt, transmit_size, pkt_data_2, transmit_size_2, pkt_data_3, transmit_size_3));
+    TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(500)));
+
+    free(test_pkt);
+    free(pkt_data_2);
+    free(pkt_data_3);
+
+    // stop Ethernet driver
+    TEST_ESP_OK(esp_eth_stop(eth_handle));
+    /* wait for connection stop */
+    bits = xEventGroupWaitBits(eth_event_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(ETH_STOP_TIMEOUT_MS));
+    TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
+    TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
+    TEST_ESP_OK(phy->del(phy));
+    TEST_ESP_OK(mac->del(mac));
+    TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
+    TEST_ESP_OK(esp_event_loop_delete_default());
+    extra_cleanup();
+    vEventGroupDelete(eth_event_group);
+    vSemaphoreDelete(recv_info.mutex);
+}

+ 277 - 0
components/esp_eth/test_apps/main/esp_eth_test_l2.c

@@ -0,0 +1,277 @@
+/*
+ * SPDX-FileCopyrightText: 2022 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_common.h"
+
+#define ETH_BROADCAST_RECV_BIT BIT(0)
+#define ETH_MULTICAST_RECV_BIT BIT(1)
+#define ETH_UNICAST_RECV_BIT BIT(2)
+
+#define POKE_REQ 0xFA
+#define POKE_RESP 0xFB
+#define DUMMY_TRAFFIC 0xFF
+
+TEST_CASE("ethernet broadcast transmit", "[ethernet_l2]")
+{
+    EventGroupHandle_t eth_event_group = xEventGroupCreate();
+    TEST_ASSERT(eth_event_group != NULL);
+
+    esp_eth_mac_t *mac = mac_init(NULL, NULL);
+    TEST_ASSERT_NOT_NULL(mac);
+    esp_eth_phy_t *phy = phy_init(NULL);
+    TEST_ASSERT_NOT_NULL(phy);
+    esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
+    esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
+    TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
+    TEST_ASSERT_NOT_NULL(eth_handle);
+    extra_eth_config(eth_handle);
+    TEST_ESP_OK(esp_event_loop_create_default());
+    TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_group));
+    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(3000));
+    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)
+    vTaskDelay(pdMS_TO_TICKS(1000));
+
+    emac_frame_t *pkt = malloc(1024);
+    pkt->proto = 0x2222;
+    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));
+    vTaskDelay(pdMS_TO_TICKS(100));
+    free(pkt);
+
+    TEST_ESP_OK(esp_eth_stop(eth_handle));
+    TEST_ESP_OK(esp_event_loop_delete_default());
+    TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
+    phy->del(phy);
+    mac->del(mac);
+    extra_cleanup();
+    vEventGroupDelete(eth_event_group);
+}
+
+static uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
+
+esp_err_t l2_packet_txrx_test_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv) {
+    EventGroupHandle_t eth_event_group = (EventGroupHandle_t)priv;
+    emac_frame_t *pkt = (emac_frame_t *) buffer;
+    // check header
+    if (pkt->proto == 0x2222 && length == 1024) {
+        // check content
+        for (int i = 0; i < (length - ETH_HEADER_LEN); ++i) {
+            if (pkt->data[i] != (i & 0xff)) {
+                printf("payload mismatch\n");
+                return ESP_OK;
+            }
+        }
+        if (memcmp(pkt->dest, "\xff\xff\xff\xff\xff\xff", ETH_ADDR_LEN) == 0) {
+            printf("broadcast received...\n");
+            xEventGroupSetBits(eth_event_group, ETH_BROADCAST_RECV_BIT);
+        } else if (pkt->dest[0] & 0x1) {
+            printf("multicast received...\n");
+            xEventGroupSetBits(eth_event_group, ETH_MULTICAST_RECV_BIT);
+        } else if (memcmp(pkt->dest, local_mac_addr, ETH_ADDR_LEN) == 0) {
+            printf("unicast received...\n");
+            xEventGroupSetBits(eth_event_group, ETH_UNICAST_RECV_BIT);
+        }
+    } else {
+        printf("unexpected frame (protocol: 0x%x, length: %" PRIu32 ")\n", pkt->proto, length);
+    }
+    return ESP_OK;
+};
+
+TEST_CASE("ethernet recv_pkt", "[ethernet_l2]")
+{
+    EventGroupHandle_t eth_event_group = xEventGroupCreate();
+    TEST_ASSERT(eth_event_group != NULL);
+
+    esp_eth_mac_t *mac = mac_init(NULL, NULL);
+    TEST_ASSERT_NOT_NULL(mac);
+    esp_eth_phy_t *phy = phy_init(NULL);
+    TEST_ASSERT_NOT_NULL(phy);
+    esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
+    esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
+    TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
+    TEST_ASSERT_NOT_NULL(eth_handle);
+    extra_eth_config(eth_handle);
+    TEST_ESP_OK(esp_event_loop_create_default());
+    TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
+
+    TEST_ESP_OK(mac->get_addr(mac, 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, eth_event_group));
+
+    EventBits_t bits = 0;
+    bits = xEventGroupWaitBits(eth_event_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
+                               true, true, pdMS_TO_TICKS(5000));
+    TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) ==
+                                 (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
+
+    TEST_ESP_OK(esp_eth_stop(eth_handle));
+    TEST_ESP_OK(esp_event_loop_delete_default());
+    TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
+    phy->del(phy);
+    mac->del(mac);
+    extra_cleanup();
+    vEventGroupDelete(eth_event_group);
+}
+
+typedef struct
+{
+    SemaphoreHandle_t mutex;
+    int rx_pkt_cnt;
+} recv_info_t;
+
+static esp_err_t eth_recv_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv)
+{
+    emac_frame_t *pkt = (emac_frame_t *)buffer;
+    recv_info_t *recv_info = (recv_info_t *)priv;
+
+    if (pkt->proto == 0x2222) {
+        switch (pkt->data[0])
+        {
+        case POKE_RESP:
+            xSemaphoreGive(recv_info->mutex);
+            break;
+
+        case DUMMY_TRAFFIC:
+            (recv_info->rx_pkt_cnt)++;
+            break;
+        default:
+            break;
+        }
+    }
+    free(buffer);
+    return ESP_OK;
+}
+
+TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]")
+{
+    recv_info_t recv_info;
+    recv_info.mutex = xSemaphoreCreateBinary();
+    TEST_ASSERT_NOT_NULL(recv_info.mutex);
+    recv_info.rx_pkt_cnt = 0;
+
+    esp_eth_mac_t *mac = mac_init(NULL, NULL);
+    TEST_ASSERT_NOT_NULL(mac);
+    esp_eth_phy_t *phy = phy_init(NULL);
+    TEST_ASSERT_NOT_NULL(phy);
+    esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
+    esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
+    TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
+    TEST_ASSERT_NOT_NULL(eth_handle);
+    extra_eth_config(eth_handle);
+
+    TEST_ESP_OK(mac->get_addr(mac, 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, eth_recv_cb, &recv_info));
+
+    EventBits_t bits = 0;
+    EventGroupHandle_t eth_event_group = xEventGroupCreate();
+    TEST_ASSERT(eth_event_group != NULL);
+    TEST_ESP_OK(esp_event_loop_create_default());
+    TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, 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 = 0x2222;
+    memset(ctrl_pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr
+    memcpy(ctrl_pkt->src, local_mac_addr, ETH_ADDR_LEN);
+
+    // create dummy data packet used for traffic generation
+    emac_frame_t *pkt = calloc(1, 1500);
+    pkt->proto = 0x2222;
+    // we don't care about dest MAC address much, however it is better to not be broadcast or multifcast to not flood
+    // other network nodes
+    memset(pkt->dest, 0xBA, ETH_ADDR_LEN);
+    memcpy(pkt->src, local_mac_addr, ETH_ADDR_LEN);
+
+    printf("EMAC start/stop stress test under heavy Tx traffic\n");
+    for (int tx_i = 0; tx_i < 10; 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(3000));
+        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)
+        vTaskDelay(pdMS_TO_TICKS(1000));
+
+        // at first, check that Tx/Rx path works as expected by poking the test script
+        // this also serves as main PASS/FAIL criteria
+        ctrl_pkt->data[0] = POKE_REQ;
+        ctrl_pkt->data[1] = tx_i;
+        TEST_ESP_OK(esp_eth_transmit(eth_handle, ctrl_pkt, 60));
+        TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(3000)));
+        printf("Tx Test iteration %d\n", tx_i);
+
+        // generate heavy Tx traffic
+        printf("Note: transmit errors are expected...\n");
+        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[0] = 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("Ethernet stopped\n");
+    }
+
+    printf("EMAC start/stop stress test under heavy Rx traffic\n");
+    for (int rx_i = 0; rx_i < 10; rx_i++) {
+        recv_info.rx_pkt_cnt = 0;
+        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(3000));
+        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)
+        vTaskDelay(pdMS_TO_TICKS(1000));
+
+        ctrl_pkt->data[0] = POKE_REQ;
+        ctrl_pkt->data[1] = rx_i;
+        TEST_ESP_OK(esp_eth_transmit(eth_handle, ctrl_pkt, 60));
+        TEST_ASSERT(xSemaphoreTake(recv_info.mutex, pdMS_TO_TICKS(3000)));
+        printf("Rx Test iteration %d\n", rx_i);
+
+        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", recv_info.rx_pkt_cnt);
+        TEST_ASSERT_GREATER_THAN_INT32(0, recv_info.rx_pkt_cnt);
+        printf("Ethernet stopped\n");
+    }
+
+    free(ctrl_pkt);
+    free(pkt);
+
+    TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
+    TEST_ESP_OK(esp_event_loop_delete_default());
+    TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
+    phy->del(phy);
+    mac->del(mac);
+    extra_cleanup();
+    vEventGroupDelete(eth_event_group);
+    vSemaphoreDelete(recv_info.mutex);
+}

+ 11 - 0
components/esp_eth/test_apps/main/esp_eth_test_main.c

@@ -0,0 +1,11 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include "test_utils.h"
+
+void app_main(void)
+{
+    unity_run_menu();
+}

+ 61 - 22
components/esp_eth/test_apps/pytest_esp_eth.py

@@ -5,6 +5,7 @@ import contextlib
 import logging
 import os
 import socket
+import time
 from multiprocessing import Pipe, Process, connection
 from typing import Iterator
 
@@ -16,28 +17,34 @@ ETH_TYPE = 0x2222
 
 
 class EthTestIntf(object):
-    def __init__(self, eth_type: int):
+    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) -> None:
+    def find_target_if(self, my_if: str = '') -> None:
         # try to determine which interface to use
         netifs = os.listdir('/sys/class/net/')
         logging.info('detected interfaces: %s', str(netifs))
 
         for netif in netifs:
-            if netif.find('eth') == 0 or netif.find('enp') == 0 or netif.find('eno') == 0:
-                self.target_if = netif
-                break
+            # if no interface defined, try to find it automatically
+            if my_if == '':
+                if netif.find('eth') == 0 or netif.find('enp') == 0 or netif.find('eno') == 0:
+                    self.target_if = netif
+                    break
+            else:
+                if netif.find(my_if) == 0:
+                    self.target_if = my_if
+                    break
         if self.target_if == '':
-            raise Exception('no network interface found')
+            raise Exception('network interface not found')
         logging.info('Use %s for testing', self.target_if)
 
     @contextlib.contextmanager
     def configure_eth_if(self) -> Iterator[socket.socket]:
         so = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(self.eth_type))
         so.bind((self.target_if, 0))
-
         try:
             yield so
         finally:
@@ -83,25 +90,34 @@ class EthTestIntf(object):
                 raise e
 
 
-def actual_test(dut: Dut) -> None:
-    target_if = EthTestIntf(ETH_TYPE)
-    target_if.find_target_if()
+def ethernet_test(dut: Dut) -> None:
+    dut.expect_exact('Press ENTER to see the list of tests')
+    dut.write('\n')
+
+    dut.expect_exact('Enter test for running.')
+    dut.write('[ethernet]')
+    dut.expect_unity_test_output(timeout=980)
+
 
+def ethernet_int_emac_hal_test(dut: Dut) -> None:
     dut.expect_exact('Press ENTER to see the list of tests')
     dut.write('\n')
 
     dut.expect_exact('Enter test for running.')
-    dut.write('"start_and_stop"')
+    dut.write('[emac_hal]')
     dut.expect_unity_test_output()
 
-    dut.expect_exact("Enter next test, or 'enter' to see menu")
-    dut.write('"get_set_mac"')
-    dut.expect_unity_test_output()
 
-    dut.expect_exact("Enter next test, or 'enter' to see menu")
+def ethernet_l2_test(dut: Dut) -> None:
+    target_if = EthTestIntf(ETH_TYPE)
+
+    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"')
+        dut.write('"ethernet broadcast transmit"')
         eth_frame = Ether(so.recv(1024))
         for i in range(0, 1010):
             if eth_frame.load[i] != i & 0xff:
@@ -109,18 +125,19 @@ def actual_test(dut: Dut) -> None:
     dut.expect_unity_test_output()
 
     dut.expect_exact("Enter next test, or 'enter' to see menu")
-    dut.write('"recv_pkt"')
+    dut.write('"ethernet recv_pkt"')
     res = dut.expect(
         r'([\s\S]*)'
         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})'
     )
+    time.sleep(1)
     target_if.send_eth_packet('ff:ff:ff:ff:ff:ff')  # broadcast frame
     target_if.send_eth_packet('01:00:00:00:00:00')  # multicast frame
     target_if.send_eth_packet(res.group(2))  # 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('"start_stop_stress_test"')
+    dut.write('"ethernet start/stop stress test under heavy traffic"')
     res = dut.expect(
         r'([\s\S]*)'
         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})'
@@ -144,21 +161,43 @@ def actual_test(dut: Dut) -> None:
     dut.expect_unity_test_output(extra_before=res.group(1))
 
 
+@pytest.mark.esp32
+@pytest.mark.ethernet
+@pytest.mark.parametrize('config', [
+    'default_ip101',
+    'release_ip101',
+    'single_core_ip101'
+], indirect=True)
+@pytest.mark.flaky(reruns=3, reruns_delay=5)
+def test_esp_ethernet(dut: Dut) -> None:
+    ethernet_test(dut)
+
+
+@pytest.mark.esp32
+@pytest.mark.ethernet
+@pytest.mark.parametrize('config', [
+    'default_ip101',
+], indirect=True)
+@pytest.mark.flaky(reruns=3, reruns_delay=5)
+def test_esp_emac_hal(dut: Dut) -> None:
+    ethernet_int_emac_hal_test(dut)
+
+
 @pytest.mark.esp32
 @pytest.mark.ip101
 @pytest.mark.parametrize('config', [
-    'ip101',
+    'default_ip101',
 ], indirect=True)
 @pytest.mark.flaky(reruns=3, reruns_delay=5)
 def test_esp_eth_ip101(dut: Dut) -> None:
-    actual_test(dut)
+    ethernet_l2_test(dut)
 
 
 @pytest.mark.esp32
 @pytest.mark.lan8720
 @pytest.mark.parametrize('config', [
-    'lan8720',
+    'default_lan8720',
 ], indirect=True)
 @pytest.mark.flaky(reruns=3, reruns_delay=5)
 def test_esp_eth_lan8720(dut: Dut) -> None:
-    actual_test(dut)
+    ethernet_l2_test(dut)

+ 8 - 0
components/esp_eth/test_apps/sdkconfig.ci.default_dm9051

@@ -0,0 +1,8 @@
+CONFIG_IDF_TARGET="esp32"
+
+CONFIG_UNITY_ENABLE_FIXTURE=y
+CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
+CONFIG_ESP_TASK_WDT=n
+
+CONFIG_TARGET_USE_SPI_ETHERNET=y
+CONFIG_TARGET_ETH_PHY_DEVICE_DM9051=y

+ 9 - 0
components/esp_eth/test_apps/sdkconfig.ci.default_dp83848

@@ -0,0 +1,9 @@
+CONFIG_IDF_TARGET="esp32"
+
+CONFIG_UNITY_ENABLE_FIXTURE=y
+CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
+CONFIG_ETH_USE_ESP32_EMAC=y
+CONFIG_ESP_TASK_WDT=n
+
+CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
+CONFIG_TARGET_ETH_PHY_DEVICE_DP83848=y

+ 2 - 0
components/esp_eth/test_apps/sdkconfig.ci.ip101 → components/esp_eth/test_apps/sdkconfig.ci.default_ip101

@@ -1,7 +1,9 @@
 CONFIG_IDF_TARGET="esp32"
+
 CONFIG_UNITY_ENABLE_FIXTURE=y
 CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
 CONFIG_ETH_USE_ESP32_EMAC=y
 CONFIG_ESP_TASK_WDT=n
 
+CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
 CONFIG_TARGET_ETH_PHY_DEVICE_IP101=y

+ 11 - 0
components/esp_eth/test_apps/sdkconfig.ci.default_ksz80xx

@@ -0,0 +1,11 @@
+CONFIG_IDF_TARGET="esp32"
+
+CONFIG_UNITY_ENABLE_FIXTURE=y
+CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
+CONFIG_ETH_USE_ESP32_EMAC=y
+CONFIG_ESP_TASK_WDT=n
+
+CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
+CONFIG_TARGET_ETH_PHY_DEVICE_KSZ80XX=y
+CONFIG_ETH_RMII_CLK_OUTPUT=y
+CONFIG_ETH_RMII_CLK_OUT_GPIO=17

+ 8 - 0
components/esp_eth/test_apps/sdkconfig.ci.default_ksz8851snl

@@ -0,0 +1,8 @@
+CONFIG_IDF_TARGET="esp32"
+
+CONFIG_UNITY_ENABLE_FIXTURE=y
+CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
+CONFIG_ESP_TASK_WDT=n
+
+CONFIG_TARGET_USE_SPI_ETHERNET=y
+CONFIG_TARGET_ETH_PHY_DEVICE_KSZ8851SNL=y

+ 2 - 0
components/esp_eth/test_apps/sdkconfig.ci.lan8720 → components/esp_eth/test_apps/sdkconfig.ci.default_lan8720

@@ -1,9 +1,11 @@
 CONFIG_IDF_TARGET="esp32"
+
 CONFIG_UNITY_ENABLE_FIXTURE=y
 CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
 CONFIG_ETH_USE_ESP32_EMAC=y
 CONFIG_ESP_TASK_WDT=n
 
+CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
 CONFIG_TARGET_ETH_PHY_DEVICE_LAN87XX=y
 CONFIG_ETH_RMII_CLK_OUTPUT=y
 CONFIG_ETH_RMII_CLK_OUT_GPIO=17

+ 9 - 0
components/esp_eth/test_apps/sdkconfig.ci.default_rtl8201

@@ -0,0 +1,9 @@
+CONFIG_IDF_TARGET="esp32"
+
+CONFIG_UNITY_ENABLE_FIXTURE=y
+CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
+CONFIG_ETH_USE_ESP32_EMAC=y
+CONFIG_ESP_TASK_WDT=n
+
+CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
+CONFIG_TARGET_ETH_PHY_DEVICE_RTL8201=y

+ 8 - 0
components/esp_eth/test_apps/sdkconfig.ci.default_w5500

@@ -0,0 +1,8 @@
+CONFIG_IDF_TARGET="esp32"
+
+CONFIG_UNITY_ENABLE_FIXTURE=y
+CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
+CONFIG_ESP_TASK_WDT=n
+
+CONFIG_TARGET_USE_SPI_ETHERNET=y
+CONFIG_TARGET_ETH_PHY_DEVICE_W5500=y

+ 12 - 0
components/esp_eth/test_apps/sdkconfig.ci.release_ip101

@@ -0,0 +1,12 @@
+CONFIG_IDF_TARGET="esp32"
+CONFIG_COMPILER_OPTIMIZATION_SIZE=y
+CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
+CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
+
+CONFIG_UNITY_ENABLE_FIXTURE=y
+CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
+CONFIG_ETH_USE_ESP32_EMAC=y
+CONFIG_ESP_TASK_WDT=n
+
+CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
+CONFIG_TARGET_ETH_PHY_DEVICE_IP101=y

+ 13 - 0
components/esp_eth/test_apps/sdkconfig.ci.single_core_ip101

@@ -0,0 +1,13 @@
+CONFIG_IDF_TARGET="esp32"
+CONFIG_MEMMAP_SMP=n
+CONFIG_FREERTOS_UNICORE=y
+CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY=y
+CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y
+
+CONFIG_UNITY_ENABLE_FIXTURE=y
+CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
+CONFIG_ETH_USE_ESP32_EMAC=y
+CONFIG_ESP_TASK_WDT=n
+
+CONFIG_TARGET_USE_INTERNAL_ETHERNET=y
+CONFIG_TARGET_ETH_PHY_DEVICE_IP101=y

+ 7 - 1
components/esp_netif/.build-test-rules.yml

@@ -1,6 +1,6 @@
 # Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
 
-components/esp_netif/test_apps:
+components/esp_netif/test_apps/test_app_esp_netif:
   disable:
     - if: IDF_TARGET == "esp32c6"
       temporary: true
@@ -9,3 +9,9 @@ components/esp_netif/test_apps:
     - if: IDF_TARGET != "esp32s2"
       temporary: true
       reason: lack of runners
+
+components/esp_netif/test_apps/test_app_vfs_l2tap:
+  disable:
+    - if: SOC_EMAC_SUPPORTED != 1
+      temporary: false
+      reason: test uses internal EMAC

+ 0 - 0
components/esp_netif/test_apps/CMakeLists.txt → components/esp_netif/test_apps/test_app_esp_netif/CMakeLists.txt


+ 0 - 0
components/esp_netif/test_apps/README.md → components/esp_netif/test_apps/test_app_esp_netif/README.md


+ 0 - 0
components/esp_netif/test_apps/main/CMakeLists.txt → components/esp_netif/test_apps/test_app_esp_netif/main/CMakeLists.txt


+ 5 - 0
components/esp_netif/test_apps/main/esp_netif_test.c → components/esp_netif/test_apps/test_app_esp_netif/main/esp_netif_test.c

@@ -1,3 +1,8 @@
+/*
+ * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
 #include <stdio.h>
 #include <string.h>
 #include "unity.h"

+ 0 - 0
components/esp_netif/test_apps/pytest_esp_netif.py → components/esp_netif/test_apps/test_app_esp_netif/pytest_esp_netif.py


+ 0 - 0
components/esp_netif/test_apps/sdkconfig.defaults → components/esp_netif/test_apps/test_app_esp_netif/sdkconfig.defaults


+ 10 - 0
components/esp_netif/test_apps/test_app_vfs_l2tap/CMakeLists.txt

@@ -0,0 +1,10 @@
+# This is the project CMakeLists.txt file for the test subproject
+cmake_minimum_required(VERSION 3.16)
+
+set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(esp_vfs_l2tap_test)
+
+idf_component_get_property(lib esp_netif COMPONENT_LIB)
+target_compile_options(${lib} PRIVATE "-fsanitize=undefined" "-fno-sanitize=shift-base")

+ 2 - 0
components/esp_netif/test_apps/test_app_vfs_l2tap/README.md

@@ -0,0 +1,2 @@
+| Supported Targets | ESP32 |
+| ----------------- | ----- |

+ 3 - 0
components/esp_netif/test_apps/test_app_vfs_l2tap/main/CMakeLists.txt

@@ -0,0 +1,3 @@
+idf_component_register(SRC_DIRS "."
+                    PRIV_INCLUDE_DIRS "."
+                    PRIV_REQUIRES cmock test_utils esp_netif driver esp_eth)

+ 17 - 14
components/esp_netif/test/test_vfs_l2tap.c → components/esp_netif/test_apps/test_app_vfs_l2tap/main/test_vfs_l2tap.c

@@ -11,6 +11,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 #include <errno.h>
+#include <inttypes.h>
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
 #include "freertos/event_groups.h"
@@ -29,8 +30,6 @@
 
 #include "esp_vfs_l2tap.h"
 
-#if CONFIG_ESP_NETIF_L2_TAP
-
 #define ETH_FILTER_LE 0x7A05
 #define ETH_FILTER_BE 0x057A
 
@@ -240,7 +239,7 @@ static void send_task(void *task_param)
  * @brief Verifies vfs register/unregister functions
  *
  */
-TEST_CASE("esp32 l2tap - vfs register", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("esp32 l2tap - vfs register", "[ethernet]")
 {
     int eth_tap_fd;
 
@@ -344,7 +343,7 @@ static void close_task(void *task_param)
     vTaskDelete(NULL);
 }
 
-TEST_CASE("esp32 l2tap - open/close", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("esp32 l2tap - open/close", "[ethernet]")
 {
     test_vfs_eth_network_t eth_network_hndls;
 
@@ -415,7 +414,7 @@ TEST_CASE("esp32 l2tap - open/close", "[ethernet][test_env=UT_T2_Ethernet]")
  * @brief Verifies that read does not block when fd is opened in non-blocking mode
  *
  */
-TEST_CASE("esp32 l2tap - non blocking read", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("esp32 l2tap - non blocking read", "[ethernet]")
 {
     test_vfs_eth_network_t eth_network_hndls;
     int eth_tap_fd;
@@ -536,7 +535,7 @@ TEST_CASE("esp32 l2tap - non blocking read", "[ethernet][test_env=UT_T2_Ethernet
  * @brief Verifies that read blocks when fd opened in blocking mode
  *
  */
-TEST_CASE("esp32 l2tap - blocking read", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("esp32 l2tap - blocking read", "[ethernet]")
 {
     test_vfs_eth_network_t eth_network_hndls;
     int eth_tap_fd;
@@ -599,7 +598,7 @@ TEST_CASE("esp32 l2tap - blocking read", "[ethernet][test_env=UT_T2_Ethernet]")
  * @brief Verifies write
  *
  */
-TEST_CASE("esp32 l2tap - write", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("esp32 l2tap - write", "[ethernet]")
 {
     test_vfs_eth_network_t eth_network_hndls;
     int eth_tap_fd;
@@ -718,12 +717,12 @@ static void multi_fds_task (void *task_param)
     for (int i = 0; i < sizeof(eth_tap_fds) / sizeof(int); i++) {
         TEST_ASSERT_EQUAL(0, close(eth_tap_fds[i]));
     }
-    ESP_LOGI(TAG, "multi_fds_task %u done", task_info->task_id);
+    ESP_LOGI(TAG, "multi_fds_task %" PRIu16 "done", task_info->task_id);
     xSemaphoreGive(task_info->semaphore);
     vTaskDelete(NULL);
 }
 
-TEST_CASE("esp32 l2tap - read/write multiple fd's used by multiple tasks", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("esp32 l2tap - read/write multiple fd's used by multiple tasks", "[ethernet]")
 {
     test_vfs_eth_network_t eth_network_hndls;
 
@@ -752,7 +751,7 @@ TEST_CASE("esp32 l2tap - read/write multiple fd's used by multiple tasks", "[eth
  * @brief Verifies proper functionality of ioctl RCV_FILTER option
  *
  */
-TEST_CASE("esp32 l2tap - ioctl - RCV_FILTER", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("esp32 l2tap - ioctl - RCV_FILTER", "[ethernet]")
 {
     test_vfs_eth_network_t eth_network_hndls;
     int eth_tap_fd;
@@ -849,7 +848,7 @@ TEST_CASE("esp32 l2tap - ioctl - RCV_FILTER", "[ethernet][test_env=UT_T2_Etherne
  * @brief Verifies proper functionality of ioctl INTF_DEVICE option
  *
  */
-TEST_CASE("esp32 l2tap - ioctl - INTF_DEVICE/DEVICE_DRV_HNDL", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("esp32 l2tap - ioctl - INTF_DEVICE/DEVICE_DRV_HNDL", "[ethernet]")
 {
     test_vfs_eth_network_t eth_network_hndls;
     int eth_tap_fd;
@@ -939,7 +938,7 @@ TEST_CASE("esp32 l2tap - ioctl - INTF_DEVICE/DEVICE_DRV_HNDL", "[ethernet][test_
  * @brief Verifies proper functionality of ioctl unknown option
  *
  */
-TEST_CASE("esp32 l2tap - ioctl - unknown", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("esp32 l2tap - ioctl - unknown", "[ethernet]")
 {
     test_vfs_eth_network_t eth_network_hndls;
     int eth_tap_fd;
@@ -964,7 +963,7 @@ TEST_CASE("esp32 l2tap - ioctl - unknown", "[ethernet][test_env=UT_T2_Ethernet]"
  * @brief Verifies proper functionality of fcntl
  *
  */
-TEST_CASE("esp32 l2tap - fcntl", "[ethernet][test_env=UT_T2_Ethernet]")
+TEST_CASE("esp32 l2tap - fcntl", "[ethernet]")
 {
     test_vfs_eth_network_t eth_network_hndls;
     int eth_tap_fd;
@@ -1064,4 +1063,8 @@ TEST_CASE("esp32 l2tap - fcntl", "[ethernet][test_env=UT_T2_Ethernet]")
     TEST_ASSERT_EQUAL(ESP_OK, esp_vfs_l2tap_intf_unregister(NULL));
     ethernet_deinit(&eth_network_hndls);
 }
-#endif // CONFIG_ESP_NETIF_L2_TAP
+
+void app_main(void)
+{
+    unity_run_menu();
+}

+ 15 - 0
components/esp_netif/test_apps/test_app_vfs_l2tap/pytest_esp_vfs_l2tap.py

@@ -0,0 +1,15 @@
+# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: CC0-1.0
+
+import pytest
+from pytest_embedded import Dut
+
+
+@pytest.mark.esp32
+@pytest.mark.ethernet
+def test_esp_netif(dut: Dut) -> None:
+    dut.expect_exact('Press ENTER to see the list of tests')
+    dut.write('\n')
+    dut.expect_exact('Enter test for running.')
+    dut.write('*')
+    dut.expect_unity_test_output()

+ 4 - 0
components/esp_netif/test_apps/test_app_vfs_l2tap/sdkconfig.defaults

@@ -0,0 +1,4 @@
+CONFIG_UNITY_ENABLE_FIXTURE=y
+CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y
+
+CONFIG_ESP_NETIF_L2_TAP=y

+ 2 - 2
examples/ethernet/basic/components/ethernet_init/ethernet_init.c

@@ -255,12 +255,12 @@ esp_err_t example_eth_init(esp_eth_handle_t *eth_handles_out[], uint8_t *eth_cnt
     // Note that Locally Administered OUI range should be used only when testing on a LAN under your control!
     uint8_t base_mac_addr[ETH_ADDR_LEN];
     ESP_GOTO_ON_ERROR(esp_efuse_mac_get_default(base_mac_addr), err, TAG, "get EFUSE MAC failed");
-    u_int8_t local_mac_1[ETH_ADDR_LEN];
+    uint8_t local_mac_1[ETH_ADDR_LEN];
     esp_derive_local_mac(local_mac_1, base_mac_addr);
     spi_eth_module_config[0].mac_addr = local_mac_1;
 #if CONFIG_EXAMPLE_SPI_ETHERNETS_NUM > 1
     INIT_SPI_ETH_MODULE_CONFIG(spi_eth_module_config, 1);
-    u_int8_t local_mac_2[ETH_ADDR_LEN];
+    uint8_t local_mac_2[ETH_ADDR_LEN];
     base_mac_addr[ETH_ADDR_LEN - 1] += 1;
     esp_derive_local_mac(local_mac_2, base_mac_addr);
     spi_eth_module_config[1].mac_addr = local_mac_2;

+ 0 - 3
tools/unit-test-app/configs/netif_l2tap

@@ -1,3 +0,0 @@
-CONFIG_IDF_TARGET="esp32"
-TEST_COMPONENTS=esp_netif
-CONFIG_ESP_NETIF_L2_TAP=y