mirror of
https://github.com/espressif/esp-idf.git
synced 2025-11-14 06:20:16 +01:00
usb: Hub Driver and USBH recover port automatically after disconnection
This commit updates the Hub Driver and USBH as follows:
- Updated communication mechanism between Hub Driver and USBH
- USBH notifies the Hub Driver via Hub request callback (usbh_hub_req_cb_t)
- Hub Driver notifies the USBH via usbh_hub_pass_event()
- Hub Driver now defers all event handling to hub_process.
- Hub Driver and USBH will now automatically recover the port after a disconnected
device has been closed.
- Fixed incorrect assert in usbh_dev_close()
This commit is contained in:
@@ -36,6 +36,8 @@ Implementation of an asynchronous MSC client used for USB Host disconnection tes
|
||||
- Deregister MSC client
|
||||
*/
|
||||
|
||||
#define TEST_DCONN_ITERATIONS 3
|
||||
|
||||
typedef enum {
|
||||
TEST_STAGE_WAIT_CONN,
|
||||
TEST_STAGE_DEV_OPEN,
|
||||
@@ -155,6 +157,7 @@ void msc_client_async_dconn_task(void *arg)
|
||||
|
||||
bool exit_loop = false;
|
||||
bool skip_event_handling = false;
|
||||
int dconn_iter = 0;
|
||||
while (!exit_loop) {
|
||||
if (!skip_event_handling) {
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_handle_events(msc_obj.client_hdl, portMAX_DELAY));
|
||||
@@ -166,6 +169,10 @@ void msc_client_async_dconn_task(void *arg)
|
||||
msc_obj.cur_stage = msc_obj.next_stage;
|
||||
|
||||
switch (msc_obj.cur_stage) {
|
||||
case TEST_STAGE_WAIT_CONN: {
|
||||
//Nothing to do while waiting for connection
|
||||
break;
|
||||
}
|
||||
case TEST_STAGE_DEV_OPEN: {
|
||||
ESP_LOGD(MSC_CLIENT_TAG, "Open");
|
||||
//Open the device
|
||||
@@ -227,7 +234,15 @@ void msc_client_async_dconn_task(void *arg)
|
||||
ESP_LOGD(MSC_CLIENT_TAG, "Close");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_interface_release(msc_obj.client_hdl, msc_obj.dev_hdl, MOCK_MSC_SCSI_INTF_NUMBER));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_close(msc_obj.client_hdl, msc_obj.dev_hdl));
|
||||
exit_loop = true;
|
||||
dconn_iter++;
|
||||
if (dconn_iter < TEST_DCONN_ITERATIONS) {
|
||||
//Start the next test iteration by going back to TEST_STAGE_WAIT_CONN and reenabling connections
|
||||
msc_obj.next_stage = TEST_STAGE_WAIT_CONN;
|
||||
skip_event_handling = true; //Need to execute TEST_STAGE_WAIT_CONN
|
||||
test_usb_set_phy_state(true, 0);
|
||||
} else {
|
||||
exit_loop = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -28,9 +28,12 @@ Implementation of an asynchronous MSC client used for USB Host enumeration test.
|
||||
- Check the device and configuration descriptor of the device
|
||||
- Check the device's information
|
||||
- Close device
|
||||
- Repeat for multiple iterations from waiting connection by forcing a disconnection
|
||||
- Deregister MSC client
|
||||
*/
|
||||
|
||||
#define TEST_ENUM_ITERATIONS 3
|
||||
|
||||
typedef enum {
|
||||
TEST_STAGE_WAIT_CONN,
|
||||
TEST_STAGE_DEV_OPEN,
|
||||
@@ -90,6 +93,7 @@ void msc_client_async_enum_task(void *arg)
|
||||
|
||||
bool exit_loop = false;
|
||||
bool skip_event_handling = false;
|
||||
int enum_iter = 0;
|
||||
while (!exit_loop) {
|
||||
if (!skip_event_handling) {
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_handle_events(msc_obj.client_hdl, portMAX_DELAY));
|
||||
@@ -101,6 +105,10 @@ void msc_client_async_enum_task(void *arg)
|
||||
msc_obj.cur_stage = msc_obj.next_stage;
|
||||
|
||||
switch (msc_obj.cur_stage) {
|
||||
case TEST_STAGE_WAIT_CONN: {
|
||||
//Wait for connection, nothing to do
|
||||
break;
|
||||
}
|
||||
case TEST_STAGE_DEV_OPEN: {
|
||||
ESP_LOGD(MSC_CLIENT_TAG, "Open");
|
||||
//Open the device
|
||||
@@ -154,7 +162,16 @@ void msc_client_async_enum_task(void *arg)
|
||||
case TEST_STAGE_DEV_CLOSE: {
|
||||
ESP_LOGD(MSC_CLIENT_TAG, "Close");
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_close(msc_obj.client_hdl, msc_obj.dev_hdl));
|
||||
exit_loop = true;
|
||||
enum_iter++;
|
||||
if (enum_iter < TEST_ENUM_ITERATIONS) {
|
||||
//Start the next test iteration by disconnecting the device, then going back to TEST_STAGE_WAIT_CONN stage
|
||||
test_usb_set_phy_state(false, 0);
|
||||
test_usb_set_phy_state(true, 0);
|
||||
msc_obj.next_stage = TEST_STAGE_WAIT_CONN;
|
||||
skip_event_handling = true; //Need to execute TEST_STAGE_WAIT_CONN
|
||||
} else {
|
||||
exit_loop = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "test_usb_common.h"
|
||||
@@ -20,14 +19,20 @@
|
||||
|
||||
// --------------------------------------------------- Test Cases ------------------------------------------------------
|
||||
|
||||
//Safe approximation of time it takes to connect and enumerate the device
|
||||
#define TEST_FORCE_DCONN_DELAY_MS 400
|
||||
/*
|
||||
Test USB Host Library Sudden Disconnection Handling (no clients)
|
||||
Purpose:
|
||||
- Test that sudden disconnections are handled properly when there are no clients
|
||||
- Test that devices can reconnect after a sudden disconnection has been handled by the USB Host Library
|
||||
|
||||
static void trigger_dconn_timer_cb(TimerHandle_t xTimer)
|
||||
{
|
||||
printf("Forcing Sudden Disconnect\n");
|
||||
test_usb_set_phy_state(false, 0);
|
||||
}
|
||||
Procedure:
|
||||
- Install USB Host Library
|
||||
- Wait for connection (and enumeration) to occur
|
||||
- Force a disconnection, then wait for disconnection to be handled (USB_HOST_LIB_EVENT_FLAGS_ALL_FREE)
|
||||
- Allow connections again, and repeat test for multiple iterations
|
||||
*/
|
||||
|
||||
#define TEST_DCONN_NO_CLIENT_ITERATIONS 3
|
||||
|
||||
TEST_CASE("Test USB Host sudden disconnection (no client)", "[usb_host][ignore]")
|
||||
{
|
||||
@@ -40,32 +45,53 @@ TEST_CASE("Test USB Host sudden disconnection (no client)", "[usb_host][ignore]"
|
||||
ESP_ERROR_CHECK(usb_host_install(&host_config));
|
||||
printf("Installed\n");
|
||||
|
||||
//Allocate timer to force disconnection after a short delay
|
||||
TimerHandle_t timer_hdl = xTimerCreate("dconn",
|
||||
pdMS_TO_TICKS(TEST_FORCE_DCONN_DELAY_MS),
|
||||
pdFALSE,
|
||||
NULL,
|
||||
trigger_dconn_timer_cb);
|
||||
TEST_ASSERT_NOT_EQUAL(NULL, timer_hdl);
|
||||
TEST_ASSERT_EQUAL(pdPASS, xTimerStart(timer_hdl, portMAX_DELAY));
|
||||
|
||||
bool connected = false;
|
||||
int dconn_iter = 0;
|
||||
while (1) {
|
||||
//Start handling system events
|
||||
uint32_t event_flags;
|
||||
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
|
||||
if (!connected) {
|
||||
usb_host_lib_info_t lib_info;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_lib_info(&lib_info));
|
||||
if (lib_info.num_devices == 1) {
|
||||
//We've just connected. Trigger a disconnect
|
||||
connected = true;
|
||||
printf("Forcing Sudden Disconnect\n");
|
||||
test_usb_set_phy_state(false, 0);
|
||||
}
|
||||
}
|
||||
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
|
||||
printf("All devices cleaned up\n");
|
||||
break;
|
||||
//The device has disconnected and it's disconnection has been handled
|
||||
printf("Dconn iter %d done\n", dconn_iter);
|
||||
if (++dconn_iter < TEST_DCONN_NO_CLIENT_ITERATIONS) {
|
||||
//Start next iteration
|
||||
connected = false;
|
||||
test_usb_set_phy_state(true, 0);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Cleanup timer
|
||||
TEST_ASSERT_EQUAL(pdPASS, xTimerDelete(timer_hdl, portMAX_DELAY));
|
||||
//Clean up USB Host
|
||||
ESP_ERROR_CHECK(usb_host_uninstall());
|
||||
test_usb_deinit_phy(); //Deinitialize the internal USB PHY after testing
|
||||
}
|
||||
|
||||
/*
|
||||
Test USB Host Library Sudden Disconnection Handling (with client)
|
||||
Purpose:
|
||||
- Test that sudden disconnections are handled properly when there are registered clients
|
||||
- Test that devices can reconnect after a sudden disconnection has been handled by the USB Host Library
|
||||
|
||||
Procedure:
|
||||
- Install USB Host Library
|
||||
- Create a task to run an MSC client
|
||||
- Start the MSC disconnect client task. It will open the device then force a disconnect for multiple iterations
|
||||
- Wait for USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS and USB_HOST_LIB_EVENT_FLAGS_ALL_FREE before uninstalling
|
||||
*/
|
||||
|
||||
#define TEST_FORCE_DCONN_NUM_TRANSFERS 3
|
||||
#define TEST_MSC_SCSI_TAG 0xDEADBEEF
|
||||
|
||||
@@ -116,6 +142,24 @@ TEST_CASE("Test USB Host sudden disconnection (single client)", "[usb_host][igno
|
||||
test_usb_deinit_phy(); //Deinitialize the internal USB PHY after testing
|
||||
}
|
||||
|
||||
/*
|
||||
Test USB Host Library Enumeration
|
||||
Purpose:
|
||||
- Test that the USB Host Library enumerates device correctly
|
||||
|
||||
Procedure:
|
||||
- Install USB Host Library
|
||||
- Create a task to run an MSC client
|
||||
- Start the MSC enumeration client task. It will:
|
||||
- Wait for device connection
|
||||
- Open the device
|
||||
- Check details of the device's enumeration
|
||||
- Disconnect the device, and repeat the steps above for multiple iterations.
|
||||
- Wait for USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS and USB_HOST_LIB_EVENT_FLAGS_ALL_FREE before uninstalling
|
||||
*/
|
||||
|
||||
#define TEST_ENUM_ITERATIONS 3
|
||||
|
||||
TEST_CASE("Test USB Host enumeration", "[usb_host][ignore]")
|
||||
{
|
||||
test_usb_init_phy(); //Initialize the internal USB PHY and USB Controller for testing
|
||||
@@ -129,20 +173,23 @@ TEST_CASE("Test USB Host enumeration", "[usb_host][ignore]")
|
||||
|
||||
//Create task to run client that checks the enumeration of the device
|
||||
TaskHandle_t task_hdl;
|
||||
xTaskCreatePinnedToCore(msc_client_async_enum_task, "async", 4096, NULL, 2, &task_hdl, 0);
|
||||
xTaskCreatePinnedToCore(msc_client_async_enum_task, "async", 6144, NULL, 2, &task_hdl, 0);
|
||||
//Start the task
|
||||
xTaskNotifyGive(task_hdl);
|
||||
|
||||
while (1) {
|
||||
bool all_clients_gone = false;
|
||||
bool all_dev_free = false;
|
||||
while (!all_clients_gone || !all_dev_free) {
|
||||
//Start handling system events
|
||||
uint32_t event_flags;
|
||||
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
|
||||
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
|
||||
printf("No more clients\n");
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FINISHED, usb_host_device_free_all());
|
||||
all_clients_gone = true;
|
||||
}
|
||||
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
|
||||
break;
|
||||
if (all_clients_gone && event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
|
||||
all_dev_free = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user