[RFC] xHCI/USB: Make xHCI driver have a BOS descriptor.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



To add USB 3.0 link power management (LPM), we need to know what the U1
and U2 exit latencies are for the xHCI host controller.  External USB 3.0
hubs report these values through the SuperSpeed Capabilities descriptor in
the BOS descriptor.  Make the USB 3.0 roothub for the xHCI host behave
like an external hub and return the BOS descriptors.

The U1 and U2 exit latencies will vary across each host controller, so we
need to dynamically fill those values in by reading the exit latencies out
of the xHC registers.

I wasn't quite sure how to pass back the length copied to the userspace
buffer by the xHCI driver, so I made xHCI's hub_control function return the
length.  I think traditionally it's always returned 0 or a negative error
value, so this might not be the best way to share the copied length.  I'm
open to opinions as to how it "should be" done. :)

I'm also not sure if the U2 Device Exit Latency conversion is endian safe.
Can someone check the HCS_U2_LATENCY lines?

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
---
 drivers/usb/core/hcd.c      |   20 ++++++++++++++++++++
 drivers/usb/host/xhci-hub.c |   41 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+), 0 deletions(-)

diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 73cbbd8..2c94495 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -562,6 +562,26 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
 			else /* unsupported IDs --> "protocol stall" */
 				goto error;
 			break;
+		case USB_DT_BOS << 8:
+			if (hcd->speed != HCD_USB3)
+				goto error;
+			/*
+			 * Pass the request down to the xHCI driver.
+			 * U1/U2 exit latencies vary per host, so
+			 * the values must be programmed dynamically.
+			 */
+			status = hcd->driver->hub_control (hcd,
+					typeReq, wValue, wIndex,
+					ubuf, wLength);
+			if (status >= 0) {
+				if (urb->transfer_buffer_length < status)
+					status = urb->transfer_buffer_length;
+				urb->actual_length = status;
+				status = 0;
+			} else {
+				goto error;
+			}
+			break;
 		default:
 			goto error;
 		}
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 9f844d4..f8ec0f7 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -28,6 +28,24 @@
 #define	PORT_RWC_BITS	(PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
 			 PORT_RC | PORT_PLC | PORT_PE)
 
+/* usb 1.1 root hub device descriptor */
+static u8 usb_bos_descriptor [] = {
+	0x5,		/*  __u8 bLength, 5 bytes */
+	USB_DT_BOS,	/*  __u8 bDescriptorType */
+	0x0F, 0x00,	/*  __le16 wTotalLength, 15 bytes */
+	0x1,		/*  __u8 bNumDeviceCaps */
+	/* First device capability */
+	0x0a,		/*  __u8 bLength, 10 bytes */
+	0x10,		/* Device Capability; value = 16 */
+	0x03,		/* bDevCapabilityType, SUPERSPEED_USB */
+	0x00,		/* bmAttributes, LTM off by default */
+	0x08, 0x00,	/* wSpeedsSupported, 5Gbps only */
+	0x03,		/* bFunctionalitySupport, USB 3.0 speed only */
+	0x00,		/* bU1DevExitLat, set later. */
+	0x00, 0x00	/* __le16 bU2DevExitLat, set later. */
+};
+
+
 static void xhci_common_hub_descriptor(struct xhci_hcd *xhci,
 		struct usb_hub_descriptor *desc, int ports)
 {
@@ -430,6 +448,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 	int slot_id;
 	struct xhci_bus_state *bus_state;
 	u16 link_state = 0;
+	u16 len;
 
 	max_ports = xhci_get_ports(hcd, &port_array);
 	bus_state = &xhci->bus_state[hcd_index(hcd)];
@@ -455,6 +474,28 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 		xhci_hub_descriptor(hcd, xhci,
 				(struct usb_hub_descriptor *) buf);
 		break;
+	case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
+		if ((wValue & 0xff00) != (USB_DT_BOS << 8))
+			goto error;
+
+		if (hcd->speed != HCD_USB3)
+			goto error;
+
+		len = min_t(u16, wLength, sizeof(usb_bos_descriptor));
+		memcpy(buf, &usb_bos_descriptor, len);
+		if (len <= sizeof(usb_bos_descriptor) - 3) {
+			spin_unlock_irqrestore(&xhci->lock, flags);
+			return len;
+		}
+
+		temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
+		buf[12] = HCS_U1_LATENCY(temp);
+		if (len == sizeof(usb_bos_descriptor)) {
+			buf[13] = HCS_U2_LATENCY(temp) & 0xff;
+			buf[14] = HCS_U2_LATENCY(temp) >> 8;
+		}
+		spin_unlock_irqrestore(&xhci->lock, flags);
+		return len;
 	case GetPortStatus:
 		if (!wIndex || wIndex > max_ports)
 			goto error;
-- 
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux