[PATCH] remove usb_suspend_device() parameter
David Brownell [Wed, 14 Sep 2005 02:57:27 +0000 (19:57 -0700)]
This patch removes the extra usb_suspend_device() parameter.  The original
reason to pass that parameter was so that this routine could suspend any
active children.  A previous patch removed that functionality ... leaving
no reason to pass the parameter.  A close analogy is pci_set_power_state,
which doesn't need a pm_message_t either.

On the internal code path that comes through the driver model, the parameter
is now used to distinguish cases where USB devices need to "freeze" but not
suspend.   It also checks for an error case that's accessible through sysfs:
attempting to suspend a device before its interfaces (or for hubs, ports).

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

 drivers/usb/core/hub.c         |   34 +++++++++++++++++++++-------------
 drivers/usb/core/usb.c         |   23 +++++++++++++++++++++--
 drivers/usb/host/ehci-hcd.c    |    2 +-
 drivers/usb/host/isp116x-hcd.c |    2 +-
 drivers/usb/host/ohci-pci.c    |    2 +-
 include/linux/usb.h            |    2 +-
 6 files changed, 46 insertions(+), 19 deletions(-)

drivers/usb/core/hub.c
drivers/usb/core/usb.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/isp116x-hcd.c
drivers/usb/host/ohci-pci.c
include/linux/usb.h

index d3337d9..33127b8 100644 (file)
@@ -1323,11 +1323,9 @@ int usb_new_device(struct usb_device *udev)
                 * (Includes HNP test device.)
                 */
                if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
-                       static int __usb_suspend_device (struct usb_device *,
-                                               int port1, pm_message_t state);
-                       err = __usb_suspend_device(udev,
-                                       udev->bus->otg_port,
-                                       PMSG_SUSPEND);
+                       static int __usb_suspend_device(struct usb_device *,
+                                               int port1);
+                       err = __usb_suspend_device(udev, udev->bus->otg_port);
                        if (err < 0)
                                dev_dbg(&udev->dev, "HNP fail, %d\n", err);
                }
@@ -1517,7 +1515,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
        /* FIXME let caller ask to power down the port:
         *  - some devices won't enumerate without a VBUS power cycle
         *  - SRP saves power that way
-        *  - usb_suspend_device(dev, PMSG_SUSPEND)
+        *  - ... new call, TBD ...
         * That's easy if this hub can switch power per-port, and
         * khubd reactivates the port later (timer, SRP, etc).
         * Powerdown must be optional, because of reset/DFU.
@@ -1599,9 +1597,12 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
  * Other than re-initializing the hub (plug/unplug, except for root hubs),
  * Linux (2.6) currently has NO mechanisms to initiate that:  no khubd
  * timer, no SRP, no requests through sysfs.
+ *
+ * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when
+ * the root hub for their bus goes into global suspend ... so we don't
+ * (falsely) update the device power state to say it suspended.
  */
-static int __usb_suspend_device (struct usb_device *udev, int port1,
-                                pm_message_t state)
+static int __usb_suspend_device (struct usb_device *udev, int port1)
 {
        int     status;
 
@@ -1648,14 +1649,13 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
                                udev);
 
        if (status == 0)
-               udev->dev.power.power_state = state;
+               udev->dev.power.power_state = PMSG_SUSPEND;
        return status;
 }
 
 /**
  * usb_suspend_device - suspend a usb device
  * @udev: device that's no longer in active use
- * @state: PMSG_SUSPEND to suspend
  * Context: must be able to sleep; device not locked
  *
  * Suspends a USB device that isn't in active use, conserving power.
@@ -1664,13 +1664,16 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
  * suspend by the host, using usb_resume_device().  It's also routine
  * to disconnect devices while they are suspended.
  *
+ * This only affects the USB hardware for a device; its interfaces
+ * (and, for hubs, child devices) must already have been suspended.
+ *
  * Suspending OTG devices may trigger HNP, if that's been enabled
  * between a pair of dual-role devices.  That will change roles, such
  * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral.
  *
  * Returns 0 on success, else negative errno.
  */
