Commit 86c57edf authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: use reset_resume when normal resume fails

This patch (as1109b) makes USB-Persist more resilient to errors.  With
the current code, if a normal resume fails, it's an unrecoverable
error.  With the patch, if a normal resume fails (and if the device is
enabled for USB-Persist) then a reset-resume is tried.

This fixes the problem reported in Bugzilla #10977.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent ac90e365
...@@ -81,8 +81,11 @@ re-enumeration shows that the device now attached to that port has the ...@@ -81,8 +81,11 @@ re-enumeration shows that the device now attached to that port has the
same descriptors as before, including the Vendor and Product IDs, then same descriptors as before, including the Vendor and Product IDs, then
the kernel continues to use the same device structure. In effect, the the kernel continues to use the same device structure. In effect, the
kernel treats the device as though it had merely been reset instead of kernel treats the device as though it had merely been reset instead of
unplugged. The same thing happens if the host controller is in the unplugged.
expected state but a USB device was unplugged and then replugged.
The same thing happens if the host controller is in the expected state
but a USB device was unplugged and then replugged, or if a USB device
fails to carry out a normal resume.
If no device is now attached to the port, or if the descriptors are If no device is now attached to the port, or if the descriptors are
different from what the kernel remembers, then the treatment is what different from what the kernel remembers, then the treatment is what
......
...@@ -1822,9 +1822,15 @@ static int check_port_resume_type(struct usb_device *udev, ...@@ -1822,9 +1822,15 @@ static int check_port_resume_type(struct usb_device *udev,
status = -ENODEV; status = -ENODEV;
} }
/* Can't do a normal resume if the port isn't enabled */ /* Can't do a normal resume if the port isn't enabled,
else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume) * so try a reset-resume instead.
status = -ENODEV; */
else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume) {
if (udev->persist_enabled)
udev->reset_resume = 1;
else
status = -ENODEV;
}
if (status) { if (status) {
dev_dbg(hub->intfdev, dev_dbg(hub->intfdev,
...@@ -1973,6 +1979,7 @@ static int finish_port_resume(struct usb_device *udev) ...@@ -1973,6 +1979,7 @@ static int finish_port_resume(struct usb_device *udev)
* resumed. * resumed.
*/ */
if (udev->reset_resume) if (udev->reset_resume)
retry_reset_resume:
status = usb_reset_and_verify_device(udev); status = usb_reset_and_verify_device(udev);
/* 10.5.4.5 says be sure devices in the tree are still there. /* 10.5.4.5 says be sure devices in the tree are still there.
...@@ -1984,6 +1991,13 @@ static int finish_port_resume(struct usb_device *udev) ...@@ -1984,6 +1991,13 @@ static int finish_port_resume(struct usb_device *udev)
status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
if (status >= 0) if (status >= 0)
status = (status > 0 ? 0 : -ENODEV); status = (status > 0 ? 0 : -ENODEV);
/* If a normal resume failed, try doing a reset-resume */
if (status && !udev->reset_resume && udev->persist_enabled) {
dev_dbg(&udev->dev, "retry with reset-resume\n");
udev->reset_resume = 1;
goto retry_reset_resume;
}
} }
if (status) { if (status) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment