瀏覽代碼

usb: Hub Driver Update and Refactor

Hub Driver is refactored as follows:

This commit update and refactors the Hub Driver as follows:

- Refactored enumeration state machine and stage functions
    - Enumeration stage is now incremented
    - Combined transfer stages of enumeration into common functions
- Comments updated
- Fixed usbh_hal_disable_debounce_lock() that would cause root_port_handle_events()
    to fail the HCD_PORT_CMD_RESET call because the previous port connection interrupt
    was not cleared.

The following features were added to the Hub Driver

- Enumeration config descriptor is now fetched in two separate stages
    - Header is fetched first to determine the wTotalLength of the descriptor
    - Fetching the full descriptor will request exactly wTotalLength bytes
    - This works around some non-compliant devices that will babble/return zero
        when requesting a length > wTotalLength
    - Closes https://github.com/espressif/esp-idf/issues/7799
- Enumeration now stores string descriptors
    - The Manufacturer, Product, and Serial Number string descriptors are
        now read and stored during enumeration
    - String descriptors are now part of usb_device_info_t
- Added unit test to test enumeration
Darian Leung 4 年之前
父節點
當前提交
ea6de613bf

+ 1 - 1
components/hal/include/hal/usbh_hal.h

@@ -494,7 +494,7 @@ static inline void usbh_hal_disable_debounce_lock(usbh_hal_context_t *hal)
     hal->flags.dbnc_lock_enabled = 0;
     //Clear Conenction and disconenction interrupt in case it triggered again
     usb_ll_intr_clear(hal->dev, USB_LL_INTR_CORE_DISCONNINT);
-    usbh_ll_hprt_intr_clear(hal->dev, USBH_LL_INTR_HPRT_PRTENCHNG);
+    usbh_ll_hprt_intr_clear(hal->dev, USBH_LL_INTR_HPRT_PRTCONNDET);
     //Reenable the hprt (connection) and disconnection interrupts
     usb_ll_en_intrs(hal->dev, USB_LL_INTR_CORE_PRTINT | USB_LL_INTR_CORE_DISCONNINT);
 }

文件差異過大導致無法顯示
+ 609 - 317
components/usb/hub.c


+ 18 - 0
components/usb/include/usb/usb_host.h

@@ -72,6 +72,16 @@ typedef struct {
     };
 } usb_host_client_event_msg_t;
 
+// ------------------------ Info ---------------------------
+
+/**
+ * @brief Current information about the USB Host Library obtained via usb_host_lib_info()
+ */
+typedef struct {
+    int num_devices;    /**< Current number of connected (and enumerated) devices */
+    int num_clients;    /**< Current number of registered clients */
+} usb_host_lib_info_t;
+
 // ---------------------- Callbacks ------------------------
 
 /**
@@ -166,6 +176,14 @@ esp_err_t usb_host_lib_handle_events(TickType_t timeout_ticks, uint32_t *event_f
  */
 esp_err_t usb_host_lib_unblock(void);
 
+/**
+ * @brief Get current information about the USB Host Library
+ *
+ * @param[out] info_ret USB Host Library Information
+ * @return esp_err_t
+ */
+esp_err_t usb_host_lib_info(usb_host_lib_info_t *info_ret);
+
 // ------------------------------------------------ Client Functions ---------------------------------------------------
 
 /**

+ 5 - 5
components/usb/include/usb/usb_types_ch9.h

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -217,11 +217,11 @@ _Static_assert(sizeof(usb_setup_packet_t) == USB_SETUP_PACKET_SIZE, "Size of usb
 /**
  * @brief Initializer for a request to get an string descriptor
  */
-#define USB_SETUP_PACKET_INIT_GET_STR_DESC(setup_pkt_ptr, string_index, desc_len) ({ \
+#define USB_SETUP_PACKET_INIT_GET_STR_DESC(setup_pkt_ptr, string_index, lang_id, desc_len) ({ \
     (setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \
     (setup_pkt_ptr)->bRequest = USB_B_REQUEST_GET_DESCRIPTOR; \
     (setup_pkt_ptr)->wValue = (USB_W_VALUE_DT_STRING << 8) | ((string_index) & 0xFF); \
-    (setup_pkt_ptr)->wIndex = 0; \
+    (setup_pkt_ptr)->wIndex = (lang_id); \
     (setup_pkt_ptr)->wLength = (desc_len); \
 })
 
