summaryrefslogtreecommitdiffstats
path: root/drivers/usb/usb-storage.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/usb-storage.c')
-rw-r--r--drivers/usb/usb-storage.c514
1 files changed, 300 insertions, 214 deletions
diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c
index 956e80a0a..0cac2ef13 100644
--- a/drivers/usb/usb-storage.c
+++ b/drivers/usb/usb-storage.c
@@ -52,6 +52,10 @@
/* direction table -- this indicates the direction of the data
* transfer for each command code -- a 1 indicates input
*/
+/* FIXME: we need to use the new direction indicators in the Scsi_Cmnd
+ * structure, not this table. First we need to evaluate if it's being set
+ * correctly for us, though
+ */
unsigned char us_direction[256/8] = {
0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77,
0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
@@ -81,13 +85,12 @@ struct us_data {
__u8 ep_int; /* interrupt . */
__u8 subclass; /* as in overview */
__u8 protocol; /* .............. */
- __u8 attention_done; /* force attn on first cmd */
trans_cmnd transport; /* protocol specific do cmd */
trans_reset transport_reset; /* .......... device reset */
proto_cmnd proto_handler; /* protocol handler */
GUID(guid); /* unique dev id */
struct Scsi_Host *host; /* our dummy host data */
- Scsi_Host_Template *htmplt; /* own host template */
+ Scsi_Host_Template htmplt; /* own host template */
int host_number; /* to find us */
int host_no; /* allocated by scsi */
Scsi_Cmnd *srb; /* current srb */
@@ -114,7 +117,9 @@ struct us_data {
#define US_ACT_BUS_RESET 4
#define US_ACT_HOST_RESET 5
+/* The list of structures and the protective lock for them */
static struct us_data *us_list;
+spinlock_t us_list_spinlock = SPIN_LOCK_UNLOCKED;
static void * storage_probe(struct usb_device *dev, unsigned int ifnum);
static void storage_disconnect(struct usb_device *dev, void *ptr);
@@ -225,7 +230,7 @@ static void us_transfer(Scsi_Cmnd *srb, int dir_in)
srb->result = result;
}
-/* calculate the length of the data transfer (not the command) for any
+/* Calculate the length of the data transfer (not the command) for any
* given SCSI command
*/
static unsigned int us_transfer_length(Scsi_Cmnd *srb)
@@ -243,14 +248,18 @@ static unsigned int us_transfer_length(Scsi_Cmnd *srb)
case TEST_UNIT_READY:
return 0;
+ /* FIXME: these should be removed and tested */
case REQUEST_SENSE:
case INQUIRY:
case MODE_SENSE:
return srb->cmnd[4];
+ /* FIXME: this needs to come out when the other
+ * fix is in place */
case READ_CAPACITY:
return 8;
+ /* FIXME: these should be removed and tested */
case LOG_SENSE:
case MODE_SENSE_10:
return (srb->cmnd[7] << 8) + srb->cmnd[8];
@@ -795,7 +804,7 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
return USB_STOR_TRANSPORT_ERROR;
}
- /* FIXME: we need to handle NAKs here */
+ /* FIXME: we need to handle NAKs here */
return USB_STOR_TRANSPORT_ERROR;
}
@@ -812,7 +821,9 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
/* STATUS STAGE */
/* go to sleep until we get this interrup */
- /* FIXME: this should be changed to use a timeout */
+ /* FIXME: this should be changed to use a timeout -- or let the
+ * device reset routine up() this for us to unjam us
+ */
down(&(us->ip_waitq));
/* FIXME: currently this code is unreachable, but the idea is
@@ -827,8 +838,8 @@ static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
US_DEBUGP("Got interrupt data 0x%x\n", us->ip_data);
/* UFI gives us ASC and ASCQ, like a request sense */
- /* FIXME: is this right? Do REQUEST_SENSE and INQUIRY need special
- * case handling?
+ /* REQUEST_SENSE and INQUIRY don't affect the sense data, so we
+ * ignore the information for those commands
*/
if (us->subclass == US_SC_UFI) {
if (srb->cmnd[0] == REQUEST_SENSE ||
@@ -887,7 +898,7 @@ static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
return USB_STOR_TRANSPORT_ERROR;
}
- /* FIXME: we need to handle NAKs here */
+ /* FIXME: we need to handle NAKs here */
return USB_STOR_TRANSPORT_ERROR;
}
@@ -1103,7 +1114,8 @@ static int us_detect(struct SHT *sht)
static int us_release(struct Scsi_Host *psh)
{
struct us_data *us = (struct us_data *)psh->hostdata[0];
- struct us_data *prev = (struct us_data *)&us_list;
+ struct us_data *prev;
+ unsigned long flags;
if (us->irq_handle) {
usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe);
@@ -1111,18 +1123,20 @@ static int us_release(struct Scsi_Host *psh)
}
/* FIXME: release the interface claim here? */
- // if (us->pusb_dev)
- // usb_deregister(&storage_driver);
- /* FIXME - leaves hanging host template copy */
- /* (because scsi layer uses it after removal !!!) */
+ /* FIXME: we need to move this elsewhere --
+ * the remove function only gets called to remove the module
+ */
+ spin_lock_irqsave(&us_list_spinlock, flags);
if (us_list == us)
us_list = us->next;
else {
+ prev = us_list;
while (prev->next != us)
prev = prev->next;
prev->next = us->next;
}
+ spin_unlock_irqrestore(&us_list_spinlock, flags);
return 0;
}
@@ -1190,11 +1204,20 @@ static int us_host_reset( Scsi_Cmnd *srb )
int usb_stor_proc_info (char *buffer, char **start, off_t offset,
int length, int hostno, int inout)
{
- struct us_data *us = us_list;
+ struct us_data *us;
char *pos = buffer;
char *tmp_ptr;
+ unsigned long flags;
+
+ /* if someone is sending us data, just throw it away */
+ if (inout)
+ return length;
+
+ /* lock the data structures */
+ spin_lock_irqsave(&us_list_spinlock, flags);
/* find our data from hostno */
+ us = us_list;
while (us) {
if (us->host_no == hostno)
break;
@@ -1202,13 +1225,11 @@ int usb_stor_proc_info (char *buffer, char **start, off_t offset,
}
/* if we couldn't find it, we return an error */
- if (!us)
+ if (!us) {
+ spin_unlock_irqrestore(&us_list_spinlock, flags);
return -ESRCH;
-
- /* if someone is sending us data, just throw it away */
- if (inout)
- return length;
-
+ }
+
/* print the controler name */
SPRINTF ("Host scsi%d: usb-storage\n", hostno);
@@ -1254,6 +1275,9 @@ int usb_stor_proc_info (char *buffer, char **start, off_t offset,
/* show the GUID of the device */
SPRINTF(" GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid));
+ /* release our lock on the data structures */
+ spin_unlock_irqrestore(&us_list_spinlock, flags);
+
/*
* Calculate start of next buffer, and return value.
*/
@@ -1365,6 +1389,7 @@ static int usb_stor_control_thread(void * __us)
switch (action) {
case US_ACT_COMMAND:
/* bad device */
+ /* FIXME: we need to enable and test multiple LUNs */
if (us->srb->target || us->srb->lun) {
US_DEBUGP( "Bad device number (%d/%d) or dev 0x%x\n",
us->srb->target, us->srb->lun, (unsigned int)us->pusb_dev);
@@ -1378,9 +1403,12 @@ static int usb_stor_control_thread(void * __us)
/* our device has gone - pretend not ready */
/* FIXME: we also need to handle INQUIRY here,
* probably */
+ /* FIXME: fix return codes and sense buffer handling */
if (!us->pusb_dev) {
+ US_DEBUGP("Request is for removed device\n");
if (us->srb->cmnd[0] == REQUEST_SENSE) {
- memcpy(us->srb->request_buffer, sense_notready,
+ memcpy(us->srb->request_buffer,
+ sense_notready,
sizeof(sense_notready));
us->srb->result = DID_OK << 16;
} else {
@@ -1443,34 +1471,29 @@ static int usb_stor_control_thread(void * __us)
/* Probe to see if a new device is actually a SCSI device */
static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
{
- struct usb_interface_descriptor *interface;
int i;
char mf[32]; /* manufacturer */
char prod[32]; /* product */
char serial[32]; /* serial number */
struct us_data *ss = NULL;
- unsigned int flags = 0;
GUID(guid); /* Global Unique Identifier */
- struct us_data *prev;
- int protocol = 0;
- int subclass = 0;
+ int result;
+ unsigned long flags;
+
+ /* these are temporary copies -- we test on these, then put them
+ * in the us-data structure
+ */
+ __u8 ep_in = 0;
+ __u8 ep_out = 0;
+ __u8 ep_int = 0;
+ __u8 subclass = 0;
+ __u8 protocol = 0;
+
+ /* the altsettting 0 on the interface we're probing */
struct usb_interface_descriptor *altsetting =
&(dev->actconfig->interface[ifnum].altsetting[0]);
- /* clear the GUID and fetch the strings */
- GUID_CLEAR(guid);
- memset(mf, 0, sizeof(mf));
- memset(prod, 0, sizeof(prod));
- memset(serial, 0, sizeof(serial));
- if (dev->descriptor.iManufacturer)
- usb_string(dev, dev->descriptor.iManufacturer, mf, sizeof(mf));
- if (dev->descriptor.iProduct)
- usb_string(dev, dev->descriptor.iProduct, prod, sizeof(prod));
- if (dev->descriptor.iSerialNumber)
- usb_string(dev, dev->descriptor.iSerialNumber, serial, sizeof(serial));
-
- /* let's examine the device now */
-
+ /* FIXME: this isn't quite right... */
/* We make an exception for the shuttle E-USB */
if (dev->descriptor.idVendor == 0x04e6 &&
dev->descriptor.idProduct == 0x0001) {
@@ -1487,6 +1510,91 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
/* At this point, we know we've got a live one */
US_DEBUGP("USB Mass Storage device detected\n");
+ /*
+ * We are expecting a minimum of 2 endpoints - in and out (bulk).
+ * An optional interrupt is OK (necessary for CBI protocol).
+ * We will ignore any others.
+ */
+ for (i = 0; i < altsetting->bNumEndpoints; i++) {
+ /* is it an BULK endpoint? */
+ if ((altsetting->endpoint[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) {
+ if (altsetting->endpoint[i].bEndpointAddress &
+ USB_DIR_IN)
+ ep_in = altsetting->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ else
+ ep_out = altsetting->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+
+ /* is it an interrupt endpoint? */
+ if ((altsetting->endpoint[i].bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) {
+ ep_int = altsetting->endpoint[i].bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK;
+ }
+ }
+ US_DEBUGP("Endpoints In %d Out %d Int %d\n",
+ ep_in, ep_out, ep_int);
+
+ /* set the interface -- STALL is an acceptable response here */
+ result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0);
+ US_DEBUGP("Result from usb_set_interface is %d\n", result);
+ if (result == -EPIPE) {
+ usb_clear_halt(dev, usb_sndctrlpipe(dev, 0));
+ } else if (result != 0) {
+ /* it's not a stall, but another error -- time to bail */
+ return NULL;
+ }
+
+ /* shuttle E-USB */
+ /* FIXME: all we should need to do here is determine the protocol */
+ if (dev->descriptor.idVendor == 0x04e6 &&
+ dev->descriptor.idProduct == 0x0001) {
+ __u8 qstat[2];
+
+ result = usb_control_msg(ss->pusb_dev,
+ usb_rcvctrlpipe(dev,0),
+ 1, 0xC0,
+ 0, ss->ifnum,
+ qstat, 2, HZ*5);
+ US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
+ ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
+ result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
+ CBI_irq, 255, (void *)ss,
+ &ss->irq_handle);
+ if (result < 0)
+ return NULL;
+
+ /* FIXME: what is this?? */
+ down(&(ss->ip_waitq));
+ }
+
+ /* Do some basic sanity checks, and bail if we find a problem */
+ if (!ep_in || !ep_out || (protocol == US_PR_CBI && !ep_int)) {
+ US_DEBUGP("Problems with device\n");
+ return NULL;
+ }
+
+ /* At this point, we're committed to using the device */
+
+ /* clear the GUID and fetch the strings */
+ GUID_CLEAR(guid);
+ memset(mf, 0, sizeof(mf));
+ memset(prod, 0, sizeof(prod));
+ memset(serial, 0, sizeof(serial));
+ if (dev->descriptor.iManufacturer)
+ usb_string(dev, dev->descriptor.iManufacturer, mf,
+ sizeof(mf));
+ if (dev->descriptor.iProduct)
+ usb_string(dev, dev->descriptor.iProduct, prod,
+ sizeof(prod));
+ if (dev->descriptor.iSerialNumber)
+ usb_string(dev, dev->descriptor.iSerialNumber, serial,
+ sizeof(serial));
+
/* Create a GUID for this device */
if (dev->descriptor.iSerialNumber && serial[0]) {
/* If we have a serial number, and it's a non-NULL string */
@@ -1498,23 +1606,50 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
dev->descriptor.idProduct, "0");
}
- /* Now check if we have seen this GUID before, and restore
- * the flags if we find it
+ /* lock access to the data structures */
+ spin_lock_irqsave(&us_list_spinlock, flags);
+
+ /*
+ * Now check if we have seen this GUID before
+ * We're looking for a device with a matching GUID that isn't
+ * allready on the system
*/
- for (ss = us_list; ss != NULL; ss = ss->next) {
- if (!ss->pusb_dev && GUID_EQUAL(guid, ss->guid)) {
- US_DEBUGP("Found existing GUID " GUID_FORMAT "\n",
- GUID_ARGS(guid));
- flags = ss->flags;
- break;
+ ss = us_list;
+ while ((ss != NULL) &&
+ ((ss->pusb_dev) || !GUID_EQUAL(guid, ss->guid)))
+ ss = ss->next;
+
+ if (ss != NULL) {
+ /* Existing device -- re-connect */
+ US_DEBUGP("Found existing GUID " GUID_FORMAT "\n",
+ GUID_ARGS(guid));
+
+ /* establish the connection to the new device upon reconnect */
+ ss->ifnum = ifnum;
+ ss->pusb_dev = dev;
+
+ /* hook up the IRQ handler again */
+ if (ss->protocol == US_PR_CBI) {
+ /* set up so we'll wait for notification */
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
+
+ /* set up the IRQ pipe and handler */
+ /* FIXME: This needs to get period from the device */
+ US_DEBUGP("Allocating IRQ for CBI transport\n");
+ ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
+ result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
+ CBI_irq, 255,
+ (void *)ss, &ss->irq_handle);
+ US_DEBUGP("usb_request_irq returned %d\n", result);
}
- }
-
- /* If ss == NULL, then this is a new device. Allocate memory for it */
- if (!ss) {
- if ((ss = (struct us_data *)kmalloc(sizeof(*ss),
+ } 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");
+ spin_unlock_irqrestore(&us_list_spinlock, flags);
return NULL;
}
memset(ss, 0, sizeof(struct us_data));
@@ -1522,104 +1657,66 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
/* Initialize the mutexes only when the struct is new */
init_MUTEX_LOCKED(&(ss->sleeper));
init_MUTEX(&(ss->queue_exclusion));
- }
-
- /* establish the connection to the new device */
- interface = altsetting;
- ss->flags = flags;
- ss->ifnum = ifnum;
- ss->attention_done = 0;
- ss->pusb_dev = dev;
-
- /* If the device has subclass and protocol, then use that. Otherwise,
- * take data from the specific interface.
- */
- if (subclass) {
- ss->subclass = subclass;
- ss->protocol = protocol;
- } else {
- ss->subclass = interface->bInterfaceSubClass;
- ss->protocol = interface->bInterfaceProtocol;
- }
- /* set the handler pointers based on the protocol */
- US_DEBUGP("Transport: ");
- switch (ss->protocol) {
- case US_PR_CB:
- US_DEBUGPX("Control/Bulk\n");
- ss->transport = CB_transport;
- ss->transport_reset = CB_reset;
- break;
-
- case US_PR_CBI:
- US_DEBUGPX("Control/Bulk/Interrupt\n");
- ss->transport = CBI_transport;
- ss->transport_reset = CB_reset;
- break;
-
- case US_PR_BULK:
- US_DEBUGPX("Bulk\n");
- ss->transport = Bulk_transport;
- ss->transport_reset = Bulk_reset;
- break;
-
- default:
- US_DEBUGPX("Unknown\n");
- kfree(ss);
- return NULL;
- break;
- }
-
- /*
- * We are expecting a minimum of 2 endpoints - in and out (bulk).
- * An optional interrupt is OK (necessary for CBI protocol).
- * We will ignore any others.
- */
- for (i = 0; i < interface->bNumEndpoints; i++) {
- /* is it an BULK endpoint? */
- if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK) {
- if (interface->endpoint[i].bEndpointAddress & USB_DIR_IN)
- ss->ep_in = interface->endpoint[i].bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- else
- ss->ep_out = interface->endpoint[i].bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
- }
-
- /* is it an interrupt endpoint? */
- if ((interface->endpoint[i].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT) {
- ss->ep_int = interface->endpoint[i].bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK;
+ /*
+ * If we've allready determined the subclass and protocol,
+ * use that. Otherwise, use the interface ones. This
+ * allows us to support devices which are compliant but
+ * don't announce it. Note that this information is
+ * maintained in the us_data struct so we only have to do
+ * this for new devices.
+ */
+ if (subclass) {
+ ss->subclass = subclass;
+ ss->protocol = protocol;
+ } else {
+ ss->subclass = altsetting->bInterfaceSubClass;
+ ss->protocol = altsetting->bInterfaceProtocol;
}
- }
- US_DEBUGP("Endpoints In %d Out %d Int %d\n",
- ss->ep_in, ss->ep_out, ss->ep_int);
- /* Do some basic sanity checks, and bail if we find a problem */
- if (usb_set_interface(dev, interface->bInterfaceNumber, 0) ||
- !ss->ep_in || !ss->ep_out ||
- (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
- US_DEBUGP("Problems with device\n");
- if (ss->host) {
- kfree(ss->htmplt->name);
- kfree(ss->htmplt);
- }
+ /* copy over the endpoint data */
+ ss->ep_in = ep_in;
+ ss->ep_out = ep_out;
+ ss->ep_int = ep_int;
- kfree(ss);
- return NULL;
- }
+ /* establish the connection to the new device */
+ ss->ifnum = ifnum;
+ ss->pusb_dev = dev;
- /* If this is a new device (i.e. we haven't seen it before), we need to
- * generate a scsi host definition, and register with scsi above us
- */
- if (!ss->host) {
/* copy the GUID we created before */
- US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid));
memcpy(ss->guid, guid, sizeof(guid));
+
+ /*
+ * Set the handler pointers based on the protocol
+ * Again, this data is persistant across reattachments
+ */
+ US_DEBUGP("Transport: ");
+ switch (ss->protocol) {
+ case US_PR_CB:
+ US_DEBUGPX("Control/Bulk\n");
+ ss->transport = CB_transport;
+ ss->transport_reset = CB_reset;
+ break;
+
+ case US_PR_CBI:
+ US_DEBUGPX("Control/Bulk/Interrupt\n");
+ ss->transport = CBI_transport;
+ ss->transport_reset = CB_reset;
+ break;
+
+ case US_PR_BULK:
+ US_DEBUGPX("Bulk\n");
+ ss->transport = Bulk_transport;
+ ss->transport_reset = Bulk_reset;
+ break;
+
+ default:
+ US_DEBUGPX("Unknown\n");
+ kfree(ss);
+ return NULL;
+ break;
+ }
- /* set class specific stuff */
US_DEBUGP("Protocol: ");
switch (ss->subclass) {
case US_SC_RBC:
@@ -1656,97 +1753,75 @@ static void * storage_probe(struct usb_device *dev, unsigned int ifnum)
break;
}
- /* Allocate memory for the SCSI Host Template */
- if ((ss->htmplt = (Scsi_Host_Template *)
- kmalloc(sizeof(Scsi_Host_Template),GFP_KERNEL))==NULL ) {
- printk(KERN_WARNING USB_STORAGE "Out of memory\n");
-
- kfree(ss);
- return NULL;
+ if (ss->protocol == US_PR_CBI) {
+ /* set up so we'll wait for notification */
+ init_MUTEX_LOCKED(&(ss->ip_waitq));
+
+ /* set up the IRQ pipe and handler */
+ /* FIXME: This needs to get period from the device */
+ US_DEBUGP("Allocating IRQ for CBI transport\n");
+ ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
+ result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
+ CBI_irq, 255,
+ (void *)ss, &ss->irq_handle);
+ US_DEBUGP("usb_request_irq returned %d", result);
}
+
+ /*
+ * Since this is a new device, we need to generate a scsi
+ * host definition, and register with the higher SCSI layers
+ */
/* Initialize the host template based on the default one */
- memcpy(ss->htmplt, &my_host_template, sizeof(my_host_template));
+ memcpy(&(ss->htmplt), &my_host_template,
+ sizeof(my_host_template));
/* Grab the next host number */
ss->host_number = my_host_number++;
-
- /* MDD: FIXME: this is bad. We abuse this pointer so we
+
+ /* FIXME: this is bad. We abuse this pointer so we
* can pass the ss pointer to the host controler thread
* in us_detect
*/
- (struct us_data *)ss->htmplt->proc_dir = ss;
-
- /* shuttle E-USB */
- if (dev->descriptor.idVendor == 0x04e6 &&
- dev->descriptor.idProduct == 0x0001) {
- __u8 qstat[2];
- int result;
-
- result = usb_control_msg(ss->pusb_dev,
- usb_rcvctrlpipe(dev,0),
- 1, 0xC0,
- 0, ss->ifnum,
- qstat, 2, HZ*5);
- US_DEBUGP("C0 status 0x%x 0x%x\n", qstat[0], qstat[1]);
- init_MUTEX_LOCKED(&(ss->ip_waitq));
- ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
- result = usb_request_irq(ss->pusb_dev, ss->irqpipe,
- CBI_irq, 255, (void *)ss,
- &ss->irq_handle);
- if (result < 0)
- return NULL;
- /* FIXME: what is this?? */
- down(&(ss->ip_waitq));
- } else if (ss->protocol == US_PR_CBI) {
- int result;
-
- /* set up so we'll wait for notification */
- init_MUTEX_LOCKED(&(ss->ip_waitq));
-
- /* set up the IRQ pipe and handler */
- /* FIXME: This needs to get the period from the device */
- ss->irqpipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int);
- result = usb_request_irq(ss->pusb_dev, ss->irqpipe, CBI_irq,
- 255, (void *)ss, &ss->irq_handle);
- if (result) {
- US_DEBUGP("usb_request_irq failed (0x%x), No interrupt for CBI\n",
- result);
- }
- }
-
-
- /* start up our thread */
+ (struct us_data *)ss->htmplt.proc_dir = ss;
+
+ /* start up our thread */
{
DECLARE_MUTEX_LOCKED(sem);
-
+
ss->notify = &sem;
ss->pid = kernel_thread(usb_stor_control_thread, ss,
- CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+ CLONE_FS | CLONE_FILES |
+ CLONE_SIGHAND);
if (ss->pid < 0) {
- printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n");
- kfree(ss->htmplt);
-
+ printk(KERN_WARNING USB_STORAGE
+ "Unable to start control thread\n");
kfree(ss);
return NULL;
}
-
- /* wait for it to start */
+
+ /* wait for it to start */
down(&sem);
}
-
+
/* now register - our detect function will be called */
- ss->htmplt->module = &__this_module;
- scsi_register_module(MODULE_SCSI_HA, ss->htmplt);
-
+ ss->htmplt.module = &__this_module;
+ scsi_register_module(MODULE_SCSI_HA, &(ss->htmplt));
+
/* put us in the list */
ss->next = us_list;
- us_list = ss;
+ us_list = ss;
}
- printk(KERN_DEBUG "WARNING: USB Mass Storage data integrity not assured\n");
- printk(KERN_DEBUG "USB Mass Storage device found at %d\n", dev->devnum);
+ /* release the data structure lock */
+ spin_unlock_irqrestore(&us_list_spinlock, flags);
+ printk(KERN_DEBUG
+ "WARNING: USB Mass Storage data integrity not assured\n");
+ printk(KERN_DEBUG
+ "USB Mass Storage device found at %d\n", dev->devnum);
+
+ /* return a pointer for the disconnect function */
return ss;
}
@@ -1758,6 +1833,14 @@ static void storage_disconnect(struct usb_device *dev, void *ptr)
if (!ss)
return;
+ /* FIXME: we need mututal exclusion and resource freeing here */
+
+ /* release the IRQ, if we have one */
+ if (ss->irq_handle) {
+ usb_release_irq(ss->pusb_dev, ss->irq_handle, ss->irqpipe);
+ ss->irq_handle = NULL;
+ }
+
ss->pusb_dev = NULL;
}
@@ -1788,12 +1871,15 @@ void __exit usb_stor_exit(void)
{
static struct us_data *ptr;
- // FIXME: this needs to be put back to free _all_ the hosts
- // for (ptr = us_list; ptr != NULL; ptr = ptr->next)
- // scsi_unregister_module(MODULE_SCSI_HA, ptr->htmplt);
- printk("MDD: us_list->htmplt is 0x%x\n", (unsigned int)(us_list->htmplt));
- scsi_unregister_module(MODULE_SCSI_HA, us_list->htmplt);
+ /* unregister all the virtual hosts */
+ for (ptr = us_list; ptr != NULL; ptr = ptr->next)
+ scsi_unregister_module(MODULE_SCSI_HA, &(ptr->htmplt));
+
+ /* free up the data structures */
+
+ /* kill the threads */
+ /* deregister the driver */
usb_deregister(&storage_driver) ;
}