mirror of
https://github.com/opnsense/src.git
synced 2026-05-28 04:12:45 -04:00
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:
parent
5e46d47f93
commit
f54ab96def
3 changed files with 110 additions and 71 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 *);
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
Loading…
Reference in a new issue