|
@@ -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,
|