summaryrefslogtreecommitdiff
path: root/drivers/usb/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/config.c4
-rw-r--r--drivers/usb/core/urb.c14
-rw-r--r--drivers/usb/core/usb.c50
3 files changed, 62 insertions, 6 deletions
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 42468bbeffd2..baf5bc844b6f 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -507,8 +507,8 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno,
}
/* Parse a possible eUSB2 periodic endpoint companion descriptor */
- if (bcdUSB == 0x0220 && d->wMaxPacketSize == 0 &&
- (usb_endpoint_xfer_isoc(d) || usb_endpoint_xfer_int(d)))
+ 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);
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 7a76d5a62db1..ff8df16cca35 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -372,6 +372,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
struct usb_host_endpoint *ep;
int is_out;
unsigned int allowed;
+ bool is_eusb2_isoch_double;
if (!urb || !urb->complete)
return -EINVAL;
@@ -434,7 +435,8 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
return -ENODEV;
max = usb_endpoint_maxp(&ep->desc);
- if (max <= 0) {
+ is_eusb2_isoch_double = usb_endpoint_is_hs_isoc_double(dev, ep);
+ if (!max && !is_eusb2_isoch_double) {
dev_dbg(&dev->dev,
"bogus endpoint ep%d%s in %s (bad maxpacket %d)\n",
usb_endpoint_num(&ep->desc), is_out ? "out" : "in",
@@ -467,9 +469,13 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
max = le32_to_cpu(isoc_ep_comp->dwBytesPerInterval);
}
- /* "high bandwidth" mode, 1-3 packets/uframe? */
- if (dev->speed == USB_SPEED_HIGH)
- max *= usb_endpoint_maxp_mult(&ep->desc);
+ /* High speed, 1-3 packets/uframe, max 6 for eUSB2 double bw */
+ if (dev->speed == USB_SPEED_HIGH) {
+ if (is_eusb2_isoch_double)
+ max = le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval);
+ else
+ max *= usb_endpoint_maxp_mult(&ep->desc);
+ }
if (urb->number_of_packets <= 0)
return -EINVAL;
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index fca7735fc660..b88b6271cb30 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -1110,6 +1110,56 @@ void usb_free_noncoherent(struct usb_device *dev, size_t size,
}
EXPORT_SYMBOL_GPL(usb_free_noncoherent);
+/**
+ * usb_endpoint_max_periodic_payload - Get maximum payload bytes per service
+ * interval
+ * @udev: The USB device
+ * @ep: The endpoint
+ *
+ * Returns: the maximum number of bytes isochronous or interrupt endpoint @ep
+ * can transfer during a service interval, or 0 for other endpoints.
+ */
+u32 usb_endpoint_max_periodic_payload(struct usb_device *udev,
+ const struct usb_host_endpoint *ep)
+{
+ if (!usb_endpoint_xfer_isoc(&ep->desc) &&
+ !usb_endpoint_xfer_int(&ep->desc))
+ return 0;
+
+ switch (udev->speed) {
+ case USB_SPEED_SUPER_PLUS:
+ if (USB_SS_SSP_ISOC_COMP(ep->ss_ep_comp.bmAttributes))
+ return le32_to_cpu(ep->ssp_isoc_ep_comp.dwBytesPerInterval);
+ fallthrough;
+ case USB_SPEED_SUPER:
+ return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval);
+ default:
+ if (usb_endpoint_is_hs_isoc_double(udev, ep))
+ return le32_to_cpu(ep->eusb2_isoc_ep_comp.dwBytesPerInterval);
+ return usb_endpoint_maxp(&ep->desc) * usb_endpoint_maxp_mult(&ep->desc);
+ }
+}
+EXPORT_SYMBOL_GPL(usb_endpoint_max_periodic_payload);
+
+/**
+ * usb_endpoint_is_hs_isoc_double - Tell whether an endpoint uses USB 2
+ * Isochronous Double IN Bandwidth
+ * @udev: The USB device
+ * @ep: The endpoint
+ *
+ * Returns: true if an endpoint @ep conforms to USB 2 Isochronous Double IN
+ * Bandwidth ECN, false otherwise.
+ */
+bool usb_endpoint_is_hs_isoc_double(struct usb_device *udev,
+ const struct usb_host_endpoint *ep)
+{
+ return ep->eusb2_isoc_ep_comp.bDescriptorType &&
+ le16_to_cpu(udev->descriptor.bcdUSB) == 0x220 &&
+ usb_endpoint_is_isoc_in(&ep->desc) &&
+ !le16_to_cpu(ep->desc.wMaxPacketSize);
+}
+EXPORT_SYMBOL_GPL(usb_endpoint_is_hs_isoc_double);
+
/*
* Notifications of device and interface registration
*/