Skip to content

Commit

Permalink
USB: xhci - fix math in xhci_get_endpoint_interval()
Browse files Browse the repository at this point in the history
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
  • Loading branch information
Dmitry Torokhov authored and Sarah Sharp committed Apr 13, 2011
1 parent 926008c commit dfa49c4
Showing 1 changed file with 62 additions and 28 deletions.
90 changes: 62 additions & 28 deletions drivers/usb/host/xhci-mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
}
Expand Down

0 comments on commit dfa49c4

Please sign in to comment.