USB: usb-storage: add BAD_SENSE flag
[safe/jmp/linux-2.6] / drivers / usb / storage / usb.c
index cf3fc91..f5c0264 100644 (file)
@@ -1,13 +1,11 @@
 /* Driver for USB Mass Storage compliant devices
  *
- * $Id: usb.c,v 1.75 2002/04/22 03:39:43 mdharm Exp $
- *
  * Current development and maintenance by:
  *   (c) 1999-2003 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
  *
  * Developed with the assistance of:
  *   (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
- *   (c) 2003 Alan Stern (stern@rowland.harvard.edu)
+ *   (c) 2003-2009 Alan Stern (stern@rowland.harvard.edu)
  *
  * Initial work by:
  *   (c) 1999 Michael Gee (michael@linuxspecific.com)
  * 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+#ifdef CONFIG_USB_STORAGE_DEBUG
+#define DEBUG
+#endif
+
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/freezer.h>
 #include "debug.h"
 #include "initializers.h"
 
-#ifdef CONFIG_USB_STORAGE_USBAT
-#include "shuttle_usbat.h"
-#endif
-#ifdef CONFIG_USB_STORAGE_SDDR09
-#include "sddr09.h"
-#endif
-#ifdef CONFIG_USB_STORAGE_SDDR55
-#include "sddr55.h"
-#endif
-#ifdef CONFIG_USB_STORAGE_DPCM
-#include "dpcm.h"
-#endif
-#ifdef CONFIG_USB_STORAGE_FREECOM
-#include "freecom.h"
-#endif
-#ifdef CONFIG_USB_STORAGE_ISD200
-#include "isd200.h"
-#endif
-#ifdef CONFIG_USB_STORAGE_DATAFAB
-#include "datafab.h"
-#endif
-#ifdef CONFIG_USB_STORAGE_JUMPSHOT
-#include "jumpshot.h"
-#endif
-#ifdef CONFIG_USB_STORAGE_ONETOUCH
-#include "onetouch.h"
-#endif
-#ifdef CONFIG_USB_STORAGE_ALAUDA
-#include "alauda.h"
-#endif
-#ifdef CONFIG_USB_STORAGE_KARMA
-#include "karma.h"
-#endif
+#include "sierra_ms.h"
+#include "option_ms.h"
 
 /* Some informational data */
 MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
@@ -111,43 +82,15 @@ static unsigned int delay_use = 5;
 module_param(delay_use, uint, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(delay_use, "seconds to delay before using a new device");
 
-
-/* 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 char quirks[128];
+module_param_string(quirks, quirks, sizeof(quirks), S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks");
 
 
 /*
  * The entries in this table correspond, line for line,
- * with the entries of us_unusual_dev_list[].
+ * with the entries in usb_storage_usb_ids[], defined in usual-tables.c.
  */
-#ifndef CONFIG_USB_LIBUSUAL
-
-#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
-                   vendorName, productName,useProtocol, useTransport, \
-                   initFunction, flags) \
-{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin,bcdDeviceMax), \
-  .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
-
-#define USUAL_DEV(useProto, useTrans, useType) \
-{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \
-  .driver_info = (USB_US_TYPE_STOR<<24) }
-
-static struct usb_device_id storage_usb_ids [] = {
-
-#      include "unusual_devs.h"
-#undef UNUSUAL_DEV
-#undef USUAL_DEV
-       /* Terminating entry */
-       { }
-};
-
-MODULE_DEVICE_TABLE (usb, storage_usb_ids);
-#endif /* CONFIG_USB_LIBUSUAL */
-
-/* This is the list of devices we recognize, along with their flag data */
 
 /* The vendor name should be kept at eight characters or less, and
  * the product name should be kept at 16 characters or less. If a device
@@ -169,6 +112,8 @@ MODULE_DEVICE_TABLE (usb, storage_usb_ids);
        .initFunction = init_function,  \
 }
 
+#define COMPLIANT_DEV  UNUSUAL_DEV
+
 #define USUAL_DEV(use_protocol, use_transport, use_type) \
 { \
        .useProtocol = use_protocol,    \
@@ -177,24 +122,24 @@ MODULE_DEVICE_TABLE (usb, storage_usb_ids);
 
 static struct us_unusual_dev us_unusual_dev_list[] = {
 #      include "unusual_devs.h" 
-#      undef UNUSUAL_DEV
-#      undef USUAL_DEV
-
-       /* Terminating entry */
-       { NULL }
+       { }             /* Terminating entry */
 };
 
+#undef UNUSUAL_DEV
+#undef COMPLIANT_DEV
+#undef USUAL_DEV
+
 
 #ifdef CONFIG_PM       /* Minimal support for suspend and resume */
 
