esp_eth_test_l2.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /*
  2. * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Unlicense OR CC0-1.0
  5. */
  6. #include <string.h>
  7. #include <inttypes.h>
  8. #include "freertos/FreeRTOS.h"
  9. #include "freertos/task.h"
  10. #include "freertos/event_groups.h"
  11. #include "esp_eth_test_common.h"
  12. #include "arpa/inet.h" // for ntohs, etc.
  13. #include "esp_log.h"
  14. #define TEST_ETH_TYPE 0x3300
  15. #define TEST_CTRL_ETH_TYPE (TEST_ETH_TYPE + 1)
  16. #define WAIT_AFTER_CONN_MS 2500
  17. #define WAIT_AFTER_CONN_TMO_MS 20000
  18. #define ETH_BROADCAST_RECV_BIT BIT(0)
  19. #define ETH_MULTICAST_RECV_BIT BIT(1)
  20. #define ETH_UNICAST_RECV_BIT BIT(2)
  21. #define ETH_POKE_RESP_RECV_BIT BIT(3)
  22. #define POKE_REQ 0xFA
  23. #define POKE_RESP 0xFB
  24. #define DUMMY_TRAFFIC 0xFF
  25. typedef struct
  26. {
  27. EventGroupHandle_t eth_event_group;
  28. uint8_t dst_mac_addr[ETH_ADDR_LEN];
  29. int unicast_rx_cnt;
  30. int multicast_rx_cnt;
  31. int brdcast_rx_cnt;
  32. bool check_rx_data;
  33. } recv_info_t;
  34. esp_err_t l2_packet_txrx_test_cb(esp_eth_handle_t hdl, uint8_t *buffer, uint32_t length, void *priv) {
  35. recv_info_t *recv_info = (recv_info_t*)priv;
  36. EventGroupHandle_t eth_event_group = recv_info->eth_event_group;
  37. emac_frame_t *pkt = (emac_frame_t *)buffer;
  38. // check header
  39. if (pkt->proto == ntohs(TEST_ETH_TYPE)) { // data packet
  40. uint8_t local_mac_addr[ETH_ADDR_LEN];
  41. esp_eth_ioctl(hdl, ETH_CMD_G_MAC_ADDR, local_mac_addr);
  42. // check data content
  43. if (recv_info->check_rx_data) {
  44. if (length == 1024) {
  45. for (int i = 0; i < (length - ETH_HEADER_LEN); ++i) {
  46. if (pkt->data[i] != (i & 0xff)) {
  47. printf("payload mismatch\n");
  48. free(buffer);
  49. return ESP_OK;
  50. }
  51. }
  52. }
  53. }
  54. if (memcmp(pkt->dest, "\xff\xff\xff\xff\xff\xff", ETH_ADDR_LEN) == 0) {
  55. recv_info->brdcast_rx_cnt++;
  56. xEventGroupSetBits(eth_event_group, ETH_BROADCAST_RECV_BIT);
  57. } else if (pkt->dest[0] & 0x1) {
  58. recv_info->multicast_rx_cnt++;
  59. xEventGroupSetBits(eth_event_group, ETH_MULTICAST_RECV_BIT);
  60. } else if (memcmp(pkt->dest, local_mac_addr, ETH_ADDR_LEN) == 0) {
  61. recv_info->unicast_rx_cnt++;
  62. xEventGroupSetBits(eth_event_group, ETH_UNICAST_RECV_BIT);
  63. }
  64. } else if (ntohs(pkt->proto) == TEST_CTRL_ETH_TYPE) { // control packet
  65. if (pkt->data[0] == POKE_RESP) {
  66. memcpy(recv_info->dst_mac_addr, pkt->dest, ETH_ADDR_LEN);
  67. printf("Poke response received\n");
  68. xEventGroupSetBits(eth_event_group, ETH_POKE_RESP_RECV_BIT);
  69. }
  70. }
  71. free(buffer);
  72. return ESP_OK;
  73. }
  74. /**
  75. * @brief The function sends a "POKE" request message over the Ethernet and waits until the test script sends a reply.
  76. * Multiple "POKE" attempts are issued when timeout for the reply expires.
  77. * This function is used to drive the test flow and to ensure that data path between the test points
  78. * has been established. I.e. if DUT is connected in network with a switch, even if link is indicated up,
  79. * it may take some time the switch starts forwarding the associated port (e.g. it runs RSTP at first).
  80. */
  81. void poke_and_wait(esp_eth_handle_t eth_handle, void *data, uint16_t size, EventGroupHandle_t eth_event_group)
  82. {
  83. // create a control frame to control test flow between the UT and the Python test script
  84. emac_frame_t *ctrl_pkt = calloc(1, 60);
  85. ctrl_pkt->proto = htons(TEST_CTRL_ETH_TYPE);
  86. memset(ctrl_pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr
  87. esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, ctrl_pkt->src);
  88. ctrl_pkt->data[0] = POKE_REQ;
  89. if (data != NULL && size > 0) {
  90. memcpy(&ctrl_pkt->data[1], data, size);
  91. }
  92. uint32_t tmo;
  93. uint32_t i;
  94. for(tmo = 0, i = 1; tmo < WAIT_AFTER_CONN_TMO_MS; tmo += WAIT_AFTER_CONN_MS, i++) {
  95. printf("Poke attempt #%" PRIu32 "\n", i);
  96. TEST_ESP_OK(esp_eth_transmit(eth_handle, ctrl_pkt, 60));
  97. EventBits_t bits = xEventGroupWaitBits(eth_event_group, ETH_POKE_RESP_RECV_BIT,
  98. true, true, pdMS_TO_TICKS(WAIT_AFTER_CONN_MS));
  99. if ((bits & ETH_POKE_RESP_RECV_BIT) == ETH_POKE_RESP_RECV_BIT) {
  100. break;
  101. }
  102. }
  103. TEST_ASSERT(tmo < WAIT_AFTER_CONN_TMO_MS);
  104. free(ctrl_pkt);
  105. }
  106. TEST_CASE("ethernet broadcast transmit", "[ethernet_l2]")
  107. {
  108. esp_eth_mac_t *mac = mac_init(NULL, NULL);
  109. TEST_ASSERT_NOT_NULL(mac);
  110. esp_eth_phy_t *phy = phy_init(NULL);
  111. TEST_ASSERT_NOT_NULL(phy);
  112. esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
  113. esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
  114. TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
  115. TEST_ASSERT_NOT_NULL(eth_handle);
  116. extra_eth_config(eth_handle);
  117. TEST_ESP_OK(esp_event_loop_create_default());
  118. EventGroupHandle_t eth_event_state_group = xEventGroupCreate();
  119. TEST_ASSERT(eth_event_state_group != NULL);
  120. TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_state_group));
  121. EventGroupHandle_t eth_event_rx_group = xEventGroupCreate();
  122. TEST_ASSERT(eth_event_rx_group != NULL);
  123. recv_info_t recv_info = {
  124. .eth_event_group = eth_event_rx_group,
  125. .check_rx_data = false,
  126. .unicast_rx_cnt = 0,
  127. .multicast_rx_cnt = 0,
  128. .brdcast_rx_cnt = 0
  129. };
  130. uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
  131. TEST_ESP_OK(mac->get_addr(mac, local_mac_addr));
  132. // test app will parse the DUT MAC from this line of log output
  133. printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
  134. local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
  135. TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &recv_info));
  136. TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
  137. EventBits_t bits = 0;
  138. bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(3000));
  139. TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
  140. // if DUT is connected in network with switch: even if link is indicated up, it may take some time the switch
  141. // starts switching the associated port (e.g. it runs RSTP at first)
  142. poke_and_wait(eth_handle, NULL, 0, eth_event_rx_group);
  143. emac_frame_t *pkt = malloc(1024);
  144. pkt->proto = htons(TEST_ETH_TYPE);
  145. TEST_ESP_OK(esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, pkt->src));
  146. memset(pkt->dest, 0xff, ETH_ADDR_LEN); // broadcast addr
  147. for (int i = 0; i < (1024 - ETH_HEADER_LEN); ++i){
  148. pkt->data[i] = i & 0xff;
  149. }
  150. TEST_ESP_OK(esp_eth_transmit(eth_handle, pkt, 1024));
  151. // give it some time to complete transmit
  152. vTaskDelay(pdMS_TO_TICKS(500));
  153. free(pkt);
  154. TEST_ESP_OK(esp_eth_stop(eth_handle));
  155. TEST_ESP_OK(esp_event_loop_delete_default());
  156. TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
  157. phy->del(phy);
  158. mac->del(mac);
  159. extra_cleanup();
  160. vEventGroupDelete(eth_event_rx_group);
  161. vEventGroupDelete(eth_event_state_group);
  162. }
  163. TEST_CASE("ethernet recv_pkt", "[ethernet_l2]")
  164. {
  165. esp_eth_mac_t *mac = mac_init(NULL, NULL);
  166. TEST_ASSERT_NOT_NULL(mac);
  167. esp_eth_phy_t *phy = phy_init(NULL);
  168. TEST_ASSERT_NOT_NULL(phy);
  169. esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
  170. esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
  171. TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
  172. TEST_ASSERT_NOT_NULL(eth_handle);
  173. extra_eth_config(eth_handle);
  174. TEST_ESP_OK(esp_event_loop_create_default());
  175. EventGroupHandle_t eth_event_state_group = xEventGroupCreate();
  176. TEST_ASSERT(eth_event_state_group != NULL);
  177. TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_state_group));
  178. EventGroupHandle_t eth_event_rx_group = xEventGroupCreate();
  179. TEST_ASSERT(eth_event_rx_group != NULL);
  180. recv_info_t recv_info = {
  181. .eth_event_group = eth_event_rx_group,
  182. .check_rx_data = true,
  183. .unicast_rx_cnt = 0,
  184. .multicast_rx_cnt = 0,
  185. .brdcast_rx_cnt = 0
  186. };
  187. uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
  188. TEST_ESP_OK(mac->get_addr(mac, local_mac_addr));
  189. // test app will parse the DUT MAC from this line of log output
  190. printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
  191. local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
  192. TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &recv_info));
  193. TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
  194. EventBits_t bits = 0;
  195. bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(3000));
  196. TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
  197. // if DUT is connected in network with switch: even if link is indicated up, it may take some time the switch
  198. // starts switching the associated port (e.g. it runs RSTP at first)
  199. poke_and_wait(eth_handle, NULL, 0, eth_event_rx_group);
  200. bits = 0;
  201. bits = xEventGroupWaitBits(eth_event_rx_group, ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT,
  202. true, true, pdMS_TO_TICKS(5000));
  203. printf("bits = 0x%" PRIu32 "\n", (uint32_t)bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
  204. TEST_ASSERT((bits & (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT)) ==
  205. (ETH_BROADCAST_RECV_BIT | ETH_MULTICAST_RECV_BIT | ETH_UNICAST_RECV_BIT));
  206. TEST_ESP_OK(esp_eth_stop(eth_handle));
  207. TEST_ESP_OK(esp_event_loop_delete_default());
  208. TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
  209. phy->del(phy);
  210. mac->del(mac);
  211. extra_cleanup();
  212. vEventGroupDelete(eth_event_state_group);
  213. vEventGroupDelete(eth_event_rx_group);
  214. }
  215. TEST_CASE("ethernet start/stop stress test under heavy traffic", "[ethernet_l2]")
  216. {
  217. // *** SPI Ethernet modules deviation ***
  218. // Rationale: The SPI bus is bottleneck when reading received frames from the module. The Rx Task would
  219. // occupy all the resources under heavy Rx traffic and it would not be possible to access
  220. // the Ethernet module to stop it. Therfore, the Rx task priority is set lower than "test" task
  221. // to be able to be preempted.
  222. #if CONFIG_TARGET_USE_SPI_ETHERNET
  223. eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
  224. mac_config.rx_task_prio = uxTaskPriorityGet(NULL) - 1;
  225. esp_eth_mac_t *mac = mac_init(NULL, &mac_config);
  226. #else
  227. esp_eth_mac_t *mac = mac_init(NULL, NULL);
  228. #endif // CONFIG_TARGET_USE_SPI_ETHERNET
  229. TEST_ASSERT_NOT_NULL(mac);
  230. esp_eth_phy_t *phy = phy_init(NULL);
  231. TEST_ASSERT_NOT_NULL(phy);
  232. esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy); // apply default driver configuration
  233. esp_eth_handle_t eth_handle = NULL; // after driver installed, we will get the handle of the driver
  234. TEST_ESP_OK(esp_eth_driver_install(&config, &eth_handle)); // install driver
  235. TEST_ASSERT_NOT_NULL(eth_handle);
  236. extra_eth_config(eth_handle);
  237. TEST_ESP_OK(esp_event_loop_create_default());
  238. EventBits_t bits = 0;
  239. EventGroupHandle_t eth_event_state_group = xEventGroupCreate();
  240. TEST_ASSERT(eth_event_state_group != NULL);
  241. TEST_ESP_OK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, eth_event_state_group));
  242. EventGroupHandle_t eth_event_rx_group = xEventGroupCreate();
  243. TEST_ASSERT(eth_event_rx_group != NULL);
  244. recv_info_t recv_info = {
  245. .eth_event_group = eth_event_rx_group,
  246. .check_rx_data = false,
  247. .unicast_rx_cnt = 0,
  248. .multicast_rx_cnt = 0,
  249. .brdcast_rx_cnt = 0
  250. };
  251. uint8_t local_mac_addr[ETH_ADDR_LEN] = {};
  252. TEST_ESP_OK(mac->get_addr(mac, local_mac_addr));
  253. // test app will parse the DUT MAC from this line of log output
  254. printf("DUT MAC: %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", local_mac_addr[0], local_mac_addr[1], local_mac_addr[2],
  255. local_mac_addr[3], local_mac_addr[4], local_mac_addr[5]);
  256. TEST_ESP_OK(esp_eth_update_input_path(eth_handle, l2_packet_txrx_test_cb, &recv_info));
  257. // create dummy data packet used for traffic generation
  258. emac_frame_t *pkt = calloc(1, 1500);
  259. pkt->proto = htons(TEST_ETH_TYPE);
  260. memcpy(pkt->dest, recv_info.dst_mac_addr, ETH_ADDR_LEN);
  261. memcpy(pkt->src, local_mac_addr, ETH_ADDR_LEN);
  262. printf("EMAC start/stop stress test under heavy Tx traffic\n");
  263. for (int tx_i = 0; tx_i < 10; tx_i++) {
  264. printf("Tx Test iteration %d\n", tx_i);
  265. TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
  266. bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(3000));
  267. TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
  268. // at first, check that Tx/Rx path works as expected by poking the test script
  269. // this also serves as main PASS/FAIL criteria
  270. poke_and_wait(eth_handle, &tx_i, sizeof(tx_i), eth_event_rx_group);
  271. // *** SPI Ethernet modules deviation ***
  272. // Rationale: Transmit errors are expected only for internal EMAC since it is possible to try to queue more
  273. // data than it is able to process at a time.
  274. #if !CONFIG_TARGET_USE_SPI_ETHERNET
  275. printf("Note: transmit errors are expected...\n");
  276. #endif
  277. // generate heavy Tx traffic
  278. for (int j = 0; j < 150; j++) {
  279. // return value is not checked on purpose since it is expected that it may fail time to time because
  280. // we may try to queue more packets than hardware is able to handle
  281. pkt->data[2] = j & 0xFF; // sequence number
  282. esp_eth_transmit(eth_handle, pkt, 1500);
  283. }
  284. TEST_ESP_OK(esp_eth_stop(eth_handle));
  285. bits = xEventGroupWaitBits(eth_event_state_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(3000));
  286. TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
  287. printf("Ethernet stopped\n");
  288. }
  289. printf("EMAC start/stop stress test under heavy Rx traffic\n");
  290. for (int rx_i = 0; rx_i < 10; rx_i++) {
  291. printf("Rx Test iteration %d\n", rx_i);
  292. TEST_ESP_OK(esp_eth_start(eth_handle)); // start Ethernet driver state machine
  293. bits = xEventGroupWaitBits(eth_event_state_group, ETH_CONNECT_BIT, true, true, pdMS_TO_TICKS(3000));
  294. TEST_ASSERT((bits & ETH_CONNECT_BIT) == ETH_CONNECT_BIT);
  295. poke_and_wait(eth_handle, &rx_i, sizeof(rx_i), eth_event_rx_group);
  296. // wait for dummy traffic
  297. xEventGroupClearBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT);
  298. recv_info.unicast_rx_cnt = 0;
  299. bits = xEventGroupWaitBits(eth_event_rx_group, ETH_UNICAST_RECV_BIT, true, true, pdMS_TO_TICKS(3000));
  300. TEST_ASSERT((bits & ETH_UNICAST_RECV_BIT) == ETH_UNICAST_RECV_BIT);
  301. vTaskDelay(pdMS_TO_TICKS(500));
  302. TEST_ESP_OK(esp_eth_stop(eth_handle));
  303. bits = xEventGroupWaitBits(eth_event_state_group, ETH_STOP_BIT, true, true, pdMS_TO_TICKS(3000));
  304. TEST_ASSERT((bits & ETH_STOP_BIT) == ETH_STOP_BIT);
  305. printf("Recv packets: %d\n", recv_info.unicast_rx_cnt);
  306. TEST_ASSERT_GREATER_THAN_INT32(0, recv_info.unicast_rx_cnt);
  307. printf("Ethernet stopped\n");
  308. }
  309. free(pkt);
  310. // Add an extra delay to be sure that there is no traffic generated by the test script during the driver un-installation.
  311. // It was observed unintended behavior of the switch used in test environment when link is set down under heavy load.
  312. vTaskDelay(pdMS_TO_TICKS(500));
  313. TEST_ESP_OK(esp_event_handler_unregister(ETH_EVENT, ESP_EVENT_ANY_ID, eth_event_handler));
  314. TEST_ESP_OK(esp_event_loop_delete_default());
  315. TEST_ESP_OK(esp_eth_driver_uninstall(eth_handle));
  316. phy->del(phy);
  317. mac->del(mac);
  318. extra_cleanup();
  319. vEventGroupDelete(eth_event_rx_group);
  320. vEventGroupDelete(eth_event_state_group);
  321. }