diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c index 0fe7e49cdc37..29ccc0150996 100644 --- a/drivers/staging/usbip/vhci_hcd.c +++ b/drivers/staging/usbip/vhci_hcd.c @@ -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, diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b574f9131b43..feb6e596c7c9 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -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, diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index f2040e8af8b1..3214ca375d64 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -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 ( diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index dfa1e1d371c8..d05ea03cfb4d 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -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)) diff --git a/drivers/usb/host/imx21-hcd.c b/drivers/usb/host/imx21-hcd.c index 2f180dfe5371..2562e92e3178 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -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; } diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 2a60a50bc420..c0e22f26da19 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -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. diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c index 6dd94b997d97..662cd002adfc 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -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__); } diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 1c8de7666d6a..f50e84ac570a 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -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; diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c index cd4b74f27d06..9154615292db 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -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; } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index ad54a4144756..38193f4e980e 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -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)) diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index 98afe75500cc..db6f8b9c19b6 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -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, diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index f3899b334c73..18b7099a8125 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -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 diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index a659e1590bca..b4785934e091 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -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; } diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 43e0a099d634..847b071b6fc9 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -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. */ diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index b46d1877e28e..489104a5ae14 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -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: diff --git a/drivers/usb/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c index cff246b7cb26..c175b7300c73 100644 --- a/drivers/usb/wusbcore/rh.c +++ b/drivers/usb/wusbcore/rh.c @@ -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; } diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h index 38c42b013641..22afcd37bc3f 100644 --- a/include/linux/usb/ch11.h +++ b/include/linux/usb/ch11.h @@ -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 @@ -61,6 +62,12 @@ #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