X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fusb%2Fstorage%2Fusb.c;h=dd108634348e3f12d299b320dc119430d700d8b8;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=9579bdf4969abbfa9c09f29b29235c8565ea0ee4;hpb=c7b5ebbddf7bcd3651947760f423e3783bbe6573;p=linux-2.6.git diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 9579bdf49..dd1086343 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -50,6 +50,12 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include #include @@ -62,7 +68,7 @@ #include "debug.h" #include "initializers.h" -#ifdef CONFIG_USB_STORAGE_HP8200e +#ifdef CONFIG_USB_STORAGE_USBAT #include "shuttle_usbat.h" #endif #ifdef CONFIG_USB_STORAGE_SDDR09 @@ -86,67 +92,57 @@ #ifdef CONFIG_USB_STORAGE_JUMPSHOT #include "jumpshot.h" #endif - - -#include -#include -#include +#ifdef CONFIG_USB_STORAGE_ONETOUCH +#include "onetouch.h" +#endif +#ifdef CONFIG_USB_STORAGE_ALAUDA +#include "alauda.h" +#endif /* Some informational data */ MODULE_AUTHOR("Matthew Dharm "); MODULE_DESCRIPTION("USB Mass Storage driver for Linux"); MODULE_LICENSE("GPL"); -static int storage_probe(struct usb_interface *iface, - const struct usb_device_id *id); +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 void storage_disconnect(struct usb_interface *iface); -/* The entries in this table, except for final ones here - * (USB_MASS_STORAGE_CLASS and the empty entry), correspond, - * line for line with the entries of us_unsuaul_dev_list[]. +/* + * The entries in this table correspond, line for line, + * with the entries of us_unusual_dev_list[]. */ +#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) } +{ 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 - /* Control/Bulk transport for all SubClass values */ - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CB) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_CB) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_CB) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_CB) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_CB) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_CB) }, - - /* Control/Bulk/Interrupt transport for all SubClass values */ - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_CBI) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_CBI) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_CBI) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_CBI) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_CBI) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_CBI) }, - - /* Bulk-only transport for all SubClass values */ - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_BULK) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_BULK) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_BULK) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_BULK) }, - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_BULK) }, -#if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE) - { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) }, -#endif - +#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 */ @@ -159,7 +155,6 @@ MODULE_DEVICE_TABLE (usb, storage_usb_ids); * are free to use as many characters as you like. */ -#undef UNUSUAL_DEV #define UNUSUAL_DEV(idVendor, idProduct, bcdDeviceMin, bcdDeviceMax, \ vendor_name, product_name, use_protocol, use_transport, \ init_function, Flags) \ @@ -169,67 +164,61 @@ MODULE_DEVICE_TABLE (usb, storage_usb_ids); .useProtocol = use_protocol, \ .useTransport = use_transport, \ .initFunction = init_function, \ - .flags = Flags, \ +} + +#define USUAL_DEV(use_protocol, use_transport, use_type) \ +{ \ + .useProtocol = use_protocol, \ + .useTransport = use_transport, \ } static struct us_unusual_dev us_unusual_dev_list[] = { # include "unusual_devs.h" # undef UNUSUAL_DEV - /* Control/Bulk transport for all SubClass values */ - { .useProtocol = US_SC_RBC, - .useTransport = US_PR_CB}, - { .useProtocol = US_SC_8020, - .useTransport = US_PR_CB}, - { .useProtocol = US_SC_QIC, - .useTransport = US_PR_CB}, - { .useProtocol = US_SC_UFI, - .useTransport = US_PR_CB}, - { .useProtocol = US_SC_8070, - .useTransport = US_PR_CB}, - { .useProtocol = US_SC_SCSI, - .useTransport = US_PR_CB}, - - /* Control/Bulk/Interrupt transport for all SubClass values */ - { .useProtocol = US_SC_RBC, - .useTransport = US_PR_CBI}, - { .useProtocol = US_SC_8020, - .useTransport = US_PR_CBI}, - { .useProtocol = US_SC_QIC, - .useTransport = US_PR_CBI}, - { .useProtocol = US_SC_UFI, - .useTransport = US_PR_CBI}, - { .useProtocol = US_SC_8070, - .useTransport = US_PR_CBI}, - { .useProtocol = US_SC_SCSI, - .useTransport = US_PR_CBI}, - - /* Bulk-only transport for all SubClass values */ - { .useProtocol = US_SC_RBC, - .useTransport = US_PR_BULK}, - { .useProtocol = US_SC_8020, - .useTransport = US_PR_BULK}, - { .useProtocol = US_SC_QIC, - .useTransport = US_PR_BULK}, - { .useProtocol = US_SC_UFI, - .useTransport = US_PR_BULK}, - { .useProtocol = US_SC_8070, - .useTransport = US_PR_BULK}, -#if !defined(CONFIG_BLK_DEV_UB) && !defined(CONFIG_BLK_DEV_UB_MODULE) - { .useProtocol = US_SC_SCSI, - .useTransport = US_PR_BULK}, -#endif +# undef USUAL_DEV /* Terminating entry */ { NULL } }; -struct usb_driver usb_storage_driver = { - .owner = THIS_MODULE, - .name = "usb-storage", - .probe = storage_probe, - .disconnect = storage_disconnect, - .id_table = storage_usb_ids, -}; + +#ifdef CONFIG_PM /* Minimal support for suspend and resume */ + +static int storage_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__); + if (us->suspend_resume_hook) + (us->suspend_resume_hook)(us, US_SUSPEND); + iface->dev.power.power_state.event = message.event; + + /* When runtime PM is working, we'll set a flag to indicate + * whether we should autoresume when a SCSI request arrives. */ + + mutex_unlock(&us->dev_mutex); + return 0; +} + +static int storage_resume(struct usb_interface *iface) +{ + struct us_data *us = usb_get_intfdata(iface); + + mutex_lock(&us->dev_mutex); + + US_DEBUGP("%s\n", __FUNCTION__); + if (us->suspend_resume_hook) + (us->suspend_resume_hook)(us, US_RESUME); + iface->dev.power.power_state.event = PM_EVENT_ON; + + mutex_unlock(&us->dev_mutex); + return 0; +} + +#endif /* CONFIG_PM */ /* * fill_inquiry_response takes an unsigned char array (which must @@ -257,16 +246,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); @@ -275,23 +265,10 @@ void fill_inquiry_response(struct us_data *us, unsigned char *data, static int usb_stor_control_thread(void * __us) { struct us_data *us = (struct us_data *)__us; - struct Scsi_Host *host = us->host; - - lock_kernel(); - - /* - * This thread doesn't need any user-level access, - * so get rid of all our resources. - */ - daemonize("usb-storage"); + struct Scsi_Host *host = us_to_host(us); current->flags |= PF_NOFREEZE; - unlock_kernel(); - - /* signal that we've started the thread */ - complete(&(us->notify)); - for(;;) { US_DEBUGP("*** thread sleeping.\n"); if(down_interruptible(&us->sema)) @@ -300,32 +277,24 @@ static int usb_stor_control_thread(void * __us) US_DEBUGP("*** thread awakened.\n"); /* lock the device pointers */ - down(&(us->dev_semaphore)); + mutex_lock(&(us->dev_mutex)); - /* 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)) { + US_DEBUGP("-- exiting\n"); + mutex_unlock(&us->dev_mutex); break; } /* 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; } - /* 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; - } - - /* set the state and release the lock */ - us->sm_state = US_STATE_RUNNING; scsi_unlock(host); /* reject the command if the direction indicator @@ -386,22 +355,27 @@ 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 - * USB processing was complete. */ - if (us->sm_state == US_STATE_ABORTING) + * 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)) { complete(&(us->notify)); - /* empty the queue, reset the state, and release the lock */ -SkipForDisconnect: + /* Allow USB transfers to resume */ + clear_bit(US_FLIDX_ABORTING, &us->flags); + clear_bit(US_FLIDX_TIMED_OUT, &us->flags); + } + + /* finished working on this command */ us->srb = NULL; - us->sm_state = US_STATE_IDLE; scsi_unlock(host); /* unlock the device pointers */ - up(&(us->dev_semaphore)); + mutex_unlock(&us->dev_mutex); } /* for (;;) */ + scsi_host_put(host); + /* notify the exit routine that we're actually exiting now * * complete()/wait_for_completion() is similar to up()/down(), @@ -416,7 +390,7 @@ SkipForDisconnect: * 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); } /*********************************************************************** @@ -433,9 +407,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); @@ -457,17 +431,29 @@ 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) +{ + const int id_index = id - storage_usb_ids; + return &us_unusual_dev_list[id_index]; +} + /* Get the unusual_devs entries and the string descriptors */ -static void get_device_info(struct us_data *us, int id_index) +static void get_device_info(struct us_data *us, const struct usb_device_id *id) { struct usb_device *dev = us->pusb_dev; struct usb_interface_descriptor *idesc = &us->pusb_intf->cur_altsetting->desc; - struct us_unusual_dev *unusual_dev = &us_unusual_dev_list[id_index]; - struct usb_device_id *id = &storage_usb_ids[id_index]; + struct us_unusual_dev *unusual_dev = find_unusual(id); /* Store the entries */ us->unusual_dev = unusual_dev; @@ -477,7 +463,14 @@ static void get_device_info(struct us_data *us, int id_index) us->protocol = (unusual_dev->useTransport == US_PR_DEVICE) ? idesc->bInterfaceProtocol : unusual_dev->useTransport; - us->flags = unusual_dev->flags; + us->flags = USB_US_ORIG_FLAGS(id->driver_info); + + /* + * This flag is only needed when we're in high-speed, so let's + * disable it if we're in full-speed + */ + if (dev->speed != USB_SPEED_HIGH) + us->flags &= ~US_FL_GO_SLOW; /* Log a message if a non-generic unusual_dev entry contains an * unnecessary subclass or protocol override. This may stimulate @@ -485,7 +478,7 @@ static void get_device_info(struct us_data *us, int id_index) * from the unusual_devs.h table. */ if (id->idVendor || id->idProduct) { - static char *msgs[3] = { + static const char *msgs[3] = { "an unneeded SubClass entry", "an unneeded Protocol entry", "unneeded SubClass and Protocol entries"}; @@ -498,49 +491,19 @@ static void get_device_info(struct us_data *us, int id_index) if (unusual_dev->useTransport != US_PR_DEVICE && us->protocol == idesc->bInterfaceProtocol) msg += 2; - if (msg >= 0 && !(unusual_dev->flags & US_FL_NEED_OVERRIDE)) + if (msg >= 0 && !(us->flags & US_FL_NEED_OVERRIDE)) printk(KERN_NOTICE USB_STORAGE "This device " "(%04x,%04x,%04x S %02x P %02x)" " has %s in unusual_devs.h\n" " Please send a copy of this message to " "\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]); } - - /* Read the device's string descriptors */ - if (dev->descriptor.iManufacturer) - usb_string(dev, dev->descriptor.iManufacturer, - us->vendor, sizeof(us->vendor)); - if (dev->descriptor.iProduct) - usb_string(dev, dev->descriptor.iProduct, - us->product, sizeof(us->product)); - if (dev->descriptor.iSerialNumber) - usb_string(dev, dev->descriptor.iSerialNumber, - us->serial, sizeof(us->serial)); - - /* Use the unusual_dev strings if the device didn't provide them */ - if (strlen(us->vendor) == 0) { - if (unusual_dev->vendorName) - strlcpy(us->vendor, unusual_dev->vendorName, - sizeof(us->vendor)); - else - strcpy(us->vendor, "Unknown"); - } - if (strlen(us->product) == 0) { - if (unusual_dev->productName) - strlcpy(us->product, unusual_dev->productName, - sizeof(us->product)); - else - strcpy(us->product, "Unknown"); - } - if (strlen(us->serial) == 0) - strcpy(us->serial, "None"); - - US_DEBUGP("Vendor: %s, Product: %s\n", us->vendor, us->product); } /* Get the transport settings */ @@ -567,10 +530,10 @@ static int get_transport(struct us_data *us) us->transport_reset = usb_stor_Bulk_reset; break; -#ifdef CONFIG_USB_STORAGE_HP8200e - case US_PR_SCM_ATAPI: - us->transport_name = "SCM/ATAPI"; - us->transport = hp8200e_transport; +#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; @@ -685,6 +648,15 @@ static int get_protocol(struct us_data *us) 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 + default: return -EIO; } @@ -753,6 +725,7 @@ static int get_pipes(struct us_data *us) static int usb_stor_acquire_resources(struct us_data *us) { int p; + struct task_struct *th; us->current_urb = usb_alloc_urb(0, GFP_KERNEL); if (!us->current_urb) { @@ -760,89 +733,44 @@ static int usb_stor_acquire_resources(struct us_data *us) return -ENOMEM; } - /* Lock the device while we carry out the next two operations */ - down(&us->dev_semaphore); - - /* For bulk-only devices, determine the max LUN value */ - if (us->protocol == US_PR_BULK) { - p = usb_stor_Bulk_max_lun(us); - if (p < 0) { - up(&us->dev_semaphore); - return p; - } - us->max_lun = p; - } - /* Just before we start our control thread, initialize * the device if it needs initialization */ - if (us->unusual_dev->initFunction) - us->unusual_dev->initFunction(us); - - 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; + if (us->unusual_dev->initFunction) { + p = us->unusual_dev->initFunction(us); + if (p) + return p; } - /* Set the hostdata to prepare for scanning */ - 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) { + th = kthread_create(usb_stor_control_thread, us, "usb-storage"); + if (IS_ERR(th)) { printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n"); - return p; + return PTR_ERR(th); } - us->pid = p; - /* Wait for the thread to start */ - wait_for_completion(&(us->notify)); + /* Take a reference to the host for the control 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; } /* Release all our dynamic resources */ -void usb_stor_release_resources(struct us_data *us) +static void usb_stor_release_resources(struct us_data *us) { 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. + /* Tell the control thread to exit. 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"); - 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 - * 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); - - up(&us->sema); - wait_for_completion(&us->notify); - } + US_DEBUGP("-- sending exit command to thread\n"); + set_bit(US_FLIDX_DISCONNECTING, &us->flags); + up(&us->sema); /* Call the destructor routine, if it exists */ if (us->extra_destructor) { @@ -850,16 +778,9 @@ void usb_stor_release_resources(struct us_data *us) 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); } /* Dissociate from the USB device */ @@ -867,6 +788,8 @@ static void dissociate_dev(struct us_data *us) { US_DEBUGP("-- %s\n", __FUNCTION__); + kfree(us->sensebuf); + /* Free the device-related DMA-mapped buffers */ if (us->cr) usb_buffer_free(us->pusb_dev, sizeof(*us->cr), us->cr, @@ -877,32 +800,123 @@ static void dissociate_dev(struct us_data *us) /* Remove our private data from the interface */ usb_set_intfdata(us->pusb_intf, NULL); +} + +/* First stage of disconnect processing: stop all commands and remove + * the host */ +static void quiesce_and_remove_host(struct us_data *us) +{ + /* Prevent new USB transfers, stop the current command, and + * interrupt a SCSI-scan or device-reset delay */ + set_bit(US_FLIDX_DISCONNECTING, &us->flags); + 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. */ + + /* Wait for the current command to finish, then remove the host */ + mutex_lock(&us->dev_mutex); + mutex_unlock(&us->dev_mutex); + + /* 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. */ + if (us->srb) { + us->srb->result = DID_NO_CONNECT << 16; + scsi_lock(us_to_host(us)); + us->srb->scsi_done(us->srb); + us->srb = NULL; + scsi_unlock(us_to_host(us)); + } + + /* Now we own no commands so it's safe to remove the SCSI host */ + scsi_remove_host(us_to_host(us)); +} + +/* Second stage of disconnect processing: deallocate all resources */ +static void release_everything(struct us_data *us) +{ + 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)); +} + +/* Thread to carry out delayed SCSI-device scanning */ +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); + + /* 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), + 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)) { + + /* For bulk-only devices, determine the max LUN value */ + if (us->protocol == US_PR_BULK && + !(us->flags & 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"); + + /* Should we unbind if no devices were detected? */ + } - /* Free the structure itself */ - kfree(us); + scsi_host_put(us_to_host(us)); + complete_and_exit(&threads_gone, 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) { + struct Scsi_Host *host; struct us_data *us; - const int id_index = id - storage_usb_ids; 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"); - /* 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)); + mutex_init(&(us->dev_mutex)); init_MUTEX_LOCKED(&(us->sema)); init_completion(&(us->notify)); - init_waitqueue_head(&us->dev_reset_wait); + init_waitqueue_head(&us->delay_wait); /* Associate the us_data structure with the USB device */ result = associate_dev(us, intf); @@ -916,29 +930,7 @@ static int storage_probe(struct usb_interface *intf, * of the match from the usb_device_id table, so we can find the * corresponding entry in the private table. */ - get_device_info(us, id_index); - -#ifdef CONFIG_USB_STORAGE_SDDR09 - if (us->protocol == US_PR_EUSB_SDDR09 || - us->protocol == US_PR_DPCM_USB) { - /* set the configuration -- STALL is an acceptable response here */ - if (us->pusb_dev->actconfig->desc.bConfigurationValue != 1) { - US_DEBUGP("active config #%d != 1 ??\n", us->pusb_dev - ->actconfig->desc.bConfigurationValue); - goto BadDevice; - } - result = usb_reset_configuration(us->pusb_dev); - - US_DEBUGP("Result of usb_reset_configuration is %d\n", result); - if (result == -EPIPE) { - US_DEBUGP("-- stall on control interface\n"); - } else if (result != 0) { - /* it's not a stall, but another error -- time to bail */ - US_DEBUGP("-- Unknown error. Rejecting device\n"); - goto BadDevice; - } - } -#endif + get_device_info(us, id); /* Get the transport, protocol, and pipe settings */ result = get_transport(us); @@ -951,30 +943,40 @@ 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); + result = scsi_add_host(host, &intf->dev); if (result) { printk(KERN_WARNING USB_STORAGE "Unable to add the scsi host\n"); goto BadDevice; } - scsi_scan_host(us->host); + /* Start up the thread for delayed SCSI-device scanning */ + th = kthread_create(usb_stor_scan_thread, us, "usb-stor-scan"); + if (IS_ERR(th)) { + printk(KERN_WARNING USB_STORAGE + "Unable to start the device-scanning thread\n"); + 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); - 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 */ BadDevice: US_DEBUGP("storage_probe() failed\n"); - usb_stor_release_resources(us); - dissociate_dev(us); + release_everything(us); return result; } @@ -984,27 +986,25 @@ static void storage_disconnect(struct usb_interface *intf) struct us_data *us = usb_get_intfdata(intf); US_DEBUGP("storage_disconnect() called\n"); - - /* Prevent new USB transfers, stop the current command, and - * interrupt a device-reset delay */ - set_bit(US_FLIDX_DISCONNECTING, &us->flags); - usb_stor_stop_transport(us); - wake_up(&us->dev_reset_wait); - - /* Wait for the current command to finish, then remove the host */ - down(&us->dev_semaphore); - up(&us->dev_semaphore); - scsi_remove_host(us->host); - - /* Wait for everything to become idle and release all our resources */ - usb_stor_release_resources(us); - dissociate_dev(us); + quiesce_and_remove_host(us); + release_everything(us); } /*********************************************************************** * Initialization and registration ***********************************************************************/ +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, +#endif + .id_table = storage_usb_ids, +}; + static int __init usb_stor_init(void) { int retval; @@ -1012,12 +1012,10 @@ 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; - - /* we're all set */ - printk(KERN_INFO "USB Mass Storage support registered.\n"); -out: + if (retval == 0) { + printk(KERN_INFO "USB Mass Storage support registered.\n"); + usb_usual_set_present(USB_US_TYPE_STOR); + } return retval; } @@ -1031,6 +1029,18 @@ 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); } module_init(usb_stor_init);