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 /drivers/usb | |
| 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(-)
Diffstat (limited to 'drivers/usb')
| -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 | ||
