Below is a composite of three patches pending for the usb-storage driver.  
Together they provide support for autosuspend.  The inactivity timeout is 
controlled by a module parameter named "autosuspend"; its value gives the 
timeout in seconds (0 means no autosuspend).

If anyone can try this out and let me know how it works, I would 
appreciate it.  If you are using a UHCI host controller, then you should 
probably also apply this patch:

http://marc.theaimsgroup.com/?l=linux-usb-devel&m=110193921701118&w=2

Thanks,

Alan Stern



===== drivers/usb/storage/datafab.c 1.36 vs edited =====
--- 1.36/drivers/usb/storage/datafab.c  2004-10-11 13:52:07 -04:00
+++ edited/drivers/usb/storage/datafab.c        2004-12-02 12:16:51 -05:00
@@ -57,9 +57,9 @@
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 
+#include "usb.h"
 #include "transport.h"
 #include "protocol.h"
-#include "usb.h"
 #include "debug.h"
 #include "datafab.h"
 
===== drivers/usb/storage/dpcm.c 1.6 vs edited =====
--- 1.6/drivers/usb/storage/dpcm.c      2004-08-24 10:55:46 -04:00
+++ edited/drivers/usb/storage/dpcm.c   2004-12-02 12:16:51 -05:00
@@ -34,9 +34,9 @@
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
 
+#include "usb.h"
 #include "transport.h"
 #include "protocol.h"
-#include "usb.h"
 #include "debug.h"
 #include "dpcm.h"
 #include "sddr09.h"
===== drivers/usb/storage/freecom.c 1.41 vs edited =====
--- 1.41/drivers/usb/storage/freecom.c  2004-11-15 12:27:17 -05:00
+++ edited/drivers/usb/storage/freecom.c        2004-12-02 12:16:51 -05:00
@@ -34,9 +34,9 @@
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 
+#include "usb.h"
 #include "transport.h"
 #include "protocol.h"
-#include "usb.h"
 #include "debug.h"
 #include "freecom.h"
 
===== drivers/usb/storage/initializers.c 1.12 vs edited =====
--- 1.12/drivers/usb/storage/initializers.c     2003-08-11 12:21:44 -04:00
+++ edited/drivers/usb/storage/initializers.c   2004-12-02 12:16:51 -05:00
@@ -39,6 +39,8 @@
 
 #include <linux/sched.h>
 #include <linux/errno.h>
+
+#include "usb.h"
 #include "initializers.h"
 #include "debug.h"
 #include "transport.h"
===== drivers/usb/storage/isd200.c 1.58 vs edited =====
--- 1.58/drivers/usb/storage/isd200.c   2004-11-22 13:42:02 -05:00
+++ edited/drivers/usb/storage/isd200.c 2004-12-02 12:16:51 -05:00
@@ -54,9 +54,9 @@
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
 
+#include "usb.h"
 #include "transport.h"
 #include "protocol.h"
-#include "usb.h"
 #include "debug.h"
 #include "scsiglue.h"
 #include "isd200.h"
===== drivers/usb/storage/jumpshot.c 1.39 vs edited =====
--- 1.39/drivers/usb/storage/jumpshot.c 2004-10-11 13:52:07 -04:00
+++ edited/drivers/usb/storage/jumpshot.c       2004-12-02 12:16:51 -05:00
@@ -54,9 +54,9 @@
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 
+#include "usb.h"
 #include "transport.h"
 #include "protocol.h"
-#include "usb.h"
 #include "debug.h"
 #include "jumpshot.h"
 
===== drivers/usb/storage/protocol.c 1.25 vs edited =====
--- 1.25/drivers/usb/storage/protocol.c 2004-10-20 12:38:15 -04:00
+++ edited/drivers/usb/storage/protocol.c       2004-12-02 12:16:51 -05:00
@@ -47,8 +47,9 @@
 #include <linux/highmem.h>
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
-#include "protocol.h"
+
 #include "usb.h"
+#include "protocol.h"
 #include "debug.h"
 #include "scsiglue.h"
 #include "transport.h"
===== drivers/usb/storage/scsiglue.c 1.90 vs edited =====
--- 1.90/drivers/usb/storage/scsiglue.c 2004-11-22 13:42:02 -05:00
+++ edited/drivers/usb/storage/scsiglue.c       2004-12-02 12:17:03 -05:00
@@ -53,10 +53,9 @@
 #include <scsi/scsi_devinfo.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_eh.h>
-#include <scsi/scsi_host.h>
 
-#include "scsiglue.h"
 #include "usb.h"
+#include "scsiglue.h"
 #include "debug.h"
 #include "transport.h"
 #include "protocol.h"
