When a SuperSpeed device is plugged into a high speed hub or an EHCI port, it will show up as a high speed device with a BCD of 0x0201 or 0x210 and it will contain a SuperSpeed Capabilities BOS descriptor. If we see such a high speed device, warn the user that they should plug their device into a USB 3.0 hub or xHCI root port. Only warn the user if there is at least one SuperSpeed roothub available. We could attempt to fetch BOS descriptors for low or full speed devices, in case the user plugs in a USB 3.0 device into an OHCI or UHCI-only port, but it's possible those devices will choke on the request and fail to be enumerated. Keep the current behavior of only asking for BOS descriptors for devices that advertise a bcdUSB of 0x0201 and above. Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx> Cc: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> Cc: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx> --- drivers/usb/core/hub.c | 18 ++++++++++++++++++ 1 files changed, 18 insertions(+), 0 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 79d339e..1224a69 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1246,6 +1246,7 @@ static void hub_release(struct kref *kref) } static unsigned highspeed_hubs; +static unsigned superspeed_hubs; static void hub_disconnect(struct usb_interface *intf) { @@ -1269,6 +1270,8 @@ static void hub_disconnect(struct usb_interface *intf) if (hub->hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; + if (hub->hdev->speed == USB_SPEED_SUPER) + superspeed_hubs--; usb_free_urb(hub->urb); kfree(hub->port_owners); @@ -1352,6 +1355,8 @@ descriptor_error: if (hdev->speed == USB_SPEED_HIGH) highspeed_hubs++; + if (hub->hdev->speed == USB_SPEED_SUPER) + superspeed_hubs++; if (hub_configure(hub, endpoint) >= 0) return 0; @@ -3142,6 +3147,15 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1) kfree(qual); } +static void check_superspeed(struct usb_hub *hub, struct usb_device *udev, + int port1) +{ + if (udev->speed != USB_SPEED_SUPER && udev->bos && udev->bos->ss_cap) { + dev_info(&udev->dev, "not running at top speed; " + "connect to a SuperSpeed port\n"); + } +} + static unsigned hub_power_remaining (struct usb_hub *hub) { @@ -3361,6 +3375,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, && udev->speed == USB_SPEED_FULL && highspeed_hubs != 0) check_highspeed (hub, udev, port1); + if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201 && + udev->speed == USB_SPEED_HIGH && + superspeed_hubs != 0) + check_superspeed(hub, udev, port1); /* Store the parent's children[] pointer. At this point * udev becomes globally accessible, although presumably -- 1.7.5.4 -- 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