diff options
author | David Brownell <david-b@pacbell.net> | 2005-09-23 01:37:29 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-10-28 19:47:40 -0400 |
commit | f3f3253d644d36a2ea5464005a9c2cf28804d200 (patch) | |
tree | 913d5d38de9acea02b95c43ea23dc6a28f791236 | |
parent | 979d5199fee9e80290ddeb532e5993bd15506712 (diff) |
[PATCH] root hub updates (greater half)
This patch associates hub suspend and resume logic (including for root hubs)
with CONFIG_PM -- instead of CONFIG_USB_SUSPEND as before -- thereby unifying
two troublesome versions of suspend logic into just one. It'll be easier to
keep things right from now on.
- Now usbcore _always_ calls hcd->hub_suspend as needed, instead of
only when USB_SUSPEND is enabled:
* Those root hub methods are now called from hub suspend/resume;
no more skipping between layers during device suspend/resume;
* It now handles cases allowed by sysfs or autosuspended root hubs,
by forcing the hub interface to resume too.
- All devices, including virtual root hubs, now get the same treatment
on their resume paths ... including re-activating all their interfaces.
Plus it gets rid of those stub copies of usb_{suspend,resume}_device(), and
updates the Kconfig to match the new definition of USB_SUSPEND: it provides
(a) selective suspend, downstream from hubs; and (b) remote wakeup, upstream
from any device configuration which supports it.
This calls for minor followup patches for most HCDs (and their PCI glue).
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/Kconfig | 11 ++-
drivers/usb/core/hub.c | 163 +++++++++++++++++++++++++----------------------
2 files changed, 97 insertions(+), 77 deletions(-)
-rw-r--r-- | drivers/usb/core/Kconfig | 11 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 163 |
2 files changed, 97 insertions, 77 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 1a9ff6184943..ff03184da403 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig | |||
@@ -61,14 +61,17 @@ config USB_DYNAMIC_MINORS | |||
61 | If you are unsure about this, say N here. | 61 | If you are unsure about this, say N here. |
62 | 62 | ||
63 | config USB_SUSPEND | 63 | config USB_SUSPEND |
64 | bool "USB suspend/resume (EXPERIMENTAL)" | 64 | bool "USB selective suspend/resume and wakeup (EXPERIMENTAL)" |
65 | depends on USB && PM && EXPERIMENTAL | 65 | depends on USB && PM && EXPERIMENTAL |
66 | help | 66 | help |
67 | If you say Y here, you can use driver calls or the sysfs | 67 | If you say Y here, you can use driver calls or the sysfs |
68 | "power/state" file to suspend or resume individual USB | 68 | "power/state" file to suspend or resume individual USB |
69 | peripherals. There are many related features, such as | 69 | peripherals. |
70 | remote wakeup and driver-specific suspend processing, that | 70 | |
71 | may not yet work as expected. | 71 | Also, USB "remote wakeup" signaling is supported, whereby some |
72 | USB devices (like keyboards and network adapters) can wake up | ||
73 | their parent hub. That wakeup cascades up the USB tree, and | ||
74 | could wake the system from states like suspend-to-RAM. | ||
72 | 75 | ||
73 | If you are unsure about this, say N here. | 76 | If you are unsure about this, say N here. |
74 | 77 | ||
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3c8d8d1f993c..6a601a4547e7 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -1612,7 +1612,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1, | |||
1612 | */ | 1612 | */ |
1613 | static int __usb_suspend_device (struct usb_device *udev, int port1) | 1613 | static int __usb_suspend_device (struct usb_device *udev, int port1) |
1614 | { | 1614 | { |
1615 | int status; | 1615 | int status = 0; |
1616 | 1616 | ||
1617 | /* caller owns the udev device lock */ | 1617 | /* caller owns the udev device lock */ |
1618 | if (port1 < 0) | 1618 | if (port1 < 0) |
@@ -1638,21 +1638,10 @@ static int __usb_suspend_device (struct usb_device *udev, int port1) | |||
1638 | } | 1638 | } |
1639 | } | 1639 | } |
1640 | 1640 | ||
1641 | /* "global suspend" of the HC-to-USB interface (root hub), or | 1641 | /* we only change a device's upstream USB link. |
1642 | * "selective suspend" of just one hub-device link. | 1642 | * root hubs have no upstream USB link. |
1643 | */ | 1643 | */ |
1644 | if (!udev->parent) { | 1644 | if (udev->parent) |
1645 | struct usb_bus *bus = udev->bus; | ||
1646 | if (bus && bus->op->hub_suspend) { | ||
1647 | status = bus->op->hub_suspend (bus); | ||
1648 | if (status == 0) { | ||
1649 | dev_dbg(&udev->dev, "usb suspend\n"); | ||
1650 | usb_set_device_state(udev, | ||
1651 | USB_STATE_SUSPENDED); | ||
1652 | } | ||
1653 | } else | ||
1654 | status = -EOPNOTSUPP; | ||
1655 | } else | ||
1656 | status = hub_port_suspend(hdev_to_hub(udev->parent), port1, | 1645 | status = hub_port_suspend(hdev_to_hub(udev->parent), port1, |
1657 | udev); | 1646 | udev); |
1658 | 1647 | ||
@@ -1661,6 +1650,8 @@ static int __usb_suspend_device (struct usb_device *udev, int port1) | |||
1661 | return status; | 1650 | return status; |
1662 | } | 1651 | } |
1663 | 1652 | ||
1653 | #endif | ||
1654 | |||
1664 | /** | 1655 | /** |
1665 | * usb_suspend_device - suspend a usb device | 1656 | * usb_suspend_device - suspend a usb device |
1666 | * @udev: device that's no longer in active use | 1657 | * @udev: device that's no longer in active use |
@@ -1683,6 +1674,7 @@ static int __usb_suspend_device (struct usb_device *udev, int port1) | |||
1683 | */ | 1674 | */ |
1684 | int usb_suspend_device(struct usb_device *udev) | 1675 | int usb_suspend_device(struct usb_device *udev) |
1685 | { | 1676 | { |
1677 | #ifdef CONFIG_USB_SUSPEND | ||
1686 | int port1, status; | 1678 | int port1, status; |
1687 | 1679 | ||
1688 | port1 = locktree(udev); | 1680 | port1 = locktree(udev); |
@@ -1692,8 +1684,14 @@ int usb_suspend_device(struct usb_device *udev) | |||
1692 | status = __usb_suspend_device(udev, port1); | 1684 | status = __usb_suspend_device(udev, port1); |
1693 | usb_unlock_device(udev); | 1685 | usb_unlock_device(udev); |
1694 | return status; | 1686 | return status; |
1687 | #else | ||
1688 | /* NOTE: udev->state unchanged, it's not lying ... */ | ||
1689 | udev->dev.power.power_state = PMSG_SUSPEND; | ||
1690 | return 0; | ||
1691 | #endif | ||
1695 | } | 1692 | } |
1696 | 1693 | ||
1694 | |||
1697 | /* | 1695 | /* |
1698 | * If the USB "suspend" state is in use (rather than "global suspend"), | 1696 | * If the USB "suspend" state is in use (rather than "global suspend"), |
1699 | * many devices will be individually taken out of suspend state using | 1697 | * many devices will be individually taken out of suspend state using |
@@ -1702,13 +1700,13 @@ int usb_suspend_device(struct usb_device *udev) | |||
1702 | * resume (by host) or remote wakeup (by device) ... now see what changed | 1700 | * resume (by host) or remote wakeup (by device) ... now see what changed |
1703 | * in the tree that's rooted at this device. | 1701 | * in the tree that's rooted at this device. |
1704 | */ | 1702 | */ |
1705 | static int finish_port_resume(struct usb_device *udev) | 1703 | static int finish_device_resume(struct usb_device *udev) |
1706 | { | 1704 | { |
1707 | int status; | 1705 | int status; |
1708 | u16 devstatus; | 1706 | u16 devstatus; |
1709 | 1707 | ||
1710 | /* caller owns the udev device lock */ | 1708 | /* caller owns the udev device lock */ |
1711 | dev_dbg(&udev->dev, "usb resume\n"); | 1709 | dev_dbg(&udev->dev, "finish resume\n"); |
1712 | 1710 | ||
1713 | /* usb ch9 identifies four variants of SUSPENDED, based on what | 1711 | /* usb ch9 identifies four variants of SUSPENDED, based on what |
1714 | * state the device resumes to. Linux currently won't see the | 1712 | * state the device resumes to. Linux currently won't see the |
@@ -1718,7 +1716,6 @@ static int finish_port_resume(struct usb_device *udev) | |||
1718 | usb_set_device_state(udev, udev->actconfig | 1716 | usb_set_device_state(udev, udev->actconfig |
1719 | ? USB_STATE_CONFIGURED | 1717 | ? USB_STATE_CONFIGURED |
1720 | : USB_STATE_ADDRESS); | 1718 | : USB_STATE_ADDRESS); |
1721 | udev->dev.power.power_state = PMSG_ON; | ||
1722 | 1719 | ||
1723 | /* 10.5.4.5 says be sure devices in the tree are still there. | 1720 | /* 10.5.4.5 says be sure devices in the tree are still there. |
1724 | * For now let's assume the device didn't go crazy on resume, | 1721 | * For now let's assume the device didn't go crazy on resume, |
@@ -1734,7 +1731,8 @@ static int finish_port_resume(struct usb_device *udev) | |||
1734 | int (*resume)(struct device *); | 1731 | int (*resume)(struct device *); |
1735 | 1732 | ||
1736 | le16_to_cpus(&devstatus); | 1733 | le16_to_cpus(&devstatus); |
1737 | if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { | 1734 | if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP) |
1735 | && udev->parent) { | ||
1738 | status = usb_control_msg(udev, | 1736 | status = usb_control_msg(udev, |
1739 | usb_sndctrlpipe(udev, 0), | 1737 | usb_sndctrlpipe(udev, 0), |
1740 | USB_REQ_CLEAR_FEATURE, | 1738 | USB_REQ_CLEAR_FEATURE, |
@@ -1764,6 +1762,8 @@ static int finish_port_resume(struct usb_device *udev) | |||
1764 | return status; | 1762 | return status; |
1765 | } | 1763 | } |
1766 | 1764 | ||
1765 | #ifdef CONFIG_USB_SUSPEND | ||
1766 | |||
1767 | static int | 1767 | static int |
1768 | hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) | 1768 | hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) |
1769 | { | 1769 | { |
@@ -1809,7 +1809,7 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) | |||
1809 | /* TRSMRCY = 10 msec */ | 1809 | /* TRSMRCY = 10 msec */ |
1810 | msleep(10); | 1810 | msleep(10); |
1811 | if (udev) | 1811 | if (udev) |
1812 | status = finish_port_resume(udev); | 1812 | status = finish_device_resume(udev); |
1813 | } | 1813 | } |
1814 | } | 1814 | } |
1815 | if (status < 0) | 1815 | if (status < 0) |
@@ -1818,7 +1818,7 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) | |||
1818 | return status; | 1818 | return status; |
1819 | } | 1819 | } |
1820 | 1820 | ||
1821 | static int hub_resume (struct usb_interface *intf); | 1821 | #endif |
1822 | 1822 | ||
1823 | /** | 1823 | /** |
1824 | * usb_resume_device - re-activate a suspended usb device | 1824 | * usb_resume_device - re-activate a suspended usb device |
@@ -1841,35 +1841,22 @@ int usb_resume_device(struct usb_device *udev) | |||
1841 | if (port1 < 0) | 1841 | if (port1 < 0) |
1842 | return port1; | 1842 | return port1; |
1843 | 1843 | ||
1844 | /* "global resume" of the HC-to-USB interface (root hub), or | 1844 | #ifdef CONFIG_USB_SUSPEND |
1845 | * selective resume of one hub-to-device port | 1845 | /* selective resume of one downstream hub-to-device port */ |
1846 | */ | 1846 | if (udev->parent) { |
1847 | if (!udev->parent) { | 1847 | if (udev->state == USB_STATE_SUSPENDED) { |
1848 | struct usb_bus *bus = udev->bus; | 1848 | // NOTE swsusp may bork us, device state being wrong... |
1849 | if (bus && bus->op->hub_resume) { | 1849 | // NOTE this fails if parent is also suspended... |
1850 | status = bus->op->hub_resume (bus); | 1850 | status = hub_port_resume(hdev_to_hub(udev->parent), |
1851 | port1, udev); | ||
1851 | } else | 1852 | } else |
1852 | status = -EOPNOTSUPP; | 1853 | status = 0; |
1853 | if (status == 0) { | 1854 | } else |
1854 | dev_dbg(&udev->dev, "usb resume\n"); | 1855 | #endif |
1855 | /* TRSMRCY = 10 msec */ | 1856 | status = finish_device_resume(udev); |
1856 | msleep(10); | 1857 | if (status < 0) |
1857 | usb_set_device_state (udev, USB_STATE_CONFIGURED); | ||
1858 | udev->dev.power.power_state = PMSG_ON; | ||
1859 | status = hub_resume (udev | ||
1860 | ->actconfig->interface[0]); | ||
1861 | } | ||
1862 | } else if (udev->state == USB_STATE_SUSPENDED) { | ||
1863 | // NOTE this fails if parent is also suspended... | ||
1864 | status = hub_port_resume(hdev_to_hub(udev->parent), | ||
1865 | port1, udev); | ||
1866 | } else { | ||
1867 | status = 0; | ||
1868 | } | ||
1869 | if (status < 0) { | ||
1870 | dev_dbg(&udev->dev, "can't resume, status %d\n", | 1858 | dev_dbg(&udev->dev, "can't resume, status %d\n", |
1871 | status); | 1859 | status); |
1872 | } | ||
1873 | 1860 | ||
1874 | usb_unlock_device(udev); | 1861 | usb_unlock_device(udev); |
1875 | 1862 | ||
@@ -1886,6 +1873,8 @@ static int remote_wakeup(struct usb_device *udev) | |||
1886 | { | 1873 | { |
1887 | int status = 0; | 1874 | int status = 0; |
1888 | 1875 | ||
1876 | #ifdef CONFIG_USB_SUSPEND | ||
1877 | |||
1889 | /* don't repeat RESUME sequence if this device | 1878 | /* don't repeat RESUME sequence if this device |
1890 | * was already woken up by some other task | 1879 | * was already woken up by some other task |
1891 | */ | 1880 | */ |
@@ -1894,9 +1883,10 @@ static int remote_wakeup(struct usb_device *udev) | |||
1894 | dev_dbg(&udev->dev, "RESUME (wakeup)\n"); | 1883 | dev_dbg(&udev->dev, "RESUME (wakeup)\n"); |
1895 | /* TRSMRCY = 10 msec */ | 1884 | /* TRSMRCY = 10 msec */ |
1896 | msleep(10); | 1885 | msleep(10); |
1897 | status = finish_port_resume(udev); | 1886 | status = finish_device_resume(udev); |
1898 | } | 1887 | } |
1899 | up(&udev->serialize); | 1888 | up(&udev->serialize); |
1889 | #endif | ||
1900 | return status; | 1890 | return status; |
1901 | } | 1891 | } |
1902 | 1892 | ||
@@ -1911,12 +1901,32 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) | |||
1911 | struct usb_device *udev; | 1901 | struct usb_device *udev; |
1912 | 1902 | ||
1913 | udev = hdev->children [port1-1]; | 1903 | udev = hdev->children [port1-1]; |
1914 | if (udev && udev->state != USB_STATE_SUSPENDED) { | 1904 | if (udev && (udev->dev.power.power_state.event |
1905 | == PM_EVENT_ON | ||
1906 | #ifdef CONFIG_USB_SUSPEND | ||
1907 | || udev->state != USB_STATE_SUSPENDED | ||
1908 | #endif | ||
1909 | )) { | ||
1915 | dev_dbg(&intf->dev, "port %d nyet suspended\n", port1); | 1910 | dev_dbg(&intf->dev, "port %d nyet suspended\n", port1); |
1916 | return -EBUSY; | 1911 | return -EBUSY; |
1917 | } | 1912 | } |
1918 | } | 1913 | } |
1919 | 1914 | ||
1915 | /* "global suspend" of the downstream HC-to-USB interface */ | ||
1916 | if (!hdev->parent) { | ||
1917 | struct usb_bus *bus = hdev->bus; | ||
1918 | if (bus && bus->op->hub_suspend) { | ||
1919 | int status = bus->op->hub_suspend (bus); | ||
1920 | |||
1921 | if (status != 0) { | ||
1922 | dev_dbg(&hdev->dev, "'global' suspend %d\n", | ||
1923 | status); | ||
1924 | return status; | ||
1925 | } | ||
1926 | } else | ||
1927 | return -EOPNOTSUPP; | ||
1928 | } | ||
1929 | |||
1920 | /* stop khubd and related activity */ | 1930 | /* stop khubd and related activity */ |
1921 | hub_quiesce(hub); | 1931 | hub_quiesce(hub); |
1922 | return 0; | 1932 | return 0; |
@@ -1926,9 +1936,36 @@ static int hub_resume(struct usb_interface *intf) | |||
1926 | { | 1936 | { |
1927 | struct usb_device *hdev = interface_to_usbdev(intf); | 1937 | struct usb_device *hdev = interface_to_usbdev(intf); |
1928 | struct usb_hub *hub = usb_get_intfdata (intf); | 1938 | struct usb_hub *hub = usb_get_intfdata (intf); |
1929 | unsigned port1; | ||
1930 | int status; | 1939 | int status; |
1931 | 1940 | ||
1941 | /* "global resume" of the downstream HC-to-USB interface */ | ||
1942 | if (!hdev->parent) { | ||
1943 | struct usb_bus *bus = hdev->bus; | ||
1944 | if (bus && bus->op->hub_resume) { | ||
1945 | status = bus->op->hub_resume (bus); | ||
1946 | if (status) { | ||
1947 | dev_dbg(&intf->dev, "'global' resume %d\n", | ||
1948 | status); | ||
1949 | return status; | ||
1950 | } | ||
1951 | } else | ||
1952 | return -EOPNOTSUPP; | ||
1953 | if (status == 0) { | ||
1954 | /* TRSMRCY = 10 msec */ | ||
1955 | msleep(10); | ||
1956 | } | ||
1957 | } | ||
1958 | |||
1959 | hub_activate(hub); | ||
1960 | |||
1961 | /* REVISIT: this recursion probably shouldn't exist. Remove | ||
1962 | * this code sometime, after retesting with different root and | ||
1963 | * external hubs. | ||
1964 | */ | ||
1965 | #ifdef CONFIG_USB_SUSPEND | ||
1966 | { | ||
1967 | unsigned port1; | ||
1968 | |||
1932 | for (port1 = 1; port1 <= hdev->maxchild; port1++) { | 1969 | for (port1 = 1; port1 <= hdev->maxchild; port1++) { |
1933 | struct usb_device *udev; | 1970 | struct usb_device *udev; |
1934 | u16 portstat, portchange; | 1971 | u16 portstat, portchange; |
@@ -1953,7 +1990,7 @@ static int hub_resume(struct usb_interface *intf) | |||
1953 | if (portstat & USB_PORT_STAT_SUSPEND) | 1990 | if (portstat & USB_PORT_STAT_SUSPEND) |
1954 | status = hub_port_resume(hub, port1, udev); | 1991 | status = hub_port_resume(hub, port1, udev); |
1955 | else { | 1992 | else { |
1956 | status = finish_port_resume(udev); | 1993 | status = finish_device_resume(udev); |
1957 | if (status < 0) { | 1994 | if (status < 0) { |
1958 | dev_dbg(&intf->dev, "resume port %d --> %d\n", | 1995 | dev_dbg(&intf->dev, "resume port %d --> %d\n", |
1959 | port1, status); | 1996 | port1, status); |
@@ -1962,8 +1999,8 @@ static int hub_resume(struct usb_interface *intf) | |||
1962 | } | 1999 | } |
1963 | up(&udev->serialize); | 2000 | up(&udev->serialize); |
1964 | } | 2001 | } |
1965 | hub->resume_root_hub = 0; | 2002 | } |
1966 | hub_activate(hub); | 2003 | #endif |
1967 | return 0; | 2004 | return 0; |
1968 | } | 2005 | } |
1969 | 2006 | ||
@@ -1987,26 +2024,6 @@ void usb_resume_root_hub(struct usb_device *hdev) | |||
1987 | kick_khubd(hub); | 2024 | kick_khubd(hub); |
1988 | } | 2025 | } |
1989 | 2026 | ||
1990 | #else /* !CONFIG_USB_SUSPEND */ | ||
1991 | |||
1992 | int usb_suspend_device(struct usb_device *udev) | ||
1993 | { | ||
1994 | /* state does NOT lie by saying it's USB_STATE_SUSPENDED! */ | ||
1995 | return 0; | ||
1996 | } | ||
1997 | |||
1998 | int usb_resume_device(struct usb_device *udev) | ||
1999 | { | ||
2000 | udev->dev.power.power_state.event = PM_EVENT_ON; | ||
2001 | return 0; | ||
2002 | } | ||
2003 | |||
2004 | #define hub_suspend NULL | ||
2005 | #define hub_resume NULL | ||
2006 | #define remote_wakeup(x) 0 | ||
2007 | |||
2008 | #endif /* CONFIG_USB_SUSPEND */ | ||
2009 | |||
2010 | EXPORT_SYMBOL(usb_suspend_device); | 2027 | EXPORT_SYMBOL(usb_suspend_device); |
2011 | EXPORT_SYMBOL(usb_resume_device); | 2028 | EXPORT_SYMBOL(usb_resume_device); |
2012 | 2029 | ||