David:
Please take a look at this proposed patch and see what you think. It
removes the recursion from the usbcore suspend/resume routines. There's
just a couple of small exceptions; the big one is that resuming a device
will cause all its interfaces to be resumed as well, and the small one is
that before suspending a device all the interfaces are checked to make
sure they are already suspended.
In addition, the code for handling interfaces has been moved from hub.c
into the generic suspend/resume routines near the end of usb.c. However I
didn't bother to change the part in usb_resume_device where the single
interface of a root hub gets resumed along with the root hub; that code
still calls hub_resume directly.
All this should work perfectly well with system sleep transitions, as the
PM core does the recursion for us. Runtime power management will be a
little more difficult; for testing purposes it will be handy to have some
scripts available.
Alan Stern
Index: usb-2.6/drivers/usb/core/hub.c
===================================================================
--- usb-2.6.orig/drivers/usb/core/hub.c
+++ usb-2.6/drivers/usb/core/hub.c
@@ -452,6 +452,7 @@ static void hub_quiesce(struct usb_hub *
{
/* stop khubd and related activity */
hub->quiescing = 1;
+ hub->activating = 0;
usb_kill_urb(hub->urb);
if (hub->has_indicators)
cancel_delayed_work(&hub->leds);
@@ -1553,50 +1554,23 @@ static int __usb_suspend_device (struct
return 0;
}
- /* suspend interface drivers; if this is a hub, it
- * suspends the child devices
+ /* check to be sure the interfaces really are suspended;
+ * the caller should have suspended them already
+ *
+ * FIXME: Should this check all the child devices?
*/
if (udev->actconfig) {
int i;
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *intf;
- struct usb_driver *driver;
intf = udev->actconfig->interface[i];
- if (state <= intf->dev.power.power_state)
- continue;
- if (!intf->dev.driver)
- continue;
- driver = to_usb_driver(intf->dev.driver);
-
- if (driver->suspend) {
- status = driver->suspend(intf, state);
- if (intf->dev.power.power_state != state
- || status)
- dev_err(&intf->dev,
- "suspend %d fail, code %d\n",
- state, status);
- }
-
- /* only drivers with suspend() can ever resume();
- * and after power loss, even they won't.
- * bus_rescan_devices() can rebind drivers later.
- *
- * FIXME the PM core self-deadlocks when unbinding
- * drivers during suspend/resume ... everything grabs
- * dpm_sem (not a spinlock, ugh). we want to unbind,
- * since we know every driver's probe/disconnect works
- * even for drivers that can't suspend.
- */
- if (!driver->suspend || state > PM_SUSPEND_MEM) {
-#if 1
- dev_warn(&intf->dev, "resume is unsafe!\n");
-#else
- down_write(&usb_bus_type.rwsem);
- device_release_driver(&intf->dev);
- up_write(&usb_bus_type.rwsem);
-#endif
+ if (intf->dev.power.power_state == PMSG_ON) {
+ dev_err(&udev->dev, "suspend %d failed, "
+ "interface %s still awake\n",
+ state, intf->dev.bus_id);
+ return -EBUSY;
}
}
}
@@ -1719,33 +1693,13 @@ static int finish_port_resume(struct usb
}
}
- /* resume interface drivers; if this is a hub, it
- * resumes the child devices
- */
+ /* resume interface drivers */
+ /* FIXME: Is this single-level recursion needed? */
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *intf;
- struct usb_driver *driver;
intf = udev->actconfig->interface[i];
- if (intf->dev.power.power_state == PMSG_SUSPEND)
- continue;
- if (!intf->dev.driver) {
- /* FIXME maybe force to alt 0 */
- continue;
- }
- driver = to_usb_driver(intf->dev.driver);
-
- /* bus_rescan_devices() may rebind drivers */
- if (!driver->resume)
- continue;
-
- /* can we do better than just logging errors? */
- status = driver->resume(intf);
- if (intf->dev.power.power_state != PMSG_ON
- || status)
- dev_dbg(&intf->dev,
- "resume fail, state %d code %d\n",
- intf->dev.power.power_state, status);
+ intf->dev.bus->resume(&intf->dev);
}
status = 0;
@@ -1897,24 +1851,20 @@ static int hub_suspend(struct usb_interf
struct usb_hub *hub = usb_get_intfdata (intf);
struct usb_device *hdev = hub->hdev;
unsigned port1;
- int status;
/* stop khubd and related activity */
hub_quiesce(hub);
- /* then suspend every port */
+ /* warn if some port isn't suspended */
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
struct usb_device *udev;
udev = hdev->children [port1-1];
if (!udev)
continue;
- down(&udev->serialize);
- status = __usb_suspend_device(udev, port1, state);
- up(&udev->serialize);
- if (status < 0)
- dev_dbg(&intf->dev, "suspend port %d --> %d\n",
- port1, status);
+ if (udev->dev.power.power_state == PMSG_ON)
+ dev_dbg(&intf->dev, "port %d isn't suspended\n",
+ port1);
}
intf->dev.power.power_state = state;
@@ -1923,47 +1873,10 @@ static int hub_suspend(struct usb_interf
static int hub_resume(struct usb_interface *intf)
{
- struct usb_device *hdev = interface_to_usbdev(intf);
struct usb_hub *hub = usb_get_intfdata (intf);
- unsigned port1;
- int status;
- if (intf->dev.power.power_state == PM_SUSPEND_ON)
+ if (intf->dev.power.power_state == PMSG_ON)
return 0;
-
- for (port1 = 1; port1 <= hdev->maxchild; port1++) {
- struct usb_device *udev;
- u16 portstat, portchange;
-
- udev = hdev->children [port1-1];
- status = hub_port_status(hub, port1, &portstat, &portchange);
- if (status == 0) {
- if (portchange & USB_PORT_STAT_C_SUSPEND) {
- clear_port_feature(hdev, port1,
- USB_PORT_FEAT_C_SUSPEND);
- portchange &= ~USB_PORT_STAT_C_SUSPEND;
- }
-
- /* let khubd handle disconnects etc */
- if (portchange)
- continue;
- }
-
- if (!udev || status < 0)
- continue;
- down (&udev->serialize);
- if (portstat & USB_PORT_STAT_SUSPEND)
- status = hub_port_resume(hub, port1, udev);
- else {
- status = finish_port_resume(udev);
- if (status < 0) {
- dev_dbg(&intf->dev, "resume port %d --> %d\n",
- port1, status);
- hub_port_logical_disconnect(hub, port1);
- }
- }
- up(&udev->serialize);
- }
intf->dev.power.power_state = PMSG_ON;
hub->resume_root_hub = 0;
Index: usb-2.6/drivers/usb/core/usb.c
===================================================================
--- usb-2.6.orig/drivers/usb/core/usb.c
+++ usb-2.6/drivers/usb/core/usb.c
@@ -1379,23 +1379,49 @@ static int usb_generic_suspend(struct de
{
struct usb_interface *intf;
struct usb_driver *driver;
+ int status;
+ /* there's only one USB suspend state */
+ if (dev->power.power_state != PMSG_ON)
+ return 0;
+
+ /* devices suspend through their hub */
if (dev->driver == &usb_generic_driver)
return usb_suspend_device (to_usb_device(dev), message);
- if ((dev->driver == NULL) ||
- (dev->driver_data == &usb_generic_driver_data))
+ if (dev->driver == NULL) {
+ dev->power.power_state = message;
return 0;
+ }
intf = to_usb_interface(dev);
driver = to_usb_driver(dev->driver);
-
- /* there's only one USB suspend state */
- if (intf->dev.power.power_state)
- return 0;
-
- if (driver->suspend)
- return driver->suspend(intf, message);
+ if (driver->suspend) {
+ status = driver->suspend(intf, message);
+ if (intf->dev.power.power_state != message || status)
+ dev_err(&intf->dev, "suspend %d fail, code %d\n",
+ message, status);
+ } else {
+ dev->power.power_state = message;
+
+ /* only drivers with suspend() can ever resume();
+ * and after power loss, even they won't.
+ * bus_rescan_devices() can rebind drivers later.
+ *
+ * FIXME the PM core self-deadlocks when unbinding
+ * drivers during suspend/resume ... everything grabs
+ * dpm_sem (not a spinlock, ugh). we want to unbind,
+ * since we know every driver's probe/disconnect works
+ * even for drivers that can't suspend.
+ */
+#if 1
+ dev_warn(&intf->dev, "resume is unsafe!\n");
+#else
+ down_write(&usb_bus_type.rwsem);
+ device_release_driver(&intf->dev);
+ up_write(&usb_bus_type.rwsem);
+#endif
+ }
return 0;
}
@@ -1403,20 +1429,40 @@ static int usb_generic_resume(struct dev
{
struct usb_interface *intf;
struct usb_driver *driver;
+ int status;
+
+ if (dev->power.power_state == PMSG_ON)
+ return 0;
/* devices resume through their hub */
if (dev->driver == &usb_generic_driver)
return usb_resume_device (to_usb_device(dev));
- if ((dev->driver == NULL) ||
- (dev->driver_data == &usb_generic_driver_data))
+ intf = to_usb_interface(dev);
+ if (dev->driver == NULL) {
+ dev->power.power_state = PMSG_ON;
+
+ /* Force to altsetting 0 for the next driver */
+ if (intf->cur_altsetting->desc.bAlternateSetting != 0)
+ usb_set_interface(interface_to_usbdev(intf),
+ intf->cur_altsetting->desc.bInterfaceNumber,
+ 0);
+
+ /* bus_rescan_devices() may rebind drivers */
return 0;
+ }
- intf = to_usb_interface(dev);
driver = to_usb_driver(dev->driver);
+ if (driver->resume) {
+
+ /* can we do better than just logging errors? */
+ status = driver->resume(intf);
+ if (intf->dev.power.power_state != PMSG_ON || status)
+ dev_dbg(&intf->dev, "resume fail, state %d code %d\n",
+ intf->dev.power.power_state, status);
+ } else
+ dev->power.power_state = PMSG_ON;
- if (driver->resume)
- return driver->resume(intf);
return 0;
}
-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now.
http://ads.osdn.com/?ad_id=6595&alloc_id=14396&op=click
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel