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

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

diff -u --recursive --new-file v2.3.51/linux/drivers/usb/usb-storage.c linux/drivers/usb/usb-storage.c
@@ -6,9 +6,9 @@
  * Further reference:
  *	This driver is based on the 'USB Mass Storage Class' document. This
  *	describes in detail the protocol used to communicate with such
- *      devices.  Clearly, the designers had SCSI commands in mind when they
- *      created this document.  The commands are all similar to commands
- *      in the SCSI-II specification.
+ *      devices.  Clearly, the designers had SCSI and ATAPI commands in mind 
+ *      when they created this document.  The commands are all very similar 
+ *      to commands in the SCSI-II and ATAPI specifications.
  *
  *	It is important to note that in a number of cases this class exhibits
  *	class-specific exemptions from the USB specification. Notably the
@@ -65,8 +65,6 @@
 
 static int my_host_number;
 
-int usb_stor_debug = 1;
-
 struct us_data;
 
 typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*);
@@ -74,7 +72,7 @@
 typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*);
 
 struct us_data {
-	struct us_data	*next;		         /* next device */
+	struct us_data	        *next;	         /* next device */
 	struct usb_device	*pusb_dev;       /* this usb_device */
 	unsigned int		flags;		 /* from filter initially */
 	__u8			ifnum;		 /* interface number */
@@ -93,15 +91,17 @@
 	int			host_number;	 /* to find us */
 	int			host_no;	 /* allocated by scsi */
 	Scsi_Cmnd		*srb;		 /* current srb */
+	Scsi_Cmnd		*queue_srb;	 /* the single queue slot */
 	int			action;		 /* what to do */
-	wait_queue_head_t	waitq;		 /* thread waits */
-	wait_queue_head_t	ip_waitq;	 /* for CBI interrupts */
+	struct semaphore	ip_waitq;	 /* for CBI interrupts */
 	__u16			ip_data;	 /* interrupt data */
 	int			ip_wanted;	 /* needed */
 	int			pid;		 /* control thread */
 	struct semaphore	*notify;	 /* wait for thread to begin */
 	void			*irq_handle;	 /* for USB int requests */
 	unsigned int		irqpipe;	 /* pipe for release_irq */
+	struct semaphore        sleeper;         /* to sleep on */
+	struct semaphore        queue_exclusion; /* to protect data structs */
 };
 
 /*
@@ -129,117 +129,100 @@
  * Data transfer routines
  ***********************************************************************/
 
-/* Transfer one buffer (breaking into packets if necessary)
- * Note that this function is necessary because if the device NAKs, we
- * need to know that information directly
+/* FIXME: the names of these functions are poorly choosen. */
+
+/*
+ * Transfer one SCSI scatter-gather buffer via bulk transfer
+ *
+ * Note that this function is necessary because we want the ability to
+ * use scatter-gather memory.  Good performance is achived by a combination
+ * of scatter-gather and clustering (which makes each chunk bigger).
  *
- * FIXME: is the above true?  Or will the URB status show ETIMEDOUT after
- * retrying several times allready?  Perhaps this is the way we should
- * be going anyway?
+ * Note that the lower layer will always retry when a NAK occurs, up to the
+ * timeout limit.  Thus we don't have to worry about it for individual
+ * packets.
  */
-static int us_one_transfer(struct us_data *us, int pipe, char *buf, int length)
+static int us_bulk_transfer(struct us_data *us, int pipe, 
+			    char *buf, int length)
 {
-	int max_size;
-	int this_xfer;
 	int result;
 	int partial;
-	int maxtry;
-
-	/* determine the maximum packet size for these transfers */
-	max_size = usb_maxpacket(us->pusb_dev, 
-				 pipe, usb_pipeout(pipe)) * 16;
-
-	/* while we have data left to transfer */
-	while (length) {
-
-		/* calculate how long this will be -- maximum or a remainder */
-		this_xfer = length > max_size ? max_size : length;
-		length -= this_xfer;
-
-		/* FIXME: this number is totally outrageous.  We need to pick
-		 * a better (smaller) number).
-		 */
-
-		/* setup the retry counter */
-		maxtry = 100;
-
-		/* set up the transfer loop */
-		do {
-			/* transfer the data */
-			US_DEBUGP("Bulk xfer 0x%x(%d) try #%d\n", 
-				  (unsigned int)buf, this_xfer, 101 - maxtry);
-			result = usb_bulk_msg(us->pusb_dev, pipe, buf,
-					      this_xfer, &partial, HZ*5);
-			US_DEBUGP("bulk_msg returned %d xferred %d/%d\n",
-				  result, partial, this_xfer);
-
-			/* 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);
-			}
-
-			/* update to show what data was transferred */
-			this_xfer -= partial;
-			buf += partial;
 
-			/* NAK - we retry a few times */
-			if (result == -ETIMEDOUT) {
-
-				US_DEBUGP("us_one_transfer: device NAKed\n");
-
-				/* if our try counter reaches 0, bail out */
-				if (!maxtry--)
-					return -ETIMEDOUT;
+	/* 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, HZ*5);
+	US_DEBUGP("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);
+	}
 
-				/* just continue the while loop */
-				continue;
-			}
-      
-			/* other errors (besides NAK) -- we just bail out*/
-			if (result != 0) {
-				US_DEBUGP("us_one_transfer: device returned error %d\n", result);
-				return result;
-			}
+	/* did we send all the data? */
+	if (partial == length) {
+		return US_BULK_TRANSFER_GOOD;
+	}
 
-			/* continue until this transfer is done */
-		} while ( this_xfer );
+	/* uh oh... we have an error code, so something went wrong. */
+	if (result) {
+		/* NAK - that means we've retried a few times allready */
+		if (result == -ETIMEDOUT) {
+			US_DEBUGP("us_bulk_transfer: device NAKed\n");
+		}
+		return US_BULK_TRANSFER_FAILED;
 	}
 
-	/* if we get here, we're done and successful */
-	return 0;
+	/* no error code, so we must have transferred some data, 
+	 * just not all of it */
+	return US_BULK_TRANSFER_SHORT;
 }
 
-static unsigned int us_transfer_length(Scsi_Cmnd *srb);
-
-/* transfer one SCSI command, using scatter-gather if requested */
-/* FIXME: what do the return codes here mean? */
-static int us_transfer(Scsi_Cmnd *srb, int dir_in)
+/*
+ * Transfer an entire SCSI command's worth of data payload over the bulk
+ * pipe.
+ *
+ * Note that this uses us_bulk_transfer to achive it's goals -- this
+ * 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)
 {
-	struct us_data *us = (struct us_data *)srb->host_scribble;
+	struct us_data *us;
 	int i;
 	int result = -1;
-	unsigned int pipe = dir_in ? usb_rcvbulkpipe(us->pusb_dev, us->ep_in) :
-		usb_sndbulkpipe(us->pusb_dev, us->ep_out);
+	unsigned int pipe;
+	struct scatterlist *sg;
 
-	/* FIXME: stop transferring data at us_transfer_length(), not 
-	 * bufflen */
+	/* 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) {
-		struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
 
+		/* loop over all the scatter gather structures and 
+		 * make the appropriate requests for each, until done
+		 */
+		sg = (struct scatterlist *) srb->request_buffer;
 		for (i = 0; i < srb->use_sg; i++) {
-			result = us_one_transfer(us, pipe, sg[i].address, sg[i].length);
+			result = us_bulk_transfer(us, pipe, sg[i].address, 
+						  sg[i].length);
 			if (result)
 				break;
 		}
 	}
 	else
