aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2006-07-01 22:11:02 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-09-27 14:58:50 -0400
commita8e7c5653562f88c0f5f53eac0a890c012655789 (patch)
treeba227138d408fdc73ccfc62ad8f75d912ece4baf
parent1cc8a25d5b680ff656927ffa9b66fae6b415b1d3 (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.c63
-rw-r--r--drivers/usb/core/generic.c14
-rw-r--r--drivers/usb/core/hub.c96
-rw-r--r--drivers/usb/core/usb.h2
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 */
787static int suspend_interface(struct usb_interface *intf, pm_message_t msg) 787static 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 */
819static int resume_interface(struct usb_interface *intf) 819static 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 */
860int 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 */
888int 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
859static int usb_suspend(struct device *dev, pm_message_t message) 904static 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
187static 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
194static int generic_suspend(struct usb_device *udev, pm_message_t msg) 187static 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);
30extern int usb_host_init(void); 30extern int usb_host_init(void);
31extern void usb_host_cleanup(void); 31extern void usb_host_cleanup(void);
32 32
33extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg);
34extern int usb_resume_both(struct usb_device *udev);
33extern int usb_port_suspend(struct usb_device *dev); 35extern int usb_port_suspend(struct usb_device *dev);
34extern int usb_port_resume(struct usb_device *dev); 36extern int usb_port_resume(struct usb_device *dev);
35 37