@@ -83,7 +82,7 @@
 
 static int slave_configure(struct scsi_device *sdev)
 {
-       struct us_data *us = (struct us_data *) sdev->host->hostdata[0];
+       struct us_data *us = host_to_us(sdev->host);
 
        /* Scatter-gather buffers (all but the last) must have a length
         * divisible by the bulk maxpacket size.  Otherwise a data packet
@@ -167,10 +166,9 @@
 static int queuecommand(struct scsi_cmnd *srb,
                        void (*done)(struct scsi_cmnd *))
 {
-       struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
+       struct us_data *us = host_to_us(srb->device->host);
 
        US_DEBUGP("%s called\n", __FUNCTION__);
-       srb->host_scribble = (unsigned char *)us;
 
        /* check for state-transition errors */
        if (us->srb != NULL) {
@@ -201,10 +199,9 @@
 
 /* Command timeout and abort */
 /* This is always called with scsi_lock(srb->host) held */
-static int command_abort(struct scsi_cmnd *srb )
+static int command_abort(struct scsi_cmnd *srb)
 {
-       struct Scsi_Host *host = srb->device->host;
-       struct us_data *us = (struct us_data *) host->hostdata[0];
+       struct us_data *us = host_to_us(srb->device->host);
 
        US_DEBUGP("%s called\n", __FUNCTION__);
 
@@ -224,13 +221,13 @@
                set_bit(US_FLIDX_ABORTING, &us->flags);
                usb_stor_stop_transport(us);
        }
-       scsi_unlock(host);
+       scsi_unlock(us_to_host(us));
 
        /* Wait for the aborted command to finish */
        wait_for_completion(&us->notify);
 
        /* Reacquire the lock and allow USB transfers to resume */
-       scsi_lock(host);
+       scsi_lock(us_to_host(us));
        clear_bit(US_FLIDX_ABORTING, &us->flags);
        clear_bit(US_FLIDX_TIMED_OUT, &us->flags);
        return SUCCESS;
@@ -241,24 +238,26 @@
 /* This is always called with scsi_lock(srb->host) held */
 static int device_reset(struct scsi_cmnd *srb)
 {
-       struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
+       struct us_data *us = host_to_us(srb->device->host);
        int result;
 
        US_DEBUGP("%s called\n", __FUNCTION__);
 
-       scsi_unlock(srb->device->host);
+       scsi_unlock(us_to_host(us));
 
        /* lock the device pointers and do the reset */
        down(&(us->dev_semaphore));
        if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
                result = FAILED;
                US_DEBUGP("No reset during disconnect\n");
-       } else
+       } else {
                result = us->transport_reset(us);
+               usb_stor_autosuspend_event(us);
+       }
        up(&(us->dev_semaphore));
 
        /* lock the host for the return */
-       scsi_lock(srb->device->host);
+       scsi_lock(us_to_host(us));
        return result;
 }
 
@@ -268,12 +267,12 @@
 /* This is always called with scsi_lock(srb->host) held */
 static int bus_reset(struct scsi_cmnd *srb)
 {
-       struct us_data *us = (struct us_data *)srb->device->host->hostdata[0];
+       struct us_data *us = host_to_us(srb->device->host);
        int result, rc;
 
        US_DEBUGP("%s called\n", __FUNCTION__);
 
-       scsi_unlock(srb->device->host);
+       scsi_unlock(us_to_host(us));
 
        /* The USB subsystem doesn't handle synchronisation between
         * a device's several drivers. Therefore we reset only devices
@@ -283,7 +282,7 @@
        if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
                result = -EIO;
                US_DEBUGP("No reset during disconnect\n");
-       } else if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1) {
+       } else if (us->flags & US_FL_COMPOSITE) {
                result = -EBUSY;
                US_DEBUGP("Refusing to reset a multi-interface device\n");
        } else {
@@ -296,12 +295,13 @@
                        if (rc)
                                usb_unlock_device(us->pusb_dev);
                        US_DEBUGP("usb_reset_device returns %d\n", result);
+                       usb_stor_autosuspend_event(us);
                }
        }
        up(&(us->dev_semaphore));
 
        /* lock the host for the return */
-       scsi_lock(srb->device->host);
+       scsi_lock(us_to_host(us));
        return result < 0 ? FAILED : SUCCESS;
 }
 
@@ -311,11 +311,12 @@
 void usb_stor_report_device_reset(struct us_data *us)
 {
        int i;
+       struct Scsi_Host *host = us_to_host(us);
 
-       scsi_report_device_reset(us->host, 0, 0);
+       scsi_report_device_reset(host, 0, 0);
        if (us->flags & US_FL_SCM_MULT_TARG) {
-               for (i = 1; i < us->host->max_id; ++i)
-                       scsi_report_device_reset(us->host, 0, i);
+               for (i = 1; i < host->max_id; ++i)
+                       scsi_report_device_reset(host, 0, i);
        }
 }
 
