diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2006-07-01 22:11:02 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-09-27 14:58:50 -0400 |
commit | a8e7c5653562f88c0f5f53eac0a890c012655789 (patch) | |
tree | ba227138d408fdc73ccfc62ad8f75d912ece4baf | |
parent | 1cc8a25d5b680ff656927ffa9b66fae6b415b1d3 (diff) |
usbcore: resume device resume recursion
This patch (as717b) removes the existing recursion in hub resume code:
Resuming a hub will no longer automatically resume the devices attached
to the hub.
At the same time, it adds one level of recursion: Suspending a USB
device will automatically suspend all the device's interfaces. Failure
at an intermediate stage will cause all the already-suspended interfaces
to be resumed. Attempts to suspend or resume an interface by itself will
do nothing, although they won't return an error. Thus the regular
system-suspend and system-resume procedures should continue to work as
before; only runtime PM will be affected.
The patch also removes the code that tests state of the interfaces
before suspending a device. It's no longer needed, since everything
gets suspended together.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-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 | ||