]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
USB 3.0 Hub Changes
authorJohn Youn <John.Youn@synopsys.com>
Mon, 17 Sep 2001 07:00:00 +0000 (00:00 -0700)
committerSarah Sharp <sarah.a.sharp@linux.intel.com>
Mon, 14 Mar 2011 01:07:11 +0000 (18:07 -0700)
Update the USB core to deal with USB 3.0 hubs.  These hubs have a slightly
different hub descriptor than USB 2.0 hubs, with a fixed (rather than
variable length) size.  Change the USB core's hub descriptor to have a
union for the last fields that differ.  Change the host controller drivers
that access those last fields (DeviceRemovable and PortPowerCtrlMask) to
use the union.

Translate the new version of the hub port status field into the old
version that khubd understands.  (Note: we need to fix it to translate the
roothub's port status once we stop converting it to USB 2.0 hub status
internally.)

Add new code to handle link state change status.  Send out new control
messages that are needed for USB 3.0 hubs, like Set Hub Depth.

This patch is a modified version of the original patch submitted by John
Youn.  It's updated to reflect the removal of the "bitmap" #define, and
change the hub descriptor accesses of a couple new host controller
drivers.

Signed-off-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Cc: Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
Cc: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
Cc: Tony Olech <tony.olech@elandigitalsystems.com>
Cc: "Robert P. J. Day" <rpjday@crashcourse.ca>
Cc: Max Vozeler <mvz@vozeler.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Cc: Rodolfo Giometti <giometti@linux.it>
Cc: Mike Frysinger <vapier@gentoo.org>
Cc: Anton Vorontsov <avorontsov@mvista.com>
Cc: Sebastian Siewior <bigeasy@linutronix.de>
Cc: Lothar Wassmann <LW@KARO-electronics.de>
Cc: Olav Kongas <ok@artecdesign.ee>
Cc: Martin Fuzzey <mfuzzey@gmail.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: David Brownell <dbrownell@users.sourceforge.net>
17 files changed:
drivers/staging/usbip/vhci_hcd.c
drivers/usb/core/hub.c
drivers/usb/gadget/dummy_hcd.c
drivers/usb/host/ehci-hub.c
drivers/usb/host/imx21-hcd.c
drivers/usb/host/isp116x-hcd.c
drivers/usb/host/isp1362-hcd.c
drivers/usb/host/isp1760-hcd.c
drivers/usb/host/ohci-hub.c
drivers/usb/host/oxu210hp-hcd.c
drivers/usb/host/r8a66597-hcd.c
drivers/usb/host/sl811-hcd.c
drivers/usb/host/u132-hcd.c
drivers/usb/host/xhci-hub.c
drivers/usb/musb/musb_virthub.c
drivers/usb/wusbcore/rh.c
include/linux/usb/ch11.h

index 0fe7e49cdc37a980f0e42a9203794915177d6a00..29ccc01509966389ed110d125ce23627592b965c 100644 (file)
@@ -255,8 +255,8 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc)
        desc->wHubCharacteristics = (__force __u16)
                (__constant_cpu_to_le16(0x0001));
        desc->bNbrPorts = VHCI_NPORTS;
-       desc->DeviceRemovable[0] = 0xff;
-       desc->DeviceRemovable[1] = 0xff;
+       desc->u.hs.DeviceRemovable[0] = 0xff;
+       desc->u.hs.DeviceRemovable[1] = 0xff;
 }
 
 static int vhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
index b574f9131b43f54f20a84a014945b4fdad93ae00..feb6e596c7c9c256f93603808367e39be4407f0d 100644 (file)
@@ -82,6 +82,10 @@ struct usb_hub {
        void                    **port_owners;
 };
 
+static inline int hub_is_superspeed(struct usb_device *hdev)
+{
+       return (hdev->descriptor.bDeviceProtocol == 3);
+}
 
 /* Protect struct usb_device->state and ->children members
  * Note: Both are also protected by ->dev.sem, except that ->state can
@@ -172,14 +176,23 @@ static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
 }
 
 /* USB 2.0 spec Section 11.24.4.5 */