@@ -465,7 +465,7 @@ _Static_assert(sizeof(usb_ep_desc_t) == USB_EP_DESC_SIZE, "Size of usb_ep_desc_t
 /**
  * @brief Size of a short USB string descriptor in bytes
  */
-#define USB_STR_DESC_SIZE       4
+#define USB_STR_DESC_SIZE       2
 
 /**
  * @brief Structure representing a USB string descriptor
@@ -474,7 +474,7 @@ typedef union {
     struct {
         uint8_t bLength;                    /**< Size of the descriptor in bytes */
         uint8_t bDescriptorType;            /**< STRING Descriptor Type */
-        uint16_t wData[1];                  /**< UTF-16LE encoded */
+        uint16_t wData[0];                  /**< UTF-16LE encoded */
     } USB_DESC_ATTR;
     uint8_t val[USB_STR_DESC_SIZE];
 } usb_str_desc_t;

+ 7 - 4
components/usb/include/usb/usb_types_stack.h

@@ -49,10 +49,13 @@ typedef struct usb_device_handle_s * usb_device_handle_t;
  * @brief Basic information of an enumerated device
  */
 typedef struct {
-    usb_speed_t speed;                  /**< Device's speed */
-    uint8_t dev_addr;                   /**< Device's address */
-    uint8_t bMaxPacketSize0;            /**< The maximum packet size of the device's default endpoint */
-    uint8_t bConfigurationValue;        /**< Device's current configuration number */
+    usb_speed_t speed;                              /**< Device's speed */
+    uint8_t dev_addr;                               /**< Device's address */
+    uint8_t bMaxPacketSize0;                        /**< The maximum packet size of the device's default endpoint */
+    uint8_t bConfigurationValue;                    /**< Device's current configuration number */
+    const usb_str_desc_t *str_desc_manufacturer;    /**< Pointer to Manufacturer string descriptor (can be NULL) */
+    const usb_str_desc_t *str_desc_product;         /**< Pointer to Product string descriptor (can be NULL) */
+    const usb_str_desc_t *str_desc_serial_num;      /**< Pointer to Serial Number string descriptor (can be NULL) */
 } usb_device_info_t;
 
 // ------------------------------------------------ Transfer Related ---------------------------------------------------

+ 20 - 10
components/usb/private_include/usbh.h

@@ -119,6 +119,15 @@ esp_err_t usbh_uninstall(void);
  */
 esp_err_t usbh_process(void);
 
+/**
+ * @brief Get the current number of devices
+ *
+ * @note This function can block
+ * @param[out] num_devs_ret Current number of devices
+ * @return esp_err_t
+ */
+esp_err_t usbh_num_devs(int *num_devs_ret);
+
 // ------------------------------------------------ Device Functions ---------------------------------------------------
 
 // --------------------- Device Pool -----------------------
@@ -337,26 +346,26 @@ esp_err_t usbh_hub_dev_port_disabled(usb_device_handle_t dev_hdl);
 // ----------------- Enumeration Related -------------------
 
 /**
- * @brief Fill the enumerating device's descriptor
+ * @brief Assign the enumerating device's address
  *
  * @note Hub Driver only
  * @note Must call in sequence
  * @param[in] dev_hdl Device handle
- * @param device_desc
+ * @param dev_addr
  * @return esp_err_t
  */
-esp_err_t usbh_hub_enum_fill_dev_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t *device_desc);
+esp_err_t usbh_hub_enum_fill_dev_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr);
 
 /**
- * @brief Assign the enumerating device's address
+ * @brief Fill the enumerating device's descriptor
  *
  * @note Hub Driver only
  * @note Must call in sequence
  * @param[in] dev_hdl Device handle
- * @param dev_addr
+ * @param device_desc
  * @return esp_err_t
  */
