patch-2.3.99-pre9 linux/drivers/usb/usb-storage.c

Next file: linux/drivers/usb/usb-storage.h
Previous file: linux/drivers/usb/usb-ohci.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.99-pre8/linux/drivers/usb/usb-storage.c linux/drivers/usb/usb-storage.c
@@ -1,7 +1,10 @@
 /* Driver for USB Mass Storage compliant devices
  *
- * (c) 1999 Michael Gee (michael@linuxspecific.com)
- * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ * Initial work by:
+ *   (c) 1999 Michael Gee (michael@linuxspecific.com)
+ *
+ * Current development and maintainance by:
+ *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
  *
  * This driver is based on the 'USB Mass Storage Class' document. This
  * describes in detail the protocol used to communicate with such
@@ -16,6 +19,9 @@
  *
  * Also, for certain devices, the interrupt endpoint is used to convey
  * status of a command.
+ *
+ * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more
+ * information about this driver.
  */
 
 #include <linux/module.h>
@@ -111,10 +117,15 @@
 	struct semaphore	ip_waitq;	 /* for CBI interrupts	 */
 	int			ip_wanted;	 /* is an IRQ expected?	 */
 
+	/* interrupt communications data */
 	struct semaphore	irq_urb_sem;	 /* to protect irq_urb	 */
 	struct urb		*irq_urb;	 /* for USB int requests */
 	unsigned char		irqbuf[2];	 /* buffer for USB IRQ	 */
 
+	/* control and bulk communications data */
+	struct semaphore	current_urb_sem; /* to protect irq_urb	 */
+	struct urb		*current_urb;	 /* non-int USB requests */
+
 	/* mutual exclusion structures */
 	struct semaphore	notify;		 /* thread begin/end	    */
 	struct semaphore	sleeper;	 /* to sleep the thread on  */
@@ -147,6 +158,155 @@
  * Data transfer routines
  ***********************************************************************/
 
+/* This is the completion handler which will wake us up when an URB
+ * completes.
+ */
+static void usb_stor_blocking_completion(urb_t *urb)
+{
+	api_wrapper_data *awd = (api_wrapper_data *)urb->context;
+
+	if (waitqueue_active(awd->wakeup))
+		wake_up(awd->wakeup);
+}
+
+/* This is our function to emulate usb_control_msg() but give us enough
+ * access to make aborts/resets work
+ */
+int usb_stor_control_msg(struct us_data *us, unsigned int pipe,
+			 u8 request, u8 requesttype, u16 value, u16 index, 
+			 void *data, u16 size)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	DECLARE_WAIT_QUEUE_HEAD(wqh);
+	api_wrapper_data awd;
+	int status;
+	devrequest *dr;
+
+	/* allocate the device request structure */
+	dr = kmalloc(sizeof(devrequest), GFP_KERNEL);
+	if (!dr)
+		return -ENOMEM;
+
+	/* fill in the structure */
+	dr->requesttype = requesttype;
+	dr->request = request;
+	dr->value = cpu_to_le16(value);
+	dr->index = cpu_to_le16(index);
+	dr->length = cpu_to_le16(size);
+
+	/* set up data structures for the wakeup system */
+	awd.wakeup = &wqh;
+	awd.handler = 0;
+	init_waitqueue_head(&wqh); 	
+	add_wait_queue(&wqh, &wait);
+
+	/* lock the URB */
+	down(&(us->current_urb_sem));
+
+	/* fill the URB */
+	FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, 
+			 (unsigned char*) dr, data, size, 
+			 usb_stor_blocking_completion, &awd);
+
+	/* submit the URB */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	status = usb_submit_urb(us->current_urb);
+	if (status) {
+		/* something went wrong */
+		up(&(us->current_urb_sem));
+		remove_wait_queue(&wqh, &wait);
+		kfree(dr);
+		return status;
+	}
+
+	/* wait for the completion of the URB */
+	up(&(us->current_urb_sem));
+	if (us->current_urb->status == -EINPROGRESS)
+		schedule_timeout(10*HZ);
+	down(&(us->current_urb_sem));
+
+	/* we either timed out or got woken up -- clean up either way */
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&wqh, &wait);
+
+	/* did we time out? */
+	if (us->current_urb->status == -EINPROGRESS) {
+		US_DEBUGP("usb_stor_control_msg() timeout\n");
+		usb_unlink_urb(us->current_urb);
+		status = -ETIMEDOUT;
+	} else
+		status = us->current_urb->status;
+
+	/* return the actual length of the data transferred if no error*/
+	if (status >= 0)
+		status = us->current_urb->actual_length;
+
+	/* release the lock and return status */
+	up(&(us->current_urb_sem));
+	kfree(dr);
+  	return status;
+}
+
+/* This is our function to emulate usb_bulk_msg() but give us enough
+ * access to make aborts/resets work
+ */
+int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe,
+		      unsigned int len, unsigned int *act_len)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	DECLARE_WAIT_QUEUE_HEAD(wqh);
+	api_wrapper_data awd;
+	int status;
+
+	/* set up data structures for the wakeup system */
+	awd.wakeup = &wqh;
+	awd.handler = 0;
+	init_waitqueue_head(&wqh); 	
+	add_wait_queue(&wqh, &wait);
+
+	/* lock the URB */
+	down(&(us->current_urb_sem));
+
+	/* fill the URB */
+	FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len,
+		      usb_stor_blocking_completion, &awd);
+
+	/* submit the URB */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	status = usb_submit_urb(us->current_urb);
+	if (status) {
+		/* something went wrong */
+		up(&(us->current_urb_sem));
+		remove_wait_queue(&wqh, &wait);
+		return status;
+	}
+
+	/* wait for the completion of the URB */
+	up(&(us->current_urb_sem));
+	if (us->current_urb->status == -EINPROGRESS)
+		schedule_timeout(10*HZ);
+	down(&(us->current_urb_sem));
+
+	/* we either timed out or got woken up -- clean up either way */
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&wqh, &wait);
+
+	/* did we time out? */
+	if (us->current_urb->status == -EINPROGRESS) {
+		US_DEBUGP("usb_stor_bulk_msg() timeout\n");
+		usb_unlink_urb(us->current_urb);
+		status = -ETIMEDOUT;
+	} else
+		status = us->current_urb->status;
+
+	/* return the actual length of the data transferred */
+	*act_len = us->current_urb->actual_length;
+
+	/* release the lock and return status */
+	up(&(us->current_urb_sem));
+	return status;
+}
+
 /*
  * Transfer one SCSI scatter-gather buffer via bulk transfer
  *
@@ -158,26 +318,33 @@
  * timeout limit.  Thus we don't have to worry about it for individual
  * packets.
  */
