patch-2.4.15 linux/drivers/usb/storage/freecom.c

Next file: linux/drivers/usb/storage/isd200.c
Previous file: linux/drivers/usb/serial/visor.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.14/linux/drivers/usb/storage/freecom.c linux/drivers/usb/storage/freecom.c
@@ -1,6 +1,6 @@
 /* Driver for Freecom USB/IDE adaptor
  *
- * $Id: freecom.c,v 1.15 2001/06/27 23:50:28 mdharm Exp $
+ * $Id: freecom.c,v 1.19 2001/11/11 05:42:34 mdharm Exp $
  *
  * Freecom v0.1:
  *
@@ -81,27 +81,28 @@
 
 /* Freecom stuffs the interrupt status in the INDEX_STAT bit of the ide
  * register. */
-#define FCM_INT_STATUS   INDEX_STAT
+#define FCM_INT_STATUS		0x02 /* INDEX_STAT */
+#define FCM_STATUS_BUSY		0x80
 
 /* These are the packet types.  The low bit indicates that this command
  * should wait for an interrupt. */
-#define FCM_PACKET_ATAPI  0x21
-#define FCM_PACKET_STATUS 0x20
+#define FCM_PACKET_ATAPI	0x21
+#define FCM_PACKET_STATUS	0x20
 
 /* Receive data from the IDE interface.  The ATAPI packet has already
  * waited, so the data should be immediately available. */
-#define FCM_PACKET_INPUT  0x81
+#define FCM_PACKET_INPUT	0x81
 
 /* Send data to the IDE interface. */
-#define FCM_PACKET_OUTPUT 0x01
+#define FCM_PACKET_OUTPUT	0x01
 
 /* Write a value to an ide register.  Or the ide register to write after
  * munging the address a bit. */
-#define FCM_PACKET_IDE_WRITE    0x40
-#define FCM_PACKET_IDE_READ     0xC0
+#define FCM_PACKET_IDE_WRITE	0x40
+#define FCM_PACKET_IDE_READ	0xC0
 
 /* All packets (except for status) are 64 bytes long. */
-#define FCM_PACKET_LENGTH 64
+#define FCM_PACKET_LENGTH	64
 
 /*
  * Transfer an entire SCSI command's worth of data payload over the bulk
@@ -132,6 +133,12 @@
 		sg = (struct scatterlist *) srb->request_buffer;
 		for (i = 0; i < srb->use_sg; i++) {
 
+			US_DEBUGP("transfer_amount: %d and total_transferred: %d\n", transfer_amount, total_transferred);
+
+			/* End this if we're done */
+			if (transfer_amount == total_transferred)
+				break;
+
 			/* transfer the lesser of the next buffer or the
 			 * remaining data */
 			if (transfer_amount - total_transferred >= 
@@ -139,10 +146,12 @@
 				result = usb_stor_transfer_partial(us,
 						sg[i].address, sg[i].length);
 				total_transferred += sg[i].length;
-			} else
+			} else {
 				result = usb_stor_transfer_partial(us,
 						sg[i].address,
 						transfer_amount - total_transferred);
+				total_transferred += transfer_amount - total_transferred;
+			}
 
 			/* if we get an error, end the loop here */
 			if (result)
@@ -158,7 +167,7 @@
 	srb->result = result;
 }
 
-
+#if 0
 /* Write a value to an ide register. */
 static int
 freecom_ide_write (struct us_data *us, int reg, int value)
@@ -197,7 +206,9 @@
 
         return USB_STOR_TRANSPORT_GOOD;
 }
+#endif
 
+#if 0 /* Unused at this time */
 /* Read a value from an ide register. */
 static int
 freecom_ide_read (struct us_data *us, int reg, int *value)
@@ -221,6 +232,8 @@
         else
                 reg = 0x0e;
 
+        US_DEBUGP("IDE in request for register 0x%02x\n", reg);
+
         idein->Type = FCM_PACKET_IDE_READ | reg;
         memset (idein->Pad, 0, sizeof (idein->Pad));
 
