aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorJohn Youn <John.Youn@synopsys.com>2001-09-17 03:00:00 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-03-13 21:07:11 -0400
commitdbe79bbe9dcb22cb3651c46f18943477141ca452 (patch)
tree86e2dd608fd6ae7902b4abfdfbcf1969980386e3 /drivers/usb/core
parentad73dff32e04cad1ff2af89512bf489224b503cc (diff)
USB 3.0 Hub Changes
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>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hub.c73
1 files changed, 65 insertions, 8 deletions
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 {
82 void **port_owners; 82 void **port_owners;
83}; 83};
84 84
85static 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 */
175static int get_hub_descriptor(struct usb_device *hdev, void *data, int size) 179static 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,