patch-2.4.0-test7 linux/drivers/usb/storage/transport.c

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

diff -u --recursive --new-file v2.4.0-test6/linux/drivers/usb/storage/transport.c linux/drivers/usb/storage/transport.c
@@ -1,12 +1,13 @@
 /* Driver for USB Mass Storage compliant devices
  *
- * $Id: transport.c,v 1.5 2000/07/28 22:40:20 mdharm Exp $
+ * $Id: transport.c,v 1.12 2000/08/08 15:22:38 gowdy Exp $
  *
  * Current development and maintainance by:
  *   (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
  *
  * Developed with the assistance of:
  *   (c) 2000 David L. Brown, Jr. (usb-storage@davidb.org)
+ *   (c) 2000 Stephen J. Gowdy (SGowdy@lbl.gov)
  *
  * Initial work by:
  *   (c) 1999 Michael Gee (michael@linuxspecific.com)
@@ -52,6 +53,353 @@
 #include <linux/malloc.h>
 
 /***********************************************************************
+ * Helper routines
+ ***********************************************************************/
+
+/* Calculate the length of the data transfer (not the command) for any
+ * given SCSI command
+ */
+static unsigned int us_transfer_length(Scsi_Cmnd *srb, struct us_data *us)
+{
+	int i;
+	unsigned int total = 0;
+	struct scatterlist *sg;
+
+	/* support those devices which need the length calculated
+	 * differently 
+	 */
+	if (srb->cmnd[0] == INQUIRY) {
+		srb->cmnd[4] = 36;
+	}
+
+	if ((srb->cmnd[0] == INQUIRY) || (srb->cmnd[0] == MODE_SENSE))
+		return srb->cmnd[4];
+
+	if (srb->cmnd[0] == TEST_UNIT_READY)
+		return 0;
+
+	/* Are we going to scatter gather? */
+	if (srb->use_sg) {
+		/* Add up the sizes of all the scatter-gather segments */
+		sg = (struct scatterlist *) srb->request_buffer;
+		for (i = 0; i < srb->use_sg; i++)
+			total += sg[i].length;
+
+		return total;
+	}
+	else
+		/* Just return the length of the buffer */
+		return srb->request_bufflen;
+}
+
+/* Calculate the length of the data transfer (not the command) for any
+ * given SCSI command
+ */
+static unsigned int us_transfer_length_new(Scsi_Cmnd *srb, struct us_data *us)
+{
+      	int i;
+	int doDefault = 0;
+	unsigned int len = 0;
+	unsigned int total = 0;
+	struct scatterlist *sg;
+
+	/* This table tells us:
+	   X = command not supported
+	   L = return length in cmnd[4] (8 bits).
+	   M = return length in cmnd[8] (8 bits).
+	   G = return length in cmnd[3] and cmnd[4] (16 bits)
+	   H = return length in cmnd[7] and cmnd[8] (16 bits)
+	   I = return length in cmnd[8] and cmnd[9] (16 bits)
+	   C = return length in cmnd[2] to cmnd[5] (32 bits)
+	   D = return length in cmnd[6] to cmnd[9] (32 bits)
+	   B = return length in blocksize so we use buff_len
+	   R = return length in cmnd[2] to cmnd[4] (24 bits)
+	   S = return length in cmnd[3] to cmnd[5] (24 bits)
+	   T = return length in cmnd[6] to cmnd[8] (24 bits)
+	   U = return length in cmnd[7] to cmnd[9] (24 bits)
+	   0-9 = fixed return length
+	   V = 20 bytes
+	   W = 24 bytes
+	   Z = return length is mode dependant or not in command, use buff_len
+	*/
+
+	static char *lengths =
+		
+		/* 0123456789ABCDEF   0123456789ABCDEF */
+		
+		"00XLZ6XZBXBBXXXB" "00LBBLG0R0L0GG0X"  /* 00-1F */
+		"XXXXT8XXB4B0BBBB" "ZZZ0B00HCSSZTBHH"  /* 20-3F */
+		"M0HHB0X000H0HH0X" "XHH00HXX0TH0H0XX"  /* 40-5F */
+		"XXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXX"  /* 60-7F */
+		"XXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXX"  /* 80-9F */
+		"X0XXX00XB0BXBXBB" "ZZZ0XUIDU000XHBX"  /* A0-BF */
+		"XXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXX"  /* C0-DF */
+		"XDXXXXXXXXXXXXXX" "XXW00HXXXXXXXXXX"; /* E0-FF */
+
+	/* Commands checked in table:
+
+	   CHANGE_DEFINITION 40
+	   COMPARE 39
+	   COPY 18
+	   COPY_AND_VERIFY 3a
+	   ERASE 19
+	   ERASE_10 2c
+	   ERASE_12 ac
+	   EXCHANGE_MEDIUM a6
+	   FORMAT_UNIT 04
+	   GET_DATA_BUFFER_STATUS 34
+	   GET_MESSAGE_10 28
+	   GET_MESSAGE_12 a8
+	   GET_WINDOW 25   !!! Has more data than READ_CAPACITY, need to fix table
+	   INITIALIZE_ELEMENT_STATUS 07 !!! REASSIGN_BLOCKS luckily uses buff_len
+	   INQUIRY 12
+	   LOAD_UNLOAD 1b
+	   LOCATE 2b
+	   LOCK_UNLOCK_CACHE 36
+	   LOG_SELECT 4c
+	   LOG_SENSE 4d
+	   MEDIUM_SCAN 38     !!! This was M
+	   MODE_SELECT6 15
+	   MODE_SELECT_10 55
+	   MODE_SENSE_6 1a
+	   MODE_SENSE_10 5a
+	   MOVE_MEDIUM a5
+	   OBJECT_POSITION 31  !!! Same as SEARCH_DATA_EQUAL
+	   PAUSE_RESUME 4b
+	   PLAY_AUDIO_10 45
+	   PLAY_AUDIO_12 a5
+	   PLAY_AUDIO_MSF 47
+	   PLAY_AUDIO_TRACK_INDEX 48
+   	   PLAY_AUDIO_TRACK_RELATIVE_10 49
+	   PLAY_AUDIO_TRACK_RELATIVE_12 a9
+	   POSITION_TO_ELEMENT 2b
+      	   PRE-FETCH 34
+	   PREVENT_ALLOW_MEDIUM_REMOVAL 1e
+	   PRINT 0a             !!! Same as WRITE_6 but is always in bytes
+	   READ_6 08
+	   READ_10 28
+	   READ_12 a8
+	   READ_BLOCK_LIMITS 05
+	   READ_BUFFER 3c
+	   READ_CAPACITY 25
+	   READ_CDROM_CAPACITY 25
+	   READ_DEFECT_DATA 37
+	   READ_DEFECT_DATA_12 b7
+	   READ_ELEMENT_STATUS b8 !!! Think this is in bytes
+	   READ_GENERATION 29 !!! Could also be M?
+	   READ_HEADER 44     !!! This was L
+	   READ_LONG 3e
+	   READ_POSITION 34   !!! This should be V but conflicts with PRE-FETCH
+	   READ_REVERSE 0f
+	   READ_SUB-CHANNEL 42 !!! Is this in bytes?
+	   READ_TOC 43         !!! Is this in bytes?
+	   READ_UPDATED_BLOCK 2d
+	   REASSIGN_BLOCKS 07
+	   RECEIVE 08        !!! Same as READ_6 probably in bytes though
+	   RECEIVE_DIAGNOSTIC_RESULTS 1c
+	   RECOVER_BUFFERED_DATA 14 !!! For PRINTERs this is bytes
+	   RELEASE_UNIT 17
+	   REQUEST_SENSE 03
+	   REQUEST_VOLUME_ELEMENT_ADDRESS b5 !!! Think this is in bytes
+	   RESERVE_UNIT 16
+	   REWIND 01
+	   REZERO_UNIT 01
+	   SCAN 1b          !!! Conflicts with various commands, should be L
+	   SEARCH_DATA_EQUAL 31
+	   SEARCH_DATA_EQUAL_12 b1
+	   SEARCH_DATA_LOW 30
+	   SEARCH_DATA_LOW_12 b0
+	   SEARCH_DATA_HIGH 32
+	   SEARCH_DATA_HIGH_12 b2
+	   SEEK_6 0b         !!! Conflicts with SLEW_AND_PRINT
+	   SEEK_10 2b
+	   SEND 0a           !!! Same as WRITE_6, probably in bytes though
+	   SEND 2a           !!! Similar to WRITE_10 but for scanners
+	   SEND_DIAGNOSTIC 1d
+	   SEND_MESSAGE_6 0a   !!! Same as WRITE_6 - is in bytes
+	   SEND_MESSAGE_10 2a  !!! Same as WRITE_10 - is in bytes
+	   SEND_MESSAGE_12 aa  !!! Same as WRITE_12 - is in bytes
+	   SEND_VOLUME_TAG b6 !!! Think this is in bytes
+	   SET_LIMITS 33
+	   SET_LIMITS_12 b3
+	   SET_WINDOW 24
+	   SLEW_AND_PRINT 0b !!! Conflicts with SEEK_6
+	   SPACE 11
+	   START_STOP_UNIT 1b
+	   STOP_PRINT 1b
+	   SYNCHRONIZE_BUFFER 10
+	   SYNCHRONIZE_CACHE 35
+	   TEST_UNIT_READY 00
+	   UPDATE_BLOCK 3d
+	   VERIFY 13
+	   VERIFY 2f
+	   VERIFY_12 af
+	   WRITE_6 0a
+	   WRITE_10 2a
+	   WRITE_12 aa
+	   WRITE_AND_VERIFY 2e
+	   WRITE_AND_VERIFY_12 ae
+	   WRITE_BUFFER 3b
+	   WRITE_FILEMARKS 10
+	   WRITE_LONG 3f
+	   WRITE_SAME 41
+	*/
+
+	/* Not sure this is right as an INQUIRY can contain nonstandard info */
+	if (srb->cmnd[0] == INQUIRY)
+		srb->cmnd[4] = 36;
+	   
+	if (srb->sc_data_direction == SCSI_DATA_WRITE) {
+     		doDefault = 1;
+	}
+	else
+		switch (lengths[srb->cmnd[0]]) {
+		case 'L':
+			len = srb->cmnd[4];
+			break;
+
+		case 'M':
+			len = srb->cmnd[8];
+			break;
+
+		case '0':
+		case '1':
+		case '2':
+		case '3':
+		case '4':
+		case '5':
+		case '6':
+		case '7':
+		case '8':
+		case '9':
+			len = lengths[srb->cmnd[0]]-'0';
+			break;
+
+		case 'G':
+			len = (((unsigned int)srb->cmnd[3])<<8) |
+				srb->cmnd[4];
+			break;
+
+		case 'H':
+			len = (((unsigned int)srb->cmnd[7])<<8) |
+				srb->cmnd[8];
+			break;
+
+		case 'I':
+			len = (((unsigned int)srb->cmnd[8])<<8) |
+				srb->cmnd[9];
+			break;
+				   
+		case 'R':
+			len = (((unsigned int)srb->cmnd[2])<<16) |
+				(((unsigned int)srb->cmnd[3])<<8) |
+				srb->cmnd[4];
+			break;
+				   
+		case 'S':
+			len = (((unsigned int)srb->cmnd[3])<<16) |
+				(((unsigned int)srb->cmnd[4])<<8) |
+				srb->cmnd[5];
+			break;
+				   
+		case 'T':
+			len = (((unsigned int)srb->cmnd[6])<<16) |
+				(((unsigned int)srb->cmnd[7])<<8) |
+				srb->cmnd[8];
+			break;
+				   
+		case 'U':
+			len = (((unsigned int)srb->cmnd[7])<<16) |
+				(((unsigned int)srb->cmnd[8])<<8) |
+				srb->cmnd[9];
+			break;
+				   
+		case 'C':
+			len = (((unsigned int)srb->cmnd[2])<<24) |
+				(((unsigned int)srb->cmnd[3])<<16) |
+				(((unsigned int)srb->cmnd[4])<<8) |
+				srb->cmnd[5];
+			break;
+				   
+		case 'D':
+			len = (((unsigned int)srb->cmnd[6])<<24) |
+				(((unsigned int)srb->cmnd[7])<<16) |
+				(((unsigned int)srb->cmnd[8])<<8) |
+				srb->cmnd[9];
+			break;
+				   
+		case 'V':
+			len = 20;
+			break;
+				   
+		case 'W':
+			len = 24;
+			break;
+				   
+		case 'B':
+			/* Use buffer size due to different block sizes */
+			doDefault = 1;
+			break;
+				   
+		case 'X':
+			US_DEBUGP("Error: UNSUPPORTED COMMAND %02X\n",
+				  srb->cmnd[0]);
+			doDefault = 1;
+			break;
+				   
+		case 'Z':
+			/* Use buffer size due to mode dependence */
+			doDefault = 1;
+			break;
+				   
+		default:
+			US_DEBUGP("Error: COMMAND %02X out of range or table inconsistent (%c).\n",
+				  srb->cmnd[0], lengths[srb->cmnd[0]] );
+			doDefault = 1;
+		}
+	   
+	if ( doDefault == 1 ) {
+		/* Are we going to scatter gather? */
+		if (srb->use_sg) {
+			/* Add up the sizes of all the sg segments */
+			sg = (struct scatterlist *) srb->request_buffer;
+			for (i = 0; i < srb->use_sg; i++)
+				total += sg[i].length;
+			len = total;
+		}
+		else
+			/* Just return the length of the buffer */
+			len = srb->request_bufflen;
+	}
+
+	return len;
+}
+
+/* This is a version of usb_clear_halt() that doesn't read the status from
+ * the device -- this is because some devices crash their internal firmware
+ * when the status is requested after a halt
+ */
+static int clear_halt(struct usb_device *dev, int pipe)
+{
+	int result;
+	int endp = usb_pipeendpoint(pipe) | (usb_pipein(pipe) << 7);
+
+	result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+				 USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0,
+				 endp, NULL, 0, HZ * 3);
+
+	/* this is a failure case */
+	if (result < 0)
+		return result;
+
+	/* reset the toggles and endpoint flags */
+	usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe));
+	usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0);
+
+	return 0;
+}
+
+/***********************************************************************
  * Data transfer routines
  ***********************************************************************/
 