@@ -330,20 +331,18 @@
 #define DO_FLAG(a) \
        do { if (us->flags & US_FL_##a) pos += sprintf(pos, " " #a); } while(0)
 
-static int proc_info (struct Scsi_Host *hostptr, char *buffer, char **start, 
off_t offset,
-               int length, int inout)
+static int proc_info (struct Scsi_Host *host, char *buffer,
+               char **start, off_t offset, int length, int inout)
 {
-       struct us_data *us;
+       struct us_data *us = host_to_us(host);
        char *pos = buffer;
 
        /* if someone is sending us data, just throw it away */
        if (inout)
                return length;
 
-       us = (struct us_data*)hostptr->hostdata[0];
-
        /* print the controller name */
-       SPRINTF("   Host scsi%d: usb-storage\n", hostptr->host_no);
+       SPRINTF("   Host scsi%d: usb-storage\n", host->host_no);
 
        /* print product, vendor, and serial number strings */
        SPRINTF("       Vendor: %s\n", us->vendor);
@@ -362,6 +361,7 @@
                DO_FLAG(SCM_MULT_TARG);
                DO_FLAG(FIX_INQUIRY);
                DO_FLAG(FIX_CAPACITY);
+               DO_FLAG(IGNORE_RESIDUE);
 
                *(pos++) = '\n';
        }
===== drivers/usb/storage/scsiglue.h 1.13 vs edited =====
--- 1.13/drivers/usb/storage/scsiglue.h 2004-08-24 10:55:47 -04:00
+++ edited/drivers/usb/storage/scsiglue.h       2004-12-02 12:16:51 -05:00
@@ -41,10 +41,8 @@
 #ifndef _SCSIGLUE_H_
 #define _SCSIGLUE_H_
 
-#include <scsi/scsi_host.h>
-
 struct us_data;
-struct scsi_cmnd;
+struct scsi_host_template;
 
 extern void usb_stor_report_device_reset(struct us_data *us);
 
===== drivers/usb/storage/sddr09.c 1.48 vs edited =====
--- 1.48/drivers/usb/storage/sddr09.c   2004-10-11 13:52:08 -04:00
+++ edited/drivers/usb/storage/sddr09.c 2004-12-02 12:16:51 -05:00
@@ -48,9 +48,9 @@
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 
+#include "usb.h"
 #include "transport.h"
 #include "protocol.h"
-#include "usb.h"
 #include "debug.h"
 #include "sddr09.h"
 
===== drivers/usb/storage/sddr55.c 1.23 vs edited =====
--- 1.23/drivers/usb/storage/sddr55.c   2004-10-11 13:52:08 -04:00
+++ edited/drivers/usb/storage/sddr55.c 2004-12-02 12:16:51 -05:00
@@ -31,9 +31,9 @@
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 
+#include "usb.h"
 #include "transport.h"
 #include "protocol.h"
-#include "usb.h"
 #include "debug.h"
 #include "sddr55.h"
 
===== drivers/usb/storage/shuttle_usbat.c 1.35 vs edited =====
--- 1.35/drivers/usb/storage/shuttle_usbat.c    2004-08-24 10:55:48 -04:00
+++ edited/drivers/usb/storage/shuttle_usbat.c  2004-12-02 12:16:51 -05:00
@@ -47,9 +47,9 @@
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 
+#include "usb.h"
 #include "transport.h"
 #include "protocol.h"
-#include "usb.h"
 #include "debug.h"
 #include "shuttle_usbat.h"
 
===== drivers/usb/storage/transport.c 1.153 vs edited =====
--- 1.153/drivers/usb/storage/transport.c       2004-11-22 13:42:02 -05:00
+++ edited/drivers/usb/storage/transport.c      2004-12-02 12:16:51 -05:00
@@ -54,10 +54,10 @@
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
 
+#include "usb.h"
 #include "transport.h"
 #include "protocol.h"
 #include "scsiglue.h"
-#include "usb.h"
 #include "debug.h"
 
 
@@ -1118,11 +1118,11 @@
         * RESETTING bit, and clear the ABORTING bit so that the reset
         * may proceed.
         */
-       scsi_lock(us->host);
+       scsi_lock(us_to_host(us));
        usb_stor_report_device_reset(us);
        set_bit(US_FLIDX_RESETTING, &us->flags);
        clear_bit(US_FLIDX_ABORTING, &us->flags);
-       scsi_unlock(us->host);
+       scsi_unlock(us_to_host(us));
 
        /* A 20-second timeout may seem rather long, but a LaCie
         * StudioDrive USB2 device takes 16+ seconds to get going
===== drivers/usb/storage/transport.h 1.44 vs edited =====
--- 1.44/drivers/usb/storage/transport.h        2004-10-11 13:52:09 -04:00
+++ edited/drivers/usb/storage/transport.h      2004-12-02 12:16:51 -05:00
@@ -43,8 +43,8 @@
 
 #include <linux/config.h>
 #include <linux/blkdev.h>
-#include "usb.h"
 
+struct us_data;
 struct scsi_cmnd;
 
 /* Protocols */
===== drivers/usb/storage/usb.c 1.130 vs edited =====
--- 1.130/drivers/usb/storage/usb.c     2004-11-22 13:42:02 -05:00
+++ edited/drivers/usb/storage/usb.c    2004-12-02 12:17:03 -05:00
@@ -51,6 +51,9 @@
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/suspend.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
@@ -89,10 +92,6 @@
 #endif
 
 
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-
 /* Some informational data */
 MODULE_AUTHOR("Matthew Dharm <[EMAIL PROTECTED]>");
 MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
@@ -102,6 +101,24 @@
 module_param(delay_use, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
 
+#ifdef CONFIG_USB_SUSPEND
+static unsigned int autosuspend = 0;
+module_param(autosuspend, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(autosuspend,
+               "timeout in seconds for autosuspending an inactive device "
+               "(0 for no autosuspend)");
+
+static int storage_suspend(struct usb_interface *iface, u32 state);
+static int storage_resume(struct usb_interface *iface);
+#endif
+
+
+/* These are used to make sure the module doesn't unload before all the
+ * threads have exited.
+ */
+static atomic_t total_threads = ATOMIC_INIT(0);
+static DECLARE_COMPLETION(threads_gone);
+
 
 static int storage_probe(struct usb_interface *iface,
                         const struct usb_device_id *id);
@@ -234,9 +251,131 @@
        .name =         "usb-storage",
        .probe =        storage_probe,
        .disconnect =   storage_disconnect,
+#ifdef CONFIG_USB_SUSPEND
+       .suspend =      storage_suspend,
+       .resume =       storage_resume,
+#endif
        .id_table =     storage_usb_ids,
 };
 
+
+#ifdef CONFIG_USB_SUSPEND
+
+static int storage_suspend(struct usb_interface *iface, u32 state)
+{
+       struct us_data *us = usb_get_intfdata(iface);
+
+       down(&us->dev_semaphore);
+       if (!test_and_set_bit(US_FLIDX_SUSPENDED, &us->flags)) {
+               US_DEBUGP("%s\n", __FUNCTION__);
+               iface->dev.power.power_state = state;
+       }
+       up(&us->dev_semaphore);
+       return 0;
+}
+
+static int storage_resume(struct usb_interface *iface)
+{
+       struct us_data *us = usb_get_intfdata(iface);
+
+       down(&us->dev_semaphore);
+       if (test_and_clear_bit(US_FLIDX_SUSPENDED, &us->flags)) {
+               US_DEBUGP("%s\n", __FUNCTION__);
+               iface->dev.power.power_state = PM_SUSPEND_ON;
+       }
+       up(&us->dev_semaphore);
+       return 0;
+}
+
+/* Return 1 if it's time to autosuspend; otherwise set the timer if needed */
+static int autosuspend_update(struct us_data *us)
+{
+       unsigned long timeout;
+
+       if (autosuspend == 0 ||
+                       test_bit(US_FLIDX_DISCONNECTING, &us->flags) ||
+                       test_bit(US_FLIDX_SUSPENDED, &us->flags) ||
+               //      !us->hdev->dev.power.wakeup_enabled ||
+                       us->flags & US_FL_COMPOSITE)
+               return 0;
+       timeout = us->last_event_time + autosuspend * HZ;
+       if (time_before(jiffies, timeout)) {
+               mod_timer(&us->autosuspend_timer, timeout);
+               return 0;
+       }
+       return 1;
+}
+
+/* An autosuspend event occurred so restart the timer.
+ * us->dev_semaphore should be locked. */
+void usb_stor_autosuspend_event(struct us_data *us)
+{
+       us->last_event_time = jiffies;
+       autosuspend_update(us);
+}
+
+/* It may be time to autosuspend.  Do it if everything is right.  Must
+ * be in process context (able to sleep) with us->dev_semaphore unlocked. */
+static inline void autosuspend_try(struct us_data *us)
+{
+       if (autosuspend_update(us)) {
+               US_DEBUGP("autosuspend\n");
+               usb_suspend_device(us->pusb_dev, PM_SUSPEND_MEM);
+
+               /* If the suspend failed, trying again later isn't
+                * likely to help.  Just let it go... */
+       }
+}
+
+/* It may be time to autosuspend.  Tell the control thread if we're ready. */
+static void autosuspend_timer_func(unsigned long __us)
+{
+       struct us_data *us = (struct us_data *) __us;
+
+       if (autosuspend_update(us)) {
+
+               /* Invoking the control thread with us->srb == NULL will
+                * cause it to try an autosuspend. */
+               up(&us->sema);
+       }
+}
+
+/* Autoresume on demand.  Returns nonzero if the device has disconnected.
+ * us->dev_semaphore must be locked. */
+static inline int autoresume_try(struct us_data *us)
+{
+       if (test_bit(US_FLIDX_SUSPENDED, &us->flags)) {
+               up(&us->dev_semaphore);
+               US_DEBUGP("autoresume\n");
+               usb_resume_device(us->pusb_dev);
+               down(&us->dev_semaphore);
+               return test_bit(US_FLIDX_DISCONNECTING, &us->flags);
+       }
+       return 0;
+}
+
+static inline void autosuspend_activate(struct us_data *us)
+{
+       init_timer(&us->autosuspend_timer);
+       us->autosuspend_timer.function = autosuspend_timer_func;
+       us->autosuspend_timer.data = (unsigned long) us;
+}
+
+static inline void autosuspend_quiesce(struct us_data *us)
+{
+       del_timer_sync(&us->autosuspend_timer);
+}
+
+#else  /* #ifdef CONFIG_USB_SUSPEND */
+
+static inline void autosuspend_try(struct us_data *us) {}
+static inline int autoresume_try(struct us_data *us) { return 0; }
+static inline void autosuspend_activate(struct us_data *us) {}
+static inline void autosuspend_quiesce(struct us_data *us) {}
+
+#endif
+
+
 /*
  * fill_inquiry_response takes an unsigned char array (which must
  * be at least 36 characters) and populates the vendor name,
@@ -281,7 +420,7 @@
 static int usb_stor_control_thread(void * __us)
 {
        struct us_data *us = (struct us_data *)__us;
-       struct Scsi_Host *host = us->host;
+       struct Scsi_Host *host = us_to_host(us);
 
        lock_kernel();
 
@@ -290,11 +429,13 @@
         * so get rid of all our resources.
         */
        daemonize("usb-storage");
-
        current->flags |= PF_NOFREEZE;
-
        unlock_kernel();
 
+       /* acquire a reference to the host, so it won't be deallocated
+        * until we're ready to exit */
+       scsi_host_get(host);
+
        /* signal that we've started the thread */
        complete(&(us->notify));
 
@@ -308,15 +449,16 @@
                /* lock the device pointers */
                down(&(us->dev_semaphore));
 
-               /* if us->srb is NULL, we are being asked to exit */
-               if (us->srb == NULL) {
-                       US_DEBUGP("-- exit command received\n");
-                       up(&(us->dev_semaphore));
+               /* if the device has disconnected, we are free to exit */
+               if (test_bit(US_FLIDX_DISCONNECTING, &us->flags))
                        break;
-               }
 
-               /* lock access to the state */
-               scsi_lock(host);
+               /* is this an autosuspend request? */
+               if (!us->srb) {
+                       up(&us->dev_semaphore);
+                       autosuspend_try(us);
+                       continue;
+               }
 
                /* has the command timed out *already* ? */
                if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
@@ -324,14 +466,6 @@
                        goto SkipForAbort;
                }
 
-               /* don't do anything if we are disconnecting */
-               if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
-                       US_DEBUGP("No command during disconnect\n");
-                       goto SkipForDisconnect;
-               }
-
-               scsi_unlock(host);
-
                /* reject the command if the direction indicator 
                 * is UNKNOWN
                 */
@@ -369,12 +503,17 @@
                        us->srb->result = SAM_STAT_GOOD;
                }
 
