Skip to content

Commit f4a28bb

Browse files
Merge pull request #335 from espressif/feat/usb_host_exit_suspend_by_port_reset
feat(usb_host): Exit suspend state by port reset
2 parents 0ea7e08 + e48b3d4 commit f4a28bb

File tree

4 files changed

+124
-11
lines changed

4 files changed

+124
-11
lines changed

host/usb/src/hcd_dwc.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,8 +1254,11 @@ static esp_err_t _port_cmd_reset(port_t *port)
12541254
{
12551255
esp_err_t ret;
12561256

1257-
// Port can only a reset when it is in the enabled or disabled (in the case of a new connection)states.
1258-
if (port->state != HCD_PORT_STATE_ENABLED && port->state != HCD_PORT_STATE_DISABLED) {
1257+
// Port can only be reset when it is in the enabled or disabled (in the case of a new connection) states, or
1258+
// in suspended state, to exit suspended state through host initiated reset
1259+
if (port->state != HCD_PORT_STATE_ENABLED &&
1260+
port->state != HCD_PORT_STATE_DISABLED &&
1261+
port->state != HCD_PORT_STATE_SUSPENDED) {
12591262
ret = ESP_ERR_INVALID_STATE;
12601263
goto exit;
12611264
}

host/usb/test/target_test/common/hcd_common.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#define EVENT_QUEUE_LEN 5
4747
#define ENUM_ADDR 1 // Device address to use for tests that enumerate the device
4848
#define ENUM_CONFIG 1 // Device configuration number to use for tests that enumerate the device
49+
#define TRANSFER_MAX_BYTES 256
4950

5051
typedef struct {
5152
hcd_port_handle_t port_hdl;
@@ -382,6 +383,8 @@ void test_hcd_free_urb(urb_t *urb)
382383
heap_caps_free(urb);
383384
}
384385

386+
// --------------------------------------------------- Enumeration -----------------------------------------------------
387+
385388
uint8_t test_hcd_enum_device(hcd_pipe_handle_t default_pipe)
386389
{
387390
// We need to create a URB for the enumeration control transfers
@@ -427,3 +430,25 @@ uint8_t test_hcd_enum_device(hcd_pipe_handle_t default_pipe)
427430
test_hcd_free_urb(urb);
428431
return ENUM_ADDR;
429432
}
433+
434+
// ---------------------------------------------- Transfer submit ------------------------------------------------------
435+
436+
void test_hcd_ping_device(hcd_pipe_handle_t default_pipe, urb_t *default_urb)
437+
{
438+
default_urb->transfer.num_bytes = sizeof(usb_setup_packet_t) + TRANSFER_MAX_BYTES;
439+
USB_SETUP_PACKET_INIT_GET_CONFIG_DESC((usb_setup_packet_t *)default_urb->transfer.data_buffer, 0, TRANSFER_MAX_BYTES);
440+
default_urb->transfer.context = URB_CONTEXT_VAL;
441+
442+
TEST_ASSERT_EQUAL(ESP_OK, hcd_urb_enqueue(default_pipe, default_urb));
443+
test_hcd_expect_pipe_event(default_pipe, HCD_PIPE_EVENT_URB_DONE);
444+
urb_t *urb = hcd_urb_dequeue(default_pipe);
445+
TEST_ASSERT_EQUAL_MESSAGE(default_urb, urb, "URB pointers not equal");
446+
TEST_ASSERT_EQUAL_MESSAGE(USB_TRANSFER_STATUS_COMPLETED, urb->transfer.status, "Transfer NOT completed");
447+
TEST_ASSERT_EQUAL_MESSAGE(URB_CONTEXT_VAL, urb->transfer.context, "URB context not equal");
448+
449+
// We must have transmitted at least the setup packet, but device may return less bytes than bytes requested
450+
TEST_ASSERT_GREATER_OR_EQUAL(sizeof(usb_setup_packet_t), urb->transfer.actual_num_bytes);
451+
TEST_ASSERT_LESS_OR_EQUAL(urb->transfer.num_bytes, urb->transfer.actual_num_bytes);
452+
usb_config_desc_t *config_desc = (usb_config_desc_t *)(urb->transfer.data_buffer + sizeof(usb_setup_packet_t));
453+
TEST_ASSERT_EQUAL(USB_B_DESCRIPTOR_TYPE_CONFIGURATION, config_desc->bDescriptorType);
454+
}

host/usb/test/target_test/common/hcd_common.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,14 @@ void test_hcd_free_urb(urb_t *urb);
191191
uint8_t test_hcd_enum_device(hcd_pipe_handle_t default_pipe);
192192

193193
/**
194-
* @brief Set endpoint descriptor
194+
* @brief Ping device to check whether the device is responsive or not
195195
*
196-
* Set endpoint descriptor of the mock device with different wMaxPacketSize according to the connected device's speed
196+
* Use this function to check whether it is possible to communicate with a device.
197+
* For example after resuming a device, to check whether the device has been resumed correctly.
198+
* The function sends a get configuration descriptor request to a device, checking both, the IN and OUT transfers
197199
*
198-
* @param port_speed Port speed after the device is connected
200+
* @note this function sends a control transfer to the device
201+
* @param default_pipe The connected device's default pipe
202+
* @param default_urb A default_pipe's URB used for get configuration descriptor request
199203
*/
200-
void test_hcd_set_mock_msc_ep_descriptor(usb_speed_t port_speed);
204+
void test_hcd_ping_device(hcd_pipe_handle_t default_pipe, urb_t *default_urb);

host/usb/test/target_test/hcd/main/test_hcd_port.c

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,12 @@ Test port suspend and resume with active pipes
146146
- Trigger a port connection
147147
- Create a default pipe
148148
- Test that port can't be suspended with an active pipe
149+
- Ping a device (send get configuration descriptor transfer)
149150
- Halt the default pipe after a short delay
150151
- Suspend the port
151152
- Resume the port
152153
- Check that all the pipe is still halted
154+
- Ping a device
153155
- Cleanup default pipe
154156
- Trigger disconnection and teardown
155157
*/
@@ -161,6 +163,9 @@ TEST_CASE("Test HCD port suspend and resume", "[port][low_speed][full_speed][hig
161163
// Allocate some URBs and initialize their data buffers with control transfers
162164
hcd_pipe_handle_t default_pipe = test_hcd_pipe_alloc(port_hdl, NULL, TEST_DEV_ADDR, port_speed); // Create a default pipe (using a NULL EP descriptor)
163165

166+
urb_t *urb = test_hcd_alloc_urb(0, URB_DATA_BUFF_SIZE);
167+
test_hcd_ping_device(default_pipe, urb);
168+
164169
// Test that suspending the port now fails as there is an active pipe
165170
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hcd_port_command(port_hdl, HCD_PORT_CMD_SUSPEND));
166171

@@ -185,6 +190,8 @@ TEST_CASE("Test HCD port suspend and resume", "[port][low_speed][full_speed][hig
185190
TEST_ASSERT_EQUAL(HCD_PIPE_STATE_ACTIVE, hcd_pipe_get_state(default_pipe));
186191
vTaskDelay(pdMS_TO_TICKS(100)); // Give some time for resumed URBs to complete
187192

193+
test_hcd_ping_device(default_pipe, urb);
194+
test_hcd_free_urb(urb);
188195
test_hcd_pipe_free(default_pipe);
189196
// Cleanup
190197
test_hcd_wait_for_disconn(port_hdl, false);
@@ -197,17 +204,22 @@ Test port suspend and resume sudden disconnect
197204
- Test port suspend and resume procedure with sudden disconnect
198205
- When suspended, the port must react to power off command by disconnecting a device
199206
- When no device is connected, the port must NOT allow to enter and exit the suspended state
207+
- Test exiting suspend state through device disconnect
200208
201209
Procedure:
202210
- Setup the HCD and a port
203211
- Trigger a port connection
204212
- Create a default pipe
213+
- Ping a device (send get configuration descriptor transfer)
205214
- Halt the default pipe after a short delay
206215
- Suspend the port
207216
- Cleanup default pipe
208217
- Power off the port and recover the port
209218
- Try to Suspend and Resume the port with no devices connected
210219
- Trigger connection and disconnection again (to make sure the port works post recovery)
220+
- Trigger connection again (to make sure the port works post recovery)
221+
- Ping a device
222+
- Trigger disconnection and cleanup
211223
- Teardown port and HCD
212224
*/
213225
TEST_CASE("Test HCD port suspend and resume sudden disconnect", "[port][low_speed][full_speed][high_speed]")
@@ -218,8 +230,8 @@ TEST_CASE("Test HCD port suspend and resume sudden disconnect", "[port][low_spee
218230
// Allocate some URBs and initialize their data buffers with control transfers
219231
hcd_pipe_handle_t default_pipe = test_hcd_pipe_alloc(port_hdl, NULL, TEST_DEV_ADDR, port_speed); // Create a default pipe (using a NULL EP descriptor)
220232

221-
// Test that suspending the port now fails as there is an active pipe
222-
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hcd_port_command(port_hdl, HCD_PORT_CMD_SUSPEND));
233+
urb_t *urb = test_hcd_alloc_urb(0, URB_DATA_BUFF_SIZE);
234+
test_hcd_ping_device(default_pipe, urb);
223235

224236
// Halt the default pipe before suspending
225237
TEST_ASSERT_EQUAL(HCD_PIPE_STATE_ACTIVE, hcd_pipe_get_state(default_pipe));
@@ -240,7 +252,8 @@ TEST_CASE("Test HCD port suspend and resume sudden disconnect", "[port][low_spee
240252
TEST_ASSERT_EQUAL(HCD_PORT_STATE_RECOVERY, hcd_port_get_state(port_hdl));
241253
printf("Sudden disconnect\n");
242254

243-
// Free the pipe, to be able to recover the port
255+
// Free the pipe and its urb, to be able to recover the port
256+
test_hcd_free_urb(urb);
244257
test_hcd_pipe_free(default_pipe);
245258
// Recover the port should return to the NOT POWERED state
246259
TEST_ASSERT_EQUAL(ESP_OK, hcd_port_recover(port_hdl));
@@ -250,8 +263,76 @@ TEST_CASE("Test HCD port suspend and resume sudden disconnect", "[port][low_spee
250263
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hcd_port_command(port_hdl, HCD_PORT_CMD_SUSPEND));
251264
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hcd_port_command(port_hdl, HCD_PORT_CMD_RESUME));
252265

253-
// Recovered port should be able to connect and disconnect again
254-
test_hcd_wait_for_conn(port_hdl);
266+
// Recovered port should be able to connect again
267+
port_speed = test_hcd_wait_for_conn(port_hdl);
268+
vTaskDelay(pdMS_TO_TICKS(100)); // Short delay send of SOF (for FS, HS) or EOPs (for LS)
269+
270+
// Allocate some URBs and initialize their data buffers with control transfers
271+
default_pipe = test_hcd_pipe_alloc(port_hdl, NULL, TEST_DEV_ADDR, port_speed); // Create a default pipe (using a NULL EP descriptor)
272+
urb = test_hcd_alloc_urb(0, URB_DATA_BUFF_SIZE);
273+
test_hcd_ping_device(default_pipe, urb);
274+
275+
test_hcd_free_urb(urb);
276+
test_hcd_pipe_free(default_pipe);
277+
test_hcd_wait_for_disconn(port_hdl, false);
278+
}
279+
280+
/*
281+
Test port suspend and resume with port reset
282+
283+
Purpose:
284+
- Test port suspend and resume procedure with port reset
285+
- When suspended, the port must react to reset command
286+
- Test exiting suspend state through port reset
287+
288+
Procedure:
289+
- Setup the HCD and a port
290+
- Trigger a port connection
291+
- Create a default pipe
292+
- Ping a device (send get configuration descriptor transfer)
293+
- Halt the default pipe
294+
- Suspend the port
295+
- Reset and recover the port
296+
- Ping a device
297+
- Trigger disconnection and cleanup
298+
- Teardown port and HCD
299+
*/
300+
TEST_CASE("Test HCD port suspend and resume port reset", "[port][low_speed][full_speed][high_speed]")
301+
{
302+
usb_speed_t port_speed = test_hcd_wait_for_conn(port_hdl); // Trigger a connection
303+
vTaskDelay(pdMS_TO_TICKS(100)); // Short delay send of SOF (for FS, HS) or EOPs (for LS)
304+
305+
// Allocate some URBs and initialize their data buffers with control transfers
306+
hcd_pipe_handle_t default_pipe = test_hcd_pipe_alloc(port_hdl, NULL, TEST_DEV_ADDR, port_speed); // Create a default pipe (using a NULL EP descriptor)
307+
urb_t *urb = test_hcd_alloc_urb(0, URB_DATA_BUFF_SIZE);
308+
test_hcd_ping_device(default_pipe, urb);
309+
310+
// Halt the default pipe before suspending
311+
TEST_ASSERT_EQUAL(HCD_PIPE_STATE_ACTIVE, hcd_pipe_get_state(default_pipe));
312+
TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_command(default_pipe, HCD_PIPE_CMD_HALT));
313+
TEST_ASSERT_EQUAL(HCD_PIPE_STATE_HALTED, hcd_pipe_get_state(default_pipe));
314+
315+
// Suspend the port
316+
TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_SUSPEND));
317+
TEST_ASSERT_EQUAL(HCD_PORT_STATE_SUSPENDED, hcd_port_get_state(port_hdl));
318+
printf("Suspended\n");
319+
vTaskDelay(pdMS_TO_TICKS(100)); // Give some time for bus to remain suspended
320+
321+
// Reset the port
322+
TEST_ASSERT_EQUAL(ESP_OK, hcd_port_command(port_hdl, HCD_PORT_CMD_RESET));
323+
printf("Port reset\n");
324+
325+
// Port should be in enabled state, and the default pipe still halted
326+
TEST_ASSERT_EQUAL(HCD_PORT_STATE_ENABLED, hcd_port_get_state(port_hdl));
327+
TEST_ASSERT_EQUAL(HCD_PIPE_STATE_HALTED, hcd_pipe_get_state(default_pipe));
328+
// Clear the pipe
329+
TEST_ASSERT_EQUAL(ESP_OK, hcd_pipe_command(default_pipe, HCD_PIPE_CMD_CLEAR));
330+
TEST_ASSERT_EQUAL(HCD_PIPE_STATE_ACTIVE, hcd_pipe_get_state(default_pipe));
331+
332+
test_hcd_ping_device(default_pipe, urb);
333+
334+
test_hcd_free_urb(urb);
335+
test_hcd_pipe_free(default_pipe);
255336
test_hcd_wait_for_disconn(port_hdl, false);
256337
}
257338

0 commit comments

Comments
 (0)