-esp_err_t usbh_hub_enum_fill_dev_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr);
+esp_err_t usbh_hub_enum_fill_dev_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t *device_desc);
 
 /**
  * @brief Fill the enumerating device's active configuration descriptor
@@ -371,15 +380,16 @@ esp_err_t usbh_hub_enum_fill_dev_addr(usb_device_handle_t dev_hdl, uint8_t dev_a
 esp_err_t usbh_hub_enum_fill_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t *config_desc_full);
 
 /**
- * @brief Assign the enumerating device's active configuration number
+ * @brief Fill one of the string descriptors of the enumerating device
  *
  * @note Hub Driver only
  * @note Must call in sequence
- * @param[in] dev_hdl Device handle
- * @param bConfigurationValue
+ * @param dev_hdl Device handle
+ * @param str_desc Pointer to string descriptor
+ * @param select Select which string descriptor. 0/1/2 for Manufacturer/Product/Serial Number string descriptors respecitvely
  * @return esp_err_t
  */
-esp_err_t usbh_hub_enum_fill_config_num(usb_device_handle_t dev_hdl, uint8_t bConfigurationValue);
+esp_err_t usbh_hub_enum_fill_str_desc(usb_device_handle_t dev_hdl, const usb_str_desc_t *str_desc, int select);
 
 /**
  * @brief Indicate the device enumeration is completed

+ 53 - 1
components/usb/test/common/test_usb_mock_classes.c

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -14,6 +14,49 @@
 
 const char *MSC_CLIENT_TAG = "MSC Client";
 
+const uint8_t mock_msc_scsi_dev_desc[] = {
+    0x12, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, 0x5F, 0x12, 0x8A, 0xC0, 0x00, 0x01, 0x01, 0x02, 0x03, 0x01,
+};
+
+const uint8_t mock_msc_scsi_config_desc[] = {
+    0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0xF0, 0x09, 0x04, 0x00, 0x00, 0x02, 0x08, 0x06, 0x50, 0x00, 0x07,
+    0x05, 0x01, 0x02, 0x40, 0x00, 0x01, 0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x01,
+};
+
+
+const uint8_t mock_msc_scsi_str_desc_manu[] = {
+    0x0c, 0x03, 0x41, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00,
+};
+
+const uint8_t mock_msc_scsi_str_desc_prod[] = {
+    0x2c, 0x03, 0x41, 0x00, 0x44, 0x00, 0x41, 0x00, 0x54, 0x00, 0x41, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42,
+    0x00, 0x20, 0x00, 0x46, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x20, 0x00, 0x44, 0x00, 0x72, 0x00,
+    0x69, 0x00, 0x76, 0x00, 0x65, 0x00,
+};
+
+const uint8_t mock_msc_scsi_str_desc_ser_num[] = {
+    0x22, 0x03, 0x31, 0x00, 0x33, 0x00, 0x43, 0x00, 0x32, 0x00, 0x38, 0x00, 0x31, 0x00, 0x36, 0x00, 0x35, 0x00, 0x38,
+    0x00, 0x32, 0x00, 0x31, 0x00, 0x38, 0x00, 0x30, 0x00, 0x30, 0x00, 0x38, 0x00, 0x45, 0x00,
+};
+
+const usb_ep_desc_t mock_msc_scsi_bulk_out_ep_desc = {
+    .bLength = sizeof(usb_ep_desc_t),
+    .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT,
+    .bEndpointAddress = 0x01,       //EP 1 OUT
+    .bmAttributes = USB_BM_ATTRIBUTES_XFER_BULK,
+    .wMaxPacketSize = 64,           //MPS of 64 bytes
+    .bInterval = 1,
+};
+
+const usb_ep_desc_t mock_msc_scsi_bulk_in_ep_desc = {
+    .bLength = sizeof(usb_ep_desc_t),
+    .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT,
+    .bEndpointAddress = 0x82,       //EP 2 IN
+    .bmAttributes = USB_BM_ATTRIBUTES_XFER_BULK,
+    .wMaxPacketSize = 64,           //MPS of 64 bytes
+    .bInterval = 1,
+};
+
 void mock_msc_scsi_init_cbw(mock_msc_bulk_cbw_t *cbw, bool is_read, int offset, int num_sectors, uint32_t tag)
 {
     cbw->dCBWSignature = 0x43425355;    //Fixed value
@@ -60,6 +103,15 @@ bool mock_msc_scsi_check_csw(mock_msc_bulk_csw_t *csw, uint32_t tag_expect)
 
 // ---------------------------------------------------- HID Mouse ------------------------------------------------------
 
+const usb_ep_desc_t mock_hid_mouse_in_ep_desc = {
+    .bLength = sizeof(usb_ep_desc_t),
+    .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT,
+    .bEndpointAddress = 0x81,       //EP 1 IN
+    .bmAttributes = USB_BM_ATTRIBUTES_XFER_INT,
+    .wMaxPacketSize = 4,            //MPS of 4 bytes
+    .bInterval = 10,                //Interval of 10ms
+};
+
 void mock_hid_process_report(mock_hid_mouse_report_t *report, int iter)
 {
     static int x_pos = 0;

+ 11 - 26
components/usb/test/common/test_usb_mock_classes.h

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -88,23 +88,14 @@ Configuration Descriptor:
 If you're using a flash driver with different endpoints, modify the endpoint descriptors below.
 */
 
