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>
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index b0c0a99..6b029cd 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -168,6 +168,10 @@
udev = to_usb_device(dev);
+#ifdef CONFIG_PM
+ cancel_delayed_work(&udev->autosuspend);
+ flush_scheduled_work();
+#endif
usb_destroy_configuration(udev);
usb_put_hcd(bus_to_hcd(udev->bus));
kfree(udev->product);
@@ -176,6 +180,21 @@
kfree(udev);
}
+#ifdef CONFIG_PM
+
+/* usb_autosuspend_work - callback routine to autosuspend a USB device */
+static void usb_autosuspend_work(void *_udev)
+{
+ struct usb_device *udev = _udev;
+
+ mutex_lock_nested(&udev->pm_mutex, udev->level);
+ udev->auto_pm = 1;
+ usb_suspend_both(udev, PMSG_SUSPEND);
+ mutex_unlock(&udev->pm_mutex);
+}
+
+#endif
+
/**
* usb_alloc_dev - usb device constructor (usbcore-internal)
* @parent: hub to which device is connected; null to allocate a root hub
@@ -251,6 +270,10 @@
dev->parent = parent;
INIT_LIST_HEAD(&dev->filelist);
+#ifdef CONFIG_PM
+ mutex_init(&dev->pm_mutex);
+ INIT_WORK(&dev->autosuspend, usb_autosuspend_work, dev);
+#endif
return dev;
}