-int usb_suspend_device(struct usb_device *udev, pm_message_t state)
+int usb_suspend_device(struct usb_device *udev)
 {
        int     port1, status;
 
@@ -1678,12 +1681,15 @@ int usb_suspend_device(struct usb_device *udev, pm_message_t state)
        if (port1 < 0)
                return port1;
 
-       status = __usb_suspend_device(udev, port1, state);
+       status = __usb_suspend_device(udev, port1);
        usb_unlock_device(udev);
        return status;
 }
 
 /*
+ * If the USB "suspend" state is in use (rather than "global suspend"),
+ * many devices will be individually taken out of suspend state using
+ * special" resume" signaling.  These routines kick in shortly after
  * hardware resume signaling is finished, either because of selective
  * resume (by host) or remote wakeup (by device) ... now see what changed
  * in the tree that's rooted at this device.
@@ -1986,13 +1992,15 @@ void usb_resume_root_hub(struct usb_device *hdev)
 
 #else  /* !CONFIG_USB_SUSPEND */
 
-int usb_suspend_device(struct usb_device *udev, pm_message_t state)
+int usb_suspend_device(struct usb_device *udev)
 {
+       /* state does NOT lie by saying it's USB_STATE_SUSPENDED! */
        return 0;
 }
 
 int usb_resume_device(struct usb_device *udev)
 {
+       udev->dev.power_state.event = PM_EVENT_ON;
        return 0;
 }
 
index 6ecfdce..e89dbd4 100644 (file)
@@ -1414,14 +1414,33 @@ void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
                        usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
 }
 
+static int verify_suspended(struct device *dev, void *unused)
+{
+       return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0;
+}
+
 static int usb_generic_suspend(struct device *dev, pm_message_t message)
 {
        struct usb_interface    *intf;
        struct usb_driver       *driver;
        int                     status;
 
-       if (dev->driver == &usb_generic_driver)
-               return usb_suspend_device (to_usb_device(dev), message);
+       /* USB devices enter SUSPEND state through their hubs, but can be
+        * marked for FREEZE as soon as their children are already idled.
+        */
+       if (dev->driver == &usb_generic_driver) {
+               if (dev->power.power_state.event == message.event)
+                       return 0;
+               /* we need to rule out bogus requests through sysfs */
+               status = device_for_each_child(dev, NULL, verify_suspended);
+               if (status)
+                       return status;
+               if (message.event == PM_EVENT_FREEZE) {
+                       dev->power.power_state = message;
+                       return 0;
+               }
+               return usb_suspend_device (to_usb_device(dev));
+       }
 
        if ((dev->driver == NULL) ||
            (dev->driver_data == &usb_generic_driver_data))
index b3eb026..513fccb 100644 (file)
@@ -759,7 +759,7 @@ static int ehci_suspend (struct usb_hcd *hcd, pm_message_t message)
                msleep (100);
 
 #ifdef CONFIG_USB_SUSPEND
-       (void) usb_suspend_device (hcd->self.root_hub, message);
+       (void) usb_suspend_device (hcd->self.root_hub);
 #else
        usb_lock_device (hcd->self.root_hub);
        (void) ehci_hub_suspend (hcd);
index 642f350..554d602 100644 (file)
@@ -1781,7 +1781,7 @@ static int isp116x_suspend(struct device *dev, pm_message_t state)
 
        VDBG("%s: state %x\n", __func__, state);
 
-       ret = usb_suspend_device(hcd->self.root_hub, state);
+       ret = usb_suspend_device(hcd->self.root_hub);
        if (!ret) {
                dev->power.power_state = state;
                INFO("%s suspended\n", hcd_name);
index eede6be..41e8598 100644 (file)
@@ -119,7 +119,7 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
                msleep (100);
 
 #ifdef CONFIG_USB_SUSPEND
-       (void) usb_suspend_device (hcd->self.root_hub, message);
+       (void) usb_suspend_device (hcd->self.root_hub);
 #else
        usb_lock_device (hcd->self.root_hub);
        (void) ohci_hub_suspend (hcd);
index 04502e1..25ec91d 100644 (file)
@@ -976,7 +976,7 @@ extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
        int timeout);
 
 /* selective suspend/resume */
-extern int usb_suspend_device(struct usb_device *dev, pm_message_t message);
+extern int usb_suspend_device(struct usb_device *dev);
 extern int usb_resume_device(struct usb_device *dev);