test_usb_host_async.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /*
  2. * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdio.h>
  7. #include "freertos/FreeRTOS.h"
  8. #include "freertos/task.h"
  9. #include "freertos/semphr.h"
  10. #include "esp_err.h"
  11. #include "esp_intr_alloc.h"
  12. #include "test_usb_common.h"
  13. #include "test_usb_mock_classes.h"
  14. #include "msc_client.h"
  15. #include "ctrl_client.h"
  16. #include "usb/usb_host.h"
  17. #include "unity.h"
  18. #include "test_utils.h"
  19. #define TEST_MSC_NUM_SECTORS_TOTAL 10
  20. #define TEST_MSC_NUM_SECTORS_PER_XFER 2
  21. #define TEST_MSC_SCSI_TAG 0xDEADBEEF
  22. #define TEST_CTRL_NUM_TRANSFERS 30
  23. // --------------------------------------------------- Test Cases ------------------------------------------------------
  24. /*
  25. Test USB Host Asynchronous API single client
  26. Requires: This test requires an MSC SCSI device to be attached (see the MSC mock class)
  27. Purpose:
  28. - Test that USB Host Asynchronous API works correctly with a single client
  29. - Test that a client can be created
  30. - Test that client can operate concurrently in a separate thread
  31. - Test that the main thread is able to detect library events (such as USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS)
  32. Procedure:
  33. - Install USB Host Library
  34. - Create a task to run an MSC client
  35. - Start the MSC client task. It will execute a bunch of MSC SCSI sector reads
  36. - Wait for the host library event handler to report a USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS event
  37. - Free all devices
  38. - Uninstall USB Host Library
  39. */
  40. TEST_CASE("Test USB Host async client (single client)", "[usb_host][ignore]")
  41. {
  42. test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing
  43. //Install USB Host
  44. usb_host_config_t host_config = {
  45. .skip_phy_setup = true, //test_usb_init_phy() will already have setup the internal USB PHY for us
  46. .intr_flags = ESP_INTR_FLAG_LEVEL1,
  47. };
  48. ESP_ERROR_CHECK(usb_host_install(&host_config));
  49. printf("Installed\n");
  50. //Create task to run client that communicates with MSC SCSI interface
  51. msc_client_test_param_t params = {
  52. .num_sectors_to_read = TEST_MSC_NUM_SECTORS_TOTAL,
  53. .num_sectors_per_xfer = TEST_MSC_NUM_SECTORS_PER_XFER,
  54. .msc_scsi_xfer_tag = TEST_MSC_SCSI_TAG,
  55. .idVendor = MOCK_MSC_SCSI_DEV_ID_VENDOR,
  56. .idProduct = MOCK_MSC_SCSI_DEV_ID_PRODUCT,
  57. };
  58. TaskHandle_t task_hdl;
  59. xTaskCreatePinnedToCore(msc_client_async_seq_task, "async", 4096, (void *)&params, 2, &task_hdl, 0);
  60. //Start the task
  61. xTaskNotifyGive(task_hdl);
  62. while (1) {
  63. //Start handling system events
  64. uint32_t event_flags;
  65. usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
  66. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
  67. printf("No more clients\n");
  68. TEST_ASSERT_EQUAL(ESP_ERR_NOT_FINISHED, usb_host_device_free_all());
  69. }
  70. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
  71. break;
  72. }
  73. }
  74. //Short delay to allow task to be cleaned up
  75. vTaskDelay(10);
  76. //Clean up USB Host
  77. ESP_ERROR_CHECK(usb_host_uninstall());
  78. test_usb_deinit_phy(); //Deinitialize the internal USB PHY after testing
  79. }
  80. /*
  81. Test USB Host Asynchronous API with multiple clients
  82. Requires: This test requires an MSC SCSI device to be attached (see the MSC mock class)
  83. Purpose:
  84. - Test the USB Host Asynchronous API works correctly with multiple clients
  85. - Test that multiple clients can be created
  86. - Test that multiple clients can operate concurrently in separate threads
  87. - Test that the main thread is able to detect library events (such as USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS)
  88. Procedure:
  89. - Install USB Host Library
  90. - Create separate tasks to run an MSC client and Ctrl Client
  91. - MSC Client will execute a bunch of MSC SCSI sector reads
  92. - Ctrl Client will execute a bunch of control transfers
  93. - Wait for the host library event handler to report a USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS event
  94. - Free all devices
  95. - Uninstall USB Host Library
  96. */
  97. TEST_CASE("Test USB Host async client (multi client)", "[usb_host][ignore]")
  98. {
  99. test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing
  100. //Install USB Host
  101. usb_host_config_t host_config = {
  102. .skip_phy_setup = true, //test_usb_init_phy() will already have setup the internal USB PHY for us
  103. .intr_flags = ESP_INTR_FLAG_LEVEL1,
  104. };
  105. ESP_ERROR_CHECK(usb_host_install(&host_config));
  106. printf("Installed\n");
  107. //Create task to run the MSC client
  108. msc_client_test_param_t msc_params = {
  109. .num_sectors_to_read = TEST_MSC_NUM_SECTORS_TOTAL,
  110. .num_sectors_per_xfer = TEST_MSC_NUM_SECTORS_PER_XFER,
  111. .msc_scsi_xfer_tag = TEST_MSC_SCSI_TAG,
  112. .idVendor = MOCK_MSC_SCSI_DEV_ID_VENDOR,
  113. .idProduct = MOCK_MSC_SCSI_DEV_ID_PRODUCT,
  114. };
  115. TaskHandle_t msc_task_hdl;
  116. xTaskCreatePinnedToCore(msc_client_async_seq_task, "msc", 4096, (void *)&msc_params, 2, &msc_task_hdl, 0);
  117. //Create task a control transfer client
  118. ctrl_client_test_param_t ctrl_params = {
  119. .num_ctrl_xfer_to_send = TEST_CTRL_NUM_TRANSFERS,
  120. .idVendor = MOCK_MSC_SCSI_DEV_ID_VENDOR,
  121. .idProduct = MOCK_MSC_SCSI_DEV_ID_PRODUCT,
  122. };
  123. TaskHandle_t ctrl_task_hdl;
  124. xTaskCreatePinnedToCore(ctrl_client_async_seq_task, "ctrl", 4096, (void *)&ctrl_params, 2, &ctrl_task_hdl, 0);
  125. //Start both tasks
  126. xTaskNotifyGive(msc_task_hdl);
  127. xTaskNotifyGive(ctrl_task_hdl);
  128. while (1) {
  129. //Start handling system events
  130. uint32_t event_flags;
  131. usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
  132. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
  133. printf("No more clients\n");
  134. TEST_ASSERT_EQUAL(ESP_ERR_NOT_FINISHED, usb_host_device_free_all());
  135. }
  136. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
  137. break;
  138. }
  139. }
  140. //Short delay to allow task to be cleaned up
  141. vTaskDelay(10);
  142. //Clean up USB Host
  143. ESP_ERROR_CHECK(usb_host_uninstall());
  144. test_usb_deinit_phy(); //Deinitialize the internal USB PHY after testing
  145. }
  146. /*
  147. Test USB Host Asynchronous API Usage
  148. Requires: This test requires an MSC SCSI device to be attached (see the MSC mock class)
  149. Purpose:
  150. - Test that incorrect usage of USB Host Asynchronous API will returns errors
  151. Procedure:
  152. - Install USB Host Library
  153. - Register two clients and all event handler functions from the same loop
  154. - Wait for each client to detect device connection
  155. - Check that both clients can open the same device
  156. - Check that a client cannot open a non-existent device
  157. - Check that only one client can claim a particular interface
  158. - Check that a client cannot release an already released interface
  159. - Wait for device disconnection
  160. - Cleanup
  161. */
  162. static uint8_t dev_addr = 0;
  163. typedef enum {
  164. CLIENT_TEST_STAGE_NONE,
  165. CLIENT_TEST_STAGE_CONN,
  166. CLIENT_TEST_STAGE_DCONN,
  167. } client_test_stage_t;
  168. static void test_async_client_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
  169. {
  170. client_test_stage_t *stage = (client_test_stage_t *)arg;
  171. switch (event_msg->event) {
  172. case USB_HOST_CLIENT_EVENT_NEW_DEV:
  173. if (dev_addr == 0) {
  174. dev_addr = event_msg->new_dev.address;
  175. } else {
  176. TEST_ASSERT_EQUAL(dev_addr, event_msg->new_dev.address);
  177. }
  178. *stage = CLIENT_TEST_STAGE_CONN;
  179. break;
  180. case USB_HOST_CLIENT_EVENT_DEV_GONE:
  181. *stage = CLIENT_TEST_STAGE_DCONN;
  182. break;
  183. default:
  184. abort();
  185. break;
  186. }
  187. }
  188. TEST_CASE("Test USB Host async API", "[usb_host][ignore]")
  189. {
  190. test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing
  191. //Install USB Host
  192. usb_host_config_t host_config = {
  193. .skip_phy_setup = true, //test_usb_init_phy() will already have setup the internal USB PHY for us
  194. .intr_flags = ESP_INTR_FLAG_LEVEL1,
  195. };
  196. ESP_ERROR_CHECK(usb_host_install(&host_config));
  197. printf("Installed\n");
  198. //Register two clients
  199. client_test_stage_t client0_stage = CLIENT_TEST_STAGE_NONE;
  200. client_test_stage_t client1_stage = CLIENT_TEST_STAGE_NONE;
  201. usb_host_client_config_t client_config = {
  202. .is_synchronous = false,
  203. .max_num_event_msg = 5,
  204. .async = {
  205. .client_event_callback = test_async_client_cb,
  206. .callback_arg = (void *)&client0_stage,
  207. },
  208. };
  209. usb_host_client_handle_t client0_hdl;
  210. usb_host_client_handle_t client1_hdl;
  211. TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_register(&client_config, &client0_hdl));
  212. client_config.async.callback_arg = (void *)&client1_stage;
  213. TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_register(&client_config, &client1_hdl));
  214. //Wait until the device connects and the clients receive the event
  215. while (!(client0_stage == CLIENT_TEST_STAGE_CONN && client1_stage == CLIENT_TEST_STAGE_CONN)) {
  216. usb_host_lib_handle_events(0, NULL);
  217. usb_host_client_handle_events(client0_hdl, 0);
  218. usb_host_client_handle_events(client1_hdl, 0);
  219. vTaskDelay(10);
  220. }
  221. //Check that both clients can open the device
  222. TEST_ASSERT_NOT_EQUAL(0, dev_addr);
  223. usb_device_handle_t client0_dev_hdl;
  224. usb_device_handle_t client1_dev_hdl;
  225. TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_open(client0_hdl, dev_addr, &client0_dev_hdl));
  226. TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_open(client1_hdl, dev_addr, &client1_dev_hdl));
  227. TEST_ASSERT_EQUAL(client0_dev_hdl, client1_dev_hdl); //Check that its the same device
  228. //Check that a client cannot open a non-existent device
  229. TEST_ASSERT_NOT_EQUAL(ESP_OK, usb_host_device_open(client0_hdl, 0, &client0_dev_hdl));
  230. //Check that the device cannot be opened again by the same client
  231. usb_device_handle_t dummy_dev_hdl;
  232. TEST_ASSERT_NOT_EQUAL(ESP_OK, usb_host_device_open(client0_hdl, dev_addr, &dummy_dev_hdl));
  233. TEST_ASSERT_NOT_EQUAL(ESP_OK, usb_host_device_open(client1_hdl, dev_addr, &dummy_dev_hdl));
  234. //Check that both clients cannot claim the same interface
  235. TEST_ASSERT_EQUAL(ESP_OK, usb_host_interface_claim(client0_hdl, client0_dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER, MOCK_MSC_SCSI_INTF_ALT_SETTING));
  236. TEST_ASSERT_NOT_EQUAL(ESP_OK, usb_host_interface_claim(client1_hdl, client1_dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER, MOCK_MSC_SCSI_INTF_ALT_SETTING));
  237. //Check that client0 cannot claim the same interface multiple times
  238. TEST_ASSERT_NOT_EQUAL(ESP_OK, usb_host_interface_claim(client0_hdl, client0_dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER, MOCK_MSC_SCSI_INTF_ALT_SETTING));
  239. //Check that client0 can release the interface
  240. TEST_ASSERT_EQUAL(ESP_OK, usb_host_interface_release(client0_hdl, client0_dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER));
  241. //Check that client0 cannot release interface it has not claimed
  242. TEST_ASSERT_NOT_EQUAL(ESP_OK, usb_host_interface_release(client0_hdl, client0_dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER));
  243. //Wait until the device disconnects and the clients receive the event
  244. test_usb_set_phy_state(false, 0);
  245. while (!(client0_stage == CLIENT_TEST_STAGE_DCONN && client1_stage == CLIENT_TEST_STAGE_DCONN)) {
  246. usb_host_lib_handle_events(0, NULL);
  247. usb_host_client_handle_events(client0_hdl, 0);
  248. usb_host_client_handle_events(client1_hdl, 0);
  249. vTaskDelay(10);
  250. }
  251. TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_close(client0_hdl, client0_dev_hdl));
  252. TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_close(client1_hdl, client1_dev_hdl));
  253. //Deregister the clients
  254. TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_deregister(client0_hdl));
  255. TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_deregister(client1_hdl));
  256. while (1) {
  257. uint32_t event_flags;
  258. usb_host_lib_handle_events(0, &event_flags);
  259. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
  260. break;
  261. }
  262. vTaskDelay(10);
  263. }
  264. //Cleanup
  265. TEST_ASSERT_EQUAL(ESP_OK, usb_host_uninstall());
  266. test_usb_deinit_phy();
  267. }