test_hcd_common.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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 <string.h>
  8. #include "freertos/FreeRTOS.h"
  9. #include "freertos/semphr.h"
  10. #include "freertos/task.h"
  11. #include "soc/usb_wrap_struct.h"
  12. #include "esp_intr_alloc.h"
  13. #include "esp_err.h"
  14. #include "esp_attr.h"
  15. #include "esp_rom_gpio.h"
  16. #include "hcd.h"
  17. #include "usb_private.h"
  18. #include "usb/usb_types_ch9.h"
  19. #include "test_hcd_common.h"
  20. #include "test_usb_common.h"
  21. #include "unity.h"
  22. #define PORT_NUM 1
  23. #define EVENT_QUEUE_LEN 5
  24. #define ENUM_ADDR 1 //Device address to use for tests that enumerate the device
  25. #define ENUM_CONFIG 1 //Device configuration number to use for tests that enumerate the device
  26. typedef struct {
  27. hcd_port_handle_t port_hdl;
  28. hcd_port_event_t port_event;
  29. } port_event_msg_t;
  30. typedef struct {
  31. hcd_pipe_handle_t pipe_hdl;
  32. hcd_pipe_event_t pipe_event;
  33. } pipe_event_msg_t;
  34. hcd_port_handle_t port_hdl = NULL;
  35. // ---------------------------------------------------- Private --------------------------------------------------------
  36. /**
  37. * @brief HCD port callback. Registered when initializing an HCD port
  38. *
  39. * @param port_hdl Port handle
  40. * @param port_event Port event that triggered the callback
  41. * @param user_arg User argument
  42. * @param in_isr Whether callback was called in an ISR context
  43. * @return true ISR should yield after this callback returns
  44. * @return false No yield required (non-ISR context calls should always return false)
  45. */
  46. static bool port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port_event, void *user_arg, bool in_isr)
  47. {
  48. //We store the port's queue handle in the port's context variable
  49. void *port_ctx = hcd_port_get_context(port_hdl);
  50. QueueHandle_t port_evt_queue = (QueueHandle_t)port_ctx;
  51. TEST_ASSERT_TRUE(in_isr); //Current HCD implementation should never call a port callback in a task context
  52. port_event_msg_t msg = {
  53. .port_hdl = port_hdl,
  54. .port_event = port_event,
  55. };
  56. BaseType_t xTaskWoken = pdFALSE;
  57. xQueueSendFromISR(port_evt_queue, &msg, &xTaskWoken);
  58. return (xTaskWoken == pdTRUE);
  59. }
  60. /**
  61. * @brief HCD pipe callback. Registered when allocating a HCD pipe
  62. *
  63. * @param pipe_hdl Pipe handle
  64. * @param pipe_event Pipe event that triggered the callback
  65. * @param user_arg User argument
  66. * @param in_isr Whether the callback was called in an ISR context
  67. * @return true ISR should yield after this callback returns
  68. * @return false No yield required (non-ISR context calls should always return false)
  69. */
  70. static bool pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr)
  71. {
  72. QueueHandle_t pipe_evt_queue = (QueueHandle_t)user_arg;
  73. pipe_event_msg_t msg = {
  74. .pipe_hdl = pipe_hdl,
  75. .pipe_event = pipe_event,
  76. };
  77. if (in_isr) {
  78. BaseType_t xTaskWoken = pdFALSE;
  79. xQueueSendFromISR(pipe_evt_queue, &msg, &xTaskWoken);
  80. return (xTaskWoken == pdTRUE);
  81. } else {
  82. xQueueSend(pipe_evt_queue, &msg, portMAX_DELAY);
  83. return false;
  84. }
  85. }
  86. // ------------------------------------------------- HCD Event Test ----------------------------------------------------
  87. void test_hcd_expect_port_event(hcd_port_handle_t port_hdl, hcd_port_event_t expected_event)
  88. {
  89. //Get the port event queue from the port's context variable
  90. QueueHandle_t port_evt_queue = (QueueHandle_t)hcd_port_get_context(port_hdl);
  91. TEST_ASSERT_NOT_NULL(port_evt_queue);
  92. //Wait for port callback to send an event message
  93. port_event_msg_t msg;
  94. BaseType_t ret = xQueueReceive(port_evt_queue, &msg, pdMS_TO_TICKS(5000));
  95. TEST_ASSERT_EQUAL_MESSAGE(pdPASS, ret, "Port event not generated on time");
  96. //Check the contents of that event message
  97. TEST_ASSERT_EQUAL(port_hdl, msg.port_hdl);
  98. TEST_ASSERT_EQUAL_MESSAGE(expected_event, msg.port_event, "Unexpected event");
  99. printf("\t-> Port event\n");
  100. }
  101. void test_hcd_expect_pipe_event(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t expected_event)
  102. {
  103. //Get the pipe's event queue from the pipe's context variable
  104. QueueHandle_t pipe_evt_queue = (QueueHandle_t)hcd_pipe_get_context(pipe_hdl);
  105. TEST_ASSERT_NOT_NULL(pipe_evt_queue);
  106. //Wait for pipe callback to send an event message
  107. pipe_event_msg_t msg;
  108. BaseType_t ret = xQueueReceive(pipe_evt_queue, &msg, pdMS_TO_TICKS(5000));
  109. TEST_ASSERT_EQUAL_MESSAGE(pdPASS, ret, "Pipe event not generated on time");
  110. //Check the contents of that event message
  111. TEST_ASSERT_EQUAL(pipe_hdl, msg.pipe_hdl);
  112. TEST_ASSERT_EQUAL_MESSAGE(expected_event, msg.pipe_event, "Unexpected event");
  113. }
  114. int test_hcd_get_num_port_events(hcd_port_handle_t port_hdl)
  115. {
  116. //Get the port event queue from the port's context variable
  117. QueueHandle_t port_evt_queue = (QueueHandle_t)hcd_port_get_context(port_hdl);
  118. TEST_ASSERT_NOT_NULL(port_evt_queue);
  119. return EVENT_QUEUE_LEN - uxQueueSpacesAvailable(port_evt_queue);
  120. }
  121. int test_hcd_get_num_pipe_events(hcd_pipe_handle_t pipe_hdl)
  122. {
  123. //Get the pipe's event queue from the pipe's context variable
  124. QueueHandle_t pipe_evt_queue = (QueueHandle_t)hcd_pipe_get_context(pipe_hdl);
  125. TEST_ASSERT_NOT_NULL(pipe_evt_queue);
  126. return EVENT_QUEUE_LEN - uxQueueSpacesAvailable(pipe_evt_queue);
  127. }
  128. // ----------------------------------------------- Driver/Port Related -------------------------------------------------
  129. hcd_port_handle_t test_hcd_setup(void)
  130. {
  131. test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing
  132. //Create a queue for port callback to queue up port events
  133. QueueHandle_t port_evt_queue = xQueueCreate(EVENT_QUEUE_LEN, sizeof(port_event_msg_t));
  134. TEST_ASSERT_NOT_NULL(port_evt_queue);
  135. //Install HCD
  136. hcd_config_t hcd_config = {
  137. .intr_flags = ESP_INTR_FLAG_LEVEL1,
  138. };
  139. TEST_ASSERT_EQUAL(ESP_OK, hcd_install(&hcd_config));
  140. //Initialize a port
  141. hcd_port_config_t port_config = {
  142. .fifo_bias = HCD_PORT_FIFO_BIAS_BALANCED,
  143. .callback = port_callback,
  144. .callback_arg = (void *)port_evt_queue,
  145. .context = (void *)port_evt_queue,
  146. };
  147. hcd_port_handle_t port_hdl;
  148. TEST_ASSERT_EQUAL(ESP_OK, hcd_port_init(PORT_NUM, &port_config, &port_hdl));
  149. TEST_ASSERT_NOT_NULL(port_hdl);
  150. TEST_ASSERT_EQUAL(HCD_PORT_STATE_NOT_POWERED, hcd_port_get_state(port_hdl));
  151. test_usb_set_phy_state(false, 0); //Force disconnected state on PHY
  152. return port_hdl;
  153. }
  154. void test_hcd_teardown(hcd_port_handle_t port_hdl)
  155. {
  156. if (!port_hdl) {
  157. return; // In case of setup stage failure, don't run tear-down stage
  158. }
  159. //Get the queue handle from the port's context variable
  160. QueueHandle_t port_evt_queue = (QueueHandle_t)hcd_port_get_context(port_hdl);
  161. TEST_ASSERT_NOT_NULL(port_evt_queue);
  162. //Deinitialize a port
  163. TEST_ASSERT_EQUAL(ESP_OK, hcd_port_deinit(port_hdl));
  164. //Uninstall the HCD
  165. TEST_ASSERT_EQUAL(ESP_OK, hcd_uninstall());
  166. vQueueDelete(port_evt_queue);
  167. test_usb_deinit_phy(); //Deinitialize the internal USB PHY after testing
  168. }
  169. usb_speed_t test_hcd_wait_for_conn(hcd_port_handle_t port_hdl)
  170. {
  171. //Power ON the port
  172. TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_POWER_ON));
  173. TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISCONNECTED, hcd_port_get_state(port_hdl));
  174. //Wait for connection event
  175. printf("Waiting for connection\n");
  176. test_usb_set_phy_state(true, pdMS_TO_TICKS(100)); //Allow for connected state on PHY
  177. test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_CONNECTION);
  178. TEST_ASSERT_EQUAL(HCD_PORT_EVENT_CONNECTION, hcd_port_handle_event(port_hdl));
  179. TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISABLED, hcd_port_get_state(port_hdl));
  180. //Reset newly connected device
  181. printf("Resetting\n");
  182. TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_RESET));
  183. TEST_ASSERT_EQUAL(HCD_PORT_STATE_ENABLED, hcd_port_get_state(port_hdl));
  184. //Get speed of connected
  185. usb_speed_t port_speed;
  186. TEST_ASSERT_EQUAL(ESP_OK, hcd_port_get_speed(port_hdl, &port_speed));
  187. if (port_speed == USB_SPEED_FULL) {
  188. printf("Full speed enabled\n");
  189. } else {
  190. printf("Low speed enabled\n");
  191. }
  192. return port_speed;
  193. }
  194. void test_hcd_wait_for_disconn(hcd_port_handle_t port_hdl, bool already_disabled)
  195. {
  196. if (!already_disabled) {
  197. //Disable the device
  198. printf("Disabling\n");
  199. TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_DISABLE));
  200. TEST_ASSERT_EQUAL(HCD_PORT_STATE_DISABLED, hcd_port_get_state(port_hdl));
  201. }
  202. //Wait for a safe disconnect
  203. printf("Waiting for disconnection\n");
  204. test_usb_set_phy_state(false, pdMS_TO_TICKS(100)); //Force disconnected state on PHY
  205. test_hcd_expect_port_event(port_hdl, HCD_PORT_EVENT_DISCONNECTION);
  206. TEST_ASSERT_EQUAL(HCD_PORT_EVENT_DISCONNECTION, hcd_port_handle_event(port_hdl));
  207. TEST_ASSERT_EQUAL(HCD_PORT_STATE_RECOVERY, hcd_port_get_state(port_hdl));
  208. //Power down the port
  209. TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_POWER_OFF));
  210. TEST_ASSERT_EQUAL(HCD_PORT_STATE_NOT_POWERED, hcd_port_get_state(port_hdl));
  211. }
  212. // ---------------------------------------------- Pipe Setup/Tear-down -------------------------------------------------
  213. hcd_pipe_handle_t test_hcd_pipe_alloc(hcd_port_handle_t port_hdl, const usb_ep_desc_t *ep_desc, uint8_t dev_addr, usb_speed_t dev_speed)
  214. {
  215. //Create a queue for pipe callback to queue up pipe events
  216. QueueHandle_t pipe_evt_queue = xQueueCreate(EVENT_QUEUE_LEN, sizeof(pipe_event_msg_t));
  217. TEST_ASSERT_NOT_NULL(pipe_evt_queue);
  218. printf("Creating pipe\n");
  219. hcd_pipe_config_t pipe_config = {
  220. .callback = pipe_callback,
  221. .callback_arg = (void *)pipe_evt_queue,
  222. .context = (void *)pipe_evt_queue,
  223. .ep_desc = ep_desc,
  224. .dev_addr = dev_addr,
  225. .dev_speed = dev_speed,
  226. };
  227. hcd_pipe_handle_t pipe_hdl;
  228. TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_alloc(port_hdl, &pipe_config, &pipe_hdl));
  229. TEST_ASSERT_NOT_NULL(pipe_hdl);
  230. return pipe_hdl;
  231. }
  232. void test_hcd_pipe_free(hcd_pipe_handle_t pipe_hdl)
  233. {
  234. //Get the pipe's event queue from its context variable
  235. QueueHandle_t pipe_evt_queue = (QueueHandle_t)hcd_pipe_get_context(pipe_hdl);
  236. TEST_ASSERT_NOT_NULL(pipe_evt_queue);
  237. //Free the pipe and queue
  238. TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_free(pipe_hdl));
  239. vQueueDelete(pipe_evt_queue);
  240. }
  241. urb_t *test_hcd_alloc_urb(int num_isoc_packets, size_t data_buffer_size)
  242. {
  243. //Allocate a URB and data buffer
  244. urb_t *urb = heap_caps_calloc(1, sizeof(urb_t) + (num_isoc_packets * sizeof(usb_isoc_packet_desc_t)), MALLOC_CAP_DEFAULT);
  245. uint8_t *data_buffer = heap_caps_malloc(data_buffer_size, MALLOC_CAP_DMA);
  246. TEST_ASSERT_NOT_NULL(urb);
  247. TEST_ASSERT_NOT_NULL(data_buffer);
  248. //Initialize URB and underlying transfer structure. Need to cast to dummy due to const fields
  249. usb_transfer_dummy_t *transfer_dummy = (usb_transfer_dummy_t *)&urb->transfer;
  250. transfer_dummy->data_buffer = data_buffer;
  251. transfer_dummy->num_isoc_packets = num_isoc_packets;
  252. return urb;
  253. }
  254. void test_hcd_free_urb(urb_t *urb)
  255. {
  256. //Free data buffer of the transfer
  257. heap_caps_free(urb->transfer.data_buffer);
  258. //Free the URB
  259. heap_caps_free(urb);
  260. }
  261. uint8_t test_hcd_enum_device(hcd_pipe_handle_t default_pipe)
  262. {
  263. //We need to create a URB for the enumeration control transfers
  264. urb_t *urb = test_hcd_alloc_urb(0, sizeof(usb_setup_packet_t) + 256);
  265. usb_setup_packet_t *setup_pkt = (usb_setup_packet_t *)urb->transfer.data_buffer;
  266. //Get the device descriptor (note that device might only return 8 bytes)
  267. USB_SETUP_PACKET_INIT_GET_DEVICE_DESC(setup_pkt);
  268. urb->transfer.num_bytes = sizeof(usb_setup_packet_t) + sizeof(usb_device_desc_t);
  269. TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(default_pipe, urb));
  270. test_hcd_expect_pipe_event(default_pipe, HCD_PIPE_EVENT_URB_DONE);
  271. TEST_ASSERT_EQUAL(urb, hcd_urb_dequeue(default_pipe));
  272. TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status, "Transfer NOT completed");
  273. //Update the MPS of the default pipe
  274. usb_device_desc_t *device_desc = (usb_device_desc_t *)(urb->transfer.data_buffer + sizeof(usb_setup_packet_t));
  275. TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_update_mps(default_pipe, device_desc->bMaxPacketSize0));
  276. //Send a set address request
  277. USB_SETUP_PACKET_INIT_SET_ADDR(setup_pkt, ENUM_ADDR); //We only support one device for now so use address 1
  278. urb->transfer.num_bytes = sizeof(usb_setup_packet_t);
  279. TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(default_pipe, urb));
  280. test_hcd_expect_pipe_event(default_pipe, HCD_PIPE_EVENT_URB_DONE);
  281. TEST_ASSERT_EQUAL(urb, hcd_urb_dequeue(default_pipe));
  282. TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status, "Transfer NOT completed");
  283. //Update address of default pipe
  284. TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_update_dev_addr(default_pipe, ENUM_ADDR));
  285. //Send a set configuration request
  286. USB_SETUP_PACKET_INIT_SET_CONFIG(setup_pkt, ENUM_CONFIG);
  287. urb->transfer.num_bytes = sizeof(usb_setup_packet_t);
  288. TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(default_pipe, urb));
  289. test_hcd_expect_pipe_event(default_pipe, HCD_PIPE_EVENT_URB_DONE);
  290. TEST_ASSERT_EQUAL(urb, hcd_urb_dequeue(default_pipe));
  291. TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status, "Transfer NOT completed");
  292. //Free URB
  293. test_hcd_free_urb(urb);
  294. return ENUM_ADDR;
  295. }