-static const usb_ep_desc_t mock_msc_scsi_bulk_out_ep_desc = {
-    .bLength = sizeof(usb_ep_desc_t),
-    .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT,
-    .bEndpointAddress = 0x01,       //EP 1 OUT
-    .bmAttributes = USB_BM_ATTRIBUTES_XFER_BULK,
-    .wMaxPacketSize = 64,           //MPS of 64 bytes
-    .bInterval = 1,
-};
-
-static const usb_ep_desc_t mock_msc_scsi_bulk_in_ep_desc = {
-    .bLength = sizeof(usb_ep_desc_t),
-    .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT,
-    .bEndpointAddress = 0x82,       //EP 2 IN
-    .bmAttributes = USB_BM_ATTRIBUTES_XFER_BULK,
-    .wMaxPacketSize = 64,           //MPS of 64 bytes
-    .bInterval = 1,
-};
+//Constant descriptors
+extern const uint8_t mock_msc_scsi_dev_desc[];
+extern const uint8_t mock_msc_scsi_config_desc[];
+extern const uint8_t mock_msc_scsi_str_desc_manu[];
+extern const uint8_t mock_msc_scsi_str_desc_prod[];
+extern const uint8_t mock_msc_scsi_str_desc_ser_num[];
+extern const usb_ep_desc_t mock_msc_scsi_bulk_out_ep_desc;
+extern const usb_ep_desc_t mock_msc_scsi_bulk_in_ep_desc;
 
 #define MOCK_MSC_SCSI_DEV_ID_VENDOR     0x125F
 #define MOCK_MSC_SCSI_DEV_ID_PRODUCT    0xc08A
@@ -246,14 +237,8 @@ Device Descriptor:
 
 If you're using another mice with different endpoints, modify the endpoint descriptor below
 */
-static const usb_ep_desc_t mock_hid_mouse_in_ep_desc = {
-    .bLength = sizeof(usb_ep_desc_t),
-    .bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT,
-    .bEndpointAddress = 0x81,       //EP 1 IN
-    .bmAttributes = USB_BM_ATTRIBUTES_XFER_INT,
-    .wMaxPacketSize = 4,            //MPS of 4 bytes
-    .bInterval = 10,                //Interval of 10ms
-};
+
+extern  const usb_ep_desc_t mock_hid_mouse_in_ep_desc;
 
 #define MOCK_HID_MOUSE_DEV_ID_VENDOR        0x413C
 #define MOCK_HID_MOUSE_DEV_ID_PRODUCT       0x301A