-static int us_transfer_partial(struct us_data *us, int pipe, 
-			       char *buf, int length)
+static int us_transfer_partial(struct us_data *us, char *buf, int length)
 {
 	int result;
 	int partial;
+	int pipe;
+
+	/* calculate the appropriate pipe information */
+	if (US_DIRECTION(us->srb->cmnd[0]))
+		pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
+	else
+		pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
 
 	/* transfer the data */
-	US_DEBUGP("Bulk xfer 0x%x(%d)\n", (unsigned int)buf, length);
-	result = usb_bulk_msg(us->pusb_dev, pipe, buf, length, &partial, 5*HZ);
-	US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
+	US_DEBUGP("us_transfer_partial(): xfer %d bytes\n", length);
+	result = usb_stor_bulk_msg(us, buf, pipe, length, &partial);
+	US_DEBUGP("usb_stor_bulk_msg() returned %d xferred %d/%d\n",
 		  result, partial, length);
-	
+
 	/* if we stall, we need to clear it before we go on */
 	if (result == -EPIPE) {
 		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
 		usb_clear_halt(us->pusb_dev, pipe);
 	}
-
+	
 	/* did we send all the data? */
 	if (partial == length) {
+		US_DEBUGP("us_transfer_partial(): transfer complete\n");
 		return US_BULK_TRANSFER_GOOD;
 	}
 
@@ -186,7 +353,17 @@
 		/* NAK - that means we've retried a few times allready */
 		if (result == -ETIMEDOUT) {
 			US_DEBUGP("us_transfer_partial(): device NAKed\n");
+			return US_BULK_TRANSFER_FAILED;
 		}
+
+		/* -ENOENT -- we canceled this transfer */
+		if (result == -ENOENT) {
+			US_DEBUGP("us_transfer_partial(): transfer aborted\n");
+			return US_BULK_TRANSFER_ABORTED;
+		}
+
+		/* the catch-all case */
+		US_DEBUGP("us_transfer_partial(): unknown error\n");
 		return US_BULK_TRANSFER_FAILED;
 	}
 
@@ -203,21 +380,12 @@
  * function simply determines if we're going to use scatter-gather or not,
  * and acts appropriately.  For now, it also re-interprets the error codes.
  */