@@ -245,16 +258,18 @@
                 else
                         return USB_STOR_TRANSPORT_ERROR;
         }
+        US_DEBUGP("IDE in partial is %d\n", partial);
 
         if (desired_length == 1)
                 *value = buffer[0];
         else
                 *value = le16_to_cpu (*(__u16 *) buffer);
 
-        US_DEBUGP("IDE in  0x%02x -> 0x%02x\n", reg, *value);
+        US_DEBUGP("IDE in 0x%02x -> 0x%02x\n", reg, *value);
 
         return USB_STOR_TRANSPORT_GOOD;
 }
+#endif
 
 static int
 freecom_readdata (Scsi_Cmnd *srb, struct us_data *us,
@@ -364,13 +379,6 @@
         opipe = usb_sndbulkpipe (us->pusb_dev, us->ep_out);
         ipipe = usb_rcvbulkpipe (us->pusb_dev, us->ep_in);
 
-#if 0
-        /* Yuck, let's see if this helps us.  Artificially increase the
-         * length on this. */
-        if (srb->cmnd[0] == 0x03 && srb->cmnd[4] == 0x12)
-                srb->cmnd[4] = 0x0E;
-#endif
-
         /* The ATAPI Command always goes out first. */
         fcb->Type = FCM_PACKET_ATAPI | 0x00;
         fcb->Timeout = 0;
@@ -412,17 +420,25 @@
 
         US_DEBUG(pdump ((void *) fst, partial));
 
-	/* while we haven't received the IRQ */
-	while (!(fst->Status & 0x2)) {
-		/* send a command to re-fetch the status */
-		US_DEBUGP("Re-attempting to get status...\n");
+	/* The firmware will time-out commands after 20 seconds. Some commands
+	 * can legitimately take longer than this, so we use a different
+	 * command that only waits for the interrupt and then sends status,
+	 * without having to send a new ATAPI command to the device. 
+	 *
+	 * NOTE: There is some indication that a data transfer after a timeout
+	 * may not work, but that is a condition that should never happen.
+	 */
+	while (fst->Status & FCM_STATUS_BUSY) {
+		US_DEBUGP("20 second USB/ATAPI bridge TIMEOUT occured!\n");
+		US_DEBUGP("fst->Status is %x\n", fst->Status);
 
+		/* Get the status again */
 		fcb->Type = FCM_PACKET_STATUS;
 		fcb->Timeout = 0;
-		memset (fcb->Atapi, 0, 12);
+		memset (fcb->Atapi, 0, sizeof(fcb->Filler));
 		memset (fcb->Filler, 0, sizeof (fcb->Filler));
 
-		/* Send it out. */
+        	/* Send it out. */
 		result = usb_stor_bulk_msg (us, fcb, opipe,
 				FCM_PACKET_LENGTH, &partial);
 
@@ -443,10 +459,12 @@
 			return USB_STOR_TRANSPORT_ERROR;
 		}
 
-		/* actually get the status info */
-		result = usb_stor_bulk_msg (us, fst, ipipe,
+		/* get the data */
+        	result = usb_stor_bulk_msg (us, fst, ipipe,
 				FCM_PACKET_LENGTH, &partial);
+
 		US_DEBUGP("bar Status result %d %d\n", result, partial);
+
 		/* -ENOENT -- we canceled this transfer */
 		if (result == -ENOENT) {
 			US_DEBUGP("freecom_transport(): transfer aborted\n");
@@ -470,13 +488,15 @@
         US_DEBUGP("Device indicates that it has %d bytes available\n",
                         le16_to_cpu (fst->Count));
 
-        /* Find the length we desire to read.  It is the lesser of the SCSI
-         * layer's requested length, and the length the device claims to
-         * have available. */
+        /* Find the length we desire to read. */
         length = usb_stor_transfer_length (srb);
         US_DEBUGP("SCSI requested %d\n", length);
-        if (length > le16_to_cpu (fst->Count))
-                length = le16_to_cpu (fst->Count);
+
+	/* verify that this amount is legal */
+	if (length > srb->request_bufflen) {
+		length = srb->request_bufflen;
+		US_DEBUGP("Truncating request to match buffer length: %d\n", length);
+	}
 
         /* What we do now depends on what direction the data is supposed to
          * move in. */
@@ -522,7 +542,6 @@
                 if (result != USB_STOR_TRANSPORT_GOOD)
                         return result;
 
-#if 1
                 US_DEBUGP("FCM: Waiting for status\n");
                 result = usb_stor_bulk_msg (us, fst, ipipe,
                                 FCM_PACKET_LENGTH, &partial);
@@ -540,7 +559,7 @@
                         US_DEBUGP("Drive seems still hungry\n");
                         return USB_STOR_TRANSPORT_FAILED;
                 }
-#endif
+
                 US_DEBUGP("Transfer happy\n");
                 break;
 
@@ -570,8 +589,7 @@
 int
 freecom_init (struct us_data *us)
 {
-        int result, value;
-        int counter;
+        int result;
 	char buffer[33];
 
         /* Allocate a buffer for us.  The upper usb transport code will
@@ -591,42 +609,29 @@
 	buffer[32] = '\0';
 	US_DEBUGP("String returned from FC init is: %s\n", buffer);
 
-        result = freecom_ide_write (us, 0x06, 0xA0);
-        if (result != USB_STOR_TRANSPORT_GOOD)
-                return result;
-        result = freecom_ide_write (us, 0x01, 0x00);
-        if (result != USB_STOR_TRANSPORT_GOOD)
-                return result;
-
-        counter = 50;
-        do {
-                result = freecom_ide_read (us, 0x07, &value);
-                if (result != USB_STOR_TRANSPORT_GOOD)
-                        return result;
-                if (counter-- < 0) {
-                        US_DEBUGP("Timeout in freecom");
-                        return USB_STOR_TRANSPORT_ERROR;
-                }
-        } while ((value & 0x80) != 0);
+	/* Special thanks to the people at Freecom for providing me with
+	 * this "magic sequence", which they use in their Windows and MacOS
+	 * drivers to make sure that all the attached perhiperals are
+	 * properly reset.
+	 */
 
-        result = freecom_ide_write (us, 0x07, 0x08);
-        if (result != USB_STOR_TRANSPORT_GOOD)
-                return result;
-
-        counter = 50;
-        do {
-                result = freecom_ide_read (us, 0x07, &value);
-                if (result != USB_STOR_TRANSPORT_GOOD)
-                        return result;
-                if (counter-- < 0) {
-                        US_DEBUGP("Timeout in freecom");
-                        return USB_STOR_TRANSPORT_ERROR;
-                }
-        } while ((value & 0x80) != 0);
+	/* send reset */
+	result = usb_control_msg(us->pusb_dev,
+			usb_sndctrlpipe(us->pusb_dev, 0),
+			0x4d, 0x40, 0x24d8, 0x0, NULL, 0x0, 3*HZ);
+	US_DEBUGP("result from activate reset is %d\n", result);
+
+	/* wait 250ms */
+	mdelay(250);
+
+	/* clear reset */
+	result = usb_control_msg(us->pusb_dev,
+			usb_sndctrlpipe(us->pusb_dev, 0),
+			0x4d, 0x40, 0x24f8, 0x0, NULL, 0x0, 3*HZ);
+	US_DEBUGP("result from clear reset is %d\n", result);
 
-        result = freecom_ide_write (us, 0x08, 0x08);
-        if (result != USB_STOR_TRANSPORT_GOOD)
-                return result;
+	/* wait 3 seconds */
+	mdelay(3 * 1000);
 
         return USB_STOR_TRANSPORT_GOOD;
 }

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