-		result = us_one_transfer(us, pipe, srb->request_buffer, 
-					 us_transfer_length(srb));
+		/* no scatter-gather, just make the request */
+		result = us_bulk_transfer(us, pipe, srb->request_buffer, 
+					  srb->request_bufflen);
 
-	if (result < 0)
-		US_DEBUGP("us_transfer returning error %d\n", result);
-	return result;
+	/* return the result in the data structure itself */
+	srb->result = result;
 }
 
 /* calculate the length of the data transfer (not the command) for any
@@ -265,6 +248,9 @@
 	case MODE_SENSE:
 		return srb->cmnd[4];
 
+	case READ_CAPACITY:
+		return 8;
+
 	case LOG_SENSE:
 	case MODE_SENSE_10:
 		return (srb->cmnd[7] << 8) + srb->cmnd[8];
@@ -274,8 +260,9 @@
 	}
 
 	if (srb->use_sg) {
-		struct scatterlist *sg = (struct scatterlist *) srb->request_buffer;
+		struct scatterlist *sg;
 
+		sg = (struct scatterlist *) srb->request_buffer;
 		for (i = 0; i < srb->use_sg; i++) {
 			total += sg[i].length;
 		}
@@ -289,12 +276,148 @@
  * Protocol routines
  ***********************************************************************/
 
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us);
-static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us);
+static void ATAPI_command(Scsi_Cmnd *srb, struct us_data *us)
+{
+	int old_cmnd = 0;
+	int result;
+  
+	/* Fix some commands -- this is a form of mode translation
+	 * ATAPI devices only accept 12 byte long commands 
+	 *
+	 * NOTE: This only works because a Scsi_Cmnd struct field contains
+	 * a unsigned char cmnd[12], so we know we have storage available
+	 */
+
+	/* set command length to 12 bytes */
+	srb->cmd_len = 12;
+
+	/* determine the correct (or minimum) data length for these commands */
+	switch (us->srb->cmnd[0]) {
+
+		/* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */
+	case MODE_SENSE:
+	case MODE_SELECT:
+		/* save the command so we can tell what it was */
+		old_cmnd = srb->cmnd[0];
+
+		srb->cmnd[11] = 0;
+		srb->cmnd[10] = 0;
+		srb->cmnd[9] = 0;
+		srb->cmnd[8] = srb->cmnd[4];
+		srb->cmnd[7] = 0;
+		srb->cmnd[6] = 0;
+		srb->cmnd[5] = 0;
+		srb->cmnd[4] = 0;
+		srb->cmnd[3] = 0;
+		srb->cmnd[2] = srb->cmnd[2];
+		srb->cmnd[1] = srb->cmnd[1];
+		srb->cmnd[0] = srb->cmnd[0] | 0x40;
+		break;
+
+		/* change READ_6/WRITE_6 to READ_10/WRITE_10, which 
+		 * are ATAPI commands */
+	case WRITE_6:
+	case READ_6:
+		srb->cmnd[11] = 0;
+		srb->cmnd[10] = 0;
+		srb->cmnd[9] = 0;
+		srb->cmnd[8] = srb->cmnd[4];
+		srb->cmnd[7] = 0;
+		srb->cmnd[6] = 0;
+		srb->cmnd[5] = srb->cmnd[3];
+		srb->cmnd[4] = srb->cmnd[2];
+		srb->cmnd[3] = srb->cmnd[1] & 0x1F;
+		srb->cmnd[2] = 0;
+		srb->cmnd[1] = srb->cmnd[1] & 0xE0;
+		srb->cmnd[0] = srb->cmnd[0] | 0x20;
+		break;
+	} /* end switch on cmnd[0] */
+  
+	/* send the command to the transport layer */
+	result = us->transport(srb, us);
+
+	/* If we got a short transfer, but it was for a command that
+	 * can have short transfers, we're actually okay
+	 */
+	if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+	    ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+	     (us->srb->cmnd[0] == INQUIRY) ||
+	     (us->srb->cmnd[0] == MODE_SENSE) ||
+	     (us->srb->cmnd[0] == LOG_SENSE) ||
+	     (us->srb->cmnd[0] == MODE_SENSE_10))) {
+		us->srb->result = DID_OK;
+	}
+
+	/*
+	 * If we have an error, we're going to do a 
+	 * REQUEST_SENSE automatically
+	 */
+	if (result != USB_STOR_TRANSPORT_GOOD) {
+		int temp_result;
+		void* old_request_buffer;
+		int old_sg;
+
+		US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
+
+		us->srb->cmnd[0] = REQUEST_SENSE;
+		us->srb->cmnd[1] = 0;
+		us->srb->cmnd[2] = 0;
+		us->srb->cmnd[3] = 0;
+		us->srb->cmnd[4] = 18;
+		us->srb->cmnd[5] = 0;
+    
+		/* set the buffer length for transfer */
+		old_request_buffer = us->srb->request_buffer;
+	        old_sg = us->srb->use_sg;
+		us->srb->request_bufflen = 18;
+		us->srb->request_buffer = us->srb->sense_buffer;
+
+		/* FIXME: what if this command fails? */
+		temp_result = us->transport(us->srb, us);
+		US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
+		US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
+			  us->srb->sense_buffer[2] & 0xf,
+			  us->srb->sense_buffer[12], 
+			  us->srb->sense_buffer[13]);
+
+		/* set the result so the higher layers expect this data */
+		us->srb->result = CHECK_CONDITION;
+
+		/* we're done here */
+		us->srb->request_buffer = old_request_buffer;
+		us->srb->use_sg = old_sg;
+		return;
+	}
+  
+	/* Fix the MODE_SENSE data if we translated the command
+	 */
+	if (old_cmnd == MODE_SENSE) {
+		unsigned char *dta = (unsigned char *)us->srb->request_buffer;
+
+		/* FIXME: we need to compress the entire data structure here
+		 */
+		dta[0] = dta[1];	/* data len */
+		dta[1] = dta[2];	/* med type */
+		dta[2] = dta[3];	/* dev-spec prm */
+		dta[3] = dta[7];	/* block desc len */
+		printk (KERN_DEBUG USB_STORAGE
+			"new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n",
+			dta[0], dta[1], dta[2], dta[3]);
+	}
+
+	/* Fix-up the return data from an INQUIRY command to show 
+	 * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
+	 */
+	if (us->srb->cmnd[0] == INQUIRY) {
+		((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
+	}
+}
+
 
 static void ufi_command(Scsi_Cmnd *srb, struct us_data *us)
 {
 	int old_cmnd = 0;
+	int result;
   
 	/* fix some commands -- this is a form of mode translation
 	 * UFI devices only accept 12 byte long commands 
@@ -372,23 +495,31 @@
 	} /* end switch on cmnd[0] */
   
 	/* send the command to the transport layer */
-	us->srb->result = us->transport(srb, us);
+	result = us->transport(srb, us);
 
-	/* if we have an error, we're going to do a 
-	 * REQUEST_SENSE automatically */
+	/* If we got a short transfer, but it was for a command that
+	 * can have short transfers, we're actually okay
+	 */
+	if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+	    ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+	     (us->srb->cmnd[0] == INQUIRY) ||
+	     (us->srb->cmnd[0] == MODE_SENSE) ||
+	     (us->srb->cmnd[0] == LOG_SENSE) ||
+	     (us->srb->cmnd[0] == MODE_SENSE_10))) {
+		us->srb->result = DID_OK;
+	}
 
