| From cd3c18ba2fac14b34d03cae111f215009735ea06 Mon Sep 17 00:00:00 2001 |
| From: Dmitry Torokhov <dtor@vmware.com> |
| Date: Tue, 31 May 2011 14:37:23 -0700 |
| Subject: USB: xhci - fix interval calculation for FS isoc endpoints |
| |
| From: Dmitry Torokhov <dtor@vmware.com> |
| |
| commit cd3c18ba2fac14b34d03cae111f215009735ea06 upstream. |
| |
| Full-speed isoc endpoints specify interval in exponent based form in |
| frames, not microframes, so we need to adjust accordingly. |
| |
| NEC xHCI host controllers will return an error code of 0x11 if a full |
| speed isochronous endpoint is added with the Interval field set to |
| something less than 3 (2^3 = 8 microframes, or one frame). It is |
| impossible for a full speed device to have an interval smaller than one |
| frame. |
| |
| This was always an issue in the xHCI driver, but commit |
| dfa49c4ad120a784ef1ff0717168aa79f55a483a "USB: xhci - fix math in |
| xhci_get_endpoint_interval()" removed the clamping of the minimum value |
| in the Interval field, which revealed this bug. |
| |
| This needs to be backported to stable kernels back to 2.6.31. |
| |
| Reported-by: Matt Evans <matt@ozlabs.org> |
| Signed-off-by: Dmitry Torokhov <dtor@vmware.com> |
| Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| drivers/usb/host/xhci-mem.c | 14 ++++++++++++-- |
| 1 file changed, 12 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/usb/host/xhci-mem.c |
| +++ b/drivers/usb/host/xhci-mem.c |
| @@ -505,9 +505,19 @@ static unsigned int xhci_parse_exponent_ |
| 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 %#x - rounding interval to %d %sframes\n", |
| ep->desc.bEndpointAddress, |
| - 1 << interval); |
| + 1 << interval, |
| + udev->speed == USB_SPEED_FULL ? "" : "micro"); |
| + |
| + if (udev->speed == USB_SPEED_FULL) { |
| + /* |
| + * Full speed isoc endpoints specify interval in frames, |
| + * not microframes. We are using microframes everywhere, |
| + * so adjust accordingly. |
| + */ |
| + interval += 3; /* 1 frame = 2^3 uframes */ |
| + } |
| |
| return interval; |
| } |