test_hcd_common.c 14 KB

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