patch-2.4.0-test9 linux/drivers/usb/storage/freecom.c
Next file: linux/drivers/usb/storage/freecom.h
Previous file: linux/drivers/usb/storage/debug.h
Back to the patch index
Back to the overall index
- Lines: 614
- Date:
Mon Oct 2 11:54:51 2000
- Orig file:
v2.4.0-test8/linux/drivers/usb/storage/freecom.c
- Orig date:
Tue Sep 5 12:56:51 2000
diff -u --recursive --new-file v2.4.0-test8/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.7 2000/08/25 00:13:51 mdharm Exp $
+ * $Id: freecom.c,v 1.12 2000/09/22 01:16:17 mdharm Exp $
*
* Freecom v0.1:
*
@@ -33,6 +33,7 @@
#include "usb.h"
#include "debug.h"
#include "freecom.h"
+#include "linux/hdreg.h"
static void pdump (void *, int);
@@ -54,6 +55,18 @@
__u8 Timeout; /* Timeout in seconds. */
__u32 Count; /* Number of bytes to transfer. */
__u8 Pad[58];
+} __attribute__ ((packed));
+
+struct freecom_ide_out {
+ __u8 Type; /* Type + IDE register. */
+ __u8 Pad;
+ __u16 Value; /* Value to write. */
+ __u8 Pad2[60];
+};
+
+struct freecom_ide_in {
+ __u8 Type; /* Type | IDE register. */
+ __u8 Pad[63];
};
struct freecom_status {
@@ -63,17 +76,182 @@
__u8 Pad[60];
};
+/* Freecom stuffs the interrupt status in the INDEX_STAT bit of the ide
+ * register. */
+#define FCM_INT_STATUS INDEX_STAT
+
/* 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
/* Receive data from the IDE interface. The ATAPI packet has already
* waited, so the data should be immediately available. */
-#define FCM_PACKET_INPUT 0x90
+#define FCM_PACKET_INPUT 0x81
+
+/* Send data to the IDE interface. */
+#define FCM_PACKET_OUTPUT 0x01
+
+/* Write a value to an ide register. Or the ide register to write after
+ * munging the addres a bit. */
+#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
+/*
+ * Transfer an entire SCSI command's worth of data payload over the bulk
+ * pipe.
+ *
+ * Note that this uses us_transfer_partial to achieve 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_freecom(Scsi_Cmnd *srb, struct us_data* us, int transfer_amount)
+{
+ int i;
+ int result = -1;
+ struct scatterlist *sg;
+ unsigned int total_transferred = 0;
+
+ /* was someone foolish enough to request more data than available
+ * buffer space? */
+ if (transfer_amount > srb->request_bufflen)
+ transfer_amount = srb->request_bufflen;
+
+ /* are we scatter-gathering? */
+ if (srb->use_sg) {
+
+ /* 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++) {
+
+ /* transfer the lesser of the next buffer or the
+ * remaining data */
+ if (transfer_amount - total_transferred >=
+ sg[i].length) {
+ result = us_transfer_partial(us, sg[i].address,
+ sg[i].length);
+ total_transferred += sg[i].length;
+ } else
+ result = us_transfer_partial(us, sg[i].address,
+ transfer_amount - total_transferred);
+
+ /* if we get an error, end the loop here */
+ if (result)
+ break;
+ }
+ }
+ else
+ /* no scatter-gather, just make the request */
+ result = us_transfer_partial(us, srb->request_buffer,
+ transfer_amount);
+
+ /* return the result in the data structure itself */
+ srb->result = result;
+}
+
+
+/* Write a value to an ide register. */
+static int
+freecom_ide_write (struct us_data *us, int reg, int value)
+{
+ freecom_udata_t extra = (freecom_udata_t) us->extra;
+ struct freecom_ide_out *ideout =
+ (struct freecom_ide_out *) extra->buffer;
+ int opipe;
+ int result, partial;
+
+ printk (KERN_DEBUG "IDE out 0x%02x <- 0x%02x\n", reg, value);
+
+ /* Get handles for both transports. */
+ opipe = usb_sndbulkpipe (us->pusb_dev, us->ep_out);
+
+ if (reg < 0 || reg > 8)
+ return USB_STOR_TRANSPORT_ERROR;
+ if (reg < 8)
+ reg |= 0x20;
+ else
+ reg = 0x0e;
+
+ ideout->Type = FCM_PACKET_IDE_WRITE | reg;
+ ideout->Pad = 0;
+ ideout->Value = cpu_to_le16 (value);
+ memset (ideout->Pad2, 0, sizeof (ideout->Pad2));
+
+ result = usb_stor_bulk_msg (us, ideout, opipe,
+ FCM_PACKET_LENGTH, &partial);
+ if (result != 0) {
+ if (result == -ENOENT)
+ return US_BULK_TRANSFER_ABORTED;
+ else
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
+/* Read a value from an ide register. */
+static int
+freecom_ide_read (struct us_data *us, int reg, int *value)
+{
+ freecom_udata_t extra = (freecom_udata_t) us->extra;
+ struct freecom_ide_in *idein =
+ (struct freecom_ide_in *) extra->buffer;
+ __u8 *buffer = extra->buffer;
+ int ipipe, opipe;
+ int result, partial;
+ int desired_length;
+
+ /* Get handles for both transports. */
+ opipe = usb_sndbulkpipe (us->pusb_dev, us->ep_out);
+ ipipe = usb_rcvbulkpipe (us->pusb_dev, us->ep_in);
+
+ if (reg < 0 || reg > 8)
+ return USB_STOR_TRANSPORT_ERROR;
+ if (reg < 8)
+ reg |= 0x10;
+ else
+ reg = 0x0e;
+
+ idein->Type = FCM_PACKET_IDE_READ | reg;
+ memset (idein->Pad, 0, sizeof (idein->Pad));
+
+ result = usb_stor_bulk_msg (us, idein, opipe,
+ FCM_PACKET_LENGTH, &partial);
+ if (result != 0) {
+ if (result == -ENOENT)
+ return US_BULK_TRANSFER_ABORTED;
+ else
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ desired_length = 1;
+ if (reg == 0x10)
+ desired_length = 2;
+
+ result = usb_stor_bulk_msg (us, buffer, ipipe,
+ desired_length, &partial);
+ if (result != 0) {
+ if (result == -ENOENT)
+ return US_BULK_TRANSFER_ABORTED;
+ else
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ if (desired_length == 1)
+ *value = buffer[0];
+ else
+ *value = le16_to_cpu (*(__u16 *) buffer);
+
+ printk (KERN_DEBUG "IDE in 0x%02x -> 0x%02x\n", reg, *value);
+
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
static int
freecom_readdata (Scsi_Cmnd *srb, struct us_data *us,
int ipipe, int opipe, int count)
@@ -83,6 +261,7 @@
(struct freecom_xfer_wrap *) extra->buffer;
int result, partial;
int offset;
+ int this_read;
__u8 *buffer = extra->buffer;
fxfr->Type = FCM_PACKET_INPUT | 0x00;
@@ -111,6 +290,9 @@
result, partial);
/* Now transfer all of our blocks. */
+ printk (KERN_DEBUG "Start of read\n");
+ us_transfer_freecom(srb, us, count);
+#if 0
if (srb->use_sg) {
US_DEBUGP ("Need to implement scatter-gather\n");
return USB_STOR_TRANSPORT_ERROR;
@@ -118,6 +300,14 @@
offset = 0;
while (offset < count) {
+#if 0
+ this_read = count - offset;
+ if (this_read > 64)
+ this_read = 64;
+#else
+ this_read = 64;
+#endif
+
printk (KERN_DEBUG "Start of read\n");
/* Use the given buffer directly, but only if there
* is space for an entire packet. */
@@ -125,7 +315,7 @@
if (offset + 64 <= srb->request_bufflen) {
result = usb_stor_bulk_msg (
us, srb->request_buffer+offset,
- ipipe, 64, &partial);
+ ipipe, this_read, &partial);
printk (KERN_DEBUG "Read111 = %d, %d\n",
result, partial);
pdump (srb->request_buffer+offset,
@@ -133,7 +323,7 @@
} else {
result = usb_stor_bulk_msg (
us, buffer,
- ipipe, 64, &partial);
+ ipipe, this_read, &partial);
printk (KERN_DEBUG "Read112 = %d, %d\n",
result, partial);
memcpy (srb->request_buffer+offset,
@@ -156,14 +346,118 @@
return USB_STOR_TRANSPORT_ERROR;
}
- offset += 64;
+ offset += this_read;
}
}
+#endif
printk (KERN_DEBUG "freecom_readdata done!\n");
return USB_STOR_TRANSPORT_GOOD;
}
+static int
+freecom_writedata (Scsi_Cmnd *srb, struct us_data *us,
+ int ipipe, int opipe, int count)
+{
+ freecom_udata_t extra = (freecom_udata_t) us->extra;
+ struct freecom_xfer_wrap *fxfr =
+ (struct freecom_xfer_wrap *) extra->buffer;
+ int result, partial;
+ int offset;
+ int this_write;
+ __u8 *buffer = extra->buffer;
+
+ fxfr->Type = FCM_PACKET_OUTPUT | 0x00;
+ fxfr->Timeout = 0; /* Short timeout for debugging. */
+ fxfr->Count = cpu_to_le32 (count);
+ memset (fxfr->Pad, 0, sizeof (fxfr->Pad));
+
+ printk (KERN_DEBUG "Write data Freecom! (c=%d)\n", count);
+
+ /* Issue the transfer command. */
+ result = usb_stor_bulk_msg (us, fxfr, opipe,
+ FCM_PACKET_LENGTH, &partial);
+ if (result != 0) {
+ US_DEBUGP ("Freecom writedata xpot failure: r=%d, p=%d\n",
+ result, partial);
+
+ /* -ENOENT -- we canceled this transfer */
+ if (result == -ENOENT) {
+ US_DEBUGP("us_transfer_partial(): transfer aborted\n");
+ return US_BULK_TRANSFER_ABORTED;
+ }
+
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ printk (KERN_DEBUG "Done issuing write request: %d %d\n",
+ result, partial);
+
+ /* Now transfer all of our blocks. */
+ printk (KERN_DEBUG "Start of write\n");
+ us_transfer_freecom(srb, us, count);
+#if 0
+ if (srb->use_sg) {
+ US_DEBUGP ("Need to implement scatter-gather\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ } else {
+ offset = 0;
+
+ while (offset < count) {
+#if 1
+ this_write = count - offset;
+ if (this_write > 64)
+ this_write = 64;
+#else
+ this_write = 64;
+#endif
+
+ printk (KERN_DEBUG "Start of write\n");
+ /* Use the given buffer directly, but only if there
+ * is space for an entire packet. */
+
+ if (offset + 64 <= srb->request_bufflen) {
+ result = usb_stor_bulk_msg (
+ us, srb->request_buffer+offset,
+ opipe, this_write, &partial);
+ printk (KERN_DEBUG "Write111 = %d, %d\n",
+ result, partial);
+ pdump (srb->request_buffer+offset,
+ partial);
+ } else {
+ result = usb_stor_bulk_msg (
+ us, buffer,
+ opipe, this_write, &partial);
+ printk (KERN_DEBUG "Write112 = %d, %d\n",
+ result, partial);
+ memcpy (buffer,
+ srb->request_buffer+offset,
+ srb->request_bufflen - offset);
+ pdump (srb->request_buffer+offset,
+ srb->request_bufflen - offset);
+ }
+
+ if (result != 0) {
+ US_DEBUGP ("Freecom writeblock r=%d, p=%d\n",
+ result, partial);
+
+ /* -ENOENT -- we canceled this transfer */
+ if (result == -ENOENT) {
+ US_DEBUGP("us_transfer_partial(): transfer aborted\n");
+ return US_BULK_TRANSFER_ABORTED;
+ }
+
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ offset += this_write;
+ }
+ }
+#endif
+
+ printk (KERN_DEBUG "freecom_writedata done!\n");
+ return USB_STOR_TRANSPORT_GOOD;
+}
+
/*
* Transport for the Freecom USB/IDE adaptor.
*
@@ -178,17 +472,6 @@
int length;
freecom_udata_t extra;
- /* Allocate a buffer for us. The upper usb transport code will
- * free this for us when cleaning up. */
- if (us->extra == NULL) {
- us->extra = kmalloc (sizeof (struct freecom_udata),
- GFP_KERNEL);
- if (us->extra == NULL) {
- printk (KERN_WARNING USB_STORAGE "Out of memory\n");
- return USB_STOR_TRANSPORT_ERROR;
- }
- }
-
extra = (freecom_udata_t) us->extra;
fcb = (struct freecom_cb_wrap *) extra->buffer;
@@ -208,7 +491,7 @@
#endif
/* The ATAPI Command always goes out first. */
- fcb->Type = FCM_PACKET_ATAPI;
+ fcb->Type = FCM_PACKET_ATAPI | 0x00;
fcb->Timeout = 0;
memcpy (fcb->Atapi, srb->cmnd, 12);
memset (fcb->Filler, 0, sizeof (fcb->Filler));
@@ -247,10 +530,54 @@
}
pdump ((void *) fst, partial);
+
+ /* while we haven't recieved the IRQ */
+ while (!(fst->Status & 0x2)) {
+ /* send a command to re-fetch the status */
+ US_DEBUGP("Re-attempting to get status...\n");
+
+ fcb->Type = FCM_PACKET_STATUS;
+ fcb->Timeout = 0;
+ memset (fcb->Atapi, 0, 12);
+ memset (fcb->Filler, 0, sizeof (fcb->Filler));
+
+ /* Send it out. */
+ result = usb_stor_bulk_msg (us, fcb, opipe,
+ FCM_PACKET_LENGTH, &partial);
+
+ /* The Freecom device will only fail if there is something wrong in
+ * USB land. It returns the status in its own registers, which
+ * come back in the bulk pipe. */
+ if (result != 0) {
+ US_DEBUGP ("freecom xport failure: r=%d, p=%d\n",
+ result, partial);
+
+ /* -ENOENT -- we canceled this transfer */
+ if (result == -ENOENT) {
+ US_DEBUGP("us_transfer_partial(): transfer aborted\n");
+ return US_BULK_TRANSFER_ABORTED;
+ }
+
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+
+ /* actually get the status info */
+ result = usb_stor_bulk_msg (us, fst, ipipe,
+ FCM_PACKET_LENGTH, &partial);
+ printk (KERN_DEBUG "bar Status result %d %d\n", result, partial);
+ /* -ENOENT -- we canceled this transfer */
+ if (result == -ENOENT) {
+ US_DEBUGP("us_transfer_partial(): transfer aborted\n");
+ return US_BULK_TRANSFER_ABORTED;
+ }
+
+ pdump ((void *) fst, partial);
+ }
+
if (partial != 4 || result != 0) {
return USB_STOR_TRANSPORT_ERROR;
}
- if ((fst->Reason & 1) != 0) {
+ if ((fst->Status & 1) != 0) {
printk (KERN_DEBUG "operation failed\n");
return USB_STOR_TRANSPORT_FAILED;
}
@@ -274,9 +601,70 @@
switch (us->srb->sc_data_direction) {
case SCSI_DATA_READ:
+ /* Make sure that the status indicates that the device
+ * wants data as well. */
+ if ((fst->Status & DRQ_STAT) == 0 || (fst->Reason & 3) != 2) {
+ printk (KERN_DEBUG "SCSI wants data, drive doesn't have any\n");
+ return USB_STOR_TRANSPORT_FAILED;
+ }
result = freecom_readdata (srb, us, ipipe, opipe, length);
if (result != USB_STOR_TRANSPORT_GOOD)
return result;
+
+ printk (KERN_DEBUG "FCM: Waiting for status\n");
+ result = usb_stor_bulk_msg (us, fst, ipipe,
+ FCM_PACKET_LENGTH, &partial);
+ pdump ((void *) fst, partial);
+ if (result == -ENOENT) {
+ US_DEBUGP ("freecom_transport: transfer aborted\n");
+ return US_BULK_TRANSFER_ABORTED;
+ }
+ if (partial != 4 || result != 0)
+ return USB_STOR_TRANSPORT_ERROR;
+ if ((fst->Status & ERR_STAT) != 0) {
+ printk (KERN_DEBUG "operation failed\n");
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+ if ((fst->Reason & 3) != 3) {
+ printk (KERN_DEBUG "Drive seems still hungry\n");
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+ printk (KERN_DEBUG "Transfer happy\n");
+ break;
+
+ case SCSI_DATA_WRITE:
+ /* Make sure the status indicates that the device wants to
+ * send us data. */
+ /* !!IMPLEMENT!! */
+ result = freecom_writedata (srb, us, ipipe, opipe, length);
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+#if 1
+ printk (KERN_DEBUG "FCM: Waiting for status\n");
+ result = usb_stor_bulk_msg (us, fst, ipipe,
+ FCM_PACKET_LENGTH, &partial);
+ if (result == -ENOENT) {
+ US_DEBUGP ("freecom_transport: transfer aborted\n");
+ return US_BULK_TRANSFER_ABORTED;
+ }
+ if (partial != 4 || result != 0)
+ return USB_STOR_TRANSPORT_ERROR;
+ if ((fst->Status & ERR_STAT) != 0) {
+ printk (KERN_DEBUG "operation failed\n");
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+ if ((fst->Reason & 3) != 3) {
+ printk (KERN_DEBUG "Drive seems still hungry\n");
+ return USB_STOR_TRANSPORT_FAILED;
+ }
+#endif
+ printk (KERN_DEBUG "Transfer happy\n");
+ break;
+
+
+ case SCSI_DATA_NONE:
+ /* Easy, do nothing. */
break;
default:
@@ -310,6 +698,69 @@
srb->sc_data_direction);
return USB_STOR_TRANSPORT_ERROR;
+}
+
+int
+freecom_init (struct us_data *us)
+{
+ int result, value;
+ int counter;
+ char buffer[33];
+
+ /* Allocate a buffer for us. The upper usb transport code will
+ * free this for us when cleaning up. */
+ if (us->extra == NULL) {
+ us->extra = kmalloc (sizeof (struct freecom_udata),
+ GFP_KERNEL);
+ if (us->extra == NULL) {
+ printk (KERN_WARNING USB_STORAGE "Out of memory\n");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ }
+
+ result = usb_stor_control_msg(us, usb_rcvctrlpipe(us->pusb_dev, 0),
+ 0x4c, 0xc0, 0x4346, 0x0, buffer, 0x20);
+ 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) {
+ printk (KERN_WARNING USB_STORAGE "Timeout in freecom");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ } while ((value & 0x80) != 0);
+
+ 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) {
+ printk (KERN_WARNING USB_STORAGE "Timeout in freecom");
+ return USB_STOR_TRANSPORT_ERROR;
+ }
+ } while ((value & 0x80) != 0);
+
+ result = freecom_ide_write (us, 0x08, 0x08);
+ if (result != USB_STOR_TRANSPORT_GOOD)
+ return result;
+
+ return USB_STOR_TRANSPORT_GOOD;
}
int usb_stor_freecom_reset(struct us_data *us)
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)