From: Alan Stern Date: Mon, 30 Jun 2008 15:14:43 +0000 (-0400) Subject: USB: use reset_resume when normal resume fails X-Git-Tag: v2.6.27-rc1~946^2~1 X-Git-Url: http://pilppa.com/gitweb/?a=commitdiff_plain;h=86c57edf60f5c98adb496880f56cd0e5a3423153;p=linux-2.6-omap-h63xx.git 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 Signed-off-by: Greg Kroah-Hartman --- diff --git a/Documentation/usb/persist.txt b/Documentation/usb/persist.txt index d56cb1a1155..074b159b77c 100644 --- a/Documentation/usb/persist.txt +++ b/Documentation/usb/persist.txt @@ -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 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 -unplugged. The same thing happens if the host controller is in the -expected state but a USB device was unplugged and then replugged. +unplugged. + +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 different from what the kernel remembers, then the treatment is what diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index f1efabbc1ca..107e1d25dde 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1822,9 +1822,15 @@ static int check_port_resume_type(struct usb_device *udev, 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 @@ static int finish_port_resume(struct usb_device *udev) * 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 @@ static int finish_port_resume(struct usb_device *udev) 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) {