diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2006-08-30 19:47:02 (GMT) |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-09-27 18:58:57 (GMT) |
commit | 645daaab0b6adc35c1838df2a82f9d729fdb1767 (patch) | |
tree | 908d58f6c0ad01e9c1622a4c12c9c8080c629baa /drivers/usb/core/hub.c | |
parent | b6956ffa595db97656d5901ca8fec77ef272d41a (diff) | |
download | linux-fsl-qoriq-645daaab0b6adc35c1838df2a82f9d729fdb1767.tar.xz |
usbcore: add autosuspend/autoresume infrastructure
This patch (as739) adds the basic infrastructure for USB autosuspend
and autoresume. The main features are:
PM usage counters added to struct usb_device and struct
usb_interface, indicating whether it's okay to autosuspend
them or they are currently in use.
Flag added to usb_device indicating whether the current
suspend/resume operation originated from outside or as an
autosuspend/autoresume.
Flag added to usb_driver indicating whether the driver
supports autosuspend. If not, no device bound to the driver
will be autosuspended.
Mutex added to usb_device for protecting PM operations.
Unlike the device semaphore, the locking rule for the pm_mutex
is that you must acquire the locks going _up_ the device tree.
New routines handling autosuspend/autoresume requests for
interfaces and devices.
Suspend and resume requests are propagated up the device tree
(but not outside the USB subsystem).
work_struct added to usb_device, for carrying out delayed
autosuspend requests.
Autoresume added (and autosuspend prevented) during probe and
disconnect.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r-- | drivers/usb/core/hub.c | 49 |
1 files changed, 34 insertions, 15 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 78e910b..dee812b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1017,19 +1017,22 @@ void usb_set_device_state(struct usb_device *udev, if (udev->state == USB_STATE_NOTATTACHED) ; /* do nothing */ else if (new_state != USB_STATE_NOTATTACHED) { - udev->state = new_state; /* root hub wakeup capabilities are managed out-of-band * and may involve silicon errata ... ignore them here. */ if (udev->parent) { - if (new_state == USB_STATE_CONFIGURED) + if (udev->state == USB_STATE_SUSPENDED + || new_state == USB_STATE_SUSPENDED) + ; /* No change to wakeup settings */ + else if (new_state == USB_STATE_CONFIGURED) device_init_wakeup(&udev->dev, (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP)); - else if (new_state != USB_STATE_SUSPENDED) + else device_init_wakeup(&udev->dev, 0); } + udev->state = new_state; } else recursively_mark_NOTATTACHED(udev); spin_unlock_irqrestore(&device_state_lock, flags); @@ -1507,7 +1510,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1, * NOTE: OTG devices may issue remote wakeup (or SRP) even when * we don't explicitly enable it here. */ - if (device_may_wakeup(&udev->dev)) { + if (udev->do_remote_wakeup) { status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, USB_DEVICE_REMOTE_WAKEUP, 0, @@ -1533,7 +1536,8 @@ static int hub_port_suspend(struct usb_hub *hub, int port1, USB_CTRL_SET_TIMEOUT); } else { /* device has up to 10 msec to fully suspend */ - dev_dbg(&udev->dev, "usb suspend\n"); + dev_dbg(&udev->dev, "usb %ssuspend\n", + udev->auto_pm ? "auto-" : ""); usb_set_device_state(udev, USB_STATE_SUSPENDED); msleep(10); } @@ -1573,7 +1577,8 @@ static int __usb_port_suspend (struct usb_device *udev, int port1) status = hub_port_suspend(hdev_to_hub(udev->parent), port1, udev); else { - dev_dbg(&udev->dev, "usb suspend\n"); + dev_dbg(&udev->dev, "usb %ssuspend\n", + udev->auto_pm ? "auto-" : ""); usb_set_device_state(udev, USB_STATE_SUSPENDED); } return status; @@ -1687,7 +1692,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) /* drive resume for at least 20 msec */ if (udev) - dev_dbg(&udev->dev, "RESUME\n"); + dev_dbg(&udev->dev, "usb %sresume\n", + udev->auto_pm ? "auto-" : ""); msleep(25); #define LIVE_FLAGS ( USB_PORT_STAT_POWER \ @@ -1754,8 +1760,11 @@ int usb_port_resume(struct usb_device *udev) // NOTE this fails if parent is also suspended... status = hub_port_resume(hdev_to_hub(udev->parent), udev->portnum, udev); - } else + } else { + dev_dbg(&udev->dev, "usb %sresume\n", + udev->auto_pm ? "auto-" : ""); status = finish_port_resume(udev); + } if (status < 0) dev_dbg(&udev->dev, "can't resume, status %d\n", status); return status; @@ -1765,19 +1774,23 @@ static int remote_wakeup(struct usb_device *udev) { int status = 0; - /* don't repeat RESUME sequence if this device - * was already woken up by some other task - */ + /* All this just to avoid sending a port-resume message + * to the parent hub! */ + usb_lock_device(udev); + mutex_lock_nested(&udev->pm_mutex, udev->level); if (udev->state == USB_STATE_SUSPENDED) { - dev_dbg(&udev->dev, "RESUME (wakeup)\n"); + dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); /* TRSMRCY = 10 msec */ msleep(10); status = finish_port_resume(udev); + if (status == 0) + udev->dev.power.power_state.event = PM_EVENT_ON; } + mutex_unlock(&udev->pm_mutex); if (status == 0) - usb_resume_both(udev); + usb_autoresume_device(udev, 0); usb_unlock_device(udev); return status; } @@ -1834,7 +1847,9 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) == PM_EVENT_ON #endif ) { - dev_dbg(&intf->dev, "port %d nyet suspended\n", port1); + if (!hdev->auto_pm) + dev_dbg(&intf->dev, "port %d nyet suspended\n", + port1); return -EBUSY; } } @@ -2587,7 +2602,7 @@ static void hub_events(void) * stub "device" node was never suspended. */ if (i) - usb_resume_both(hdev); + usb_autoresume_device(hdev, 0); /* If this is an inactive or suspended hub, do nothing */ if (hub->quiescing) @@ -2993,6 +3008,9 @@ int usb_reset_composite_device(struct usb_device *udev, return -EINVAL; } + /* Prevent autosuspend during the reset */ + usb_autoresume_device(udev, 1); + if (iface && iface->condition != USB_INTERFACE_BINDING) iface = NULL; @@ -3034,6 +3052,7 @@ int usb_reset_composite_device(struct usb_device *udev, } } + usb_autosuspend_device(udev, 1); return ret; } EXPORT_SYMBOL(usb_reset_composite_device); |