+               /* autoresume if we are suspended */
+               else if (autoresume_try(us) != 0)
+                       break;
+
                /* we've got a command, let's do it! */
                else {
                        US_DEBUG(usb_stor_show_command(us->srb));
                        us->proto_handler(us->srb, us);
                }
 
+SkipForAbort:
                /* lock access to the state */
                scsi_lock(host);
 
@@ -384,7 +523,6 @@
                                   us->srb->result);
                        us->srb->scsi_done(us->srb);
                } else {
-SkipForAbort:
                        US_DEBUGP("scsi command aborted\n");
                }
 
@@ -397,14 +535,18 @@
                        complete(&(us->notify));
 
                /* finished working on this command */
-SkipForDisconnect:
                us->srb = NULL;
                scsi_unlock(host);
+               usb_stor_autosuspend_event(us);
 
                /* unlock the device pointers */
                up(&(us->dev_semaphore));
        } /* for (;;) */
 
+       up(&us->dev_semaphore);
+       US_DEBUGP("-- exiting\n");
+       scsi_host_put(host);
+
        /* notify the exit routine that we're actually exiting now 
         *
         * complete()/wait_for_completion() is similar to up()/down(),
@@ -419,7 +561,7 @@
         * This is important in preemption kernels, which transfer the flow
         * of execution immediately upon a complete().
         */
