diff --git a/components/hal/include/hal/usbh_hal.h b/components/hal/include/hal/usbh_hal.h index bec00b30cf..5326deb2dc 100644 --- a/components/hal/include/hal/usbh_hal.h +++ b/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); } diff --git a/components/usb/hub.c b/components/usb/hub.c index f970c41b3f..9aed57fad8 100644 --- a/components/usb/hub.c +++ b/components/usb/hub.c @@ -1,11 +1,13 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "sdkconfig.h" #include +#include +#include #include "freertos/FreeRTOS.h" #include "freertos/portmacro.h" #include "esp_err.h" @@ -14,9 +16,10 @@ #include "usb_private.h" #include "hcd.h" #include "hub.h" +#include "usb/usb_helpers.h" /* -Implementation of the HUB driver that only supports the root hub with a single port. Therefore, we currently don't +Implementation of the HUB driver that only supports the Root Hub with a single port. Therefore, we currently don't implement the bare minimum to control the root HCD port. */ @@ -29,43 +32,133 @@ implement the bare minimum to control the root HCD port. #define HUB_ROOT_HCD_PORT_FIFO_BIAS HCD_PORT_FIFO_BIAS_BALANCED #endif -#define ENUM_CTRL_TRANSFER_MAX_LEN CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE +#define ENUM_CTRL_TRANSFER_MAX_DATA_LEN CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE #define ENUM_DEV_ADDR 1 //Device address used in enumeration #define ENUM_CONFIG_INDEX 0 //Index of the first configuration of the device -#define ENUM_DEV_DESC_REQ_SIZE 64 //Worst case size for device descriptor request +#define ENUM_SHORT_DESC_REQ_LEN 8 //Number of bytes to request when getting a short descriptor (just enough to get bMaxPacketSize0 or wTotalLength) +#define ENUM_WORST_CASE_MPS_LS 8 //The worst case MPS of EP0 for a LS device +#define ENUM_WORST_CASE_MPS_FS 64 //The worst case MPS of EP0 for a FS device #define ENUM_LOW_SPEED_MPS 8 //Worst case MPS for the default endpoint of a low-speed device #define ENUM_FULL_SPEED_MPS 64 //Worst case MPS for the default endpoint of a full-speed device +#define ENUM_LANGID 0x409 //Current enumeration only supports English (United States) string descriptors #define HUB_DRIVER_FLAG_ACTION_ROOT_EVENT 0x01 #define HUB_DRIVER_FLAG_ACTION_ENUM_EVENT 0x02 #define HUB_DRIVER_FLAG_ACTION_PORT_RECOVER 0x04 +/** + * @brief Hub driver states + * + * These states represent a Hub driver that only has a single port (the root port) + */ typedef enum { - HUB_DRIVER_STATE_INSTALLED, - HUB_DRIVER_STATE_ROOT_POWERD, - HUB_DRIVER_STATE_ROOT_ENUMERATING, - HUB_DRIVER_STATE_ROOT_ACTIVE, - HUB_DRIVER_STATE_ROOT_RECOVERY, + HUB_DRIVER_STATE_INSTALLED, /**< Hub driver is installed. Root port is not powered */ + HUB_DRIVER_STATE_ROOT_POWERED, /**< Root port is powered, is not connected */ + HUB_DRIVER_STATE_ROOT_ENUMERATING, /**< A device has connected to the root port and is undergoing enumeration */ + HUB_DRIVER_STATE_ROOT_ACTIVE, /**< The connected device is enumerated */ + HUB_DRIVER_STATE_ROOT_RECOVERY, /**< Root port encountered an error and needs to be recovered */ } hub_driver_state_t; +/** + * @brief Stages of device enumeration listed in their order of execution + * + * - These stages MUST BE LISTED IN THE ORDER OF THEIR EXECUTION as the enumeration will simply increment the current stage + * - If an error occurs at any stage, ENUM_STAGE_CLEANUP_FAILED acts as a common exit stage on failure + * - Must start with 0 as enum is also used as an index + * - The short descriptor stages are used to fetch the start particular descriptors that don't have a fixed length in order to determine the full descriptors length + */ typedef enum { - ENUM_STAGE_NONE, /**< There is no device awaiting enumeration */ - ENUM_STAGE_START, /**< Start of enumeration. Allocates device object */ - ENUM_STAGE_GET_SHORT_DEV_DESC, /**< Getting short device descriptor */ - ENUM_STAGE_GET_SHORT_DEV_DESC_CHECK, /**< Check that short device descriptor was obtained */ - ENUM_STAGE_SECOND_RESET, /**< Doing second reset */ - ENUM_STAGE_SET_ADDR, /**< Setting address */ - ENUM_STAGE_SET_ADDR_CHECK, /**< Check that address was set successful */ - ENUM_STAGE_GET_FULL_DEV_DESC, /**< Getting full device descriptor */ - ENUM_STAGE_GET_FULL_DEV_DESC_CHECK, /**< Check that full device descriptor was obtained */ - ENUM_STAGE_GET_CONFIG_DESC, /**< Getting full configuration descriptor */ - ENUM_STAGE_GET_CONFIG_DESC_CHECK, /**< Check that full configuration descriptor was obtained */ - ENUM_STAGE_SET_CONFIG, /**< Set configuration number */ - ENUM_STAGE_SET_CONFIG_CHECK, /**< Check that configuration number was set */ - ENUM_STAGE_CLEANUP, /**< Clean up successful enumeration. Adds enumerated device to USBH */ - ENUM_STAGE_CLEANUP_FAILED, /**< Cleanup failed enumeration. Free device resources */ + ENUM_STAGE_NONE = 0, /**< There is no device awaiting enumeration. Start requires device connection and first reset. */ + ENUM_STAGE_START, /**< A device has connected and has already been reset once. Allocate a device object in USBH */ + //Basic device enumeration + ENUM_STAGE_GET_SHORT_DEV_DESC, /**< Getting short dev desc (wLength is ENUM_SHORT_DESC_REQ_LEN) */ + ENUM_STAGE_CHECK_SHORT_DEV_DESC, /**< Save bMaxPacketSize0 from the short dev desc. Update the MPS of the enum pipe */ + ENUM_STAGE_SECOND_RESET, /**< Reset the device again (Workaround for old USB devices that get confused by the previous short dev desc request). */ + ENUM_STAGE_SET_ADDR, /**< Send SET_ADDRESS request */ + ENUM_STAGE_CHECK_ADDR, /**< Update the enum pipe's target address */ + ENUM_STAGE_GET_FULL_DEV_DESC, /**< Get the full dev desc */ + ENUM_STAGE_CHECK_FULL_DEV_DESC, /**< Check the full dev desc, fill it into the device object in USBH. Save the string descriptor indexes*/ + ENUM_STAGE_GET_SHORT_CONFIG_DESC, /**< Getting a short config desc (wLength is ENUM_SHORT_DESC_REQ_LEN) */ + ENUM_STAGE_CHECK_SHORT_CONFIG_DESC, /**< Save wTotalLength of the short config desc */ + ENUM_STAGE_GET_FULL_CONFIG_DESC, /**< Get the full config desc (wLength is the saved wTotalLength) */ + ENUM_STAGE_CHECK_FULL_CONFIG_DESC, /**< Check the full config desc, fill it into the device object in USBH */ + ENUM_STAGE_SET_CONFIG, /**< Send SET_CONFIGURATION request */ + ENUM_STAGE_CHECK_CONFIG, /**< Check that SET_CONFIGURATION request was successful */ + //Get string descriptors + ENUM_STAGE_GET_SHORT_LANGID_TABLE, /**< Get the header of the LANGID table string descriptor */ + ENUM_STAGE_CHECK_SHORT_LANGID_TABLE, /**< Save the bLength of the LANGID table string descriptor */ + ENUM_STAGE_GET_FULL_LANGID_TABLE, /**< Get the full LANGID table string descriptor */ + ENUM_STAGE_CHECK_FULL_LANGID_TABLE, /**< Check whether ENUM_LANGID is in the LANGID table */ + ENUM_STAGE_GET_SHORT_MANU_STR_DESC, /**< Get the header of the iManufacturer string descriptor */ + ENUM_STAGE_CHECK_SHORT_MANU_STR_DESC, /**< Save the bLength of the iManufacturer string descriptor */ + ENUM_STAGE_GET_FULL_MANU_STR_DESC, /**< Get the full iManufacturer string descriptor */ + ENUM_STAGE_CHECK_FULL_MANU_STR_DESC, /**< Check and fill the full iManufacturer string descriptor */ + ENUM_STAGE_GET_SHORT_PROD_STR_DESC, /**< Get the header of the string descriptor at index iProduct */ + ENUM_STAGE_CHECK_SHORT_PROD_STR_DESC, /**< Save the bLength of the iProduct string descriptor */ + ENUM_STAGE_GET_FULL_PROD_STR_DESC, /**< Get the full iProduct string descriptor */ + ENUM_STAGE_CHECK_FULL_PROD_STR_DESC, /**< Check and fill the full iProduct string descriptor */ + ENUM_STAGE_GET_SHORT_SER_STR_DESC, /**< Get the header of the string descriptor at index iSerialNumber */ + ENUM_STAGE_CHECK_SHORT_SER_STR_DESC, /**< Save the bLength of the iSerialNumber string descriptor */ + ENUM_STAGE_GET_FULL_SER_STR_DESC, /**< Get the full iSerialNumber string descriptor */ + ENUM_STAGE_CHECK_FULL_SER_STR_DESC, /**< Check and fill the full iSerialNumber string descriptor */ + //Cleanup + ENUM_STAGE_CLEANUP, /**< Clean up after successful enumeration. Adds enumerated device to USBH */ + ENUM_STAGE_CLEANUP_FAILED, /**< Cleanup failed enumeration. Free device resources */ } enum_stage_t; +const char *const enum_stage_strings[] = { + "NONE", + "START", + "GET_SHORT_DEV_DESC", + "CHECK_SHORT_DEV_DESC", + "SECOND_RESET", + "SET_ADDR", + "CHECK_ADDR", + "GET_FULL_DEV_DESC", + "CHECK_FULL_DEV_DESC", + "GET_SHORT_CONFIG_DESC", + "CHECK_SHORT_CONFIG_DESC", + "GET_FULL_CONFIG_DESC", + "CHECK_FULL_CONFIG_DESC", + "SET_CONFIG", + "CHECK_CONFIG", + "GET_SHORT_LANGID_TABLE", + "CHECK_SHORT_LANGID_TABLE", + "GET_FULL_LANGID_TABLE", + "CHECK_FULL_LANGID_TABLE", + "GET_SHORT_MANU_STR_DESC", + "CHECK_SHORT_MANU_STR_DESC", + "GET_FULL_MANU_STR_DESC", + "CHECK_FULL_MANU_STR_DESC", + "GET_SHORT_PROD_STR_DESC", + "CHECK_SHORT_PROD_STR_DESC", + "GET_FULL_PROD_STR_DESC", + "CHECK_FULL_PROD_STR_DESC", + "GET_SHORT_SER_STR_DESC", + "CHECK_SHORT_SER_STR_DESC", + "GET_FULL_SER_STR_DESC", + "CHECK_FULL_SER_STR_DESC", + "CLEANUP", + "CLEANUP_FAILED", +}; + +typedef struct { + //Constant + urb_t *urb; /**< URB used for enumeration control transfers. Max data length of ENUM_CTRL_TRANSFER_MAX_DATA_LEN */ + //Initialized at start of a particular enumeration + usb_device_handle_t dev_hdl; /**< Handle of device being enumerated */ + hcd_pipe_handle_t pipe; /**< Default pipe handle of the device being enumerated */ + //Updated during enumeration + enum_stage_t stage; /**< Current enumeration stage */ + int expect_num_bytes; /**< Expected number of bytes for IN transfers stages. Set to 0 for OUT transfer */ + uint8_t bMaxPacketSize0; /**< Max packet size of the device's EP0. Read from bMaxPacketSize0 field of device descriptor */ + uint8_t wTotalLength; /**< Total length of device's configuration descriptor. Read from wTotalLength field of config descriptor */ + uint8_t iManufacturer; /**< Index of the Manufacturer string descriptor */ + uint8_t iProduct; /**< Index of the Product string descriptor */ + uint8_t iSerialNumber; /**< Index of the Serial Number string descriptor */ + uint8_t str_desc_bLength; /**< Saved bLength from getting a short string descriptor */ +} enum_ctrl_t; + typedef struct { //Dynamic members require a critical section struct { @@ -81,10 +174,7 @@ typedef struct { } dynamic; //Single thread members don't require a critical section so long as they are never accessed from multiple threads struct { - enum_stage_t enum_stage; - urb_t *enum_urb; - usb_device_handle_t enum_dev_hdl; //Handle of the device being enumerated. Moves to root_dev_hdl on enumeration completion - hcd_pipe_handle_t enum_dflt_pipe_hdl; + enum_ctrl_t enum_ctrl; } single_thread; //Constant members do no change after installation thus do not require a critical section struct { @@ -118,6 +208,446 @@ const char *HUB_DRIVER_TAG = "HUB"; } \ }) +// ------------------------------------------------- Forward Declare --------------------------------------------------- + +static bool enum_dflt_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr); + +// ------------------------------------------------- Enum Functions ---------------------------------------------------- + +static bool enum_stage_start(enum_ctrl_t *enum_ctrl) +{ + //Get the speed of the device, and set the enum MPS to the worst case size for now + usb_speed_t speed; + if (hcd_port_get_speed(p_hub_driver_obj->constant.root_port_hdl, &speed) != ESP_OK) { + return false; + } + enum_ctrl->bMaxPacketSize0 = (speed == USB_SPEED_LOW) ? ENUM_WORST_CASE_MPS_LS : ENUM_WORST_CASE_MPS_FS; + //Try to add the device to USBH + usb_device_handle_t enum_dev_hdl; + hcd_pipe_handle_t enum_dflt_pipe_hdl; + //We use NULL as the parent device to indicate the Root Hub port 1. We currently only support a single device + if (usbh_hub_add_dev(p_hub_driver_obj->constant.root_port_hdl, speed, &enum_dev_hdl, &enum_dflt_pipe_hdl) != ESP_OK) { + return false; + } + //Set our own default pipe callback + ESP_ERROR_CHECK(hcd_pipe_update_callback(enum_dflt_pipe_hdl, enum_dflt_pipe_callback, NULL)); + enum_ctrl->dev_hdl = enum_dev_hdl; + enum_ctrl->pipe = enum_dflt_pipe_hdl; + return true; +} + +static bool enum_stage_second_reset(enum_ctrl_t *enum_ctrl) +{ + ESP_ERROR_CHECK(hcd_pipe_set_persist_reset(enum_ctrl->pipe)); //Persist the default pipe through the reset + if (hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_RESET) != ESP_OK) { + ESP_LOGE(HUB_DRIVER_TAG, "Failed to issue second reset"); + return false; + } + return true; +} + +static uint8_t get_string_desc_index(enum_ctrl_t *enum_ctrl) +{ + uint8_t index; + switch (enum_ctrl->stage) { + case ENUM_STAGE_GET_SHORT_LANGID_TABLE: + case ENUM_STAGE_GET_FULL_LANGID_TABLE: + index = 0; //The LANGID table uses an index of 0 + break; + case ENUM_STAGE_GET_SHORT_MANU_STR_DESC: + case ENUM_STAGE_GET_FULL_MANU_STR_DESC: + index = enum_ctrl->iManufacturer; + break; + case ENUM_STAGE_GET_SHORT_PROD_STR_DESC: + case ENUM_STAGE_GET_FULL_PROD_STR_DESC: + index = enum_ctrl->iProduct; + break; + case ENUM_STAGE_GET_SHORT_SER_STR_DESC: + case ENUM_STAGE_GET_FULL_SER_STR_DESC: + index = enum_ctrl->iSerialNumber; + break; + default: + //Should not occur + index = 0; + abort(); + break; + } + return index; +} + +static bool enum_stage_transfer(enum_ctrl_t *enum_ctrl) +{ + usb_transfer_t *transfer = &enum_ctrl->urb->transfer; + switch (enum_ctrl->stage) { + case ENUM_STAGE_GET_SHORT_DEV_DESC: { + //Initialize a short device descriptor request + USB_SETUP_PACKET_INIT_GET_DEVICE_DESC((usb_setup_packet_t *)transfer->data_buffer); + ((usb_setup_packet_t *)transfer->data_buffer)->wLength = ENUM_SHORT_DESC_REQ_LEN; + transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(ENUM_SHORT_DESC_REQ_LEN, enum_ctrl->bMaxPacketSize0); + //IN data stage should return exactly ENUM_SHORT_DESC_REQ_LEN bytes + enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + ENUM_SHORT_DESC_REQ_LEN; + break; + } + case ENUM_STAGE_SET_ADDR: { + USB_SETUP_PACKET_INIT_SET_ADDR((usb_setup_packet_t *)transfer->data_buffer, ENUM_DEV_ADDR); + transfer->num_bytes = sizeof(usb_setup_packet_t); //No data stage + enum_ctrl->expect_num_bytes = 0; //OUT transfer. No need to check number of bytes returned + break; + } + case ENUM_STAGE_GET_FULL_DEV_DESC: { + USB_SETUP_PACKET_INIT_GET_DEVICE_DESC((usb_setup_packet_t *)transfer->data_buffer); + transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(sizeof(usb_device_desc_t), enum_ctrl->bMaxPacketSize0); + //IN data stage should return exactly sizeof(usb_device_desc_t) bytes + enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + sizeof(usb_device_desc_t); + break; + } + case ENUM_STAGE_GET_SHORT_CONFIG_DESC: { + //Get a short config descriptor at index 0 + USB_SETUP_PACKET_INIT_GET_CONFIG_DESC((usb_setup_packet_t *)transfer->data_buffer, ENUM_CONFIG_INDEX, ENUM_SHORT_DESC_REQ_LEN); + transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(ENUM_SHORT_DESC_REQ_LEN, enum_ctrl->bMaxPacketSize0); + //IN data stage should return exactly ENUM_SHORT_DESC_REQ_LEN bytes + enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + ENUM_SHORT_DESC_REQ_LEN; + break; + } + case ENUM_STAGE_GET_FULL_CONFIG_DESC: { + //Get the full configuration descriptor at index 0, requesting its exact length. + USB_SETUP_PACKET_INIT_GET_CONFIG_DESC((usb_setup_packet_t *)transfer->data_buffer, ENUM_CONFIG_INDEX, enum_ctrl->wTotalLength); + transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(enum_ctrl->wTotalLength, enum_ctrl->bMaxPacketSize0); + //IN data stage should return exactly wTotalLength bytes + enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + enum_ctrl->wTotalLength; + break; + } + case ENUM_STAGE_SET_CONFIG: { + USB_SETUP_PACKET_INIT_SET_CONFIG((usb_setup_packet_t *)transfer->data_buffer, ENUM_CONFIG_INDEX + 1); + transfer->num_bytes = sizeof(usb_setup_packet_t); //No data stage + enum_ctrl->expect_num_bytes = 0; //OUT transfer. No need to check number of bytes returned + break; + } + case ENUM_STAGE_GET_SHORT_LANGID_TABLE: + case ENUM_STAGE_GET_SHORT_MANU_STR_DESC: + case ENUM_STAGE_GET_SHORT_PROD_STR_DESC: + case ENUM_STAGE_GET_SHORT_SER_STR_DESC: { + uint8_t index = get_string_desc_index(enum_ctrl); + //Get only the header of the string descriptor + USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)transfer->data_buffer, + index, + ENUM_LANGID, + sizeof(usb_str_desc_t)); + transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(sizeof(usb_str_desc_t), enum_ctrl->bMaxPacketSize0); + //IN data stage should return exactly sizeof(usb_str_desc_t) bytes + enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + sizeof(usb_str_desc_t); + break; + } + case ENUM_STAGE_GET_FULL_LANGID_TABLE: + case ENUM_STAGE_GET_FULL_MANU_STR_DESC: + case ENUM_STAGE_GET_FULL_PROD_STR_DESC: + case ENUM_STAGE_GET_FULL_SER_STR_DESC: { + uint8_t index = get_string_desc_index(enum_ctrl); + //Get the full string descriptor at a particular index, requesting the descriptors exact length + USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)transfer->data_buffer, + index, + ENUM_LANGID, + enum_ctrl->str_desc_bLength); + transfer->num_bytes = sizeof(usb_setup_packet_t) + usb_round_up_to_mps(enum_ctrl->str_desc_bLength, enum_ctrl->bMaxPacketSize0); + //IN data stage should return exactly str_desc_bLength bytes + enum_ctrl->expect_num_bytes = sizeof(usb_setup_packet_t) + enum_ctrl->str_desc_bLength; + break; + } + default: //Should never occur + abort(); + break; + } + if (hcd_urb_enqueue(enum_ctrl->pipe, enum_ctrl->urb) != ESP_OK) { + ESP_LOGE(HUB_DRIVER_TAG, "Failed to submit: %s", enum_stage_strings[enum_ctrl->stage]); + return false; + } + return true; +} + +static bool enum_stage_transfer_check(enum_ctrl_t *enum_ctrl) +{ + //Dequeue the URB + urb_t *dequeued_enum_urb = hcd_urb_dequeue(enum_ctrl->pipe); + assert(dequeued_enum_urb == enum_ctrl->urb); + + //Check transfer status + usb_transfer_t *transfer = &dequeued_enum_urb->transfer; + if (transfer->status != USB_TRANSFER_STATUS_COMPLETED) { + ESP_LOGE(HUB_DRIVER_TAG, "Bad transfer status: %s", enum_stage_strings[enum_ctrl->stage]); + return false; + } + //Check IN transfer returned the expected correct number of bytes + if (enum_ctrl->expect_num_bytes != 0 && enum_ctrl->expect_num_bytes != transfer->actual_num_bytes) { + ESP_LOGE(HUB_DRIVER_TAG, "Incorrect number of bytes returned: %s", enum_stage_strings[enum_ctrl->stage]); + return false; + } + + //Stage specific checks and updates + bool ret; + switch (enum_ctrl->stage) { + case ENUM_STAGE_CHECK_SHORT_DEV_DESC: { + const usb_device_desc_t *device_desc = (usb_device_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t)); + //Check if the returned descriptor is corrupted + if (device_desc->bDescriptorType != USB_B_DESCRIPTOR_TYPE_DEVICE) { + ESP_LOGE(HUB_DRIVER_TAG, "Short dev desc corrupt"); + ret = false; + break; + } + //Update and save the MPS of the default pipe + if (hcd_pipe_update_mps(enum_ctrl->pipe, device_desc->bMaxPacketSize0) != ESP_OK) { + ESP_LOGE(HUB_DRIVER_TAG, "Failed to update MPS"); + ret = false; + break; + } + //Save the actual MPS of EP0 + enum_ctrl->bMaxPacketSize0 = device_desc->bMaxPacketSize0; + ret = true; + break; + } + case ENUM_STAGE_CHECK_ADDR: { + //Update the pipe and device's address, and fill the address into the device object + ESP_ERROR_CHECK(hcd_pipe_update_dev_addr(enum_ctrl->pipe, ENUM_DEV_ADDR)); + ESP_ERROR_CHECK(usbh_hub_enum_fill_dev_addr(enum_ctrl->dev_hdl, ENUM_DEV_ADDR)); + ret = true; + break; + } + case ENUM_STAGE_CHECK_FULL_DEV_DESC: { + //Fill device descriptor into the device object + const usb_device_desc_t *device_desc = (const usb_device_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t)); + ESP_ERROR_CHECK(usbh_hub_enum_fill_dev_desc(enum_ctrl->dev_hdl, device_desc)); + enum_ctrl->iManufacturer = device_desc->iManufacturer; + enum_ctrl->iProduct = device_desc->iProduct; + enum_ctrl->iSerialNumber = device_desc->iSerialNumber; + ret = true; + break; + } + case ENUM_STAGE_CHECK_SHORT_CONFIG_DESC: { + const usb_config_desc_t *config_desc = (usb_config_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t)); + //Check if the returned descriptor is corrupted + if (config_desc->bDescriptorType != USB_B_DESCRIPTOR_TYPE_CONFIGURATION) { + ESP_LOGE(HUB_DRIVER_TAG, "Short config desc corrupt"); + ret = false; + break; + } +#if (ENUM_CTRL_TRANSFER_MAX_DATA_LEN < UINT16_MAX) //Suppress -Wtype-limits warning due to uint16_t wTotalLength + //Check if the descriptor is too long to be supported + if (config_desc->wTotalLength > ENUM_CTRL_TRANSFER_MAX_DATA_LEN) { + ESP_LOGE(HUB_DRIVER_TAG, "Configuration descriptor larger than control transfer max length"); + ret = false; + break; + } +#endif + //Save the configuration descriptors full length + enum_ctrl->wTotalLength = config_desc->wTotalLength; + ret = true; + break; + } + case ENUM_STAGE_CHECK_FULL_CONFIG_DESC: { + //Fill configuration descriptor into the device object + const usb_config_desc_t *config_desc = (usb_config_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t)); + ESP_ERROR_CHECK(usbh_hub_enum_fill_config_desc(enum_ctrl->dev_hdl, config_desc)); + ret = true; + break; + } + case ENUM_STAGE_CHECK_CONFIG: { + ret = true; + //Nothing to do + break; + } + case ENUM_STAGE_CHECK_SHORT_LANGID_TABLE: + case ENUM_STAGE_CHECK_SHORT_MANU_STR_DESC: + case ENUM_STAGE_CHECK_SHORT_PROD_STR_DESC: + case ENUM_STAGE_CHECK_SHORT_SER_STR_DESC: { + const usb_str_desc_t *str_desc = (usb_str_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t)); + //Check if the returned descriptor is supported or corrupted + if (str_desc->bDescriptorType == 0) { + ESP_LOGW(HUB_DRIVER_TAG, "String desc not supported"); + ret = false; + break; + } else if (str_desc->bDescriptorType != USB_B_DESCRIPTOR_TYPE_STRING) { + ESP_LOGE(HUB_DRIVER_TAG, "Full string desc corrupt"); + ret = false; + break; + } +#if (ENUM_CTRL_TRANSFER_MAX_DATA_LEN < UINT8_MAX) //Suppress -Wtype-limits warning due to uint8_t bLength + //Check if the descriptor is too long to be supported + if (str_desc->bLength > (uint32_t)ENUM_CTRL_TRANSFER_MAX_DATA_LEN) { + ESP_LOGE(HUB_DRIVER_TAG, "String descriptor larger than control transfer max length"); + ret = false; + break; + } +#endif + //Save the descriptors full length + enum_ctrl->str_desc_bLength = str_desc->bLength; + ret = true; + break; + } + case ENUM_STAGE_CHECK_FULL_LANGID_TABLE: + case ENUM_STAGE_CHECK_FULL_MANU_STR_DESC: + case ENUM_STAGE_CHECK_FULL_PROD_STR_DESC: + case ENUM_STAGE_CHECK_FULL_SER_STR_DESC: { + const usb_str_desc_t *str_desc = (usb_str_desc_t *)(transfer->data_buffer + sizeof(usb_setup_packet_t)); + //Check if the returned descriptor is supported or corrupted + if (str_desc->bDescriptorType == 0) { + ESP_LOGW(HUB_DRIVER_TAG, "String desc not supported"); + ret = false; + break; + } else if (str_desc->bDescriptorType != USB_B_DESCRIPTOR_TYPE_STRING) { + ESP_LOGE(HUB_DRIVER_TAG, "Full string desc corrupt"); + ret = false; + break; + } + if (enum_ctrl->stage == ENUM_STAGE_CHECK_FULL_LANGID_TABLE) { + //Scan the LANGID table for our target LANGID + bool target_langid_found = false; + int langid_table_num_entries = (str_desc->bLength - sizeof(usb_str_desc_t))/2; //Each LANGID is 2 bytes + for (int i = 0; i < langid_table_num_entries; i++) { //Each LANGID is 2 bytes + if (str_desc->wData[i] == ENUM_LANGID) { + target_langid_found = true; + break; + } + } + if (!target_langid_found) { + ESP_LOGE(HUB_DRIVER_TAG, "LANGID 0x%x not found", ENUM_LANGID); + } + ret = target_langid_found; + break; + } else { + //Fill the string descriptor into the device object + int select; + if (enum_ctrl->stage == ENUM_STAGE_CHECK_FULL_MANU_STR_DESC) { + select = 0; + } else if (enum_ctrl->stage == ENUM_STAGE_CHECK_FULL_PROD_STR_DESC) { + select = 1; + } else { //ENUM_STAGE_CHECK_FULL_PROD_STR_DESC + select = 2; + } + ESP_ERROR_CHECK(usbh_hub_enum_fill_str_desc(enum_ctrl->dev_hdl, str_desc, select)); + ret = true; + break; + } + } + default: //Should never occur + ret = false; + abort(); + break; + } + return ret; +} + +static void enum_stage_cleanup(enum_ctrl_t *enum_ctrl) +{ + //We currently only support a single device connected to the root port. Move the device handle from enum to root + HUB_DRIVER_ENTER_CRITICAL(); + p_hub_driver_obj->dynamic.root_dev_hdl = enum_ctrl->dev_hdl; + p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_ACTIVE; + HUB_DRIVER_EXIT_CRITICAL(); + usb_device_handle_t dev_hdl = enum_ctrl->dev_hdl; + //Clear values in enum_ctrl + enum_ctrl->dev_hdl = NULL; + enum_ctrl->pipe = NULL; + //Update device object after enumeration is done + ESP_ERROR_CHECK(usbh_hub_enum_done(dev_hdl)); +} + +static void enum_stage_cleanup_failed(enum_ctrl_t *enum_ctrl) +{ + //Enumeration failed. Clear the enum device handle and pipe + if (enum_ctrl->dev_hdl) { + //Halt, flush, and dequeue enum default pipe + ESP_ERROR_CHECK(hcd_pipe_command(enum_ctrl->pipe, HCD_PIPE_CMD_HALT)); + ESP_ERROR_CHECK(hcd_pipe_command(enum_ctrl->pipe, HCD_PIPE_CMD_FLUSH)); + urb_t *dequeued_enum_urb = hcd_urb_dequeue(enum_ctrl->pipe); + assert(dequeued_enum_urb == enum_ctrl->urb); + ESP_ERROR_CHECK(usbh_hub_enum_failed(enum_ctrl->dev_hdl)); //Free the underlying device object first before recovering the port + } + //Clear values in enum_ctrl + enum_ctrl->dev_hdl = NULL; + enum_ctrl->pipe = NULL; + HUB_DRIVER_ENTER_CRITICAL(); + //Enum could have failed due to a port error. If so, we need to trigger a port recovery + if (p_hub_driver_obj->dynamic.driver_state == HUB_DRIVER_STATE_ROOT_RECOVERY) { + p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_RECOVER; + } + HUB_DRIVER_EXIT_CRITICAL(); +} + +static void enum_set_next_stage(enum_ctrl_t *enum_ctrl, bool last_stage_pass) +{ + //Set next stage + if (last_stage_pass) { + if (enum_ctrl->stage != ENUM_STAGE_NONE && + enum_ctrl->stage != ENUM_STAGE_CLEANUP && + enum_ctrl->stage != ENUM_STAGE_CLEANUP_FAILED) { + enum_ctrl->stage++; //Go to next stage + } else { + enum_ctrl->stage = ENUM_STAGE_NONE; + } + } else { + switch (enum_ctrl->stage) { + case ENUM_STAGE_START: + //Stage failed but clean up not required + enum_ctrl->stage = ENUM_STAGE_NONE; + break; + case ENUM_STAGE_GET_SHORT_LANGID_TABLE: + case ENUM_STAGE_CHECK_SHORT_LANGID_TABLE: + case ENUM_STAGE_GET_FULL_LANGID_TABLE: + case ENUM_STAGE_CHECK_FULL_LANGID_TABLE: + case ENUM_STAGE_GET_SHORT_MANU_STR_DESC: + case ENUM_STAGE_CHECK_SHORT_MANU_STR_DESC: + case ENUM_STAGE_GET_FULL_MANU_STR_DESC: + case ENUM_STAGE_CHECK_FULL_MANU_STR_DESC: + case ENUM_STAGE_GET_SHORT_PROD_STR_DESC: + case ENUM_STAGE_CHECK_SHORT_PROD_STR_DESC: + case ENUM_STAGE_GET_FULL_PROD_STR_DESC: + case ENUM_STAGE_CHECK_FULL_PROD_STR_DESC: + case ENUM_STAGE_GET_SHORT_SER_STR_DESC: + case ENUM_STAGE_CHECK_SHORT_SER_STR_DESC: + case ENUM_STAGE_GET_FULL_SER_STR_DESC: + case ENUM_STAGE_CHECK_FULL_SER_STR_DESC: + //String descriptor stages are allow to fail. We just don't fetch them and treat enumeration as successful + enum_ctrl->stage = ENUM_STAGE_CLEANUP; + break; + default: + //Enumeration failed. Go to failure clean up + enum_ctrl->stage = ENUM_STAGE_CLEANUP_FAILED; + break; + } + } + + //These stages are not waiting for a callback, so we need to re-trigger the enum event + bool re_trigger; + switch (enum_ctrl->stage) { + case ENUM_STAGE_GET_SHORT_DEV_DESC: + case ENUM_STAGE_SECOND_RESET: + case ENUM_STAGE_SET_ADDR: + case ENUM_STAGE_GET_FULL_DEV_DESC: + case ENUM_STAGE_GET_SHORT_CONFIG_DESC: + case ENUM_STAGE_GET_FULL_CONFIG_DESC: + case ENUM_STAGE_SET_CONFIG: + case ENUM_STAGE_GET_SHORT_LANGID_TABLE: + case ENUM_STAGE_GET_FULL_LANGID_TABLE: + case ENUM_STAGE_GET_SHORT_MANU_STR_DESC: + case ENUM_STAGE_GET_FULL_MANU_STR_DESC: + case ENUM_STAGE_GET_SHORT_PROD_STR_DESC: + case ENUM_STAGE_GET_FULL_PROD_STR_DESC: + case ENUM_STAGE_GET_SHORT_SER_STR_DESC: + case ENUM_STAGE_GET_FULL_SER_STR_DESC: + case ENUM_STAGE_CLEANUP: + case ENUM_STAGE_CLEANUP_FAILED: + re_trigger = true; + break; + default: + re_trigger = false; + break; + } + if (re_trigger) { + HUB_DRIVER_ENTER_CRITICAL(); + p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT; + HUB_DRIVER_EXIT_CRITICAL(); + } +} + // ------------------------------------------------- Event Handling ---------------------------------------------------- // ---------------------- Callbacks ------------------------ @@ -127,13 +657,8 @@ static bool root_port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port HUB_DRIVER_ENTER_CRITICAL_SAFE(); p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ROOT_EVENT; HUB_DRIVER_EXIT_CRITICAL_SAFE(); - bool yield; - if (in_isr) { - p_hub_driver_obj->constant.notif_cb(USB_NOTIF_SOURCE_HUB, in_isr, p_hub_driver_obj->constant.notif_cb_arg); - } else { - yield = false; - } - return yield; + assert(in_isr); //Currently, this callback should only ever be called from an ISR context + return p_hub_driver_obj->constant.notif_cb(USB_NOTIF_SOURCE_HUB, in_isr, p_hub_driver_obj->constant.notif_cb_arg);; } static bool enum_dflt_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr) @@ -161,7 +686,7 @@ static void usbh_hub_callback(hcd_port_handle_t port_hdl, usbh_hub_event_t hub_e p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_RECOVER; break; case USBH_HUB_EVENT_DISABLE_PORT: - p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_POWERD; + p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_POWERED; call_port_disable = true; break; default: @@ -193,7 +718,7 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl) p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT; p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_ENUMERATING; HUB_DRIVER_EXIT_CRITICAL(); - p_hub_driver_obj->single_thread.enum_stage = ENUM_STAGE_START; + p_hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_START; } else { ESP_LOGE(HUB_DRIVER_TAG, "Root port reset failed"); } @@ -205,14 +730,14 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl) usb_device_handle_t dev_hdl = NULL; HUB_DRIVER_ENTER_CRITICAL(); switch (p_hub_driver_obj->dynamic.driver_state) { - case HUB_DRIVER_STATE_ROOT_POWERD: + case HUB_DRIVER_STATE_ROOT_POWERED: //This occurred before enumeration. Therefore, there's no device and we can go straight to port recovery p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT; break; case HUB_DRIVER_STATE_ROOT_ENUMERATING: //This occurred during enumeration. Therefore, we need to recover the failed enumeration p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT; - p_hub_driver_obj->single_thread.enum_stage = ENUM_STAGE_CLEANUP_FAILED; + p_hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_CLEANUP_FAILED; break; case HUB_DRIVER_STATE_ROOT_ACTIVE: //There was an enumerated device. We need to indicate to USBH that the device is gone @@ -235,303 +760,70 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl) } } -// ------------------------------------------------- Enum Functions ---------------------------------------------------- - -static enum_stage_t enum_stage_start(void) -{ - usb_speed_t speed; - if (hcd_port_get_speed(p_hub_driver_obj->constant.root_port_hdl, &speed) != ESP_OK) { - return ENUM_STAGE_NONE; - } - usb_device_handle_t enum_dev_hdl; - hcd_pipe_handle_t enum_dflt_pipe_hdl; - //We use NULL as the parent device to indicate the root hub port 1. We currently only support a single device - if (usbh_hub_add_dev(p_hub_driver_obj->constant.root_port_hdl, speed, &enum_dev_hdl, &enum_dflt_pipe_hdl) != ESP_OK) { - return ENUM_STAGE_NONE; - } - //Set our own default pipe callback - ESP_ERROR_CHECK(hcd_pipe_update_callback(enum_dflt_pipe_hdl, enum_dflt_pipe_callback, NULL)); - HUB_DRIVER_ENTER_CRITICAL(); - p_hub_driver_obj->single_thread.enum_dev_hdl = enum_dev_hdl; - p_hub_driver_obj->single_thread.enum_dflt_pipe_hdl = enum_dflt_pipe_hdl; - HUB_DRIVER_EXIT_CRITICAL(); - ESP_LOGD(HUB_DRIVER_TAG, "Enumeration starting"); - return ENUM_STAGE_GET_SHORT_DEV_DESC; -} - -static enum_stage_t enum_stage_get_short_dev_desc(hcd_pipe_handle_t pipe_hdl, urb_t *enum_urb) -{ - USB_SETUP_PACKET_INIT_GET_DEVICE_DESC((usb_setup_packet_t *)enum_urb->transfer.data_buffer); - enum_urb->transfer.num_bytes = sizeof(usb_setup_packet_t) + ENUM_DEV_DESC_REQ_SIZE; - if (hcd_urb_enqueue(pipe_hdl, enum_urb) != ESP_OK) { - ESP_LOGE(HUB_DRIVER_TAG, "Failed to get short device descriptor"); - return ENUM_STAGE_CLEANUP_FAILED; - } - ESP_LOGD(HUB_DRIVER_TAG, "Getting short device descriptor"); - return ENUM_STAGE_GET_SHORT_DEV_DESC_CHECK; -} - -static enum_stage_t enum_stage_check_short_dev_desc(hcd_pipe_handle_t pipe_hdl, urb_t *enum_urb) -{ - //Dequeue the URB - urb_t *dequeued_enum_urb = hcd_urb_dequeue(pipe_hdl); - assert(dequeued_enum_urb == enum_urb); - if (enum_urb->transfer.status != USB_TRANSFER_STATUS_COMPLETED) { - ESP_LOGE(HUB_DRIVER_TAG, "Short device descriptor transfer failed"); - return ENUM_STAGE_CLEANUP_FAILED; - } - usb_device_desc_t *device_desc = (usb_device_desc_t *)(enum_urb->transfer.data_buffer + sizeof(usb_setup_packet_t)); - if (enum_urb->transfer.actual_num_bytes < sizeof(usb_setup_packet_t) + 8) { //Device must return at least 8 bytes in its data stage - ESP_LOGE(HUB_DRIVER_TAG, "Short device descriptor too short"); - return ENUM_STAGE_CLEANUP_FAILED; - } - //Update the MPS of the default pipe - if (hcd_pipe_update_mps(pipe_hdl, device_desc->bMaxPacketSize0) != ESP_OK) { - ESP_LOGE(HUB_DRIVER_TAG, "Failed to update default pipe MPS"); - return ENUM_STAGE_CLEANUP_FAILED; - } - ESP_LOGD(HUB_DRIVER_TAG, "Short device descriptor obtained"); - return ENUM_STAGE_SECOND_RESET; -} - -static enum_stage_t enum_stage_second_reset(hcd_pipe_handle_t pipe_hdl, urb_t *enum_urb) -{ - ESP_ERROR_CHECK(hcd_pipe_set_persist_reset(pipe_hdl)); //Persist the default pipe through the reset - if (hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_RESET) != ESP_OK) { - ESP_LOGE(HUB_DRIVER_TAG, "Failed to issue second reset"); - return ENUM_STAGE_CLEANUP_FAILED; - } - ESP_LOGD(HUB_DRIVER_TAG, "Second reset done"); - return ENUM_STAGE_SET_ADDR; -} - -static enum_stage_t enum_stage_set_addr(hcd_pipe_handle_t pipe_hdl, urb_t *enum_urb) -{ - USB_SETUP_PACKET_INIT_SET_ADDR((usb_setup_packet_t *)enum_urb->transfer.data_buffer, ENUM_DEV_ADDR); - enum_urb->transfer.num_bytes = sizeof(usb_setup_packet_t); //No data stage - if (hcd_urb_enqueue(pipe_hdl, enum_urb) != ESP_OK) { - ESP_LOGE(HUB_DRIVER_TAG, "Failed to set address"); - return ENUM_STAGE_CLEANUP_FAILED; - } - ESP_LOGD(HUB_DRIVER_TAG, "Setting address %d", ENUM_DEV_ADDR); - return ENUM_STAGE_SET_ADDR_CHECK; -} - -static enum_stage_t enum_stage_check_set_addr(hcd_pipe_handle_t pipe_hdl, urb_t *enum_urb, usb_device_handle_t dev_hdl) -{ - //Dequeue the URB - urb_t *dequeued_enum_urb = hcd_urb_dequeue(pipe_hdl); - assert(dequeued_enum_urb == enum_urb); - if (enum_urb->transfer.status != USB_TRANSFER_STATUS_COMPLETED) { - ESP_LOGE(HUB_DRIVER_TAG, "Set address transfer failed"); - return ENUM_STAGE_CLEANUP_FAILED; - } - //Update the pipe and device's address, and fill the address into the device object - ESP_ERROR_CHECK(hcd_pipe_update_dev_addr(pipe_hdl, ENUM_DEV_ADDR)); - ESP_ERROR_CHECK(usbh_hub_enum_fill_dev_addr(dev_hdl, ENUM_DEV_ADDR)); - ESP_LOGD(HUB_DRIVER_TAG, "Address set successfully"); - return ENUM_STAGE_GET_FULL_DEV_DESC; -} - -static enum_stage_t enum_stage_get_full_dev_desc(hcd_pipe_handle_t pipe_hdl, urb_t *enum_urb) -{ - USB_SETUP_PACKET_INIT_GET_DEVICE_DESC((usb_setup_packet_t *)enum_urb->transfer.data_buffer); - enum_urb->transfer.num_bytes = sizeof(usb_setup_packet_t) + ENUM_DEV_DESC_REQ_SIZE; - if (hcd_urb_enqueue(pipe_hdl, enum_urb) != ESP_OK) { - ESP_LOGE(HUB_DRIVER_TAG, "Failed to get full device descriptor"); - return ENUM_STAGE_CLEANUP_FAILED; - } - ESP_LOGD(HUB_DRIVER_TAG, "Getting full device descriptor"); - return ENUM_STAGE_GET_FULL_DEV_DESC_CHECK; -} - -static enum_stage_t enum_stage_check_full_dev_desc(hcd_pipe_handle_t pipe_hdl, urb_t *enum_urb, usb_device_handle_t dev_hdl) -{ - //Dequeue the URB - urb_t *dequeued_enum_urb = hcd_urb_dequeue(pipe_hdl); - assert(dequeued_enum_urb == enum_urb); - if (enum_urb->transfer.status != USB_TRANSFER_STATUS_COMPLETED) { - ESP_LOGE(HUB_DRIVER_TAG, "Full device descriptor transfer failed"); - return ENUM_STAGE_CLEANUP_FAILED; - } - if (enum_urb->transfer.actual_num_bytes < sizeof(usb_setup_packet_t) + sizeof(usb_device_desc_t)) { - ESP_LOGE(HUB_DRIVER_TAG, "Full device descriptor too short"); - return ENUM_STAGE_CLEANUP_FAILED; - } - //Fill device descriptor into device - const usb_device_desc_t *device_desc = (const usb_device_desc_t *)(enum_urb->transfer.data_buffer + sizeof(usb_setup_packet_t)); - ESP_ERROR_CHECK(usbh_hub_enum_fill_dev_desc(dev_hdl, device_desc)); - ESP_LOGD(HUB_DRIVER_TAG, "Full device descriptor obtained"); - return ENUM_STAGE_GET_CONFIG_DESC; -} - -static enum_stage_t enum_stage_get_config_desc(hcd_pipe_handle_t pipe_hdl, urb_t *enum_urb) -{ - //Get the configuration descriptor at index 0 - USB_SETUP_PACKET_INIT_GET_CONFIG_DESC((usb_setup_packet_t *)enum_urb->transfer.data_buffer, ENUM_CONFIG_INDEX, ENUM_CTRL_TRANSFER_MAX_LEN); - enum_urb->transfer.num_bytes = sizeof(usb_setup_packet_t) + ENUM_CTRL_TRANSFER_MAX_LEN; - if (hcd_urb_enqueue(pipe_hdl, enum_urb) != ESP_OK) { - ESP_LOGE(HUB_DRIVER_TAG, "Failed to get configuration descriptor"); - return ENUM_STAGE_CLEANUP_FAILED; - } - ESP_LOGD(HUB_DRIVER_TAG, "Getting configuration descriptor"); - return ENUM_STAGE_GET_CONFIG_DESC_CHECK; -} - -static enum_stage_t enum_stage_check_config_desc(hcd_pipe_handle_t pipe_hdl, urb_t *enum_urb, usb_device_handle_t dev_hdl) -{ - //Dequeue the URB - urb_t *dequeued_enum_urb = hcd_urb_dequeue(pipe_hdl); - assert(dequeued_enum_urb == enum_urb); - if (enum_urb->transfer.status != USB_TRANSFER_STATUS_COMPLETED) { - ESP_LOGE(HUB_DRIVER_TAG, "Configuration descriptor transfer failed"); - return ENUM_STAGE_CLEANUP_FAILED; - } - usb_config_desc_t *config_desc = (usb_config_desc_t *)(enum_urb->transfer.data_buffer + sizeof(usb_setup_packet_t)); - if (enum_urb->transfer.actual_num_bytes < sizeof(usb_setup_packet_t) + sizeof(usb_config_desc_t)) { - ESP_LOGE(HUB_DRIVER_TAG, "Configuration descriptor too small"); - return ENUM_STAGE_CLEANUP_FAILED; - } - if (config_desc->wTotalLength > ENUM_CTRL_TRANSFER_MAX_LEN) { - ESP_LOGE(HUB_DRIVER_TAG, "Configuration descriptor larger than control transfer max length"); - return ENUM_STAGE_CLEANUP_FAILED; - } - //Fill configuration descriptor into device - ESP_ERROR_CHECK(usbh_hub_enum_fill_config_desc(dev_hdl, config_desc)); - return ENUM_STAGE_SET_CONFIG; -} - -static enum_stage_t enum_stage_set_config(hcd_pipe_handle_t pipe_hdl, urb_t *enum_urb) -{ - USB_SETUP_PACKET_INIT_SET_CONFIG((usb_setup_packet_t *)enum_urb->transfer.data_buffer, ENUM_CONFIG_INDEX + 1); - enum_urb->transfer.num_bytes = sizeof(usb_setup_packet_t); //No data stage - if (hcd_urb_enqueue(pipe_hdl, enum_urb) != ESP_OK) { - ESP_LOGE(HUB_DRIVER_TAG, "Failed to set configuration"); - return ENUM_STAGE_CLEANUP_FAILED; - } - ESP_LOGD(HUB_DRIVER_TAG, "Setting configuration"); - return ENUM_STAGE_SET_CONFIG_CHECK; -} - -static enum_stage_t enum_stage_check_config(hcd_pipe_handle_t pipe_hdl, urb_t *enum_urb) -{ - //Dequeue the URB - urb_t *dequeued_enum_urb = hcd_urb_dequeue(pipe_hdl); - assert(dequeued_enum_urb == enum_urb); - if (enum_urb->transfer.status != USB_TRANSFER_STATUS_COMPLETED) { - ESP_LOGE(HUB_DRIVER_TAG, "Set configuration transfer failed"); - return ENUM_STAGE_CLEANUP_FAILED; - } - ESP_LOGD(HUB_DRIVER_TAG, "Configuration set successfully"); - return ENUM_STAGE_CLEANUP; -} - -static enum_stage_t enum_stage_cleanup(void) -{ - //We currently only support a single device connected to the root port. Move the device handle from enum to root - HUB_DRIVER_ENTER_CRITICAL(); - p_hub_driver_obj->dynamic.root_dev_hdl = p_hub_driver_obj->single_thread.enum_dev_hdl; - p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_ACTIVE; - HUB_DRIVER_EXIT_CRITICAL(); - usb_device_handle_t dev_hdl = p_hub_driver_obj->single_thread.enum_dev_hdl; - p_hub_driver_obj->single_thread.enum_dev_hdl = NULL; - p_hub_driver_obj->single_thread.enum_dflt_pipe_hdl = NULL; - //Update device object after enumeration is done - ESP_ERROR_CHECK(usbh_hub_enum_done(dev_hdl)); - return ENUM_STAGE_NONE; -} - -static enum_stage_t enum_stage_cleanup_failed(void) -{ - //Enumeration failed. Clear the enum device handle and pipe - usb_device_handle_t dev_hdl = p_hub_driver_obj->single_thread.enum_dev_hdl; - if (p_hub_driver_obj->single_thread.enum_dev_hdl) { - //Halt, flush, and dequeue enum default pipe - ESP_ERROR_CHECK(hcd_pipe_command(p_hub_driver_obj->single_thread.enum_dflt_pipe_hdl, HCD_PIPE_CMD_HALT)); - ESP_ERROR_CHECK(hcd_pipe_command(p_hub_driver_obj->single_thread.enum_dflt_pipe_hdl, HCD_PIPE_CMD_FLUSH)); - urb_t *dequeued_enum_urb = hcd_urb_dequeue(p_hub_driver_obj->single_thread.enum_dflt_pipe_hdl); - assert(dequeued_enum_urb == p_hub_driver_obj->single_thread.enum_urb); - ESP_ERROR_CHECK(usbh_hub_enum_failed(dev_hdl)); //Free the underlying device object first before recovering the port - } - p_hub_driver_obj->single_thread.enum_dev_hdl = NULL; - p_hub_driver_obj->single_thread.enum_dflt_pipe_hdl = NULL; - HUB_DRIVER_ENTER_CRITICAL(); - //Enum could have failed due to a port error. If so, we need to trigger a port recovery - if (p_hub_driver_obj->dynamic.driver_state == HUB_DRIVER_STATE_ROOT_RECOVERY) { - p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_RECOVER; - } - HUB_DRIVER_EXIT_CRITICAL(); - return ENUM_STAGE_NONE; -} - static void enum_handle_events(void) { - enum_stage_t cur_stage = p_hub_driver_obj->single_thread.enum_stage; - hcd_pipe_handle_t enum_dflt_pipe_hdl = p_hub_driver_obj->single_thread.enum_dflt_pipe_hdl; - urb_t *enum_urb = p_hub_driver_obj->single_thread.enum_urb; - usb_device_handle_t dev_hdl = p_hub_driver_obj->single_thread.enum_dev_hdl; - enum_stage_t next_stage; - switch (cur_stage) { + bool stage_pass; + enum_ctrl_t *enum_ctrl = &p_hub_driver_obj->single_thread.enum_ctrl; + switch (enum_ctrl->stage) { case ENUM_STAGE_START: - next_stage = enum_stage_start(); - break; - case ENUM_STAGE_GET_SHORT_DEV_DESC: - next_stage = enum_stage_get_short_dev_desc(enum_dflt_pipe_hdl, enum_urb); - break; - case ENUM_STAGE_GET_SHORT_DEV_DESC_CHECK: - next_stage = enum_stage_check_short_dev_desc(enum_dflt_pipe_hdl, enum_urb); + stage_pass = enum_stage_start(enum_ctrl); break; case ENUM_STAGE_SECOND_RESET: - next_stage = enum_stage_second_reset(enum_dflt_pipe_hdl, enum_urb); + stage_pass = enum_stage_second_reset(enum_ctrl); break; + //Transfer submission stages + case ENUM_STAGE_GET_SHORT_DEV_DESC: case ENUM_STAGE_SET_ADDR: - next_stage = enum_stage_set_addr(enum_dflt_pipe_hdl, enum_urb); - break; - case ENUM_STAGE_SET_ADDR_CHECK: - next_stage = enum_stage_check_set_addr(enum_dflt_pipe_hdl, enum_urb, dev_hdl); - break; case ENUM_STAGE_GET_FULL_DEV_DESC: - next_stage = enum_stage_get_full_dev_desc(enum_dflt_pipe_hdl, enum_urb); - break; - case ENUM_STAGE_GET_FULL_DEV_DESC_CHECK: - next_stage = enum_stage_check_full_dev_desc(enum_dflt_pipe_hdl, enum_urb, dev_hdl); - break; - case ENUM_STAGE_GET_CONFIG_DESC: - next_stage = enum_stage_get_config_desc(enum_dflt_pipe_hdl, enum_urb); - break; - case ENUM_STAGE_GET_CONFIG_DESC_CHECK: - next_stage = enum_stage_check_config_desc(enum_dflt_pipe_hdl, enum_urb, dev_hdl); - break; + case ENUM_STAGE_GET_SHORT_CONFIG_DESC: + case ENUM_STAGE_GET_FULL_CONFIG_DESC: case ENUM_STAGE_SET_CONFIG: - next_stage = enum_stage_set_config(enum_dflt_pipe_hdl, enum_urb); + case ENUM_STAGE_GET_SHORT_LANGID_TABLE: + case ENUM_STAGE_GET_FULL_LANGID_TABLE: + case ENUM_STAGE_GET_SHORT_MANU_STR_DESC: + case ENUM_STAGE_GET_FULL_MANU_STR_DESC: + case ENUM_STAGE_GET_SHORT_PROD_STR_DESC: + case ENUM_STAGE_GET_FULL_PROD_STR_DESC: + case ENUM_STAGE_GET_SHORT_SER_STR_DESC: + case ENUM_STAGE_GET_FULL_SER_STR_DESC: + stage_pass = enum_stage_transfer(enum_ctrl); break; - case ENUM_STAGE_SET_CONFIG_CHECK: - next_stage = enum_stage_check_config(enum_dflt_pipe_hdl, enum_urb); + //Transfer check stages + case ENUM_STAGE_CHECK_SHORT_DEV_DESC: + case ENUM_STAGE_CHECK_ADDR: + case ENUM_STAGE_CHECK_FULL_DEV_DESC: + case ENUM_STAGE_CHECK_SHORT_CONFIG_DESC: + case ENUM_STAGE_CHECK_FULL_CONFIG_DESC: + case ENUM_STAGE_CHECK_CONFIG: + case ENUM_STAGE_CHECK_SHORT_LANGID_TABLE: + case ENUM_STAGE_CHECK_FULL_LANGID_TABLE: + case ENUM_STAGE_CHECK_SHORT_MANU_STR_DESC: + case ENUM_STAGE_CHECK_FULL_MANU_STR_DESC: + case ENUM_STAGE_CHECK_SHORT_PROD_STR_DESC: + case ENUM_STAGE_CHECK_FULL_PROD_STR_DESC: + case ENUM_STAGE_CHECK_SHORT_SER_STR_DESC: + case ENUM_STAGE_CHECK_FULL_SER_STR_DESC: + stage_pass = enum_stage_transfer_check(enum_ctrl); break; case ENUM_STAGE_CLEANUP: - next_stage = enum_stage_cleanup(); + enum_stage_cleanup(enum_ctrl); + stage_pass = true; break; case ENUM_STAGE_CLEANUP_FAILED: - next_stage = enum_stage_cleanup_failed(); + enum_stage_cleanup_failed(enum_ctrl); + stage_pass = true; break; default: - //Note: Don't abort here. The enum_dflt_pipe_callback() can trigger a HUB_DRIVER_FLAG_ACTION_ENUM_EVENT after a cleanup - next_stage = ENUM_STAGE_NONE; + //Note: Don't abort here. The enum_dflt_pipe_callback() can trigger a HUB_DRIVER_FLAG_ACTION_ENUM_EVENT after a cleanup. + stage_pass = true; break; } - p_hub_driver_obj->single_thread.enum_stage = next_stage; - HUB_DRIVER_ENTER_CRITICAL(); - if (next_stage == ENUM_STAGE_GET_SHORT_DEV_DESC || - next_stage == ENUM_STAGE_SECOND_RESET || - next_stage == ENUM_STAGE_SET_ADDR || - next_stage == ENUM_STAGE_GET_FULL_DEV_DESC || - next_stage == ENUM_STAGE_GET_CONFIG_DESC || - next_stage == ENUM_STAGE_SET_CONFIG || - next_stage == ENUM_STAGE_CLEANUP || - next_stage == ENUM_STAGE_CLEANUP_FAILED) { - p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ENUM_EVENT; + if (stage_pass) { + ESP_LOGD(HUB_DRIVER_TAG, "Stage done: %s", enum_stage_strings[enum_ctrl->stage]); + } else { + ESP_LOGE(HUB_DRIVER_TAG, "Stage failed: %s", enum_stage_strings[enum_ctrl->stage]); } - HUB_DRIVER_EXIT_CRITICAL(); + enum_set_next_stage(enum_ctrl, stage_pass); } // ---------------------------------------------- Hub Driver Functions ------------------------------------------------- @@ -543,7 +835,7 @@ esp_err_t hub_install(hub_config_t *hub_config) HUB_DRIVER_EXIT_CRITICAL(); //Allocate Hub driver object hub_driver_t *hub_driver_obj = heap_caps_calloc(1, sizeof(hub_driver_t), MALLOC_CAP_DEFAULT); - urb_t *enum_urb = urb_alloc(sizeof(usb_setup_packet_t) + ENUM_CTRL_TRANSFER_MAX_LEN, 0, 0); + urb_t *enum_urb = urb_alloc(sizeof(usb_setup_packet_t) + ENUM_CTRL_TRANSFER_MAX_DATA_LEN, 0, 0); if (hub_driver_obj == NULL || enum_urb == NULL) { return ESP_ERR_NO_MEM; } @@ -560,10 +852,10 @@ esp_err_t hub_install(hub_config_t *hub_config) if (ret != ESP_OK) { goto err; } - //Initialize hub driver object + //Initialize Hub driver object hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_INSTALLED; - hub_driver_obj->single_thread.enum_stage = ENUM_STAGE_NONE; - hub_driver_obj->single_thread.enum_urb = enum_urb; + hub_driver_obj->single_thread.enum_ctrl.stage = ENUM_STAGE_NONE; + hub_driver_obj->single_thread.enum_ctrl.urb = enum_urb; hub_driver_obj->constant.root_port_hdl = port_hdl; hub_driver_obj->constant.notif_cb = hub_config->notif_cb; hub_driver_obj->constant.notif_cb_arg = hub_config->notif_cb_arg; @@ -599,7 +891,7 @@ esp_err_t hub_uninstall(void) ESP_ERROR_CHECK(hcd_port_deinit(hub_driver_obj->constant.root_port_hdl)); //Free Hub driver resources - urb_free(hub_driver_obj->single_thread.enum_urb); + urb_free(hub_driver_obj->single_thread.enum_ctrl.urb); heap_caps_free(hub_driver_obj); return ESP_OK; } @@ -615,7 +907,7 @@ esp_err_t hub_root_start(void) ret = hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_POWER_ON); if (ret == ESP_OK) { HUB_DRIVER_ENTER_CRITICAL(); - p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_POWERD; + p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_POWERED; HUB_DRIVER_EXIT_CRITICAL(); } return ret; @@ -656,7 +948,7 @@ esp_err_t hub_process(void) ESP_ERROR_CHECK(hcd_port_recover(p_hub_driver_obj->constant.root_port_hdl)); ESP_ERROR_CHECK(hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_POWER_ON)); HUB_DRIVER_ENTER_CRITICAL(); - p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_POWERD; + p_hub_driver_obj->dynamic.driver_state = HUB_DRIVER_STATE_ROOT_POWERED; HUB_DRIVER_EXIT_CRITICAL(); } HUB_DRIVER_ENTER_CRITICAL(); diff --git a/components/usb/include/usb/usb_host.h b/components/usb/include/usb/usb_host.h index 72ad885545..9b8bc3c3ab 100644 --- a/components/usb/include/usb/usb_host.h +++ b/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 --------------------------------------------------- /** diff --git a/components/usb/include/usb/usb_types_ch9.h b/components/usb/include/usb/usb_types_ch9.h index d49b81eb33..3cd853b9f6 100644 --- a/components/usb/include/usb/usb_types_ch9.h +++ b/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; diff --git a/components/usb/include/usb/usb_types_stack.h b/components/usb/include/usb/usb_types_stack.h index c35de488b6..b7a525b20d 100644 --- a/components/usb/include/usb/usb_types_stack.h +++ b/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 --------------------------------------------------- diff --git a/components/usb/private_include/usbh.h b/components/usb/private_include/usbh.h index e27441fa70..4dd523c7d7 100644 --- a/components/usb/private_include/usbh.h +++ b/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 ----------------------- @@ -336,17 +345,6 @@ esp_err_t usbh_hub_dev_port_disabled(usb_device_handle_t dev_hdl); // ----------------- Enumeration Related ------------------- -/** - * @brief Fill the enumerating device's descriptor - * - * @note Hub Driver only - * @note Must call in sequence - * @param[in] dev_hdl Device handle - * @param device_desc - * @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); - /** * @brief Assign the enumerating device's address * @@ -358,6 +356,17 @@ esp_err_t usbh_hub_enum_fill_dev_desc(usb_device_handle_t dev_hdl, const usb_dev */ esp_err_t usbh_hub_enum_fill_dev_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr); +/** + * @brief Fill the enumerating device's descriptor + * + * @note Hub Driver only + * @note Must call in sequence + * @param[in] dev_hdl Device handle + * @param device_desc + * @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); + /** * @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 diff --git a/components/usb/test/common/test_usb_mock_classes.c b/components/usb/test/common/test_usb_mock_classes.c index 0e819281de..7a626cfbcd 100644 --- a/components/usb/test/common/test_usb_mock_classes.c +++ b/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; diff --git a/components/usb/test/common/test_usb_mock_classes.h b/components/usb/test/common/test_usb_mock_classes.h index ef39aff127..b225a37517 100644 --- a/components/usb/test/common/test_usb_mock_classes.h +++ b/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 diff --git a/components/usb/test/usb_host/msc_client.h b/components/usb/test/usb_host/msc_client.h index ab2f0f4dd3..c3bf2063f2 100644 --- a/components/usb/test/usb_host/msc_client.h +++ b/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); diff --git a/components/usb/test/usb_host/msc_client_async_enum.c b/components/usb/test/usb_host/msc_client_async_enum.c new file mode 100644 index 0000000000..ccedce51fc --- /dev/null +++ b/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 +#include +#include +#include +#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); +} diff --git a/components/usb/test/usb_host/test_usb_host_plugging.c b/components/usb/test/usb_host/test_usb_host_plugging.c index 146dcb83ee..c6c8e5d04c 100644 --- a/components/usb/test/usb_host/test_usb_host_plugging.c +++ b/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 +} diff --git a/components/usb/usb_host.c b/components/usb/usb_host.c index 41b9ce9dcc..25dc3659ad 100644 --- a/components/usb/usb_host.c +++ b/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 ------------------------- diff --git a/components/usb/usbh.c b/components/usb/usbh.c index 0681e886cf..b50a8fec18 100644 --- a/components/usb/usbh.c +++ b/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); diff --git a/examples/peripherals/usb/host/msc/components/msc/src/msc_host.c b/examples/peripherals/usb/host/msc/components/msc/src/msc_host.c index ad4b3f642f..940c2e2ac5 100644 --- a/examples/peripherals/usb/host/msc/components/msc/src/msc_host.c +++ b/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);