Refresh the USB device strings when a USB device is re-enumerated.

Submitted by:	Horse Ma <Shichun.Ma@dell.com>
MFC after:	1 week
Sponsored by:	Mellanox Technologies
This commit is contained in:
Hans Petter Selasky 2020-05-11 20:55:04 +00:00
parent 5e46d47f93
commit f54ab96def
3 changed files with 110 additions and 71 deletions

View file

@ -103,7 +103,6 @@ static void usb_suspend_resume_sub(struct usb_device *, device_t,
uint8_t);
static usb_proc_callback_t usbd_clear_stall_proc;
static usb_error_t usb_config_parse(struct usb_device *, uint8_t, uint8_t);
static void usbd_set_device_strings(struct usb_device *);
#if USB_HAVE_DEVCTL
static void usb_notify_addq(const char *type, struct usb_device *);
#endif
@ -1651,6 +1650,85 @@ usbd_clear_stall_proc(struct usb_proc_msg *_pm)
USB_BUS_LOCK(udev->bus);
}
/*------------------------------------------------------------------------*
* usb_get_langid
*
* This function tries to figure out the USB string language to use.
*------------------------------------------------------------------------*/
void
usb_get_langid(struct usb_device *udev)
{
uint8_t *scratch_ptr;
uint8_t do_unlock;
int err;
/*
* Workaround for buggy USB devices.
*
* It appears that some string-less USB chips will crash and
* disappear if any attempts are made to read any string
* descriptors.
*
* Try to detect such chips by checking the strings in the USB
* device descriptor. If no strings are present there we
* simply disable all USB strings.
*/
/* Protect scratch area */
do_unlock = usbd_ctrl_lock(udev);
scratch_ptr = udev->scratch.data;
if (udev->flags.no_strings) {
err = USB_ERR_INVAL;
} else if (udev->ddesc.iManufacturer ||
udev->ddesc.iProduct ||
udev->ddesc.iSerialNumber) {
/* read out the language ID string */
err = usbd_req_get_string_desc(udev, NULL,
(char *)scratch_ptr, 4, 0, USB_LANGUAGE_TABLE);
} else {
err = USB_ERR_INVAL;
}
if (err || (scratch_ptr[0] < 4)) {
udev->flags.no_strings = 1;
} else {
uint16_t langid;
uint16_t pref;
uint16_t mask;
uint8_t x;
/* load preferred value and mask */
pref = usb_lang_id;
mask = usb_lang_mask;
/* align length correctly */
scratch_ptr[0] &= ~1U;
/* fix compiler warning */
langid = 0;
/* search for preferred language */
for (x = 2; x < scratch_ptr[0]; x += 2) {
langid = UGETW(scratch_ptr + x);
if ((langid & mask) == pref)
break;
}
if (x >= scratch_ptr[0]) {
/* pick the first language as the default */
DPRINTFN(1, "Using first language\n");
langid = UGETW(scratch_ptr + 2);
}
DPRINTFN(1, "Language selected: 0x%04x\n", langid);
udev->langid = langid;
}
if (do_unlock)
usbd_ctrl_unlock(udev);
}
/*------------------------------------------------------------------------*
* usb_alloc_device
*
@ -1672,13 +1750,11 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
struct usb_device *udev;
struct usb_device *adev;
struct usb_device *hub;
uint8_t *scratch_ptr;
usb_error_t err;
uint8_t device_index;
uint8_t config_index;
uint8_t config_quirk;
uint8_t set_config_failed;
uint8_t do_unlock;
DPRINTF("parent_dev=%p, bus=%p, parent_hub=%p, depth=%u, "
"port_index=%u, port_no=%u, speed=%u, usb_mode=%u\n",
@ -1888,76 +1964,13 @@ usb_alloc_device(device_t parent_dev, struct usb_bus *bus,
if (usb_test_quirk(&uaa, UQ_NO_STRINGS)) {
udev->flags.no_strings = 1;
}
/*
* Workaround for buggy USB devices.
*
* It appears that some string-less USB chips will crash and
* disappear if any attempts are made to read any string
* descriptors.
*
* Try to detect such chips by checking the strings in the USB
* device descriptor. If no strings are present there we
* simply disable all USB strings.
*/
/* Protect scratch area */
do_unlock = usbd_ctrl_lock(udev);
scratch_ptr = udev->scratch.data;
if (udev->flags.no_strings) {
err = USB_ERR_INVAL;
} else if (udev->ddesc.iManufacturer ||
udev->ddesc.iProduct ||
udev->ddesc.iSerialNumber) {
/* read out the language ID string */
err = usbd_req_get_string_desc(udev, NULL,
(char *)scratch_ptr, 4, 0, USB_LANGUAGE_TABLE);
} else {
err = USB_ERR_INVAL;
}
if (err || (scratch_ptr[0] < 4)) {
udev->flags.no_strings = 1;
} else {
uint16_t langid;
uint16_t pref;
uint16_t mask;
uint8_t x;
/* load preferred value and mask */
pref = usb_lang_id;
mask = usb_lang_mask;
/* align length correctly */
scratch_ptr[0] &= ~1U;
/* fix compiler warning */
langid = 0;
/* search for preferred language */
for (x = 2; (x < scratch_ptr[0]); x += 2) {
langid = UGETW(scratch_ptr + x);
if ((langid & mask) == pref)
break;
}
if (x >= scratch_ptr[0]) {
/* pick the first language as the default */
DPRINTFN(1, "Using first language\n");
langid = UGETW(scratch_ptr + 2);
}
DPRINTFN(1, "Language selected: 0x%04x\n", langid);
udev->langid = langid;
}
if (do_unlock)
usbd_ctrl_unlock(udev);
usb_get_langid(udev);
/* assume 100mA bus powered for now. Changed when configured. */
udev->power = USB_MIN_POWER;
/* fetch the vendor and product strings from the device */
usbd_set_device_strings(udev);
usb_set_device_strings(udev);
if (udev->flags.usb_mode == USB_MODE_DEVICE) {
/* USB device mode setup is complete */
@ -2477,8 +2490,8 @@ struct usb_knowndev {
#include "usbdevs_data.h"
#endif /* USB_VERBOSE */
static void
usbd_set_device_strings(struct usb_device *udev)
void
usb_set_device_strings(struct usb_device *udev)
{
struct usb_device_descriptor *udd = &udev->ddesc;
#ifdef USB_VERBOSE
@ -2499,6 +2512,16 @@ usbd_set_device_strings(struct usb_device *udev)
vendor_id = UGETW(udd->idVendor);
product_id = UGETW(udd->idProduct);
/* cleanup old strings, if any */
free(udev->serial, M_USB);
free(udev->manufacturer, M_USB);
free(udev->product, M_USB);
/* zero the string pointers */
udev->serial = NULL;
udev->manufacturer = NULL;
udev->product = NULL;
/* get serial number string */
usbd_req_get_string_any(udev, NULL, temp_ptr, temp_size,
udev->ddesc.iSerialNumber);

View file

@ -328,6 +328,9 @@ struct usb_endpoint *usb_endpoint_foreach(struct usb_device *udev, struct usb_en
void usb_set_device_state(struct usb_device *, enum usb_dev_state);
enum usb_dev_state usb_get_device_state(struct usb_device *);
void usb_set_device_strings(struct usb_device *);
void usb_get_langid(struct usb_device *);
uint8_t usbd_enum_lock(struct usb_device *);
#if USB_HAVE_UGEN
uint8_t usbd_enum_lock_sig(struct usb_device *);

View file

@ -441,8 +441,14 @@ uhub_explore_handle_re_enumerate(struct usb_device *child)
} else {
err = usbd_req_re_enumerate(child, NULL);
}
if (err == 0)
if (err == 0) {
/* refresh device strings */
usb_get_langid(child);
usb_set_device_strings(child);
/* set default configuration */
err = usbd_set_config_index(child, 0);
}
if (err == 0) {
err = usb_probe_and_attach(child,
USB_IFACE_INDEX_ANY);
@ -1689,6 +1695,7 @@ uhub_child_pnpinfo_string(device_t parent, device_t child,
struct usb_hub *hub;
struct usb_interface *iface;
struct hub_result res;
uint8_t do_unlock;
if (!device_is_attached(parent)) {
if (buflen)
@ -1710,6 +1717,9 @@ uhub_child_pnpinfo_string(device_t parent, device_t child,
}
iface = usbd_get_iface(res.udev, res.iface_index);
if (iface && iface->idesc) {
/* Make sure device information is not changed during the print. */
do_unlock = usbd_ctrl_lock(res.udev);
snprintf(buf, buflen, "vendor=0x%04x product=0x%04x "
"devclass=0x%02x devsubclass=0x%02x "
"devproto=0x%02x "
@ -1731,6 +1741,9 @@ uhub_child_pnpinfo_string(device_t parent, device_t child,
iface->idesc->bInterfaceProtocol,
iface->pnpinfo ? " " : "",
iface->pnpinfo ? iface->pnpinfo : "");
if (do_unlock)
usbd_ctrl_unlock(res.udev);
} else {
if (buflen) {
buf[0] = '\0';