-       complete_and_exit(&(us->notify), 0);
+       complete_and_exit(&threads_gone, 0);
 }      
 
 /***********************************************************************
@@ -481,6 +623,8 @@
                        idesc->bInterfaceProtocol :
                        unusual_dev->useTransport;
        us->flags = unusual_dev->flags;
+       if (us->pusb_dev->actconfig->desc.bNumInterfaces != 1)
+               us->flags |= US_FL_COMPOSITE;
 
        /* Log a message if a non-generic unusual_dev entry contains an
         * unnecessary subclass or protocol override.  This may stimulate
@@ -783,20 +927,6 @@
 
        up(&us->dev_semaphore);
 
-       /*
-        * Since this is a new device, we need to register a SCSI
-        * host definition with the higher SCSI layers.
-        */
-       us->host = scsi_host_alloc(&usb_stor_host_template, sizeof(us));
-       if (!us->host) {
-               printk(KERN_WARNING USB_STORAGE
-                       "Unable to allocate the scsi host\n");
-               return -EBUSY;
-       }
-
-       /* Set the hostdata to prepare for scanning */
-       us->host->hostdata[0] = (unsigned long) us;
-
        /* Start up our control thread */
        p = kernel_thread(usb_stor_control_thread, us, CLONE_VM);
        if (p < 0) {
@@ -805,10 +935,14 @@
                return p;
        }
        us->pid = p;
