diff options
Diffstat (limited to 'drivers/usb/core/config.c')
| -rw-r--r-- | drivers/usb/core/config.c | 214 | 
1 files changed, 75 insertions, 139 deletions
| diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 0d3af6a6ee49..83126b03e7cf 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -1,12 +1,14 @@  #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/init.h>  #include <linux/slab.h>  #include <linux/device.h>  #include <asm/byteorder.h>  #include "usb.h" -#include "hcd.h" +  #define USB_MAXALTSETTING		128	/* Hard limit */  #define USB_MAXENDPOINTS		30	/* Hard limit */ @@ -19,32 +21,6 @@ static inline const char *plural(int n)  	return (n == 1 ? "" : "s");  } -/* FIXME: this is a kludge */ -static int find_next_descriptor_more(unsigned char *buffer, int size, -    int dt1, int dt2, int dt3, int *num_skipped) -{ -	struct usb_descriptor_header *h; -	int n = 0; -	unsigned char *buffer0 = buffer; - -	/* Find the next descriptor of type dt1 or dt2 or dt3 */ -	while (size > 0) { -		h = (struct usb_descriptor_header *) buffer; -		if (h->bDescriptorType == dt1 || h->bDescriptorType == dt2 || -				h->bDescriptorType == dt3) -			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 int find_next_descriptor(unsigned char *buffer, int size,      int dt1, int dt2, int *num_skipped)  { @@ -69,47 +45,41 @@ static int find_next_descriptor(unsigned char *buffer, int size,  	return buffer - buffer0;  } -static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, +static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,  		int inum, int asnum, struct usb_host_endpoint *ep, -		int num_ep, unsigned char *buffer, int size) +		unsigned char *buffer, int size)  { -	unsigned char *buffer_start = buffer; -	struct usb_ss_ep_comp_descriptor	*desc; -	int retval; -	int num_skipped; +	struct usb_ss_ep_comp_descriptor *desc;  	int max_tx; -	int i; +	/* 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) { +	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); -		/* -		 * The next descriptor is for an Endpoint or Interface, -		 * no extra descriptors to copy into the companion structure, -		 * and we didn't eat up any of the buffer. + +		/* 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.  		 */ -		return 0; +		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, desc, USB_DT_SS_EP_COMP_SIZE); -	desc = &ep->ss_ep_comp->desc; -	buffer += desc->bLength; -	size -= desc->bLength; -	/* Eat up the other descriptors we don't care about */ -	ep->ss_ep_comp->extra = buffer; -	i = find_next_descriptor(buffer, size, USB_DT_ENDPOINT, -			USB_DT_INTERFACE, &num_skipped); -	ep->ss_ep_comp->extralen = i; -	buffer += i; -	size -= i; -	retval = buffer - buffer_start; -	if (num_skipped > 0) -		dev_dbg(ddev, "skipped %d descriptor%s after %s\n", -				num_skipped, plural(num_skipped), -				"SuperSpeed endpoint companion"); +	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) { @@ -117,47 +87,48 @@ static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,  				"config %d interface %d altsetting %d ep %d: "  				"setting to zero\n", desc->bMaxBurst,  				cfgno, inum, asnum, ep->desc.bEndpointAddress); -		desc->bMaxBurst = 0; -	} -	if (desc->bMaxBurst > 15) { +		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); -		desc->bMaxBurst = 15; +		ep->ss_ep_comp.bMaxBurst = 15;  	} -	if ((usb_endpoint_xfer_control(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) -			&& desc->bmAttributes != 0) { + +	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); -		desc->bmAttributes = 0; -	} -	if (usb_endpoint_xfer_bulk(&ep->desc) && desc->bmAttributes > 16) { +		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); -		desc->bmAttributes = 16; -	} -	if (usb_endpoint_xfer_isoc(&ep->desc) && desc->bmAttributes > 2) { +		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); -		desc->bmAttributes = 2; +		ep->ss_ep_comp.bmAttributes = 2;  	} -	if (usb_endpoint_xfer_isoc(&ep->desc)) { + +	if (usb_endpoint_xfer_isoc(&ep->desc))  		max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1) *  			(desc->bmAttributes + 1); -	} else if (usb_endpoint_xfer_int(&ep->desc)) { +	else if (usb_endpoint_xfer_int(&ep->desc))  		max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1); -	} else { -		goto valid; -	} +	else +		max_tx = 999999;  	if (desc->wBytesPerInterval > max_tx) {  		dev_warn(ddev, "%s endpoint with wBytesPerInterval of %d in "  				"config %d interface %d altsetting %d ep %d: " @@ -166,10 +137,8 @@ static int usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,  				desc->wBytesPerInterval,  				cfgno, inum, asnum, ep->desc.bEndpointAddress,  				max_tx); -		desc->wBytesPerInterval = max_tx; +		ep->ss_ep_comp.wBytesPerInterval = max_tx;  	} -valid: -	return retval;  }  static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, @@ -291,61 +260,19 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,  				cfgno, inum, asnum, d->bEndpointAddress,  				maxp);  	} -	/* Allocate room for and parse any SS endpoint companion descriptors */ -	if (to_usb_device(ddev)->speed == USB_SPEED_SUPER) { -		endpoint->extra = buffer; -		i = find_next_descriptor_more(buffer, size, USB_DT_SS_ENDPOINT_COMP, -				USB_DT_ENDPOINT, USB_DT_INTERFACE, &n); -		endpoint->extralen = i; -		buffer += i; -		size -= i; - -		/* Allocate space for the SS endpoint companion descriptor */ -		endpoint->ss_ep_comp = kzalloc(sizeof(struct usb_host_ss_ep_comp), -				GFP_KERNEL); -		if (!endpoint->ss_ep_comp) -			return -ENOMEM; -		/* Fill in some default values (may be overwritten later) */ -		endpoint->ss_ep_comp->desc.bLength = USB_DT_SS_EP_COMP_SIZE; -		endpoint->ss_ep_comp->desc.bDescriptorType = USB_DT_SS_ENDPOINT_COMP; -		endpoint->ss_ep_comp->desc.bMaxBurst = 0; -		/* -		 * 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. -		 */ -		if (usb_endpoint_xfer_isoc(&endpoint->desc) || -				usb_endpoint_xfer_int(&endpoint->desc)) -			endpoint->ss_ep_comp->desc.wBytesPerInterval = -				endpoint->desc.wMaxPacketSize; - -		if (size > 0) { -			retval = usb_parse_ss_endpoint_companion(ddev, cfgno, -					inum, asnum, endpoint, num_ep, buffer, -					size); -			if (retval >= 0) { -				buffer += retval; -				retval = buffer - buffer0; -			} -		} else { -			dev_warn(ddev, "config %d interface %d altsetting %d " -				"endpoint 0x%X has no " -				"SuperSpeed companion descriptor\n", -				cfgno, inum, asnum, d->bEndpointAddress); -			retval = buffer - buffer0; -		} -	} else { -		/* 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; -	} +	/* 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"); @@ -478,9 +405,10 @@ skip_to_next_interface_descriptor:  	return buffer - buffer0 + i;  } -static int usb_parse_configuration(struct device *ddev, int cfgidx, +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; @@ -549,6 +477,16 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx,  			}  			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", @@ -722,7 +660,6 @@ int usb_get_configuration(struct usb_device *dev)  	int ncfg = dev->descriptor.bNumConfigurations;  	int result = 0;  	unsigned int cfgno, length; -	unsigned char *buffer;  	unsigned char *bigbuffer;  	struct usb_config_descriptor *desc; @@ -751,17 +688,16 @@ int usb_get_configuration(struct usb_device *dev)  	if (!dev->rawdescriptors)  		goto err2; -	buffer = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL); -	if (!buffer) +	desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL); +	if (!desc)  		goto err2; -	desc = (struct usb_config_descriptor *)buffer;  	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, -		    buffer, USB_DT_CONFIG_SIZE); +		    desc, USB_DT_CONFIG_SIZE);  		if (result < 0) {  			dev_err(ddev, "unable to read config index %d "  			    "descriptor/%s: %d\n", cfgno, "start", result); @@ -800,7 +736,7 @@ int usb_get_configuration(struct usb_device *dev)  		dev->rawdescriptors[cfgno] = bigbuffer; -		result = usb_parse_configuration(&dev->dev, cfgno, +		result = usb_parse_configuration(dev, cfgno,  		    &dev->config[cfgno], bigbuffer, length);  		if (result < 0) {  			++cfgno; @@ -810,7 +746,7 @@ int usb_get_configuration(struct usb_device *dev)  	result = 0;  err: -	kfree(buffer); +	kfree(desc);  out_not_authorized:  	dev->descriptor.bNumConfigurations = cfgno;  err2: | 