-	/* FIXME: we should only do this for device 
-	 * errors, not system errors */
-	if (us->srb->result) {
+	/*
+	 * If we have an error, we're going to do a 
+	 * REQUEST_SENSE automatically
+	 */
+	if (result != USB_STOR_TRANSPORT_GOOD) {
 		int temp_result;
-		int count;
 		void* old_request_buffer;
+		int old_sg;
 
 		US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
 
-		/* set the result so the higher layers expect this data */
-		us->srb->result = CHECK_CONDITION;
-
 		us->srb->cmnd[0] = REQUEST_SENSE;
 		us->srb->cmnd[1] = 0;
 		us->srb->cmnd[2] = 0;
@@ -398,49 +529,34 @@
     
 		/* set the buffer length for transfer */
 		old_request_buffer = us->srb->request_buffer;
+	        old_sg = us->srb->use_sg;
 		us->srb->request_bufflen = 18;
-		us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+		us->srb->request_buffer = us->srb->sense_buffer;
 
 		/* FIXME: what if this command fails? */
 		temp_result = us->transport(us->srb, us);
 		US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
-		/* copy the data from the request buffer to the sense buffer */
-		for(count = 0; count < 18; count++)
-			us->srb->sense_buffer[count] = 
-				((unsigned char *)(us->srb->request_buffer))[count];
-
 		US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
 			  us->srb->sense_buffer[2] & 0xf,
-			  us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+			  us->srb->sense_buffer[12], 
+			  us->srb->sense_buffer[13]);
+
+		/* set the result so the higher layers expect this data */
+		us->srb->result = CHECK_CONDITION;
 
 		/* we're done here */
-		kfree(us->srb->request_buffer);
 		us->srb->request_buffer = old_request_buffer;
+		us->srb->use_sg = old_sg;
 		return;
 	}
   
-	/* FIXME: if we need to send more data, or recieve data, we should
-	 * do it here.  Then, we can do status handling here also.
-	 *
-	 * This includes MODE_SENSE from above
+	/* Fix the MODE_SENSE data here if we had to translate the command
 	 */
 	if (old_cmnd == MODE_SENSE) {
 		unsigned char *dta = (unsigned char *)us->srb->request_buffer;
 
-		/* calculate the new length */
-		int length = (dta[0] << 8) + dta[1] + 2;
-
-		/* copy the available data length into the structure */
-		us->srb->cmnd[7] = length >> 8;
-		us->srb->cmnd[8] = length & 0xFF;
-
-		/* send the command to the transport layer */
-		us->srb->result = us->transport(srb, us);
-
-		/* FIXME: this assumes that the 2nd attempt is always
-		 * successful convert MODE_SENSE_10 return data format 
-		 * to MODE_SENSE_6 format */
+		/* FIXME: we need to compress the entire data structure here
+		 */
 		dta[0] = dta[1];	/* data len */
 		dta[1] = dta[2];	/* med type */
 		dta[2] = dta[3];	/* dev-spec prm */
@@ -450,126 +566,18 @@
 			dta[0], dta[1], dta[2], dta[3]);
 	}
 
-	/* FIXME: if this was a TEST_UNIT_READY, and we get a NOT READY/
-	 * LOGICAL DRIVE NOT READY then we do a START_STOP, and retry 
+	/* Fix-up the return data from an INQUIRY command to show 
+	 * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us
 	 */
-
-	/* FIXME: here is where we need to fix-up the return data from 
-	 * an INQUIRY command to show ANSI SCSI rev 2
-	 */
-
-	/* FIXME: The rest of this is bogus.  usb_control_msg() will only
-	 * return an error if we've really honked things up.  If it just
-	 * needs a START_STOP, then we'll get some data back via 
-	 * REQUEST_SENSE --  either way, this belongs at a higher level
-	 */
-
-#if 0
-	/* For UFI, if this is the first time we've sent this TEST_UNIT_READY 
-	 * command, we can try again
-	 */
-	if (!done_start && (us->subclass == US_SC_UFI)
-	    && (cmd[0] == TEST_UNIT_READY) && (result < 0)) {
-    
-		/* as per spec try a start command, wait and retry */
-		wait_ms(100);
-    
-		done_start++;
-		memset(cmd, 0, sizeof(cmd));
-		cmd[0] = START_STOP;
-		cmd[4] = 1;		/* start */
-    
-		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,
-					 cmd, 12, HZ*5);
-		US_DEBUGP("Next usb_control_msg returns %d\n", result);
-    
-				/* allow another retry */
-		retry++;
-		continue;
+	if (us->srb->cmnd[0] == INQUIRY) {
+		((unsigned char *)us->srb->request_buffer)[2] |= 0x2;
 	}
