Hub descriptor structure now has a union field to account for different superspeed fields. Signed-off-by: John Youn <johnyoun@xxxxxxxxxxxx> --- This might not be the best solution. I had to touch a whole bunch of hcd's that initialize the DeviceRemovable field for their root hub descriptor. Using a cast might be cleaner for existing code. Tested with a several different hubs superspeed and HS. drivers/usb/core/hub.c | 71 ++++++++++++++++++++++++++++++++++---- drivers/usb/host/ehci-hub.c | 4 +- drivers/usb/host/imx21-hcd.c | 4 +- drivers/usb/host/isp116x-hcd.c | 4 +- drivers/usb/host/isp1362-hcd.c | 4 +- drivers/usb/host/isp1760-hcd.c | 4 +- drivers/usb/host/ohci-hub.c | 10 +++--- drivers/usb/host/oxu210hp-hcd.c | 4 +- drivers/usb/host/r8a66597-hcd.c | 4 +- drivers/usb/host/sl811-hcd.c | 4 +- drivers/usb/host/u132-hcd.c | 10 +++--- drivers/usb/host/xhci-hub.c | 4 +- drivers/usb/wusbcore/rh.c | 4 +- include/linux/usb/ch11.h | 50 +++++++++++++++++++++++++-- 14 files changed, 139 insertions(+), 42 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 70cccc7..f79a0f6 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,24 @@ 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 +379,17 @@ 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 & USB3_PORT_STAT_MASK; + if (*status & USB3_PORT_STAT_POWER) + tmp |= USB_PORT_STAT_POWER; + if ((*status & USB3_PORT_STAT_SPEED) == USB3_PORT_STAT_SPEED_SUPER) + tmp |= USB_PORT_STAT_SUPER_SPEED; + *status = tmp; + } + ret = 0; } mutex_unlock(&hub->status_mutex); @@ -607,7 +632,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", @@ -771,6 +796,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. @@ -940,12 +970,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; @@ -967,12 +1008,13 @@ 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; @@ -1988,6 +2030,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; @@ -3380,6 +3424,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/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index e7d3d8d..57ba6f5 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -676,8 +676,8 @@ ehci_hub_descriptor ( desc->bDescLength = 7 + 2 * temp; /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ - memset (&desc->bitmap [0], 0, temp); - memset (&desc->bitmap [temp], 0xff, temp); + memset (&desc->u.hs.bitmap [0], 0, temp); + memset (&desc->u.hs.bitmap [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 ca0e98d..873e4e7 100644 --- a/drivers/usb/host/imx21-hcd.c +++ b/drivers/usb/host/imx21-hcd.c @@ -1320,8 +1320,8 @@ static int get_hub_descriptor(struct usb_hcd *hcd, 0x0010 | /* No over current protection */ 0); - desc->bitmap[0] = 1 << 1; - desc->bitmap[1] = ~0; + desc->u.hs.bitmap[0] = 1 << 1; + desc->u.hs.bitmap[1] = ~0; return 0; } diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index d9e8212..b3d5a41 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); /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ - desc->bitmap[0] = 0; - desc->bitmap[1] = ~0; + desc->u.hs.bitmap[0] = 0; + desc->u.hs.bitmap[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 0587ad4..3103368 100644 --- a/drivers/usb/host/isp1362-hcd.c +++ b/drivers/usb/host/isp1362-hcd.c @@ -1557,8 +1557,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; /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ - desc->bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1; - desc->bitmap[1] = ~0; + desc->u.hs.bitmap[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1; + desc->u.hs.bitmap[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 dbcafa2..1a05f7a 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -1844,8 +1844,8 @@ static void isp1760_hub_descriptor(struct isp1760_hcd *priv, desc->bDescLength = 7 + 2 * temp; /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ - memset(&desc->bitmap[0], 0, temp); - memset(&desc->bitmap[temp], 0xff, temp); + memset(&desc->u.hs.bitmap[0], 0, temp); + memset(&desc->u.hs.bitmap[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 65cac8c..7883cbb 100644 --- a/drivers/usb/host/ohci-hub.c +++ b/drivers/usb/host/ohci-hub.c @@ -573,13 +573,13 @@ ohci_hub_descriptor ( /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ rh = roothub_b (ohci); - memset(desc->bitmap, 0xff, sizeof(desc->bitmap)); - desc->bitmap [0] = rh & RH_B_DR; + memset(desc->u.hs.bitmap, 0xff, sizeof(desc->u.hs.bitmap)); + desc->u.hs.bitmap [0] = rh & RH_B_DR; if (ohci->num_ports > 7) { - desc->bitmap [1] = (rh & RH_B_DR) >> 8; - desc->bitmap [2] = 0xff; + desc->u.hs.bitmap [1] = (rh & RH_B_DR) >> 8; + desc->u.hs.bitmap [2] = 0xff; } else - desc->bitmap [1] = 0xff; + desc->u.hs.bitmap [1] = 0xff; } /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index f608dfd..e190a55 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; /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ - memset(&desc->bitmap[0], 0, temp); - memset(&desc->bitmap[temp], 0xff, temp); + memset(&desc->u.hs.bitmap[0], 0, temp); + memset(&desc->u.hs.bitmap[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 77be3c2..ae66928 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -2150,8 +2150,8 @@ static void r8a66597_hub_descriptor(struct r8a66597 *r8a66597, desc->bDescLength = 9; desc->bPwrOn2PwrGood = 0; desc->wHubCharacteristics = cpu_to_le16(0x0011); - desc->bitmap[0] = ((1 << r8a66597->max_root_hub) - 1) << 1; - desc->bitmap[1] = ~0; + desc->u.hs.bitmap[0] = ((1 << r8a66597->max_root_hub) - 1) << 1; + desc->u.hs.bitmap[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 bcf9f0e..fa811fa 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -1108,8 +1108,8 @@ sl811h_hub_descriptor ( desc->wHubCharacteristics = cpu_to_le16(temp); /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ - desc->bitmap[0] = 0 << 1; - desc->bitmap[1] = ~0; + desc->u.hs.bitmap[0] = 0 << 1; + desc->u.hs.bitmap[1] = ~0; } static void diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index 5b31bae..dab178f 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -2607,13 +2607,13 @@ static int u132_roothub_descriptor(struct u132 *u132, retval = u132_read_pcimem(u132, roothub.b, &rh_b); if (retval) return retval; - memset(desc->bitmap, 0xff, sizeof(desc->bitmap)); - desc->bitmap[0] = rh_b & RH_B_DR; + memset(desc->u.hs.bitmap, 0xff, sizeof(desc->u.hs.bitmap)); + desc->u.hs.bitmap[0] = rh_b & RH_B_DR; if (u132->num_ports > 7) { - desc->bitmap[1] = (rh_b & RH_B_DR) >> 8; - desc->bitmap[2] = 0xff; + desc->u.hs.bitmap[1] = (rh_b & RH_B_DR) >> 8; + desc->u.hs.bitmap[2] = 0xff; } else - desc->bitmap[1] = 0xff; + desc->u.hs.bitmap[1] = 0xff; return 0; } diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index a1a7a97..e230b7a 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -42,8 +42,8 @@ static void xhci_hub_descriptor(struct xhci_hcd *xhci, desc->bDescLength = 7 + 2 * temp; /* Why does core/hcd.h define bitmap? It's just confusing. */ - 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/wusbcore/rh.c b/drivers/usb/wusbcore/rh.c index a68ad7a..248017a 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->bitmap[0], 0, temp); - memset(&descr->bitmap[temp], 0xff, temp); + memset(&descr->u.hs.bitmap[0], 0, temp); + memset(&descr->u.hs.bitmap[temp], 0xff, temp); return 0; } diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h index 119194c..f145b40 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 feature numbers @@ -54,6 +55,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 /* * Hub Status and Hub Change results @@ -84,6 +91,21 @@ struct usb_port_status { #define USB_PORT_STAT_SUPER_SPEED 0x8000 /* Linux-internal */ /* + * USB 3.0 wPortStatus bit fields + * See USB 3.0 spec Table 10-10 + */ +/* Bits that are the same from USB 2.0 */ +#define USB3_PORT_STAT_MASK (USB_PORT_STAT_CONNECTION | \ + USB_PORT_STAT_ENABLE | \ + USB_PORT_STAT_OVERCURRENT | \ + USB_PORT_STAT_RESET) +/* 3.0 specific bits */ +#define USB3_PORT_STAT_LINK_STATE 0x01e0 +#define USB3_PORT_STAT_POWER 0x0200 +#define USB3_PORT_STAT_SPEED 0x1c00 +#define USB3_PORT_STAT_SPEED_SUPER 0x0 + +/* * wPortChange bit field * See USB 2.0 spec Table 11-22 * Bits 0 to 4 shown, bits 5 to 15 are reserved @@ -94,6 +116,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) @@ -128,7 +157,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; @@ -137,11 +168,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 -- 1.7.2.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