-static int storage_suspend(struct usb_interface *iface, pm_message_t message)
+int usb_stor_suspend(struct usb_interface *iface, pm_message_t message)
 {
        struct us_data *us = usb_get_intfdata(iface);
 
        /* Wait until no command is running */
        mutex_lock(&us->dev_mutex);
 
-       US_DEBUGP("%s\n", __FUNCTION__);
+       US_DEBUGP("%s\n", __func__);
        if (us->suspend_resume_hook)
                (us->suspend_resume_hook)(us, US_SUSPEND);
 
@@ -204,26 +149,28 @@ static int storage_suspend(struct usb_interface *iface, pm_message_t message)
        mutex_unlock(&us->dev_mutex);
        return 0;
 }
+EXPORT_SYMBOL_GPL(usb_stor_suspend);
 
-static int storage_resume(struct usb_interface *iface)
+int usb_stor_resume(struct usb_interface *iface)
 {
        struct us_data *us = usb_get_intfdata(iface);
 
        mutex_lock(&us->dev_mutex);
 
-       US_DEBUGP("%s\n", __FUNCTION__);
+       US_DEBUGP("%s\n", __func__);
        if (us->suspend_resume_hook)
                (us->suspend_resume_hook)(us, US_RESUME);
 
        mutex_unlock(&us->dev_mutex);
        return 0;
 }
+EXPORT_SYMBOL_GPL(usb_stor_resume);
 
-static int storage_reset_resume(struct usb_interface *iface)
+int usb_stor_reset_resume(struct usb_interface *iface)
 {
        struct us_data *us = usb_get_intfdata(iface);
 
-       US_DEBUGP("%s\n", __FUNCTION__);
+       US_DEBUGP("%s\n", __func__);
 
        /* Report the reset to the SCSI core */
        usb_stor_report_bus_reset(us);
@@ -232,6 +179,7 @@ static int storage_reset_resume(struct usb_interface *iface)
         * the device */
        return 0;
 }
+EXPORT_SYMBOL_GPL(usb_stor_reset_resume);
 
 #endif /* CONFIG_PM */
 
@@ -240,22 +188,23 @@ static int storage_reset_resume(struct usb_interface *iface)
  * a USB port reset, whether from this driver or a different one.
  */
 
-static int storage_pre_reset(struct usb_interface *iface)
+int usb_stor_pre_reset(struct usb_interface *iface)
 {
        struct us_data *us = usb_get_intfdata(iface);
 
-       US_DEBUGP("%s\n", __FUNCTION__);
+       US_DEBUGP("%s\n", __func__);
 
        /* Make sure no command runs during the reset */
        mutex_lock(&us->dev_mutex);
        return 0;
 }
+EXPORT_SYMBOL_GPL(usb_stor_pre_reset);
 
-static int storage_post_reset(struct usb_interface *iface)
+int usb_stor_post_reset(struct usb_interface *iface)
 {
        struct us_data *us = usb_get_intfdata(iface);
 
-       US_DEBUGP("%s\n", __FUNCTION__);
+       US_DEBUGP("%s\n", __func__);
 
        /* Report the reset to the SCSI core */
        usb_stor_report_bus_reset(us);
@@ -266,6 +215,7 @@ static int storage_post_reset(struct usb_interface *iface)
        mutex_unlock(&us->dev_mutex);
        return 0;
 }
+EXPORT_SYMBOL_GPL(usb_stor_post_reset);
 
 /*
  * fill_inquiry_response takes an unsigned char array (which must
@@ -308,36 +258,36 @@ void fill_inquiry_response(struct us_data *us, unsigned char *data,
 
        usb_stor_set_xfer_buf(data, data_len, us->srb);
 }
+EXPORT_SYMBOL_GPL(fill_inquiry_response);
 
 static int usb_stor_control_thread(void * __us)
 {
        struct us_data *us = (struct us_data *)__us;
        struct Scsi_Host *host = us_to_host(us);
 
-       current->flags |= PF_NOFREEZE;
-
        for(;;) {
                US_DEBUGP("*** thread sleeping.\n");
-               if(down_interruptible(&us->sema))
+               if (wait_for_completion_interruptible(&us->cmnd_ready))
                        break;
-                       
+
                US_DEBUGP("*** thread awakened.\n");
 
                /* lock the device pointers */
                mutex_lock(&(us->dev_mutex));
 
-               /* if the device has disconnected, we are free to exit */
-               if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
-                       US_DEBUGP("-- exiting\n");
+               /* lock access to the state */
+               scsi_lock(host);
+
+               /* When we are called with no command pending, we're done */
+               if (us->srb == NULL) {
+                       scsi_unlock(host);
                        mutex_unlock(&us->dev_mutex);
+                       US_DEBUGP("-- exiting\n");
                        break;
                }
 
-               /* lock access to the state */
-               scsi_lock(host);
-
                /* has the command timed out *already* ? */
-               if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
+               if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
                        us->srb->result = DID_ABORT << 16;
                        goto SkipForAbort;
                }
