Commit 340a3504 authored by Sarah Sharp's avatar Sarah Sharp

xhci: Fix encoding for HS bulk/control NAK rate.

The xHCI 0.96 spec says that HS bulk and control endpoint NAK rate must
be encoded as an exponent of two number of microframes.  The endpoint
descriptor has the NAK rate encoded in number of microframes.  We were
just copying the value from the endpoint descriptor into the endpoint
context interval field, which was not correct.  This lead to the VIA
host rejecting the add of a bulk OUT endpoint from any USB 2.0 mass
storage device.

The fix is to use the correct encoding.  Refactor the code to convert
number of frames to an exponential number of microframes, and make sure
we convert the number of microframes in HS bulk and control endpoints to
an exponent.

This should be back ported to kernels as old as 2.6.31, that contain the
commit dfa49c4a "USB: xhci - fix math
in xhci_get_endpoint_interval"
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Tested-by: default avatarFelipe Contreras <felipe.contreras@gmail.com>
Suggested-by: default avatarAndiry Xu <andiry.xu@amd.com>
Cc: stable@vger.kernel.org
parent a45aa3b3
...@@ -1126,26 +1126,42 @@ static unsigned int xhci_parse_exponent_interval(struct usb_device *udev, ...@@ -1126,26 +1126,42 @@ static unsigned int xhci_parse_exponent_interval(struct usb_device *udev,
} }
/* /*
* Convert bInterval expressed in frames (in 1-255 range) to exponent of * Convert bInterval expressed in microframes (in 1-255 range) to exponent of
* microframes, rounded down to nearest power of 2. * microframes, rounded down to nearest power of 2.
*/ */
static unsigned int xhci_parse_frame_interval(struct usb_device *udev, static unsigned int xhci_microframes_to_exponent(struct usb_device *udev,
struct usb_host_endpoint *ep) struct usb_host_endpoint *ep, unsigned int desc_interval,
unsigned int min_exponent, unsigned int max_exponent)
{ {
unsigned int interval; unsigned int interval;
interval = fls(8 * ep->desc.bInterval) - 1; interval = fls(desc_interval) - 1;
interval = clamp_val(interval, 3, 10); interval = clamp_val(interval, min_exponent, max_exponent);
if ((1 << interval) != 8 * ep->desc.bInterval) if ((1 << interval) != desc_interval)
dev_warn(&udev->dev, dev_warn(&udev->dev,
"ep %#x - rounding interval to %d microframes, ep desc says %d microframes\n", "ep %#x - rounding interval to %d microframes, ep desc says %d microframes\n",
ep->desc.bEndpointAddress, ep->desc.bEndpointAddress,
1 << interval, 1 << interval,
8 * ep->desc.bInterval); desc_interval);
return interval; return interval;
} }
static unsigned int xhci_parse_microframe_interval(struct usb_device *udev,
struct usb_host_endpoint *ep)
{
return xhci_microframes_to_exponent(udev, ep,
ep->desc.bInterval, 0, 15);
}
static unsigned int xhci_parse_frame_interval(struct usb_device *udev,
struct usb_host_endpoint *ep)
{
return xhci_microframes_to_exponent(udev, ep,
ep->desc.bInterval * 8, 3, 10);
}
/* 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
...@@ -1164,7 +1180,7 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, ...@@ -1164,7 +1180,7 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev,
/* 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 = xhci_parse_microframe_interval(udev, ep);
break; break;
} }
/* Fall through - SS and HS isoc/int have same decoding */ /* Fall through - SS and HS isoc/int have same decoding */
......
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