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: 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
This commit is contained in:
Родитель
926008c938
Коммит
dfa49c4ad1
|
@ -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();
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче