Merge branch 'bugfix/usb_host_hub_enumeration_string_handling_v5.0' into 'release/v5.0'

USB Host: Fix string descriptor handling by the hub driver during enumeration (backport v5.0)

See merge request espressif/esp-idf!22324
This commit is contained in:
Saurabh Kumar Bansal
2023-02-17 21:22:59 +08:00
2 changed files with 45 additions and 18 deletions

View File

@ -288,33 +288,34 @@ static bool enum_stage_second_reset(enum_ctrl_t *enum_ctrl)
return true; return true;
} }
static uint8_t get_string_desc_index(enum_ctrl_t *enum_ctrl) static void get_string_desc_index_and_langid(enum_ctrl_t *enum_ctrl, uint8_t *index, uint16_t *langid)
{ {
uint8_t index;
switch (enum_ctrl->stage) { switch (enum_ctrl->stage) {
case ENUM_STAGE_GET_SHORT_LANGID_TABLE: case ENUM_STAGE_GET_SHORT_LANGID_TABLE:
case ENUM_STAGE_GET_FULL_LANGID_TABLE: case ENUM_STAGE_GET_FULL_LANGID_TABLE:
index = 0; //The LANGID table uses an index of 0 *index = 0; //The LANGID table uses an index of 0
*langid = 0; //Getting the LANGID table itself should use a LANGID of 0
break; break;
case ENUM_STAGE_GET_SHORT_MANU_STR_DESC: case ENUM_STAGE_GET_SHORT_MANU_STR_DESC:
case ENUM_STAGE_GET_FULL_MANU_STR_DESC: case ENUM_STAGE_GET_FULL_MANU_STR_DESC:
index = enum_ctrl->iManufacturer; *index = enum_ctrl->iManufacturer;
*langid = ENUM_LANGID; //Use the default LANGID
break; break;
case ENUM_STAGE_GET_SHORT_PROD_STR_DESC: case ENUM_STAGE_GET_SHORT_PROD_STR_DESC:
case ENUM_STAGE_GET_FULL_PROD_STR_DESC: case ENUM_STAGE_GET_FULL_PROD_STR_DESC:
index = enum_ctrl->iProduct; *index = enum_ctrl->iProduct;
*langid = ENUM_LANGID; //Use the default LANGID
break; break;
case ENUM_STAGE_GET_SHORT_SER_STR_DESC: case ENUM_STAGE_GET_SHORT_SER_STR_DESC:
case ENUM_STAGE_GET_FULL_SER_STR_DESC: case ENUM_STAGE_GET_FULL_SER_STR_DESC:
index = enum_ctrl->iSerialNumber; *index = enum_ctrl->iSerialNumber;
*langid = ENUM_LANGID; //Use the default LANGID
break; break;
default: default:
//Should not occur //Should not occur
index = 0;
abort(); abort();
break; break;
} }
return index;
} }
static bool enum_stage_transfer(enum_ctrl_t *enum_ctrl) static bool enum_stage_transfer(enum_ctrl_t *enum_ctrl)
@ -369,11 +370,13 @@ static bool enum_stage_transfer(enum_ctrl_t *enum_ctrl)
case ENUM_STAGE_GET_SHORT_MANU_STR_DESC: case ENUM_STAGE_GET_SHORT_MANU_STR_DESC:
case ENUM_STAGE_GET_SHORT_PROD_STR_DESC: case ENUM_STAGE_GET_SHORT_PROD_STR_DESC:
case ENUM_STAGE_GET_SHORT_SER_STR_DESC: { case ENUM_STAGE_GET_SHORT_SER_STR_DESC: {
uint8_t index = get_string_desc_index(enum_ctrl); uint8_t index;
uint16_t langid;
get_string_desc_index_and_langid(enum_ctrl, &index, &langid);
//Get only the header of the string descriptor //Get only the header of the string descriptor
USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)transfer->data_buffer, USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)transfer->data_buffer,
index, index,
ENUM_LANGID, langid,
sizeof(usb_str_desc_t)); 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); 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 //IN data stage should return exactly sizeof(usb_str_desc_t) bytes
@ -384,11 +387,13 @@ static bool enum_stage_transfer(enum_ctrl_t *enum_ctrl)
case ENUM_STAGE_GET_FULL_MANU_STR_DESC: case ENUM_STAGE_GET_FULL_MANU_STR_DESC:
case ENUM_STAGE_GET_FULL_PROD_STR_DESC: case ENUM_STAGE_GET_FULL_PROD_STR_DESC:
case ENUM_STAGE_GET_FULL_SER_STR_DESC: { case ENUM_STAGE_GET_FULL_SER_STR_DESC: {
uint8_t index = get_string_desc_index(enum_ctrl); uint8_t index;
uint16_t langid;
get_string_desc_index_and_langid(enum_ctrl, &index, &langid);
//Get the full string descriptor at a particular index, requesting the descriptors exact length //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, USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)transfer->data_buffer,
index, index,
ENUM_LANGID, langid,
enum_ctrl->str_desc_bLength); 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); 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 //IN data stage should return exactly str_desc_bLength bytes
@ -415,12 +420,16 @@ static bool enum_stage_transfer_check(enum_ctrl_t *enum_ctrl)
//Check transfer status //Check transfer status
usb_transfer_t *transfer = &dequeued_enum_urb->transfer; usb_transfer_t *transfer = &dequeued_enum_urb->transfer;
if (transfer->status != USB_TRANSFER_STATUS_COMPLETED) { if (transfer->status != USB_TRANSFER_STATUS_COMPLETED) {
ESP_LOGE(HUB_DRIVER_TAG, "Bad transfer status: %s", enum_stage_strings[enum_ctrl->stage]); ESP_LOGE(HUB_DRIVER_TAG, "Bad transfer status %d: %s", transfer->status, enum_stage_strings[enum_ctrl->stage]);
if (transfer->status == USB_TRANSFER_STATUS_STALL) {
//EP stalled, clearing the pipe to execute further stages
ESP_ERROR_CHECK(hcd_pipe_command(enum_ctrl->pipe, HCD_PIPE_CMD_CLEAR));
}
return false; return false;
} }
//Check IN transfer returned the expected correct number of bytes //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) { 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]); ESP_LOGE(HUB_DRIVER_TAG, "Incorrect number of bytes returned %d: %s", transfer->actual_num_bytes, enum_stage_strings[enum_ctrl->stage]);
return false; return false;
} }
@ -617,6 +626,27 @@ static void enum_stage_cleanup_failed(enum_ctrl_t *enum_ctrl)
HUB_DRIVER_EXIT_CRITICAL(); HUB_DRIVER_EXIT_CRITICAL();
} }
static enum_stage_t get_next_stage(enum_stage_t old_stage, enum_ctrl_t *enum_ctrl)
{
enum_stage_t new_stage = old_stage + 1;
//Skip the GET_DESCRIPTOR string type corresponding stages if a particular index is 0.
while(((new_stage == ENUM_STAGE_GET_SHORT_MANU_STR_DESC ||
new_stage == ENUM_STAGE_CHECK_SHORT_MANU_STR_DESC ||
new_stage == ENUM_STAGE_GET_FULL_MANU_STR_DESC ||
new_stage == ENUM_STAGE_CHECK_FULL_MANU_STR_DESC) && enum_ctrl->iManufacturer == 0) ||
((new_stage == ENUM_STAGE_GET_SHORT_PROD_STR_DESC ||
new_stage == ENUM_STAGE_CHECK_SHORT_PROD_STR_DESC ||
new_stage == ENUM_STAGE_GET_FULL_PROD_STR_DESC ||
new_stage == ENUM_STAGE_CHECK_FULL_PROD_STR_DESC) && enum_ctrl->iProduct == 0) ||
((new_stage == ENUM_STAGE_GET_SHORT_SER_STR_DESC ||
new_stage == ENUM_STAGE_CHECK_SHORT_SER_STR_DESC ||
new_stage == ENUM_STAGE_GET_FULL_SER_STR_DESC ||
new_stage == ENUM_STAGE_CHECK_FULL_SER_STR_DESC) && enum_ctrl->iSerialNumber == 0)) {
new_stage++;
}
return new_stage;
}
static void enum_set_next_stage(enum_ctrl_t *enum_ctrl, bool last_stage_pass) static void enum_set_next_stage(enum_ctrl_t *enum_ctrl, bool last_stage_pass)
{ {
//Set next stage //Set next stage
@ -624,7 +654,7 @@ static void enum_set_next_stage(enum_ctrl_t *enum_ctrl, bool last_stage_pass)
if (enum_ctrl->stage != ENUM_STAGE_NONE && if (enum_ctrl->stage != ENUM_STAGE_NONE &&
enum_ctrl->stage != ENUM_STAGE_CLEANUP && enum_ctrl->stage != ENUM_STAGE_CLEANUP &&
enum_ctrl->stage != ENUM_STAGE_CLEANUP_FAILED) { enum_ctrl->stage != ENUM_STAGE_CLEANUP_FAILED) {
enum_ctrl->stage++; //Go to next stage enum_ctrl->stage = get_next_stage(enum_ctrl->stage, enum_ctrl);
} else { } else {
enum_ctrl->stage = ENUM_STAGE_NONE; enum_ctrl->stage = ENUM_STAGE_NONE;
} }

View File

@ -76,9 +76,6 @@ int usb_parse_interface_number_of_alternate(const usb_config_desc_t *config_desc
const usb_intf_desc_t *usb_parse_interface_descriptor(const usb_config_desc_t *config_desc, uint8_t bInterfaceNumber, uint8_t bAlternateSetting, int *offset) const usb_intf_desc_t *usb_parse_interface_descriptor(const usb_config_desc_t *config_desc, uint8_t bInterfaceNumber, uint8_t bAlternateSetting, int *offset)
{ {
assert(config_desc != NULL); assert(config_desc != NULL);
if (bInterfaceNumber >= config_desc->bNumInterfaces) {
return NULL; //bInterfaceNumber is out of range
}
//Walk to first interface descriptor of bInterfaceNumber //Walk to first interface descriptor of bInterfaceNumber
int offset_temp = 0; int offset_temp = 0;