patch-1.3.96 linux/drivers/scsi/wd33c93.c
Next file: linux/drivers/scsi/wd33c93.h
Previous file: linux/drivers/scsi/scsi.c
Back to the patch index
Back to the overall index
- Lines: 1459
- Date:
Fri Apr 26 12:13:24 1996
- Orig file:
v1.3.95/linux/drivers/scsi/wd33c93.c
- Orig date:
Tue Apr 23 13:57:10 1996
diff -u --recursive --new-file v1.3.95/linux/drivers/scsi/wd33c93.c linux/drivers/scsi/wd33c93.c
@@ -28,18 +28,23 @@
*
* - Target Disconnection/Reconnection is now supported. Any
* system with more than one device active on the SCSI bus
- * will benefit from this.
+ * will benefit from this. The driver defaults to what I'm
+ * 'adaptive disconnect' - meaning that each command is
+ * evaluated individually as to whether or not it should
+ * be run with the option to disconnect/reslect (if the
+ * device chooses), or as a "SCSI-bus-hog".
*
- * - Synchronous data transfers are now supported. The driver
- * automatically uses this faster protocol with any device
- * able to handle it.
+ * - Synchronous data transfers are now supported. Because of
+ * a few devices that choke after telling the driver that
+ * they can do sync transfers, we don't automatically use
+ * this faster protocol - it can be enabled via the command-
+ * line on a device-by-device basis.
*
* - Runtime operating parameters can now be specified through
- * either the 'amiboot' or the LILO command line. Something
- * like:
- * "wd33c93=0x0000"
- * The value 0x0000 results in the defaults being used; bits
- * are defined in wd33c93.h.
+ * the 'amiboot' or the 'insmod' command line. For amiboot do:
+ * "amiboot [usual stuff] wd33c93=blah,blah,blah"
+ * The defaults should be good for most people. See the comment
+ * for 'setup_strings' below for more details.
*
* - The old driver relied exclusively on what the Western Digital
* docs call "Combination Level 2 Commands", which are a great
@@ -52,7 +57,7 @@
*
*
* TODO:
- * more speed. tagged queuing.
+ * more speed. linked commands.
*
*
* People with bug reports, wish-lists, complaints, comments,
@@ -67,18 +72,33 @@
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/delay.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= 0x010300
#include <linux/blk.h>
+#else
+#include "../block/blk.h"
+#endif
+
#include "scsi.h"
#include "hosts.h"
#include "wd33c93.h"
+#ifdef MODULE
+#include <linux/module.h>
+#endif
-#define SYNC_DEBUG
+/* Leave this undefined for now - need to make some changes in the
+ * a3000/a2019/gvp11 files to get it working right
+ */
+/*#define PROC_INTERFACE*/ /* add code for /proc/scsi/wd33c93/xxx interface */
-#define DEBUGGING_ON
+#define SYNC_DEBUG /* extra info on sync negotiation printed */
+#define DEBUGGING_ON /* enable command-line debugging bitmask */
+#define DEBUG_DEFAULTS 0 /* default debugging bitmask */
-#define WD33C93_VERSION "1.17"
-#define WD33C93_DATE "06/Feb/1996"
+#define WD33C93_VERSION "1.21"
+#define WD33C93_DATE "20/Apr/1996"
#ifdef DEBUGGING_ON
#define DB(f,a) if (hostdata->args & (f)) a;
@@ -92,15 +112,59 @@
/*
- * setup_default is a bunch of bits that define some of the operating
- * parameters and settings for this driver. It is used unless a LILO
- * or insmod command line has been specified, in which case setup_default
- * is _completely_ ignored. Take a look at the "defines for hostdata->args"
- * section in wd33c93.h - that stuff is what you'd use here if you want
- * to change the defaults.
+ * setup_strings is an array of strings that define some of the operating
+ * parameters and settings for this driver. It is used unless an amiboot
+ * or insmod command line has been specified, in which case those settings
+ * are combined with the ones here. The driver recognizes the following
+ * keywords (lower case required) and arguments:
+ *
+ * - nosync:bitmask -bitmask is a byte where the 1st 7 bits correspond with
+ * the 7 possible SCSI devices. Set a bit to prevent sync
+ * negotiation on that device. To maintain backwards
+ * compatability, a command-line such as "wd33c93=255" will
+ * be automatically translated to "wd33c93=nosync:0xff".
+ * - period:ns -ns is the minimum # of nanoseconds in a SCSI data transfer
+ * period. Default is 500; acceptable values are 250 - 1000.
+ * - disconnect:x -x = 0 to never allow disconnects, 2 to always allow them.
+ * x = 1 does 'adaptive' disconnects, which is the default
+ * and generally the best choice.
+ * - debug:x -If 'DEBUGGING_ON' is defined, x is a bit mask that causes
+ * various types of debug output to printed - see the DB_xxx
+ * defines in wd33c93.h
+ * - clock:x -x = clock input in MHz for WD33c93 chip. Normal values
+ * would be from 8 through 20. Default is 8.
+ * - next -No argument. Used to separate blocks of keywords when
+ * there's more than one host adapter in the system.
+ *
+ * Syntax Notes:
+ * - Numeric arguments can be decimal or the '0x' form of hex notation. There
+ * _must_ be a colon between a keyword and its numeric argument, with no
+ * spaces.
+ * - Keywords are separated by commas, no spaces, in the standard kernel
+ * command-line manner, except in the case of 'setup_strings[]' (see
+ * below), which is simply a C array of pointers to char. Each element
+ * in the array is a string comprising one keyword & argument.
+ * - A keyword in the 'nth' comma-separated command-line member will overwrite
+ * the 'nth' element of setup_strings[]. A blank command-line member (in
+ * other words, a comma with no preceding keyword) will _not_ overwrite
+ * the corresponding setup_strings[] element.
+ * - If a keyword is used more than once, the first one applies to the first
+ * SCSI host found, the second to the second card, etc, unless the 'next'
+ * keyword is used to change the order.
+ *
+ * Some amiboot examples (for insmod, use 'setup_strings' instead of 'wd33c93'):
+ * - wd33c93=nosync:255
+ * - wd33c93=disconnect:2,nosync:0x08,period:250
+ * - wd33c93=debug:0x1c
*/
-static unsigned int setup_default = 0;
+static char *setup_strings[] =
+ {"","","","","","","","","","","",""};
+
+#ifdef PROC_INTERFACE
+unsigned long disc_allowed_total;
+unsigned long disc_taken_total;
+#endif
inline uchar read_wd33c93(wd33c93_regs *regp,uchar reg_num)
@@ -176,27 +240,32 @@
{1000,0x00},
{0, 0} };
-uchar calc_sync_xfer(unsigned int period, unsigned int offset)
+int round_period(unsigned int period)
{
-uchar result;
int x;
- period *= 4; /* convert SDTR code to ns */
- result = 0x00;
for (x=1; sx_table[x].period_ns; x++) {
if ((period <= sx_table[x-0].period_ns) &&
(period > sx_table[x-1].period_ns)) {
- result = sx_table[x].reg_value;
- break;
+ return x;
}
}
+ return 7;
+}
+
+uchar calc_sync_xfer(unsigned int period, unsigned int offset)
+{
+uchar result;
+
+ period *= 4; /* convert SDTR code to ns */
+ result = sx_table[round_period(period)].reg_value;
result |= (offset < OPTIMUM_SX_OFF)?offset:OPTIMUM_SX_OFF;
return result;
}
-static void wd33c93_execute(struct Scsi_Host *instance);
+void wd33c93_execute(struct Scsi_Host *instance);
int wd33c93_queuecommand (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
{
@@ -204,13 +273,14 @@
Scsi_Cmnd *tmp;
unsigned long flags;
+
+ save_flags(flags);
+ cli();
hostdata = (struct WD33C93_hostdata *)cmd->host->hostdata;
DB(DB_QUEUE_COMMAND,printk("Q-%d-%02x-%ld( ",cmd->target,cmd->cmnd[0],cmd->pid))
-
-/*
- * Set up a few fields in the Scsi_Cmnd structure for our own use:
+/* Set up a few fields in the Scsi_Cmnd structure for our own use:
* - host_scribble is the pointer to the next cmd in the input queue
* - scsi_done points to the routine we call when a cmd is finished
* - result is what you'd expect
@@ -230,6 +300,9 @@
* - SCp.this_residual is the size of that buffer
* - SCp.buffer points to the current scatter-gather buffer
* - SCp.buffers_residual tells us how many S.G. buffers there are
+ * - SCp.have_data_in is not used
+ * - SCp.sent_command is not used
+ * - SCp.phase records this command's SRCID_ER bit setting
*/
if (cmd->use_sg) {
@@ -255,19 +328,16 @@
* sense data is not lost before REQUEST_SENSE executes.
*/
- save_flags(flags);
- cli();
if (!(hostdata->input_Q) || (cmd->cmnd[0] == REQUEST_SENSE)) {
cmd->host_scribble = (uchar *)hostdata->input_Q;
hostdata->input_Q = cmd;
}
- else {
+ else { /* find the end of the queue */
for (tmp=(Scsi_Cmnd *)hostdata->input_Q; tmp->host_scribble;
tmp=(Scsi_Cmnd *)tmp->host_scribble)
;
tmp->host_scribble = (uchar *)cmd;
}
- restore_flags(flags);
/* We know that there's at least one command in 'input_Q' now.
* Go see if any of them are runnable!
@@ -275,7 +345,9 @@
wd33c93_execute(cmd->host);
-DB(DB_QUEUE_COMMAND,printk(")Q-%d-%02x-%ld ",cmd->target,cmd->cmnd[0],cmd->pid))
+DB(DB_QUEUE_COMMAND,printk(")Q-%ld ",cmd->pid))
+
+ restore_flags(flags);
return 0;
}
@@ -287,7 +359,7 @@
* the input_Q, using the first command we find that's intended
* for a currently non-busy target/lun.
*/
-static void wd33c93_execute (struct Scsi_Host *instance)
+void wd33c93_execute (struct Scsi_Host *instance)
{
struct WD33C93_hostdata *hostdata;
wd33c93_regs *regp;
@@ -301,10 +373,12 @@
hostdata = (struct WD33C93_hostdata *)instance->hostdata;
regp = hostdata->regp;
-DB(DB_EXECUTE,printk("EX( "))
+DB(DB_EXECUTE,printk("EX("))
if (hostdata->selecting || hostdata->connected) {
+
DB(DB_EXECUTE,printk(")EX-0 "))
+
restore_flags(flags);
return;
}
@@ -326,18 +400,19 @@
/* quit if queue empty or all possible targets are busy */
if (!cmd) {
+
DB(DB_EXECUTE,printk(")EX-1 "))
+
restore_flags(flags);
return;
}
- /* remove command from queue, put it in selecting */
+ /* remove command from queue */
if (prev)
prev->host_scribble = cmd->host_scribble;
else
hostdata->input_Q = (Scsi_Cmnd *)cmd->host_scribble;
- hostdata->selecting = cmd;
/*
* Start the selection process
@@ -348,22 +423,78 @@
else
write_wd33c93(regp, WD_DESTINATION_ID, cmd->target | DSTID_DPD);
+/* Now we need to figure out whether or not this command is a good
+ * candidate for disconnect/reselect. We guess to the best of our
+ * ability, based on a set of hierarchical rules. When several
+ * devices are operating simultaneously, disconnects are usually
+ * an advantage. In a single device system, or if only 1 device
+ * is being accessed, transfers usually go faster if disconnects
+ * are not allowed:
+ *
+ * + Commands should NEVER disconnect if hostdata->disconnect =
+ * DIS_NEVER (this holds for tape drives also), and ALWAYS
+ * disconnect if hostdata->disconnect = DIS_ALWAYS.
+ * + Tape drive commands should always be allowed to disconnect.
+ * + Disconnect should be allowed if disconnected_Q isn't empty.
+ * + Commands should NOT disconnect if input_Q is empty.
+ * + Disconnect should be allowed if there are commands in input_Q
+ * for a different target/lun. In this case, the other commands
+ * should be made disconnect-able, if not already.
+ *
+ * I know, I know - this code would flunk me out of any
+ * "C Programming 101" class ever offered. But it's easy
+ * to change around and experiment with for now.
+ */
+
+ cmd->SCp.phase = 0; /* assume no disconnect */
+ if (hostdata->disconnect == DIS_NEVER)
+ goto no;
+ if (hostdata->disconnect == DIS_ALWAYS)
+ goto yes;
+ if (cmd->device->type == 1) /* tape drive? */
+ goto yes;
+ if (hostdata->disconnected_Q) /* other commands disconnected? */
+ goto yes;
+ if (!(hostdata->input_Q)) /* input_Q empty? */
+ goto no;
+ for (prev=(Scsi_Cmnd *)hostdata->input_Q; prev;
+ prev=(Scsi_Cmnd *)prev->host_scribble) {
+ if ((prev->target != cmd->target) || (prev->lun != cmd->lun)) {
+ for (prev=(Scsi_Cmnd *)hostdata->input_Q; prev;
+ prev=(Scsi_Cmnd *)prev->host_scribble)
+ prev->SCp.phase = 1;
+ goto yes;
+ }
+ }
+ goto no;
+
+yes:
+ cmd->SCp.phase = 1;
+
+#ifdef PROC_INTERFACE
+ disc_allowed_total++;
+#endif
+
+no:
+ write_wd33c93(regp, WD_SOURCE_ID, ((cmd->SCp.phase)?SRCID_ER:0));
+
write_wd33c93(regp, WD_TARGET_LUN, cmd->lun);
- write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
- write_wd33c93_count(regp, 0); /* this guarantees a DATA_PHASE interrupt */
+ write_wd33c93(regp,WD_SYNCHRONOUS_TRANSFER,hostdata->sync_xfer[cmd->target]);
hostdata->busy[cmd->target] |= (1 << cmd->lun);
if ((hostdata->level2 == L2_NONE) ||
(hostdata->sync_stat[cmd->target] == SS_UNSET)) {
/*
- * Now do a 'Select-With-ATN' command. This will end with
+ * Do a 'Select-With-ATN' command. This will end with
* one of the following interrupts:
* CSR_RESEL_AM: failure - can try again later.
* CSR_TIMEOUT: failure - give up.
* CSR_SELECT: success - proceed.
*/
+ hostdata->selecting = cmd;
+
/* Every target has its own synchronous transfer setting, kept in the
* sync_xfer array, and a corresponding status byte in sync_stat[].
* Each target's sync_stat[] entry is initialized to SX_UNSET, and its
@@ -376,19 +507,20 @@
* make the defaults final.
*/
if (hostdata->sync_stat[cmd->target] == SS_UNSET) {
- if (hostdata->args & (1 << cmd->target))
+ if (hostdata->no_sync & (1 << cmd->target))
hostdata->sync_stat[cmd->target] = SS_SET;
else
hostdata->sync_stat[cmd->target] = SS_FIRST;
}
hostdata->state = S_SELECTING;
+ write_wd33c93_count(regp,0); /* guarantee a DATA_PHASE interrupt */
write_wd33c93_cmd(regp, WD_CMD_SEL_ATN);
}
else {
/*
- * Now do a 'Select-With-ATN-Xfer' command. This will end with
+ * Do a 'Select-With-ATN-Xfer' command. This will end with
* one of the following interrupts:
* CSR_RESEL_AM: failure - can try again later.
* CSR_TIMEOUT: failure - give up.
@@ -396,13 +528,15 @@
*/
hostdata->connected = cmd;
- hostdata->selecting = NULL;
write_wd33c93(regp, WD_COMMAND_PHASE, 0);
- /* copy command_descriptor_block into WD chip */
+ /* copy command_descriptor_block into WD chip
+ * (take advantage of auto-incrementing)
+ */
+ regp->SASR = WD_CDB_1;
for (i=0; i<cmd->cmd_len; i++)
- write_wd33c93(regp, WD_CDB_1+i, cmd->cmnd[i]);
+ regp->SCMD = cmd->cmnd[i];
/* The wd33c93 only knows about Group 0, 1, and 5 commands when
* it's doing a 'select-and-transfer'. To be safe, we write the
@@ -412,6 +546,23 @@
write_wd33c93(regp, WD_OWN_ID, cmd->cmd_len);
+ /* When doing a non-disconnect command, we can save ourselves a DATA
+ * phase interrupt later by setting everything up now.
+ */
+
+ if (cmd->SCp.phase == 0) {
+ if (hostdata->dma_setup(cmd,
+ (IS_DIR_OUT(cmd))?DATA_OUT_DIR:DATA_IN_DIR))
+ write_wd33c93_count(regp,0); /* guarantee a DATA_PHASE interrupt */
+ else {
+ write_wd33c93_count(regp, cmd->SCp.this_residual);
+ write_wd33c93(regp,WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_DMA);
+ hostdata->dma = D_DMA_RUNNING;
+ }
+ }
+ else
+ write_wd33c93_count(regp,0); /* guarantee a DATA_PHASE interrupt */
+
hostdata->state = S_RUNNING_LEVEL2;
write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER);
}
@@ -423,47 +574,38 @@
* to search the input_Q again...
*/
-DB(DB_EXECUTE,printk(")EX-2 "))
+DB(DB_EXECUTE,printk("%s%ld)EX-2 ",(cmd->SCp.phase)?"d:":"",cmd->pid))
+
restore_flags(flags);
}
-void transfer_pio(wd33c93_regs *regp, uchar **buf, int *cnt,
+void transfer_pio(wd33c93_regs *regp, uchar *buf, int cnt,
int data_in_dir, struct WD33C93_hostdata *hostdata)
{
-uchar *b, asr;
-int c;
+uchar asr;
+
+DB(DB_TRANSFER,printk("(%p,%d,%s)",buf,cnt,data_in_dir?"in":"out"))
write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
- b = *buf;
- c = *cnt;
-DB(DB_TRANSFER_DATA,printk("[[%p/%d]]",b,c))
- write_wd33c93_count(regp,c);
+ write_wd33c93_count(regp,cnt);
write_wd33c93_cmd(regp, WD_CMD_TRANS_INFO);
if (data_in_dir) {
do {
asr = READ_AUX_STAT();
if (asr & ASR_DBR)
- *b++ = read_wd33c93(regp, WD_DATA);
+ *buf++ = read_wd33c93(regp, WD_DATA);
} while (!(asr & ASR_INT));
}
else {
do {
asr = READ_AUX_STAT();
if (asr & ASR_DBR)
- write_wd33c93(regp, WD_DATA, *b++);
+ write_wd33c93(regp, WD_DATA, *buf++);
} while (!(asr & ASR_INT));
}
-/* update original buffer pointer and original count */
-
- *cnt = read_wd33c93_count(regp);
- if (data_in_dir)
- *buf = b;
- else
- *buf += (c - *cnt);
-
/* Note: we are returning with the interrupt UN-cleared.
* Since (presumably) an entire I/O operation has
* completed, the bus phase is probably different, and
@@ -501,7 +643,7 @@
/* 'dma_setup()' will return TRUE if we can't do DMA. */
if (hostdata->dma_setup(cmd, data_in_dir)) {
- transfer_pio(regp, (uchar **)&cmd->SCp.ptr, &cmd->SCp.this_residual,
+ transfer_pio(regp, (uchar *)&cmd->SCp.ptr, cmd->SCp.this_residual,
data_in_dir, hostdata);
}
@@ -516,11 +658,10 @@
else {
write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_DMA);
-DB(DB_TRANSFER_DATA,printk("[%p/%d]",cmd->SCp.ptr,cmd->SCp.this_residual))
write_wd33c93_count(regp,cmd->SCp.this_residual);
- if (hostdata->level2 >= L2_DATA) {
- write_wd33c93(regp, WD_COMMAND_PHASE, 0x41);
+ if ((hostdata->level2 >= L2_DATA) || (cmd->SCp.phase == 0)) {
+ write_wd33c93(regp, WD_COMMAND_PHASE, 0x45);
write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER);
hostdata->state = S_RUNNING_LEVEL2;
}
@@ -538,9 +679,9 @@
struct WD33C93_hostdata *hostdata;
Scsi_Cmnd *patch, *cmd;
wd33c93_regs *regp;
+unsigned long flags;
uchar asr, sr, phs, id, lun, *ucp, msg;
unsigned long length;
-int i;
hostdata = (struct WD33C93_hostdata *)instance->hostdata;
@@ -550,6 +691,11 @@
if (!(asr & ASR_INT) || (asr & ASR_BSY))
return;
+/* OK - it should be safe to re-enable system interrupts */
+
+ save_flags(flags);
+ sti();
+
cmd = (Scsi_Cmnd *)hostdata->connected; /* assume we're connected */
sr = read_wd33c93(regp, WD_SCSI_STATUS); /* clear the interrupt */
phs = read_wd33c93(regp, WD_COMMAND_PHASE);
@@ -572,13 +718,13 @@
*/
if (hostdata->dma == D_DMA_RUNNING) {
-DB(DB_TRANSFER_DATA,printk("[%p/%d:",cmd->SCp.ptr,cmd->SCp.this_residual))
+DB(DB_TRANSFER,printk("[%p/%d:",cmd->SCp.ptr,cmd->SCp.this_residual))
hostdata->dma_stop(cmd->host, cmd, 1);
hostdata->dma = D_DMA_OFF;
length = cmd->SCp.this_residual;
cmd->SCp.this_residual = read_wd33c93_count(regp);
cmd->SCp.ptr += (length - cmd->SCp.this_residual);
-DB(DB_TRANSFER_DATA,printk("%p/%d]",cmd->SCp.ptr,cmd->SCp.this_residual))
+DB(DB_TRANSFER,printk("%p/%d]",cmd->SCp.ptr,cmd->SCp.this_residual))
}
/* Respond to the specific WD3393 interrupt - there are quite a few! */
@@ -586,26 +732,26 @@
switch (sr) {
case CSR_TIMEOUT:
- cli();
DB(DB_INTR,printk("TIMEOUT"))
- if (hostdata->state == S_RUNNING_LEVEL2) {
+
+ cli();
+ if (hostdata->state == S_RUNNING_LEVEL2)
hostdata->connected = NULL;
- hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
- }
else {
cmd = (Scsi_Cmnd *)hostdata->selecting; /* get a valid cmd */
hostdata->selecting = NULL;
}
cmd->result = DID_NO_CONNECT << 16;
- hostdata->selecting = NULL;
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
hostdata->state = S_UNCONNECTED;
- cmd->scsi_done(cmd); /* I think scsi_done() enables ints */
+ cmd->scsi_done(cmd);
/* We are not connected to a target - check to see if there
* are commands waiting to be executed.
*/
+ sti();
wd33c93_execute(instance);
break;
@@ -620,10 +766,9 @@
/* construct an IDENTIFY message with correct disconnect bit */
- if (hostdata->args & A_NO_DISCONNECT)
- hostdata->outgoing_msg[0] = (0x80 | cmd->lun);
- else
- hostdata->outgoing_msg[0] = (0x80 | 0x40 | cmd->lun);
+ hostdata->outgoing_msg[0] = (0x80 | 0x00 | cmd->lun);
+ if (cmd->SCp.phase)
+ hostdata->outgoing_msg[0] |= 0x40;
if (hostdata->sync_stat[cmd->target] == SS_FIRST) {
#ifdef SYNC_DEBUG
@@ -671,11 +816,10 @@
/* Note: this interrupt should not occur in a LEVEL2 command */
case CSR_XFER_DONE|PHS_COMMAND:
+ case CSR_UNEXP |PHS_COMMAND:
case CSR_SRV_REQ |PHS_COMMAND:
DB(DB_INTR,printk("CMND-%02x,%ld",cmd->cmnd[0],cmd->pid))
- ucp = cmd->cmnd;
- i = cmd->cmd_len;
- transfer_pio(regp, &ucp, &i, DATA_OUT_DIR, hostdata);
+ transfer_pio(regp, cmd->cmnd, cmd->cmd_len, DATA_OUT_DIR, hostdata);
hostdata->state = S_CONNECTED;
break;
@@ -704,6 +848,7 @@
case CSR_SRV_REQ |PHS_MESS_IN:
DB(DB_INTR,printk("MSG_IN="))
+ cli();
msg = read_1_byte(regp);
sr = read_wd33c93(regp, WD_SCSI_STATUS); /* clear interrupt */
@@ -730,7 +875,6 @@
case RESTORE_POINTERS:
DB(DB_INTR,printk("RDP"))
-
if (hostdata->level2 >= L2_BASIC) {
write_wd33c93(regp, WD_COMMAND_PHASE, 0x45);
write_wd33c93_cmd(regp, WD_CMD_SEL_ATN_XFER);
@@ -777,24 +921,50 @@
case EXTENDED_SDTR:
id = calc_sync_xfer(ucp[3],ucp[4]);
if (hostdata->sync_stat[cmd->target] != SS_WAITING) {
- printk("Rejecting target's SDTR message ");
+
+/* A device has sent an unsolicited SDTR message; rather than go
+ * through the effort of decoding it and then figuring out what
+ * our reply should be, we're just gonna say that we have a
+ * synchronous fifo depth of 0. This will result in asynchronous
+ * transfers - not ideal but so much easier.
+ * Actually, this is OK because it assures us that if we don't
+ * specifically ask for sync transfers, we won't do any.
+ */
+
write_wd33c93_cmd(regp,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
- hostdata->outgoing_msg[0] = MESSAGE_REJECT;
- hostdata->outgoing_len = 1;
+ hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;
+ hostdata->outgoing_msg[1] = 3;
+ hostdata->outgoing_msg[2] = EXTENDED_SDTR;
+ hostdata->outgoing_msg[3] = hostdata->default_sx_per/4;
+ hostdata->outgoing_msg[4] = 0;
+ hostdata->outgoing_len = 5;
+ hostdata->sync_xfer[cmd->target] =
+ calc_sync_xfer(hostdata->default_sx_per/4,0);
}
else {
hostdata->sync_xfer[cmd->target] = id;
- hostdata->sync_stat[cmd->target] = SS_SET;
}
#ifdef SYNC_DEBUG
-printk("sync_xfer=%02x",id);
+printk("sync_xfer=%02x",hostdata->sync_xfer[cmd->target]);
#endif
+ hostdata->sync_stat[cmd->target] = SS_SET;
+ write_wd33c93_cmd(regp,WD_CMD_NEGATE_ACK);
+ hostdata->state = S_CONNECTED;
+ break;
+ case EXTENDED_WDTR:
+ write_wd33c93_cmd(regp,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ printk("sending WDTR ");
+ hostdata->outgoing_msg[0] = EXTENDED_MESSAGE;
+ hostdata->outgoing_msg[1] = 2;
+ hostdata->outgoing_msg[2] = EXTENDED_WDTR;
+ hostdata->outgoing_msg[3] = 0; /* 8 bit transfer width */
+ hostdata->outgoing_len = 4;
write_wd33c93_cmd(regp,WD_CMD_NEGATE_ACK);
hostdata->state = S_CONNECTED;
break;
default:
- printk("Rejecting Unknown Extended Message(%02x). ",ucp[2]);
write_wd33c93_cmd(regp,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
+ printk("Rejecting Unknown Extended Message(%02x). ",ucp[2]);
hostdata->outgoing_msg[0] = MESSAGE_REJECT;
hostdata->outgoing_len = 1;
write_wd33c93_cmd(regp,WD_CMD_NEGATE_ACK);
@@ -814,7 +984,7 @@
break;
default:
- printk("Rejecting Unknown Message(%02x) ",ucp[0]);
+ printk("Rejecting Unknown Message(%02x) ",msg);
write_wd33c93_cmd(regp,WD_CMD_ASSERT_ATN); /* want MESS_OUT */
hostdata->outgoing_msg[0] = MESSAGE_REJECT;
hostdata->outgoing_len = 1;
@@ -828,6 +998,12 @@
case CSR_SEL_XFER_DONE:
cli();
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+ write_wd33c93(regp,WD_SOURCE_ID, SRCID_ER);
if (phs == 0x60) {
DB(DB_INTR,printk("SX-DONE-%ld",cmd->pid))
cmd->SCp.Message = COMMAND_COMPLETE;
@@ -841,7 +1017,7 @@
cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
hostdata->state = S_UNCONNECTED;
- cmd->scsi_done(cmd); /* I think scsi_done() enables ints */
+ cmd->scsi_done(cmd);
/* We are no longer connected to a target - check to see if
* there are commands waiting to be executed.
@@ -877,8 +1053,8 @@
* it - like when our SDTR message is rejected by a target. Some
* targets send the REJECT before receiving all of the extended
* message, and then seem to go back to MESSAGE_OUT for a byte
- * or two. Not sure why, ot if I'm doing something wrong to
- * casue this to happen. Regardless, it seems that sending
+ * or two. Not sure why, or if I'm doing something wrong to
+ * cause this to happen. Regardless, it seems that sending
* NOP messages in these situations results in no harm and
* makes everyone happy.
*/
@@ -887,22 +1063,67 @@
hostdata->outgoing_len = 1;
hostdata->outgoing_msg[0] = NOP;
}
- ucp = hostdata->outgoing_msg;
- i = hostdata->outgoing_len;
- transfer_pio(regp, &ucp, &i, DATA_OUT_DIR, hostdata);
+ transfer_pio(regp, hostdata->outgoing_msg, hostdata->outgoing_len,
+ DATA_OUT_DIR, hostdata);
DB(DB_INTR,printk("%02x",hostdata->outgoing_msg[0]))
hostdata->outgoing_len = 0;
hostdata->state = S_CONNECTED;
break;
- case CSR_DISC:
-DB(DB_INTR,printk("DISC"))
+ case CSR_UNEXP_DISC:
+
+/* I think I've seen this after a request-sense that was in response
+ * to an error condition, but not sure. We certainly need to do
+ * something when we get this interrupt - the question is 'what?'.
+ * Let's think positively, and assume some command has finished
+ * in a legal manner (like a command that provokes a request-sense),
+ * so we treat it as a normal command-complete-disconnect.
+ */
+
+ cli();
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+ write_wd33c93(regp,WD_SOURCE_ID, SRCID_ER);
if (cmd == NULL) {
printk(" - Already disconnected! ");
hostdata->state = S_UNCONNECTED;
return;
}
+DB(DB_INTR,printk("UNEXP_DISC-%ld",cmd->pid))
+ hostdata->connected = NULL;
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+ hostdata->state = S_UNCONNECTED;
+ if (cmd->cmnd[0] != REQUEST_SENSE)
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ else if (cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ cmd->scsi_done(cmd);
+
+/* We are no longer connected to a target - check to see if
+ * there are commands waiting to be executed.
+ */
+
+ wd33c93_execute(instance);
+ break;
+
+
+ case CSR_DISC:
+ cli();
+
+/* Make sure that reselection is enabled at this point - it may
+ * have been turned off for the command that just completed.
+ */
+
+ write_wd33c93(regp,WD_SOURCE_ID, SRCID_ER);
+DB(DB_INTR,printk("DISC-%ld",cmd->pid))
+ if (cmd == NULL) {
+ printk(" - Already disconnected! ");
+ hostdata->state = S_UNCONNECTED;
+ }
switch (hostdata->state) {
case S_PRE_CMP_DISC:
hostdata->connected = NULL;
@@ -912,7 +1133,7 @@
cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
else if (cmd->SCp.Status != GOOD)
cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
- cmd->scsi_done(cmd); /* I think scsi_done() enables ints */
+ cmd->scsi_done(cmd);
break;
case S_PRE_TMP_DISC:
case S_RUNNING_LEVEL2:
@@ -920,6 +1141,11 @@
hostdata->disconnected_Q = cmd;
hostdata->connected = NULL;
hostdata->state = S_UNCONNECTED;
+
+#ifdef PROC_INTERFACE
+ disc_taken_total++;
+#endif
+
break;
default:
printk("*** Unexpected DISCONNECT interrupt! ***");
@@ -943,7 +1169,7 @@
/* happen during Arbitration/Selection of some other device. */
/* If yes, put losing command back on top of input_Q. */
- if (hostdata->level2 == L2_NONE) {
+ if (hostdata->level2 <= L2_NONE) {
if (hostdata->selecting) {
cmd = (Scsi_Cmnd *)hostdata->selecting;
@@ -971,7 +1197,7 @@
}
- /* OK - find out which device reslected us. */
+ /* OK - find out which device reselected us. */
id = read_wd33c93(regp, WD_SOURCE_ID);
id &= SRCID_MASK;
@@ -1034,9 +1260,11 @@
break;
default:
- printk("\n---UNKNOWN INTERRUPT:%02x:%02x:%02x!!---",asr,sr,phs);
+ printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--",asr,sr,phs);
}
+ restore_flags(flags);
+
DB(DB_INTR,printk("} "))
}
@@ -1056,7 +1284,7 @@
instance->this_id | hostdata->clock_freq);
write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
write_wd33c93(regp, WD_SYNCHRONOUS_TRANSFER,
- calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF));
+ calc_sync_xfer(hostdata->default_sx_per/4,DEFAULT_SX_OFF));
write_wd33c93(regp, WD_COMMAND, WD_CMD_RESET);
while (!(READ_AUX_STAT() & ASR_INT))
@@ -1080,26 +1308,44 @@
hostdata->chip = C_UNKNOWN_CHIP;
write_wd33c93(regp, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE);
- if (hostdata->args & A_NO_DISCONNECT)
- write_wd33c93(regp, WD_SOURCE_ID, 0);
- else
- write_wd33c93(regp, WD_SOURCE_ID, SRCID_ER);
write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
}
+#if LINUX_VERSION_CODE >= 0x010300
+int wd33c93_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+#else
int wd33c93_reset(Scsi_Cmnd *SCpnt)
+#endif
{
unsigned long flags;
struct Scsi_Host *instance;
+struct WD33C93_hostdata *hostdata;
+int i;
instance = SCpnt->host;
+ hostdata = (struct WD33C93_hostdata *)instance->hostdata;
printk("scsi%d: reset. ", instance->host_no);
save_flags(flags);
cli();
+
((struct WD33C93_hostdata *)instance->hostdata)->dma_stop(instance,NULL,0);
+ for (i = 0; i < 8; i++) {
+ hostdata->busy[i] = 0;
+ hostdata->sync_xfer[i] = calc_sync_xfer(DEFAULT_SX_PER/4,DEFAULT_SX_OFF);
+ hostdata->sync_stat[i] = SS_UNSET; /* using default sync values */
+ }
+ hostdata->input_Q = NULL;
+ hostdata->selecting = NULL;
+ hostdata->connected = NULL;
+ hostdata->disconnected_Q = NULL;
+ hostdata->state = S_UNCONNECTED;
+ hostdata->dma = D_DMA_OFF;
+ hostdata->incoming_ptr = 0;
+ hostdata->outgoing_len = 0;
+
reset_wd33c93(instance);
SCpnt->result = DID_RESET << 16;
restore_flags(flags);
@@ -1113,38 +1359,41 @@
struct Scsi_Host *instance;
struct WD33C93_hostdata *hostdata;
wd33c93_regs *regp;
-Scsi_Cmnd *tmp, **prev;
+Scsi_Cmnd *tmp, *prev;
unsigned long flags;
+ save_flags (flags);
+ cli();
+
instance = cmd->host;
hostdata = (struct WD33C93_hostdata *)instance->hostdata;
regp = hostdata->regp;
- printk ("scsi%d: abort. ", instance->host_no);
-
- save_flags (flags);
- cli();
-
/*
* Case 1 : If the command hasn't been issued yet, we simply remove it
- * from the issue queue.
+ * from the input_Q.
*/
- for (prev=(Scsi_Cmnd **)&(hostdata->input_Q),tmp=(Scsi_Cmnd *)hostdata->input_Q;
- tmp;
- prev=(Scsi_Cmnd **)&(tmp->host_scribble),tmp=(Scsi_Cmnd *)tmp->host_scribble)
- if (cmd == tmp) {
- (*prev) = (Scsi_Cmnd *)tmp->host_scribble;
- tmp->host_scribble = NULL;
- tmp->result = DID_ABORT << 16;
+
+ tmp = (Scsi_Cmnd *)hostdata->input_Q;
+ prev = 0;
+ while (tmp) {
+ if (tmp == cmd) {
+ if (prev)
+ prev->host_scribble = cmd->host_scribble;
+ cmd->host_scribble = NULL;
+ cmd->result = DID_ABORT << 16;
+ printk("scsi%d: Abort - removing command %ld from input_Q. ",
+ instance->host_no, cmd->pid);
+ cmd->scsi_done(cmd);
restore_flags(flags);
- printk("scsi%d : abort removed command from issue queue. ",
- instance->host_no);
- tmp->scsi_done(tmp);
return SCSI_ABORT_SUCCESS;
}
+ prev = tmp;
+ tmp = (Scsi_Cmnd *)tmp->host_scribble;
+ }
/*
- * Case 2 : If any commands are connected, we're going to fail the abort
+ * Case 2 : If the command is connected, we're going to fail the abort
* and let the high level SCSI driver retry at a later time or
* issue a reset.
*
@@ -1158,20 +1407,22 @@
uchar sr, asr;
unsigned long timeout;
- printk("scsi%d : aborting connected command. ", instance->host_no);
+ printk("scsi%d: Aborting connected command %ld - ",
+ instance->host_no, cmd->pid);
+ printk("stopping DMA - ");
if (hostdata->dma == D_DMA_RUNNING) {
hostdata->dma_stop(instance, cmd, 0);
hostdata->dma = D_DMA_OFF;
}
- printk("scsi%d : wd33c93 asr is %x. ", instance->host_no, READ_AUX_STAT());
-
+ printk("sending wd33c93 ABORT command - ");
write_wd33c93(regp, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);
write_wd33c93_cmd(regp, WD_CMD_ABORT);
/* Now we have to attempt to flush out the FIFO... */
+ printk("flushing fifo - ");
timeout = 1000000;
do {
asr = READ_AUX_STAT();
@@ -1179,46 +1430,34 @@
read_wd33c93(regp, WD_DATA);
} while (!(asr & ASR_INT) && timeout-- > 0);
sr = read_wd33c93(regp, WD_SCSI_STATUS);
- printk("scsi%d : wd33c93 sr is %x. ", instance->host_no,
- read_wd33c93(regp, WD_SCSI_STATUS));
+ printk("asr=%02x, sr=%02x, %ld bytes un-transferred (timeout=%ld) - ",
+ asr, sr, read_wd33c93_count(regp), timeout);
- if (sr >= (CSR_ABORT|PHS_DATA_OUT) && sr <= (CSR_ABORT|PHS_MESS_IN)) {
/*
* Abort command processed.
* Still connected.
* We must disconnect.
*/
- printk("scsi%d : count was %ld. ", instance->host_no,
- read_wd33c93_count(regp));
- timeout = 1000000;
- while ((asr & ASR_CIP) && timeout-- > 0)
- asr = READ_AUX_STAT();
- write_wd33c93_cmd(regp, WD_CMD_DISCONNECT);
- asr = READ_AUX_STAT();
- if (asr & ASR_LCI)
- printk ("scsi%d: disconnect command ignored. ",
- instance->host_no);
- timeout = 1000000;
- while ((asr & ASR_CIP) && timeout-- > 0)
- asr = READ_AUX_STAT();
- }
- asr = READ_AUX_STAT();
- sr = read_wd33c93(regp, WD_SCSI_STATUS);
- printk("scsi%d : asr is %x, sr is %x. ",instance->host_no,asr,sr);
+ printk("sending wd33c93 DISCONNECT command - ");
write_wd33c93_cmd(regp, WD_CMD_DISCONNECT);
+
timeout = 1000000;
+ asr = READ_AUX_STAT();
while ((asr & ASR_CIP) && timeout-- > 0)
asr = READ_AUX_STAT();
sr = read_wd33c93(regp, WD_SCSI_STATUS);
- printk("scsi%d : asr is %x, sr is %x. ",instance->host_no,asr,sr);
- reset_wd33c93(instance);
- cmd->result = DID_ABORT << 16;
- cmd->scsi_done(cmd);
+ printk("asr=%02x, sr=%02x.",asr,sr);
+
hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
hostdata->connected = NULL;
hostdata->state = S_UNCONNECTED;
+ cmd->result = DID_ABORT << 16;
+ cmd->scsi_done(cmd);
+
+/* sti();*/
wd33c93_execute (instance);
+
restore_flags(flags);
return SCSI_ABORT_SUCCESS;
}
@@ -1229,12 +1468,17 @@
* an ABORT_SNOOZE and hope for the best...
*/
- for (tmp=(Scsi_Cmnd *)hostdata->disconnected_Q; tmp;
- tmp=(Scsi_Cmnd *)tmp->host_scribble)
- if (cmd == tmp) {
+ tmp = (Scsi_Cmnd *)hostdata->disconnected_Q;
+ while (tmp) {
+ if (tmp == cmd) {
+ printk("scsi%d: Abort - command %ld found on disconnected_Q - ",
+ instance->host_no, cmd->pid);
+ printk("returning ABORT_SNOOZE. ");
restore_flags(flags);
return SCSI_ABORT_SNOOZE;
}
+ tmp = (Scsi_Cmnd *)tmp->host_scribble;
+ }
/*
* Case 4 : If we reached this point, the command was not found in any of
@@ -1242,29 +1486,101 @@
*
* We probably reached this point because of an unlikely race condition
* between the command completing successfully and the abortion code,
- * so we won't panic, but we will notify the user in case somethign really
+ * so we won't panic, but we will notify the user in case something really
* broke.
*/
+/* sti();*/
+ wd33c93_execute (instance);
+
restore_flags(flags);
- printk("scsi%d : warning : SCSI command probably completed successfully\n"
+ printk("scsi%d: warning : SCSI command probably completed successfully"
" before abortion. ", instance->host_no);
return SCSI_ABORT_NOT_RUNNING;
}
-#define MAX_WD33C93_HOSTS 8
-static unsigned int setup_args_array[MAX_WD33C93_HOSTS];
-static int setup_args_array_x = 0;
+#define MAX_WD33C93_HOSTS 4
+#define MAX_SETUP_STRINGS (sizeof(setup_strings) / sizeof(char *))
+#define SETUP_BUFFER_SIZE 200
+static char setup_buffer[SETUP_BUFFER_SIZE];
+static char setup_used[MAX_SETUP_STRINGS];
void wd33c93_setup (char *str, int *ints)
{
-int i;
+int i,x;
+char *p1,*p2;
+
+ /* The kernel does some processing of the command-line before calling
+ * this function: If it begins with any decimal or hex number arguments,
+ * ints[0] = how many numbers found and ints[1] through [n] are the values
+ * themselves. str points to where the non-numeric arguments (if any)
+ * start: We do our own parsing of those. We construct synthetic 'nosync'
+ * keywords out of numeric args (to maintain compatibility with older
+ * versions) and then add the rest of the arguments.
+ */
+
+ p1 = setup_buffer;
+ *p1 = '\0';
+ if (ints[0]) {
+ for (i=0; i<ints[0]; i++) {
+ x = vsprintf(p1,"nosync:0x%02x,",&(ints[i+1]));
+ p1 += x;
+ }
+ }
+ if (str)
+ strncpy(p1, str, SETUP_BUFFER_SIZE - strlen(setup_buffer));
+ setup_buffer[SETUP_BUFFER_SIZE - 1] = '\0';
+ p1 = setup_buffer;
+ i = 0;
+ while (*p1 && (i < MAX_SETUP_STRINGS)) {
+ p2 = strchr(p1, ',');
+ if (p2) {
+ *p2 = '\0';
+ if (p1 != p2)
+ setup_strings[i] = p1;
+ p1 = p2 + 1;
+ i++;
+ }
+ else {
+ setup_strings[i] = p1;
+ break;
+ }
+ }
+ for (i=0; i<MAX_SETUP_STRINGS; i++)
+ setup_used[i] = 0;
+}
+
+
+/* check_setup_strings() returns index if key found, 0 if not
+ */
+
+int check_setup_strings(char *key, int *flags, int *val, char *buf)
+{
+int x;
+char *cp;
- for (i=0; i<ints[0]; i++) {
- setup_args_array[i] = ints[i+1];
+ for (x=0; x<MAX_SETUP_STRINGS; x++) {
+ if (setup_used[x])
+ continue;
+ if (!strncmp(setup_strings[x], key, strlen(key)))
+ break;
+ if (!strncmp(setup_strings[x], "next", strlen("next")))
+ return 0;
+ }
+ if (x == MAX_SETUP_STRINGS)
+ return 0;
+ setup_used[x] = 1;
+ cp = setup_strings[x] + strlen(key);
+ *val = -1;
+ if (*cp != ':')
+ return ++x;
+ cp++;
+ if ((*cp >= '0') && (*cp <= '9')) {
+ *val = simple_strtoul(cp,NULL,0);
}
+ return ++x;
}
@@ -1274,6 +1590,9 @@
{
struct WD33C93_hostdata *hostdata;
int i;
+int flags;
+int val;
+char buf[32];
hostdata = (struct WD33C93_hostdata *)instance->hostdata;
@@ -1294,46 +1613,226 @@
hostdata->disconnected_Q = NULL;
hostdata->state = S_UNCONNECTED;
hostdata->dma = D_DMA_OFF;
+ hostdata->level2 = L2_BASIC;
+ hostdata->disconnect = DIS_ADAPTIVE;
+ hostdata->args = DEBUG_DEFAULTS;
hostdata->incoming_ptr = 0;
hostdata->outgoing_len = 0;
+ hostdata->default_sx_per = DEFAULT_SX_PER;
+ hostdata->no_sync = 0xff; /* sync defaults to off */
+
+#ifdef PROC_INTERFACE
+ hostdata->proc = PR_VERSION|PR_INFO|PR_TOTALS|
+ PR_CONNECTED|PR_INPUTQ|PR_DISCQ|
+ PR_STOP;
+
+ disc_allowed_total = 0;
+ disc_taken_total = 0;
+#endif
+
+
+ if (check_setup_strings("nosync",&flags,&val,buf))
+ hostdata->no_sync = val;
+
+ if (check_setup_strings("period",&flags,&val,buf))
+ hostdata->default_sx_per = sx_table[round_period((unsigned int)val)].period_ns;
+
+ if (check_setup_strings("disconnect",&flags,&val,buf)) {
+ if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS))
+ hostdata->disconnect = val;
+ else
+ hostdata->disconnect = DIS_ADAPTIVE;
+ }
+
+ if (check_setup_strings("debug",&flags,&val,buf))
+ hostdata->args = val & DB_MASK;
+
+ if (check_setup_strings("clock",&flags,&val,buf)) {
+ if (val>7 && val<11)
+ val = WD33C93_FS_8_10;
+ else if (val>11 && val<16)
+ val = WD33C93_FS_12_15;
+ else if (val>15 && val<21)
+ val = WD33C93_FS_16_20;
+ else
+ val = WD33C93_FS_8_10;
+ hostdata->clock_freq = val;
+ }
+
+ if ((i = check_setup_strings("next",&flags,&val,buf))) {
+ while (i)
+ setup_used[--i] = 1;
+ }
+
+#ifdef PROC_INTERFACE
+ if (check_setup_strings("proc",&flags,&val,buf))
+ hostdata->proc = val;
+#endif
- hostdata->args = setup_default;
- if ((setup_args_array_x < MAX_WD33C93_HOSTS) &&
- (setup_args_array[setup_args_array_x]))
- hostdata->args = setup_args_array[setup_args_array_x];
- setup_args_array_x++;
-
- i = hostdata->args & (A_LEVEL2_0 | A_LEVEL2_1 | A_LEVEL2_2);
- i >>= 8;
- if (i == 0)
- i = L2_DEFAULT;
- hostdata->level2 = i;
cli();
reset_wd33c93(instance);
sti();
- printk("wd33c93-%d: ",instance->host_no);
- switch (hostdata->chip) {
- case C_WD33C93:
- printk("Found WD33c93 chip! This driver probably needs at least the 'A' version!\n");
- break;
- case C_WD33C93A:
- printk("Found WD33c93A chip: microcode=%02x\n",hostdata->microcode);
- break;
- case C_WD33C93B:
- printk("Found WD33c93B chip: microcode=%02x\n",hostdata->microcode);
- break;
- default:
- printk("Unknown 3393 chip!\n");
- }
- printk("wd33c93-%d: LEVEL2 commands %s (%d)\n",instance->host_no,
- (hostdata->level2 == L2_NONE)?"disabled":"enabled",
- hostdata->level2);
+ printk("wd33c93-%d: chip=%s microcode=%02x\n",instance->host_no,
+ (hostdata->chip==C_WD33C93)?"WD33c93":
+ (hostdata->chip==C_WD33C93A)?"WD33c93A":
+ (hostdata->chip==C_WD33C93B)?"WD33c93B":"unknown",
+ hostdata->microcode);
+
#ifdef DEBUGGING_ON
+ printk("wd33c93-%d: setup_strings=",instance->host_no);
+ for (i=0; i<MAX_SETUP_STRINGS; i++)
+ printk("%s,",setup_strings[i]);
+ printk("\n");
printk("wd33c93-%d: debug_flags = %04x\n",instance->host_no,hostdata->args);
#endif
printk("wd33c93-%d: driver version %s - %s\n",instance->host_no,
WD33C93_VERSION,WD33C93_DATE);
+ printk("wd33c93-%d: compiled on %s at %s\n",instance->host_no,
+ __DATE__,__TIME__);
+}
+
+
+int wd33c93_proc_info(char *buf, char **start, off_t off, int len, int hn, int in)
+{
+
+#ifdef PROC_INTERFACE
+
+char *bp;
+char tbuf[128];
+unsigned long flags;
+struct Scsi_Host *instance;
+struct WD33C93_hostdata *hd;
+Scsi_Cmnd *cmd;
+int x,i;
+static int stop = 0;
+
+ for (instance=instance_list; instance; instance=instance->next) {
+ if (instance->host_no == hn)
+ break;
+ }
+ if (!instance) {
+ printk("*** Hmm... Can't find host #%d!\n",hn);
+ return (-ESRCH);
+ }
+ hd = (struct WD33C93_hostdata *)instance->hostdata;
+
+/* If 'in' is TRUE we need to _read_ the proc file. We accept the following
+ * keywords (same format as command-line, but only ONE per read):
+ * debug
+ * disconnect
+ * period
+ * resync
+ * proc
+ */
+
+ if (in) {
+ buf[len] = '\0';
+ bp = buf;
+ if (!strncmp(bp,"debug:",6)) {
+ bp += 6;
+ hd->args = simple_strtoul(bp,NULL,0) & DB_MASK;
+ }
+ else if (!strncmp(bp,"disconnect:",11)) {
+ bp += 11;
+ x = simple_strtoul(bp,NULL,0);
+ if (x < DIS_NEVER || x > DIS_ALWAYS)
+ x = DIS_ADAPTIVE;
+ hd->disconnect = x;
+ }
+ else if (!strncmp(bp,"period:",7)) {
+ bp += 7;
+ x = simple_strtoul(bp,NULL,0);
+ hd->default_sx_per = sx_table[round_period((unsigned int)x)].period_ns;
+ }
+ else if (!strncmp(bp,"resync:",7)) {
+ bp += 7;
+ x = simple_strtoul(bp,NULL,0);
+ for (i=0; i<7; i++)
+ if (x & (1<<i))
+ hd->sync_stat[i] = SS_UNSET;
+ }
+ else if (!strncmp(bp,"proc:",5)) {
+ bp += 5;
+ hd->proc = simple_strtoul(bp,NULL,0);
+ }
+ return len;
+ }
+
+ save_flags(flags);
+ cli();
+ bp = buf;
+ *bp = '\0';
+ if (hd->proc & PR_VERSION) {
+ sprintf(tbuf,"\nVersion %s - %s. Compiled %s %s",
+ WD33C93_VERSION,WD33C93_DATE,__DATE__,__TIME__);
+ strcat(bp,tbuf);
+ }
+ if (hd->proc & PR_INFO) {
+ ;
+ }
+ if (hd->proc & PR_TOTALS) {
+ sprintf(tbuf,"\n%ld disc_allowed, %ld disc_taken",
+ disc_allowed_total,disc_taken_total);
+ strcat(bp,tbuf);
+ }
+ if (hd->proc & PR_CONNECTED) {
+ strcat(bp,"\nconnected: ");
+ if (hd->connected) {
+ cmd = (Scsi_Cmnd *)hd->connected;
+ sprintf(tbuf," %ld-%d:%d(%02x)",
+ cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+ strcat(bp,tbuf);
+ }
+ }
+ if (hd->proc & PR_INPUTQ) {
+ strcat(bp,"\ninput_Q: ");
+ cmd = (Scsi_Cmnd *)hd->input_Q;
+ while (cmd) {
+ sprintf(tbuf," %ld-%d:%d(%02x)",
+ cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+ strcat(bp,tbuf);
+ cmd = (Scsi_Cmnd *)cmd->host_scribble;
+ }
+ }
+ if (hd->proc & PR_DISCQ) {
+ strcat(bp,"\ndisconnected_Q:");
+ cmd = (Scsi_Cmnd *)hd->disconnected_Q;
+ while (cmd) {
+ sprintf(tbuf," %ld-%d:%d(%02x)",
+ cmd->pid, cmd->target, cmd->lun, cmd->cmnd[0]);
+ strcat(bp,tbuf);
+ cmd = (Scsi_Cmnd *)cmd->host_scribble;
+ }
+ }
+ strcat(bp,"\n");
+ restore_flags(flags);
+ *start = buf;
+ if (stop) {
+ stop = 0;
+ return 0;
+ }
+ if (off > 0x40000) /* ALWAYS stop after 256k bytes have been read */
+ stop = 1;;
+ if (hd->proc & PR_STOP) /* stop every other time */
+ stop = 1;
+ return strlen(bp);
+
+#else /* PROC_INTERFACE */
+
+ return 0;
+
+#endif /* PROC_INTERFACE */
+
}
+
+
+#ifdef MODULE
+
+Scsi_Host_Template driver_template = WD33C93;
+
+#include "scsi_module.c"
+
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov
with Sam's (original) version of this