diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2006-11-20 17:14:30 +0100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-12-01 23:25:52 +0100 |
commit | d25450c68767481f7c9cc4823a6da8235db40be6 (patch) | |
tree | 1a982e5b7ed83f1911bbc3e58a02552d765d192a /drivers/usb | |
parent | USB: struct usb_device: change flag to bitflag (diff) | |
download | linux-d25450c68767481f7c9cc4823a6da8235db40be6.tar.xz linux-d25450c68767481f7c9cc4823a6da8235db40be6.zip |
USB hub: simplify remote-wakeup handling
This patch (as817) simplifies the remote-wakeup processing in the hub
driver. Now instead of using a specialized code path, it relies on
the standard USB resume routines. The hub_port_resume() function does
an initial get_port_status() to see whether the port has already
resumed itself (as it does when a remote-wakeup request is sent).
This will slow down handling of other resume events slightly, but not
enough to matter.
The patch also changes the hub_port_status() routine, making it return
an error if a short reply is received.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/hub.c | 43 |
1 files changed, 20 insertions, 23 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 55812a5ac661..46df5e60764b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1409,10 +1409,12 @@ static int hub_port_status(struct usb_hub *hub, int port1, int ret; ret = get_port_status(hub->hdev, port1, &hub->status->port); - if (ret < 0) + if (ret < 4) { dev_err (hub->intfdev, "%s failed (err = %d)\n", __FUNCTION__, ret); - else { + if (ret >= 0) + ret = -EIO; + } else { *status = le16_to_cpu(hub->status->port.wPortStatus); *change = le16_to_cpu(hub->status->port.wPortChange); ret = 0; @@ -1760,6 +1762,12 @@ static int hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) { int status; + u16 portchange, portstatus; + + /* Skip the initial Clear-Suspend step for a remote wakeup */ + status = hub_port_status(hub, port1, &portstatus, &portchange); + if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND)) + goto SuspendCleared; // dev_dbg(hub->intfdev, "resume port %d\n", port1); @@ -1773,9 +1781,6 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) "can't resume port %d, status %d\n", port1, status); } else { - u16 devstatus; - u16 portchange; - /* drive resume for at least 20 msec */ if (udev) dev_dbg(&udev->dev, "usb %sresume\n", @@ -1790,16 +1795,15 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) * stop resume signaling. Then finish the resume * sequence. */ - devstatus = portchange = 0; - status = hub_port_status(hub, port1, - &devstatus, &portchange); + status = hub_port_status(hub, port1, &portstatus, &portchange); +SuspendCleared: if (status < 0 - || (devstatus & LIVE_FLAGS) != LIVE_FLAGS - || (devstatus & USB_PORT_STAT_SUSPEND) != 0 + || (portstatus & LIVE_FLAGS) != LIVE_FLAGS + || (portstatus & USB_PORT_STAT_SUSPEND) != 0 ) { dev_dbg(hub->intfdev, "port %d status %04x.%04x after resume, %d\n", - port1, portchange, devstatus, status); + port1, portchange, portstatus, status); if (status >= 0) status = -ENODEV; } else { @@ -1860,23 +1864,16 @@ static int remote_wakeup(struct usb_device *udev) { int status = 0; - /* All this just to avoid sending a port-resume message - * to the parent hub! */ - usb_lock_device(udev); - usb_pm_lock(udev); if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); - /* TRSMRCY = 10 msec */ - msleep(10); - status = finish_port_resume(udev); + status = usb_autoresume_device(udev, 1); + + /* Give the interface drivers a chance to do something, + * then autosuspend the device again. */ if (status == 0) - udev->dev.power.power_state.event = PM_EVENT_ON; + usb_autosuspend_device(udev, 1); } - usb_pm_unlock(udev); - - if (status == 0) - usb_autoresume_device(udev, 0); usb_unlock_device(udev); return status; } |