-static int get_hub_descriptor(struct usb_device *hdev, void *data, int size)
+static int get_hub_descriptor(struct usb_device *hdev, void *data)
 {
-       int i, ret;
+       int i, ret, size;
+       unsigned dtype;
+
+       if (hub_is_superspeed(hdev)) {
+               dtype = USB_DT_SS_HUB;
+               size = USB_DT_SS_HUB_SIZE;
+       } else {
+               dtype = USB_DT_HUB;
+               size = sizeof(struct usb_hub_descriptor);
+       }
 
        for (i = 0; i < 3; i++) {
                ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
                        USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
-                       USB_DT_HUB << 8, 0, data, size,
+                       dtype << 8, 0, data, size,
                        USB_CTRL_GET_TIMEOUT);
                if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))
                        return ret;
@@ -365,6 +378,19 @@ static int hub_port_status(struct usb_hub *hub, int port1,
        } else {
                *status = le16_to_cpu(hub->status->port.wPortStatus);
                *change = le16_to_cpu(hub->status->port.wPortChange);
+
+               if ((hub->hdev->parent != NULL) &&
+                               hub_is_superspeed(hub->hdev)) {
+                       /* Translate the USB 3 port status */
+                       u16 tmp = *status & USB_SS_PORT_STAT_MASK;
+                       if (*status & USB_SS_PORT_STAT_POWER)
+                               tmp |= USB_PORT_STAT_POWER;
+                       if ((*status & USB_SS_PORT_STAT_SPEED) ==
+                                       USB_PORT_STAT_SPEED_5GBPS)
+                               tmp |= USB_PORT_STAT_SUPER_SPEED;
+                       *status = tmp;
+               }
+
                ret = 0;
        }
        mutex_unlock(&hub->status_mutex);
@@ -607,7 +633,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
        if (hdev->children[port1-1] && set_state)
                usb_set_device_state(hdev->children[port1-1],
                                USB_STATE_NOTATTACHED);
-       if (!hub->error)
+       if (!hub->error && !hub_is_superspeed(hub->hdev))
                ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
        if (ret)
                dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n",
@@ -795,6 +821,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                        clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_ENABLE);
                }
+               if (portchange & USB_PORT_STAT_C_LINK_STATE) {
+                       need_debounce_delay = true;
+                       clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_PORT_LINK_STATE);
+               }
 
                /* We can forget about a "removed" device when there's a
                 * physical disconnect or the connect status changes.
@@ -964,12 +995,23 @@ static int hub_configure(struct usb_hub *hub,
                goto fail;
        }
 
+       if (hub_is_superspeed(hdev) && (hdev->parent != NULL)) {
+               ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),
+                               HUB_SET_DEPTH, USB_RT_HUB,
+                               hdev->level - 1, 0, NULL, 0,
+                               USB_CTRL_SET_TIMEOUT);
+
+               if (ret < 0) {
+                       message = "can't set hub depth";
+                       goto fail;
+               }
+       }
+
        /* Request the entire hub descriptor.
         * hub->descriptor can handle USB_MAXCHILDREN ports,
         * but the hub can/will return fewer bytes here.
         */
