summaryrefslogtreecommitdiffstats
path: root/drivers/usb/core/config.c
diff options
context:
space:
mode:
authorMathias Nyman <mathias.nyman@linux.intel.com>2016-02-12 16:40:13 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-02-14 17:03:23 -0800
commitb37d83a6a41499d582b8faedff1913ec75d9e70b (patch)
treed41550d8ace906159e0af51465f222e1be6afe7f /drivers/usb/core/config.c
parentc8b1d8977eee3acc63a65811dd72ec4a93b74388 (diff)
downloadlinux-stable-b37d83a6a41499d582b8faedff1913ec75d9e70b.tar.gz
linux-stable-b37d83a6a41499d582b8faedff1913ec75d9e70b.tar.bz2
linux-stable-b37d83a6a41499d582b8faedff1913ec75d9e70b.zip
usb: Parse the new USB 3.1 SuperSpeedPlus Isoc endpoint companion descriptor
USB 3.1 devices can return a new SuperSpeedPlus isoc endpoint companion descriptor for a isochronous endpoint that requires more than 48K bytes per Service Interval. The new descriptor immediately follows the old USB 3.0 SuperSpeed Endpoint Companion and will provide a new BytesPerInterval value. It is parsed and stored in struct usb_host_endpoint with the other endpoint related descriptors, and should be used by USB3.1 capable hosts to reserve bus time in the schedule. Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core/config.c')
-rw-r--r--drivers/usb/core/config.c31
1 files changed, 31 insertions, 0 deletions
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index bbcf4009f99e..58d089802ab3 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -43,6 +43,27 @@ static int find_next_descriptor(unsigned char *buffer, int size,
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 (desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP ||
+ size < USB_DT_SSP_ISOC_EP_COMP_SIZE) {
+ dev_warn(ddev, "Invalid SuperSpeedPlus isoc endpoint companion"
+ "for config %d interface %d altsetting %d ep %d.\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_ss_endpoint_companion(struct device *ddev, int cfgno,
int inum, int asnum, struct usb_host_endpoint *ep,
unsigned char *buffer, int size)
@@ -54,6 +75,9 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
* be the first thing immediately following the endpoint descriptor.
*/
desc = (struct usb_ss_ep_comp_descriptor *) buffer;
+ buffer += desc->bLength;
+ size -= desc->bLength;
+
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 "
@@ -112,6 +136,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
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_warn(ddev, "Isoc endpoint has Mult of %d in "
"config %d interface %d altsetting %d ep %d: "
@@ -121,6 +146,12 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
ep->ss_ep_comp.bmAttributes = 2;
}
+ /* 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);
+
if (usb_endpoint_xfer_isoc(&ep->desc))
max_tx = (desc->bMaxBurst + 1) *
(USB_SS_MULT(desc->bmAttributes)) *