@@ -217,7 +565,7 @@
 	/* 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);
+		clear_halt(us->pusb_dev, pipe);
 	}
 	
 	/* did we send all the data? */
@@ -287,44 +635,6 @@
 	srb->result = result;
 }
 
-/* Calculate the length of the data transfer (not the command) for any
- * given SCSI command
- */
-static unsigned int us_transfer_length(Scsi_Cmnd *srb, struct us_data *us)
-{
-	int i;
-	unsigned int total = 0;
-	struct scatterlist *sg;
-
-	/* support those devices which need the length calculated
-	 * differently 
-	 */
-	if (us->flags & US_FL_ALT_LENGTH) {
-		if (srb->cmnd[0] == INQUIRY) {
-			srb->cmnd[4] = 36;
-		}
-
-		if ((srb->cmnd[0] == INQUIRY) || (srb->cmnd[0] == MODE_SENSE))
-			return srb->cmnd[4];
-
-		if (srb->cmnd[0] == TEST_UNIT_READY)
-			return 0;
-	}
-
-	/* Are we going to scatter gather? */
-	if (srb->use_sg) {
-		/* Add up the sizes of all the scatter-gather segments */
-		sg = (struct scatterlist *) srb->request_buffer;
-		for (i = 0; i < srb->use_sg; i++)
-			total += sg[i].length;
-
-		return total;
-	}
-	else
-		/* Just return the length of the buffer */
-		return srb->request_bufflen;
-}
-
 /***********************************************************************
  * Transport routines
  ***********************************************************************/
