On Tue, 20 May 2014, Dan Williams wrote: > Resuming a powered down port sometimes results in the port state being > stuck in the training sequence. > > hub 3-0:1.0: debounce: port 1: total 2000ms stable 0ms status 0x2e0 > port1: can't get reconnection after setting port power on, status -110 > hub 3-0:1.0: port 1 status 0000.02e0 after resume, -19 > usb 3-1: can't resume, status -19 > hub 3-0:1.0: logical disconnect on port 1 > > In the case above we wait for the port re-connect timeout of 2 seconds > and observe that the port status is USB_SS_PORT_LS_POLLING (although it > is likely toggling between this state and USB_SS_PORT_LS_RX_DETECT). > This is indicative of a case where the device is failing to progress the > link training state machine. > > It is resolved by issuing a warm reset to get the hub and device link > state machines back in sync. > > hub 3-0:1.0: debounce: port 1: total 2000ms stable 0ms status 0x2e0 > usb usb3: port1 usb_port_runtime_resume requires warm reset > hub 3-0:1.0: port 1 not warm reset yet, waiting 50ms > usb 3-1: reset SuperSpeed USB device number 2 using xhci_hcd > > After a reconnect timeout when we expect the device to be present, force > a warm reset of the device. Note that we can not simply look at the > link status to determine if a warm reset is required as any of the > training states USB_SS_PORT_LS_POLLING, USB_SS_PORT_LS_RX_DETECT, or > USB_SS_PORT_LS_COMP_MOD are valid states that do not indicate the need > for warm reset by themselves. > --- a/drivers/usb/core/hub.c > +++ b/drivers/usb/core/hub.c > @@ -2570,10 +2570,12 @@ static int hub_port_reset(struct usb_hub *hub, int port1, > /* Is a USB 3.0 port in the Inactive or Compliance Mode state? > * Port worm reset is required to recover > */ > -static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus) > +static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1, > + u16 portstatus) > { > return hub_is_superspeed(hub->hdev) && > - (((portstatus & USB_PORT_STAT_LINK_STATE) == > + (test_bit(port1, hub->warm_reset_bits) || > + ((portstatus & USB_PORT_STAT_LINK_STATE) == > USB_SS_PORT_LS_SS_INACTIVE) || > ((portstatus & USB_PORT_STAT_LINK_STATE) == > USB_SS_PORT_LS_COMP_MOD)) ; This might be a little more clear if you separate out the tests and remove the excess parens: unsigned ls; if (!hub_is_superspeed(hub->hdev)) return 0; if (test_bit(port1, hub->warm_reset_bits)) return 1; ls = portstatus & USB_PORT_STAT_LINK_STATE; return ls == USB_SS_PORT_LS_SS_INACTIVE || ls == USB_SS_PORT_LS_COMP_MOD; > @@ -2839,8 +2843,13 @@ static int check_port_resume_type(struct usb_device *udev, > { > struct usb_port *port_dev = hub->ports[port1 - 1]; > > + /* Is a warm reset needed to recover the connection? */ > + if (udev->reset_resume > + && hub_port_warm_reset_required(hub, port1, portstatus)) { > + /* pass */; You mustn't call hub_port_warm_reset_required when status < 0, because then the portstatus value is undefined. Have you tested this after turning off the device's persist_enabled flag? Without a reset-resume, this will go through an awkward sequence involving disabling the port. I think you still end up performing the warm reset, but it's worth checking. Alan Stern -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html