Re: [PATCH v2] usb: hub: Cycle HUB power when initialization fails

From: Alan Stern
Date: Thu Nov 09 2017 - 11:23:14 EST


On Thu, 9 Nov 2017, Mike Looijmans wrote:

> Sometimes the USB device gets confused about the state of the initialization and
> the connection fails. In particular, the device thinks that it's already set up
> and running while the host thinks the device still needs to be configured. To
> work around this issue, power-cycle the hub's output to issue a sort of "reset"
> to the device. This makes the device restart its state machine and then the
> initialization succeeds.
>
> This fixes problems where the kernel reports a list of errors like this:
>
> usb 1-1.3: device not accepting address 19, error -71
>
> The end result is a non-functioning device. After this patch, the sequence
> becomes like this:
>
> usb 1-1.3: new high-speed USB device number 18 using ci_hdrc
> usb 1-1.3: device not accepting address 18, error -71
> usb 1-1.3: new high-speed USB device number 19 using ci_hdrc
> usb 1-1.3: device not accepting address 19, error -71
> usb 1-1-port3: attempt power cycle
> usb 1-1.3: new high-speed USB device number 21 using ci_hdrc
> usb-storage 1-1.3:1.2: USB Mass Storage device detected
>
> Signed-off-by: Mike Looijmans <mike.looijmans@xxxxxxxx>
> ---
> v2: On feedback from Alan Stern:
> No blank line changes
> Don't further increase the retry count
> Delay 2x the power-up delay (100ms minimum) instead of magic "800"
>
> drivers/usb/core/hub.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
>
> diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
> index e9ce6bb..8f7d942 100644
> --- a/drivers/usb/core/hub.c
> +++ b/drivers/usb/core/hub.c
> @@ -4935,6 +4935,15 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
> usb_put_dev(udev);
> if ((status == -ENOTCONN) || (status == -ENOTSUPP))
> break;
> +
> + /* When halfway through our retry count, power-cycle the port */
> + if (i == (SET_CONFIG_TRIES / 2) - 1) {
> + dev_info(&port_dev->dev, "attempt power cycle\n");
> + usb_hub_set_port_power(hdev, hub, port1, false);
> + msleep(2 * hub_power_on_good_delay(hub));
> + usb_hub_set_port_power(hdev, hub, port1, true);
> + msleep(hub_power_on_good_delay(hub));
> + }
> }
> if (hub->hdev->parent ||
> !hcd->driver->port_handed_over ||

Acked-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>