vserver 1.9.5.x5
[linux-2.6.git] / drivers / usb / storage / usb.c
index 9579bdf..0a8c42a 100644 (file)
@@ -50,6 +50,7 @@
 #include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
+#include <linux/suspend.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
@@ -97,6 +98,11 @@ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
 MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
 MODULE_LICENSE("GPL");
 
+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");
+
+
 static int storage_probe(struct usb_interface *iface,
                         const struct usb_device_id *id);
 
@@ -257,16 +263,17 @@ void fill_inquiry_response(struct us_data *us, unsigned char *data,
                              available from the device."). */
                memset(data+8,0,28);
        } else {
+               u16 bcdDevice = le16_to_cpu(us->pusb_dev->descriptor.bcdDevice);
                memcpy(data+8, us->unusual_dev->vendorName, 
                        strlen(us->unusual_dev->vendorName) > 8 ? 8 :
                        strlen(us->unusual_dev->vendorName));
                memcpy(data+16, us->unusual_dev->productName, 
                        strlen(us->unusual_dev->productName) > 16 ? 16 :
                        strlen(us->unusual_dev->productName));
-               data[32] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice>>12) & 0x0F);
-               data[33] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice>>8) & 0x0F);
-               data[34] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice>>4) & 0x0F);
-               data[35] = 0x30 + ((us->pusb_dev->descriptor.bcdDevice) & 0x0F);
+               data[32] = 0x30 + ((bcdDevice>>12) & 0x0F);
+               data[33] = 0x30 + ((bcdDevice>>8) & 0x0F);
+               data[34] = 0x30 + ((bcdDevice>>4) & 0x0F);
+               data[35] = 0x30 + ((bcdDevice) & 0x0F);
        }
 
        usb_stor_set_xfer_buf(data, data_len, us->srb);
@@ -312,8 +319,8 @@ static int usb_stor_control_thread(void * __us)
                /* lock access to the state */
                scsi_lock(host);
 
-               /* has the command been aborted *already* ? */
-               if (us->sm_state == US_STATE_ABORTING) {
+               /* has the command timed out *already* ? */
+               if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) {
                        us->srb->result = DID_ABORT << 16;
                        goto SkipForAbort;
                }
@@ -324,8 +331,6 @@ static int usb_stor_control_thread(void * __us)
                        goto SkipForDisconnect;
                }
 
-               /* set the state and release the lock */
-               us->sm_state = US_STATE_RUNNING;
                scsi_unlock(host);
 
                /* reject the command if the direction indicator 
@@ -386,16 +391,15 @@ SkipForAbort:
 
                /* If an abort request was received we need to signal that
                 * the abort has finished.  The proper test for this is
-                * sm_state == US_STATE_ABORTING, not srb->result == DID_ABORT,
-                * because an abort request might be received after all the
+                * the TIMED_OUT flag, not srb->result == DID_ABORT, because
+                * a timeout/abort request might be received after all the
                 * USB processing was complete. */
-               if (us->sm_state == US_STATE_ABORTING)
+               if (test_bit(US_FLIDX_TIMED_OUT, &us->flags))
                        complete(&(us->notify));
 
-               /* empty the queue, reset the state, and release the lock */
+               /* finished working on this command */
 SkipForDisconnect:
                us->srb = NULL;
-               us->sm_state = US_STATE_IDLE;
                scsi_unlock(host);
 
                /* unlock the device pointers */
@@ -433,9 +437,9 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf)
        us->pusb_intf = intf;
        us->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
        US_DEBUGP("Vendor: 0x%04x, Product: 0x%04x, Revision: 0x%04x\n",
-                       us->pusb_dev->descriptor.idVendor,
-                       us->pusb_dev->descriptor.idProduct,
-                       us->pusb_dev->descriptor.bcdDevice);
+                       le16_to_cpu(us->pusb_dev->descriptor.idVendor),
+                       le16_to_cpu(us->pusb_dev->descriptor.idProduct),
+                       le16_to_cpu(us->pusb_dev->descriptor.bcdDevice));
        US_DEBUGP("Interface Subclass: 0x%02x, Protocol: 0x%02x\n",
                        intf->cur_altsetting->desc.bInterfaceSubClass,
                        intf->cur_altsetting->desc.bInterfaceProtocol);
@@ -504,8 +508,9 @@ static void get_device_info(struct us_data *us, int id_index)
                                " has %s in unusual_devs.h\n"
                                "   Please send a copy of this message to "
                                "<linux-usb-devel@lists.sourceforge.net>\n",
