Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=6b157c9bf3bace6eeb4a973da63923ef24995cce
Commit:     6b157c9bf3bace6eeb4a973da63923ef24995cce
Parent:     378465396e74aa6ef36271cecaea49eb742025a8
Author:     Alan Stern <[EMAIL PROTECTED]>
AuthorDate: Tue Mar 13 16:37:30 2007 -0400
Committer:  Greg Kroah-Hartman <[EMAIL PROTECTED]>
CommitDate: Fri Apr 27 13:28:35 2007 -0700

    USB: separate autosuspend from external suspend
    
    This patch (as866) adds new entry points for external USB device
    suspend and resume requests, as opposed to internally-generated
    autosuspend or autoresume.  It also changes the existing
    remote-wakeup code paths to use the new routines, since remote wakeup
    is not the same as autoresume.
    
    As part of the change, it turns out to be necessary to do remote
    wakeup of root hubs from a workqueue.  We had been using khubd, but it
    does autoresume rather than an external resume.  Using the
    ksuspend_usb_wq workqueue for this purpose seemed a logical choice.
    
    Signed-off-by: Alan Stern <[EMAIL PROTECTED]>
    Signed-off-by: Greg Kroah-Hartman <[EMAIL PROTECTED]>
---
 drivers/usb/core/driver.c |   82 ++++++++++++++++++++++++++++++++------------
 drivers/usb/core/hcd.c    |   25 ++++++++++++--
 drivers/usb/core/hcd.h    |    3 ++
 drivers/usb/core/hub.c    |   14 +-------
 drivers/usb/core/usb.c    |    3 +-
 drivers/usb/core/usb.h    |    4 ++-
 6 files changed, 90 insertions(+), 41 deletions(-)

diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 8c0a7de..abea48d 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1424,48 +1424,84 @@ void usb_autosuspend_work(struct work_struct *work)
 
 #endif /* CONFIG_USB_SUSPEND */
 
-static int usb_suspend(struct device *dev, pm_message_t message)
+/**
+ * usb_external_suspend_device - external suspend of a USB device and its 
interfaces
+ * @udev: the usb_device to suspend
+ * @msg: Power Management message describing this state transition
+ *
+ * This routine handles external suspend requests: ones not generated
+ * internally by a USB driver (autosuspend) but rather coming from the user
+ * (via sysfs) or the PM core (system sleep).  The suspend will be carried
+ * out regardless of @udev's usage counter or those of its interfaces,
+ * and regardless of whether or not remote wakeup is enabled.  Of course,
+ * interface drivers still have the option of failing the suspend (if
+ * there are unsuspended children, for example).
+ *
+ * The caller must hold @udev's device lock.
+ */
+int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
 {
        int     status;
 
-       if (is_usb_device(dev)) {
-               struct usb_device *udev = to_usb_device(dev);
-
-               usb_pm_lock(udev);
-               udev->auto_pm = 0;
-               status = usb_suspend_both(udev, message);
-               usb_pm_unlock(udev);
-       } else
-               status = 0;
+       usb_pm_lock(udev);
+       udev->auto_pm = 0;
+       status = usb_suspend_both(udev, msg);
+       usb_pm_unlock(udev);
        return status;
 }
 
-static int usb_resume(struct device *dev)
+/**
+ * usb_external_resume_device - external resume of a USB device and its 
interfaces
+ * @udev: the usb_device to resume
+ *
+ * This routine handles external resume requests: ones not generated
+ * internally by a USB driver (autoresume) but rather coming from the user
+ * (via sysfs), the PM core (system resume), or the device itself (remote
+ * wakeup).  @udev's usage counter is unaffected.
+ *
+ * The caller must hold @udev's device lock.
+ */
+int usb_external_resume_device(struct usb_device *udev)
 {
        int     status;
 
-       if (is_usb_device(dev)) {
-               struct usb_device *udev = to_usb_device(dev);
-
-               usb_pm_lock(udev);
-               udev->auto_pm = 0;
-               status = usb_resume_both(udev);
-               usb_pm_unlock(udev);
+       usb_pm_lock(udev);
+       udev->auto_pm = 0;
+       status = usb_resume_both(udev);
+       usb_pm_unlock(udev);
 
-               /* Rebind drivers that had no suspend method? */
-       } else
-               status = 0;
+       /* Now that the device is awake, we can start trying to autosuspend
+        * it again. */
+       if (status == 0)
+               usb_try_autosuspend_device(udev);
        return status;
 }
 
+static int usb_suspend(struct device *dev, pm_message_t message)
+{
+       if (!is_usb_device(dev))        /* Ignore PM for interfaces */
+               return 0;
+       return usb_external_suspend_device(to_usb_device(dev), message);
+}
+
+static int usb_resume(struct device *dev)
+{
+       if (!is_usb_device(dev))        /* Ignore PM for interfaces */
+               return 0;
+       return usb_external_resume_device(to_usb_device(dev));
+}
+
+#else
+
+#define usb_suspend    NULL
+#define usb_resume     NULL
+
 #endif /* CONFIG_PM */
 
 struct bus_type usb_bus_type = {
        .name =         "usb",
        .match =        usb_device_match,
        .uevent =       usb_uevent,
-#ifdef CONFIG_PM
        .suspend =      usb_suspend,
        .resume =       usb_resume,
-#endif
 };
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index af7aed1..8bc3ce6 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -37,6 +37,7 @@
 #include <asm/irq.h>
 #include <asm/byteorder.h>
 #include <linux/platform_device.h>
