Skip to content

Commit

Permalink
USB: Set hub depth after USB3 hub reset
Browse files Browse the repository at this point in the history
The superspeed device attached to a USB 3.0 hub(such as VIA's)
doesn't respond the address device command after resume. The
root cause is the superspeed hub will miss the Hub Depth value
that is used as an offset into the route string to locate the
bits it uses to determine the downstream port number after
reset, and all packets can't be routed to the device attached
to the superspeed hub.

Hub driver sends a Set Hub Depth request to the superspeed hub
except for USB 3.0 root hub when the hub is initialized and
doesn't send the request again after reset due to the resume
process. So moving the code that sends the Set Hub Depth request
to the superspeed hub from hub_configure() to hub_activate()
is to cover those situations include initialization and reset.

The patch should be backported to kernels as old as 2.6.39.

Signed-off-by: Elric Fu <elricfu1@gmail.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Cc: stable@vger.kernel.org
  • Loading branch information
Elric Fu authored and Sarah Sharp committed Feb 21, 2012
1 parent cab928e commit a45aa3b
Showing 1 changed file with 17 additions and 13 deletions.
30 changes: 17 additions & 13 deletions drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -705,10 +705,26 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
if (type == HUB_INIT3)
goto init3;

/* After a resume, port power should still be on.
/* The superspeed hub except for root hub has to use Hub Depth
* value as an offset into the route string to locate the bits
* it uses to determine the downstream port number. So hub driver
* should send a set hub depth request to superspeed hub after
* the superspeed hub is set configuration in initialization or
* reset procedure.
*
* After a resume, port power should still be on.
* For any other type of activation, turn it on.
*/
if (type != HUB_RESUME) {
if (hdev->parent && hub_is_superspeed(hdev)) {
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)
dev_err(hub->intfdev,
"set hub depth failed\n");
}

/* Speed up system boot by using a delayed_work for the
* hub's initial power-up delays. This is pretty awkward
Expand Down Expand Up @@ -987,18 +1003,6 @@ 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.
Expand Down

0 comments on commit a45aa3b

Please sign in to comment.