aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core/driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/driver.c')
-rw-r--r--drivers/usb/core/driver.c39
1 files changed, 24 insertions, 15 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index e8b447e06c54..12dd986bdffd 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -824,8 +824,9 @@ static int usb_resume_device(struct usb_device *udev)
struct usb_device_driver *udriver;
int status = 0;
- if (udev->state == USB_STATE_NOTATTACHED ||
- udev->state != USB_STATE_SUSPENDED)
+ if (udev->state == USB_STATE_NOTATTACHED)
+ goto done;
+ if (udev->state != USB_STATE_SUSPENDED && !udev->reset_resume)
goto done;
/* Can't resume it if it doesn't have a driver. */
@@ -882,7 +883,7 @@ done:
}
/* Caller has locked intf's usb_device's pm_mutex */
-static int usb_resume_interface(struct usb_interface *intf)
+static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
{
struct usb_driver *driver;
int status = 0;
@@ -902,21 +903,21 @@ static int usb_resume_interface(struct usb_interface *intf)
}
driver = to_usb_driver(intf->dev.driver);
- if (driver->resume) {
+ if (reset_resume && driver->post_reset)
+ driver->post_reset(intf, reset_resume);
+ else if (driver->resume) {
status = driver->resume(intf);
if (status)
dev_err(&intf->dev, "%s error %d\n",
"resume", status);
- else
- mark_active(intf);
- } else {
+ } else
dev_warn(&intf->dev, "no resume for driver %s?\n",
driver->name);
- mark_active(intf);
- }
done:
// dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
+ if (status == 0)
+ mark_active(intf);
return status;
}
@@ -1063,7 +1064,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
if (status != 0) {
while (--i >= 0) {
intf = udev->actconfig->interface[i];
- usb_resume_interface(intf);
+ usb_resume_interface(intf, 0);
}
/* Try another autosuspend when the interfaces aren't busy */
@@ -1162,20 +1163,21 @@ static int usb_resume_both(struct usb_device *udev)
}
} else {
- /* Needed only for setting udev->dev.power.power_state.event
- * and for possible debugging message. */
+ /* Needed for setting udev->dev.power.power_state.event,
+ * for possible debugging message, and for reset_resume. */
status = usb_resume_device(udev);
}
if (status == 0 && udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
- usb_resume_interface(intf);
+ usb_resume_interface(intf, udev->reset_resume);
}
}
done:
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+ udev->reset_resume = 0;
return status;
}
@@ -1510,8 +1512,15 @@ static int usb_resume(struct device *dev)
if (!is_usb_device(dev)) /* Ignore PM for interfaces */
return 0;
udev = to_usb_device(dev);
- if (udev->autoresume_disabled)
- return -EPERM;
+
+ /* If autoresume is disabled then we also want to prevent resume
+ * during system wakeup. However, a "persistent-device" reset-resume
+ * after power loss counts as a wakeup event. So allow a
+ * reset-resume to occur if remote wakeup is enabled. */
+ if (udev->autoresume_disabled) {
+ if (!(udev->reset_resume && udev->do_remote_wakeup))
+ return -EPERM;
+ }
return usb_external_resume_device(udev);
}