Commit 82469dfd authored by Matthew Dharm's avatar Matthew Dharm Committed by Greg Kroah-Hartman

[PATCH] usb-storage: move to SCSI hotplugging

The attached patch is my first implementation of SCSI hotplugging.

It's only been tested that it compiles, as I can't get the current
linux-2.5 tree from linuxusb to boot.  It dies _very_ early.  Greg, I'm not
sure if you'll want to apply this.  Linus seemed to want this very much,
and it is 2.5.x... I say go for it, but I can understand if you have
reservations.

I would definately like to see this tested by anyone who can get a kernel
to boot.

This patch is quite large.  Lots of things had to be changed.  Among them:

(o) The proc interface now uses the host number to look up the SCSI host
    structure, and then finds the usb-storage structure from that.
(o) The SCSI interface has been changed.  The code flow is now much
    clearer, as more work is done from the USB probe/detach functions than
    from auxillary functions.
(o) Names have been changed for newer conventions
(o) GUIDs have been removed
(o) The linked-list of devices has been removed, and it's associated
    semaphore
(o) All code dealing with re-attaching a device to it's old association has
    been removed
(o) Some spaces changed to tabs
(o) usb-storage now takes one directory under /proc/scsi instead of
    one per virtual-HBA
(o) All control threads now have the same name.  This could be changed back
    to the old behavior, if enough people want it.

Known problems:
(o) Testing, testing, testing
(o) More dead code needs to be cut
(o) It's a unclear how a LLD is supposed to cut off the flow of
    commands, so that the unregister() call always succeeds.  SCSI folks
    need to work on this.