-static void us_transfer(Scsi_Cmnd *srb, int dir_in)
+static void us_transfer(Scsi_Cmnd *srb, struct us_data* us, int dir_in)
 {
-	struct us_data *us;
 	int i;
 	int result = -1;
-	unsigned int pipe;
 	struct scatterlist *sg;
 
-	/* calculate the appropriate pipe information */
-	us = (struct us_data*) srb->host_scribble;
-	if (dir_in)
-		pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
-	else
-		pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
-
 	/* are we scatter-gathering? */
 	if (srb->use_sg) {
 
@@ -226,7 +394,7 @@
 		 */
 		sg = (struct scatterlist *) srb->request_buffer;
 		for (i = 0; i < srb->use_sg; i++) {
-			result = us_transfer_partial(us, pipe, sg[i].address, 
+			result = us_transfer_partial(us, sg[i].address, 
 						     sg[i].length);
 			if (result)
 				break;
@@ -234,7 +402,7 @@
 	}
 	else
 		/* no scatter-gather, just make the request */
-		result = us_transfer_partial(us, pipe, srb->request_buffer, 
+		result = us_transfer_partial(us, srb->request_buffer, 
 					     srb->request_bufflen);
 
 	/* return the result in the data structure itself */
@@ -433,6 +601,8 @@
 /*
  * Control/Bulk/Interrupt transport
  */
+
+/* The interrupt handler for CBI devices */
 static void CBI_irq(struct urb *urb)
 {
 	struct us_data *us = (struct us_data *)urb->context;
@@ -465,13 +635,13 @@
 
 	/* COMMAND STAGE */
 	/* let's send the command via the control pipe */
-	result = usb_control_msg(us->pusb_dev, 
-				 usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, 
-				 USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 
-				 us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
+	result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0),
+				      US_CBI_ADSC, 
+				      USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 
+				      us->ifnum, srb->cmnd, srb->cmd_len);
 
 	/* check the return code for the command */
-	US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
+	US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result);
 	if (result < 0) {
 		/* STALL must be cleared when they are detected */
 		if (result == -EPIPE) {
@@ -493,7 +663,7 @@
 	/* DATA STAGE */
 	/* transfer the data payload for this command, if one exists*/
 	if (us_transfer_length(srb, us)) {
-		us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+		us_transfer(srb, us, US_DIRECTION(srb->cmnd[0]));
 		US_DEBUGP("CBI data stage result is 0x%x\n", srb->result);
 	}
 
@@ -561,15 +731,14 @@
 
 	/* COMMAND STAGE */
 	/* let's send the command via the control pipe */
-	result = usb_control_msg(us->pusb_dev, 
-				 usb_sndctrlpipe(us->pusb_dev,0), US_CBI_ADSC, 
-				 USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 
-				 us->ifnum, srb->cmnd, srb->cmd_len, HZ*5);
+	result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0),
+				      US_CBI_ADSC, 
+				      USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, 
+				      us->ifnum, srb->cmnd, srb->cmd_len);
 
 	/* check the return code for the command */
+	US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result);
 	if (result < 0) {
-		US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
-
 		/* a stall is a fatal condition from the device */
 		if (result == -EPIPE) {
 			US_DEBUGP("-- Stall on control pipe. Clearing\n");
@@ -587,7 +756,7 @@
 	/* DATA STAGE */
 	/* transfer the data payload for this command, if one exists*/
 	if (us_transfer_length(srb, us)) {
-		us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+		us_transfer(srb, us, US_DIRECTION(srb->cmnd[0]));
 		US_DEBUGP("CB data stage result is 0x%x\n", srb->result);
 	}
 	
@@ -602,6 +771,39 @@
 /*
  * Bulk only transport
  */
+
+/* Determine what the maximum LUN supported is */
+static int Bulk_max_lun(struct us_data *us)
+{
+	unsigned char data;
+	int result;
+	int pipe;
+
+	/* issue the command */
+	pipe = usb_rcvctrlpipe(us->pusb_dev, 0);
+	result = usb_control_msg(us->pusb_dev, pipe,
+				 US_BULK_GET_MAX_LUN, 
+				 USB_DIR_IN | USB_TYPE_CLASS | 
+				 USB_RECIP_INTERFACE,
+				 0, us->ifnum, &data, sizeof(data), HZ);
+
+	US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", 
+		  result, data);
+
+	/* if we have a successful request, return the result */
+	if (!result)
+		return data;
+
+	/* if we get a STALL, clear the stall */
+	if (result == -EPIPE) {
+		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
+		usb_clear_halt(us->pusb_dev, pipe);
+	}
+
+	/* return the default -- no LUNs */
+	return 0;
+}
+
 static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
 	struct bulk_cb_wrap bcb;
@@ -629,8 +831,8 @@
 	US_DEBUGP("Bulk command S 0x%x T 0x%x LUN %d L %d F %d CL %d\n",
 		  le32_to_cpu(bcb.Signature), bcb.Tag, bcb.Lun, 
 		  bcb.DataTransferLength, bcb.Flags, bcb.Length);
-	result = usb_bulk_msg(us->pusb_dev, pipe, &bcb,
-			      US_BULK_CB_WRAP_LEN, &partial, HZ*5);
+	result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN, 
+				   &partial);
 	US_DEBUGP("Bulk command transfer result=%d\n", result);
 	
 	/* if we stall, we need to clear it before we go on */
@@ -643,7 +845,7 @@
 	if (result == 0) {
 		/* send/receive data payload, if there is any */
 		if (bcb.DataTransferLength) {
-			us_transfer(srb, bcb.Flags);
+			us_transfer(srb, us, bcb.Flags);
 			US_DEBUGP("Bulk data transfer result 0x%x\n", 
 				  srb->result);
 		}
@@ -658,8 +860,8 @@
 	
 	/* get CSW for device status */
 	US_DEBUGP("Attempting to get CSW...\n");
-	result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
-			      US_BULK_CS_WRAP_LEN, &partial, HZ*2);
+	result = usb_stor_bulk_msg(us, &bcs, pipe, US_BULK_CS_WRAP_LEN, 
+				   &partial);
 	
 	/* did the attempt to read the CSW fail? */
 	if (result == -EPIPE) {
@@ -668,8 +870,8 @@
 	       
 		/* get the status again */
 		US_DEBUGP("Attempting to get CSW (2nd try)...\n");
-		result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
-				      US_BULK_CS_WRAP_LEN, &partial, HZ*2);
+		result = usb_stor_bulk_msg(us, &bcs, pipe,
+					   US_BULK_CS_WRAP_LEN, &partial);
 		
 		/* if it fails again, we need a reset and return an error*/
 		if (result == -EPIPE) {
@@ -1025,9 +1227,9 @@
 
 	result = usb_control_msg(us->pusb_dev, 
 				 usb_sndctrlpipe(us->pusb_dev,0), 
-				 US_BULK_RESET, 
+				 US_BULK_RESET_REQUEST, 
 				 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-				 US_BULK_RESET_HARD, us->ifnum, NULL, 0, HZ*5);
+				 0, us->ifnum, NULL, 0, HZ*5);
 
 	if (result < 0)
 		US_DEBUGP("Bulk hard reset failed %d\n", result);
@@ -1049,7 +1251,7 @@
 
 static const char* us_info(struct Scsi_Host *host)
 {
-	return "SCSI emulation for USB Mass Storage devices\n";
+	return "SCSI emulation for USB Mass Storage devices";
 }
 
 /* detect a virtual adapter (always works) */
@@ -1109,6 +1311,7 @@
 	
 	/* free the data structure we were using */
 	US_DEBUGP("-- freeing private host data structure\n");
+	kfree(us->current_urb);
 	kfree(us);
 	(struct us_data*)psh->hostdata[0] = NULL;
 
@@ -1289,22 +1492,11 @@
 };
 
 static unsigned char sense_notready[] = {
-	0x70,			    /* current error */
-	0x00,
-	0x02,			    /* not ready */
-	0x00,
-	0x00,
-	0x0a,			    /* additional length */
-	0x00,
-	0x00,
-	0x00,
-	0x00,
-	0x04,			    /* not ready */
-	0x03,			    /* manual intervention */
-	0x00,
-	0x00,
-	0x00,
-	0x00
+	[0]	= 0x70,			    /* current error */
+	[2]	= 0x02,			    /* not ready */
+	[5]	= 0x0a,			    /* additional length */
+	[10]	= 0x04,			    /* not ready */
+	[11]	= 0x03			    /* manual intervention */
 };
 
 static int usb_stor_control_thread(void * __us)
@@ -1429,25 +1621,31 @@
 	return 0;
 }	
 
