This patch (as818b) simplifies autosuspend processing by keeping track
of the number of unsuspended children of each USB hub.  This will
permit us to avoid a good deal of unnecessary work all the time; we
will no longer have to create a bunch of workqueue entries to carry
out autosuspend requests, only to have them fail because one of the
hub's children isn't suspended.

The basic idea is simple.  There already is a usage counter in the
usb_device structure for preventing autosuspends.  The patch just
increments that counter for every unsuspended child.  There's only one
tricky part: When a device disconnects we need to remember whether it
was suspended at the time (leave the counter alone) or not (decrement
the counter).

Signed-off-by: Alan Stern <[EMAIL PROTECTED]>

---

This is a slight revision of as818 (number 5/6, posted a couple of days
ago).  I forgot that it's possible for a resume to fail while leaving the
device still connected.

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
@@ -1039,6 +1039,8 @@ static void recursively_mark_NOTATTACHED
                if (udev->children[i])
                        recursively_mark_NOTATTACHED(udev->children[i]);
        }
+       if (udev->state == USB_STATE_SUSPENDED)
+               udev->discon_suspended = 1;
        udev->state = USB_STATE_NOTATTACHED;
 }
 
@@ -1228,6 +1230,14 @@ void usb_disconnect(struct usb_device **
        *pdev = NULL;
        spin_unlock_irq(&device_state_lock);
 
+       /* Decrement the parent's count of unsuspended children */
+       if (udev->parent) {
+               usb_pm_lock(udev);
+               if (!udev->discon_suspended)
+                       usb_autosuspend_device(udev->parent, 1);
+               usb_pm_unlock(udev);
+       }
+
        put_device(&udev->dev);
 }
 
@@ -1354,6 +1364,10 @@ static int __usb_new_device(void *void_d
                goto fail;
        }
 
+       /* Increment the parent's count of unsuspended children */
+       if (udev->parent)
+               usb_autoresume_device(udev->parent, 1);
+
 exit:
        module_put(THIS_MODULE);
        return err;
Index: usb-2.6/drivers/usb/core/driver.c
===================================================================
--- usb-2.6.orig/drivers/usb/core/driver.c
+++ usb-2.6/drivers/usb/core/driver.c
@@ -1052,7 +1052,7 @@ int usb_suspend_both(struct usb_device *
 
        /* If the suspend succeeded, propagate it up the tree */
        } else if (parent)
-               usb_autosuspend_device(parent, 0);
+               usb_autosuspend_device(parent, 1);
 
        // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
        return status;
@@ -1100,9 +1100,25 @@ int usb_resume_both(struct usb_device *u
        /* Propagate the resume up the tree, if necessary */
        if (udev->state == USB_STATE_SUSPENDED) {
                if (parent) {
-                       usb_pm_lock(parent);
-                       parent->auto_pm = 1;
-                       status = usb_resume_both(parent);
+                       status = usb_autoresume_device(parent, 1);
+                       if (status == 0) {
+                               status = usb_resume_device(udev);
+                               if (status) {
+                                       usb_autosuspend_device(parent, 1);
+
+                                       /* It's possible usb_resume_device()
+                                        * failed after the port was
+                                        * unsuspended, causing udev to be
+                                        * logically disconnected.  We don't
+                                        * want usb_disconnect() to autosuspend
+                                        * the parent again, so tell it that
+                                        * udev disconnected while still
+                                        * suspended. */
+                                       if (udev->state ==
+                                                       USB_STATE_NOTATTACHED)
+                                               udev->discon_suspended = 1;
+                               }
+                       }
                } else {
 
                        /* We can't progagate beyond the USB subsystem,
@@ -1111,11 +1127,9 @@ int usb_resume_both(struct usb_device *u
                        if (udev->dev.parent->power.power_state.event !=
                                        PM_EVENT_ON)
                                status = -EHOSTUNREACH;
-               }
-               if (status == 0)
-                       status = usb_resume_device(udev);
-               if (parent)
-                       usb_pm_unlock(parent);
+                       else
+                               status = usb_resume_device(udev);
+               }
        } else {
 
                /* Needed only for setting udev->dev.power.power_state.event
@@ -1123,8 +1137,6 @@ int usb_resume_both(struct usb_device *u
                status = usb_resume_device(udev);
        }
 
-       /* Now the parent won't suspend until we are finished */
-
        if (status == 0 && udev->actconfig) {
                for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
                        intf = udev->actconfig->interface[i];
Index: usb-2.6/include/linux/usb.h
===================================================================
--- usb-2.6.orig/include/linux/usb.h
+++ usb-2.6/include/linux/usb.h
@@ -363,6 +363,7 @@ struct usb_device {
        u8 portnum;                     /* Parent port number (origin 1) */
        u8 level;                       /* Number of USB hub ancestors */
 
+       unsigned discon_suspended:1;    /* Disconnected while suspended */
        unsigned have_langid:1;         /* whether string_langid is valid */
        int string_langid;              /* language ID for strings */
 


-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to