+ 3 - 1
components/usb/test/usb_host/msc_client.h

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -19,3 +19,5 @@ typedef struct {
 void msc_client_async_seq_task(void *arg);
 
 void msc_client_async_dconn_task(void *arg);
+
+void msc_client_async_enum_task(void *arg);

+ 169 - 0
components/usb/test/usb_host/msc_client_async_enum.c

@@ -0,0 +1,169 @@
+/*
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_err.h"
+#include "esp_log.h"
+#include "test_usb_mock_classes.h"
+#include "test_usb_common.h"
+#include "msc_client.h"
+#include "usb/usb_host.h"
+#include "unity.h"
+#include "test_utils.h"
+
+/*
+Implementation of an asynchronous MSC client used for USB Host enumeration test.
+
+- The MSC client will:
+    - Register itself as a client
+    - Receive USB_HOST_CLIENT_EVENT_NEW_DEV event message, and open the device
+    - Check the device and configuration descriptor of the device
+    - Check the device's information
+    - Close device
+    - Deregister MSC client
+*/
+
+typedef enum {
+    TEST_STAGE_WAIT_CONN,
+    TEST_STAGE_DEV_OPEN,
+    TEST_STAGE_CHECK_DEV_DESC,
+    TEST_STAGE_CHECK_CONFIG_DESC,
+    TEST_STAGE_CHECK_STR_DESC,
+    TEST_STAGE_DEV_CLOSE,
+} test_stage_t;
+
+typedef struct {
+    test_stage_t cur_stage;
+    test_stage_t next_stage;
+    uint8_t dev_addr_to_open;
+    usb_host_client_handle_t client_hdl;
+    usb_device_handle_t dev_hdl;
+} msc_client_obj_t;
+
+static void msc_client_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg)
+{
+    msc_client_obj_t *msc_obj = (msc_client_obj_t *)arg;
+    switch (event_msg->event) {
+        case USB_HOST_CLIENT_EVENT_NEW_DEV:
+            TEST_ASSERT_EQUAL(TEST_STAGE_WAIT_CONN, msc_obj->cur_stage);
+            msc_obj->next_stage = TEST_STAGE_DEV_OPEN;
+            msc_obj->dev_addr_to_open = event_msg->new_dev.address;
+            break;
+        default:
+            abort();    //Should never occur in this test
+            break;
+
+    }
+}
+
+void msc_client_async_enum_task(void *arg)
+{
+    msc_client_obj_t msc_obj;
+    msc_obj.cur_stage = TEST_STAGE_WAIT_CONN;
+    msc_obj.next_stage = TEST_STAGE_WAIT_CONN;
+    msc_obj.client_hdl = NULL;
+    msc_obj.dev_addr_to_open = 0;
+    msc_obj.dev_hdl = NULL;
+
+    //Register client
+    usb_host_client_config_t client_config = {
+        .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));
+
+    //Wait to be started by main thread
+    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
+    ESP_LOGD(MSC_CLIENT_TAG, "Starting");
+
+    bool exit_loop = false;
+    bool skip_event_handling = false;
+    while (!exit_loop) {
+        if (!skip_event_handling) {
+            TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_handle_events(msc_obj.client_hdl, portMAX_DELAY));
+        }
+        skip_event_handling = false;
+        if (msc_obj.cur_stage == msc_obj.next_stage) {
+            continue;
+        }
+        msc_obj.cur_stage = msc_obj.next_stage;
+
+        switch (msc_obj.cur_stage) {
+            case TEST_STAGE_DEV_OPEN: {
+                ESP_LOGD(MSC_CLIENT_TAG, "Open");
+                //Open the device
+                TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_open(msc_obj.client_hdl, msc_obj.dev_addr_to_open, &msc_obj.dev_hdl));
+                msc_obj.next_stage = TEST_STAGE_CHECK_DEV_DESC;
+                skip_event_handling = true; //Need to execute TEST_STAGE_CHECK_DEV_DESC
+                break;
+            }
+            case TEST_STAGE_CHECK_DEV_DESC: {
+                //Check the device descriptor
+                const usb_device_desc_t *device_desc;
+                const usb_device_desc_t *device_desc_ref = (const usb_device_desc_t *)mock_msc_scsi_dev_desc;
+                TEST_ASSERT_EQUAL(ESP_OK, usb_host_get_device_descriptor(msc_obj.dev_hdl, &device_desc));
+                TEST_ASSERT_EQUAL(device_desc_ref->bLength, device_desc->bLength);
+                TEST_ASSERT_EQUAL(0, memcmp(device_desc_ref, device_desc, device_desc_ref->bLength));
+                msc_obj.next_stage = TEST_STAGE_CHECK_CONFIG_DESC;
+                skip_event_handling = true; //Need to execute TEST_STAGE_CHECK_CONFIG_DESC
+                break;
+            }
+
+            case TEST_STAGE_CHECK_CONFIG_DESC: {
+                //Check the configuration descriptor
+                const usb_config_desc_t *config_desc;
+                const usb_config_desc_t *config_desc_ref = (const usb_config_desc_t *)mock_msc_scsi_config_desc;
+                TEST_ASSERT_EQUAL(ESP_OK, usb_host_get_active_config_descriptor(msc_obj.dev_hdl, &config_desc));
+                TEST_ASSERT_EQUAL(config_desc_ref->wTotalLength, config_desc->wTotalLength);
+                TEST_ASSERT_EQUAL(0, memcmp(config_desc_ref, config_desc, config_desc_ref->wTotalLength));
+                msc_obj.next_stage = TEST_STAGE_CHECK_STR_DESC;
+                skip_event_handling = true; //Need to execute TEST_STAGE_CHECK_STR_DESC
+                break;
+            }
+            case TEST_STAGE_CHECK_STR_DESC: {
+                usb_device_info_t dev_info;
+                TEST_ASSERT_EQUAL(ESP_OK, usb_host_device_info(msc_obj.dev_hdl, &dev_info));
+                //Check manufacturer string descriptors
+                const usb_str_desc_t *manu_str_desc_ref = (const usb_str_desc_t *)mock_msc_scsi_str_desc_manu;
+                const usb_str_desc_t *product_str_desc_ref = (const usb_str_desc_t *)mock_msc_scsi_str_desc_prod;
+                const usb_str_desc_t *ser_num_str_desc_ref = (const usb_str_desc_t *)mock_msc_scsi_str_desc_ser_num;
+                TEST_ASSERT_EQUAL(manu_str_desc_ref->bLength, dev_info.str_desc_manufacturer->bLength);
+                TEST_ASSERT_EQUAL(product_str_desc_ref->bLength, dev_info.str_desc_product->bLength);
+                TEST_ASSERT_EQUAL(ser_num_str_desc_ref->bLength, dev_info.str_desc_serial_num->bLength);
+                TEST_ASSERT_EQUAL(0, memcmp(manu_str_desc_ref, dev_info.str_desc_manufacturer , manu_str_desc_ref->bLength));
+                TEST_ASSERT_EQUAL(0, memcmp(product_str_desc_ref, dev_info.str_desc_product , manu_str_desc_ref->bLength));
+                TEST_ASSERT_EQUAL(0, memcmp(ser_num_str_desc_ref, dev_info.str_desc_serial_num , manu_str_desc_ref->bLength));
+                //Get dev info and compare
+                msc_obj.next_stage = TEST_STAGE_DEV_CLOSE;
+                skip_event_handling = true; //Need to execute TEST_STAGE_DEV_CLOSE
+                break;
+            }
+
+            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;
+                break;
+            }
+            default:
+                abort();
+                break;
+        }
+    }
+    //Free transfers and deregister the client
+    TEST_ASSERT_EQUAL(ESP_OK, usb_host_client_deregister(msc_obj.client_hdl));
+    ESP_LOGD(MSC_CLIENT_TAG, "Done");
+    vTaskDelete(NULL);
+}