(o) Probing needs to be broken down into smaller functions, probably.
parent 6a4f58ed
...@@ -50,23 +50,25 @@ ...@@ -50,23 +50,25 @@
#include "transport.h" #include "transport.h"
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h>
/*********************************************************************** /***********************************************************************
* Host functions * Host functions
***********************************************************************/ ***********************************************************************/
static const char* host_info(struct Scsi_Host *host) static const char* usb_storage_info(struct Scsi_Host *host)
{ {
return "SCSI emulation for USB Mass Storage devices"; return "SCSI emulation for USB Mass Storage devices";
} }
#if 0
/* detect a virtual adapter (always works) /* detect a virtual adapter (always works)
* Synchronization: 2.4: with the io_request_lock * Synchronization: 2.4: with the io_request_lock
* 2.5: no locks. * 2.5: no locks.
* fortunately we don't care. * fortunately we don't care.
* */ * */
static int detect(struct SHT *sht) static int usb_storage_detect(struct SHT *sht)
{ {
struct us_data *us; struct us_data *us;
char local_name[32]; char local_name[32];
...@@ -109,7 +111,7 @@ static int detect(struct SHT *sht) ...@@ -109,7 +111,7 @@ static int detect(struct SHT *sht)
* the driver and we're doing each virtual host in turn, not in parallel * the driver and we're doing each virtual host in turn, not in parallel
* Synchronization: BKL, no spinlock. * Synchronization: BKL, no spinlock.
*/ */
static int release(struct Scsi_Host *psh) static int usb_storage_release(struct Scsi_Host *psh)
{ {
struct us_data *us = (struct us_data *)psh->hostdata[0]; struct us_data *us = (struct us_data *)psh->hostdata[0];
...@@ -132,18 +134,11 @@ static int release(struct Scsi_Host *psh) ...@@ -132,18 +134,11 @@ static int release(struct Scsi_Host *psh)
/* we always have a successful release */ /* we always have a successful release */
return 0; return 0;
} }
#endif
/* run command */
static int command( Scsi_Cmnd *srb )
{
US_DEBUGP("Bad use of us_command\n");
return DID_BAD_TARGET << 16;
}
/* queue a command */ /* queue a command */
/* This is always called with scsi_lock(srb->host) held */ /* This is always called with scsi_lock(srb->host) held */
static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *)) static int usb_storage_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
{ {
struct us_data *us = (struct us_data *)srb->host->hostdata[0]; struct us_data *us = (struct us_data *)srb->host->hostdata[0];
...@@ -168,7 +163,7 @@ static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *)) ...@@ -168,7 +163,7 @@ static int queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *))
/* Command abort */ /* Command abort */
/* This is always called with scsi_lock(srb->host) held */ /* This is always called with scsi_lock(srb->host) held */
static int command_abort( Scsi_Cmnd *srb ) static int usb_storage_command_abort( Scsi_Cmnd *srb )
{ {
struct us_data *us = (struct us_data *)srb->host->hostdata[0]; struct us_data *us = (struct us_data *)srb->host->hostdata[0];
...@@ -187,7 +182,7 @@ static int command_abort( Scsi_Cmnd *srb ) ...@@ -187,7 +182,7 @@ static int command_abort( Scsi_Cmnd *srb )
/* This invokes the transport reset mechanism to reset the state of the /* This invokes the transport reset mechanism to reset the state of the
* device */ * device */
/* This is always called with scsi_lock(srb->host) held */ /* This is always called with scsi_lock(srb->host) held */
static int device_reset( Scsi_Cmnd *srb ) static int usb_storage_device_reset( Scsi_Cmnd *srb )
{ {
struct us_data *us = (struct us_data *)srb->host->hostdata[0]; struct us_data *us = (struct us_data *)srb->host->hostdata[0];
int result; int result;
...@@ -219,7 +214,7 @@ static int device_reset( Scsi_Cmnd *srb ) ...@@ -219,7 +214,7 @@ static int device_reset( Scsi_Cmnd *srb )
* disconnect/reconnect for all drivers which have claimed * disconnect/reconnect for all drivers which have claimed
* interfaces, including ourself. */ * interfaces, including ourself. */
/* This is always called with scsi_lock(srb->host) held */ /* This is always called with scsi_lock(srb->host) held */
static int bus_reset( Scsi_Cmnd *srb ) static int usb_storage_bus_reset( Scsi_Cmnd *srb )
{ {
struct us_data *us = (struct us_data *)srb->host->hostdata[0]; struct us_data *us = (struct us_data *)srb->host->hostdata[0];
int i; int i;
...@@ -274,14 +269,6 @@ static int bus_reset( Scsi_Cmnd *srb ) ...@@ -274,14 +269,6 @@ static int bus_reset( Scsi_Cmnd *srb )
return SUCCESS; return SUCCESS;
} }
/* FIXME: This doesn't do anything right now */
static int host_reset( Scsi_Cmnd *srb )
{
printk(KERN_CRIT "usb-storage: host_reset() requested but not implemented\n" );
bus_reset(srb);
return FAILED;
}
/*********************************************************************** /***********************************************************************
* /proc/scsi/ functions * /proc/scsi/ functions
***********************************************************************/ ***********************************************************************/
...@@ -291,29 +278,24 @@ static int host_reset( Scsi_Cmnd *srb ) ...@@ -291,29 +278,24 @@ static int host_reset( Scsi_Cmnd *srb )
#define SPRINTF(args...) \ #define SPRINTF(args...) \
do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0) do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0)
static int proc_info (char *buffer, char **start, off_t offset, int length, static int usb_storage_proc_info (char *buffer, char **start, off_t offset,
int hostno, int inout) int length, int hostno, int inout)
{ {
struct us_data *us; struct us_data *us;
char *pos = buffer; char *pos = buffer;
struct Scsi_Host *hostptr;
/* if someone is sending us data, just throw it away */ /* if someone is sending us data, just throw it away */
if (inout) if (inout)
return length; return length;
/* lock the data structures */ /* find our data from the given hostno */
down(&us_list_semaphore); hostptr = scsi_host_hn_get(hostno);
if (!hostptr) { /* if we couldn't find it, we return an error */
/* find our data from hostno */ return -ESRCH;
us = us_list;
while (us) {
if (us->host_no == hostno)
break;
us = us->next;
} }
us = (struct us_data*)hostptr->hostdata[0];
/* release our lock on the data structures */ scsi_host_put(hostptr);
up(&us_list_semaphore);
/* if we couldn't find it, we return an error */ /* if we couldn't find it, we return an error */
if (!us) { if (!us) {
...@@ -332,8 +314,7 @@ static int proc_info (char *buffer, char **start, off_t offset, int length, ...@@ -332,8 +314,7 @@ static int proc_info (char *buffer, char **start, off_t offset, int length,
SPRINTF(" Protocol: %s\n", us->protocol_name); SPRINTF(" Protocol: %s\n", us->protocol_name);
SPRINTF(" Transport: %s\n", us->transport_name); SPRINTF(" Transport: %s\n", us->transport_name);
/* show the GUID of the device */ /* show attached status of the device */
SPRINTF(" GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid));
SPRINTF(" Attached: %s\n", (us->flags & US_FL_DEV_ATTACHED ? SPRINTF(" Attached: %s\n", (us->flags & US_FL_DEV_ATTACHED ?
"Yes" : "No")); "Yes" : "No"));
...@@ -351,33 +332,69 @@ static int proc_info (char *buffer, char **start, off_t offset, int length, ...@@ -351,33 +332,69 @@ static int proc_info (char *buffer, char **start, off_t offset, int length,
} }
/* /*
* this defines our 'host' * this defines our host template, with which we'll allocate hosts
*/ */
Scsi_Host_Template usb_stor_host_template = { struct SHT usb_stor_host_template = {
.name = "usb-storage", /* basic userland interface stuff */
.proc_info = proc_info, .name = "usb-storage",
.info = host_info, .proc_name = "usb-storage",
.proc_info = usb_storage_proc_info,
.detect = detect, .proc_dir = NULL,
.release = release, .info = usb_storage_info,
.command = command, .ioctl = NULL,
.queuecommand = queuecommand,
/* old-style detect and release */
.eh_abort_handler = command_abort, .detect = NULL,
.eh_device_reset_handler =device_reset, .release = NULL,
.eh_bus_reset_handler = bus_reset,
.eh_host_reset_handler =host_reset, /* command interface -- queued only */
.command = NULL,
.can_queue = 1, .queuecommand = usb_storage_queuecommand,
.this_id = -1,
/* error and abort handlers */
.sg_tablesize = SG_ALL, .eh_abort_handler = usb_storage_command_abort,
.cmd_per_lun = 1, .eh_device_reset_handler = usb_storage_device_reset,
.present = 0, .eh_bus_reset_handler = usb_storage_bus_reset,
.unchecked_isa_dma = FALSE, .eh_host_reset_handler = NULL,
.use_clustering = TRUE, .eh_strategy_handler = NULL,
.emulated = TRUE
/* queue commands only, only one command per LUN */
.can_queue = 1,
.cmd_per_lun = 1,
/* unknown initiator id */
.this_id = -1,
/* no limit on commands */
.max_sectors = 0,
/* pre- and post- device scan functions */
.slave_alloc = NULL,
.slave_configure = NULL,
.slave_destroy = NULL,
/* lots of sg segments can be handled */
.sg_tablesize = SG_ALL,
/* use 32-bit address space for DMA */
.unchecked_isa_dma = FALSE,
.highmem_io = FALSE,
/* merge commands... this seems to help performance, but
* periodically someone should test to see which setting is more
* optimal.
*/
.use_clustering = TRUE,
/* emulated HBA */
.emulated = TRUE,
/* sorry, no BIOS to help us */
.bios_param = NULL,
/* module management */
.module = THIS_MODULE
}; };
/* For a device that is "Not Ready" */ /* For a device that is "Not Ready" */
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
extern unsigned char usb_stor_sense_notready[18]; extern unsigned char usb_stor_sense_notready[18];
extern unsigned char usb_stor_sense_invalidCDB[18]; extern unsigned char usb_stor_sense_invalidCDB[18];
extern Scsi_Host_Template usb_stor_host_template; extern struct SHT usb_stor_host_template;
extern int usb_stor_scsiSense10to6(Scsi_Cmnd*); extern int usb_stor_scsiSense10to6(Scsi_Cmnd*);
extern int usb_stor_scsiSense6to10(Scsi_Cmnd*); extern int usb_stor_scsiSense6to10(Scsi_Cmnd*);
......
...@@ -93,16 +93,6 @@ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>"); ...@@ -93,16 +93,6 @@ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
MODULE_DESCRIPTION("USB Mass Storage driver for Linux"); MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
/*
* Per device data
*/
static int my_host_number;
/* The list of structures and the protective lock for them */
struct us_data *us_list;
struct semaphore us_list_semaphore;
static int storage_probe(struct usb_interface *iface, static int storage_probe(struct usb_interface *iface,
const struct usb_device_id *id); const struct usb_device_id *id);
...@@ -319,7 +309,7 @@ static int usb_stor_control_thread(void * __us) ...@@ -319,7 +309,7 @@ static int usb_stor_control_thread(void * __us)
spin_unlock_irq(&current->sig->siglock); spin_unlock_irq(&current->sig->siglock);
/* set our name for identification purposes */ /* set our name for identification purposes */
sprintf(current->comm, "usb-storage-%d", us->host_number); sprintf(current->comm, "usb-storage");
unlock_kernel(); unlock_kernel();
...@@ -563,12 +553,10 @@ static int storage_probe(struct usb_interface *intf, ...@@ -563,12 +553,10 @@ static int storage_probe(struct usb_interface *intf,
char mf[USB_STOR_STRING_LEN]; /* manufacturer */ char mf[USB_STOR_STRING_LEN]; /* manufacturer */
char prod[USB_STOR_STRING_LEN]; /* product */ char prod[USB_STOR_STRING_LEN]; /* product */
char serial[USB_STOR_STRING_LEN]; /* serial number */ char serial[USB_STOR_STRING_LEN]; /* serial number */
GUID(guid); /* Global Unique Identifier */
unsigned int flags; unsigned int flags;
struct us_unusual_dev *unusual_dev; struct us_unusual_dev *unusual_dev;
struct us_data *ss = NULL; struct us_data *ss = NULL;
int result; int result;
int new_device = 0;
/* these are temporary copies -- we test on these, then put them /* these are temporary copies -- we test on these, then put them
* in the us-data structure * in the us-data structure
...@@ -676,8 +664,7 @@ static int storage_probe(struct usb_interface *intf, ...@@ -676,8 +664,7 @@ static int storage_probe(struct usb_interface *intf,
/* At this point, we've decided to try to use the device */ /* At this point, we've decided to try to use the device */
usb_get_dev(dev); usb_get_dev(dev);
/* clear the GUID and fetch the strings */ /* fetch the strings */
GUID_CLEAR(guid);
if (dev->descriptor.iManufacturer) if (dev->descriptor.iManufacturer)
usb_string(dev, dev->descriptor.iManufacturer, usb_string(dev, dev->descriptor.iManufacturer,
mf, sizeof(mf)); mf, sizeof(mf));
...@@ -688,348 +675,276 @@ static int storage_probe(struct usb_interface *intf, ...@@ -688,348 +675,276 @@ static int storage_probe(struct usb_interface *intf,
usb_string(dev, dev->descriptor.iSerialNumber, usb_string(dev, dev->descriptor.iSerialNumber,
serial, sizeof(serial)); serial, sizeof(serial));
/* Create a GUID for this device */ /* New device -- allocate memory and initialize */
if (dev->descriptor.iSerialNumber && serial[0]) { if ((ss = (struct us_data *)kmalloc(sizeof(struct us_data),
/* If we have a serial number, and it's a non-NULL string */ GFP_KERNEL)) == NULL) {
make_guid(guid, dev->descriptor.idVendor, printk(KERN_WARNING USB_STORAGE "Out of memory\n");
dev->descriptor.idProduct, serial); usb_put_dev(dev);
} else { return -ENOMEM;
/* We don't have a serial number, so we use 0 */
make_guid(guid, dev->descriptor.idVendor,
dev->descriptor.idProduct, "0");
} }
memset(ss, 0, sizeof(struct us_data));
/*
* Now check if we have seen this GUID before /* Initialize the mutexes only when the struct is new */
* We're looking for a device with a matching GUID that isn't init_completion(&(ss->notify));
* already on the system init_MUTEX_LOCKED(&(ss->dev_semaphore));
*/
ss = us_list; /* copy over the subclass and protocol data */
while ((ss != NULL) && ss->subclass = subclass;
((ss->flags & US_FL_DEV_ATTACHED) || ss->protocol = protocol;
!GUID_EQUAL(guid, ss->guid))) ss->flags = flags | US_FL_DEV_ATTACHED;
ss = ss->next; ss->unusual_dev = unusual_dev;
if (ss != NULL) { /* copy over the endpoint data */
/* Existing device -- re-connect */ ss->ep_in = ep_in->bEndpointAddress &
US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", USB_ENDPOINT_NUMBER_MASK;
GUID_ARGS(guid)); ss->ep_out = ep_out->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
/* lock the device pointers */ if (ep_int) {
down(&(ss->dev_semaphore)); ss->ep_int = ep_int->bEndpointAddress &
/* establish the connection to the new device upon reconnect */
ss->ifnum = ifnum;
ss->pusb_dev = dev;
ss->flags |= US_FL_DEV_ATTACHED;
/* copy over the endpoint data */
ss->ep_in = ep_in->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
ss->ep_out = ep_out->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK; USB_ENDPOINT_NUMBER_MASK;
if (ep_int) { ss->ep_bInterval = ep_int->bInterval;
ss->ep_int = ep_int->bEndpointAddress & }
USB_ENDPOINT_NUMBER_MASK; else
ss->ep_bInterval = ep_int->bInterval; ss->ep_int = ss->ep_bInterval = 0;
}
/* establish the connection to the new device */
ss->ifnum = ifnum;
ss->pusb_dev = dev;
/* copy over the identifiying strings */
strncpy(ss->vendor, mf, USB_STOR_STRING_LEN);
strncpy(ss->product, prod, USB_STOR_STRING_LEN);
strncpy(ss->serial, serial, USB_STOR_STRING_LEN);
if (strlen(ss->vendor) == 0) {
if (unusual_dev->vendorName)
strncpy(ss->vendor, unusual_dev->vendorName,
USB_STOR_STRING_LEN);
else else
ss->ep_int = ss->ep_bInterval = 0; strncpy(ss->vendor, "Unknown",
USB_STOR_STRING_LEN);
/* allocate the URB, the usb_ctrlrequest, and the IRQ URB */ }
if (usb_stor_allocate_urbs(ss)) if (strlen(ss->product) == 0) {
goto BadDevice; if (unusual_dev->productName)
strncpy(ss->product, unusual_dev->productName,
/* Re-Initialize the device if it needs it */ USB_STOR_STRING_LEN);
if (unusual_dev && unusual_dev->initFunction)
(unusual_dev->initFunction)(ss);
/* unlock the device pointers */
up(&(ss->dev_semaphore));
} else {
/* New device -- allocate memory and initialize */
US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
if ((ss = (struct us_data *)kmalloc(sizeof(struct us_data),
GFP_KERNEL)) == NULL) {
printk(KERN_WARNING USB_STORAGE "Out of memory\n");
usb_put_dev(dev);
return -ENOMEM;
}
memset(ss, 0, sizeof(struct us_data));
new_device = 1;
/* Initialize the mutexes only when the struct is new */
init_completion(&(ss->notify));
init_MUTEX_LOCKED(&(ss->dev_semaphore));
/* copy over the subclass and protocol data */
ss->subclass = subclass;
ss->protocol = protocol;
ss->flags = flags | US_FL_DEV_ATTACHED;
ss->unusual_dev = unusual_dev;
/* copy over the endpoint data */
ss->ep_in = ep_in->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
ss->ep_out = ep_out->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
if (ep_int) {
ss->ep_int = ep_int->bEndpointAddress &
USB_ENDPOINT_NUMBER_MASK;
ss->ep_bInterval = ep_int->bInterval;
}
else else
ss->ep_int = ss->ep_bInterval = 0; strncpy(ss->product, "Unknown",
USB_STOR_STRING_LEN);
/* establish the connection to the new device */ }
ss->ifnum = ifnum; if (strlen(ss->serial) == 0)
ss->pusb_dev = dev; strncpy(ss->serial, "None", USB_STOR_STRING_LEN);
/* copy over the identifiying strings */
strncpy(ss->vendor, mf, USB_STOR_STRING_LEN);
strncpy(ss->product, prod, USB_STOR_STRING_LEN);
strncpy(ss->serial, serial, USB_STOR_STRING_LEN);
if (strlen(ss->vendor) == 0) {
if (unusual_dev->vendorName)
strncpy(ss->vendor, unusual_dev->vendorName,
USB_STOR_STRING_LEN);
else
strncpy(ss->vendor, "Unknown",
USB_STOR_STRING_LEN);
}
if (strlen(ss->product) == 0) {
if (unusual_dev->productName)
strncpy(ss->product, unusual_dev->productName,
USB_STOR_STRING_LEN);
else
strncpy(ss->product, "Unknown",
USB_STOR_STRING_LEN);
}
if (strlen(ss->serial) == 0)
strncpy(ss->serial, "None", USB_STOR_STRING_LEN);
/* copy the GUID we created before */
memcpy(ss->guid, guid, sizeof(guid));
/*
* Set the handler pointers based on the protocol
* Again, this data is persistant across reattachments
*/
switch (ss->protocol) {
case US_PR_CB:
ss->transport_name = "Control/Bulk";
ss->transport = usb_stor_CB_transport;
ss->transport_reset = usb_stor_CB_reset;
ss->max_lun = 7;
break;
case US_PR_CBI:
ss->transport_name = "Control/Bulk/Interrupt";
ss->transport = usb_stor_CBI_transport;
ss->transport_reset = usb_stor_CB_reset;
ss->max_lun = 7;
break;
case US_PR_BULK: /*
ss->transport_name = "Bulk"; * Set the handler pointers based on the protocol
ss->transport = usb_stor_Bulk_transport; * Again, this data is persistant across reattachments
ss->transport_reset = usb_stor_Bulk_reset; */
ss->max_lun = usb_stor_Bulk_max_lun(ss); switch (ss->protocol) {
break; case US_PR_CB:
ss->transport_name = "Control/Bulk";
ss->transport = usb_stor_CB_transport;
ss->transport_reset = usb_stor_CB_reset;
ss->max_lun = 7;
break;
case US_PR_CBI:
ss->transport_name = "Control/Bulk/Interrupt";
ss->transport = usb_stor_CBI_transport;
ss->transport_reset = usb_stor_CB_reset;
ss->max_lun = 7;
break;
case US_PR_BULK:
ss->transport_name = "Bulk";
ss->transport = usb_stor_Bulk_transport;
ss->transport_reset = usb_stor_Bulk_reset;
ss->max_lun = usb_stor_Bulk_max_lun(ss);
break;
#ifdef CONFIG_USB_STORAGE_HP8200e #ifdef CONFIG_USB_STORAGE_HP8200e
case US_PR_SCM_ATAPI: case US_PR_SCM_ATAPI:
ss->transport_name = "SCM/ATAPI"; ss->transport_name = "SCM/ATAPI";
ss->transport = hp8200e_transport; ss->transport = hp8200e_transport;
ss->transport_reset = usb_stor_CB_reset; ss->transport_reset = usb_stor_CB_reset;
ss->max_lun = 1; ss->max_lun = 1;
break; break;
#endif #endif
#ifdef CONFIG_USB_STORAGE_SDDR09 #ifdef CONFIG_USB_STORAGE_SDDR09
case US_PR_EUSB_SDDR09: case US_PR_EUSB_SDDR09:
ss->transport_name = "EUSB/SDDR09"; ss->transport_name = "EUSB/SDDR09";
ss->transport = sddr09_transport; ss->transport = sddr09_transport;
ss->transport_reset = usb_stor_CB_reset; ss->transport_reset = usb_stor_CB_reset;
ss->max_lun = 0; ss->max_lun = 0;
break; break;
#endif #endif
#ifdef CONFIG_USB_STORAGE_SDDR55 #ifdef CONFIG_USB_STORAGE_SDDR55
case US_PR_SDDR55: case US_PR_SDDR55:
ss->transport_name = "SDDR55"; ss->transport_name = "SDDR55";
ss->transport = sddr55_transport; ss->transport = sddr55_transport;
ss->transport_reset = sddr55_reset; ss->transport_reset = sddr55_reset;
ss->max_lun = 0; ss->max_lun = 0;
break; break;
#endif #endif
#ifdef CONFIG_USB_STORAGE_DPCM #ifdef CONFIG_USB_STORAGE_DPCM
case US_PR_DPCM_USB: case US_PR_DPCM_USB:
ss->transport_name = "Control/Bulk-EUSB/SDDR09"; ss->transport_name = "Control/Bulk-EUSB/SDDR09";
ss->transport = dpcm_transport; ss->transport = dpcm_transport;
ss->transport_reset = usb_stor_CB_reset; ss->transport_reset = usb_stor_CB_reset;
ss->max_lun = 1; ss->max_lun = 1;
break; break;
#endif #endif
#ifdef CONFIG_USB_STORAGE_FREECOM #ifdef CONFIG_USB_STORAGE_FREECOM
case US_PR_FREECOM: case US_PR_FREECOM:
ss->transport_name = "Freecom"; ss->transport_name = "Freecom";
ss->transport = freecom_transport; ss->transport = freecom_transport;
ss->transport_reset = usb_stor_freecom_reset; ss->transport_reset = usb_stor_freecom_reset;
ss->max_lun = 0; ss->max_lun = 0;
break; break;
#endif #endif
#ifdef CONFIG_USB_STORAGE_DATAFAB #ifdef CONFIG_USB_STORAGE_DATAFAB
case US_PR_DATAFAB: case US_PR_DATAFAB:
ss->transport_name = "Datafab Bulk-Only"; ss->transport_name = "Datafab Bulk-Only";
ss->transport = datafab_transport; ss->transport = datafab_transport;
ss->transport_reset = usb_stor_Bulk_reset; ss->transport_reset = usb_stor_Bulk_reset;
ss->max_lun = 1; ss->max_lun = 1;
break; break;
#endif #endif
#ifdef CONFIG_USB_STORAGE_JUMPSHOT #ifdef CONFIG_USB_STORAGE_JUMPSHOT
case US_PR_JUMPSHOT: case US_PR_JUMPSHOT:
ss->transport_name = "Lexar Jumpshot Control/Bulk"; ss->transport_name = "Lexar Jumpshot Control/Bulk";
ss->transport = jumpshot_transport; ss->transport = jumpshot_transport;
ss->transport_reset = usb_stor_Bulk_reset; ss->transport_reset = usb_stor_Bulk_reset;
ss->max_lun = 1; ss->max_lun = 1;
break; break;
#endif #endif
default: default:
/* ss->transport_name = "Unknown"; */ /* ss->transport_name = "Unknown"; */
goto BadDevice; goto BadDevice;
} }
US_DEBUGP("Transport: %s\n", ss->transport_name); US_DEBUGP("Transport: %s\n", ss->transport_name);
/* fix for single-lun devices */ /* fix for single-lun devices */
if (ss->flags & US_FL_SINGLE_LUN) if (ss->flags & US_FL_SINGLE_LUN)
ss->max_lun = 0; ss->max_lun = 0;
switch (ss->subclass) { switch (ss->subclass) {
case US_SC_RBC: case US_SC_RBC:
ss->protocol_name = "Reduced Block Commands (RBC)"; ss->protocol_name = "Reduced Block Commands (RBC)";
ss->proto_handler = usb_stor_transparent_scsi_command; ss->proto_handler = usb_stor_transparent_scsi_command;
break; break;
case US_SC_8020: case US_SC_8020:
ss->protocol_name = "8020i"; ss->protocol_name = "8020i";
ss->proto_handler = usb_stor_ATAPI_command; ss->proto_handler = usb_stor_ATAPI_command;
ss->max_lun = 0; ss->max_lun = 0;
break; break;
case US_SC_QIC: case US_SC_QIC:
ss->protocol_name = "QIC-157"; ss->protocol_name = "QIC-157";
ss->proto_handler = usb_stor_qic157_command; ss->proto_handler = usb_stor_qic157_command;
ss->max_lun = 0; ss->max_lun = 0;
break; break;
case US_SC_8070: case US_SC_8070:
ss->protocol_name = "8070i"; ss->protocol_name = "8070i";
ss->proto_handler = usb_stor_ATAPI_command; ss->proto_handler = usb_stor_ATAPI_command;
ss->max_lun = 0; ss->max_lun = 0;
break; break;
case US_SC_SCSI: case US_SC_SCSI:
ss->protocol_name = "Transparent SCSI"; ss->protocol_name = "Transparent SCSI";
ss->proto_handler = usb_stor_transparent_scsi_command; ss->proto_handler = usb_stor_transparent_scsi_command;
break; break;
case US_SC_UFI: case US_SC_UFI:
ss->protocol_name = "Uniform Floppy Interface (UFI)"; ss->protocol_name = "Uniform Floppy Interface (UFI)";
ss->proto_handler = usb_stor_ufi_command; ss->proto_handler = usb_stor_ufi_command;
break; break;
#ifdef CONFIG_USB_STORAGE_ISD200 #ifdef CONFIG_USB_STORAGE_ISD200
case US_SC_ISD200: case US_SC_ISD200:
ss->protocol_name = "ISD200 ATA/ATAPI"; ss->protocol_name = "ISD200 ATA/ATAPI";
ss->proto_handler = isd200_ata_command; ss->proto_handler = isd200_ata_command;
break; break;
#endif #endif
default: default:
/* ss->protocol_name = "Unknown"; */ /* ss->protocol_name = "Unknown"; */
goto BadDevice; goto BadDevice;
} }
US_DEBUGP("Protocol: %s\n", ss->protocol_name); US_DEBUGP("Protocol: %s\n", ss->protocol_name);
/* allocate the URB, the usb_ctrlrequest, and the IRQ URB */ /* allocate the URB, the usb_ctrlrequest, and the IRQ URB */
if (usb_stor_allocate_urbs(ss)) if (usb_stor_allocate_urbs(ss))
goto BadDevice; goto BadDevice;
/* /*
* Since this is a new device, we need to generate a scsi * Since this is a new device, we need to generate a scsi
* host definition, and register with the higher SCSI layers * host definition, and register with the higher SCSI layers
*/ */
/* Initialize the host template based on the default one */ /* Just before we start our control thread, initialize
memcpy(&(ss->htmplt), &usb_stor_host_template, * the device if it needs initialization */
sizeof(usb_stor_host_template)); if (unusual_dev && unusual_dev->initFunction)
unusual_dev->initFunction(ss);
/* start up our control thread */
atomic_set(&ss->sm_state, US_STATE_IDLE);
ss->pid = kernel_thread(usb_stor_control_thread, ss,
CLONE_VM);
if (ss->pid < 0) {
printk(KERN_WARNING USB_STORAGE
"Unable to start control thread\n");
goto BadDevice;
}
/* Grab the next host number */ /* wait for the thread to start */
ss->host_number = my_host_number++; wait_for_completion(&(ss->notify));
/* We abuse this pointer so we can pass the ss pointer to /* unlock the device pointers */
* the host controller thread in us_detect. But how else are up(&(ss->dev_semaphore));
* we to do it?
*/
(struct us_data *)ss->htmplt.proc_dir = ss;
/* Just before we start our control thread, initialize
* the device if it needs initialization */
if (unusual_dev && unusual_dev->initFunction)
unusual_dev->initFunction(ss);
/* start up our control thread */
atomic_set(&ss->sm_state, US_STATE_IDLE);
ss->pid = kernel_thread(usb_stor_control_thread, ss,
CLONE_VM);
if (ss->pid < 0) {
printk(KERN_WARNING USB_STORAGE
"Unable to start control thread\n");
goto BadDevice;
}
/* wait for the thread to start */ /* now register */
wait_for_completion(&(ss->notify)); ss->host = scsi_register(&usb_stor_host_template, sizeof(ss));
if (ss->host) {
printk(KERN_WARNING USB_STORAGE
"Unable to register the scsi host\n");
/* unlock the device pointers */ /* tell the control thread to exit */
up(&(ss->dev_semaphore)); ss->srb = NULL;
up(&ss->sema);
/* now register - our detect function will be called */ wait_for_completion(&ss->notify);
ss->htmplt.module = THIS_MODULE;
result = scsi_register_host(&(ss->htmplt)); /* re-lock the device pointers */
if (result) { down(&ss->dev_semaphore);
printk(KERN_WARNING USB_STORAGE goto BadDevice;
"Unable to register the scsi host\n"); }
/* tell the control thread to exit */
ss->srb = NULL;
up(&ss->sema);
wait_for_completion(&ss->notify);
/* re-lock the device pointers */
down(&ss->dev_semaphore);
goto BadDevice;
}
/* lock access to the data structures */ /* now add the host */
down(&us_list_semaphore); result = scsi_add_host(ss->host, NULL);
if (result) {
printk(KERN_WARNING USB_STORAGE
"Unable to add the scsi host\n");
/* put us in the list */ /* tell the control thread to exit */
ss->next = us_list; ss->srb = NULL;
us_list = ss; up(&ss->sema);
wait_for_completion(&ss->notify);
/* release the data structure lock */ /* re-lock the device pointers */
up(&us_list_semaphore); down(&ss->dev_semaphore);
goto BadDevice;
} }
ss->host->hostdata[0] = (unsigned long)ss;
scsi_set_device(ss->host, &intf->dev);
printk(KERN_DEBUG printk(KERN_DEBUG
"WARNING: USB Mass Storage data integrity not assured\n"); "WARNING: USB Mass Storage data integrity not assured\n");
printk(KERN_DEBUG printk(KERN_DEBUG
...@@ -1041,12 +956,11 @@ static int storage_probe(struct usb_interface *intf, ...@@ -1041,12 +956,11 @@ static int storage_probe(struct usb_interface *intf,
/* we come here if there are any problems */ /* we come here if there are any problems */
/* ss->dev_semaphore must be locked */ /* ss->dev_semaphore must be locked */
BadDevice: BadDevice:
US_DEBUGP("storage_probe() failed\n"); US_DEBUGP("storage_probe() failed\n");
usb_stor_deallocate_urbs(ss); usb_stor_deallocate_urbs(ss);
up(&ss->dev_semaphore); up(&ss->dev_semaphore);
if (new_device) kfree(ss);
kfree(ss);
return -EIO; return -EIO;
} }
...@@ -1065,9 +979,52 @@ static void storage_disconnect(struct usb_interface *intf) ...@@ -1065,9 +979,52 @@ static void storage_disconnect(struct usb_interface *intf)
return; return;
} }
/* lock device access -- no need to unlock, as we're going away */
down(&(ss->dev_semaphore)); down(&(ss->dev_semaphore));
/* remove the pointer to the data structure we were using */
(struct us_data*)ss->host->hostdata[0] = NULL;
/* begin SCSI host removal sequence */
if(scsi_remove_host(ss->host)) {
US_DEBUGP("-- SCSI refused to unregister\n");
BUG();
return;
};
/* finish SCSI host removal sequence */
scsi_unregister(ss->host);
/* Kill the control threads
*
* Enqueue the command, wake up the thread, and wait for
* notification that it has exited.
*/
US_DEBUGP("-- sending exit command to thread\n");
BUG_ON(atomic_read(&ss->sm_state) != US_STATE_IDLE);
ss->srb = NULL;
up(&(ss->sema));
wait_for_completion(&(ss->notify));
/* free allocated urbs */
usb_stor_deallocate_urbs(ss); usb_stor_deallocate_urbs(ss);
up(&(ss->dev_semaphore));
/* If there's extra data in the us_data structure then
* free that first */
if (ss->extra) {
/* call the destructor routine, if it exists */
if (ss->extra_destructor) {
US_DEBUGP("-- calling extra_destructor()\n");
ss->extra_destructor(ss->extra);
}
/* destroy the extra data */
US_DEBUGP("-- freeing the data structure\n");
kfree(ss->extra);
}
/* free the structure itself */
kfree (ss);
} }
/*********************************************************************** /***********************************************************************
...@@ -1078,11 +1035,6 @@ int __init usb_stor_init(void) ...@@ -1078,11 +1035,6 @@ int __init usb_stor_init(void)
{ {
printk(KERN_INFO "Initializing USB Mass Storage driver...\n"); printk(KERN_INFO "Initializing USB Mass Storage driver...\n");
/* initialize internal global data elements */
us_list = NULL;
init_MUTEX(&us_list_semaphore);
my_host_number = 0;
/* register the driver, return -1 if error */ /* register the driver, return -1 if error */
if (usb_register(&usb_storage_driver) < 0) if (usb_register(&usb_storage_driver) < 0)
return -1; return -1;
...@@ -1094,16 +1046,16 @@ int __init usb_stor_init(void) ...@@ -1094,16 +1046,16 @@ int __init usb_stor_init(void)
void __exit usb_stor_exit(void) void __exit usb_stor_exit(void)
{ {
struct us_data *next;
US_DEBUGP("usb_stor_exit() called\n"); US_DEBUGP("usb_stor_exit() called\n");
/* Deregister the driver /* Deregister the driver
* This eliminates races with probes and disconnects * This will cause disconnect() to be called for each
* attached unit
*/ */
US_DEBUGP("-- calling usb_deregister()\n"); US_DEBUGP("-- calling usb_deregister()\n");
usb_deregister(&usb_storage_driver) ; usb_deregister(&usb_storage_driver) ;
#if 0
/* While there are still virtual hosts, unregister them /* While there are still virtual hosts, unregister them
* Note that it's important to do this completely before removing * Note that it's important to do this completely before removing
* the structures because of possible races with the /proc * the structures because of possible races with the /proc
...@@ -1111,7 +1063,7 @@ void __exit usb_stor_exit(void) ...@@ -1111,7 +1063,7 @@ void __exit usb_stor_exit(void)
*/ */
for (next = us_list; next; next = next->next) { for (next = us_list; next; next = next->next) {
US_DEBUGP("-- calling scsi_unregister_host()\n"); US_DEBUGP("-- calling scsi_unregister_host()\n");
scsi_unregister_host(&(next->htmplt)); scsi_unregister_host(&usb_stor_host_template);
} }
/* While there are still structures, free them. Note that we are /* While there are still structures, free them. Note that we are
...@@ -1137,11 +1089,12 @@ void __exit usb_stor_exit(void) ...@@ -1137,11 +1089,12 @@ void __exit usb_stor_exit(void)
} }
/* free the structure itself */ /* free the structure itself */
kfree (us_list); kfree (us_list);
/* advance the list pointer */ /* advance the list pointer */
us_list = next; us_list = next;
} }
#endif
} }
module_init(usb_stor_init); module_init(usb_stor_init);
......
...@@ -52,33 +52,6 @@ ...@@ -52,33 +52,6 @@
#include "scsi.h" #include "scsi.h"
#include "hosts.h" #include "hosts.h"
/*
* GUID definitions
*/
#define GUID(x) __u32 x[3]
#define GUID_EQUAL(x, y) (x[0] == y[0] && x[1] == y[1] && x[2] == y[2])
#define GUID_CLEAR(x) x[0] = x[1] = x[2] = 0;
#define GUID_NONE(x) (!x[0] && !x[1] && !x[2])
#define GUID_FORMAT "%08x%08x%08x"
#define GUID_ARGS(x) x[0], x[1], x[2]
static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *serial)
{
pg[0] = (vendor << 16) | product;
pg[1] = pg[2] = 0;
while (*serial) {
pg[1] <<= 4;
pg[1] |= pg[2] >> 28;
pg[2] <<= 4;
if (*serial >= 'a')
*serial -= 'a' - 'A';
pg[2] |= (*serial <= '9' && *serial >= '0') ? *serial - '0'
: *serial - 'A' + 10;
serial++;
}
}
struct us_data; struct us_data;
/* /*
...@@ -124,8 +97,6 @@ typedef void (*extra_data_destructor)(void *); /* extra data destructor */ ...@@ -124,8 +97,6 @@ typedef void (*extra_data_destructor)(void *); /* extra data destructor */
/* we allocate one of these for every device that we remember */ /* we allocate one of these for every device that we remember */
struct us_data { struct us_data {
struct us_data *next; /* next device */
/* The device we're working with /* The device we're working with
* It's important to note: * It's important to note:
* (o) you must hold dev_semaphore to change pusb_dev * (o) you must hold dev_semaphore to change pusb_dev
...@@ -163,11 +134,7 @@ struct us_data { ...@@ -163,11 +134,7 @@ struct us_data {
proto_cmnd proto_handler; /* protocol handler */ proto_cmnd proto_handler; /* protocol handler */
/* SCSI interfaces */ /* SCSI interfaces */
GUID(guid); /* unique dev id */
struct Scsi_Host *host; /* our dummy host data */ struct Scsi_Host *host; /* our dummy host data */
Scsi_Host_Template htmplt; /* own host template */
int host_number; /* to find us */
int host_no; /* allocated by scsi */
Scsi_Cmnd *srb; /* current srb */ Scsi_Cmnd *srb; /* current srb */
/* thread information */ /* thread information */
...@@ -192,10 +159,6 @@ struct us_data { ...@@ -192,10 +159,6 @@ struct us_data {
extra_data_destructor extra_destructor;/* extra data destructor */ extra_data_destructor extra_destructor;/* extra data destructor */
}; };
/* The list of structures and the protective lock for them */
extern struct us_data *us_list;
extern struct semaphore us_list_semaphore;
/* The structure which defines our driver */ /* The structure which defines our driver */
extern struct usb_driver usb_storage_driver; extern struct usb_driver usb_storage_driver;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment