From 189899180f6339f0f291a632f9f3a3c12559e960 Mon Sep 17 00:00:00 2001 From: Darian Leung Date: Thu, 28 Oct 2021 00:54:27 +0800 Subject: [PATCH] usb: Change API and refactor tests This commit updates the USB Host Library API in the following wasy: - usb_host_client_handle_t and usb_device_handle_t made into struct pointers to generate compiler warnings about conflicting handle types - usb_host_client_config_t changed to future proof API for Synchronous Clients - Added usb_host_lib_unblock() - Added usb_host_device_addr_list_fill() - Return of usb_host_device_free_all() updated to indicate whether there are still devices yet to be freed. - Blockg APIs are now marked explicitly - Fixed a bug in usb_host_transfer_submit_control() when checking the bEndpointAddress of a control transfer. Tests are also refactored to move some common macros into shared headers Closes https://github.com/espressif/esp-idf/issues/7786 --- components/usb/include/usb/usb_host.h | 70 ++++++++++++++----- components/usb/include/usb/usb_types_stack.h | 2 +- components/usb/private_include/usbh.h | 19 ++++- .../usb/test/usb_host/ctrl_client_async_seq.c | 11 +-- components/usb/test/usb_host/msc_client.h | 2 + .../test/usb_host/msc_client_async_dconn.c | 13 ++-- .../usb/test/usb_host/msc_client_async_seq.c | 14 ++-- .../usb/test/usb_host/test_usb_host_async.c | 4 +- components/usb/usb_host.c | 31 ++++++-- components/usb/usbh.c | 34 ++++++++- 10 files changed, 158 insertions(+), 42 deletions(-) diff --git a/components/usb/include/usb/usb_host.h b/components/usb/include/usb/usb_host.h index 6c104af6cf..9a91e7ed18 100644 --- a/components/usb/include/usb/usb_host.h +++ b/components/usb/include/usb/usb_host.h @@ -27,7 +27,14 @@ extern "C" { // ----------------------- Handles ------------------------- -typedef void * usb_host_client_handle_t; /**< Handle to a client using the USB Host Library */ +/** + * @brief Handle to a USB Host Library asynchronous client + * + * An asynchronous client can be registered using usb_host_client_register() + * + * @note Asynchronous API + */ +typedef struct usb_host_client_handle_s * usb_host_client_handle_t; // ----------------------- Events -------------------------- @@ -93,9 +100,14 @@ typedef struct { * Configuration structure for a USB Host Library client. Provided in usb_host_client_register() */ typedef struct { - usb_host_client_event_cb_t client_event_callback; /**< Client's event callback function */ - void *callback_arg; /**< Event callback function argument */ - int max_num_event_msg; /**< Maximum number of event messages that can be stored (e.g., 3) */ + bool is_synchronous; /**< Whether the client is asynchronous or synchronous or not. Set to false for now. */ + int max_num_event_msg; /**< Maximum number of event messages that can be stored (e.g., 3) */ + union { //Note: Made into union or future expansion + struct { + usb_host_client_event_cb_t client_event_callback; /**< Client's event callback function */ + void *callback_arg; /**< Event callback function argument */ + } async; + }; } usb_host_client_config_t; // ------------------------------------------------ Library Functions -------------------------------------------------- @@ -129,12 +141,22 @@ esp_err_t usb_host_uninstall(void); * - This function handles all of the USB Host Library's processing and should be called repeatedly in a loop * - Check event_flags_ret to see if an flags are set indicating particular USB Host Library events * + * @note This function can block * @param[in] timeout_ticks Timeout in ticks to wait for an event to occur * @param[out] event_flags_ret Event flags that indicate what USB Host Library event occurred * @return esp_err_t */ esp_err_t usb_host_lib_handle_events(TickType_t timeout_ticks, uint32_t *event_flags_ret); +/** + * @brief Unblock the USB Host Library handler + * + * - This function simply unblocks the USB Host Library event handling function (usb_host_lib_handle_events()) + * + * @return esp_err_t + */ +esp_err_t usb_host_lib_unblock(void); + // ------------------------------------------------ Client Functions --------------------------------------------------- /** @@ -165,6 +187,7 @@ esp_err_t usb_host_client_deregister(usb_host_client_handle_t client_hdl); * * - This function handles all of a client's processing and should be called repeatedly in a loop * + * @note This function can block * @param[in] client_hdl Client handle * @param[in] timeout_ticks Timeout in ticks to wait for an event to occur * @return esp_err_t @@ -204,6 +227,7 @@ esp_err_t usb_host_device_open(usb_host_client_handle_t client_hdl, uint8_t dev_ * - A client must close a device after it has finished using the device (claimed interfaces must also be released) * - A client must close all devices it has opened before deregistering * + * @note This function can block * @param[in] client_hdl Client handle * @param[in] dev_hdl Device handle * @return esp_err_t @@ -220,10 +244,28 @@ esp_err_t usb_host_device_close(usb_host_client_handle_t client_hdl, usb_device_ * when all devices have been freed * - This function is useful when cleaning up devices before uninstalling the USB Host Library * - * @return esp_err_t + * @return + * - ESP_ERR_NOT_FINISHED: There are one or more devices that still need to be freed. Wait for USB_HOST_LIB_EVENT_FLAGS_ALL_FREE event + * - ESP_OK: All devices already freed (i.e., there were no devices) + * - Other: Error */ esp_err_t usb_host_device_free_all(void); +/** + * @brief Fill a list of device address + * + * - This function fills an empty list with the address of connected devices + * - The Device addresses can then used in usb_host_device_open() + * - If there are more devices than the list_len, this function will only fill + * up to list_len number of devices. + * + * @param[in] list_len Length of the empty list + * @param[inout] dev_addr_list Empty list to be filled + * @param[out] num_dev_ret Number of devices + * @return esp_err_t + */ +esp_err_t usb_host_device_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret); + // ------------------------------------------------- Device Requests --------------------------------------------------- // ------------------- Cached Requests --------------------- @@ -234,6 +276,7 @@ esp_err_t usb_host_device_free_all(void); * - This function gets some basic information of a device * - The device must be opened first before attempting to get its information * + * @note This function can block * @param[in] dev_hdl Device handle * @param[out] dev_info Device information * @return esp_err_t @@ -265,6 +308,7 @@ esp_err_t usb_host_get_device_descriptor(usb_device_handle_t dev_hdl, const usb_ * - No control transfer is sent. The device's active configuration descriptor is cached on enumeration * - This function simple returns a pointer to the cached descriptor * + * @note This function can block * @note No control transfer is sent. A device's active configuration descriptor is cached on enumeration * @param[in] dev_hdl Device handle * @param[out] config_desc Configuration descriptor @@ -280,6 +324,7 @@ esp_err_t usb_host_get_active_config_descriptor(usb_device_handle_t dev_hdl, con * - A client must claim a device's interface before attempting to communicate with any of its endpoints * - Once an interface is claimed by a client, it cannot be claimed by any other client. * + * @note This function can block * @param[in] client_hdl Client handle * @param[in] dev_hdl Device handle * @param[in] bInterfaceNumber Interface number @@ -294,6 +339,7 @@ esp_err_t usb_host_interface_claim(usb_host_client_handle_t client_hdl, usb_devi * - A client should release a device's interface after it no longer needs to communicate with the interface * - A client must release all of its interfaces of a device it has claimed before being able to close the device * + * @note This function can block * @param[in] client_hdl Client handle * @param[in] dev_hdl Device handle * @param[in] bInterfaceNumber Interface number @@ -308,6 +354,7 @@ esp_err_t usb_host_interface_release(usb_host_client_handle_t client_hdl, usb_de * - The endpoint must be part of an interface claimed by a client * - Once halted, the endpoint must be cleared using usb_host_endpoint_clear() before it can communicate again * + * @note This function can block * @param dev_hdl Device handle * @param bEndpointAddress Endpoint address * @return esp_err_t @@ -322,6 +369,7 @@ esp_err_t usb_host_endpoint_halt(usb_device_handle_t dev_hdl, uint8_t bEndpointA * - The endpoint must have been halted (either through a transfer error, or usb_host_endpoint_halt()) * - Flushing an endpoint will caused an queued up transfers to be canceled * + * @note This function can block * @param dev_hdl Device handle * @param bEndpointAddress Endpoint address * @return esp_err_t @@ -336,6 +384,7 @@ esp_err_t usb_host_endpoint_flush(usb_device_handle_t dev_hdl, uint8_t bEndpoint * - The endpoint must have been halted (either through a transfer error, or usb_host_endpoint_halt()) * - If the endpoint has any queued up transfers, clearing a halt will resume their execution * + * @note This function can block * @param dev_hdl Device handle * @param bEndpointAddress Endpoint address * @return esp_err_t @@ -396,17 +445,6 @@ esp_err_t usb_host_transfer_submit(usb_transfer_t *transfer); */ esp_err_t usb_host_transfer_submit_control(usb_host_client_handle_t client_hdl, usb_transfer_t *transfer); -/** - * @brief Cancel a submitted transfer - * - * - Cancel a previously submitted transfer - * - In its current implementation, any transfer that is already in-flight will not be canceled - * - * @param transfer Transfer object - * @return esp_err_t - */ -esp_err_t usb_host_transfer_cancel(usb_transfer_t *transfer); - #ifdef __cplusplus } #endif diff --git a/components/usb/include/usb/usb_types_stack.h b/components/usb/include/usb/usb_types_stack.h index 4305555fe7..974d01def5 100644 --- a/components/usb/include/usb/usb_types_stack.h +++ b/components/usb/include/usb/usb_types_stack.h @@ -43,7 +43,7 @@ typedef enum { /** * @brief Handle of a USB Device connected to a USB Host */ -typedef void * usb_device_handle_t; +typedef struct usb_device_handle_s * usb_device_handle_t; /** * @brief Basic information of an enumerated device diff --git a/components/usb/private_include/usbh.h b/components/usb/private_include/usbh.h index 9fa2140537..6d530f7f49 100644 --- a/components/usb/private_include/usbh.h +++ b/components/usb/private_include/usbh.h @@ -119,6 +119,21 @@ esp_err_t usbh_process(void); // --------------------- Device Pool ----------------------- +/** + * @brief Fill list with address of currently connected devices + * + * - This function fills the provided list with the address of current connected devices + * - Device address can then be used in usbh_dev_open() + * - If there are more devices than the list_len, this function will only fill + * up to list_len number of devices. + * + * @param[in] list_len Length of empty list + * @param[inout] dev_addr_list Empty list to be filled + * @param[out] num_dev_ret Number of devices filled into list + * @return esp_err_t + */ +esp_err_t usbh_dev_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret); + /** * @brief Open a device by address * @@ -145,7 +160,9 @@ esp_err_t usbh_dev_close(usb_device_handle_t dev_hdl); * * A device marked as free will not be freed until the last client using the device has called usbh_dev_close() * - * @return esp_err_t + * @return + * - ESP_OK: There were no devices to free to begin with. Current state is all free + * - ESP_ERR_NOT_FINISHED: One or more devices still need to be freed (but have been marked "to be freed") */ esp_err_t usbh_dev_mark_all_free(void); diff --git a/components/usb/test/usb_host/ctrl_client_async_seq.c b/components/usb/test/usb_host/ctrl_client_async_seq.c index 85751adeb6..18cd4ca646 100644 --- a/components/usb/test/usb_host/ctrl_client_async_seq.c +++ b/components/usb/test/usb_host/ctrl_client_async_seq.c @@ -10,6 +10,7 @@ #include "freertos/task.h" #include "esp_err.h" #include "esp_log.h" +#include "test_usb_common.h" #include "ctrl_client.h" #include "usb/usb_host.h" #include "unity.h" @@ -32,7 +33,6 @@ Implementation of a control transfer client used for USB Host Tests. - Deregister control client */ -#define MAX(x,y) (((x) >= (y)) ? (x) : (y)) #define CTRL_CLIENT_MAX_EVENT_MSGS 5 #define NUM_TRANSFER_OBJ 3 #define MAX_TRANSFER_BYTES 256 @@ -97,9 +97,12 @@ void ctrl_client_async_seq_task(void *arg) //Register client usb_host_client_config_t client_config = { - .client_event_callback = ctrl_client_event_cb, - .callback_arg = (void *)&ctrl_obj, + .is_synchronous = false, .max_num_event_msg = CTRL_CLIENT_MAX_EVENT_MSGS, + .async = { + .client_event_callback = ctrl_client_event_cb, + .callback_arg = (void *)&ctrl_obj, + }, }; TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_register(&client_config, &ctrl_obj.client_hdl)); @@ -153,7 +156,7 @@ void ctrl_client_async_seq_task(void *arg) usb_transfer_t *transfer = ctrl_xfer[ctrl_obj.num_xfer_sent % NUM_TRANSFER_OBJ]; USB_SETUP_PACKET_INIT_GET_CONFIG_DESC((usb_setup_packet_t *)transfer->data_buffer, 0, MAX_TRANSFER_BYTES); transfer->num_bytes = sizeof(usb_setup_packet_t) + MAX_TRANSFER_BYTES; - transfer->bEndpointAddress = 0; + transfer->bEndpointAddress = 0x80; TEST_ASSERT_EQUAL(ESP_OK, usb_host_transfer_submit_control(ctrl_obj.client_hdl, transfer)); ctrl_obj.num_xfer_sent++; ctrl_obj.next_stage = TEST_STAGE_CTRL_XFER_WAIT; diff --git a/components/usb/test/usb_host/msc_client.h b/components/usb/test/usb_host/msc_client.h index 90850701d8..ab2f0f4dd3 100644 --- a/components/usb/test/usb_host/msc_client.h +++ b/components/usb/test/usb_host/msc_client.h @@ -6,6 +6,8 @@ #include +#define MSC_ASYNC_CLIENT_MAX_EVENT_MSGS 5 + typedef struct { int num_sectors_to_read; int num_sectors_per_xfer; diff --git a/components/usb/test/usb_host/msc_client_async_dconn.c b/components/usb/test/usb_host/msc_client_async_dconn.c index 67cd134edd..bbbd9170c2 100644 --- a/components/usb/test/usb_host/msc_client_async_dconn.c +++ b/components/usb/test/usb_host/msc_client_async_dconn.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_err.h" @@ -35,9 +36,6 @@ Implementation of an asynchronous MSC client used for USB Host disconnection tes - Deregister MSC client */ -#define MAX(x,y) (((x) >= (y)) ? (x) : (y)) -#define MSC_CLIENT_MAX_EVENT_MSGS 5 - typedef enum { TEST_STAGE_WAIT_CONN, TEST_STAGE_DEV_OPEN, @@ -130,9 +128,12 @@ void msc_client_async_dconn_task(void *arg) //Register client usb_host_client_config_t client_config = { - .client_event_callback = msc_client_event_cb, - .callback_arg = (void *)&msc_obj, - .max_num_event_msg = MSC_CLIENT_MAX_EVENT_MSGS, + .is_synchronous = false, + .max_num_event_msg = MSC_ASYNC_CLIENT_MAX_EVENT_MSGS, + .async = { + .client_event_callback = msc_client_event_cb, + .callback_arg = (void *)&msc_obj, + }, }; TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_register(&client_config, &msc_obj.client_hdl)); diff --git a/components/usb/test/usb_host/msc_client_async_seq.c b/components/usb/test/usb_host/msc_client_async_seq.c index 279962cbb2..d4be014b1c 100644 --- a/components/usb/test/usb_host/msc_client_async_seq.c +++ b/components/usb/test/usb_host/msc_client_async_seq.c @@ -7,10 +7,12 @@ #include #include #include +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_err.h" #include "esp_log.h" +#include "test_usb_common.h" #include "test_usb_mock_classes.h" #include "msc_client.h" #include "usb/usb_host.h" @@ -34,9 +36,6 @@ Implementation of an MSC client used for USB Host Tests - Deregister MSC client */ -#define MAX(x,y) (((x) >= (y)) ? (x) : (y)) -#define MSC_CLIENT_MAX_EVENT_MSGS 5 - typedef enum { TEST_STAGE_WAIT_CONN, TEST_STAGE_DEV_OPEN, @@ -130,9 +129,12 @@ void msc_client_async_seq_task(void *arg) //Register client usb_host_client_config_t client_config = { - .client_event_callback = msc_client_event_cb, - .callback_arg = (void *)&msc_obj, - .max_num_event_msg = MSC_CLIENT_MAX_EVENT_MSGS, + .is_synchronous = false, + .max_num_event_msg = MSC_ASYNC_CLIENT_MAX_EVENT_MSGS, + .async = { + .client_event_callback = msc_client_event_cb, + .callback_arg = (void *)&msc_obj, + }, }; TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_register(&client_config, &msc_obj.client_hdl)); diff --git a/components/usb/test/usb_host/test_usb_host_async.c b/components/usb/test/usb_host/test_usb_host_async.c index 5dbef9f535..aa3a394e96 100644 --- a/components/usb/test/usb_host/test_usb_host_async.c +++ b/components/usb/test/usb_host/test_usb_host_async.c @@ -72,7 +72,7 @@ TEST_CASE("Test USB Host async (single client)", "[usb_host][ignore]") 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_OK, usb_host_device_free_all()); + TEST_ASSERT_EQUAL(ESP_ERR_NOT_FINISHED, usb_host_device_free_all()); } if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { break; @@ -144,7 +144,7 @@ TEST_CASE("Test USB Host async (multi client)", "[usb_host][ignore]") 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_OK, usb_host_device_free_all()); + TEST_ASSERT_EQUAL(ESP_ERR_NOT_FINISHED, usb_host_device_free_all()); } if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { break; diff --git a/components/usb/usb_host.c b/components/usb/usb_host.c index 5759fd6a41..1aec4b7ee0 100644 --- a/components/usb/usb_host.c +++ b/components/usb/usb_host.c @@ -508,6 +508,16 @@ exit: return ret; } +esp_err_t usb_host_lib_unblock(void) +{ + //All devices must have been freed at this point + HOST_ENTER_CRITICAL(); + HOST_CHECK_FROM_CRIT(p_host_lib_obj != NULL, ESP_ERR_INVALID_STATE); + _unblock_lib(false); + HOST_EXIT_CRITICAL(); + return ESP_OK; +} + // ------------------------------------------------ Client Functions --------------------------------------------------- // ----------------------- Private ------------------------- @@ -562,7 +572,11 @@ static void _handle_pending_ep(client_t *client_obj) esp_err_t usb_host_client_register(const usb_host_client_config_t *client_config, usb_host_client_handle_t *client_hdl_ret) { HOST_CHECK(client_config != NULL && client_hdl_ret != NULL, ESP_ERR_INVALID_ARG); - HOST_CHECK(client_config->client_event_callback != NULL && client_config->max_num_event_msg > 0, ESP_ERR_INVALID_ARG); + HOST_CHECK(client_config->max_num_event_msg > 0, ESP_ERR_INVALID_ARG); + if (!client_config->is_synchronous) { + //Asynchronous clients must provide a + HOST_CHECK(client_config->async.client_event_callback != NULL, ESP_ERR_INVALID_ARG); + } esp_err_t ret; //Create client object @@ -579,8 +593,8 @@ esp_err_t usb_host_client_register(const usb_host_client_config_t *client_config TAILQ_INIT(&client_obj->mux_protected.interface_tailq); TAILQ_INIT(&client_obj->dynamic.done_ctrl_xfer_tailq); client_obj->constant.event_sem = event_sem; - client_obj->constant.event_callback = client_config->client_event_callback; - client_obj->constant.callback_arg = client_config->callback_arg; + client_obj->constant.event_callback = client_config->async.client_event_callback; + client_obj->constant.callback_arg = client_config->async.callback_arg; client_obj->constant.event_msg_queue = event_msg_queue; //Add client to the host library's list of clients @@ -819,10 +833,16 @@ esp_err_t usb_host_device_free_all(void) HOST_EXIT_CRITICAL(); esp_err_t ret; ret = usbh_dev_mark_all_free(); - //Wait for USB_HOST_LIB_EVENT_FLAGS_ALL_FREE to confirm all devices free + //If ESP_ERR_NOT_FINISHED is returned, caller must wait for USB_HOST_LIB_EVENT_FLAGS_ALL_FREE to confirm all devices are free return ret; } +esp_err_t usb_host_device_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret) +{ + HOST_CHECK(dev_addr_list != NULL && num_dev_ret != NULL, ESP_ERR_INVALID_ARG); + return usbh_dev_addr_list_fill(list_len, dev_addr_list, num_dev_ret); +} + // ------------------------------------------------- Device Requests --------------------------------------------------- // ------------------- Cached Requests --------------------- @@ -1266,7 +1286,8 @@ esp_err_t usb_host_transfer_submit_control(usb_host_client_handle_t client_hdl, usb_device_info_t dev_info; ESP_ERROR_CHECK(usbh_dev_get_info(dev_hdl, &dev_info)); HOST_CHECK(transfer_check(transfer, USB_TRANSFER_TYPE_CTRL, dev_info.bMaxPacketSize0, xfer_is_in), ESP_ERR_INVALID_ARG); - HOST_CHECK(transfer->bEndpointAddress == 0, ESP_ERR_INVALID_ARG); + //Control transfers must be targeted at EP 0 + HOST_CHECK((transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_NUM_MASK) == 0, ESP_ERR_INVALID_ARG); //Save client handle into URB urb_t *urb_obj = __containerof(transfer, urb_t, transfer); urb_obj->usb_host_client = (void *)client_hdl; diff --git a/components/usb/usbh.c b/components/usb/usbh.c index 0952492c6b..ab72e97fb4 100644 --- a/components/usb/usbh.c +++ b/components/usb/usbh.c @@ -468,6 +468,36 @@ esp_err_t usbh_process(void) // --------------------- Device Pool ----------------------- +esp_err_t usbh_dev_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *num_dev_ret) +{ + USBH_CHECK(dev_addr_list != NULL && num_dev_ret != NULL, ESP_ERR_INVALID_ARG); + USBH_ENTER_CRITICAL(); + int num_filled = 0; + device_t *dev_obj; + //Fill list with devices from idle tailq + TAILQ_FOREACH(dev_obj, &p_usbh_obj->dynamic.devs_idle_tailq, dynamic.tailq_entry) { + if (num_filled < list_len) { + dev_addr_list[num_filled] = dev_obj->constant.address; + num_filled++; + } else { + break; + } + } + //Fill list with devices from pending tailq + TAILQ_FOREACH(dev_obj, &p_usbh_obj->dynamic.devs_pending_tailq, dynamic.tailq_entry) { + if (num_filled < list_len) { + dev_addr_list[num_filled] = dev_obj->constant.address; + num_filled++; + } else { + break; + } + } + USBH_EXIT_CRITICAL(); + //Write back number of devices filled + *num_dev_ret = num_filled; + return ESP_OK; +} + esp_err_t usbh_dev_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl) { USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG); @@ -547,6 +577,7 @@ esp_err_t usbh_dev_mark_all_free(void) Note: We manually traverse the list because we need to add/remove items while traversing */ bool call_notif_cb = false; + bool wait_for_free = false; for (int i = 0; i < 2; i++) { device_t *dev_obj_cur; device_t *dev_obj_next; @@ -568,6 +599,7 @@ esp_err_t usbh_dev_mark_all_free(void) //Device is still opened. Just mark it as waiting to be closed dev_obj_cur->dynamic.flags.waiting_close = 1; } + wait_for_free = true; //As long as there is still a device, we need to wait for an event indicating it is freed dev_obj_cur = dev_obj_next; } } @@ -576,7 +608,7 @@ esp_err_t usbh_dev_mark_all_free(void) if (call_notif_cb) { p_usbh_obj->constant.notif_cb(USB_NOTIF_SOURCE_USBH, false, p_usbh_obj->constant.notif_cb_arg); } - return ESP_OK; + return (wait_for_free) ? ESP_ERR_NOT_FINISHED : ESP_OK; } // ------------------- Single Device ----------------------