+       atomic_inc(&total_threads);
 
        /* Wait for the thread to start */
        wait_for_completion(&(us->notify));
 
+       /* Initialize the autosuspend stuff */
+       autosuspend_activate(us);
+
        return 0;
 }
 
@@ -817,33 +951,15 @@
 {
        US_DEBUGP("-- %s\n", __FUNCTION__);
 
-       /* Kill the control thread.  The SCSI host must already have been
-        * removed so it won't try to queue any more commands.
-        */
-       if (us->pid) {
-
-               /* Wait for the thread to be idle */
-               down(&us->dev_semaphore);
-               US_DEBUGP("-- sending exit command to thread\n");
-
-               /* If the SCSI midlayer queued a final command just before
-                * scsi_remove_host() was called, us->srb might not be
-                * NULL.  We can overwrite it safely, because the midlayer
-                * will not wait for the command to finish.  Also the
-                * control thread will already have been awakened.
-                * That's okay, an extra up() on us->sema won't hurt.
-                *
-                * Enqueue the command, wake up the thread, and wait for 
-                * notification that it has exited.
-                */
-               scsi_lock(us->host);
-               us->srb = NULL;
-               scsi_unlock(us->host);
-               up(&us->dev_semaphore);
+       /* Stop the autosuspend timer */
+       autosuspend_quiesce(us);
 
-               up(&us->sema);
-               wait_for_completion(&us->notify);
-       }
+       /* Tell the control thread to exit.  The SCSI host must
+        * already have been removed so it won't try to queue
+        * any more commands.
+        */
+       US_DEBUGP("-- sending exit command to thread\n");
+       up(&us->sema);
 
        /* Call the destructor routine, if it exists */
        if (us->extra_destructor) {
@@ -851,15 +967,9 @@
                us->extra_destructor(us->extra);
        }
 
-       /* Finish the host removal sequence */
-       if (us->host)
-               scsi_host_put(us->host);
-
        /* Free the extra data and the URB */
-       if (us->extra)
-               kfree(us->extra);
-       if (us->current_urb)
-               usb_free_urb(us->current_urb);
+       kfree(us->extra);
+       usb_free_urb(us->current_urb);
 
 }
 
@@ -878,9 +988,6 @@
 
        /* Remove our private data from the interface */
        usb_set_intfdata(us->pusb_intf, NULL);
-
-       /* Free the structure itself */
-       kfree(us);
 }
 
 /* Thread to carry out delayed SCSI-device scanning */
@@ -896,6 +1003,13 @@
        daemonize("usb-stor-scan");
        unlock_kernel();
 
