aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2008-04-28 11:06:11 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-07-21 18:15:47 -0400
commitb01b03f3ad82b4293f6ca4da9b2692b6a377c609 (patch)
tree9d3ab94600f6fa0491256ab1ac0fd824e55ee880 /drivers
parentbd2c784595e3dd551c2b3aa4167657bcc802f598 (diff)
USB: add new routine for checking port-resume type
This patch (as1070) creates a new subroutine to check whether a device can be resumed. This code is needed even when CONFIG_USB_SUSPEND isn't set, because devices do suspend themselves when the root hub (and hence the entire bus) is suspended, and power sessions can get lost during a system sleep even without individual port suspends. The patch also fixes a loose end in USB-Persist reset-resume handling. When a low- or full-speed device is attached to an EHCI's companion controller, the port handoff during resume will cause the companion port's connect-status-change feature to be set. If that flag isn't cleared, the port-reset code will think it indicates that the device has been unplugged and the reset-resume will fail. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/core/hub.c89
1 files changed, 63 insertions, 26 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 2a5c2833de38..d14da2123eb5 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1821,6 +1821,45 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
1821 1821
1822#ifdef CONFIG_PM 1822#ifdef CONFIG_PM
1823 1823
1824#define MASK_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | \
1825 USB_PORT_STAT_SUSPEND)
1826#define WANT_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION)
1827
1828/* Determine whether the device on a port is ready for a normal resume,
1829 * is ready for a reset-resume, or should be disconnected.
1830 */
1831static int check_port_resume_type(struct usb_device *udev,
1832 struct usb_hub *hub, int port1,
1833 int status, unsigned portchange, unsigned portstatus)
1834{
1835 /* Is the device still present? */
1836 if (status || (portstatus & MASK_BITS) != WANT_BITS) {
1837 if (status >= 0)
1838 status = -ENODEV;
1839 }
1840
1841 /* Can't do a normal resume if the port isn't enabled */
1842 else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume)
1843 status = -ENODEV;
1844
1845 if (status) {
1846 dev_dbg(hub->intfdev,
1847 "port %d status %04x.%04x after resume, %d\n",
1848 port1, portchange, portstatus, status);
1849 } else if (udev->reset_resume) {
1850
1851 /* Late port handoff can set status-change bits */
1852 if (portchange & USB_PORT_STAT_C_CONNECTION)
1853 clear_port_feature(hub->hdev, port1,
1854 USB_PORT_FEAT_C_CONNECTION);
1855 if (portchange & USB_PORT_STAT_C_ENABLE)
1856 clear_port_feature(hub->hdev, port1,
1857 USB_PORT_FEAT_C_ENABLE);
1858 }
1859
1860 return status;
1861}
1862
1824#ifdef CONFIG_USB_SUSPEND 1863#ifdef CONFIG_USB_SUSPEND
1825 1864
1826/* 1865/*
@@ -2025,7 +2064,6 @@ int usb_port_resume(struct usb_device *udev)
2025 int port1 = udev->portnum; 2064 int port1 = udev->portnum;
2026 int status; 2065 int status;
2027 u16 portchange, portstatus; 2066 u16 portchange, portstatus;
2028 unsigned mask_flags, want_flags;
2029 2067
2030 /* Skip the initial Clear-Suspend step for a remote wakeup */ 2068 /* Skip the initial Clear-Suspend step for a remote wakeup */
2031 status = hub_port_status(hub, port1, &portstatus, &portchange); 2069 status = hub_port_status(hub, port1, &portstatus, &portchange);
@@ -2054,35 +2092,23 @@ int usb_port_resume(struct usb_device *udev)
2054 */ 2092 */
2055 status = hub_port_status(hub, port1, &portstatus, &portchange); 2093 status = hub_port_status(hub, port1, &portstatus, &portchange);
2056 2094
2057 SuspendCleared: 2095 /* TRSMRCY = 10 msec */
2058 if (udev->reset_resume) 2096 msleep(10);
2059 want_flags = USB_PORT_STAT_POWER 2097 }
2060 | USB_PORT_STAT_CONNECTION;
2061 else
2062 want_flags = USB_PORT_STAT_POWER
2063 | USB_PORT_STAT_CONNECTION
2064 | USB_PORT_STAT_ENABLE;
2065 mask_flags = want_flags | USB_PORT_STAT_SUSPEND;
2066 2098
2067 if (status < 0 || (portstatus & mask_flags) != want_flags) { 2099 SuspendCleared:
2068 dev_dbg(hub->intfdev, 2100 if (status == 0) {
2069 "port %d status %04x.%04x after resume, %d\n", 2101 if (portchange & USB_PORT_STAT_C_SUSPEND)
2070 port1, portchange, portstatus, status); 2102 clear_port_feature(hub->hdev, port1,
2071 if (status >= 0) 2103 USB_PORT_FEAT_C_SUSPEND);
2072 status = -ENODEV;
2073 } else {
2074 if (portchange & USB_PORT_STAT_C_SUSPEND)
2075 clear_port_feature(hub->hdev, port1,
2076 USB_PORT_FEAT_C_SUSPEND);
2077 /* TRSMRCY = 10 msec */
2078 msleep(10);
2079 }
2080 } 2104 }
2081 2105
2082 clear_bit(port1, hub->busy_bits); 2106 clear_bit(port1, hub->busy_bits);
2083 if (!hub->hdev->parent && !hub->busy_bits[0]) 2107 if (!hub->hdev->parent && !hub->busy_bits[0])
2084 usb_enable_root_hub_irq(hub->hdev->bus); 2108 usb_enable_root_hub_irq(hub->hdev->bus);
2085 2109
2110 status = check_port_resume_type(udev,
2111 hub, port1, status, portchange, portstatus);
2086 if (status == 0) 2112 if (status == 0)
2087 status = finish_port_resume(udev); 2113 status = finish_port_resume(udev);
2088 if (status < 0) { 2114 if (status < 0) {
@@ -2115,12 +2141,23 @@ int usb_port_suspend(struct usb_device *udev)
2115 return 0; 2141 return 0;
2116} 2142}
2117 2143
2144/* However we may need to do a reset-resume */
2145
2118int usb_port_resume(struct usb_device *udev) 2146int usb_port_resume(struct usb_device *udev)
2119{ 2147{
2120 int status = 0; 2148 struct usb_hub *hub = hdev_to_hub(udev->parent);
2149 int port1 = udev->portnum;
2150 int status;
2151 u16 portchange, portstatus;
2121 2152
2122 /* However we may need to do a reset-resume */ 2153 status = hub_port_status(hub, port1, &portstatus, &portchange);
2123 if (udev->reset_resume) { 2154 status = check_port_resume_type(udev,
2155 hub, port1, status, portchange, portstatus);
2156
2157 if (status) {
2158 dev_dbg(&udev->dev, "can't resume, status %d\n", status);
2159 hub_port_logical_disconnect(hub, port1);
2160 } else if (udev->reset_resume) {
2124 dev_dbg(&udev->dev, "reset-resume\n"); 2161 dev_dbg(&udev->dev, "reset-resume\n");
2125 status = usb_reset_device(udev); 2162 status = usb_reset_device(udev);
2126 } 2163 }