-#endif
 }
 
 static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us)
 {
-	unsigned int savelen = us->srb->request_bufflen;
-	unsigned int saveallocation = 0;
-
-#if 0
-	/* force attention on first command */
-	if (!us->attention_done) {
-		if (us->srb->cmnd[0] == REQUEST_SENSE) {
-			US_DEBUGP("forcing unit attention\n");
-			us->attention_done = 1;
-
-			if (us->srb->result == USB_STOR_TRANSPORT_GOOD) {
-				unsigned char *p = (unsigned char *)us->srb->request_buffer;
-	
-				if ((p[2] & 0x0f) != UNIT_ATTENTION) {
-					p[2] = UNIT_ATTENTION;
-					p[12] = 0x29;	/* power on, reset or bus-reset */
-					p[13] = 0;
-				} /* if ((p[2] & 0x0f) != UNIT_ATTENTION) */
-			} /* if (us->srb->result == USB_STORE_TRANSPORT_GOOD) */
-		}
-	} /* if (!us->attention_done) */
-#endif
+	unsigned int result = 0;
 
-	/* If the command has a variable-length payload, then we do them
-	 * in two steps -- first we do the minimum, then we recalculate
-	 * then length, and re-issue the command 
-	 *
-	 * we use savelen to remember how much buffer we really have
-	 * we use savealloction to remember how much was really requested
-	 */
-
-	/* FIXME: remove savelen based on mods to us_transfer_length() */
-	switch (us->srb->cmnd[0]) {
-	case REQUEST_SENSE:
-		if (us->srb->request_bufflen > 18)
-			us->srb->request_bufflen = 18;
-		else
-			break;
-		saveallocation = us->srb->cmnd[4];
-		us->srb->cmnd[4] = 18;
-		break;
-    
-	case INQUIRY:
-		if (us->srb->request_bufflen > 36)
-			us->srb->request_bufflen = 36;
-		else
-			break;
-		saveallocation = us->srb->cmnd[4];
-		us->srb->cmnd[4] = 36;
-		break;
-    
-	case MODE_SENSE:
-		if (us->srb->request_bufflen > 4)
-			us->srb->request_bufflen = 4;
-		else
-			break;
-		saveallocation = us->srb->cmnd[4];
-		us->srb->cmnd[4] = 4;
-		break;
-    
-	case LOG_SENSE:
-	case MODE_SENSE_10:
-		if (us->srb->request_bufflen > 8)
-			us->srb->request_bufflen = 8;
-		else
-			break;
-		saveallocation = (us->srb->cmnd[7] << 8) | us->srb->cmnd[8];
-		us->srb->cmnd[7] = 0;
-		us->srb->cmnd[8] = 8;
-		break;
-    
-	default:
-		break;
-	} /* end switch on cmnd[0] */
-  
 	/* This code supports devices which do not support {READ|WRITE}_6
 	 * Apparently, neither Windows or MacOS will use these commands,
 	 * so some devices do not support them
@@ -631,25 +639,33 @@
 			US_DEBUGP("Changing WRITE_6 to WRITE_10\n");
 			US_DEBUG(us_show_command(us->srb));
 		}
-	} /* end if (us->flags & US_FL_MODE_XLATE) */
+	} /* if (us->flags & US_FL_MODE_XLATE) */
   
 	/* send the command to the transport layer */
-	us->srb->result = us->transport(us->srb, us);
+	result = us->transport(us->srb, us);
+
+	/* If we got a short transfer, but it was for a command that
+	 * can have short transfers, we're actually okay
+	 */
+	if ((us->srb->result == US_BULK_TRANSFER_SHORT) &&
+	    ((us->srb->cmnd[0] == REQUEST_SENSE) ||
+	     (us->srb->cmnd[0] == INQUIRY) ||
+	     (us->srb->cmnd[0] == MODE_SENSE) ||
+	     (us->srb->cmnd[0] == LOG_SENSE) ||
+	     (us->srb->cmnd[0] == MODE_SENSE_10))) {
+		us->srb->result = DID_OK;
+	}
 
 	/* if we have an error, we're going to do a REQUEST_SENSE 
 	 * automatically */
-	/* FIXME: we should only do this for device errors, not 
-	 * system errors */
-	if (us->srb->result) {
+	if (result != USB_STOR_TRANSPORT_GOOD) {
 		int temp_result;
-		int count;
+		int old_sg;
 		void* old_request_buffer;
 
 		US_DEBUGP("Command FAILED: Issuing auto-REQUEST_SENSE\n");
 
-		/* set the result so the higher layers expect this data */
-		us->srb->result = CHECK_CONDITION;
-
+		/* set up the REQUEST_SENSE command and parameters */
 		us->srb->cmnd[0] = REQUEST_SENSE;
 		us->srb->cmnd[1] = 0;
 		us->srb->cmnd[2] = 0;
@@ -659,115 +675,32 @@
     
 		/* set the buffer length for transfer */
 		old_request_buffer = us->srb->request_buffer;
+	        old_sg = us->srb->use_sg;
 		us->srb->request_bufflen = 18;
-		us->srb->request_buffer = kmalloc(18, GFP_KERNEL);
+		us->srb->request_buffer = us->srb->sense_buffer;
 
 		/* FIXME: what if this command fails? */
 		temp_result = us->transport(us->srb, us);
 		US_DEBUGP("-- Result from auto-sense is %d\n", temp_result);
-
-		/* copy the data from the request buffer to the sense buffer */
-		for(count = 0; count < 18; count++)
-			us->srb->sense_buffer[count] = 
-				((unsigned char *)(us->srb->request_buffer))[count];
-
 		US_DEBUGP("-- sense key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n",
 			  us->srb->sense_buffer[2] & 0xf,
-			  us->srb->sense_buffer[12], us->srb->sense_buffer[13]);
+			  us->srb->sense_buffer[12], 
+			  us->srb->sense_buffer[13]);
+
+		/* set the result so the higher layers expect this data */
+		us->srb->result = CHECK_CONDITION;
 
 		/* we're done here */
-		kfree(us->srb->request_buffer);
+		us->srb->use_sg = old_sg;
 		us->srb->request_buffer = old_request_buffer;
 		return;
 	}
 
-	if (savelen != us->srb->request_bufflen) {
-		unsigned char *p = (unsigned char *)us->srb->request_buffer;
-		unsigned int length = 0;
-    
-		/* set correct length and retry */
-		switch (us->srb->cmnd[0]) {
-
-			/* FIXME: we should try to get all the sense data */
-		case REQUEST_SENSE:
-			/* simply return 18 bytes */
-			p[7] = 10;
-			length = us->srb->request_bufflen;
-			break;
-      
-		case INQUIRY:
-			length = p[4] + 5 > savelen ? savelen : p[4] + 5;
-			us->srb->cmnd[4] = length;
-			break;
-      
-		case MODE_SENSE:
-			US_DEBUGP("MODE_SENSE Mode data length is %d\n", p[0]);
-			length = p[0] + 1 > savelen ? savelen : p[0] + 1;
-			us->srb->cmnd[4] = length;
-			break;
-      
-		case LOG_SENSE:
-			length = ((p[2] << 8) + p[3]) + 4 > savelen ? savelen : ((p[2] << 8) + p[3]) + 4;
-			us->srb->cmnd[7] = length >> 8;
-			us->srb->cmnd[8] = length;
-			break;
-      
-		case MODE_SENSE_10:
-			US_DEBUGP("MODE_SENSE_10 Mode data length is %d\n",
-				  (p[0] << 8) + p[1]);
-			length = ((p[0] << 8) + p[1]) + 6 > savelen ? savelen : ((p[0] << 8) + p[1]) + 6;
-			us->srb->cmnd[7] = length >> 8;
-			us->srb->cmnd[8] = length;
-			break;
-		} /* end switch on cmnd[0] */
-    
-		US_DEBUGP("Old/New length = %d/%d\n",
-			  savelen, length);
-    
-		/* issue the new command */
-		/* FIXME: this assumes that the second attempt is 
-		 * always successful */
-		if (us->srb->request_bufflen != length) {
-			US_DEBUGP("redoing cmd with len=%d\n", length);
-			us->srb->request_bufflen = length;
-			us->srb->result = us->transport(us->srb, us);
-		}
-    
-		/* reset back to original values */
-		us->srb->request_bufflen = savelen;
-
-		/* fix data as necessary */
-		switch (us->srb->cmnd[0]) {
-		case INQUIRY:
-			if ((((unsigned char*)us->srb->request_buffer)[2] & 0x7) == 0) { 
-				US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
-				((unsigned char*)us->srb->request_buffer)[2] |= 2;
-			}
-			/* FALL THROUGH */
-		case REQUEST_SENSE:
-		case MODE_SENSE:
-			if (us->srb->use_sg == 0 && length > 0) {
-				int i;
-				printk(KERN_DEBUG "Data is");
-				for (i = 0; i < 32 && i < length; ++i)
-					printk(" %.2x", ((unsigned char *)us->srb->request_buffer)[i]);
-				if (i < length)
-					printk(" ...");
-				printk("\n");
-			}
-
-			/* FIXME: is this really necessary? */
-			us->srb->cmnd[4] = saveallocation;
-			break;
-      
-		case LOG_SENSE:
-		case MODE_SENSE_10:
-			/* FIXME: is this really necessary? */
-			us->srb->cmnd[7] = saveallocation >> 8;
-			us->srb->cmnd[8] = saveallocation;
-			break;
-		} /* end switch on cmnd[0] */
-	} /* if good command */
+	/* fix the results of an INQUIRY */
+	if (us->srb->cmnd[0] == INQUIRY) {
+		US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n");
+		((unsigned char*)us->srb->request_buffer)[2] |= 2;
+	}
 }
 
 /***********************************************************************
@@ -789,7 +722,7 @@
 	/* was this a wanted interrupt? */
 	if (us->ip_wanted) {
 		us->ip_wanted = 0;
-		wake_up(&us->ip_waitq);
+		up(&(us->ip_waitq));
 	} else {
 		US_DEBUGP("ERROR: Unwanted interrupt received!\n");
 	}
