cd83ce9e61
This patch adds a usb quirk to support devices with interupt endpoints and bInterval values expressed as microframes. The quirk causes the parse endpoint function to modify the reported bInterval to a standards conforming value. There is currently code in the endpoint parser that checks for bIntervals that are outside of the valid range (1-16 for USB 2+ high speed and super speed interupt endpoints). In this case, the code assumes the bInterval is being reported in 1ms frames. As well, the correction is only applied if the original bInterval value is out of the 1-16 range. With this quirk applied to the device, the bInterval will be accurately adjusted from microframes to an exponent. Signed-off-by: James P Michels III <james.p.michels@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
870 lines
24 KiB
C
870 lines
24 KiB
C
#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/device.h>
|
|
#include <asm/byteorder.h>
|
|
#include "usb.h"
|
|
|
|
|
|
#define USB_MAXALTSETTING 128 /* Hard limit */
|
|
|
|
#define USB_MAXCONFIG 8 /* Arbitrary limit */
|
|
|
|
|
|
static inline const char *plural(int n)
|
|
{
|
|
return (n == 1 ? "" : "s");
|
|
}
|
|
|
|
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_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 (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP ||
|
|
size < USB_DT_SS_EP_COMP_SIZE) {
|
|
dev_warn(ddev, "No SuperSpeed endpoint companion for config %d "
|
|
" interface %d altsetting %d ep %d: "
|
|
"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;
|
|
}
|
|
|
|
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_warn(ddev, "Control endpoint with bMaxBurst = %d in "
|
|
"config %d interface %d altsetting %d ep %d: "
|
|
"setting to zero\n", desc->bMaxBurst,
|
|
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
|
ep->ss_ep_comp.bMaxBurst = 0;
|
|
} else if (desc->bMaxBurst > 15) {
|
|
dev_warn(ddev, "Endpoint with bMaxBurst = %d in "
|
|
"config %d interface %d altsetting %d ep %d: "
|
|
"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_warn(ddev, "%s endpoint with bmAttributes = %d in "
|
|
"config %d interface %d altsetting %d ep %d: "
|
|
"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_warn(ddev, "Bulk endpoint with more than 65536 streams in "
|
|
"config %d interface %d altsetting %d ep %d: "
|
|
"setting to max\n",
|
|
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
|
ep->ss_ep_comp.bmAttributes = 16;
|
|
} else if (usb_endpoint_xfer_isoc(&ep->desc) &&
|
|
desc->bmAttributes > 2) {
|
|
dev_warn(ddev, "Isoc endpoint has Mult of %d in "
|
|
"config %d interface %d altsetting %d ep %d: "
|
|
"setting to 3\n", desc->bmAttributes + 1,
|
|
cfgno, inum, asnum, ep->desc.bEndpointAddress);
|
|
ep->ss_ep_comp.bmAttributes = 2;
|
|
}
|
|
|
|
if (usb_endpoint_xfer_isoc(&ep->desc))
|
|
max_tx = (desc->bMaxBurst + 1) * (desc->bmAttributes + 1) *
|
|
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;
|
|
if (le16_to_cpu(desc->wBytesPerInterval) > max_tx) {
|
|
dev_warn(ddev, "%s endpoint with wBytesPerInterval of %d in "
|
|
"config %d interface %d altsetting %d ep %d: "
|
|
"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);
|
|
}
|
|
}
|
|
|
|
static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,
|
|
int asnum, struct usb_host_interface *ifp, int num_ep,
|
|
unsigned char *buffer, int size)
|
|
{
|
|
unsigned char *buffer0 = buffer;
|
|
struct usb_endpoint_descriptor *d;
|
|
struct usb_host_endpoint *endpoint;
|
|
int n, i, j, retval;
|
|
|
|
d = (struct usb_endpoint_descriptor *) buffer;
|
|
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_warn(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 = d->bEndpointAddress & ~USB_ENDPOINT_DIR_MASK;
|
|
if (i >= 16 || i == 0) {
|
|
dev_warn(ddev, "config %d interface %d altsetting %d has an "
|
|
"invalid endpoint with address 0x%X, skipping\n",
|
|
cfgno, inum, asnum, d->bEndpointAddress);
|
|
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;
|
|
|
|
endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints];
|
|
++ifp->desc.bNumEndpoints;
|
|
|
|
memcpy(&endpoint->desc, d, n);
|
|
INIT_LIST_HEAD(&endpoint->urb_list);
|
|
|
|
/* Fix up bInterval values outside the legal range. Use 32 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 (to_usb_device(ddev)->speed) {
|
|
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 a
|
|
* 32 ms default value otherwise. */
|
|
n = fls(d->bInterval*8);
|
|
if (n == 0)
|
|
n = 9; /* 32 ms = 2^(9-1) uframes */
|
|
j = 16;
|
|
|
|
/*
|
|
* Adjust bInterval for quirked devices.
|
|
* This quirk fixes bIntervals reported in
|
|
* linear microframes.
|
|
*/
|
|
if (to_usb_device(ddev)->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 = 32;
|
|
break;
|
|
}
|
|
} else if (usb_endpoint_xfer_isoc(d)) {
|
|
i = 1;
|
|
j = 16;
|
|
switch (to_usb_device(ddev)->speed) {
|
|
case USB_SPEED_HIGH:
|
|
n = 9; /* 32 ms = 2^(9-1) uframes */
|
|
break;
|
|
default: /* USB_SPEED_FULL */
|
|
n = 6; /* 32 ms = 2^(6-1) frames */
|
|
break;
|
|
}
|
|
}
|
|
if (d->bInterval < i || d->bInterval > j) {
|
|
dev_warn(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 (to_usb_device(ddev)->speed == USB_SPEED_LOW &&
|
|
usb_endpoint_xfer_bulk(d)) {
|
|
dev_warn(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);
|
|
}
|
|
|
|
/*
|
|
* 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 (to_usb_device(ddev)->speed == USB_SPEED_HIGH
|
|
&& usb_endpoint_xfer_bulk(d)) {
|
|
unsigned maxp;
|
|
|
|
maxp = usb_endpoint_maxp(&endpoint->desc) & 0x07ff;
|
|
if (maxp != 512)
|
|
dev_warn(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 SuperSpeed endpoint companion descriptor */
|
|
if (to_usb_device(ddev)->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, 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_warn(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, 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_warn(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, inum, asnum, alt,
|
|
num_ep, buffer, size);
|
|
if (retval < 0)
|
|
return retval;
|
|
++n;
|
|
|
|
buffer += retval;
|
|
size -= retval;
|
|
}
|
|
|
|
if (n != num_ep_orig)
|
|
dev_warn(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, 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 len, retval;
|
|
u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
|
|
unsigned iad_num = 0;
|
|
|
|
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
|
|
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
|
|
config->desc.bLength < USB_DT_CONFIG_SIZE ||
|
|
config->desc.bLength > size) {
|
|
dev_err(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;
|
|
|
|
nintf = nintf_orig = config->desc.bNumInterfaces;
|
|
if (nintf > USB_MAXINTERFACES) {
|
|
dev_warn(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_warn(ddev, "config %d descriptor has %d excess "
|
|
"byte%s, ignoring\n",
|
|
cfgno, size2, plural(size2));
|
|
break;
|
|
}
|
|
|
|
header = (struct usb_descriptor_header *) buffer2;
|
|
if ((header->bLength > size2) || (header->bLength < 2)) {
|
|
dev_warn(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_warn(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_warn(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_warn(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) {
|
|
if (iad_num == USB_MAXIADS) {
|
|
dev_warn(ddev, "found more Interface "
|
|
"Association Descriptors "
|
|
"than allocated for in "
|
|
"configuration %d\n", cfgno);
|
|
} else {
|
|
config->intf_assoc[iad_num] =
|
|
(struct usb_interface_assoc_descriptor
|
|
*)header;
|
|
iad_num++;
|
|
}
|
|
|
|
} else if (header->bDescriptorType == USB_DT_DEVICE ||
|
|
header->bDescriptorType == USB_DT_CONFIG)
|
|
dev_warn(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_warn(ddev, "config %d has %d interface%s, different from "
|
|
"the descriptor's value: %d\n",
|
|
cfgno, n, plural(n), nintf_orig);
|
|
else if (n == 0)
|
|
dev_warn(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_warn(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_warn(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;
|
|
}
|
|
|
|
len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j;
|
|
config->intf_cache[i] = intfc = kzalloc(len, GFP_KERNEL);
|
|
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, 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_warn(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;
|
|
int result = 0;
|
|
unsigned int cfgno, length;
|
|
unsigned char *bigbuffer;
|
|
struct usb_config_descriptor *desc;
|
|
|
|
cfgno = 0;
|
|
result = -ENOMEM;
|
|
if (ncfg > USB_MAXCONFIG) {
|
|
dev_warn(ddev, "too many configurations: %d, "
|
|
"using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
|
|
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
|
|
}
|
|
|
|
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)
|
|
goto err2;
|
|
|
|
length = ncfg * sizeof(char *);
|
|
dev->rawdescriptors = kzalloc(length, GFP_KERNEL);
|
|
if (!dev->rawdescriptors)
|
|
goto err2;
|
|
|
|
desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
|
|
if (!desc)
|
|
goto err2;
|
|
|
|
result = 0;
|
|
for (; cfgno < ncfg; cfgno++) {
|
|
/* We grab just the first descriptor so we know how long
|
|
* the whole configuration is */
|
|
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
|
|
desc, USB_DT_CONFIG_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_err(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 "
|
|
"(expected %i, got %i)\n", cfgno,
|
|
USB_DT_CONFIG_SIZE, result);
|
|
result = -EINVAL;
|
|
goto err;
|
|
}
|
|
length = max((int) le16_to_cpu(desc->wTotalLength),
|
|
USB_DT_CONFIG_SIZE);
|
|
|
|
/* Now that we know the length, get the whole thing */
|
|
bigbuffer = kmalloc(length, GFP_KERNEL);
|
|
if (!bigbuffer) {
|
|
result = -ENOMEM;
|
|
goto err;
|
|
}
|
|
|
|
if (dev->quirks & USB_QUIRK_DELAY_INIT)
|
|
msleep(100);
|
|
|
|
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_warn(ddev, "config index %d descriptor too short "
|
|
"(expected %i, got %i)\n", cfgno, length, result);
|
|
length = result;
|
|
}
|
|
|
|
dev->rawdescriptors[cfgno] = bigbuffer;
|
|
|
|
result = usb_parse_configuration(dev, cfgno,
|
|
&dev->config[cfgno], bigbuffer, length);
|
|
if (result < 0) {
|
|
++cfgno;
|
|
goto err;
|
|
}
|
|
}
|
|
result = 0;
|
|
|
|
err:
|
|
kfree(desc);
|
|
dev->descriptor.bNumConfigurations = cfgno;
|
|
err2:
|
|
if (result == -ENOMEM)
|
|
dev_err(ddev, "out of memory\n");
|
|
return result;
|
|
}
|
|
|
|
void usb_release_bos_descriptor(struct usb_device *dev)
|
|
{
|
|
if (dev->bos) {
|
|
kfree(dev->bos->desc);
|
|
kfree(dev->bos);
|
|
dev->bos = NULL;
|
|
}
|
|
}
|
|
|
|
/* 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;
|
|
unsigned char *buffer;
|
|
int length, total_len, num, i;
|
|
int ret;
|
|
|
|
bos = kzalloc(sizeof(struct usb_bos_descriptor), GFP_KERNEL);
|
|
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) {
|
|
dev_err(ddev, "unable to get BOS descriptor\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(sizeof(struct usb_host_bos), GFP_KERNEL);
|
|
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_err(ddev, "unable to get BOS descriptor set\n");
|
|
if (ret >= 0)
|
|
ret = -ENOMSG;
|
|
goto err;
|
|
}
|
|
total_len -= length;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
buffer += length;
|
|
cap = (struct usb_dev_cap_header *)buffer;
|
|
length = cap->bLength;
|
|
|
|
if (total_len < length)
|
|
break;
|
|
total_len -= length;
|
|
|
|
if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) {
|
|
dev_warn(ddev, "descriptor type invalid, skip\n");
|
|
continue;
|
|
}
|
|
|
|
switch (cap->bDevCapabilityType) {
|
|
case USB_CAP_TYPE_WIRELESS_USB:
|
|
/* Wireless USB cap descriptor is handled by wusb */
|
|
break;
|
|
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 CONTAINER_ID_TYPE:
|
|
dev->bos->ss_id =
|
|
(struct usb_ss_container_id_descriptor *)buffer;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
usb_release_bos_descriptor(dev);
|
|
return ret;
|
|
}
|