+ 37 - 0
components/usb/test/usb_host/test_usb_host_plugging.c

@@ -115,3 +115,40 @@ TEST_CASE("Test USB Host sudden disconnection (single client)", "[usb_host][igno
     ESP_ERROR_CHECK(usb_host_uninstall());
     test_usb_deinit_phy();  //Deinitialize the internal USB PHY after testing
 }
+
+TEST_CASE("Test USB Host enumeration", "[usb_host][ignore]")
+{
+    test_usb_init_phy();    //Initialize the internal USB PHY and USB Controller for testing
+    //Install USB Host
+    usb_host_config_t host_config = {
+        .skip_phy_setup = true,     //test_usb_init_phy() will already have setup the internal USB PHY for us
+        .intr_flags = ESP_INTR_FLAG_LEVEL1,
+    };
+    ESP_ERROR_CHECK(usb_host_install(&host_config));
+    printf("Installed\n");
+
+    //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);
+    //Start the task
+    xTaskNotifyGive(task_hdl);
+
+    while (1) {
+        //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());
+        }
+        if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
+            break;
+        }
+    }
+
+    //Short delay to allow task to be cleaned up
+    vTaskDelay(10);
+    //Clean up USB Host
+    ESP_ERROR_CHECK(usb_host_uninstall());
+    test_usb_deinit_phy();  //Deinitialize the internal USB PHY after testing
+}

