USB: xhci - fix math in xhci_get_endpoint_interval()
authorDmitry Torokhov <dtor@vmware.com>
Thu, 24 Mar 2011 05:41:23 +0000 (22:41 -0700)
committerSarah Sharp <sarah.a.sharp@linux.intel.com>
Wed, 13 Apr 2011 23:19:48 +0000 (16:19 -0700)
When parsing exponent-expressed intervals we subtract 1 from the
value and then expect it to match with original + 1, which is
highly unlikely, and we end with frequent spew:

usb 3-4: ep 0x83 - rounding interval to 512 microframes

Also, parsing interval for fullspeed isochronous endpoints was
incorrect - according to USB spec they use exponent-based
intervals (but xHCI spec claims frame-based intervals). I trust
USB spec more, especially since USB core agrees with it.

This should be queued for stable kernels back to 2.6.31.

Reviewed-by: Micah Elizabeth Scott <micah@vmware.com>
Signed-off-by: Dmitry Torokhov <dtor@vmware.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: stable@kernel.org
drivers/usb/host/xhci-mem.c

index 7f56c9b..627f343 100644 (file)
@@ -974,6 +974,47 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
        return 0;
 }
 
+/*
+ * Convert interval expressed as 2^(bInterval - 1) == interval into
+ * straight exponent value 2^n == interval.
+ *
+ */
+static unsigned int xhci_parse_exponent_interval(struct usb_device *udev,
+               struct usb_host_endpoint *ep)
+{
+       unsigned int interval;
+
+       interval = clamp_val(ep->desc.bInterval, 1, 16) - 1;
+       if (interval != ep->desc.bInterval - 1)
+               dev_warn(&udev->dev,
+                        "ep %#x - rounding interval to %d microframes\n",
+                        ep->desc.bEndpointAddress,
+                        1 << interval);
+
+       return interval;
+}
+
+/*
+ * Convert bInterval expressed in frames (in 1-255 range) to exponent of
+ * microframes, rounded down to nearest power of 2.
+ */
+static unsigned int xhci_parse_frame_interval(struct usb_device *udev,
+               struct usb_host_endpoint *ep)
+{
+       unsigned int interval;
+
+       interval = fls(8 * ep->desc.bInterval) - 1;
+       interval = clamp_val(interval, 3, 10);
+       if ((1 << interval) != 8 * ep->desc.bInterval)
+               dev_warn(&udev->dev,
+                        "ep %#x - rounding interval to %d microframes, ep desc says %d microframes\n",
+                        ep->desc.bEndpointAddress,
+                        1 << interval,
+                        8 * ep->desc.bInterval);
+
+       return interval;
+}
+
 /* Return the polling or NAK interval.
  *
  * The polling interval is expressed in "microframes".  If xHCI's Interval field
@@ -991,45 +1032,38 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
        case USB_SPEED_HIGH:
                /* Max NAK rate */
                if (usb_endpoint_xfer_control(&ep->desc) ||
-                               usb_endpoint_xfer_bulk(&ep->desc))
+                   usb_endpoint_xfer_bulk(&ep->desc)) {
                        interval = ep->desc.bInterval;
+                       break;
+               }
                /* Fall through - SS and HS isoc/int have same decoding */
+
        case USB_SPEED_SUPER:
                if (usb_endpoint_xfer_int(&ep->desc) ||
-                               usb_endpoint_xfer_isoc(&ep->desc)) {
-                       if (ep->desc.bInterval == 0)
-                               interval = 0;
-                       else
-                               interval = ep->desc.bInterval - 1;
-                       if (interval > 15)
-                               interval = 15;
-                       if (interval != ep->desc.bInterval + 1)
-                               dev_warn(&udev->dev, "ep %#x - rounding interval to %d microframes\n",
-                                               ep->desc.bEndpointAddress, 1 << interval);
+                   usb_endpoint_xfer_isoc(&ep->desc)) {
+                       interval = xhci_parse_exponent_interval(udev, ep);
                }
                break;
-       /* Convert bInterval (in 1-255 frames) to microframes and round down to
-        * nearest power of 2.
-        */
+
        case USB_SPEED_FULL:
+               if (usb_endpoint_xfer_int(&ep->desc)) {
+                       interval = xhci_parse_exponent_interval(udev, ep);
+                       break;
+               }
+               /*
+                * Fall through for isochronous endpoint interval decoding
+                * since it uses the same rules as low speed interrupt
+                * endpoints.
+                */
+
        case USB_SPEED_LOW:
                if (usb_endpoint_xfer_int(&ep->desc) ||
-                               usb_endpoint_xfer_isoc(&ep->desc)) {
-                       interval = fls(8*ep->desc.bInterval) - 1;
-                       if (interval > 10)
-                               interval = 10;
-                       if (interval < 3)
-                               interval = 3;
-                       if ((1 << interval) != 8*ep->desc.bInterval)
-                               dev_warn(&udev->dev,
-                                               "ep %#x - rounding interval"
-                                               " to %d microframes, "
-                                               "ep desc says %d microframes\n",
-                                               ep->desc.bEndpointAddress,
-                                               1 << interval,
-                                               8*ep->desc.bInterval);
+                   usb_endpoint_xfer_isoc(&ep->desc)) {
+
+                       interval = xhci_parse_frame_interval(udev, ep);
                }
                break;
+
        default:
                BUG();
        }