+       /* Acquire a reference to the host, so it won't be deallocated
+        * until we're ready to exit */
+       scsi_host_get(us_to_host(us));
+
+       /* Signal that we've started the thread */
+       complete(&(us->notify));
+
        printk(KERN_DEBUG
                "usb-storage: device found at %d\n", us->pusb_dev->devnum);
 
@@ -904,7 +1018,7 @@
                printk(KERN_DEBUG "usb-storage: waiting for device "
                                "to settle before scanning\n");
 retry:
-               wait_event_interruptible_timeout(us->scsi_scan_wait,
+               wait_event_interruptible_timeout(us->dev_reset_wait,
                                test_bit(US_FLIDX_DISCONNECTING, &us->flags),
                                delay_use * HZ);
                if (current->flags & PF_FREEZE) {
@@ -915,11 +1029,14 @@
 
        /* If the device is still connected, perform the scanning */
        if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
-               scsi_scan_host(us->host);
+               scsi_scan_host(us_to_host(us));
                printk(KERN_DEBUG "usb-storage: device scan complete\n");
+
+               /* Should we unbind if no devices were detected? */
        }
 
-       complete_and_exit(&us->scsi_scan_done, 0);
+       scsi_host_put(us_to_host(us));
+       complete_and_exit(&threads_gone, 0);
 }
 
 
@@ -927,25 +1044,30 @@
 static int storage_probe(struct usb_interface *intf,
                         const struct usb_device_id *id)
 {
+       struct Scsi_Host *host;
        struct us_data *us;
        const int id_index = id - storage_usb_ids; 
        int result;
 
        US_DEBUGP("USB Mass Storage device detected\n");
 
-       /* Allocate the us_data structure and initialize the mutexes */
-       us = (struct us_data *) kmalloc(sizeof(*us), GFP_KERNEL);
-       if (!us) {
-               printk(KERN_WARNING USB_STORAGE "Out of memory\n");
+       /*
+        * Ask the SCSI layer to allocate a host structure, with extra
+        * space at the end for our private us_data structure.
+        */
+       host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us));
+       if (!host) {
+               printk(KERN_WARNING USB_STORAGE
+                       "Unable to allocate the scsi host\n");
                return -ENOMEM;
        }
+
+       us = host_to_us(host);
        memset(us, 0, sizeof(struct us_data));
        init_MUTEX(&(us->dev_semaphore));
        init_MUTEX_LOCKED(&(us->sema));
        init_completion(&(us->notify));
        init_waitqueue_head(&us->dev_reset_wait);
-       init_waitqueue_head(&us->scsi_scan_wait);
-       init_completion(&us->scsi_scan_done);
 
        /* Associate the us_data structure with the USB device */
        result = associate_dev(us, intf);
@@ -998,7 +1120,7 @@
        result = usb_stor_acquire_resources(us);
        if (result)
                goto BadDevice;
-       result = scsi_add_host(us->host, &intf->dev);
+       result = scsi_add_host(host, &intf->dev);
        if (result) {
                printk(KERN_WARNING USB_STORAGE
                        "Unable to add the scsi host\n");
@@ -1010,17 +1132,23 @@
        if (result < 0) {
                printk(KERN_WARNING USB_STORAGE 
                       "Unable to start the device-scanning thread\n");
-               scsi_remove_host(us->host);
+               scsi_remove_host(host);
                goto BadDevice;
        }
+       atomic_inc(&total_threads);
+
+       /* Wait for the thread to start */
+       wait_for_completion(&(us->notify));
 
        return 0;
 
        /* We come here if there are any problems */
 BadDevice:
        US_DEBUGP("storage_probe() failed\n");
+       set_bit(US_FLIDX_DISCONNECTING, &us->flags);
        usb_stor_release_resources(us);
        dissociate_dev(us);
+       scsi_host_put(host);
        return result;
 }
 
@@ -1032,24 +1160,26 @@
        US_DEBUGP("storage_disconnect() called\n");
 
        /* Prevent new USB transfers, stop the current command, and
-        * interrupt a device-reset delay */
+        * interrupt a device-reset or SCSI-scan delay */
        set_bit(US_FLIDX_DISCONNECTING, &us->flags);
        usb_stor_stop_transport(us);
        wake_up(&us->dev_reset_wait);
 
-       /* Interrupt the SCSI-device-scanning thread's time delay, and
-        * wait for the thread to finish */
-       wake_up(&us->scsi_scan_wait);
-       wait_for_completion(&us->scsi_scan_done);
+       /* It doesn't matter if the SCSI-scanning thread is still running.
+        * The thread will exit when it sees the DISCONNECTING flag. */
 
        /* Wait for the current command to finish, then remove the host */
        down(&us->dev_semaphore);
        up(&us->dev_semaphore);