+ 17 - 0
components/usb/usb_host.c

@@ -542,6 +542,23 @@ esp_err_t usb_host_lib_unblock(void)
     return ESP_OK;
 }
 
+esp_err_t usb_host_lib_info(usb_host_lib_info_t *info_ret)
+{
+    HOST_CHECK(info_ret != NULL, ESP_ERR_INVALID_ARG);
+    int num_devs_temp;
+    int num_clients_temp;
+    HOST_ENTER_CRITICAL();
+    HOST_CHECK_FROM_CRIT(p_host_lib_obj != NULL, ESP_ERR_INVALID_STATE);
+    num_clients_temp = p_host_lib_obj->dynamic.flags.num_clients;
+    HOST_EXIT_CRITICAL();
+    usbh_num_devs(&num_devs_temp);
+
+    //Write back return values
+    info_ret->num_devices = num_devs_temp;
+    info_ret->num_clients = num_clients_temp;
+    return ESP_OK;
+}
+
 // ------------------------------------------------ Client Functions ---------------------------------------------------
 
 // ----------------------- Private -------------------------

+ 56 - 1
components/usb/usbh.c

@@ -68,6 +68,9 @@ struct device_s {
         usb_speed_t speed;
         const usb_device_desc_t *desc;
         const usb_config_desc_t *config_desc;
+        const usb_str_desc_t *str_desc_manu;
+        const usb_str_desc_t *str_desc_product;
+        const usb_str_desc_t *str_desc_ser_num;
     } constant;
 };
 
@@ -171,6 +174,16 @@ static void device_free(device_t *dev_obj)
     if (dev_obj->constant.config_desc) {
         heap_caps_free((usb_config_desc_t *)dev_obj->constant.config_desc);
     }
+    //String descriptors might not have been allocated (in case of early enumeration failure)
+    if (dev_obj->constant.str_desc_manu) {
+        heap_caps_free((usb_str_desc_t *)dev_obj->constant.str_desc_manu);
+    }
+    if (dev_obj->constant.str_desc_product) {
+        heap_caps_free((usb_str_desc_t *)dev_obj->constant.str_desc_product);
+    }
+    if (dev_obj->constant.str_desc_ser_num) {
+        heap_caps_free((usb_str_desc_t *)dev_obj->constant.str_desc_ser_num);
+    }
     heap_caps_free((usb_device_desc_t *)dev_obj->constant.desc);
     ESP_ERROR_CHECK(hcd_pipe_free(dev_obj->constant.default_pipe));
     heap_caps_free(dev_obj);
@@ -458,6 +471,15 @@ esp_err_t usbh_process(void)
     return ESP_OK;
 }
 
