aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/core/driver.c27
-rw-r--r--include/linux/usb.h1
2 files changed, 21 insertions, 7 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 3f734240e0ec..8c1eac27f2de 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1540,9 +1540,21 @@ int usb_external_resume_device(struct usb_device *udev)
1540 1540
1541static int usb_suspend(struct device *dev, pm_message_t message) 1541static int usb_suspend(struct device *dev, pm_message_t message)
1542{ 1542{
1543 struct usb_device *udev;
1544
1543 if (!is_usb_device(dev)) /* Ignore PM for interfaces */ 1545 if (!is_usb_device(dev)) /* Ignore PM for interfaces */
1544 return 0; 1546 return 0;
1545 return usb_external_suspend_device(to_usb_device(dev), message); 1547 udev = to_usb_device(dev);
1548
1549 /* If udev is already suspended, we can skip this suspend and
1550 * we should also skip the upcoming system resume. */
1551 if (udev->state == USB_STATE_SUSPENDED) {
1552 udev->skip_sys_resume = 1;
1553 return 0;
1554 }
1555
1556 udev->skip_sys_resume = 0;
1557 return usb_external_suspend_device(udev, message);
1546} 1558}
1547 1559
1548static int usb_resume(struct device *dev) 1560static int usb_resume(struct device *dev)
@@ -1553,13 +1565,14 @@ static int usb_resume(struct device *dev)
1553 return 0; 1565 return 0;
1554 udev = to_usb_device(dev); 1566 udev = to_usb_device(dev);
1555 1567
1556 /* If autoresume is disabled then we also want to prevent resume 1568 /* If udev->skip_sys_resume is set then udev was already suspended
1557 * during system wakeup. However, a "persistent-device" reset-resume 1569 * when the system suspend started, so we don't want to resume
1558 * after power loss counts as a wakeup event. So allow a 1570 * udev during this system wakeup. However a reset-resume counts
1559 * reset-resume to occur if remote wakeup is enabled. */ 1571 * as a wakeup event, so allow a reset-resume to occur if remote
1560 if (udev->autoresume_disabled) { 1572 * wakeup is enabled. */
1573 if (udev->skip_sys_resume) {
1561 if (!(udev->reset_resume && udev->do_remote_wakeup)) 1574 if (!(udev->reset_resume && udev->do_remote_wakeup))
1562 return -EPERM; 1575 return -EHOSTUNREACH;
1563 } 1576 }
1564 return usb_external_resume_device(udev); 1577 return usb_external_resume_device(udev);
1565} 1578}
diff --git a/include/linux/usb.h b/include/linux/usb.h
index c10935fdc03a..c5c8f169d3cf 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -430,6 +430,7 @@ struct usb_device {
430 unsigned persist_enabled:1; /* USB_PERSIST enabled for this dev */ 430 unsigned persist_enabled:1; /* USB_PERSIST enabled for this dev */
431 unsigned autosuspend_disabled:1; /* autosuspend and autoresume */ 431 unsigned autosuspend_disabled:1; /* autosuspend and autoresume */
432 unsigned autoresume_disabled:1; /* disabled by the user */ 432 unsigned autoresume_disabled:1; /* disabled by the user */
433 unsigned skip_sys_resume:1; /* skip the next system resume */
433#endif 434#endif
434}; 435};
435#define to_usb_device(d) container_of(d, struct usb_device, dev) 436#define to_usb_device(d) container_of(d, struct usb_device, dev)