-       ret = get_hub_descriptor(hdev, hub->descriptor,
-                       sizeof(*hub->descriptor));
+       ret = get_hub_descriptor(hdev, hub->descriptor);
        if (ret < 0) {
                message = "can't read hub descriptor";
                goto fail;
@@ -991,12 +1033,14 @@ static int hub_configure(struct usb_hub *hub,
 
        wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
 
-       if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
+       /* FIXME for USB 3.0, skip for now */
+       if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
+                       !(hub_is_superspeed(hdev))) {
                int     i;
                char    portstr [USB_MAXCHILDREN + 1];
 
                for (i = 0; i < hdev->maxchild; i++)
-                       portstr[i] = hub->descriptor->DeviceRemovable
+                       portstr[i] = hub->descriptor->u.hs.DeviceRemovable
                                    [((i + 1) / 8)] & (1 << ((i + 1) % 8))
                                ? 'F' : 'R';
                portstr[hdev->maxchild] = 0;
@@ -2029,6 +2073,8 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                                udev->speed = USB_SPEED_HIGH;
                        else if (portstatus & USB_PORT_STAT_LOW_SPEED)
                                udev->speed = USB_SPEED_LOW;
+                       else if (portstatus & USB_PORT_STAT_SUPER_SPEED)
+                               udev->speed = USB_SPEED_SUPER;
                        else
                                udev->speed = USB_SPEED_FULL;
                        return 0;
@@ -3430,6 +3476,17 @@ static void hub_events(void)
                                clear_port_feature(hdev, i,
                                        USB_PORT_FEAT_C_RESET);
                        }
+                       if (portchange & USB_PORT_STAT_C_LINK_STATE) {
+                               clear_port_feature(hub->hdev, i,
+                                               USB_PORT_FEAT_C_PORT_LINK_STATE);
+                       }
+                       if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
+                               dev_warn(hub_dev,
+                                       "config error on port %d\n",
+                                       i);
+                               clear_port_feature(hub->hdev, i,
+                                               USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
+                       }
 
                        if (connect_change)
                                hub_port_connect_change(hub, i,
index f2040e8af8b154bafac79f0cfe8cfe48397b88a7..3214ca375d640536ab871b05a76b3e9b8924ad62 100644 (file)
@@ -1593,8 +1593,8 @@ hub_descriptor (struct usb_hub_descriptor *desc)
        desc->bDescLength = 9;
        desc->wHubCharacteristics = cpu_to_le16(0x0001);
        desc->bNbrPorts = 1;
-       desc->DeviceRemovable[0] = 0xff;
-       desc->DeviceRemovable[1] = 0xff;
+       desc->u.hs.DeviceRemovable[0] = 0xff;
+       desc->u.hs.DeviceRemovable[1] = 0xff;
 }
 
 static int dummy_hub_control (
index dfa1e1d371c8590086a31cfea64a1a303f713d71..d05ea03cfb4d4d8aeae6a8db0db7559cebed6b82 100644 (file)
@@ -717,8 +717,8 @@ ehci_hub_descriptor (
        desc->bDescLength = 7 + 2 * temp;
 
        /* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
-       memset(&desc->DeviceRemovable[0], 0, temp);
-       memset(&desc->DeviceRemovable[temp], 0xff, temp);
+       memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
+       memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
 
        temp = 0x0008;                  /* per-port overcurrent reporting */
        if (HCS_PPC (ehci->hcs_params))
index 2f180dfe5371fb1ff78ca44dad718df9baf88045..2562e92e3178184246fe0ec4139e523f1bf06de9 100644 (file)
@@ -1472,8 +1472,8 @@ static int get_hub_descriptor(struct usb_hcd *hcd,
                0x0010 |        /* No over current protection */
                0);
 
-       desc->DeviceRemovable[0] = 1 << 1;
-       desc->DeviceRemovable[1] = ~0;
+       desc->u.hs.DeviceRemovable[0] = 1 << 1;
+       desc->u.hs.DeviceRemovable[1] = ~0;
        return 0;
 }
 
index 2a60a50bc420250af5aaaaeca8c908d75acaebb2..c0e22f26da199d4189ed511315d19ade8b043b5f 100644 (file)
@@ -952,8 +952,8 @@ static void isp116x_hub_descriptor(struct isp116x *isp116x,
        desc->wHubCharacteristics = cpu_to_le16((u16) ((reg >> 8) & 0x1f));
        desc->bPwrOn2PwrGood = (u8) ((reg >> 24) & 0xff);
        /* ports removable, and legacy PortPwrCtrlMask */
-       desc->DeviceRemovable[0] = 0;
-       desc->DeviceRemovable[1] = ~0;
+       desc->u.hs.DeviceRemovable[0] = 0;
+       desc->u.hs.DeviceRemovable[1] = ~0;
 }
 
 /* Perform reset of a given port.
index 6dd94b997d972d381308614ae8235f4f101084ca..662cd002adfc42a5a69e000d7397064ab9332e5a 100644 (file)
@@ -1553,8 +1553,8 @@ static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd,
        DBG(0, "%s: hubcharacteristics = %02x\n", __func__, cpu_to_le16((reg >> 8) & 0x1f));
        desc->bPwrOn2PwrGood = (reg >> 24) & 0xff;
        /* ports removable, and legacy PortPwrCtrlMask */
-       desc->DeviceRemovable[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
-       desc->DeviceRemovable[1] = ~0;
+       desc->u.hs.DeviceRemovable[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1;
+       desc->u.hs.DeviceRemovable[1] = ~0;
 
        DBG(3, "%s: exit\n", __func__);
 }
index 1c8de7666d6ae544ddfc1407dbe1f005b2f35e6a..f50e84ac570aa9eced485e6372db76287fa9df76 100644 (file)
@@ -1752,8 +1752,8 @@ static void isp1760_hub_descriptor(struct isp1760_hcd *priv,
        desc->bDescLength = 7 + 2 * temp;
 
        /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
-       memset(&desc->DeviceRemovable[0], 0, temp);
-       memset(&desc->DeviceRemovable[temp], 0xff, temp);
+       memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
+       memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
 
        /* per-port overcurrent reporting */
        temp = 0x0008;
index cd4b74f27d06b4ec3b99abcba4bde6b08c86f197..9154615292db7a98ad5126c0fc5c9446b90bf119 100644 (file)
@@ -582,13 +582,14 @@ ohci_hub_descriptor (
 
        /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
        rh = roothub_b (ohci);
-       memset(desc->DeviceRemovable, 0xff, sizeof(desc->DeviceRemovable));
-       desc->DeviceRemovable[0] = rh & RH_B_DR;
+       memset(desc->u.hs.DeviceRemovable, 0xff,
+                       sizeof(desc->u.hs.DeviceRemovable));
+       desc->u.hs.DeviceRemovable[0] = rh & RH_B_DR;
        if (ohci->num_ports > 7) {
-               desc->DeviceRemovable[1] = (rh & RH_B_DR) >> 8;
-               desc->DeviceRemovable[2] = 0xff;
+               desc->u.hs.DeviceRemovable[1] = (rh & RH_B_DR) >> 8;
+               desc->u.hs.DeviceRemovable[2] = 0xff;
        } else
-               desc->DeviceRemovable[1] = 0xff;
+               desc->u.hs.DeviceRemovable[1] = 0xff;
 }
 
 /*-------------------------------------------------------------------------*/
index ad54a41447567f0a28a26214e993b27b726cede6..38193f4e980e04eda0bb8ac5169095c27b544b3e 100644 (file)
@@ -452,8 +452,8 @@ static void ehci_hub_descriptor(struct oxu_hcd *oxu,
        desc->bDescLength = 7 + 2 * temp;
 
        /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
-       memset(&desc->DeviceRemovable[0], 0, temp);
-       memset(&desc->DeviceRemovable[temp], 0xff, temp);
+       memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
+       memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
 
        temp = 0x0008;                  /* per-port overcurrent reporting */
        if (HCS_PPC(oxu->hcs_params))
index 98afe75500ccdd38d21a0ffef7d71b1539457249..db6f8b9c19b6d5a1a9768f0bb50bae3bdf8ae5ee 100644 (file)
@@ -2150,8 +2150,9 @@ static void r8a66597_hub_descriptor(struct r8a66597 *r8a66597,
        desc->bDescLength = 9;
        desc->bPwrOn2PwrGood = 0;
        desc->wHubCharacteristics = cpu_to_le16(0x0011);
-       desc->DeviceRemovable[0] = ((1 << r8a66597->max_root_hub) - 1) << 1;
-       desc->DeviceRemovable[1] = ~0;
+       desc->u.hs.DeviceRemovable[0] =
+               ((1 << r8a66597->max_root_hub) - 1) << 1;
+       desc->u.hs.DeviceRemovable[1] = ~0;
 }
 
 static int r8a66597_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
index f3899b334c73af0e0bea3a18c22001ee2c3a0428..18b7099a8125c9e0cbcfe9858259dfe85482826d 100644 (file)
@@ -1112,8 +1112,8 @@ sl811h_hub_descriptor (
        desc->wHubCharacteristics = cpu_to_le16(temp);
 
        /* ports removable, and legacy PortPwrCtrlMask */
-       desc->DeviceRemovable[0] = 0 << 1;
-       desc->DeviceRemovable[1] = ~0;
+       desc->u.hs.DeviceRemovable[0] = 0 << 1;
+       desc->u.hs.DeviceRemovable[1] = ~0;
 }
 
 static void
index a659e1590bca5ee51bb053127d37611a47132aaf..b4785934e091afd2b358530271c9037e722c5507 100644 (file)
@@ -2604,13 +2604,14 @@ static int u132_roothub_descriptor(struct u132 *u132,
        retval = u132_read_pcimem(u132, roothub.b, &rh_b);
        if (retval)
                return retval;
-       memset(desc->DeviceRemovable, 0xff, sizeof(desc->DeviceRemovable));
-       desc->DeviceRemovable[0] = rh_b & RH_B_DR;
+       memset(desc->u.hs.DeviceRemovable, 0xff,
+                       sizeof(desc->u.hs.DeviceRemovable));
+       desc->u.hs.DeviceRemovable[0] = rh_b & RH_B_DR;
        if (u132->num_ports > 7) {
-               desc->DeviceRemovable[1] = (rh_b & RH_B_DR) >> 8;
-               desc->DeviceRemovable[2] = 0xff;
+               desc->u.hs.DeviceRemovable[1] = (rh_b & RH_B_DR) >> 8;
+               desc->u.hs.DeviceRemovable[2] = 0xff;
        } else
-               desc->DeviceRemovable[1] = 0xff;
+               desc->u.hs.DeviceRemovable[1] = 0xff;
        return 0;
 }
 
index 43e0a099d63420db38b82d66c367bb3dfb420090..847b071b6fc95718e89dce93c23bbe34793c477f 100644 (file)
@@ -45,8 +45,8 @@ static void xhci_hub_descriptor(struct xhci_hcd *xhci,
        temp = 1 + (ports / 8);
        desc->bDescLength = 7 + 2 * temp;
 
-       memset(&desc->DeviceRemovable[0], 0, temp);
-       memset(&desc->DeviceRemovable[temp], 0xff, temp);
+       memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
+       memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
 
        /* Ugh, these should be #defines, FIXME */
        /* Using table 11-13 in USB 2.0 spec. */
index b46d1877e28e85cf8a036da5e38a8f8af8a466b2..489104a5ae14507f8d2f1010ced652981d0e59c2 100644 (file)
@@ -305,8 +305,8 @@ int musb_hub_control(
                desc->bHubContrCurrent = 0;
 
                /* workaround bogus struct definition */
-               desc->DeviceRemovable[0] = 0x02;        /* port 1 */
-               desc->DeviceRemovable[1] = 0xff;
+               desc->u.hs.DeviceRemovable[0] = 0x02;   /* port 1 */
+               desc->u.hs.DeviceRemovable[1] = 0xff;
                }
                break;
        case GetHubStatus:
index cff246b7cb26e255b0568a7afd81a05176866f31..c175b7300c7368016deb5118a579fe3b6073fde5 100644 (file)
@@ -184,8 +184,8 @@ static int wusbhc_rh_get_hub_descr(struct wusbhc *wusbhc, u16 wValue,
        descr->bPwrOn2PwrGood = 0;
        descr->bHubContrCurrent = 0;
        /* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
-       memset(&descr->DeviceRemovable[0], 0, temp);
-       memset(&descr->DeviceRemovable[temp], 0xff, temp);
+       memset(&descr->u.hs.DeviceRemovable[0], 0, temp);
+       memset(&descr->u.hs.DeviceRemovable[temp], 0xff, temp);
        return 0;
 }
 
index 38c42b013641bc69e0a5587e0aeeac3e3c864f3a..22afcd37bc3f5d1b69072f5f3359b41ce9d56ca6 100644 (file)
@@ -26,6 +26,7 @@
 #define HUB_RESET_TT           9
 #define HUB_GET_TT_STATE       10
 #define HUB_STOP_TT            11
+#define HUB_SET_DEPTH          12
 
 /*
  * Hub class additional requests defined by USB 3.0 spec
 #define USB_PORT_FEAT_TEST              21
 #define USB_PORT_FEAT_INDICATOR         22
 #define USB_PORT_FEAT_C_PORT_L1         23
+#define USB_PORT_FEAT_C_PORT_LINK_STATE        25
+#define USB_PORT_FEAT_C_PORT_CONFIG_ERROR 26
+#define USB_PORT_FEAT_PORT_REMOTE_WAKE_MASK 27
+#define USB_PORT_FEAT_BH_PORT_RESET     28
+#define USB_PORT_FEAT_C_BH_PORT_RESET   29
+#define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT 30
 
 /*
  * Port feature selectors added by USB 3.0 spec.
@@ -110,8 +117,14 @@ struct usb_port_status {
  */
 #define USB_PORT_STAT_LINK_STATE       0x01e0
 #define USB_SS_PORT_STAT_POWER         0x0200
+#define USB_SS_PORT_STAT_SPEED         0x1c00
 #define USB_PORT_STAT_SPEED_5GBPS      0x0000
 /* Valid only if port is enabled */
+/* Bits that are the same from USB 2.0 */
+#define USB_SS_PORT_STAT_MASK (USB_PORT_STAT_CONNECTION |          \
+                               USB_PORT_STAT_ENABLE |      \
+                               USB_PORT_STAT_OVERCURRENT | \
+                               USB_PORT_STAT_RESET)
 
 /*
  * Definitions for PORT_LINK_STATE values
@@ -141,6 +154,13 @@ struct usb_port_status {
 #define USB_PORT_STAT_C_OVERCURRENT    0x0008
 #define USB_PORT_STAT_C_RESET          0x0010
 #define USB_PORT_STAT_C_L1             0x0020
+/*
+ * USB 3.0 wPortChange bit fields
+ * See USB 3.0 spec Table 10-11
+ */
+#define USB_PORT_STAT_C_BH_RESET       0x0020
+#define USB_PORT_STAT_C_LINK_STATE     0x0040
+#define USB_PORT_STAT_C_CONFIG_ERROR   0x0080
 
 /*
  * wHubCharacteristics (masks)
@@ -175,7 +195,9 @@ struct usb_hub_status {
  */
 
 #define USB_DT_HUB                     (USB_TYPE_CLASS | 0x09)
+#define USB_DT_SS_HUB                  (USB_TYPE_CLASS | 0x0a)
 #define USB_DT_HUB_NONVAR_SIZE         7
+#define USB_DT_SS_HUB_SIZE              12
 
 struct usb_hub_descriptor {
        __u8  bDescLength;
@@ -184,11 +206,22 @@ struct usb_hub_descriptor {
        __le16 wHubCharacteristics;
        __u8  bPwrOn2PwrGood;
        __u8  bHubContrCurrent;
-               /* add 1 bit for hub status change; round to bytes */
-       __u8  DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
-       __u8  PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
-} __attribute__ ((packed));
 
+       /* 2.0 and 3.0 hubs differ here */
+       union {
+               struct {
+                       /* add 1 bit for hub status change; round to bytes */
+                       __u8  DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];
+                       __u8  PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
+               }  __attribute__ ((packed)) hs;
+
+               struct {
+                       __u8 bHubHdrDecLat;
+                       __u16 wHubDelay;
+                       __u16 DeviceRemovable;
+               }  __attribute__ ((packed)) ss;
+       } u;
+} __attribute__ ((packed));
 
 /* port indicator status selectors, tables 11-7 and 11-25 */
 #define HUB_LED_AUTO   0