@@ -356,7 +306,7 @@ static int usb_stor_control_thread(void * __us)
                 * the maximum known LUN
                 */
                else if (us->srb->device->id && 
-                               !(us->flags & US_FL_SCM_MULT_TARG)) {
+                               !(us->fflags & US_FL_SCM_MULT_TARG)) {
                        US_DEBUGP("Bad target number (%d:%d)\n",
                                  us->srb->device->id, us->srb->device->lun);
                        us->srb->result = DID_BAD_TARGET << 16;
@@ -371,7 +321,7 @@ static int usb_stor_control_thread(void * __us)
                /* Handle those devices which need us to fake 
                 * their inquiry data */
                else if ((us->srb->cmnd[0] == INQUIRY) &&
-                           (us->flags & US_FL_FIX_INQUIRY)) {
+                           (us->fflags & US_FL_FIX_INQUIRY)) {
                        unsigned char data_ptr[36] = {
                            0x00, 0x80, 0x02, 0x02,
                            0x1F, 0x00, 0x00, 0x00};
@@ -390,12 +340,8 @@ static int usb_stor_control_thread(void * __us)
                /* lock access to the state */
                scsi_lock(host);
 
-               /* did the command already complete because of a disconnect? */
-               if (!us->srb)
-                       ;               /* nothing to do */
-
                /* indicate that the command is done */
-               else if (us->srb->result != DID_ABORT << 16) {
+               if (us->srb->result != DID_ABORT << 16) {
                        US_DEBUGP("scsi cmd done, result=0x%x\n", 
                                   us->srb->result);
                        us->srb->scsi_done(us->srb);
@@ -409,12 +355,12 @@ SkipForAbort:
                 * the TIMED_OUT flag, not srb->result == DID_ABORT, because
                 * the timeout might have occurred after the command had
                 * already completed with a different result code. */
-               if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
+               if (test_bit(US_FLIDX_TIMED_OUT, &us->dflags)) {
                        complete(&(us->notify));
 
                        /* Allow USB transfers to resume */
-                       clear_bit(US_FLIDX_ABORTING, &us->flags);
-                       clear_bit(US_FLIDX_TIMED_OUT, &us->flags);
+                       clear_bit(US_FLIDX_ABORTING, &us->dflags);
+                       clear_bit(US_FLIDX_TIMED_OUT, &us->dflags);
                }
 
                /* finished working on this command */
@@ -443,7 +389,7 @@ SkipForAbort:
 /* Associate our private data with the USB device */
 static int associate_dev(struct us_data *us, struct usb_interface *intf)
 {
-       US_DEBUGP("-- %s\n", __FUNCTION__);
+       US_DEBUGP("-- %s\n", __func__);
 
        /* Fill in the device-related fields */
        us->pusb_dev = interface_to_usbdev(intf);
@@ -474,29 +420,95 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf)
                US_DEBUGP("I/O buffer allocation failed\n");
                return -ENOMEM;
        }
-
-       us->sensebuf = kmalloc(US_SENSE_SIZE, GFP_KERNEL);
-       if (!us->sensebuf) {
-               US_DEBUGP("Sense buffer allocation failed\n");
-               return -ENOMEM;
-       }
        return 0;
 }
 
-/* Find an unusual_dev descriptor (always succeeds in the current code) */
-static struct us_unusual_dev *find_unusual(const struct usb_device_id *id)
+/* Works only for digits and letters, but small and fast */
+#define TOLOWER(x) ((x) | 0x20)
+
+/* Adjust device flags based on the "quirks=" module parameter */
+static void adjust_quirks(struct us_data *us)
 {
-       const int id_index = id - storage_usb_ids;
-       return &us_unusual_dev_list[id_index];
+       char *p;
+       u16 vid = le16_to_cpu(us->pusb_dev->descriptor.idVendor);
+       u16 pid = le16_to_cpu(us->pusb_dev->descriptor.idProduct);
+       unsigned f = 0;
+       unsigned int mask = (US_FL_SANE_SENSE | US_FL_FIX_CAPACITY |
+                       US_FL_CAPACITY_HEURISTICS | US_FL_IGNORE_DEVICE |
+                       US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 |
+                       US_FL_CAPACITY_OK | US_FL_IGNORE_RESIDUE |
+                       US_FL_SINGLE_LUN | US_FL_NO_WP_DETECT);
+
+       p = quirks;
+       while (*p) {
+               /* Each entry consists of VID:PID:flags */
+               if (vid == simple_strtoul(p, &p, 16) &&
+                               *p == ':' &&
+                               pid == simple_strtoul(p+1, &p, 16) &&
+                               *p == ':')
+                       break;
+
+               /* Move forward to the next entry */
+               while (*p) {
+                       if (*p++ == ',')
+                               break;
+               }
+       }
+       if (!*p)        /* No match */
+               return;
+
+       /* Collect the flags */
+       while (*++p && *p != ',') {
+               switch (TOLOWER(*p)) {
+               case 'a':
+                       f |= US_FL_SANE_SENSE;
+                       break;
+               case 'b':
+                       f |= US_FL_BAD_SENSE;
+                       break;
+               case 'c':
+                       f |= US_FL_FIX_CAPACITY;
+                       break;
+               case 'h':
+                       f |= US_FL_CAPACITY_HEURISTICS;
+                       break;
+               case 'i':
+                       f |= US_FL_IGNORE_DEVICE;
+                       break;
+               case 'l':
+                       f |= US_FL_NOT_LOCKABLE;
+                       break;
+               case 'm':
+                       f |= US_FL_MAX_SECTORS_64;
+                       break;
+               case 'o':
+                       f |= US_FL_CAPACITY_OK;
+                       break;
+               case 'r':
+                       f |= US_FL_IGNORE_RESIDUE;
+                       break;
+               case 's':
+                       f |= US_FL_SINGLE_LUN;
+                       break;
+               case 'w':
+                       f |= US_FL_NO_WP_DETECT;
+                       break;
+               /* Ignore unrecognized flag characters */
+               }
+       }
+       us->fflags = (us->fflags & ~mask) | f;
+       dev_info(&us->pusb_intf->dev, "Quirks match for "
+                       "vid %04x pid %04x: %x\n",
+                       vid, pid, f);
 }
 
 /* Get the unusual_devs entries and the string descriptors */
-static int get_device_info(struct us_data *us, const struct usb_device_id *id)
+static int get_device_info(struct us_data *us, const struct usb_device_id *id,
+               struct us_unusual_dev *unusual_dev)
 {
        struct usb_device *dev = us->pusb_dev;
        struct usb_interface_descriptor *idesc =
                &us->pusb_intf->cur_altsetting->desc;
-       struct us_unusual_dev *unusual_dev = find_unusual(id);
 
        /* Store the entries */
        us->unusual_dev = unusual_dev;
@@ -506,9 +518,10 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id)
        us->protocol = (unusual_dev->useTransport == US_PR_DEVICE) ?
                        idesc->bInterfaceProtocol :
                        unusual_dev->useTransport;
-       us->flags = USB_US_ORIG_FLAGS(id->driver_info);
+       us->fflags = USB_US_ORIG_FLAGS(id->driver_info);
+       adjust_quirks(us);
 
-       if (us->flags & US_FL_IGNORE_DEVICE) {
+       if (us->fflags & US_FL_IGNORE_DEVICE) {
                printk(KERN_INFO USB_STORAGE "device ignored\n");
                return -ENODEV;
        }
@@ -518,7 +531,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id)
         * disable it if we're in full-speed
         */
        if (dev->speed != USB_SPEED_HIGH)
-               us->flags &= ~US_FL_GO_SLOW;
+               us->fflags &= ~US_FL_GO_SLOW;
 
        /* Log a message if a non-generic unusual_dev entry contains an
         * unnecessary subclass or protocol override.  This may stimulate
@@ -539,13 +552,14 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id)
                if (unusual_dev->useTransport != US_PR_DEVICE &&
                        us->protocol == idesc->bInterfaceProtocol)
                        msg += 2;
-               if (msg >= 0 && !(us->flags & US_FL_NEED_OVERRIDE))
+               if (msg >= 0 && !(us->fflags & US_FL_NEED_OVERRIDE))
                        printk(KERN_NOTICE USB_STORAGE "This device "
                                "(%04x,%04x,%04x S %02x P %02x)"
                                " has %s in unusual_devs.h (kernel"
                                " %s)\n"
                                "   Please send a copy of this message to "
-                               "<linux-usb-devel@lists.sourceforge.net>\n",
+                               "<linux-usb@vger.kernel.org> and "
+                               "<usb-storage@lists.one-eyed-alien.net>\n",
                                le16_to_cpu(ddesc->idVendor),
                                le16_to_cpu(ddesc->idProduct),
                                le16_to_cpu(ddesc->bcdDevice),
@@ -559,7 +573,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id)
 }
 
 /* Get the transport settings */
-static int get_transport(struct us_data *us)
+static void get_transport(struct us_data *us)
 {
        switch (us->protocol) {
        case US_PR_CB:
@@ -571,7 +585,7 @@ static int get_transport(struct us_data *us)
 
        case US_PR_CBI:
                us->transport_name = "Control/Bulk/Interrupt";
-               us->transport = usb_stor_CBI_transport;
+               us->transport = usb_stor_CB_transport;
                us->transport_reset = usb_stor_CB_reset;
                us->max_lun = 7;
                break;
@@ -581,100 +595,11 @@ static int get_transport(struct us_data *us)
                us->transport = usb_stor_Bulk_transport;
                us->transport_reset = usb_stor_Bulk_reset;
                break;
-
-#ifdef CONFIG_USB_STORAGE_USBAT
-       case US_PR_USBAT:
-               us->transport_name = "Shuttle USBAT";
-               us->transport = usbat_transport;
-               us->transport_reset = usb_stor_CB_reset;
-               us->max_lun = 1;
-               break;
-#endif
-
-#ifdef CONFIG_USB_STORAGE_SDDR09
-       case US_PR_EUSB_SDDR09:
-               us->transport_name = "EUSB/SDDR09";
-               us->transport = sddr09_transport;
-               us->transport_reset = usb_stor_CB_reset;
-               us->max_lun = 0;
-               break;
-#endif
-
-#ifdef CONFIG_USB_STORAGE_SDDR55
-       case US_PR_SDDR55:
-               us->transport_name = "SDDR55";
-               us->transport = sddr55_transport;
-               us->transport_reset = sddr55_reset;
-               us->max_lun = 0;
-               break;
-#endif
-
-#ifdef CONFIG_USB_STORAGE_DPCM
-       case US_PR_DPCM_USB:
-               us->transport_name = "Control/Bulk-EUSB/SDDR09";
-               us->transport = dpcm_transport;
-               us->transport_reset = usb_stor_CB_reset;
-               us->max_lun = 1;
-               break;
-#endif
-
-#ifdef CONFIG_USB_STORAGE_FREECOM
-       case US_PR_FREECOM:
-               us->transport_name = "Freecom";
-               us->transport = freecom_transport;
-               us->transport_reset = usb_stor_freecom_reset;
-               us->max_lun = 0;
-               break;
-#endif
-
-#ifdef CONFIG_USB_STORAGE_DATAFAB
-       case US_PR_DATAFAB:
-               us->transport_name  = "Datafab Bulk-Only";
-               us->transport = datafab_transport;
-               us->transport_reset = usb_stor_Bulk_reset;
-               us->max_lun = 1;
-               break;
-#endif
-
-#ifdef CONFIG_USB_STORAGE_JUMPSHOT
-       case US_PR_JUMPSHOT:
-               us->transport_name  = "Lexar Jumpshot Control/Bulk";
-               us->transport = jumpshot_transport;
-               us->transport_reset = usb_stor_Bulk_reset;
-               us->max_lun = 1;
-               break;
-#endif
-
-#ifdef CONFIG_USB_STORAGE_ALAUDA
-       case US_PR_ALAUDA:
-               us->transport_name  = "Alauda Control/Bulk";
-               us->transport = alauda_transport;
-               us->transport_reset = usb_stor_Bulk_reset;
-               us->max_lun = 1;
-               break;
-#endif
-
-#ifdef CONFIG_USB_STORAGE_KARMA
-       case US_PR_KARMA:
-               us->transport_name = "Rio Karma/Bulk";
-               us->transport = rio_karma_transport;
-               us->transport_reset = usb_stor_Bulk_reset;
-               break;
-#endif
-
-       default:
-               return -EIO;
        }
-       US_DEBUGP("Transport: %s\n", us->transport_name);
-
-       /* fix for single-lun devices */
-       if (us->flags & US_FL_SINGLE_LUN)
-               us->max_lun = 0;
-       return 0;
 }
 
 /* Get the protocol settings */
-static int get_protocol(struct us_data *us)
+static void get_protocol(struct us_data *us)
 {
        switch (us->subclass) {
        case US_SC_RBC:
@@ -684,19 +609,19 @@ static int get_protocol(struct us_data *us)
 
        case US_SC_8020:
                us->protocol_name = "8020i";
-               us->proto_handler = usb_stor_ATAPI_command;
+               us->proto_handler = usb_stor_pad12_command;
                us->max_lun = 0;
                break;
 
        case US_SC_QIC:
                us->protocol_name = "QIC-157";
-               us->proto_handler = usb_stor_qic157_command;
+               us->proto_handler = usb_stor_pad12_command;
                us->max_lun = 0;
                break;
 
        case US_SC_8070:
                us->protocol_name = "8070i";
-               us->proto_handler = usb_stor_ATAPI_command;
+               us->proto_handler = usb_stor_pad12_command;
                us->max_lun = 0;
                break;
 
@@ -709,19 +634,7 @@ static int get_protocol(struct us_data *us)
                us->protocol_name = "Uniform Floppy Interface (UFI)";
                us->proto_handler = usb_stor_ufi_command;
                break;
-
-#ifdef CONFIG_USB_STORAGE_ISD200
-       case US_SC_ISD200:
-               us->protocol_name = "ISD200 ATA/ATAPI";
-               us->proto_handler = isd200_ata_command;
-               break;
-#endif
-
-       default:
-               return -EIO;
        }
-       US_DEBUGP("Protocol: %s\n", us->protocol_name);
-       return 0;
 }
 
 /* Get the pipe settings */
@@ -769,12 +682,12 @@ static int get_pipes(struct us_data *us)
        us->send_ctrl_pipe = usb_sndctrlpipe(us->pusb_dev, 0);
        us->recv_ctrl_pipe = usb_rcvctrlpipe(us->pusb_dev, 0);
        us->send_bulk_pipe = usb_sndbulkpipe(us->pusb_dev,
-               ep_out->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+               usb_endpoint_num(ep_out));
        us->recv_bulk_pipe = usb_rcvbulkpipe(us->pusb_dev, 
-               ep_in->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+               usb_endpoint_num(ep_in));
        if (ep_int) {
                us->recv_intr_pipe = usb_rcvintpipe(us->pusb_dev,
-                       ep_int->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+                       usb_endpoint_num(ep_int));
                us->ep_bInterval = ep_int->bInterval;
        }
        return 0;
@@ -815,15 +728,14 @@ static int usb_stor_acquire_resources(struct us_data *us)
 /* Release all our dynamic resources */
 static void usb_stor_release_resources(struct us_data *us)
 {
-       US_DEBUGP("-- %s\n", __FUNCTION__);
+       US_DEBUGP("-- %s\n", __func__);
 
        /* Tell the control thread to exit.  The SCSI host must
-        * already have been removed so it won't try to queue
-        * any more commands.
+        * already have been removed and the DISCONNECTING flag set
+        * so that we won't accept any more commands.
         */
        US_DEBUGP("-- sending exit command to thread\n");
-       set_bit(US_FLIDX_DISCONNECTING, &us->flags);
-       up(&us->sema);
+       complete(&us->cmnd_ready);
        if (us->ctl_thread)
                kthread_stop(us->ctl_thread);
 
@@ -841,9 +753,7 @@ static void usb_stor_release_resources(struct us_data *us)
 /* Dissociate from the USB device */
 static void dissociate_dev(struct us_data *us)
 {
-       US_DEBUGP("-- %s\n", __FUNCTION__);
-
-       kfree(us->sensebuf);
+       US_DEBUGP("-- %s\n", __func__);
 
        /* Free the device-related DMA-mapped buffers */
        if (us->cr)
@@ -857,38 +767,36 @@ static void dissociate_dev(struct us_data *us)
        usb_set_intfdata(us->pusb_intf, NULL);
 }
 
-/* First stage of disconnect processing: stop all commands and remove
- * the host */
+/* First stage of disconnect processing: stop SCSI scanning,
+ * remove the host, and stop accepting new commands
+ */
 static void quiesce_and_remove_host(struct us_data *us)
 {
        struct Scsi_Host *host = us_to_host(us);
 
-       /* Prevent new USB transfers, stop the current command, and
-        * interrupt a SCSI-scan or device-reset delay */
-       scsi_lock(host);
-       set_bit(US_FLIDX_DISCONNECTING, &us->flags);
-       scsi_unlock(host);
-       usb_stor_stop_transport(us);
-       wake_up(&us->delay_wait);
-
-       /* It doesn't matter if the SCSI-scanning thread is still running.
-        * The thread will exit when it sees the DISCONNECTING flag. */
+       /* If the device is really gone, cut short reset delays */
+       if (us->pusb_dev->state == USB_STATE_NOTATTACHED)
+               set_bit(US_FLIDX_DISCONNECTING, &us->dflags);
 
-       /* queuecommand won't accept any new commands and the control
-        * thread won't execute a previously-queued command.  If there
-        * is such a command pending, complete it with an error. */
-       mutex_lock(&us->dev_mutex);
-       if (us->srb) {
-               us->srb->result = DID_NO_CONNECT << 16;
-               scsi_lock(host);
-               us->srb->scsi_done(us->srb);
-               us->srb = NULL;
-               scsi_unlock(host);
-       }
-       mutex_unlock(&us->dev_mutex);
+       /* Prevent SCSI-scanning (if it hasn't started yet)
+        * and wait for the SCSI-scanning thread to stop.
+        */
+       set_bit(US_FLIDX_DONT_SCAN, &us->dflags);
+       wake_up(&us->delay_wait);
+       wait_for_completion(&us->scanning_done);
 
-       /* Now we own no commands so it's safe to remove the SCSI host */
+       /* Removing the host will perform an orderly shutdown: caches
+        * synchronized, disks spun down, etc.
+        */
        scsi_remove_host(host);
+
+       /* Prevent any new commands from being accepted and cut short
+        * reset delays.
+        */
+       scsi_lock(host);
+       set_bit(US_FLIDX_DISCONNECTING, &us->dflags);
+       scsi_unlock(host);
+       wake_up(&us->delay_wait);
 }
 
 /* Second stage of disconnect processing: deallocate all resources */
@@ -907,53 +815,56 @@ static int usb_stor_scan_thread(void * __us)
 {
        struct us_data *us = (struct us_data *)__us;
 
-       printk(KERN_DEBUG
-               "usb-storage: device found at %d\n", us->pusb_dev->devnum);
+       dev_dbg(&us->pusb_intf->dev, "device found\n");
 
+       set_freezable();
        /* Wait for the timeout to expire or for a disconnect */
        if (delay_use > 0) {
-               printk(KERN_DEBUG "usb-storage: waiting for device "
-                               "to settle before scanning\n");
-retry:
-               wait_event_interruptible_timeout(us->delay_wait,
-                               test_bit(US_FLIDX_DISCONNECTING, &us->flags),
+               dev_dbg(&us->pusb_intf->dev, "waiting for device to settle "
+                               "before scanning\n");
+               wait_event_freezable_timeout(us->delay_wait,
+                               test_bit(US_FLIDX_DONT_SCAN, &us->dflags),
                                delay_use * HZ);
-               if (try_to_freeze())
-                       goto retry;
        }
 
        /* If the device is still connected, perform the scanning */
-       if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+       if (!test_bit(US_FLIDX_DONT_SCAN, &us->dflags)) {
 
                /* For bulk-only devices, determine the max LUN value */
                if (us->protocol == US_PR_BULK &&
-                               !(us->flags & US_FL_SINGLE_LUN)) {
+                               !(us->fflags & US_FL_SINGLE_LUN)) {
                        mutex_lock(&us->dev_mutex);
                        us->max_lun = usb_stor_Bulk_max_lun(us);
                        mutex_unlock(&us->dev_mutex);
                }
                scsi_scan_host(us_to_host(us));
-               printk(KERN_DEBUG "usb-storage: device scan complete\n");
+               dev_dbg(&us->pusb_intf->dev, "scan complete\n");
 
                /* Should we unbind if no devices were detected? */
        }
 
-       scsi_host_put(us_to_host(us));
-       complete_and_exit(&threads_gone, 0);
+       complete_and_exit(&us->scanning_done, 0);
 }
 
+static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf)
+{
+       struct usb_device *usb_dev = interface_to_usbdev(intf);
 
-/* Probe to see if we can drive a newly-connected USB device */
-static int storage_probe(struct usb_interface *intf,
-                        const struct usb_device_id *id)
+       if (usb_dev->bus->sg_tablesize) {
+               return usb_dev->bus->sg_tablesize;
+       }
+       return SG_ALL;
+}
+
+/* First part of general USB mass-storage probing */
+int usb_stor_probe1(struct us_data **pus,
+               struct usb_interface *intf,
+               const struct usb_device_id *id,
+               struct us_unusual_dev *unusual_dev)
 {
        struct Scsi_Host *host;
        struct us_data *us;
        int result;
-       struct task_struct *th;
-
-       if (usb_usual_check_type(id, USB_US_TYPE_STOR))
-               return -ENXIO;
 
        US_DEBUGP("USB Mass Storage device detected\n");
 
@@ -968,36 +879,64 @@ static int storage_probe(struct usb_interface *intf,
                return -ENOMEM;
        }
 
-       us = host_to_us(host);
+       /*
+        * Allow 16-byte CDBs and thus > 2TB
+        */
+       host->max_cmd_len = 16;
+       host->sg_tablesize = usb_stor_sg_tablesize(intf);
+       *pus = us = host_to_us(host);
        memset(us, 0, sizeof(struct us_data));
        mutex_init(&(us->dev_mutex));
-       init_MUTEX_LOCKED(&(us->sema));
+       init_completion(&us->cmnd_ready);
        init_completion(&(us->notify));
        init_waitqueue_head(&us->delay_wait);
+       init_completion(&us->scanning_done);
 
        /* Associate the us_data structure with the USB device */
        result = associate_dev(us, intf);
        if (result)
                goto BadDevice;
 
-       /*
-        * Get the unusual_devs entries and the descriptors
-        *
-        * id_index is calculated in the declaration to be the index number
-        * of the match from the usb_device_id table, so we can find the
-        * corresponding entry in the private table.
-        */
-       result = get_device_info(us, id);
+       /* Get the unusual_devs entries and the descriptors */
+       result = get_device_info(us, id, unusual_dev);
        if (result)
                goto BadDevice;
 
-       /* Get the transport, protocol, and pipe settings */
-       result = get_transport(us);
-       if (result)
-               goto BadDevice;
-       result = get_protocol(us);
-       if (result)
+       /* Get standard transport and protocol settings */
+       get_transport(us);
+       get_protocol(us);
+
+       /* Give the caller a chance to fill in specialized transport
+        * or protocol settings.
+        */
+       return 0;
+
+BadDevice:
+       US_DEBUGP("storage_probe() failed\n");
+       release_everything(us);
+       return result;
+}
+EXPORT_SYMBOL_GPL(usb_stor_probe1);
+
+/* Second part of general USB mass-storage probing */
+int usb_stor_probe2(struct us_data *us)
+{
+       struct task_struct *th;
+       int result;
+
+       /* Make sure the transport and protocol have both been set */
+       if (!us->transport || !us->proto_handler) {
+               result = -ENXIO;
                goto BadDevice;
+       }
+       US_DEBUGP("Transport: %s\n", us->transport_name);
+       US_DEBUGP("Protocol: %s\n", us->protocol_name);
+
+       /* fix for single-lun devices */
+       if (us->fflags & US_FL_SINGLE_LUN)
+               us->max_lun = 0;
+
+       /* Find the endpoints and calculate pipe values */
        result = get_pipes(us);
        if (result)
                goto BadDevice;
@@ -1006,7 +945,9 @@ static int storage_probe(struct usb_interface *intf,
        result = usb_stor_acquire_resources(us);
        if (result)
                goto BadDevice;
-       result = scsi_add_host(host, &intf->dev);
+       snprintf(us->scsi_name, sizeof(us->scsi_name), "usb-storage %s",
+                                       dev_name(&us->pusb_intf->dev));
+       result = scsi_add_host(us_to_host(us), &us->pusb_intf->dev);
        if (result) {
                printk(KERN_WARNING USB_STORAGE
                        "Unable to add the scsi host\n");
@@ -1018,16 +959,12 @@ static int storage_probe(struct usb_interface *intf,
        if (IS_ERR(th)) {
                printk(KERN_WARNING USB_STORAGE 
                       "Unable to start the device-scanning thread\n");
+               complete(&us->scanning_done);
                quiesce_and_remove_host(us);
                result = PTR_ERR(th);
                goto BadDevice;
        }
 
-       /* Take a reference to the host for the scanning thread and
-        * count it among all the threads we have launched.  Then
-        * start it up. */
-       scsi_host_get(us_to_host(us));
-       atomic_inc(&total_threads);
        wake_up_process(th);
 
        return 0;
@@ -1038,9 +975,10 @@ BadDevice:
        release_everything(us);
        return result;
 }
+EXPORT_SYMBOL_GPL(usb_stor_probe2);
 
-/* Handle a disconnect event from the USB core */
-static void storage_disconnect(struct usb_interface *intf)
+/* Handle a USB mass-storage disconnect */
+void usb_stor_disconnect(struct usb_interface *intf)
 {
        struct us_data *us = usb_get_intfdata(intf);
 
@@ -1048,6 +986,42 @@ static void storage_disconnect(struct usb_interface *intf)
        quiesce_and_remove_host(us);
        release_everything(us);
 }
+EXPORT_SYMBOL_GPL(usb_stor_disconnect);
+
+/* The main probe routine for standard devices */
+static int storage_probe(struct usb_interface *intf,
+                        const struct usb_device_id *id)
+{
+       struct us_data *us;
+       int result;
+
+       /*
+        * If libusual is configured, let it decide whether a standard
+        * device should be handled by usb-storage or by ub.
+        * If the device isn't standard (is handled by a subdriver
+        * module) then don't accept it.
+        */
+       if (usb_usual_check_type(id, USB_US_TYPE_STOR) ||
+                       usb_usual_ignore_device(intf))
+               return -ENXIO;
+
+       /*
+        * Call the general probe procedures.
+        *
+        * The unusual_dev_list array is parallel to the usb_storage_usb_ids
+        * table, so we use the index of the id entry to find the
+        * corresponding unusual_devs entry.
+        */
+       result = usb_stor_probe1(&us, intf, id,
+                       (id - usb_storage_usb_ids) + us_unusual_dev_list);
+       if (result)
+               return result;
+
+       /* No special transport or protocol settings in the main module */
+
+       result = usb_stor_probe2(us);
+       return result;
+}
 
 /***********************************************************************
  * Initialization and registration
@@ -1056,20 +1030,20 @@ static void storage_disconnect(struct usb_interface *intf)
 static struct usb_driver usb_storage_driver = {
        .name =         "usb-storage",
        .probe =        storage_probe,
-       .disconnect =   storage_disconnect,
-#ifdef CONFIG_PM
-       .suspend =      storage_suspend,
-       .resume =       storage_resume,
-       .reset_resume = storage_reset_resume,
-#endif
-       .pre_reset =    storage_pre_reset,
-       .post_reset =   storage_post_reset,
-       .id_table =     storage_usb_ids,
+       .disconnect =   usb_stor_disconnect,
+       .suspend =      usb_stor_suspend,
+       .resume =       usb_stor_resume,
+       .reset_resume = usb_stor_reset_resume,
+       .pre_reset =    usb_stor_pre_reset,
+       .post_reset =   usb_stor_post_reset,
+       .id_table =     usb_storage_usb_ids,
+       .soft_unbind =  1,
 };
 
 static int __init usb_stor_init(void)
 {
        int retval;
+
        printk(KERN_INFO "Initializing USB Mass Storage driver...\n");
 
        /* register the driver, return usb_register return code if error */
@@ -1092,16 +1066,6 @@ static void __exit usb_stor_exit(void)
        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);
-       }
-
        usb_usual_clear_present(USB_US_TYPE_STOR);
 }