usb_host: Fix error when fetching LANGID table

USB devices may support string descriptors in multiple languages. The supported
languages are stored in a LANGID table, which itself is a string descriptor at
index 0.

When fetching the LANGID table itself, the USB 2.0 specification does not
specify what LANGID to use, thus the Hub driver would use the default LANGID
"ENUM_LANGID". However, this would cause some devices to stall.

This commit fixes the issue by always requesting the LANGID table itself using
a LANGID of 0.
This commit is contained in:
Darian Leung 2022-11-22 00:41:15 +08:00 committed by Saurabh Kumar Bansal
parent 3f1cff699d
commit 6e9500ad13

View File

@ -288,33 +288,34 @@ static bool enum_stage_second_reset(enum_ctrl_t *enum_ctrl)
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) {
case ENUM_STAGE_GET_SHORT_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;
case ENUM_STAGE_GET_SHORT_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;
case ENUM_STAGE_GET_SHORT_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;
case ENUM_STAGE_GET_SHORT_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;
default:
//Should not occur
index = 0;
abort();
break;
}
return index;
}
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_PROD_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
USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)transfer->data_buffer,
index,
ENUM_LANGID,
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
@ -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_PROD_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
USB_SETUP_PACKET_INIT_GET_STR_DESC((usb_setup_packet_t *)transfer->data_buffer,
index,
ENUM_LANGID,
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
@ -415,12 +420,12 @@ static bool enum_stage_transfer_check(enum_ctrl_t *enum_ctrl)
//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]);
ESP_LOGE(HUB_DRIVER_TAG, "Bad transfer status %d: %s", transfer->status, 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]);
ESP_LOGE(HUB_DRIVER_TAG, "Incorrect number of bytes returned %d: %s", transfer->actual_num_bytes, enum_stage_strings[enum_ctrl->stage]);
return false;
}