@@ -801,9 +734,7 @@
 	return 0;
 }
 
-/* FIXME: this reset function doesn't really reset the port, and it
- * should. Actually it should probably do what it's doing here, and
- * reset the port physically
+/* This issues a CB[I] Reset to the device in question
  */
 static int CB_reset(struct us_data *us)
 {
@@ -816,41 +747,39 @@
 	cmd[0] = SEND_DIAGNOSTIC;
 	cmd[1] = 4;
 	result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
-				 US_CBI_ADSC, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+				 US_CBI_ADSC, 
+				 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
 				 0, us->ifnum, cmd, sizeof(cmd), HZ*5);
 
 	/* long wait for reset */
 	schedule_timeout(HZ*6);
 
 	US_DEBUGP("CB_reset: clearing endpoint halt\n");
-	usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
-	usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
+	usb_clear_halt(us->pusb_dev, 
+		       usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+	usb_clear_halt(us->pusb_dev, 
+		       usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
 
 	US_DEBUGP("CB_reset done\n");
 	return 0;
 }
 
-static int pop_CB_status(Scsi_Cmnd *srb);
-
-/* FIXME: we also need a CBI_command which sets up the completion
- * interrupt, and waits for it
+/*
+ * Control/Bulk/Interrupt transport
  */
-static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
+static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
 	int result;
 
 	US_DEBUGP("CBI gets a command:\n");
 	US_DEBUG(us_show_command(srb));
 
-	/* FIXME: we aren't setting the ip_wanted indicator early enough, which
-	 * causes some commands to never complete.  This hangs the driver.
-	 */
-
+	/* 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_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);
 
 	/* check the return code for the command */
 	if (result < 0) {
@@ -858,131 +787,160 @@
 
 		/* a stall is a fatal condition from the device */
 		if (result == -EPIPE) {
-			US_DEBUGP("-- Stall on control pipe detected. Clearing\n");
-      
+			US_DEBUGP("-- Stall on control pipe. Clearing\n");
 			US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
 				  usb_clear_halt(us->pusb_dev, 
-						 usb_sndctrlpipe(us->pusb_dev, 0)));
+						 usb_sndctrlpipe(us->pusb_dev,
+								 0)));
 			return USB_STOR_TRANSPORT_ERROR;
 		}
 
-		/* FIXME: we need to handle NAKs here */
+				/* FIXME: we need to handle NAKs here */
 		return USB_STOR_TRANSPORT_ERROR;
 	}
 
+	/* Set up for status notification */
+	us->ip_wanted = 1;
+
+	/* DATA STAGE */
 	/* transfer the data payload for this command, if one exists*/
 	if (us_transfer_length(srb)) {
-		result = us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
-		US_DEBUGP("CBI attempted to transfer data, result is 0x%x\n", result);
+		us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+		US_DEBUGP("CBI data stage result is 0x%x\n", result);
+	}
 
-		/* FIXME: what do the return codes from us_transfer mean? */
-		if ((result < 0) && 
-		    (result != USB_ST_DATAUNDERRUN) && 
-		    (result != USB_ST_STALL)) {
-			return DID_ERROR << 16;
-		}
-	} /* if (us_transfer_length(srb)) */
+	/* STATUS STAGE */
 
