mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 01:11:51 -04:00
Certain third-party USB game controllers exposing (or spoofing) an Xbox
360-compatible interface (VID:PID 045e:028e) fail to enumerate under Linux.
The device disconnects from the bus without responding to the initial
GET_DESCRIPTOR(CONFIGURATION) request, and the kernel logs 'unable to read
config index 0 descriptor/start: -71'.
The device then falls back to a secondary Android HID mode (with a
different VID:PID), losing XInput functionality including rumble support.
The failure reproduces across multiple machines, host controller types, and
kernel versions including current mainline and LTS. The device enumerates
correctly and remains in XInput mode under Windows. Notably, the device
enumerates correctly in Android mode when the same 9-byte request
is issued for that mode's configuration descriptor, confirming the firmware
bug is specific to the XInput mode.
usbmon traces from Linux and Wireshark/USBPcap traces from Windows are
identical up to the point of failure, with no visible protocol-level
difference explaining the divergence. The root cause was identified when
Michal Pecio discovered via a QEMU bus-level capture that Windows does not
use wLength=9 for the initial config descriptor request; it uses
wLength=255. Alan Stern subsequently confirmed this with a bus
analyzer on a different USB 2.0 device, and Michal verified the behavior
goes back to Windows 95 OSR2.1.
So, add a new quirk flag USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE which causes
usb_get_configuration() to issue a 255 byte sized configuration request
instead of USB_DT_CONFIG_SIZE (9) for the initial
GET_DESCRIPTOR(CONFIGURATION) request, mimicking long-standing Windows
behavior.
This patch intentionally does not add any new VID:PID entries using this
quirk. Some affected Xbox 360-compatible controllers spoof Microsoft's
VID:PID, while genuine Microsoft controllers already enumerate correctly
and do not require this quirk. Other affected clone devices use their own
VID:PID pairs and can be added individually as they are identified.
Suggested-by: Alan Stern <stern@rowland.harvard.edu>
Suggested-by: Michal Pecio <michal.pecio@gmail.com>
Closes: https://lore.kernel.org/linux-usb/CAFgddh+JWdT4LLwMc5qjM8q_pBu-fRo2qADR5ovAKoGHWMQrRw@mail.gmail.com/
Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Cc: stable <stable@kernel.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Nikhil Solanke <nikhilsolanke5@gmail.com>
Link: https://patch.msgid.link/20260728195158.65162-2-nikhilsolanke5@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1164 lines
34 KiB
C
1164 lines
34 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Released under the GPLv2 only.
|
|
*/
|
|
|
|
#include <linux/usb.h>
|
|
#include <linux/usb/ch9.h>
|
|
#include <linux/usb/hcd.h>
|
|
#include <linux/usb/quirks.h>
|
|
#include <linux/module.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string_choices.h>
|
|
#include <linux/device.h>
|
|
#include <asm/byteorder.h>
|
|
#include "usb.h"
|
|
|
|
|
|
#define USB_MAXALTSETTING 128 /* Hard limit */
|
|
|
|
#define USB_MAXCONFIG 8 /* Arbitrary limit */
|
|
|
|
static int find_next_descriptor(unsigned char *buffer, int size,
|
|
int dt1, int dt2, int *num_skipped)
|
|
{
|
|
struct usb_descriptor_header *h;
|
|
int n = 0;
|
|
unsigned char *buffer0 = buffer;
|
|
|
|
/* Find the next descriptor of type dt1 or dt2 */
|
|
while (size > 0) {
|
|
h = (struct usb_descriptor_header *) buffer;
|
|
if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2)
|
|
break;
|
|
buffer += h->bLength;
|
|
size -= h->bLength;
|
|
++n;
|
|
}
|
|
|
|
/* Store the number of descriptors skipped and return the
|
|
* number of bytes skipped */
|
|
if (num_skipped)
|
|
*num_skipped = n;
|
|
return buffer - buffer0;
|
|
}
|
|
|
|
static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev,
|
|
int cfgno, int inum, int asnum, struct usb_host_endpoint *ep,
|
|
unsigned char *buffer, int size)
|
|
{
|
|
struct usb_ssp_isoc_ep_comp_descriptor *desc;
|
|
|
|
/*
|
|
* The SuperSpeedPlus Isoc endpoint companion descriptor immediately
|
|
* follows the SuperSpeed Endpoint Companion descriptor
|
|
*/
|
|
desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer;
|
|
if (size < USB_DT_SSP_ISOC_EP_COMP_SIZE ||
|
|
desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP) {
|
|
dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion for config %d interface %d altsetting %d ep 0x%X.\n",
|
|
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
|
return;
|
|
}
|
|
memcpy(&ep->ssp_isoc_ep_comp, desc, USB_DT_SSP_ISOC_EP_COMP_SIZE);
|
|
}
|
|
|
|
static void usb_parse_eusb2_isoc_endpoint_companion(struct device *ddev,
|
|
int cfgno, int inum, int asnum, struct usb_host_endpoint *ep,
|
|
unsigned char *buffer, int size)
|
|
{
|
|
struct usb_eusb2_isoc_ep_comp_descriptor *desc;
|
|
struct usb_descriptor_header *h;
|
|
|
|
/*
|
|
* eUSB2 isochronous endpoint companion descriptor for this endpoint
|
|
* shall be declared before the next endpoint or interface descriptor
|
|
*/
|
|
while (size >= USB_DT_EUSB2_ISOC_EP_COMP_SIZE) {
|
|
h = (struct usb_descriptor_header *)buffer;
|
|
|
|
if (h->bDescriptorType == USB_DT_EUSB2_ISOC_ENDPOINT_COMP) {
|
|
desc = (struct usb_eusb2_isoc_ep_comp_descriptor *)buffer;
|
|
ep->eusb2_isoc_ep_comp = *desc;
|
|
return;
|
|
}
|
|
if (h->bDescriptorType == USB_DT_ENDPOINT ||
|
|
h->bDescriptorType == USB_DT_INTERFACE)
|
|
break;
|
|
|
|
buffer += h->bLength;
|
|
size -= h->bLength;
|
|
}
|
|
|
|
dev_notice(ddev, "No eUSB2 isoc ep 0x%X companion for config %d interface %d altsetting %d\n",
|
|
ep->desc.bEndpointAddress, cfgno, inum, asnum);
|
|
}
|
|
|
|
static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
|
|
int inum, int asnum, struct usb_host_endpoint *ep,
|
|
unsigned char *buffer, int size)
|
|
{
|
|
struct usb_ss_ep_comp_descriptor *desc;
|
|
int max_tx;
|
|
|
|
/* The SuperSpeed endpoint companion descriptor is supposed to
|
|
* be the first thing immediately following the endpoint descriptor.
|
|
*/
|
|
desc = (struct usb_ss_ep_comp_descriptor *) buffer;
|
|
|
|
if (size < USB_DT_SS_EP_COMP_SIZE) {
|
|
dev_notice(ddev,
|
|
"invalid SuperSpeed endpoint companion descriptor "
|
|
"of length %d, skipping\n", size);
|
|
return;
|
|
}
|
|
|
|
if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) {
|
|
dev_notice(ddev, "No SuperSpeed endpoint companion for config %d interface %d altsetting %d ep 0x%X: using minimum values\n",
|
|
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
|
|
|
/* Fill in some default values.
|
|
* Leave bmAttributes as zero, which will mean no streams for
|
|
* bulk, and isoc won't support multiple bursts of packets.
|
|
* With bursts of only one packet, and a Mult of 1, the max
|
|
* amount of data moved per endpoint service interval is one
|
|
* packet.
|
|
*/
|
|
ep->ss_ep_comp.bLength = USB_DT_SS_EP_COMP_SIZE;
|
|
ep->ss_ep_comp.bDescriptorType = USB_DT_SS_ENDPOINT_COMP;
|
|
if (usb_endpoint_xfer_isoc(&ep->desc) ||
|
|
usb_endpoint_xfer_int(&ep->desc))
|
|
ep->ss_ep_comp.wBytesPerInterval =
|
|
ep->desc.wMaxPacketSize;
|
|
return;
|
|
}
|
|
buffer += desc->bLength;
|
|
size -= desc->bLength;
|
|
memcpy(&ep->ss_ep_comp, desc, USB_DT_SS_EP_COMP_SIZE);
|
|
|
|
/* Check the various values */
|
|
if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) {
|
|
dev_notice(ddev, "Control endpoint with bMaxBurst = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n",
|
|
desc->bMaxBurst, cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
|
ep->ss_ep_comp.bMaxBurst = 0;
|
|
} else if (desc->bMaxBurst > 15) {
|
|
dev_notice(ddev, "Endpoint with bMaxBurst = %d in config %d interface %d altsetting %d ep 0x%X: setting to 15\n",
|
|
desc->bMaxBurst, cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
|
ep->ss_ep_comp.bMaxBurst = 15;
|
|
}
|
|
|
|
if ((usb_endpoint_xfer_control(&ep->desc) ||
|
|
usb_endpoint_xfer_int(&ep->desc)) &&
|
|
desc->bmAttributes != 0) {
|
|
dev_notice(ddev, "%s endpoint with bmAttributes = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n",
|
|
usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk",
|
|
desc->bmAttributes,
|
|
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
|
ep->ss_ep_comp.bmAttributes = 0;
|
|
} else if (usb_endpoint_xfer_bulk(&ep->desc) &&
|
|
desc->bmAttributes > 16) {
|
|
dev_notice(ddev, "Bulk endpoint with more than 65536 streams in config %d interface %d altsetting %d ep 0x%X: setting to max\n",
|
|
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
|
ep->ss_ep_comp.bmAttributes = 16;
|
|
} else if (usb_endpoint_xfer_isoc(&ep->desc) &&
|
|
!USB_SS_SSP_ISOC_COMP(desc->bmAttributes) &&
|
|
USB_SS_MULT(desc->bmAttributes) > 3) {
|
|
dev_notice(ddev, "Isoc endpoint has Mult of %d in config %d interface %d altsetting %d ep 0x%X: setting to 3\n",
|
|
USB_SS_MULT(desc->bmAttributes),
|
|
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
|
ep->ss_ep_comp.bmAttributes = 2;
|
|
}
|
|
|
|
if (usb_endpoint_xfer_isoc(&ep->desc))
|
|
max_tx = (desc->bMaxBurst + 1) *
|
|
(USB_SS_MULT(desc->bmAttributes)) *
|
|
usb_endpoint_maxp(&ep->desc);
|
|
else if (usb_endpoint_xfer_int(&ep->desc))
|
|
max_tx = usb_endpoint_maxp(&ep->desc) *
|
|
(desc->bMaxBurst + 1);
|
|
else
|
|
max_tx = 999999;
|
|
/*
|
|
* wBytesPerInterval > max_tx is bogus, but USB3 spec doesn't forbid the opposite.
|
|
* Experience shows that wBytesPerInterval < wMaxPacketSize on common interrupt IN
|
|
* endpoints is usually bogus too, and recent HCs enforce interrupt BW limits.
|
|
*/
|
|
if (le16_to_cpu(desc->wBytesPerInterval) > max_tx ||
|
|
(le16_to_cpu(desc->wBytesPerInterval) < usb_endpoint_maxp(&ep->desc) &&
|
|
usb_endpoint_is_int_in(&ep->desc))) {
|
|
dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in config %d interface %d altsetting %d ep 0x%X: setting to %d\n",
|
|
usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int",
|
|
le16_to_cpu(desc->wBytesPerInterval),
|
|
cfgno, inum, asnum, ep->desc.bEndpointAddress,
|
|
max_tx);
|
|
ep->ss_ep_comp.wBytesPerInterval = cpu_to_le16(max_tx);
|
|
}
|
|
/* Parse a possible SuperSpeedPlus isoc ep companion descriptor */
|
|
if (usb_endpoint_xfer_isoc(&ep->desc) &&
|
|
USB_SS_SSP_ISOC_COMP(desc->bmAttributes))
|
|
usb_parse_ssp_isoc_endpoint_companion(ddev, cfgno, inum, asnum,
|
|
ep, buffer, size);
|
|
}
|
|
|
|
static const unsigned short low_speed_maxpacket_maxes[4] = {
|
|
[USB_ENDPOINT_XFER_CONTROL] = 8,
|
|
[USB_ENDPOINT_XFER_ISOC] = 0,
|
|
[USB_ENDPOINT_XFER_BULK] = 0,
|
|
[USB_ENDPOINT_XFER_INT] = 8,
|
|
};
|
|
static const unsigned short full_speed_maxpacket_maxes[4] = {
|
|
[USB_ENDPOINT_XFER_CONTROL] = 64,
|
|
[USB_ENDPOINT_XFER_ISOC] = 1023,
|
|
[USB_ENDPOINT_XFER_BULK] = 64,
|
|
[USB_ENDPOINT_XFER_INT] = 64,
|
|
};
|
|
static const unsigned short high_speed_maxpacket_maxes[4] = {
|
|
[USB_ENDPOINT_XFER_CONTROL] = 64,
|
|
[USB_ENDPOINT_XFER_ISOC] = 1024,
|
|
|
|
/* Bulk should be 512, but some devices use 1024: we will warn below */
|
|
[USB_ENDPOINT_XFER_BULK] = 1024,
|
|
[USB_ENDPOINT_XFER_INT] = 1024,
|
|
};
|
|
static const unsigned short super_speed_maxpacket_maxes[4] = {
|
|
[USB_ENDPOINT_XFER_CONTROL] = 512,
|
|
[USB_ENDPOINT_XFER_ISOC] = 1024,
|
|
[USB_ENDPOINT_XFER_BULK] = 1024,
|
|
[USB_ENDPOINT_XFER_INT] = 1024,
|
|
};
|
|
|
|
static bool endpoint_is_duplicate(struct usb_endpoint_descriptor *e1,
|
|
struct usb_endpoint_descriptor *e2)
|
|
{
|
|
if (e1->bEndpointAddress == e2->bEndpointAddress)
|
|
return true;
|
|
|
|
if (usb_endpoint_xfer_control(e1) || usb_endpoint_xfer_control(e2)) {
|
|
if (usb_endpoint_num(e1) == usb_endpoint_num(e2))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* Check for duplicate endpoint addresses in other interfaces and in the
|
|
* altsetting currently being parsed.
|
|
*/
|
|
static bool config_endpoint_is_duplicate(struct usb_host_config *config,
|
|
int inum, int asnum, struct usb_endpoint_descriptor *d)
|
|
{
|
|
struct usb_endpoint_descriptor *epd;
|
|
struct usb_interface_cache *intfc;
|
|
struct usb_host_interface *alt;
|
|
int i, j, k;
|
|
|
|
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
|
intfc = config->intf_cache[i];
|
|
|
|
for (j = 0; j < intfc->num_altsetting; ++j) {
|
|
alt = &intfc->altsetting[j];
|
|
|
|
if (alt->desc.bInterfaceNumber == inum &&
|
|
alt->desc.bAlternateSetting != asnum)
|
|
continue;
|
|
|
|
for (k = 0; k < alt->desc.bNumEndpoints; ++k) {
|
|
epd = &alt->endpoint[k].desc;
|
|
|
|
if (endpoint_is_duplicate(epd, d))
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static int usb_parse_endpoint(struct device *ddev, int cfgno,
|
|
struct usb_host_config *config, int inum, int asnum,
|
|
struct usb_host_interface *ifp, int num_ep,
|
|
unsigned char *buffer, int size)
|
|
{
|
|
struct usb_device *udev = to_usb_device(ddev);
|
|
unsigned char *buffer0 = buffer;
|
|
struct usb_endpoint_descriptor *d;
|
|
struct usb_host_endpoint *endpoint;
|
|
int n, i, j, retval;
|
|
unsigned int maxp;
|
|
const unsigned short *maxpacket_maxes;
|
|
u16 bcdUSB;
|
|
|
|
d = (struct usb_endpoint_descriptor *) buffer;
|
|
bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB);
|
|
buffer += d->bLength;
|
|
size -= d->bLength;
|
|
|
|
if (d->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE)
|
|
n = USB_DT_ENDPOINT_AUDIO_SIZE;
|
|
else if (d->bLength >= USB_DT_ENDPOINT_SIZE)
|
|
n = USB_DT_ENDPOINT_SIZE;
|
|
else {
|
|
dev_notice(ddev, "config %d interface %d altsetting %d has an "
|
|
"invalid endpoint descriptor of length %d, skipping\n",
|
|
cfgno, inum, asnum, d->bLength);
|
|
goto skip_to_next_endpoint_or_interface_descriptor;
|
|
}
|
|
|
|
i = usb_endpoint_num(d);
|
|
if (i == 0) {
|
|
dev_notice(ddev, "config %d interface %d altsetting %d has an "
|
|
"invalid descriptor for endpoint zero, skipping\n",
|
|
cfgno, inum, asnum);
|
|
goto skip_to_next_endpoint_or_interface_descriptor;
|
|
}
|
|
|
|
/* Only store as many endpoints as we have room for */
|
|
if (ifp->desc.bNumEndpoints >= num_ep)
|
|
goto skip_to_next_endpoint_or_interface_descriptor;
|
|
|
|
/* Save a copy of the descriptor and use it instead of the original */
|
|
endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
|
|
memcpy(&endpoint->desc, d, n);
|
|
d = &endpoint->desc;
|
|
|
|
/* Clear the reserved bits in bEndpointAddress */
|
|
i = d->bEndpointAddress &
|
|
(USB_ENDPOINT_DIR_MASK | USB_ENDPOINT_NUMBER_MASK);
|
|
if (i != d->bEndpointAddress) {
|
|
dev_notice(ddev, "config %d interface %d altsetting %d has an endpoint descriptor with address 0x%X, changing to 0x%X\n",
|
|
cfgno, inum, asnum, d->bEndpointAddress, i);
|
|
endpoint->desc.bEndpointAddress = i;
|
|
}
|
|
|
|
/* Check for duplicate endpoint addresses */
|
|
if (config_endpoint_is_duplicate(config, inum, asnum, d)) {
|
|
dev_notice(ddev, "config %d interface %d altsetting %d has a duplicate endpoint with address 0x%X, skipping\n",
|
|
cfgno, inum, asnum, d->bEndpointAddress);
|
|
goto skip_to_next_endpoint_or_interface_descriptor;
|
|
}
|
|
|
|
/* Ignore some endpoints */
|
|
if (udev->quirks & USB_QUIRK_ENDPOINT_IGNORE) {
|
|
if (usb_endpoint_is_ignored(udev, ifp, d)) {
|
|
dev_notice(ddev, "config %d interface %d altsetting %d has an ignored endpoint with address 0x%X, skipping\n",
|
|
cfgno, inum, asnum,
|
|
d->bEndpointAddress);
|
|
goto skip_to_next_endpoint_or_interface_descriptor;
|
|
}
|
|
}
|
|
|
|
/* Accept this endpoint */
|
|
++ifp->desc.bNumEndpoints;
|
|
INIT_LIST_HEAD(&endpoint->urb_list);
|
|
|
|
/*
|
|
* Fix up bInterval values outside the legal range.
|
|
* Use 10 or 8 ms if no proper value can be guessed.
|
|
*/
|
|
i = 0; /* i = min, j = max, n = default */
|
|
j = 255;
|
|
if (usb_endpoint_xfer_int(d)) {
|
|
i = 1;
|
|
switch (udev->speed) {
|
|
case USB_SPEED_SUPER_PLUS:
|
|
case USB_SPEED_SUPER:
|
|
case USB_SPEED_HIGH:
|
|
/*
|
|
* Many device manufacturers are using full-speed
|
|
* bInterval values in high-speed interrupt endpoint
|
|
* descriptors. Try to fix those and fall back to an
|
|
* 8-ms default value otherwise.
|
|
*/
|
|
n = fls(d->bInterval*8);
|
|
if (n == 0)
|
|
n = 7; /* 8 ms = 2^(7-1) uframes */
|
|
j = 16;
|
|
|
|
/*
|
|
* Adjust bInterval for quirked devices.
|
|
*/
|
|
/*
|
|
* This quirk fixes bIntervals reported in ms.
|
|
*/
|
|
if (udev->quirks & USB_QUIRK_LINEAR_FRAME_INTR_BINTERVAL) {
|
|
n = clamp(fls(d->bInterval) + 3, i, j);
|
|
i = j = n;
|
|
}
|
|
/*
|
|
* This quirk fixes bIntervals reported in
|
|
* linear microframes.
|
|
*/
|
|
if (udev->quirks & USB_QUIRK_LINEAR_UFRAME_INTR_BINTERVAL) {
|
|
n = clamp(fls(d->bInterval), i, j);
|
|
i = j = n;
|
|
}
|
|
break;
|
|
default: /* USB_SPEED_FULL or _LOW */
|
|
/*
|
|
* For low-speed, 10 ms is the official minimum.
|
|
* But some "overclocked" devices might want faster
|
|
* polling so we'll allow it.
|
|
*/
|
|
n = 10;
|
|
break;
|
|
}
|
|
} else if (usb_endpoint_xfer_isoc(d)) {
|
|
i = 1;
|
|
j = 16;
|
|
switch (udev->speed) {
|
|
case USB_SPEED_HIGH:
|
|
n = 7; /* 8 ms = 2^(7-1) uframes */
|
|
break;
|
|
default: /* USB_SPEED_FULL */
|
|
n = 4; /* 8 ms = 2^(4-1) frames */
|
|
break;
|
|
}
|
|
}
|
|
if (d->bInterval < i || d->bInterval > j) {
|
|
dev_notice(ddev, "config %d interface %d altsetting %d "
|
|
"endpoint 0x%X has an invalid bInterval %d, "
|
|
"changing to %d\n",
|
|
cfgno, inum, asnum,
|
|
d->bEndpointAddress, d->bInterval, n);
|
|
endpoint->desc.bInterval = n;
|
|
}
|
|
|
|
/* Some buggy low-speed devices have Bulk endpoints, which is
|
|
* explicitly forbidden by the USB spec. In an attempt to make
|
|
* them usable, we will try treating them as Interrupt endpoints.
|
|
*/
|
|
if (udev->speed == USB_SPEED_LOW && usb_endpoint_xfer_bulk(d)) {
|
|
dev_notice(ddev, "config %d interface %d altsetting %d "
|
|
"endpoint 0x%X is Bulk; changing to Interrupt\n",
|
|
cfgno, inum, asnum, d->bEndpointAddress);
|
|
endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT;
|
|
endpoint->desc.bInterval = 1;
|
|
if (usb_endpoint_maxp(&endpoint->desc) > 8)
|
|
endpoint->desc.wMaxPacketSize = cpu_to_le16(8);
|
|
}
|
|
|
|
/*
|
|
* Validate the wMaxPacketSize field.
|
|
* eUSB2 devices (see USB 2.0 Double Isochronous IN ECN 9.6.6 Endpoint)
|
|
* and devices with isochronous endpoints in altsetting 0 (see USB 2.0
|
|
* end of section 5.6.3) have wMaxPacketSize = 0.
|
|
* So don't warn about those.
|
|
*/
|
|
maxp = le16_to_cpu(endpoint->desc.wMaxPacketSize);
|
|
|
|
if (maxp == 0 && bcdUSB != 0x0220 &&
|
|
!(usb_endpoint_xfer_isoc(d) && asnum == 0))
|
|
dev_notice(ddev, "config %d interface %d altsetting %d endpoint 0x%X has invalid wMaxPacketSize 0\n",
|
|
cfgno, inum, asnum, d->bEndpointAddress);
|
|
|
|
/* Find the highest legal maxpacket size for this endpoint */
|
|
i = 0; /* additional transactions per microframe */
|
|
switch (udev->speed) {
|
|
case USB_SPEED_LOW:
|
|
maxpacket_maxes = low_speed_maxpacket_maxes;
|
|
break;
|
|
case USB_SPEED_FULL:
|
|
maxpacket_maxes = full_speed_maxpacket_maxes;
|
|
break;
|
|
case USB_SPEED_HIGH:
|
|
/* Multiple-transactions bits are allowed only for HS periodic endpoints */
|
|
if (usb_endpoint_xfer_int(d) || usb_endpoint_xfer_isoc(d)) {
|
|
i = maxp & USB_EP_MAXP_MULT_MASK;
|
|
maxp &= ~i;
|
|
}
|
|
fallthrough;
|
|
default:
|
|
maxpacket_maxes = high_speed_maxpacket_maxes;
|
|
break;
|
|
case USB_SPEED_SUPER:
|
|
case USB_SPEED_SUPER_PLUS:
|
|
maxpacket_maxes = super_speed_maxpacket_maxes;
|
|
break;
|
|
}
|
|
j = maxpacket_maxes[usb_endpoint_type(&endpoint->desc)];
|
|
|
|
if (maxp > j) {
|
|
dev_notice(ddev, "config %d interface %d altsetting %d endpoint 0x%X has invalid maxpacket %d, setting to %d\n",
|
|
cfgno, inum, asnum, d->bEndpointAddress, maxp, j);
|
|
maxp = j;
|
|
endpoint->desc.wMaxPacketSize = cpu_to_le16(i | maxp);
|
|
}
|
|
|
|
/*
|
|
* Some buggy high speed devices have bulk endpoints using
|
|
* maxpacket sizes other than 512. High speed HCDs may not
|
|
* be able to handle that particular bug, so let's warn...
|
|
*/
|
|
if (udev->speed == USB_SPEED_HIGH && usb_endpoint_xfer_bulk(d)) {
|
|
if (maxp != 512)
|
|
dev_notice(ddev, "config %d interface %d altsetting %d "
|
|
"bulk endpoint 0x%X has invalid maxpacket %d\n",
|
|
cfgno, inum, asnum, d->bEndpointAddress,
|
|
maxp);
|
|
}
|
|
|
|
/* Parse a possible eUSB2 periodic endpoint companion descriptor */
|
|
if (udev->speed == USB_SPEED_HIGH && bcdUSB == 0x0220 &&
|
|
!le16_to_cpu(d->wMaxPacketSize) && usb_endpoint_is_isoc_in(d))
|
|
usb_parse_eusb2_isoc_endpoint_companion(ddev, cfgno, inum, asnum,
|
|
endpoint, buffer, size);
|
|
|
|
/* Parse a possible SuperSpeed endpoint companion descriptor */
|
|
if (udev->speed >= USB_SPEED_SUPER)
|
|
usb_parse_ss_endpoint_companion(ddev, cfgno,
|
|
inum, asnum, endpoint, buffer, size);
|
|
|
|
/* Skip over any Class Specific or Vendor Specific descriptors;
|
|
* find the next endpoint or interface descriptor */
|
|
endpoint->extra = buffer;
|
|
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
|
USB_DT_INTERFACE, &n);
|
|
endpoint->extralen = i;
|
|
retval = buffer - buffer0 + i;
|
|
if (n > 0)
|
|
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
|
|
n, str_plural(n), "endpoint");
|
|
return retval;
|
|
|
|
skip_to_next_endpoint_or_interface_descriptor:
|
|
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
|
USB_DT_INTERFACE, NULL);
|
|
return buffer - buffer0 + i;
|
|
}
|
|
|
|
void usb_release_interface_cache(struct kref *ref)
|
|
{
|
|
struct usb_interface_cache *intfc = ref_to_usb_interface_cache(ref);
|
|
int j;
|
|
|
|
for (j = 0; j < intfc->num_altsetting; j++) {
|
|
struct usb_host_interface *alt = &intfc->altsetting[j];
|
|
|
|
kfree(alt->endpoint);
|
|
kfree(alt->string);
|
|
}
|
|
kfree(intfc);
|
|
}
|
|
|
|
static int usb_parse_interface(struct device *ddev, int cfgno,
|
|
struct usb_host_config *config, unsigned char *buffer, int size,
|
|
u8 inums[], u8 nalts[])
|
|
{
|
|
unsigned char *buffer0 = buffer;
|
|
struct usb_interface_descriptor *d;
|
|
int inum, asnum;
|
|
struct usb_interface_cache *intfc;
|
|
struct usb_host_interface *alt;
|
|
int i, n;
|
|
int len, retval;
|
|
int num_ep, num_ep_orig;
|
|
|
|
d = (struct usb_interface_descriptor *) buffer;
|
|
buffer += d->bLength;
|
|
size -= d->bLength;
|
|
|
|
if (d->bLength < USB_DT_INTERFACE_SIZE)
|
|
goto skip_to_next_interface_descriptor;
|
|
|
|
/* Which interface entry is this? */
|
|
intfc = NULL;
|
|
inum = d->bInterfaceNumber;
|
|
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
|
|
if (inums[i] == inum) {
|
|
intfc = config->intf_cache[i];
|
|
break;
|
|
}
|
|
}
|
|
if (!intfc || intfc->num_altsetting >= nalts[i])
|
|
goto skip_to_next_interface_descriptor;
|
|
|
|
/* Check for duplicate altsetting entries */
|
|
asnum = d->bAlternateSetting;
|
|
for ((i = 0, alt = &intfc->altsetting[0]);
|
|
i < intfc->num_altsetting;
|
|
(++i, ++alt)) {
|
|
if (alt->desc.bAlternateSetting == asnum) {
|
|
dev_notice(ddev, "Duplicate descriptor for config %d "
|
|
"interface %d altsetting %d, skipping\n",
|
|
cfgno, inum, asnum);
|
|
goto skip_to_next_interface_descriptor;
|
|
}
|
|
}
|
|
|
|
++intfc->num_altsetting;
|
|
memcpy(&alt->desc, d, USB_DT_INTERFACE_SIZE);
|
|
|
|
/* Skip over any Class Specific or Vendor Specific descriptors;
|
|
* find the first endpoint or interface descriptor */
|
|
alt->extra = buffer;
|
|
i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT,
|
|
USB_DT_INTERFACE, &n);
|
|
alt->extralen = i;
|
|
if (n > 0)
|
|
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
|
|
n, str_plural(n), "interface");
|
|
buffer += i;
|
|
size -= i;
|
|
|
|
/* Allocate space for the right(?) number of endpoints */
|
|
num_ep = num_ep_orig = alt->desc.bNumEndpoints;
|
|
alt->desc.bNumEndpoints = 0; /* Use as a counter */
|
|
if (num_ep > USB_MAXENDPOINTS) {
|
|
dev_notice(ddev, "too many endpoints for config %d interface %d "
|
|
"altsetting %d: %d, using maximum allowed: %d\n",
|
|
cfgno, inum, asnum, num_ep, USB_MAXENDPOINTS);
|
|
num_ep = USB_MAXENDPOINTS;
|
|
}
|
|
|
|
if (num_ep > 0) {
|
|
/* Can't allocate 0 bytes */
|
|
len = sizeof(struct usb_host_endpoint) * num_ep;
|
|
alt->endpoint = kzalloc(len, GFP_KERNEL);
|
|
if (!alt->endpoint)
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* Parse all the endpoint descriptors */
|
|
n = 0;
|
|
while (size > 0) {
|
|
if (((struct usb_descriptor_header *) buffer)->bDescriptorType
|
|
== USB_DT_INTERFACE)
|
|
break;
|
|
retval = usb_parse_endpoint(ddev, cfgno, config, inum, asnum,
|
|
alt, num_ep, buffer, size);
|
|
if (retval < 0)
|
|
return retval;
|
|
++n;
|
|
|
|
buffer += retval;
|
|
size -= retval;
|
|
}
|
|
|
|
if (n != num_ep_orig)
|
|
dev_notice(ddev, "config %d interface %d altsetting %d has %d "
|
|
"endpoint descriptor%s, different from the interface "
|
|
"descriptor's value: %d\n",
|
|
cfgno, inum, asnum, n, str_plural(n), num_ep_orig);
|
|
return buffer - buffer0;
|
|
|
|
skip_to_next_interface_descriptor:
|
|
i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,
|
|
USB_DT_INTERFACE, NULL);
|
|
return buffer - buffer0 + i;
|
|
}
|
|
|
|
static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
|
|
struct usb_host_config *config, unsigned char *buffer, int size)
|
|
{
|
|
struct device *ddev = &dev->dev;
|
|
unsigned char *buffer0 = buffer;
|
|
int cfgno;
|
|
int nintf, nintf_orig;
|
|
int i, j, n;
|
|
struct usb_interface_cache *intfc;
|
|
unsigned char *buffer2;
|
|
int size2;
|
|
struct usb_descriptor_header *header;
|
|
int retval;
|
|
u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
|
|
unsigned iad_num = 0;
|
|
|
|
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
|
|
nintf = nintf_orig = config->desc.bNumInterfaces;
|
|
config->desc.bNumInterfaces = 0; // Adjusted later
|
|
|
|
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
|
|
config->desc.bLength < USB_DT_CONFIG_SIZE ||
|
|
config->desc.bLength > size) {
|
|
dev_notice(ddev, "invalid descriptor for config index %d: "
|
|
"type = 0x%X, length = %d\n", cfgidx,
|
|
config->desc.bDescriptorType, config->desc.bLength);
|
|
return -EINVAL;
|
|
}
|
|
cfgno = config->desc.bConfigurationValue;
|
|
|
|
buffer += config->desc.bLength;
|
|
size -= config->desc.bLength;
|
|
|
|
if (nintf > USB_MAXINTERFACES) {
|
|
dev_notice(ddev, "config %d has too many interfaces: %d, "
|
|
"using maximum allowed: %d\n",
|
|
cfgno, nintf, USB_MAXINTERFACES);
|
|
nintf = USB_MAXINTERFACES;
|
|
}
|
|
|
|
/* Go through the descriptors, checking their length and counting the
|
|
* number of altsettings for each interface */
|
|
n = 0;
|
|
for ((buffer2 = buffer, size2 = size);
|
|
size2 > 0;
|
|
(buffer2 += header->bLength, size2 -= header->bLength)) {
|
|
|
|
if (size2 < sizeof(struct usb_descriptor_header)) {
|
|
dev_notice(ddev, "config %d descriptor has %d excess "
|
|
"byte%s, ignoring\n",
|
|
cfgno, size2, str_plural(size2));
|
|
break;
|
|
}
|
|
|
|
header = (struct usb_descriptor_header *) buffer2;
|
|
if ((header->bLength > size2) || (header->bLength < 2)) {
|
|
dev_notice(ddev, "config %d has an invalid descriptor "
|
|
"of length %d, skipping remainder of the config\n",
|
|
cfgno, header->bLength);
|
|
break;
|
|
}
|
|
|
|
if (header->bDescriptorType == USB_DT_INTERFACE) {
|
|
struct usb_interface_descriptor *d;
|
|
int inum;
|
|
|
|
d = (struct usb_interface_descriptor *) header;
|
|
if (d->bLength < USB_DT_INTERFACE_SIZE) {
|
|
dev_notice(ddev, "config %d has an invalid "
|
|
"interface descriptor of length %d, "
|
|
"skipping\n", cfgno, d->bLength);
|
|
continue;
|
|
}
|
|
|
|
inum = d->bInterfaceNumber;
|
|
|
|
if ((dev->quirks & USB_QUIRK_HONOR_BNUMINTERFACES) &&
|
|
n >= nintf_orig) {
|
|
dev_notice(ddev, "config %d has more interface "
|
|
"descriptors, than it declares in "
|
|
"bNumInterfaces, ignoring interface "
|
|
"number: %d\n", cfgno, inum);
|
|
continue;
|
|
}
|
|
|
|
if (inum >= nintf_orig)
|
|
dev_notice(ddev, "config %d has an invalid "
|
|
"interface number: %d but max is %d\n",
|
|
cfgno, inum, nintf_orig - 1);
|
|
|
|
/* Have we already encountered this interface?
|
|
* Count its altsettings */
|
|
for (i = 0; i < n; ++i) {
|
|
if (inums[i] == inum)
|
|
break;
|
|
}
|
|
if (i < n) {
|
|
if (nalts[i] < 255)
|
|
++nalts[i];
|
|
} else if (n < USB_MAXINTERFACES) {
|
|
inums[n] = inum;
|
|
nalts[n] = 1;
|
|
++n;
|
|
}
|
|
|
|
} else if (header->bDescriptorType ==
|
|
USB_DT_INTERFACE_ASSOCIATION) {
|
|
struct usb_interface_assoc_descriptor *d;
|
|
|
|
d = (struct usb_interface_assoc_descriptor *)header;
|
|
if (d->bLength < USB_DT_INTERFACE_ASSOCIATION_SIZE) {
|
|
dev_notice(ddev,
|
|
"config %d has an invalid interface association descriptor of length %d, skipping\n",
|
|
cfgno, d->bLength);
|
|
continue;
|
|
}
|
|
|
|
if (iad_num == USB_MAXIADS) {
|
|
dev_notice(ddev, "found more Interface "
|
|
"Association Descriptors "
|
|
"than allocated for in "
|
|
"configuration %d\n", cfgno);
|
|
} else {
|
|
config->intf_assoc[iad_num] = d;
|
|
iad_num++;
|
|
}
|
|
|
|
} else if (header->bDescriptorType == USB_DT_DEVICE ||
|
|
header->bDescriptorType == USB_DT_CONFIG)
|
|
dev_notice(ddev, "config %d contains an unexpected "
|
|
"descriptor of type 0x%X, skipping\n",
|
|
cfgno, header->bDescriptorType);
|
|
|
|
} /* for ((buffer2 = buffer, size2 = size); ...) */
|
|
size = buffer2 - buffer;
|
|
config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0);
|
|
|
|
if (n != nintf)
|
|
dev_notice(ddev, "config %d has %d interface%s, different from "
|
|
"the descriptor's value: %d\n",
|
|
cfgno, n, str_plural(n), nintf_orig);
|
|
else if (n == 0)
|
|
dev_notice(ddev, "config %d has no interfaces?\n", cfgno);
|
|
config->desc.bNumInterfaces = nintf = n;
|
|
|
|
/* Check for missing interface numbers */
|
|
for (i = 0; i < nintf; ++i) {
|
|
for (j = 0; j < nintf; ++j) {
|
|
if (inums[j] == i)
|
|
break;
|
|
}
|
|
if (j >= nintf)
|
|
dev_notice(ddev, "config %d has no interface number "
|
|
"%d\n", cfgno, i);
|
|
}
|
|
|
|
/* Allocate the usb_interface_caches and altsetting arrays */
|
|
for (i = 0; i < nintf; ++i) {
|
|
j = nalts[i];
|
|
if (j > USB_MAXALTSETTING) {
|
|
dev_notice(ddev, "too many alternate settings for "
|
|
"config %d interface %d: %d, "
|
|
"using maximum allowed: %d\n",
|
|
cfgno, inums[i], j, USB_MAXALTSETTING);
|
|
nalts[i] = j = USB_MAXALTSETTING;
|
|
}
|
|
|
|
intfc = kzalloc_flex(*intfc, altsetting, j);
|
|
config->intf_cache[i] = intfc;
|
|
if (!intfc)
|
|
return -ENOMEM;
|
|
kref_init(&intfc->ref);
|
|
}
|
|
|
|
/* FIXME: parse the BOS descriptor */
|
|
|
|
/* Skip over any Class Specific or Vendor Specific descriptors;
|
|
* find the first interface descriptor */
|
|
config->extra = buffer;
|
|
i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,
|
|
USB_DT_INTERFACE, &n);
|
|
config->extralen = i;
|
|
if (n > 0)
|
|
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
|
|
n, str_plural(n), "configuration");
|
|
buffer += i;
|
|
size -= i;
|
|
|
|
/* Parse all the interface/altsetting descriptors */
|
|
while (size > 0) {
|
|
retval = usb_parse_interface(ddev, cfgno, config,
|
|
buffer, size, inums, nalts);
|
|
if (retval < 0)
|
|
return retval;
|
|
|
|
buffer += retval;
|
|
size -= retval;
|
|
}
|
|
|
|
/* Check for missing altsettings */
|
|
for (i = 0; i < nintf; ++i) {
|
|
intfc = config->intf_cache[i];
|
|
for (j = 0; j < intfc->num_altsetting; ++j) {
|
|
for (n = 0; n < intfc->num_altsetting; ++n) {
|
|
if (intfc->altsetting[n].desc.
|
|
bAlternateSetting == j)
|
|
break;
|
|
}
|
|
if (n >= intfc->num_altsetting)
|
|
dev_notice(ddev, "config %d interface %d has no "
|
|
"altsetting %d\n", cfgno, inums[i], j);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* hub-only!! ... and only exported for reset/reinit path.
|
|
* otherwise used internally on disconnect/destroy path
|
|
*/
|
|
void usb_destroy_configuration(struct usb_device *dev)
|
|
{
|
|
int c, i;
|
|
|
|
if (!dev->config)
|
|
return;
|
|
|
|
if (dev->rawdescriptors) {
|
|
for (i = 0; i < dev->descriptor.bNumConfigurations; i++)
|
|
kfree(dev->rawdescriptors[i]);
|
|
|
|
kfree(dev->rawdescriptors);
|
|
dev->rawdescriptors = NULL;
|
|
}
|
|
|
|
for (c = 0; c < dev->descriptor.bNumConfigurations; c++) {
|
|
struct usb_host_config *cf = &dev->config[c];
|
|
|
|
kfree(cf->string);
|
|
for (i = 0; i < cf->desc.bNumInterfaces; i++) {
|
|
if (cf->intf_cache[i])
|
|
kref_put(&cf->intf_cache[i]->ref,
|
|
usb_release_interface_cache);
|
|
}
|
|
}
|
|
kfree(dev->config);
|
|
dev->config = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the USB config descriptors, cache and parse'em
|
|
*
|
|
* hub-only!! ... and only in reset path, or usb_new_device()
|
|
* (used by real hubs and virtual root hubs)
|
|
*/
|
|
int usb_get_configuration(struct usb_device *dev)
|
|
{
|
|
struct device *ddev = &dev->dev;
|
|
int ncfg = dev->descriptor.bNumConfigurations;
|
|
unsigned int cfgno, length;
|
|
unsigned char *bigbuffer;
|
|
struct usb_config_descriptor *desc;
|
|
int result;
|
|
size_t usb_config_req_size;
|
|
|
|
/*
|
|
* We usually start by grabbing the first 9-bytes descriptor so we know
|
|
* how long the whole configuration is. Some devices with quirky
|
|
* firmware will fail enumeration, so if the quirk is set, use 255 instead,
|
|
* mirroring the behavior of Windows.
|
|
*/
|
|
if (dev->quirks & USB_QUIRK_WINDOWS_CONFIG_REQ_SIZE)
|
|
usb_config_req_size = 255;
|
|
else
|
|
usb_config_req_size = USB_DT_CONFIG_SIZE;
|
|
|
|
if (ncfg > USB_MAXCONFIG) {
|
|
dev_notice(ddev, "too many configurations: %d, "
|
|
"using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
|
|
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
|
|
}
|
|
|
|
if (ncfg < 1 && dev->quirks & USB_QUIRK_FORCE_ONE_CONFIG) {
|
|
dev_info(ddev, "Device claims zero configurations, forcing to 1\n");
|
|
dev->descriptor.bNumConfigurations = 1;
|
|
ncfg = 1;
|
|
} else if (ncfg < 1) {
|
|
dev_err(ddev, "no configurations\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
length = ncfg * sizeof(struct usb_host_config);
|
|
dev->config = kzalloc(length, GFP_KERNEL);
|
|
if (!dev->config)
|
|
return -ENOMEM;
|
|
|
|
length = ncfg * sizeof(char *);
|
|
dev->rawdescriptors = kzalloc(length, GFP_KERNEL);
|
|
if (!dev->rawdescriptors)
|
|
return -ENOMEM;
|
|
|
|
desc = kmalloc(usb_config_req_size, GFP_KERNEL);
|
|
if (!desc)
|
|
return -ENOMEM;
|
|
|
|
for (cfgno = 0; cfgno < ncfg; cfgno++) {
|
|
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
|
|
desc, usb_config_req_size);
|
|
if (result < 0) {
|
|
dev_err(ddev, "unable to read config index %d "
|
|
"descriptor/%s: %d\n", cfgno, "start", result);
|
|
if (result != -EPIPE)
|
|
goto err;
|
|
dev_notice(ddev, "chopping to %d config(s)\n", cfgno);
|
|
dev->descriptor.bNumConfigurations = cfgno;
|
|
break;
|
|
} else if (result < 4) {
|
|
dev_err(ddev, "config index %d descriptor too short (asked for %zu, got %i)\n",
|
|
cfgno, usb_config_req_size, result);
|
|
result = -EINVAL;
|
|
goto err;
|
|
}
|
|
length = max_t(int, le16_to_cpu(desc->wTotalLength),
|
|
USB_DT_CONFIG_SIZE);
|
|
|
|
bigbuffer = kmalloc(length, GFP_KERNEL);
|
|
if (!bigbuffer) {
|
|
result = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
if (dev->quirks & USB_QUIRK_DELAY_INIT)
|
|
msleep(200);
|
|
|
|
/* Skip the second read if we already got everything */
|
|
if (result >= length) {
|
|
memcpy(bigbuffer, desc, length);
|
|
goto store_and_parse;
|
|
}
|
|
|
|
/* Get the whole thing */
|
|
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
|
|
bigbuffer, length);
|
|
if (result < 0) {
|
|
dev_err(ddev, "unable to read config index %d "
|
|
"descriptor/%s\n", cfgno, "all");
|
|
kfree(bigbuffer);
|
|
goto err;
|
|
}
|
|
if (result < length) {
|
|
dev_notice(ddev, "config index %d descriptor too short "
|
|
"(expected %i, got %i)\n", cfgno, length, result);
|
|
length = result;
|
|
}
|
|
|
|
store_and_parse:
|
|
dev->rawdescriptors[cfgno] = bigbuffer;
|
|
|
|
result = usb_parse_configuration(dev, cfgno,
|
|
&dev->config[cfgno], bigbuffer, length);
|
|
if (result < 0) {
|
|
++cfgno;
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
err:
|
|
kfree(desc);
|
|
dev->descriptor.bNumConfigurations = cfgno;
|
|
|
|
return result;
|
|
}
|
|
|
|
void usb_release_bos_descriptor(struct usb_device *dev)
|
|
{
|
|
if (dev->bos) {
|
|
kfree(dev->bos->desc);
|
|
kfree(dev->bos);
|
|
dev->bos = NULL;
|
|
}
|
|
}
|
|
|
|
static const __u8 bos_desc_len[256] = {
|
|
[USB_CAP_TYPE_WIRELESS_USB] = USB_DT_USB_WIRELESS_CAP_SIZE,
|
|
[USB_CAP_TYPE_EXT] = USB_DT_USB_EXT_CAP_SIZE,
|
|
[USB_SS_CAP_TYPE] = USB_DT_USB_SS_CAP_SIZE,
|
|
[USB_SSP_CAP_TYPE] = USB_DT_USB_SSP_CAP_SIZE(1),
|
|
[CONTAINER_ID_TYPE] = USB_DT_USB_SS_CONTN_ID_SIZE,
|
|
[USB_PTM_CAP_TYPE] = USB_DT_USB_PTM_ID_SIZE,
|
|
};
|
|
|
|
/* Get BOS descriptor set */
|
|
int usb_get_bos_descriptor(struct usb_device *dev)
|
|
{
|
|
struct device *ddev = &dev->dev;
|
|
struct usb_bos_descriptor *bos;
|
|
struct usb_dev_cap_header *cap;
|
|
struct usb_ssp_cap_descriptor *ssp_cap;
|
|
unsigned char *buffer, *buffer0;
|
|
int length, total_len, num, i, ssac;
|
|
__u8 cap_type;
|
|
int ret;
|
|
|
|
if (dev->quirks & USB_QUIRK_NO_BOS) {
|
|
dev_dbg(ddev, "skipping BOS descriptor\n");
|
|
return -ENOMSG;
|
|
}
|
|
|
|
bos = kzalloc_obj(*bos);
|
|
if (!bos)
|
|
return -ENOMEM;
|
|
|
|
/* Get BOS descriptor */
|
|
ret = usb_get_descriptor(dev, USB_DT_BOS, 0, bos, USB_DT_BOS_SIZE);
|
|
if (ret < USB_DT_BOS_SIZE || bos->bLength < USB_DT_BOS_SIZE) {
|
|
dev_notice(ddev, "unable to get BOS descriptor or descriptor too short\n");
|
|
if (ret >= 0)
|
|
ret = -ENOMSG;
|
|
kfree(bos);
|
|
return ret;
|
|
}
|
|
|
|
length = bos->bLength;
|
|
total_len = le16_to_cpu(bos->wTotalLength);
|
|
num = bos->bNumDeviceCaps;
|
|
kfree(bos);
|
|
if (total_len < length)
|
|
return -EINVAL;
|
|
|
|
dev->bos = kzalloc_obj(*dev->bos);
|
|
if (!dev->bos)
|
|
return -ENOMEM;
|
|
|
|
/* Now let's get the whole BOS descriptor set */
|
|
buffer = kzalloc(total_len, GFP_KERNEL);
|
|
if (!buffer) {
|
|
ret = -ENOMEM;
|
|
goto err;
|
|
}
|
|
dev->bos->desc = (struct usb_bos_descriptor *)buffer;
|
|
|
|
ret = usb_get_descriptor(dev, USB_DT_BOS, 0, buffer, total_len);
|
|
if (ret < total_len) {
|
|
dev_notice(ddev, "unable to get BOS descriptor set\n");
|
|
if (ret >= 0)
|
|
ret = -ENOMSG;
|
|
goto err;
|
|
}
|
|
|
|
buffer0 = buffer;
|
|
total_len -= length;
|
|
buffer += length;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
cap = (struct usb_dev_cap_header *)buffer;
|
|
|
|
if (total_len < sizeof(*cap) || total_len < cap->bLength) {
|
|
dev->bos->desc->bNumDeviceCaps = i;
|
|
break;
|
|
}
|
|
cap_type = cap->bDevCapabilityType;
|
|
length = cap->bLength;
|
|
if (bos_desc_len[cap_type] && length < bos_desc_len[cap_type]) {
|
|
dev->bos->desc->bNumDeviceCaps = i;
|
|
break;
|
|
}
|
|
|
|
if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) {
|
|
dev_notice(ddev, "descriptor type invalid, skip\n");
|
|
goto skip_to_next_descriptor;
|
|
}
|
|
|
|
switch (cap_type) {
|
|
case USB_CAP_TYPE_EXT:
|
|
dev->bos->ext_cap =
|
|
(struct usb_ext_cap_descriptor *)buffer;
|
|
break;
|
|
case USB_SS_CAP_TYPE:
|
|
dev->bos->ss_cap =
|
|
(struct usb_ss_cap_descriptor *)buffer;
|
|
break;
|
|
case USB_SSP_CAP_TYPE:
|
|
ssp_cap = (struct usb_ssp_cap_descriptor *)buffer;
|
|
ssac = (le32_to_cpu(ssp_cap->bmAttributes) &
|
|
USB_SSP_SUBLINK_SPEED_ATTRIBS);
|
|
if (length >= USB_DT_USB_SSP_CAP_SIZE(ssac))
|
|
dev->bos->ssp_cap = ssp_cap;
|
|
break;
|
|
case CONTAINER_ID_TYPE:
|
|
dev->bos->ss_id =
|
|
(struct usb_ss_container_id_descriptor *)buffer;
|
|
break;
|
|
case USB_PTM_CAP_TYPE:
|
|
dev->bos->ptm_cap =
|
|
(struct usb_ptm_cap_descriptor *)buffer;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
skip_to_next_descriptor:
|
|
total_len -= length;
|
|
buffer += length;
|
|
}
|
|
dev->bos->desc->wTotalLength = cpu_to_le16(buffer - buffer0);
|
|
|
|
return 0;
|
|
|
|
err:
|
|
usb_release_bos_descriptor(dev);
|
|
return ret;
|
|
}
|