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