@@ -363,7 +673,7 @@
 	 * of determining status on it's own, we need to auto-sense almost
 	 * every time.
 	 */
-	if (us->protocol == US_PR_CB) {
+	if (us->protocol == US_PR_CB || us->protocol == US_PR_DPCM_USB) {
 		US_DEBUGP("-- CB transport device requiring auto-sense\n");
 		need_auto_sense = 1;
 
@@ -490,7 +800,7 @@
 	 * This is necessary because the auto-sense for some devices always
 	 * sets byte 0 == 0x70, even if there is no error
 	 */
-	if ((us->protocol == US_PR_CB) && 
+	if ((us->protocol == US_PR_CB || us->protocol == US_PR_DPCM_USB) && 
 	    (result == USB_STOR_TRANSPORT_GOOD) &&
 	    ((srb->sense_buffer[2] & 0xf) == 0x0))
 		srb->sense_buffer[0] = 0x0;
@@ -548,10 +858,10 @@
 		/* STALL must be cleared when they are detected */
 		if (result == -EPIPE) {
 			US_DEBUGP("-- Stall on control pipe. Clearing\n");
-			result = usb_clear_halt(us->pusb_dev,	
-						usb_sndctrlpipe(us->pusb_dev,
-								0));
-			US_DEBUGP("-- usb_clear_halt() returns %d\n", result);
+			result = clear_halt(us->pusb_dev,	
+					    usb_sndctrlpipe(us->pusb_dev,
+							    0));
+			US_DEBUGP("-- clear_halt() returns %d\n", result);
 			return USB_STOR_TRANSPORT_FAILED;
 		}
 
@@ -652,10 +962,10 @@
 		/* a stall is a fatal condition from the device */
 		if (result == -EPIPE) {
 			US_DEBUGP("-- Stall on control pipe. Clearing\n");
-			result = usb_clear_halt(us->pusb_dev, 
-						usb_sndctrlpipe(us->pusb_dev,
-								0));
-			US_DEBUGP("-- usb_clear_halt() returns %d\n", result);
+			result = clear_halt(us->pusb_dev, 
+					    usb_sndctrlpipe(us->pusb_dev,
+							    0));
+			US_DEBUGP("-- clear_halt() returns %d\n", result);
 			return USB_STOR_TRANSPORT_FAILED;
 		}
 
@@ -710,7 +1020,7 @@
 	/* 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);
+		clear_halt(us->pusb_dev, pipe);
 	}
 
 	/* return the default -- no LUNs */
@@ -757,7 +1067,7 @@
 	/* 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);
+		clear_halt(us->pusb_dev, pipe);
 	} else if (result) {
 		/* unknown error -- we've got a problem */
 		return USB_STOR_TRANSPORT_ERROR;
@@ -796,7 +1106,7 @@
 	/* 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);
+		clear_halt(us->pusb_dev, pipe);
 	       
 		/* get the status again */
 		US_DEBUGP("Attempting to get CSW (2nd try)...\n");
@@ -810,7 +1120,7 @@
 		/* if it fails again, we need a reset and return an error*/
 		if (result == -EPIPE) {
 			US_DEBUGP("clearing halt for pipe 0x%x\n", pipe);
-			usb_clear_halt(us->pusb_dev, pipe);
+			clear_halt(us->pusb_dev, pipe);
 			return USB_STOR_TRANSPORT_ERROR;
 		}
 	}
@@ -877,10 +1187,10 @@
 	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));
+	clear_halt(us->pusb_dev, 
+		   usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+	clear_halt(us->pusb_dev, 
+		   usb_rcvbulkpipe(us->pusb_dev, us->ep_out));
 
 	US_DEBUGP("CB_reset done\n");
 	return 0;
@@ -904,14 +1214,13 @@
 	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));
+	clear_halt(us->pusb_dev, 
+		   usb_rcvbulkpipe(us->pusb_dev, us->ep_in));
+	clear_halt(us->pusb_dev, 
+		   usb_sndbulkpipe(us->pusb_dev, us->ep_out));
 
 	/* long wait for reset */
 	schedule_timeout(HZ*6);
 
 	return result;
 }
-

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