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: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>


diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index f1efabb..107e1d2 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1822,9 +1822,15 @@
 			status = -ENODEV;
 	}
 
-	/* Can't do a normal resume if the port isn't enabled */
-	else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume)
-		status = -ENODEV;
+	/* Can't do a normal resume if the port isn't enabled,
+	 * so try a reset-resume instead.
+	 */
+	else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume) {
+		if (udev->persist_enabled)
+			udev->reset_resume = 1;
+		else
+			status = -ENODEV;
+	}
 
 	if (status) {
 		dev_dbg(hub->intfdev,
@@ -1973,6 +1979,7 @@
 	 * resumed.
 	 */
 	if (udev->reset_resume)
+ retry_reset_resume:
 		status = usb_reset_and_verify_device(udev);
 
  	/* 10.5.4.5 says be sure devices in the tree are still there.
@@ -1984,6 +1991,13 @@
 		status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
 		if (status >= 0)
 			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) {