diff options
-rw-r--r-- | drivers/usb/core/driver.c | 27 | ||||
-rw-r--r-- | include/linux/usb.h | 1 |
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 | ||
1541 | static int usb_suspend(struct device *dev, pm_message_t message) | 1541 | static 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 | ||
1548 | static int usb_resume(struct device *dev) | 1560 | static 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) |