diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r-- | drivers/usb/core/hub.c | 73 |
1 files changed, 65 insertions, 8 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index b574f9131b4..feb6e596c7c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -82,6 +82,10 @@ struct usb_hub { | |||
82 | void **port_owners; | 82 | void **port_owners; |
83 | }; | 83 | }; |
84 | 84 | ||
85 | static inline int hub_is_superspeed(struct usb_device *hdev) | ||
86 | { | ||
87 | return (hdev->descriptor.bDeviceProtocol == 3); | ||
88 | } | ||
85 | 89 | ||
86 | /* Protect struct usb_device->state and ->children members | 90 | /* Protect struct usb_device->state and ->children members |
87 | * Note: Both are also protected by ->dev.sem, except that ->state can | 91 | * 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) | |||
172 | } | 176 | } |
173 | 177 | ||
174 | /* USB 2.0 spec Section 11.24.4.5 */ | 178 | /* USB 2.0 spec Section 11.24.4.5 */ |
175 | static int get_hub_descriptor(struct usb_device *hdev, void *data, int size) | 179 | static int get_hub_descriptor(struct usb_device *hdev, void *data) |
176 | { | 180 | { |
177 | int i, ret; | 181 | int i, ret, size; |
182 | unsigned dtype; | ||
183 | |||
184 | if (hub_is_superspeed(hdev)) { | ||
185 | dtype = USB_DT_SS_HUB; | ||
186 | size = USB_DT_SS_HUB_SIZE; | ||
187 | } else { | ||
188 | dtype = USB_DT_HUB; | ||
189 | size = sizeof(struct usb_hub_descriptor); | ||
190 | } | ||
178 | 191 | ||
179 | for (i = 0; i < 3; i++) { | 192 | for (i = 0; i < 3; i++) { |
180 | ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), | 193 | ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), |
181 | USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, | 194 | USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, |
182 | USB_DT_HUB << 8, 0, data, size, | 195 | dtype << 8, 0, data, size, |
183 | USB_CTRL_GET_TIMEOUT); | 196 | USB_CTRL_GET_TIMEOUT); |
184 | if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2)) | 197 | if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2)) |
185 | return ret; | 198 | return ret; |
@@ -365,6 +378,19 @@ static int hub_port_status(struct usb_hub *hub, int port1, | |||
365 | } else { | 378 | } else { |
366 | *status = le16_to_cpu(hub->status->port.wPortStatus); | 379 | *status = le16_to_cpu(hub->status->port.wPortStatus); |
367 | *change = le16_to_cpu(hub->status->port.wPortChange); | 380 | *change = le16_to_cpu(hub->status->port.wPortChange); |
381 | |||
382 | if ((hub->hdev->parent != NULL) && | ||
383 | hub_is_superspeed(hub->hdev)) { | ||
384 | /* Translate the USB 3 port status */ | ||
385 | u16 tmp = *status & USB_SS_PORT_STAT_MASK; | ||
386 | if (*status & USB_SS_PORT_STAT_POWER) | ||
387 | tmp |= USB_PORT_STAT_POWER; | ||
388 | if ((*status & USB_SS_PORT_STAT_SPEED) == | ||
389 | USB_PORT_STAT_SPEED_5GBPS) | ||
390 | tmp |= USB_PORT_STAT_SUPER_SPEED; | ||
391 | *status = tmp; | ||
392 | } | ||
393 | |||
368 | ret = 0; | 394 | ret = 0; |
369 | } | 395 | } |
370 | mutex_unlock(&hub->status_mutex); | 396 | mutex_unlock(&hub->status_mutex); |
@@ -607,7 +633,7 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) | |||
607 | if (hdev->children[port1-1] && set_state) | 633 | if (hdev->children[port1-1] && set_state) |
608 | usb_set_device_state(hdev->children[port1-1], | 634 | usb_set_device_state(hdev->children[port1-1], |
609 | USB_STATE_NOTATTACHED); | 635 | USB_STATE_NOTATTACHED); |
610 | if (!hub->error) | 636 | if (!hub->error && !hub_is_superspeed(hub->hdev)) |
611 | ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); | 637 | ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); |
612 | if (ret) | 638 | if (ret) |
613 | dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", | 639 | 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) | |||
795 | clear_port_feature(hub->hdev, port1, | 821 | clear_port_feature(hub->hdev, port1, |
796 | USB_PORT_FEAT_C_ENABLE); | 822 | USB_PORT_FEAT_C_ENABLE); |
797 | } | 823 | } |
824 | if (portchange & USB_PORT_STAT_C_LINK_STATE) { | ||
825 | need_debounce_delay = true; | ||
826 | clear_port_feature(hub->hdev, port1, | ||
827 | USB_PORT_FEAT_C_PORT_LINK_STATE); | ||
828 | } | ||
798 | 829 | ||
799 | /* We can forget about a "removed" device when there's a | 830 | /* We can forget about a "removed" device when there's a |
800 | * physical disconnect or the connect status changes. | 831 | * physical disconnect or the connect status changes. |
@@ -964,12 +995,23 @@ static int hub_configure(struct usb_hub *hub, | |||
964 | goto fail; | 995 | goto fail; |
965 | } | 996 | } |
966 | 997 | ||
998 | if (hub_is_superspeed(hdev) && (hdev->parent != NULL)) { | ||
999 | ret = usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), | ||
1000 | HUB_SET_DEPTH, USB_RT_HUB, | ||
1001 | hdev->level - 1, 0, NULL, 0, | ||
1002 | USB_CTRL_SET_TIMEOUT); | ||
1003 | |||
1004 | if (ret < 0) { | ||
1005 | message = "can't set hub depth"; | ||
1006 | goto fail; | ||
1007 | } | ||
1008 | } | ||
1009 | |||
967 | /* Request the entire hub descriptor. | 1010 | /* Request the entire hub descriptor. |
968 | * hub->descriptor can handle USB_MAXCHILDREN ports, | 1011 | * hub->descriptor can handle USB_MAXCHILDREN ports, |
969 | * but the hub can/will return fewer bytes here. | 1012 | * but the hub can/will return fewer bytes here. |
970 | */ | 1013 | */ |
971 | ret = get_hub_descriptor(hdev, hub->descriptor, | 1014 | ret = get_hub_descriptor(hdev, hub->descriptor); |
972 | sizeof(*hub->descriptor)); | ||
973 | if (ret < 0) { | 1015 | if (ret < 0) { |
974 | message = "can't read hub descriptor"; | 1016 | message = "can't read hub descriptor"; |
975 | goto fail; | 1017 | goto fail; |
@@ -991,12 +1033,14 @@ static int hub_configure(struct usb_hub *hub, | |||
991 | 1033 | ||
992 | wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); | 1034 | wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); |
993 | 1035 | ||
994 | if (wHubCharacteristics & HUB_CHAR_COMPOUND) { | 1036 | /* FIXME for USB 3.0, skip for now */ |
1037 | if ((wHubCharacteristics & HUB_CHAR_COMPOUND) && | ||
1038 | !(hub_is_superspeed(hdev))) { | ||
995 | int i; | 1039 | int i; |
996 | char portstr [USB_MAXCHILDREN + 1]; | 1040 | char portstr [USB_MAXCHILDREN + 1]; |
997 | 1041 | ||
998 | for (i = 0; i < hdev->maxchild; i++) | 1042 | for (i = 0; i < hdev->maxchild; i++) |
999 | portstr[i] = hub->descriptor->DeviceRemovable | 1043 | portstr[i] = hub->descriptor->u.hs.DeviceRemovable |
1000 | [((i + 1) / 8)] & (1 << ((i + 1) % 8)) | 1044 | [((i + 1) / 8)] & (1 << ((i + 1) % 8)) |
1001 | ? 'F' : 'R'; | 1045 | ? 'F' : 'R'; |
1002 | portstr[hdev->maxchild] = 0; | 1046 | portstr[hdev->maxchild] = 0; |
@@ -2029,6 +2073,8 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, | |||
2029 | udev->speed = USB_SPEED_HIGH; | 2073 | udev->speed = USB_SPEED_HIGH; |
2030 | else if (portstatus & USB_PORT_STAT_LOW_SPEED) | 2074 | else if (portstatus & USB_PORT_STAT_LOW_SPEED) |
2031 | udev->speed = USB_SPEED_LOW; | 2075 | udev->speed = USB_SPEED_LOW; |
2076 | else if (portstatus & USB_PORT_STAT_SUPER_SPEED) | ||
2077 | udev->speed = USB_SPEED_SUPER; | ||
2032 | else | 2078 | else |
2033 | udev->speed = USB_SPEED_FULL; | 2079 | udev->speed = USB_SPEED_FULL; |
2034 | return 0; | 2080 | return 0; |
@@ -3430,6 +3476,17 @@ static void hub_events(void) | |||
3430 | clear_port_feature(hdev, i, | 3476 | clear_port_feature(hdev, i, |
3431 | USB_PORT_FEAT_C_RESET); | 3477 | USB_PORT_FEAT_C_RESET); |
3432 | } | 3478 | } |
3479 | if (portchange & USB_PORT_STAT_C_LINK_STATE) { | ||
3480 | clear_port_feature(hub->hdev, i, | ||
3481 | USB_PORT_FEAT_C_PORT_LINK_STATE); | ||
3482 | } | ||
3483 | if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) { | ||
3484 | dev_warn(hub_dev, | ||
3485 | "config error on port %d\n", | ||
3486 | i); | ||
3487 | clear_port_feature(hub->hdev, i, | ||
3488 | USB_PORT_FEAT_C_PORT_CONFIG_ERROR); | ||
3489 | } | ||
3433 | 3490 | ||
3434 | if (connect_change) | 3491 | if (connect_change) |
3435 | hub_port_connect_change(hub, i, | 3492 | hub_port_connect_change(hub, i, |