-	/* get status and return it */
-	return pop_CB_status(srb);
+	/* go to sleep until we get this interrup */
+	/* FIXME: this should be changed to use a timeout */
+	down(&(us->ip_waitq));
+	
+	/* FIXME: currently this code is unreachable, but the idea is
+	 * necessary.  See above comment.
+	 */
+	if (us->ip_wanted) {
+		US_DEBUGP("Did not get interrupt on CBI\n");
+		us->ip_wanted = 0;
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+	
+	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?
+	 */
+	if (us->subclass == US_SC_UFI) {
+		if (srb->cmnd[0] == REQUEST_SENSE ||
+		    srb->cmnd[0] == INQUIRY)
+			return USB_STOR_TRANSPORT_GOOD;
+		else
+			if (us->ip_data)
+				return USB_STOR_TRANSPORT_FAILED;
+			else
+				return USB_STOR_TRANSPORT_GOOD;
+	}
+	
+	/* otherwise, we interpret the data normally */
+	switch (us->ip_data) {
+	case 0x0001: 
+		return USB_STOR_TRANSPORT_GOOD;
+	case 0x0002: 
+		return USB_STOR_TRANSPORT_FAILED;
+	default: 
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+
+	US_DEBUGP("CBI_transport() reached end of function\n");
+	return USB_STOR_TRANSPORT_ERROR;
 }
 
 /*
- * Control/Bulk status handler
+ * Control/Bulk transport
  */
-
-static int pop_CB_status(Scsi_Cmnd *srb)
+static int CB_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
-	struct us_data *us = (struct us_data *)srb->host_scribble;
-	int result = 0;
+	int result;
 	__u8 status[2];
-	int retry = 5;
-
-	US_DEBUGP("pop_CB_status, proto=0x%x\n", us->protocol);
-	switch (us->protocol) {
-	case US_PR_CB:
-		/* get from control */
 
-		while (retry--) {
-			result = usb_control_msg(us->pusb_dev, usb_rcvctrlpipe(us->pusb_dev,0),
-						 USB_REQ_GET_STATUS, USB_DIR_IN |
-						 USB_TYPE_STANDARD | USB_RECIP_DEVICE,
-						 0, us->ifnum, status, sizeof(status), HZ*5);
-			if (result != USB_ST_TIMEOUT)
-				break;
-		}
-		if (result) {
-			US_DEBUGP("Bad AP status request %d\n", result);
-			return DID_ABORT << 16;
-		}
-		US_DEBUGP("Got AP status 0x%x 0x%x\n", status[0], status[1]);
-		if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
-		    ( (status[0] & ~3) || status[1]))
-			return (DID_OK << 16) | 2;
-		else
-			return USB_STOR_TRANSPORT_GOOD;
-		break;
+	US_DEBUGP("CBC gets a command:\n");
+	US_DEBUG(us_show_command(srb));
 
-		/* FIXME: this should be in a separate function */
-	case US_PR_CBI:
-		/* get from interrupt pipe */
+	/* 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);
 
-		/* add interrupt transfer, marked for removal */
-		us->ip_wanted = 1;
+	/* check the return code for the command */
+	if (result < 0) {
+		US_DEBUGP("Call to usb_control_msg() returned %d\n", result);
 
-		/* go to sleep until we get this interrup */
-		/* FIXME: this should be changed to use a timeout */
-		sleep_on(&us->ip_waitq);
-    
-		if (us->ip_wanted) {
-			US_DEBUGP("Did not get interrupt on CBI\n");
-			us->ip_wanted = 0;
+		/* a stall is a fatal condition from the device */
+		if (result == -EPIPE) {
+			US_DEBUGP("-- Stall on control pipe. Clearing\n");
+			US_DEBUGP("-- Return from usb_clear_halt() is %d\n",
+				  usb_clear_halt(us->pusb_dev, 
+						 usb_sndctrlpipe(us->pusb_dev,
+								 0)));
 			return USB_STOR_TRANSPORT_ERROR;
 		}
-    
-		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?
-		 */
-		if (us->subclass == US_SC_UFI) {
-			if (srb->cmnd[0] == REQUEST_SENSE ||
-			    srb->cmnd[0] == INQUIRY)
-				return USB_STOR_TRANSPORT_GOOD;
-			else
-				if (us->ip_data)
-					return USB_STOR_TRANSPORT_FAILED;
-				else
-					return USB_STOR_TRANSPORT_GOOD;
-		}
+				/* FIXME: we need to handle NAKs here */
+		return USB_STOR_TRANSPORT_ERROR;
+	}
 
-		/* otherwise, we interpret the data normally */
-		switch (us->ip_data) {
-		case 0x0001: 
-			return USB_STOR_TRANSPORT_GOOD;
-		case 0x0002: 
-			return USB_STOR_TRANSPORT_FAILED;
-		default: 
-			return USB_STOR_TRANSPORT_ERROR;
-		}
+	/* DATA STAGE */
+	/* transfer the data payload for this command, if one exists*/
+	if (us_transfer_length(srb)) {
+		us_transfer(srb, US_DIRECTION(srb->cmnd[0]));
+		US_DEBUGP("CBC data stage result is 0x%x\n", result);
 	}
-	US_DEBUGP("pop_CB_status, reached end of function\n");
+	
+	
+	/* STATUS STAGE */
+	/* FIXME: this is wrong */
+	result = usb_control_msg(us->pusb_dev, 
+				 usb_rcvctrlpipe(us->pusb_dev,0),
+				 USB_REQ_GET_STATUS, USB_DIR_IN |
+				 USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+				 0, us->ifnum, status, sizeof(status), HZ*5);
+
+	if (result < 0) {
+		US_DEBUGP("CBC Status stage returns %d\n", result);
+		return USB_STOR_TRANSPORT_ERROR;
+	}
+
+	US_DEBUGP("Got CB status 0x%x 0x%x\n", status[0], status[1]);
+	if (srb->cmnd[0] != REQUEST_SENSE && srb->cmnd[0] != INQUIRY &&
+	    ( (status[0] & ~3) || status[1]))
+		return USB_STOR_TRANSPORT_FAILED;
+	else
+		return USB_STOR_TRANSPORT_GOOD;
+	
+	US_DEBUGP("CB_transport() reached end of function\n");
 	return USB_STOR_TRANSPORT_ERROR;
 }
 
+/* FIXME: Does this work? */
 static int Bulk_reset(struct us_data *us)
 {
 	int result;
 
-	result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0),
-				 US_BULK_RESET, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-				 US_BULK_RESET_HARD, us->ifnum,
-				 NULL, 0, HZ*5);
-	if (result)
+	result = usb_control_msg(us->pusb_dev, 
+				 usb_sndctrlpipe(us->pusb_dev,0), 
+				 US_BULK_RESET, 
+				 USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+				 US_BULK_RESET_HARD, us->ifnum, NULL, 0, HZ*5);
+
+	if (result < 0)
 		US_DEBUGP("Bulk hard reset failed %d\n", result);
-	usb_clear_halt(us->pusb_dev, usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
-	usb_clear_halt(us->pusb_dev, usb_sndbulkpipe(us->pusb_dev, us->ep_out));
+
+	usb_clear_halt(us->pusb_dev, 
+		       usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+	usb_clear_halt(us->pusb_dev, 
+		       usb_sndbulkpipe(us->pusb_dev, us->ep_out));
 
 	/* long wait for reset */
 	schedule_timeout(HZ*6);
@@ -991,8 +949,7 @@
 }
 
 /*
- * The bulk only protocol handler.
- *	Uses the in and out endpoints to transfer commands and data
+ * Bulk only transport
  */
 static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us)
 {
@@ -1001,7 +958,7 @@
 	int result;
 	int pipe;
 	int partial;
-
+	
 	/* set up the command wrapper */
 	bcb.Signature = US_BULK_CB_SIGN;
 	bcb.DataTransferLength = us_transfer_length(srb);
@@ -1009,14 +966,14 @@
 	bcb.Tag = srb->serial_number;
 	bcb.Lun = 0;
 	bcb.Length = srb->cmd_len;
-
+	
 	/* construct the pipe handle */
 	pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out);
-
+	
 	/* copy the command payload */
 	memset(bcb.CDB, 0, sizeof(bcb.CDB));
 	memcpy(bcb.CDB, srb->cmnd, bcb.Length);
-
+	
 	/* send it to out endpoint */
 	US_DEBUGP("Bulk command S 0x%x T 0x%x L %d F %d CL %d\n",
 		  bcb.Signature, bcb.Tag, bcb.DataTransferLength,
@@ -1024,94 +981,83 @@
 	result = usb_bulk_msg(us->pusb_dev, pipe, &bcb,
 			      US_BULK_CB_WRAP_LEN, &partial, HZ*5);
 	US_DEBUGP("Bulk command transfer result=%d\n", result);
-
+	
 	/* 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);
 	}
-  
+	
 	/* if the command transfered well, then we go to the data stage */
-	/* FIXME: Regardless of the status of the data stage, we go on to the
-	 * status stage.  Note that this implies that if a command is
-	 * partially successful, we rely on the device reporting an error
-	 * the CSW. The spec says that the device may just decide to short us.
-	 */
 	if (result == 0) {
 		/* send/receive data payload, if there is any */
 		if (bcb.DataTransferLength) {
-			result = us_transfer(srb, bcb.Flags);
-			US_DEBUGP("Bulk data transfer result 0x%x\n", result);
-#if 0
-			if ((result < 0) && (result != USB_ST_DATAUNDERRUN) 
-			    && (result != USB_ST_STALL)) {
-				US_DEBUGP("Bulk data transfer result 0x%x\n", result);
-				return DID_ABORT << 16;
-			}
-#endif
+			us_transfer(srb, bcb.Flags);
+			US_DEBUGP("Bulk data transfer result 0x%x\n", 
+				  srb->result);
 		}
 	}
-
+	
 	/* See flow chart on pg 15 of the Bulk Only Transport spec for
 	 * an explanation of how this code works.
 	 */
-
+	
 	/* construct the pipe handle */
 	pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in);
