diff options
Diffstat (limited to 'drivers/usb/core/config.c')
| -rw-r--r-- | drivers/usb/core/config.c | 82 | 
1 files changed, 66 insertions, 16 deletions
| diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 5f40117e68e7..26bc05e48d8a 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -203,9 +203,58 @@ static const unsigned short super_speed_maxpacket_maxes[4] = {  	[USB_ENDPOINT_XFER_INT] = 1024,  }; -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) +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)  {  	unsigned char *buffer0 = buffer;  	struct usb_endpoint_descriptor *d; @@ -242,13 +291,10 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,  		goto skip_to_next_endpoint_or_interface_descriptor;  	/* Check for duplicate endpoint addresses */ -	for (i = 0; i < ifp->desc.bNumEndpoints; ++i) { -		if (ifp->endpoint[i].desc.bEndpointAddress == -		    d->bEndpointAddress) { -			dev_warn(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; -		} +	if (config_endpoint_is_duplicate(config, inum, asnum, d)) { +		dev_warn(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;  	}  	endpoint = &ifp->endpoint[ifp->desc.bNumEndpoints]; @@ -346,12 +392,16 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum,  			endpoint->desc.wMaxPacketSize = cpu_to_le16(8);  	} -	/* Validate the wMaxPacketSize field */ +	/* +	 * Validate the wMaxPacketSize field. +	 * Some devices have isochronous endpoints in altsetting 0; +	 * the USB-2 spec requires such endpoints to have wMaxPacketSize = 0 +	 * (see the end of section 5.6.3), so don't warn about them. +	 */  	maxp = usb_endpoint_maxp(&endpoint->desc); -	if (maxp == 0) { -		dev_warn(ddev, "config %d interface %d altsetting %d endpoint 0x%X has wMaxPacketSize 0, skipping\n", +	if (maxp == 0 && !(usb_endpoint_xfer_isoc(d) && asnum == 0)) { +		dev_warn(ddev, "config %d interface %d altsetting %d endpoint 0x%X has invalid wMaxPacketSize 0\n",  		    cfgno, inum, asnum, d->bEndpointAddress); -		goto skip_to_next_endpoint_or_interface_descriptor;  	}  	/* Find the highest legal maxpacket size for this endpoint */ @@ -522,8 +572,8 @@ static int usb_parse_interface(struct device *ddev, int cfgno,  		if (((struct usb_descriptor_header *) buffer)->bDescriptorType  		     == USB_DT_INTERFACE)  			break; -		retval = usb_parse_endpoint(ddev, cfgno, inum, asnum, alt, -		    num_ep, buffer, size); +		retval = usb_parse_endpoint(ddev, cfgno, config, inum, asnum, +				alt, num_ep, buffer, size);  		if (retval < 0)  			return retval;  		++n; | 
