diff options
| -rw-r--r-- | drivers/usb/core/driver.c | 63 | ||||
| -rw-r--r-- | drivers/usb/core/generic.c | 14 | ||||
| -rw-r--r-- | drivers/usb/core/hub.c | 96 | ||||
| -rw-r--r-- | drivers/usb/core/usb.h | 2 |
4 files changed, 70 insertions, 105 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index b0db1583c522..eefc98584eac 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
| @@ -783,7 +783,7 @@ static int resume_device(struct usb_device *udev) | |||
| 783 | return udriver->resume(udev); | 783 | return udriver->resume(udev); |
| 784 | } | 784 | } |
| 785 | 785 | ||
| 786 | /* Caller has locked intf */ | 786 | /* Caller has locked intf's usb_device */ |
| 787 | static int suspend_interface(struct usb_interface *intf, pm_message_t msg) | 787 | static int suspend_interface(struct usb_interface *intf, pm_message_t msg) |
| 788 | { | 788 | { |
| 789 | struct usb_driver *driver; | 789 | struct usb_driver *driver; |
| @@ -815,7 +815,7 @@ static int suspend_interface(struct usb_interface *intf, pm_message_t msg) | |||
| 815 | return status; | 815 | return status; |
| 816 | } | 816 | } |
| 817 | 817 | ||
| 818 | /* Caller has locked intf */ | 818 | /* Caller has locked intf's usb_device */ |
| 819 | static int resume_interface(struct usb_interface *intf) | 819 | static int resume_interface(struct usb_interface *intf) |
| 820 | { | 820 | { |
| 821 | struct usb_driver *driver; | 821 | struct usb_driver *driver; |
| @@ -856,14 +856,59 @@ static int resume_interface(struct usb_interface *intf) | |||
| 856 | return 0; | 856 | return 0; |
| 857 | } | 857 | } |
| 858 | 858 | ||
| 859 | /* Caller has locked udev */ | ||
| 860 | int usb_suspend_both(struct usb_device *udev, pm_message_t msg) | ||
| 861 | { | ||
| 862 | int status = 0; | ||
| 863 | int i = 0; | ||
| 864 | struct usb_interface *intf; | ||
| 865 | |||
| 866 | if (udev->actconfig) { | ||
| 867 | for (; i < udev->actconfig->desc.bNumInterfaces; i++) { | ||
| 868 | intf = udev->actconfig->interface[i]; | ||
| 869 | status = suspend_interface(intf, msg); | ||
| 870 | if (status != 0) | ||
| 871 | break; | ||
| 872 | } | ||
| 873 | } | ||
| 874 | if (status == 0) | ||
| 875 | status = suspend_device(udev, msg); | ||
| 876 | |||
| 877 | /* If the suspend failed, resume interfaces that did get suspended */ | ||
| 878 | if (status != 0) { | ||
| 879 | while (--i >= 0) { | ||
| 880 | intf = udev->actconfig->interface[i]; | ||
| 881 | resume_interface(intf); | ||
| 882 | } | ||
| 883 | } | ||
| 884 | return status; | ||
| 885 | } | ||
| 886 | |||
| 887 | /* Caller has locked udev */ | ||
| 888 | int usb_resume_both(struct usb_device *udev) | ||
| 889 | { | ||
| 890 | int status; | ||
| 891 | int i; | ||
| 892 | struct usb_interface *intf; | ||
| 893 | |||
| 894 | status = resume_device(udev); | ||
| 895 | if (status == 0 && udev->actconfig) { | ||
| 896 | for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { | ||
| 897 | intf = udev->actconfig->interface[i]; | ||
| 898 | resume_interface(intf); | ||
| 899 | } | ||
| 900 | } | ||
| 901 | return status; | ||
| 902 | } | ||
| 903 | |||
| 859 | static int usb_suspend(struct device *dev, pm_message_t message) | 904 | static int usb_suspend(struct device *dev, pm_message_t message) |
| 860 | { | 905 | { |
| 861 | int status; | 906 | int status; |
| 862 | 907 | ||
| 863 | if (is_usb_device(dev)) | 908 | if (is_usb_device(dev)) |
| 864 | status = suspend_device(to_usb_device(dev), message); | 909 | status = usb_suspend_both(to_usb_device(dev), message); |
| 865 | else | 910 | else |
| 866 | status = suspend_interface(to_usb_interface(dev), message); | 911 | status = 0; |
| 867 | return status; | 912 | return status; |
| 868 | } | 913 | } |
| 869 | 914 | ||
| @@ -871,10 +916,12 @@ static int usb_resume(struct device *dev) | |||
| 871 | { | 916 | { |
| 872 | int status; | 917 | int status; |
| 873 | 918 | ||
| 874 | if (is_usb_device(dev)) | 919 | if (is_usb_device(dev)) { |
| 875 | status = resume_device(to_usb_device(dev)); | 920 | status = usb_resume_both(to_usb_device(dev)); |
| 876 | else | 921 | |
| 877 | status = resume_interface(to_usb_interface(dev)); | 922 | /* Rebind drivers that had no suspend method? */ |
| 923 | } else | ||
| 924 | status = 0; | ||
| 878 | return status; | 925 | return status; |
| 879 | } | 926 | } |
| 880 | 927 | ||
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 1522195de715..b6dacd7551d2 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c | |||
| @@ -184,22 +184,8 @@ static void generic_disconnect(struct usb_device *udev) | |||
| 184 | 184 | ||
| 185 | #ifdef CONFIG_PM | 185 | #ifdef CONFIG_PM |
| 186 | 186 | ||
| 187 | static int verify_suspended(struct device *dev, void *unused) | ||
| 188 | { | ||
| 189 | if (dev->driver == NULL) | ||
| 190 | return 0; | ||
| 191 | return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0; | ||
| 192 | } | ||
| 193 | |||
| 194 | static int generic_suspend(struct usb_device *udev, pm_message_t msg) | 187 | static int generic_suspend(struct usb_device *udev, pm_message_t msg) |
| 195 | { | 188 | { |
| 196 | int status; | ||
| 197 | |||
| 198 | /* rule out bogus requests through sysfs */ | ||
| 199 | status = device_for_each_child(&udev->dev, NULL, verify_suspended); | ||
| 200 | if (status) | ||
| 201 | return status; | ||
| 202 | |||
| 203 | /* USB devices enter SUSPEND state through their hubs, but can be | 189 | /* USB devices enter SUSPEND state through their hubs, but can be |
| 204 | * marked for FREEZE as soon as their children are already idled. | 190 | * marked for FREEZE as soon as their children are already idled. |
| 205 | * But those semantics are useless, so we equate the two (sigh). | 191 | * But those semantics are useless, so we equate the two (sigh). |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index a372332440b2..a39112041e69 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
| @@ -1662,9 +1662,6 @@ static int finish_port_resume(struct usb_device *udev) | |||
| 1662 | "gone after usb resume? status %d\n", | 1662 | "gone after usb resume? status %d\n", |
| 1663 | status); | 1663 | status); |
| 1664 | else if (udev->actconfig) { | 1664 | else if (udev->actconfig) { |
| 1665 | unsigned i; | ||
| 1666 | int (*resume)(struct device *); | ||
| 1667 | |||
| 1668 | le16_to_cpus(&devstatus); | 1665 | le16_to_cpus(&devstatus); |
| 1669 | if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) | 1666 | if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) |
| 1670 | && udev->parent) { | 1667 | && udev->parent) { |
| @@ -1675,24 +1672,9 @@ static int finish_port_resume(struct usb_device *udev) | |||
| 1675 | USB_DEVICE_REMOTE_WAKEUP, 0, | 1672 | USB_DEVICE_REMOTE_WAKEUP, 0, |
| 1676 | NULL, 0, | 1673 | NULL, 0, |
| 1677 | USB_CTRL_SET_TIMEOUT); | 1674 | USB_CTRL_SET_TIMEOUT); |
| 1678 | if (status) { | 1675 | if (status) |
| 1679 | dev_dbg(&udev->dev, "disable remote " | 1676 | dev_dbg(&udev->dev, "disable remote " |
| 1680 | "wakeup, status %d\n", status); | 1677 | "wakeup, status %d\n", status); |
| 1681 | status = 0; | ||
| 1682 | } | ||
| 1683 | } | ||
| 1684 | |||
| 1685 | /* resume interface drivers; if this is a hub, it | ||
| 1686 | * may have a child resume event to deal with soon | ||
| 1687 | */ | ||
| 1688 | resume = udev->dev.bus->resume; | ||
| 1689 | for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { | ||
| 1690 | struct device *dev = | ||
| 1691 | &udev->actconfig->interface[i]->dev; | ||
| 1692 | |||
| 1693 | down(&dev->sem); | ||
| 1694 | (void) resume(dev); | ||
| 1695 | up(&dev->sem); | ||
| 1696 | } | 1678 | } |
| 1697 | status = 0; | 1679 | status = 0; |
| 1698 | 1680 | ||
| @@ -1802,15 +1784,7 @@ int usb_port_resume(struct usb_device *udev) | |||
| 1802 | } else | 1784 | } else |
| 1803 | status = finish_port_resume(udev); | 1785 | status = finish_port_resume(udev); |
| 1804 | if (status < 0) | 1786 | if (status < 0) |
| 1805 | dev_dbg(&udev->dev, "can't resume, status %d\n", | 1787 | dev_dbg(&udev->dev, "can't resume, status %d\n", status); |
| 1806 | status); | ||
| 1807 | |||
| 1808 | /* rebind drivers that had no suspend() */ | ||
| 1809 | if (status == 0) { | ||
| 1810 | usb_unlock_device(udev); | ||
| 1811 | bus_rescan_devices(&usb_bus_type); | ||
| 1812 | usb_lock_device(udev); | ||
| 1813 | } | ||
| 1814 | return status; | 1788 | return status; |
| 1815 | } | 1789 | } |
| 1816 | 1790 | ||
| @@ -1830,6 +1804,9 @@ static int remote_wakeup(struct usb_device *udev) | |||
| 1830 | msleep(10); | 1804 | msleep(10); |
| 1831 | status = finish_port_resume(udev); | 1805 | status = finish_port_resume(udev); |
| 1832 | } | 1806 | } |
| 1807 | |||
| 1808 | if (status == 0) | ||
| 1809 | usb_resume_both(udev); | ||
| 1833 | usb_unlock_device(udev); | 1810 | usb_unlock_device(udev); |
| 1834 | #endif | 1811 | #endif |
| 1835 | return status; | 1812 | return status; |
| @@ -1901,51 +1878,8 @@ static int hub_resume(struct usb_interface *intf) | |||
| 1901 | } | 1878 | } |
| 1902 | } | 1879 | } |
| 1903 | 1880 | ||
| 1881 | /* tell khubd to look for changes on this hub */ | ||
| 1904 | hub_activate(hub); | 1882 | hub_activate(hub); |
| 1905 | |||
| 1906 | /* REVISIT: this recursion probably shouldn't exist. Remove | ||
| 1907 | * this code sometime, after retesting with different root and | ||
| 1908 | * external hubs. | ||
| 1909 | */ | ||
| 1910 | #ifdef CONFIG_USB_SUSPEND | ||
| 1911 | { | ||
| 1912 | unsigned port1; | ||
| 1913 | |||
| 1914 | for (port1 = 1; port1 <= hdev->maxchild; port1++) { | ||
| 1915 | struct usb_device *udev; | ||
| 1916 | u16 portstat, portchange; | ||
| 1917 | |||
| 1918 | udev = hdev->children [port1-1]; | ||
| 1919 | status = hub_port_status(hub, port1, &portstat, &portchange); | ||
| 1920 | if (status == 0) { | ||
| 1921 | if (portchange & USB_PORT_STAT_C_SUSPEND) { | ||
| 1922 | clear_port_feature(hdev, port1, | ||
| 1923 | USB_PORT_FEAT_C_SUSPEND); | ||
| 1924 | portchange &= ~USB_PORT_STAT_C_SUSPEND; | ||
| 1925 | } | ||
| 1926 | |||
| 1927 | /* let khubd handle disconnects etc */ | ||
| 1928 | if (portchange) | ||
| 1929 | continue; | ||
| 1930 | } | ||
| 1931 | |||
| 1932 | if (!udev || status < 0) | ||
| 1933 | continue; | ||
| 1934 | usb_lock_device(udev); | ||
| 1935 | if (portstat & USB_PORT_STAT_SUSPEND) | ||
| 1936 | status = hub_port_resume(hub, port1, udev); | ||
| 1937 | else { | ||
| 1938 | status = finish_port_resume(udev); | ||
| 1939 | if (status < 0) { | ||
| 1940 | dev_dbg(&intf->dev, "resume port %d --> %d\n", | ||
| 1941 | port1, status); | ||
| 1942 | hub_port_logical_disconnect(hub, port1); | ||
| 1943 | } | ||
| 1944 | } | ||
| 1945 | usb_unlock_device(udev); | ||
| 1946 | } | ||
| 1947 | } | ||
| 1948 | #endif | ||
| 1949 | return 0; | 1883 | return 0; |
| 1950 | } | 1884 | } |
| 1951 | 1885 | ||
| @@ -2602,17 +2536,6 @@ static void hub_events(void) | |||
| 2602 | usb_get_intf(intf); | 2536 | usb_get_intf(intf); |
| 2603 | spin_unlock_irq(&hub_event_lock); | 2537 | spin_unlock_irq(&hub_event_lock); |
| 2604 | 2538 | ||
| 2605 | /* Is this is a root hub wanting to reactivate the downstream | ||
| 2606 | * ports? If so, be sure the interface resumes even if its | ||
| 2607 | * stub "device" node was never suspended. | ||
| 2608 | */ | ||
| 2609 | if (i) { | ||
| 2610 | dpm_runtime_resume(&hdev->dev); | ||
| 2611 | dpm_runtime_resume(&intf->dev); | ||
| 2612 | usb_put_intf(intf); | ||
| 2613 | continue; | ||
| 2614 | } | ||
| 2615 | |||
| 2616 | /* Lock the device, then check to see if we were | 2539 | /* Lock the device, then check to see if we were |
| 2617 | * disconnected while waiting for the lock to succeed. */ | 2540 | * disconnected while waiting for the lock to succeed. */ |
| 2618 | if (locktree(hdev) < 0) { | 2541 | if (locktree(hdev) < 0) { |
| @@ -2629,6 +2552,13 @@ static void hub_events(void) | |||
| 2629 | goto loop; | 2552 | goto loop; |
| 2630 | } | 2553 | } |
| 2631 | 2554 | ||
| 2555 | /* Is this is a root hub wanting to reactivate the downstream | ||
| 2556 | * ports? If so, be sure the interface resumes even if its | ||
| 2557 | * stub "device" node was never suspended. | ||
| 2558 | */ | ||
| 2559 | if (i) | ||
| 2560 | usb_resume_both(hdev); | ||
| 2561 | |||
| 2632 | /* If this is an inactive or suspended hub, do nothing */ | 2562 | /* If this is an inactive or suspended hub, do nothing */ |
| 2633 | if (hub->quiescing) | 2563 | if (hub->quiescing) |
| 2634 | goto loop; | 2564 | goto loop; |
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 1d25ccac7832..cc42972b6bb0 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h | |||
| @@ -30,6 +30,8 @@ extern void usb_major_cleanup(void); | |||
| 30 | extern int usb_host_init(void); | 30 | extern int usb_host_init(void); |
| 31 | extern void usb_host_cleanup(void); | 31 | extern void usb_host_cleanup(void); |
| 32 | 32 | ||
| 33 | extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg); | ||
| 34 | extern int usb_resume_both(struct usb_device *udev); | ||
| 33 | extern int usb_port_suspend(struct usb_device *dev); | 35 | extern int usb_port_suspend(struct usb_device *dev); |
| 34 | extern int usb_port_resume(struct usb_device *dev); | 36 | extern int usb_port_resume(struct usb_device *dev); |
| 35 | 37 | ||