-
+	
 	/* get CSW for device status */
 	result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
 			      US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-
+	
 	/* did the attempt to read the CSW fail? */
 	if (result == -EPIPE) {
 		US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe);
 		usb_clear_halt(us->pusb_dev, pipe);
-
+		
 		/* get the status again */
 		result = usb_bulk_msg(us->pusb_dev, pipe, &bcs,
 				      US_BULK_CS_WRAP_LEN, &partial, HZ*5);
-   
+		
 		/* if it fails again, we need a reset and return an error*/
 		if (result == -EPIPE) {
 			Bulk_reset(us);
-			return (DID_ABORT << 16);
+			return USB_STOR_TRANSPORT_ERROR;
 		}
 	}
-
+	
 	/* if we still have a failure at this point, we're in trouble */
 	if (result) {
-		US_DEBUGP("Bulk status result = 0x%x\n", result);
-		return DID_ABORT << 16;
+		US_DEBUGP("Bulk status result = %d\n", result);
+		return USB_STOR_TRANSPORT_ERROR;
 	}
-
+	
 	/* check bulk status */
 	US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n",
 		  bcs.Signature, bcs.Tag, bcs.Residue, bcs.Status);
 	if (bcs.Signature != US_BULK_CS_SIGN || bcs.Tag != bcb.Tag ||
 	    bcs.Status > US_BULK_STAT_PHASE || partial != 13) {
 		US_DEBUGP("Bulk logical error\n");
-		return DID_ABORT << 16;
+		return USB_STOR_TRANSPORT_ERROR;
 	}
-
+	
 	/* based on the status code, we report good or bad */
 	switch (bcs.Status) {
 	case US_BULK_STAT_OK:
-		/* if there is residue, we really didn't finish the command */
-		if (bcs.Residue)
-			return DID_ERROR << 16;
-		else
-			return DID_OK << 16;
+		/* command good -- note that we could be short on data */
+		return USB_STOR_TRANSPORT_GOOD;
 
 	case US_BULK_STAT_FAIL:
-		return DID_ERROR << 16;
-
+		/* command failed */
+		return USB_STOR_TRANSPORT_FAILED;
+		
 	case US_BULK_STAT_PHASE:
+		/* phase error */
 		Bulk_reset(us);
-		return DID_ERROR << 16;
+		return USB_STOR_TRANSPORT_ERROR;
 	}
-
-	return DID_OK << 16;	    /* check sense required */
+	
+	/* we should never get here, but if we do, we're in trouble */
+	return USB_STOR_TRANSPORT_ERROR;
 }
 
 /***********************************************************************
@@ -1163,14 +1109,20 @@
 		usb_release_irq(us->pusb_dev, us->irq_handle, us->irqpipe);
 		us->irq_handle = NULL;
 	}
-	if (us->pusb_dev)
-		usb_deregister(&storage_driver);
+
+	/* 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 !!!) */
-	while (prev->next != us)
-		prev = prev->next;
-	prev->next = us->next;
+	if (us_list == us)
+		us_list = us->next;
+	else {
+		while (prev->next != us)
+			prev = prev->next;
+		prev->next = us->next;
+	}
 	return 0;
 }
 
@@ -1188,17 +1140,19 @@
 	struct us_data *us = (struct us_data *)srb->host->hostdata[0];
 
 	US_DEBUGP("Command wakeup\n");
-	if (us->srb) {
-		/* busy */
-	}
 	srb->host_scribble = (unsigned char *)us;
-	us->srb = srb;
+
+	/* get exclusive access to the structures we want */
+	down(&(us->queue_exclusion));
+
+	/* enqueue the command */
+	us->queue_srb = srb;
 	srb->scsi_done = done;
 	us->action = US_ACT_COMMAND;
 
 	/* wake up the process task */
-
-	wake_up_interruptible(&us->waitq);
+	up(&(us->queue_exclusion));
+	up(&(us->sleeper));
 
 	return 0;
 }
@@ -1209,6 +1163,7 @@
 	return 0;
 }
 
+/* FIXME: this doesn't do anything right now */
 static int us_bus_reset( Scsi_Cmnd *srb )
 {
 	//  struct us_data *us = (struct us_data *)srb->host->hostdata[0];
@@ -1340,11 +1295,11 @@
 	NULL,			    /* select_queue_depths */
 	1,			    /* can_queue */
 	-1,			    /* this_id */
-	SG_ALL,		    /* sg_tablesize */
+	SG_ALL, 		    /* sg_tablesize */
 	1,			    /* cmd_per_lun */
 	0,			    /* present */
-	FALSE,		    /* unchecked_isa_dma */
-	FALSE,		    /* use_clustering */
+	FALSE,		            /* unchecked_isa_dma */
+	TRUE,  		            /* use_clustering */
 	TRUE,			    /* use_new_eh_code */
 	TRUE			    /* emulated */
 };
@@ -1391,10 +1346,18 @@
 		siginfo_t info;
 		int unsigned long signr;
 
-		interruptible_sleep_on(&us->waitq);
+		US_DEBUGP("*** thread sleeping.\n");
+		down(&(us->sleeper));
+		down(&(us->queue_exclusion));
+		US_DEBUGP("*** thread awakened.\n");
 
+				/* take the command off the queue */
 		action = us->action;
 		us->action = 0;
+		us->srb = us-> queue_srb;
+     	
+		/* release the queue lock as fast as possible */
+		up(&(us->queue_exclusion));
 
 		/* FIXME: we need to examine placment of break; and 
 		 * scsi_done() calls */
@@ -1460,29 +1423,20 @@
 			break;
 
 		} /* end switch on action */
-    
+
+		/* FIXME: we ignore TERM and KILL... is this right? */
 		if (signal_pending(current)) {
 			/* sending SIGUSR1 makes us print out some info */
 			spin_lock_irq(&current->sigmask_lock);
 			signr = dequeue_signal(&current->blocked, &info);
 			spin_unlock_irq(&current->sigmask_lock);
-
-			if (signr == SIGUSR2) {
-				usb_stor_debug = !usb_stor_debug;
-				printk(USB_STORAGE "debug toggle = %d\n", usb_stor_debug);
-			} else {
-				break;	    /* exit the loop on any other signal */
-			}
-		}
-	}
+		} /* if (singal_pending(current)) */
+	} /* for (;;) */
   
 	//  MOD_DEC_USE_COUNT;
 
 	printk("usb_stor_control_thread exiting\n");
 