-       scsi_remove_host(us->host);
+       scsi_remove_host(us_to_host(us));
 
        /* Wait for everything to become idle and release all our resources */
        usb_stor_release_resources(us);
        dissociate_dev(us);
+
+       /* Drop our reference to the host; the SCSI core will free it
+        * (and "us" along with it) when the refcount becomes 0. */
+       scsi_host_put(us_to_host(us));
 }
 
 /***********************************************************************
@@ -1079,6 +1209,16 @@
         */
        US_DEBUGP("-- calling usb_deregister()\n");
        usb_deregister(&usb_storage_driver) ;
+
+       /* Don't return until all of our control and scanning threads
+        * have exited.  Since each thread signals threads_gone as its
+        * last act, we have to call wait_for_completion the right number
+        * of times.
+        */
+       while (atomic_read(&total_threads) > 0) {
+               wait_for_completion(&threads_gone);
+               atomic_dec(&total_threads);
+       }
 }
 
 module_init(usb_stor_init);
===== drivers/usb/storage/usb.h 1.66 vs edited =====
--- 1.66/drivers/usb/storage/usb.h      2004-11-22 13:42:02 -05:00
+++ edited/drivers/usb/storage/usb.h    2004-12-02 12:17:03 -05:00
@@ -48,6 +48,8 @@
 #include <linux/blkdev.h>
 #include <linux/smp_lock.h>
 #include <linux/completion.h>
+#include <linux/timer.h>
+#include <scsi/scsi_host.h>
 
 struct us_data;
 struct scsi_cmnd;
@@ -74,6 +76,7 @@
 #define US_FL_FIX_INQUIRY     0x00000040 /* INQUIRY response needs faking   */
 #define US_FL_FIX_CAPACITY    0x00000080 /* READ CAPACITY response too big  */
 #define US_FL_IGNORE_RESIDUE  0x00000100 /* reported residue is wrong      */
+#define US_FL_COMPOSITE       0x00000800 /* there are multiple interfaces   */
 
 /* Dynamic flag definitions: used in set_bit() etc. */
 #define US_FLIDX_URB_ACTIVE    18  /* 0x00040000  current_urb is in use  */
@@ -84,6 +87,7 @@
                                         (1UL << US_FLIDX_DISCONNECTING))
 #define US_FLIDX_RESETTING     22  /* 0x00400000  device reset in progress */
 #define US_FLIDX_TIMED_OUT     23  /* 0x00800000  SCSI midlayer timed out  */
+#define US_FLIDX_SUSPENDED     24  /* 0x01000000  device is suspended      */
 
 
 #define USB_STOR_STRING_LEN 32
@@ -138,7 +142,6 @@
        proto_cmnd              proto_handler;   /* protocol handler       */
 
        /* SCSI interfaces */
-       struct Scsi_Host        *host;           /* our dummy host data */
        struct scsi_cmnd        *srb;            /* current srb         */
 
        /* thread information */
@@ -156,14 +159,26 @@
        struct semaphore        sema;            /* to sleep thread on   */
        struct completion       notify;          /* thread begin/end     */
        wait_queue_head_t       dev_reset_wait;  /* wait during reset    */
-       wait_queue_head_t       scsi_scan_wait;  /* wait before scanning */
-       struct completion       scsi_scan_done;  /* scan thread end      */
 
        /* subdriver information */
        void                    *extra;          /* Any extra data          */
        extra_data_destructor   extra_destructor;/* extra data destructor   */
+
+#ifdef CONFIG_USB_SUSPEND
+       /* autosuspend support */
+       unsigned long           last_event_time;
+       struct timer_list       autosuspend_timer;
+#endif
 };
 
+/* Convert between us_data and the corresponding Scsi_Host */
+static struct Scsi_Host inline *us_to_host(struct us_data *us) {
+       return container_of((void *) us, struct Scsi_Host, hostdata);
+}
+static struct us_data inline *host_to_us(struct Scsi_Host *host) {
+       return (struct us_data *) host->hostdata;
+}
+
 /* The structure which defines our driver */
 extern struct usb_driver usb_storage_driver;
 
@@ -175,6 +190,13 @@
  * single queue element srb for write access */
 #define scsi_unlock(host)      spin_unlock_irq(host->host_lock)
 #define scsi_lock(host)                spin_lock_irq(host->host_lock)
+
+/* Function for reporting events that should delay autosuspend */
+#ifdef CONFIG_USB_SUSPEND
+extern void usb_stor_autosuspend_event(struct us_data *us);
+#else
+static inline void usb_stor_autosuspend_event(struct us_data *us) {}
+#endif
 
 
 /* Vendor ID list for devices that require special handling */



-------------------------------------------------------
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://productguide.itmanagersjournal.com/
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to