+/* This is the list of devices we recognize, along with their flag data */
 static struct us_unusual_dev us_unusual_dev_list[] = {
+	{ 0x03f0, 0x0107, 0x0200,
+	  "HP USB CD-Writer Plus", US_SC_8070, US_PR_CB, 0}, 
+	{ 0x04e6, 0x0001, 0x0200,
+	  "Matshita LS-120", US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN},
+	{ 0x04e6, 0x0002, 0x0100,
+	  "Shuttle eUSCSI Bridge", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, 
+	{ 0x04e6, 0x0006, 0x0100,
+	  "Shuttle eUSB MMC Adapter", US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, 
 	{ 0x057b, 0x0000, 0x0114,
-	  "Y-E Data Flashbuster-U", US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN },
+	  "Y-E Data Flashbuster-U", US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN},
 	{ 0x059b, 0x0030, 0x0100,
-	  "Iomega Zip 250", US_SC_SCSI, US_PR_BULK, US_FL_SINGLE_LUN },
+	  "Iomega Zip 250", US_SC_SCSI, US_PR_BULK, US_FL_SINGLE_LUN},
+	{ 0x0693, 0x0002, 0x0100,
+	  "Hagiwara FlashGate SmartMedia", US_SC_SCSI, US_PR_BULK,
+	  US_FL_ALT_LENGTH},
 	{ 0x0781, 0x0001, 0x0200,
-	  "Sandisk ImageMate (w/eject button)", US_SC_SCSI, US_PR_CB, 
-	  US_FL_SINGLE_LUN | US_FL_START_STOP },
+	  "Sandisk ImageMate (SDDR-01)", US_SC_SCSI, US_PR_CB, 
+	  US_FL_SINGLE_LUN | US_FL_START_STOP},
 	{ 0x0781, 0x0002, 0x0009,
-	  "** SECRET DEVICE **", US_SC_SCSI, US_PR_BULK, 
-	  US_FL_SINGLE_LUN | US_FL_IGNORE_SER },
+	  "Sandisk Imagemate (SDDR-31)", US_SC_SCSI, US_PR_BULK, 
+	  US_FL_SINGLE_LUN | US_FL_IGNORE_SER},
 	{ 0x07af, 0x0005, 0x0100,
 	  "Microtech USB-SCSI-HD50", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, 
-	{ 0x04e6, 0x0002, 0x0100,
-	  "Shuttle eUSCSI Bridge", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, 
-	{ 0x04e6, 0x0006, 0x0100,
-	  "Shuttle eUSB MMC Adapter", US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, 
-	{ 0x03f0, 0x0107, 0x0200,
-	  "HP USB CD-Writer Plus", US_SC_8070, US_PR_CB, 0}, 
 	{ 0x0000, 0x0000, 0x0,
 	  "", 0, 0, 0}
 };
@@ -1713,12 +1911,20 @@
 		}
 		memset(ss, 0, sizeof(struct us_data));
 
+		/* allocate the URB we're going to use */
+		ss->current_urb = usb_alloc_urb(0);
+		if (!ss->current_urb) {
+			kfree(ss);
+			return NULL;
+		}
+
 		/* Initialize the mutexes only when the struct is new */
 		init_MUTEX_LOCKED(&(ss->sleeper));
 		init_MUTEX_LOCKED(&(ss->notify));
 		init_MUTEX_LOCKED(&(ss->ip_waitq));
 		init_MUTEX(&(ss->queue_exclusion));
 		init_MUTEX(&(ss->irq_urb_sem));
+		init_MUTEX(&(ss->current_urb_sem));
 		init_MUTEX(&(ss->dev_semaphore));
 
 		/* copy over the subclass and protocol data */
@@ -1774,11 +1980,14 @@
 			ss->transport_name = "Bulk";
 			ss->transport = Bulk_transport;
 			ss->transport_reset = Bulk_reset;
+			/* FIXME: for testing purposes only */
+			Bulk_max_lun(ss);
 			break;
 			
 		default:
 			ss->transport_name = "Unknown";
 			up(&us_list_semaphore);
+			kfree(ss->current_urb);
 			kfree(ss);
 			return NULL;
 			break;
@@ -1802,6 +2011,7 @@
 			US_DEBUGP("contact mdharm-usb@one-eyed-alien.net\n");
 			US_DEBUGP("if you see this message.\n");
 			up(&us_list_semaphore);
+			kfree(ss->current_urb);
 			kfree(ss);
 			return NULL;
 			break;
@@ -1824,6 +2034,7 @@
 		default:
 			ss->protocol_name = "Unknown";
 			up(&us_list_semaphore);
+			kfree(ss->current_urb);
 			kfree(ss);
 			return NULL;
 			break;
@@ -1859,6 +2070,7 @@
 		if (ss->pid < 0) {
 			printk(KERN_WARNING USB_STORAGE 
 			       "Unable to start control thread\n");
+			kfree(ss->current_urb);
 			kfree(ss);
 			return NULL;
 		}

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)