+esp_err_t usbh_num_devs(int *num_devs_ret)
+{
+    USBH_CHECK(num_devs_ret != NULL, ESP_ERR_INVALID_ARG);
+    xSemaphoreTake(p_usbh_obj->constant.mux_lock, portMAX_DELAY);
+    *num_devs_ret = p_usbh_obj->mux_protected.num_device;
+    xSemaphoreGive(p_usbh_obj->constant.mux_lock);
+    return ESP_OK;
+}
+
 // ------------------------------------------------ Device Functions ---------------------------------------------------
 
 // --------------------- Device Pool -----------------------
@@ -640,6 +662,10 @@ esp_err_t usbh_dev_get_info(usb_device_handle_t dev_hdl, usb_device_info_t *dev_
     USBH_EXIT_CRITICAL();
     assert(dev_obj->constant.config_desc);
     dev_info->bConfigurationValue = dev_obj->constant.config_desc->bConfigurationValue;
+    //String descriptors are allowed to be NULL as not all devices support them
+    dev_info->str_desc_manufacturer = dev_obj->constant.str_desc_manu;
+    dev_info->str_desc_product = dev_obj->constant.str_desc_product;
+    dev_info->str_desc_serial_num = dev_obj->constant.str_desc_ser_num;
     ret = ESP_OK;
 exit:
     return ret;
@@ -964,12 +990,41 @@ esp_err_t usbh_hub_enum_fill_config_desc(usb_device_handle_t dev_hdl, const usb_
     }
     //Copy the configuration descriptor
     memcpy(config_desc, config_desc_full, config_desc_full->wTotalLength);
-    //Assign the config object to the device object
+    //Assign the config desc to the device object
     assert(dev_obj->constant.config_desc == NULL);
     dev_obj->constant.config_desc = config_desc;
     return ESP_OK;
 }
 
+esp_err_t usbh_hub_enum_fill_str_desc(usb_device_handle_t dev_hdl, const usb_str_desc_t *str_desc, int select)
+{
+    USBH_CHECK(dev_hdl != NULL && str_desc != NULL && (select >= 0 && select < 3), ESP_ERR_INVALID_ARG);
+    device_t *dev_obj = (device_t *)dev_hdl;
+    //Allocate memory to store the manufacturer string descriptor
+    usb_str_desc_t *str_desc_fill = heap_caps_malloc(str_desc->bLength, MALLOC_CAP_DEFAULT);
+    if (str_desc_fill == NULL) {
+        return ESP_ERR_NO_MEM;
+    }
+    //Copy the string descriptor
+    memcpy(str_desc_fill, str_desc, str_desc->bLength);
+    //Assign filled string descriptor to the device object
+    switch (select) {
+        case 0:
+            assert(dev_obj->constant.str_desc_manu == NULL);
+            dev_obj->constant.str_desc_manu = str_desc_fill;
+            break;
+        case 1:
+            assert(dev_obj->constant.str_desc_product == NULL);
+            dev_obj->constant.str_desc_product = str_desc_fill;
+            break;
+        default:    //2
+            assert(dev_obj->constant.str_desc_ser_num == NULL);
+            dev_obj->constant.str_desc_ser_num = str_desc_fill;
+            break;
+    }
+    return ESP_OK;
+}
+
 esp_err_t usbh_hub_enum_done(usb_device_handle_t dev_hdl)
 {
     USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);

+ 2 - 2
examples/peripherals/usb/host/msc/components/msc/src/msc_host.c

@@ -1,5 +1,5 @@
 /*
- * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
  *
  * SPDX-License-Identifier: Apache-2.0
  */
@@ -432,7 +432,7 @@ static esp_err_t msc_read_string_desc(msc_device_t *dev, uint8_t index, wchar_t
     }
 
     usb_transfer_t *xfer = dev->xfer;
-    USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)xfer->data_buffer, index, 64);
+    USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)xfer->data_buffer, index, 0x409, 64);
     MSC_RETURN_ON_ERROR( msc_control_transfer(dev, xfer, USB_SETUP_PACKET_SIZE + 64) );
 
     usb_standard_desc_t *desc = (usb_standard_desc_t *)(xfer->data_buffer + USB_SETUP_PACKET_SIZE);

部分文件因文件數量過多而無法顯示