Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

xHCI/USB: Make xHCI driver have a BOS descriptor.

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. Make the roothub code in the USB core handle
hub_control() returning the length of the data copied.

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Sarah Sharp and committed by
Greg Kroah-Hartman
48e82361 fa3ae0c1

+50 -2
+16 -2
drivers/usb/core/hcd.c
··· 442 442 struct usb_ctrlrequest *cmd; 443 443 u16 typeReq, wValue, wIndex, wLength; 444 444 u8 *ubuf = urb->transfer_buffer; 445 - u8 tbuf [sizeof (struct usb_hub_descriptor)] 445 + /* 446 + * tbuf should be as big as the BOS descriptor and 447 + * the USB hub descriptor. 448 + */ 449 + u8 tbuf[USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE] 446 450 __attribute__((aligned(4))); 447 451 const u8 *bufp = tbuf; 448 452 unsigned len = 0; ··· 566 562 else /* unsupported IDs --> "protocol stall" */ 567 563 goto error; 568 564 break; 565 + case USB_DT_BOS << 8: 566 + goto nongeneric; 569 567 default: 570 568 goto error; 571 569 } ··· 602 596 /* CLASS REQUESTS (and errors) */ 603 597 604 598 default: 599 + nongeneric: 605 600 /* non-generic request */ 606 601 switch (typeReq) { 607 602 case GetHubStatus: ··· 611 604 break; 612 605 case GetHubDescriptor: 613 606 len = sizeof (struct usb_hub_descriptor); 607 + break; 608 + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: 609 + /* len is returned by hub_control */ 614 610 break; 615 611 } 616 612 status = hcd->driver->hub_control (hcd, ··· 625 615 status = -EPIPE; 626 616 } 627 617 628 - if (status) { 618 + if (status < 0) { 629 619 len = 0; 630 620 if (status != -EPIPE) { 631 621 dev_dbg (hcd->self.controller, ··· 634 624 typeReq, wValue, wIndex, 635 625 wLength, status); 636 626 } 627 + } else if (status > 0) { 628 + /* hub_control may return the length of data copied. */ 629 + len = status; 630 + status = 0; 637 631 } 638 632 if (len) { 639 633 if (urb->transfer_buffer_length < len)
+34
drivers/usb/host/xhci-hub.c
··· 28 28 #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ 29 29 PORT_RC | PORT_PLC | PORT_PE) 30 30 31 + /* usb 1.1 root hub device descriptor */ 32 + static u8 usb_bos_descriptor [] = { 33 + USB_DT_BOS_SIZE, /* __u8 bLength, 5 bytes */ 34 + USB_DT_BOS, /* __u8 bDescriptorType */ 35 + 0x0F, 0x00, /* __le16 wTotalLength, 15 bytes */ 36 + 0x1, /* __u8 bNumDeviceCaps */ 37 + /* First device capability */ 38 + USB_DT_USB_SS_CAP_SIZE, /* __u8 bLength, 10 bytes */ 39 + USB_DT_DEVICE_CAPABILITY, /* Device Capability */ 40 + USB_SS_CAP_TYPE, /* bDevCapabilityType, SUPERSPEED_USB */ 41 + 0x00, /* bmAttributes, LTM off by default */ 42 + USB_5GBPS_OPERATION, 0x00, /* wSpeedsSupported, 5Gbps only */ 43 + 0x03, /* bFunctionalitySupport, 44 + USB 3.0 speed only */ 45 + 0x00, /* bU1DevExitLat, set later. */ 46 + 0x00, 0x00 /* __le16 bU2DevExitLat, set later. */ 47 + }; 48 + 49 + 31 50 static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, 32 51 struct usb_hub_descriptor *desc, int ports) 33 52 { ··· 474 455 xhci_hub_descriptor(hcd, xhci, 475 456 (struct usb_hub_descriptor *) buf); 476 457 break; 458 + case DeviceRequest | USB_REQ_GET_DESCRIPTOR: 459 + if ((wValue & 0xff00) != (USB_DT_BOS << 8)) 460 + goto error; 461 + 462 + if (hcd->speed != HCD_USB3) 463 + goto error; 464 + 465 + memcpy(buf, &usb_bos_descriptor, 466 + USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE); 467 + temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3); 468 + buf[12] = HCS_U1_LATENCY(temp); 469 + put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); 470 + 471 + spin_unlock_irqrestore(&xhci->lock, flags); 472 + return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; 477 473 case GetPortStatus: 478 474 if (!wIndex || wIndex > max_ports) 479 475 goto error;