-	/* FIXME: this is a hack to allow for debugging */
-	// scsi_unregister_module(MODULE_SCSI_HA, us->htmplt);
-
 	return 0;
 }	
 
@@ -1498,7 +1452,6 @@
 	unsigned int flags = 0;
 	GUID(guid);		     /* Global Unique Identifier */
 	struct us_data *prev;
-	Scsi_Host_Template *htmplt;
 	int protocol = 0;
 	int subclass = 0;
 	struct usb_interface_descriptor *altsetting = 
@@ -1565,14 +1518,18 @@
 			return NULL;
 		}
 		memset(ss, 0, sizeof(struct us_data));
+
+		/* Initialize the mutexes only when the struct is new */
+		init_MUTEX_LOCKED(&(ss->sleeper));
+		init_MUTEX(&(ss->queue_exclusion));
 	}
 
-	/* Initialize the us_data structure with some useful info */
+	/* establish the connection to the new device */
 	interface = altsetting;
 	ss->flags = flags;
 	ss->ifnum = ifnum;
-	ss->pusb_dev = dev;
 	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.
@@ -1596,7 +1553,7 @@
 
 	case US_PR_CBI:
 		US_DEBUGPX("Control/Bulk/Interrupt\n");
-		ss->transport = CB_transport;
+		ss->transport = CBI_transport;
 		ss->transport_reset = CB_reset;
 		break;
 
@@ -1620,7 +1577,7 @@
 	 */
 	for (i = 0; i < interface->bNumEndpoints; i++) {
 		/* is it an BULK endpoint? */
-		if ((interface->endpoint[i].bmAttributes &  USB_ENDPOINT_XFERTYPE_MASK)
+		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 &
@@ -1646,7 +1603,6 @@
 	    (ss->protocol == US_PR_CBI && ss->ep_int == 0)) {
 		US_DEBUGP("Problems with device\n");
 		if (ss->host) {
-			scsi_unregister_module(MODULE_SCSI_HA, ss->htmplt);
 			kfree(ss->htmplt->name);
 			kfree(ss->htmplt);
 		}
@@ -1667,11 +1623,13 @@
 		US_DEBUGP("Protocol: ");
 		switch (ss->subclass) {
 		case US_SC_RBC:
-			US_DEBUGPX("Reduced Block Commands\n");
+			US_DEBUGPX("Reduced Block Commands (RBC)\n");
+			ss->proto_handler = transparent_scsi_command;
 			break;
 
 		case US_SC_8020:
-			US_DEBUGPX("8020\n");
+			US_DEBUGPX("8020i\n");
+			ss->proto_handler = ATAPI_command;
 			break;
 
 		case US_SC_QIC:
@@ -1679,7 +1637,8 @@
 			break;
 
 		case US_SC_8070:
-			US_DEBUGPX("8070\n");
+			US_DEBUGPX("8070i\n");
+			ss->proto_handler = ATAPI_command;
 			break;
 
 		case US_SC_SCSI:
@@ -1697,22 +1656,9 @@
 			break;
 		}
 
-		/* We only handle certain protocols.  Currently, these are
-		 *the only ones that devices use.
-		 */
-		if ((ss->subclass != US_SC_SCSI) && (ss->subclass != US_SC_UFI)) {
-			US_DEBUGP("Sorry, we do not support that protocol yet.\n");
-			US_DEBUGP("If you have a device which uses one of the unsupported\n");
-			US_DEBUGP("protocols, please contact mdharm-usb@one-eyed-alien.net\n");
-     
-			kfree(ss);
-			return NULL;
-		}
-
 		/* Allocate memory for the SCSI Host Template */
-		if ((htmplt = (Scsi_Host_Template *)
-		     kmalloc(sizeof(*ss->htmplt), GFP_KERNEL)) == NULL ) {
-
+		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);
@@ -1720,7 +1666,7 @@
 		}
 
 		/* Initialize the host template based on the default one */
-		memcpy(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++;
@@ -1729,32 +1675,34 @@
 		 * can pass the ss pointer to the host controler thread
 		 * in us_detect
 		 */
-		(struct us_data *)htmplt->proc_dir = ss; 
+		(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),
+			
+			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_waitqueue_head(&ss->ip_waitq);
+			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)
+			result = usb_request_irq(ss->pusb_dev, ss->irqpipe, 
+						 CBI_irq, 255, (void *)ss, 
+						 &ss->irq_handle);
+			if (result < 0)
 				return NULL;
-
-			interruptible_sleep_on_timeout(&ss->ip_waitq, HZ*6);
-		} else if (ss->protocol == US_PR_CBI)
-		{
+			/* FIXME: what is this?? */
+			down(&(ss->ip_waitq));
+		} else if (ss->protocol == US_PR_CBI) {
 			int result; 
-
-			init_waitqueue_head(&ss->ip_waitq);
+			
+			/* 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 */
@@ -1768,18 +1716,16 @@
 		}
     
 
-		/* start up our thread */
+				/* start up our thread */
 		{
 			DECLARE_MUTEX_LOCKED(sem);
 
-			init_waitqueue_head(&ss->waitq);
-
 			ss->notify = &sem;
 			ss->pid = kernel_thread(usb_stor_control_thread, ss,
 						CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
 			if (ss->pid < 0) {
 				printk(KERN_WARNING USB_STORAGE "Unable to start control thread\n");
-				kfree(htmplt);
+				kfree(ss->htmplt);
 
 				kfree(ss);
 				return NULL;
@@ -1790,17 +1736,16 @@
 		}
 
 		/* now register - our detect function will be called */
-		scsi_register_module(MODULE_SCSI_HA, htmplt);
+		ss->htmplt->module = &__this_module;
+		scsi_register_module(MODULE_SCSI_HA, ss->htmplt);
 
 		/* put us in the list */
-		prev = (struct us_data *)&us_list;
-		while (prev->next)
-			prev = prev->next;
-		prev->next = ss;
+		ss->next = us_list;
+                us_list = ss;
 	}
 
-	printk(KERN_INFO "WARNING: USB Mass Storage data integrity not assured\n");
-	printk(KERN_INFO "USB Mass Storage device found at %d\n", dev->devnum);
+	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 ss;
 }
@@ -1814,7 +1759,6 @@
 		return;
 
 	ss->pusb_dev = NULL;
-	//  MOD_DEC_USE_COUNT;
 }
 
 
@@ -1824,8 +1768,6 @@
 
 int __init usb_stor_init(void)
 {
-	//  MOD_INC_USE_COUNT;
-
 	if (sizeof(my_host_template) != SCSI_HOST_TEMPLATE_SIZE) {
 		printk(KERN_ERR "usb-storage: SCSI_HOST_TEMPLATE_SIZE does not match\n") ;
 		printk(KERN_ERR "usb-storage: expected %d bytes, got %d bytes\n", 
@@ -1844,6 +1786,14 @@
 
 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);
+
 	usb_deregister(&storage_driver) ;
 }
 

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