Commit dfa49c4a authored by Dmitry Torokhov's avatar Dmitry Torokhov Committed by Sarah Sharp

USB: xhci - fix math in xhci_get_endpoint_interval()

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: default avatarMicah Elizabeth Scott <micah@vmware.com>
Signed-off-by: default avatarDmitry Torokhov <dtor@vmware.com>
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: stable@kernel.org
parent 926008c9
...@@ -974,6 +974,47 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud ...@@ -974,6 +974,47 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
return 0; 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. /* Return the polling or NAK interval.
* *
* The polling interval is expressed in "microframes". If xHCI's Interval field * 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, ...@@ -991,45 +1032,38 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
case USB_SPEED_HIGH: case USB_SPEED_HIGH:
/* Max NAK rate */ /* Max NAK rate */
if (usb_endpoint_xfer_control(&ep->desc) || if (usb_endpoint_xfer_control(&ep->desc) ||
usb_endpoint_xfer_bulk(&ep->desc)) usb_endpoint_xfer_bulk(&ep->desc)) {
interval = ep->desc.bInterval; interval = ep->desc.bInterval;
break;
}
/* Fall through - SS and HS isoc/int have same decoding */ /* Fall through - SS and HS isoc/int have same decoding */
case USB_SPEED_SUPER: case USB_SPEED_SUPER:
if (usb_endpoint_xfer_int(&ep->desc) || if (usb_endpoint_xfer_int(&ep->desc) ||
usb_endpoint_xfer_isoc(&ep->desc)) { usb_endpoint_xfer_isoc(&ep->desc)) {
if (ep->desc.bInterval == 0) interval = xhci_parse_exponent_interval(udev, ep);
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);
} }
break; break;
/* Convert bInterval (in 1-255 frames) to microframes and round down to
* nearest power of 2.
*/
case USB_SPEED_FULL: 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: case USB_SPEED_LOW:
if (usb_endpoint_xfer_int(&ep->desc) || if (usb_endpoint_xfer_int(&ep->desc) ||
usb_endpoint_xfer_isoc(&ep->desc)) { usb_endpoint_xfer_isoc(&ep->desc)) {
interval = fls(8*ep->desc.bInterval) - 1;
if (interval > 10) interval = xhci_parse_frame_interval(udev, ep);
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);
} }
break; break;
default: default:
BUG(); BUG();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment