diff options
Diffstat (limited to 'drivers/usb/storage')
-rw-r--r-- | drivers/usb/storage/Kconfig | 12 | ||||
-rw-r--r-- | drivers/usb/storage/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/storage/onetouch.c | 210 | ||||
-rw-r--r-- | drivers/usb/storage/onetouch.h | 9 | ||||
-rw-r--r-- | drivers/usb/storage/scsiglue.c | 8 | ||||
-rw-r--r-- | drivers/usb/storage/shuttle_usbat.c | 97 | ||||
-rw-r--r-- | drivers/usb/storage/transport.c | 17 | ||||
-rw-r--r-- | drivers/usb/storage/unusual_devs.h | 19 | ||||
-rw-r--r-- | drivers/usb/storage/usb.c | 79 | ||||
-rw-r--r-- | drivers/usb/storage/usb.h | 1 |
10 files changed, 348 insertions, 105 deletions
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index f1f1c0608c2..bb9819cc882 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -111,3 +111,15 @@ config USB_STORAGE_JUMPSHOT Say Y here to include additional code to support the Lexar Jumpshot USB CompactFlash reader. + +config USB_STORAGE_ONETOUCH + bool "Support OneTouch Button on Maxtor Hard Drives (EXPERIMENTAL)" + depends on USB_STORAGE && INPUT_EVDEV && EXPERIMENTAL + help + Say Y here to include additional code to support the Maxtor OneTouch + USB hard drive's onetouch button. + + This code registers the button on the front of Maxtor OneTouch USB + hard drive's as an input device. An action can be associated with + this input in any keybinding software. (e.g. gnome's keyboard short- + cuts) diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile index 56652ccc288..44ab8f9978f 100644 --- a/drivers/usb/storage/Makefile +++ b/drivers/usb/storage/Makefile @@ -18,6 +18,7 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_DPCM) += dpcm.o usb-storage-obj-$(CONFIG_USB_STORAGE_ISD200) += isd200.o usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o +usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH) += onetouch.o usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \ initializers.o $(usb-storage-obj-y) diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c new file mode 100644 index 00000000000..2c9402dc702 --- /dev/null +++ b/drivers/usb/storage/onetouch.c @@ -0,0 +1,210 @@ +/* + * Support for the Maxtor OneTouch USB hard drive's button + * + * Current development and maintenance by: + * Copyright (c) 2005 Nick Sillik <n.sillik@temple.edu> + * + * Initial work by: + * Copyright (c) 2003 Erik Thyren <erth7411@student.uu.se> + * + * Based on usbmouse.c (Vojtech Pavlik) and xpad.c (Marko Friedemann) + * + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/input.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb_ch9.h> +#include <linux/usb_input.h> +#include "usb.h" +#include "onetouch.h" +#include "debug.h" + +void onetouch_release_input(void *onetouch_); + +struct usb_onetouch { + char name[128]; + char phys[64]; + struct input_dev dev; /* input device interface */ + struct usb_device *udev; /* usb device */ + + struct urb *irq; /* urb for interrupt in report */ + unsigned char *data; /* input data */ + dma_addr_t data_dma; +}; + +static void usb_onetouch_irq(struct urb *urb, struct pt_regs *regs) +{ + struct usb_onetouch *onetouch = urb->context; + signed char *data = onetouch->data; + struct input_dev *dev = &onetouch->dev; + int status; + + switch (urb->status) { + case 0: /* success */ + break; + case -ECONNRESET: /* unlink */ + case -ENOENT: + case -ESHUTDOWN: + return; + /* -EPIPE: should clear the halt */ + default: /* error */ + goto resubmit; + } + + input_regs(dev, regs); + + input_report_key(&onetouch->dev, ONETOUCH_BUTTON, + data[0] & 0x02); + + input_sync(dev); +resubmit: + status = usb_submit_urb (urb, SLAB_ATOMIC); + if (status) + err ("can't resubmit intr, %s-%s/input0, status %d", + onetouch->udev->bus->bus_name, + onetouch->udev->devpath, status); +} + +static int usb_onetouch_open(struct input_dev *dev) +{ + struct usb_onetouch *onetouch = dev->private; + + onetouch->irq->dev = onetouch->udev; + if (usb_submit_urb(onetouch->irq, GFP_KERNEL)) { + err("usb_submit_urb failed"); + return -EIO; + } + + return 0; +} + +static void usb_onetouch_close(struct input_dev *dev) +{ + struct usb_onetouch *onetouch = dev->private; + + usb_kill_urb(onetouch->irq); +} + +int onetouch_connect_input(struct us_data *ss) +{ + struct usb_device *udev = ss->pusb_dev; + struct usb_host_interface *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_onetouch *onetouch; + int pipe, maxp; + char path[64]; + + interface = ss->pusb_intf->cur_altsetting; + + if (interface->desc.bNumEndpoints != 3) + return -ENODEV; + + endpoint = &interface->endpoint[2].desc; + if(!(endpoint->bEndpointAddress & USB_DIR_IN)) + return -ENODEV; + if((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_INT) + return -ENODEV; + + pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); + + if (!(onetouch = kcalloc(1, sizeof(struct usb_onetouch), GFP_KERNEL))) + return -ENOMEM; + + onetouch->data = usb_buffer_alloc(udev, ONETOUCH_PKT_LEN, + SLAB_ATOMIC, &onetouch->data_dma); + if (!onetouch->data){ + kfree(onetouch); + return -ENOMEM; + } + + onetouch->irq = usb_alloc_urb(0, GFP_KERNEL); + if (!onetouch->irq){ + kfree(onetouch); + usb_buffer_free(udev, ONETOUCH_PKT_LEN, + onetouch->data, onetouch->data_dma); + return -ENODEV; + } + + + onetouch->udev = udev; + + set_bit(EV_KEY, onetouch->dev.evbit); + set_bit(ONETOUCH_BUTTON, onetouch->dev.keybit); + clear_bit(0, onetouch->dev.keybit); + + onetouch->dev.private = onetouch; + onetouch->dev.open = usb_onetouch_open; + onetouch->dev.close = usb_onetouch_close; + + usb_make_path(udev, path, sizeof(path)); + sprintf(onetouch->phys, "%s/input0", path); + + onetouch->dev.name = onetouch->name; + onetouch->dev.phys = onetouch->phys; + + usb_to_input_id(udev, &onetouch->dev.id); + + onetouch->dev.dev = &udev->dev; + + if (udev->manufacturer) + strcat(onetouch->name, udev->manufacturer); + if (udev->product) + sprintf(onetouch->name, "%s %s", onetouch->name, + udev->product); + if (!strlen(onetouch->name)) + sprintf(onetouch->name, "Maxtor Onetouch %04x:%04x", + onetouch->dev.id.vendor, onetouch->dev.id.product); + + usb_fill_int_urb(onetouch->irq, udev, pipe, onetouch->data, + (maxp > 8 ? 8 : maxp), + usb_onetouch_irq, onetouch, endpoint->bInterval); + onetouch->irq->transfer_dma = onetouch->data_dma; + onetouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + + ss->extra_destructor = onetouch_release_input; + ss->extra = onetouch; + + input_register_device(&onetouch->dev); + printk(KERN_INFO "usb-input: %s on %s\n", onetouch->dev.name, path); + + return 0; +} + +void onetouch_release_input(void *onetouch_) +{ + struct usb_onetouch *onetouch = (struct usb_onetouch *) onetouch_; + + if (onetouch) { + usb_kill_urb(onetouch->irq); + input_unregister_device(&onetouch->dev); + usb_free_urb(onetouch->irq); + usb_buffer_free(onetouch->udev, ONETOUCH_PKT_LEN, + onetouch->data, onetouch->data_dma); + printk(KERN_INFO "usb-input: deregistering %s\n", + onetouch->dev.name); + } +} diff --git a/drivers/usb/storage/onetouch.h b/drivers/usb/storage/onetouch.h new file mode 100644 index 00000000000..41c7aa8f044 --- /dev/null +++ b/drivers/usb/storage/onetouch.h @@ -0,0 +1,9 @@ +#ifndef _ONETOUCH_H_ +#define _ONETOUCH_H_ + +#define ONETOUCH_PKT_LEN 0x02 +#define ONETOUCH_BUTTON KEY_PROG1 + +int onetouch_connect_input(struct us_data *ss); + +#endif diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index af294bb68c3..d34dc9f417f 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -156,6 +156,14 @@ static int slave_configure(struct scsi_device *sdev) if (us->flags & US_FL_FIX_CAPACITY) sdev->fix_capacity = 1; + /* Some devices report a SCSI revision level above 2 but are + * unable to handle the REPORT LUNS command (for which + * support is mandatory at level 3). Since we already have + * a Get-Max-LUN request, we won't lose much by setting the + * revision level down to 2. The only devices that would be + * affected are those with sparse LUNs. */ + sdev->scsi_level = SCSI_2; + /* USB-IDE bridges tend to report SK = 0x04 (Non-recoverable * Hardware Error) when any low-level error occurs, * recoverable or not. Setting this flag tells the SCSI diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index f3b60288696..356342c6e7a 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -839,34 +839,31 @@ static int usbat_identify_device(struct us_data *us, rc = usbat_device_reset(us); if (rc != USB_STOR_TRANSPORT_GOOD) return rc; + msleep(25); /* - * By examining the device signature after a reset, we can identify - * whether the device supports the ATAPI packet interface. - * The flash-devices do not support this, whereas the HP CDRW's obviously - * do. - * - * This method is not ideal, but works because no other devices have been - * produced based on the USBAT/USBAT02. - * - * Section 9.1 of the ATAPI-4 spec states (amongst other things) that - * after a device reset, a Cylinder low of 0x14 indicates that the device - * does support packet commands. + * In attempt to distinguish between HP CDRW's and Flash readers, we now + * execute the IDENTIFY PACKET DEVICE command. On ATA devices (i.e. flash + * readers), this command should fail with error. On ATAPI devices (i.e. + * CDROM drives), it should succeed. */ - rc = usbat_read(us, USBAT_ATA, USBAT_ATA_LBA_ME, &status); - if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; + rc = usbat_write(us, USBAT_ATA, USBAT_ATA_CMD, 0xA1); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("usbat_identify_device: Cylinder low is %02X\n", status); + rc = usbat_get_status(us, &status); + if (rc != USB_STOR_XFER_GOOD) + return USB_STOR_TRANSPORT_ERROR; - if (status == 0x14) { + // Check for error bit + if (status & 0x01) { + // Device is a CompactFlash reader/writer + US_DEBUGP("usbat_identify_device: Detected Flash reader/writer\n"); + info->devicetype = USBAT_DEV_FLASH; + } else { // Device is HP 8200 US_DEBUGP("usbat_identify_device: Detected HP8200 CDRW\n"); info->devicetype = USBAT_DEV_HP8200; - } else { - // Device is a CompactFlash reader/writer - US_DEBUGP("usbat_identify_device: Detected Flash reader/writer\n"); - info->devicetype = USBAT_DEV_FLASH; } return USB_STOR_TRANSPORT_GOOD; @@ -1239,16 +1236,10 @@ static int usbat_select_and_test_registers(struct us_data *us) { int selector; unsigned char *status = us->iobuf; - unsigned char max_selector = 0xB0; - if (usbat_get_device_type(us) == USBAT_DEV_FLASH) - max_selector = 0xA0; // try device = master, then device = slave. - - for (selector = 0xA0; selector <= max_selector; selector += 0x10) { - - if (usbat_get_device_type(us) == USBAT_DEV_HP8200 && - usbat_write(us, USBAT_ATA, USBAT_ATA_DEVICE, selector) != + for (selector = 0xA0; selector <= 0xB0; selector += 0x10) { + if (usbat_write(us, USBAT_ATA, USBAT_ATA_DEVICE, selector) != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; @@ -1334,60 +1325,30 @@ int init_usbat(struct us_data *us) US_DEBUGP("INIT 3\n"); - // At this point, we need to detect which device we are using - if (usbat_set_transport(us, info)) - return USB_STOR_TRANSPORT_ERROR; - - US_DEBUGP("INIT 4\n"); - - if (usbat_get_device_type(us) == USBAT_DEV_HP8200) { - msleep(250); - - // Write 0x80 to ISA port 0x3F - rc = usbat_write(us, USBAT_ISA, 0x3F, 0x80); - if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; - - US_DEBUGP("INIT 5\n"); - - // Read ISA port 0x27 - rc = usbat_read(us, USBAT_ISA, 0x27, status); - if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; - - US_DEBUGP("INIT 6\n"); - - rc = usbat_read_user_io(us, status); - if (rc != USB_STOR_XFER_GOOD) - return USB_STOR_TRANSPORT_ERROR; - - US_DEBUGP("INIT 7\n"); - } - rc = usbat_select_and_test_registers(us); if (rc != USB_STOR_TRANSPORT_GOOD) return rc; - US_DEBUGP("INIT 8\n"); + US_DEBUGP("INIT 4\n"); rc = usbat_read_user_io(us, status); if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 9\n"); + US_DEBUGP("INIT 5\n"); // Enable peripheral control signals and card detect rc = usbat_device_enable_cdt(us); if (rc != USB_STOR_TRANSPORT_GOOD) return rc; - US_DEBUGP("INIT 10\n"); + US_DEBUGP("INIT 6\n"); rc = usbat_read_user_io(us, status); if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 11\n"); + US_DEBUGP("INIT 7\n"); msleep(1400); @@ -1395,13 +1356,19 @@ int init_usbat(struct us_data *us) if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 12\n"); + US_DEBUGP("INIT 8\n"); rc = usbat_select_and_test_registers(us); if (rc != USB_STOR_TRANSPORT_GOOD) return rc; - US_DEBUGP("INIT 13\n"); + US_DEBUGP("INIT 9\n"); + + // At this point, we need to detect which device we are using + if (usbat_set_transport(us, info)) + return USB_STOR_TRANSPORT_ERROR; + + US_DEBUGP("INIT 10\n"); if (usbat_get_device_type(us) == USBAT_DEV_FLASH) { subcountH = 0x02; @@ -1412,7 +1379,7 @@ int init_usbat(struct us_data *us) if (rc != USB_STOR_XFER_GOOD) return USB_STOR_TRANSPORT_ERROR; - US_DEBUGP("INIT 14\n"); + US_DEBUGP("INIT 11\n"); return USB_STOR_TRANSPORT_GOOD; } diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index e6b1c6cf07f..c1ba5301ebf 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -96,8 +96,8 @@ * or before the URB_ACTIVE bit was set. If so, it's essential to cancel * the URB if it hasn't been cancelled already (i.e., if the URB_ACTIVE bit * is still set). Either way, the function must then wait for the URB to - * finish. Note that because the URB_ASYNC_UNLINK flag is set, the URB can - * still be in progress even after a call to usb_unlink_urb() returns. + * finish. Note that the URB can still be in progress even after a call to + * usb_unlink_urb() returns. * * The idea is that (1) once the ABORTING or DISCONNECTING bit is set, * either the stop_transport() function or the submitting function @@ -158,8 +158,7 @@ static int usb_stor_msg_common(struct us_data *us, int timeout) * hasn't been mapped for DMA. Yes, this is clunky, but it's * easier than always having the caller tell us whether the * transfer buffer has already been mapped. */ - us->current_urb->transfer_flags = - URB_ASYNC_UNLINK | URB_NO_SETUP_DMA_MAP; + us->current_urb->transfer_flags = URB_NO_SETUP_DMA_MAP; if (us->current_urb->transfer_buffer == us->iobuf) us->current_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; us->current_urb->transfer_dma = us->iobuf_dma; @@ -611,7 +610,6 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) unsigned char old_sc_data_direction; unsigned char old_cmd_len; unsigned char old_cmnd[MAX_COMMAND_SIZE]; - unsigned long old_serial_number; int old_resid; US_DEBUGP("Issuing auto-REQUEST_SENSE\n"); @@ -648,10 +646,6 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) old_sg = srb->use_sg; srb->use_sg = 0; - /* change the serial number -- toggle the high bit*/ - old_serial_number = srb->serial_number; - srb->serial_number ^= 0x80000000; - /* issue the auto-sense command */ old_resid = srb->resid; srb->resid = 0; @@ -662,7 +656,6 @@ void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) srb->request_buffer = old_request_buffer; srb->request_bufflen = old_request_bufflen; srb->use_sg = old_sg; - srb->serial_number = old_serial_number; srb->sc_data_direction = old_sc_data_direction; srb->cmd_len = old_cmd_len; memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE); @@ -985,7 +978,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN); bcb->DataTransferLength = cpu_to_le32(transfer_length); bcb->Flags = srb->sc_data_direction == DMA_FROM_DEVICE ? 1 << 7 : 0; - bcb->Tag = srb->serial_number; + bcb->Tag = ++us->tag; bcb->Lun = srb->device->lun; if (us->flags & US_FL_SCM_MULT_TARG) bcb->Lun |= srb->device->id << 4; @@ -1074,7 +1067,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) US_DEBUGP("Bulk Status S 0x%x T 0x%x R %u Stat 0x%x\n", le32_to_cpu(bcs->Signature), bcs->Tag, residue, bcs->Status); - if (bcs->Tag != srb->serial_number || bcs->Status > US_BULK_STAT_PHASE) { + if (bcs->Tag != us->tag || bcs->Status > US_BULK_STAT_PHASE) { US_DEBUGP("Bulk logical error\n"); return USB_STOR_TRANSPORT_ERROR; } diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index bd0ab3039bd..ad0cfd7a782 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -79,6 +79,13 @@ UNUSUAL_DEV( 0x03f0, 0x0307, 0x0001, 0x0001, US_SC_8070, US_PR_SCM_ATAPI, init_usbat, 0), #endif +/* Patch submitted by Mihnea-Costin Grigore <mihnea@zulu.ro> */ +UNUSUAL_DEV( 0x040d, 0x6205, 0x0003, 0x0003, + "VIA Technologies Inc.", + "USB 2.0 Card Reader", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE ), + /* Deduced by Jonathan Woithe <jwoithe@physics.adelaide.edu.au> * Entry needed for flags: US_FL_FIX_INQUIRY because initial inquiry message * always fails and confuses drive. @@ -929,6 +936,18 @@ UNUSUAL_DEV( 0x0c0b, 0xa109, 0x0000, 0xffff, US_FL_SINGLE_LUN ), #endif +/* Submitted by: Nick Sillik <n.sillik@temple.edu> + * Needed for OneTouch extension to usb-storage + * + */ +#ifdef CONFIG_USB_STORAGE_ONETOUCH + UNUSUAL_DEV( 0x0d49, 0x7010, 0x0000, 0x9999, + "Maxtor", + "OneTouch External Harddrive", + US_SC_DEVICE, US_PR_DEVICE, onetouch_connect_input, + 0), +#endif + /* Submitted by Joris Struyve <joris@struyve.be> */ UNUSUAL_DEV( 0x0d96, 0x410a, 0x0001, 0xffff, "Medion", diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 77e7fc258aa..cb4c770baf3 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -90,7 +90,9 @@ #ifdef CONFIG_USB_STORAGE_JUMPSHOT #include "jumpshot.h" #endif - +#ifdef CONFIG_USB_STORAGE_ONETOUCH +#include "onetouch.h" +#endif /* Some informational data */ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>"); @@ -786,6 +788,7 @@ static void usb_stor_release_resources(struct us_data *us) * any more commands. */ 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 */ @@ -816,6 +819,49 @@ 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 */ +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 */ + down(&us->dev_semaphore); + up(&us->dev_semaphore); + + /* 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) { @@ -956,7 +1002,7 @@ static int storage_probe(struct usb_interface *intf, if (result < 0) { printk(KERN_WARNING USB_STORAGE "Unable to start the device-scanning thread\n"); - scsi_remove_host(host); + quiesce_and_remove_host(us); goto BadDevice; } atomic_inc(&total_threads); @@ -969,10 +1015,7 @@ static int storage_probe(struct usb_interface *intf, /* 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); + release_everything(us); return result; } @@ -982,28 +1025,8 @@ 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 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 */ - down(&us->dev_semaphore); - up(&us->dev_semaphore); - 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)); + quiesce_and_remove_host(us); + release_everything(us); } /*********************************************************************** diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 625b7aa9807..a195adae57b 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -158,6 +158,7 @@ struct us_data { /* SCSI interfaces */ struct scsi_cmnd *srb; /* current srb */ + unsigned int tag; /* current dCBWTag */ /* thread information */ int pid; /* control thread */ |