Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+62 −28
Original line number Original line Diff line number Diff line
@@ -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,
	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();
	}
	}