-                               ddesc->idVendor, ddesc->idProduct,
-                               ddesc->bcdDevice,
+                               le16_to_cpu(ddesc->idVendor),
+                               le16_to_cpu(ddesc->idProduct),
+                               le16_to_cpu(ddesc->bcdDevice),
                                idesc->bInterfaceSubClass,
                                idesc->bInterfaceProtocol,
                                msgs[msg]);
@@ -795,7 +800,6 @@ static int usb_stor_acquire_resources(struct us_data *us)
        us->host->hostdata[0] = (unsigned long) us;
 
        /* Start up our control thread */
-       us->sm_state = US_STATE_IDLE;
        p = kernel_thread(usb_stor_control_thread, us, CLONE_VM);
        if (p < 0) {
                printk(KERN_WARNING USB_STORAGE 
@@ -823,7 +827,6 @@ void usb_stor_release_resources(struct us_data *us)
                /* Wait for the thread to be idle */
                down(&us->dev_semaphore);
                US_DEBUGP("-- sending exit command to thread\n");
-               BUG_ON(us->sm_state != US_STATE_IDLE);
 
                /* If the SCSI midlayer queued a final command just before
                 * scsi_remove_host() was called, us->srb might not be
@@ -882,6 +885,46 @@ static void dissociate_dev(struct us_data *us)
        kfree(us);
 }
 
+/* Thread to carry out delayed SCSI-device scanning */
+static int usb_stor_scan_thread(void * __us)
+{
+       struct us_data *us = (struct us_data *)__us;
+
+       /*
+        * This thread doesn't need any user-level access,
+        * so get rid of all our resources.
+        */
+       lock_kernel();
+       daemonize("usb-stor-scan");
+       unlock_kernel();
+
+       printk(KERN_DEBUG
+               "usb-storage: device found at %d\n", us->pusb_dev->devnum);
+
+       /* 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->scsi_scan_wait,
+                               test_bit(US_FLIDX_DISCONNECTING, &us->flags),
+                               delay_use * HZ);
+               if (current->flags & PF_FREEZE) {
+                       refrigerator(PF_FREEZE);
+                       goto retry;
+               }
+       }
+
+       /* If the device is still connected, perform the scanning */
+       if (!test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
+               scsi_scan_host(us->host);
+               printk(KERN_DEBUG "usb-storage: device scan complete\n");
+       }
+
+       complete_and_exit(&us->scsi_scan_done, 0);
+}
+
+
 /* 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)
@@ -903,6 +946,8 @@ static int storage_probe(struct usb_interface *intf,
        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);
@@ -951,12 +996,10 @@ static int storage_probe(struct usb_interface *intf,
        if (result)
                goto BadDevice;
 
-       /* Acquire all the other resources */
+       /* Acquire all the other resources and add the host */
        result = usb_stor_acquire_resources(us);
        if (result)
                goto BadDevice;
-
-       /* Finally, add the host (this does SCSI device scanning) */
        result = scsi_add_host(us->host, &intf->dev);
        if (result) {
                printk(KERN_WARNING USB_STORAGE
@@ -964,10 +1007,15 @@ static int storage_probe(struct usb_interface *intf,
                goto BadDevice;
        }
 
-       scsi_scan_host(us->host);
+       /* Start up the thread for delayed SCSI-device scanning */
+       result = kernel_thread(usb_stor_scan_thread, us, CLONE_VM);
+       if (result < 0) {
+               printk(KERN_WARNING USB_STORAGE 
+                      "Unable to start the device-scanning thread\n");
+               scsi_remove_host(us->host);
+               goto BadDevice;
+       }
 
-       printk(KERN_DEBUG 
-              "USB Mass Storage device found at %d\n", us->pusb_dev->devnum);
        return 0;
 
        /* We come here if there are any problems */
@@ -991,6 +1039,11 @@ static void storage_disconnect(struct usb_interface *intf)
        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);
+
        /* Wait for the current command to finish, then remove the host */
        down(&us->dev_semaphore);
        up(&us->dev_semaphore);
@@ -1012,12 +1065,9 @@ static int __init usb_stor_init(void)
 
        /* register the driver, return usb_register return code if error */
        retval = usb_register(&usb_storage_driver);
-       if (retval)
-               goto out;
+       if (retval == 0)
+               printk(KERN_INFO "USB Mass Storage support registered.\n");
 
-       /* we're all set */
-       printk(KERN_INFO "USB Mass Storage support registered.\n");
-out:
        return retval;
 }