+#include <linux/workqueue.h>
 
 #include <linux/usb.h>
 
@@ -1298,14 +1299,25 @@ int hcd_bus_resume (struct usb_bus *bus)
        return status;
 }
 
+/* Workqueue routine for root-hub remote wakeup */
+static void hcd_resume_work(struct work_struct *work)
+{
+       struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);
+       struct usb_device *udev = hcd->self.root_hub;
+
+       usb_lock_device(udev);
+       usb_external_resume_device(udev);
+       usb_unlock_device(udev);
+}
+
 /**
  * usb_hcd_resume_root_hub - called by HCD to resume its root hub 
  * @hcd: host controller for this root hub
  *
  * The USB host controller calls this function when its root hub is
  * suspended (with the remote wakeup feature enabled) and a remote
- * wakeup request is received.  It queues a request for khubd to
- * resume the root hub (that is, manage its downstream ports again).
+ * wakeup request is received.  The routine submits a workqueue request
+ * to resume the root hub (that is, manage its downstream ports again).
  */
 void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
 {
@@ -1313,7 +1325,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
 
        spin_lock_irqsave (&hcd_root_hub_lock, flags);
        if (hcd->rh_registered)
-               usb_resume_root_hub (hcd->self.root_hub);
+               queue_work(ksuspend_usb_wq, &hcd->wakeup_work);
        spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
 }
 EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
@@ -1502,6 +1514,9 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver 
*driver,
        init_timer(&hcd->rh_timer);
        hcd->rh_timer.function = rh_timer_func;
        hcd->rh_timer.data = (unsigned long) hcd;
+#ifdef CONFIG_PM
+       INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
+#endif
 
        hcd->driver = driver;
        hcd->product_desc = (driver->product_desc) ? driver->product_desc :
@@ -1668,6 +1683,10 @@ void usb_remove_hcd(struct usb_hcd *hcd)
        hcd->rh_registered = 0;
        spin_unlock_irq (&hcd_root_hub_lock);
 
+#ifdef CONFIG_PM
+       flush_workqueue(ksuspend_usb_wq);
+#endif
+
        mutex_lock(&usb_bus_list_lock);
        usb_disconnect(&hcd->self.root_hub);
        mutex_unlock(&usb_bus_list_lock);
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 2a269ca..ef50fa4 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -68,6 +68,9 @@ struct usb_hcd {
 
        struct timer_list       rh_timer;       /* drives root-hub polling */
        struct urb              *status_urb;    /* the current status urb */
+#ifdef CONFIG_PM
+       struct work_struct      wakeup_work;    /* for remote wakeup */
+#endif
 
        /*
         * hardware info/state
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 7a60285..19abe81 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1855,12 +1855,7 @@ static int remote_wakeup(struct usb_device *udev)
        usb_lock_device(udev);
        if (udev->state == USB_STATE_SUSPENDED) {
                dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
-               status = usb_autoresume_device(udev);
-
-               /* Give the interface drivers a chance to do something,
-                * then autosuspend the device again. */
-               if (status == 0)
-                       usb_autosuspend_device(udev);
+               status = usb_external_resume_device(udev);
        }
        usb_unlock_device(udev);
        return status;
@@ -1984,13 +1979,6 @@ static inline int remote_wakeup(struct usb_device *udev)
 #define hub_resume NULL
 #endif
 
-void usb_resume_root_hub(struct usb_device *hdev)
-{
-       struct usb_hub *hub = hdev_to_hub(hdev);
-
-       kick_khubd(hub);
-}
-
 
 /* USB 2.0 spec, 7.1.7.3 / fig 7-29:
  *
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 82338f4..138252e 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -49,7 +49,8 @@ const char *usbcore_name = "usbcore";
 
 static int nousb;      /* Disable USB when built into kernel image */
 
-struct workqueue_struct *ksuspend_usb_wq;      /* For autosuspend */
+/* Workqueue for autosuspend and for remote wakeup of root hubs */
+struct workqueue_struct *ksuspend_usb_wq;
 
 #ifdef CONFIG_USB_SUSPEND
 static int usb_autosuspend_delay = 2;          /* Default delay value,
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index b98bc0d..c94379e 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -21,7 +21,6 @@ extern char *usb_cache_string(struct usb_device *udev, int 
index);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 
 extern void usb_kick_khubd(struct usb_device *dev);
-extern void usb_resume_root_hub(struct usb_device *dev);
 extern int usb_match_device(struct usb_device *dev,
                            const struct usb_device_id *id);
 
@@ -37,6 +36,9 @@ extern void usb_host_cleanup(void);
 extern void usb_autosuspend_work(struct work_struct *work);
 extern int usb_port_suspend(struct usb_device *dev);
 extern int usb_port_resume(struct usb_device *dev);
+extern int usb_external_suspend_device(struct usb_device *udev,
+               pm_message_t msg);
+extern int usb_external_resume_device(struct usb_device *udev);
 
 static inline void usb_pm_lock(struct usb_device *udev)
 {
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to