patch-2.4.22 linux-2.4.22/drivers/acorn/scsi/fas216.c

Next file: linux-2.4.22/drivers/acorn/scsi/fas216.h
Previous file: linux-2.4.22/drivers/acorn/scsi/eesox.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.21/drivers/acorn/scsi/fas216.c linux-2.4.22/drivers/acorn/scsi/fas216.c
@@ -1,7 +1,7 @@
 /*
- *  linux/arch/arm/drivers/scsi/fas216.c
+ *  linux/drivers/acorn/scsi/fas216.c
  *
- *  Copyright (C) 1997-2000 Russell King
+ *  Copyright (C) 1997-2003 Russell King
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -10,7 +10,7 @@
  * Based on information in qlogicfas.c by Tom Zerucha, Michael Griffith, and
  * other sources, including:
  *   the AMD Am53CF94 data sheet
- *   the AMD Am53C94 data sheet 
+ *   the AMD Am53C94 data sheet
  *
  * This is a generic driver.  To use it, have a look at cumana_2.c.  You
  * should define your own structure that overlays FAS216_Info, eg:
@@ -33,9 +33,6 @@
  *  02-04-2000	RMK	Converted to use the new error handling, and
  *			automatically request sense data upon check
  *			condition status from targets.
- *
- * Todo:
- *  - allow individual devices to enable sync xfers.
  */
 #include <linux/module.h>
 #include <linux/blk.h>
@@ -44,25 +41,20 @@
 #include <linux/ioport.h>
 #include <linux/sched.h>
 #include <linux/proc_fs.h>
-#include <linux/unistd.h>
-#include <linux/stat.h>
 #include <linux/delay.h>
+#include <linux/bitops.h>
 #include <linux/init.h>
+#include <linux/interrupt.h>
 
 #include <asm/dma.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/ecard.h>
 
-#define FAS216_C
-
 #include "../../scsi/scsi.h"
 #include "../../scsi/hosts.h"
 #include "fas216.h"
-
-#define VER_MAJOR	0
-#define VER_MINOR	0
-#define VER_PATCH	5
+#include "scsi.h"
 
 /* NOTE: SCSI2 Synchronous transfers *require* DMA according to
  *  the data sheet.  This restriction is crazy, especially when
@@ -88,35 +80,68 @@
  *  I was thinking that this was a good chip until I found this restriction ;(
  */
 #define SCSI2_SYNC
-#undef  SCSI2_WIDE
 #undef  SCSI2_TAG
 
 #undef DEBUG_CONNECT
-#undef DEBUG_BUSSERVICE
-#undef DEBUG_FUNCTIONDONE
 #undef DEBUG_MESSAGES
 
 #undef CHECK_STRUCTURE
 
-static struct { int stat, ssr, isr, ph; } list[8];
-static int ptr;
+#ifndef ABORT_TAG
+#define ABORT_TAG	0x0d
+#endif
+
+#define LOG_CONNECT		(1 << 0)
+#define LOG_BUSSERVICE		(1 << 1)
+#define LOG_FUNCTIONDONE	(1 << 2)
+#define LOG_MESSAGES		(1 << 3)
+#define LOG_BUFFER		(1 << 4)
+#define LOG_ERROR		(1 << 8)
+
+static int level_mask = LOG_ERROR;
+
+MODULE_PARM(level_mask, "i");
+
+static inline unsigned char fas216_readb(FAS216_Info *info, unsigned int reg)
+{
+	unsigned int off = reg << info->scsi.io_shift;
+
+	return inb(info->scsi.io_port + off);
+}
+
+static inline void fas216_writeb(FAS216_Info *info, unsigned int reg, unsigned int val)
+{
+	unsigned int off = reg << info->scsi.io_shift;
+
+	outb(val, info->scsi.io_port + off);
+}
 
 static void fas216_dumpstate(FAS216_Info *info)
 {
 	unsigned char is, stat, inst;
 
-	is   = inb(REG_IS(info));
-	stat = inb(REG_STAT(info));
-	inst = inb(REG_INST(info));
+	is   = fas216_readb(info, REG_IS);
+	stat = fas216_readb(info, REG_STAT);
+	inst = fas216_readb(info, REG_INST);
 	
 	printk("FAS216: CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X"
 	       " INST=%02X IS=%02X CFIS=%02X",
-		inb(REG_CTCL(info)), inb(REG_CTCM(info)),
-		inb(REG_CMD(info)),  stat, inst, is,
-		inb(REG_CFIS(info)));
+		fas216_readb(info, REG_CTCL),
+		fas216_readb(info, REG_CTCM),
+		fas216_readb(info, REG_CMD),  stat, inst, is,
+		fas216_readb(info, REG_CFIS));
 	printk(" CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X\n",
-		inb(REG_CNTL1(info)), inb(REG_CNTL2(info)),
-		inb(REG_CNTL3(info)), inb(REG_CTCH(info)));
+		fas216_readb(info, REG_CNTL1),
+		fas216_readb(info, REG_CNTL2),
+		fas216_readb(info, REG_CNTL3),
+		fas216_readb(info, REG_CTCH));
+}
+
+static void print_SCp(Scsi_Pointer *SCp, const char *prefix, const char *suffix)
+{
+	printk("%sptr %p this_residual 0x%x buffer %p buffers_residual 0x%x%s",
+		prefix, SCp->ptr, SCp->this_residual, SCp->buffer,
+		SCp->buffers_residual, suffix);
 }
 
 static void fas216_dumpinfo(FAS216_Info *info)
@@ -135,13 +160,9 @@
 		info->scsi.io_port, info->scsi.io_shift, info->scsi.irq,
 		info->scsi.cfg[0], info->scsi.cfg[1], info->scsi.cfg[2],
 		info->scsi.cfg[3]);
-	printk("           type=%p phase=%X reconnected={ target=%d lun=%d tag=%d }\n",
-		info->scsi.type, info->scsi.phase,
-		info->scsi.reconnected.target,
-		info->scsi.reconnected.lun, info->scsi.reconnected.tag);
-	printk("           SCp={ ptr=%p this_residual=%X buffer=%p buffers_residual=%X }\n",
-		info->scsi.SCp.ptr, info->scsi.SCp.this_residual,
-		info->scsi.SCp.buffer, info->scsi.SCp.buffers_residual);
+	printk("           type=%p phase=%X\n",
+		info->scsi.type, info->scsi.phase);
+	print_SCp(&info->scsi.SCp, "           SCp={ ", " }\n");
 	printk("      msgs async_stp=%X disconnectable=%d aborting=%d }\n",
 		info->scsi.async_stp,
 		info->scsi.disconnectable, info->scsi.aborting);
@@ -155,7 +176,7 @@
 		info->ifcfg.clockrate, info->ifcfg.select_timeout,
 		info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth);
 	for (i = 0; i < 8; i++) {
-		printk("    busyluns[%d]=%X dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n",
+		printk("    busyluns[%d]=%02x dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n",
 			i, info->busyluns[i], i,
 			info->device[i].disconnect_ok, info->device[i].stp,
 			info->device[i].sof, info->device[i].sync_state);
@@ -203,21 +224,24 @@
 
 static const char *fas216_drv_phase(FAS216_Info *info)
 {
-	switch (info->scsi.phase) {
-	case PHASE_IDLE:		return "idle";
-	case PHASE_SELECTION:		return "selection";
-	case PHASE_COMMAND:		return "command";
-	case PHASE_RECONNECTED:		return "reconnected";
-	case PHASE_DATAOUT:		return "data out";
-	case PHASE_DATAIN:		return "data in";
-	case PHASE_MSGIN:		return "message in";
-	case PHASE_MSGIN_DISCONNECT:	return "disconnect";
-	case PHASE_MSGOUT_EXPECT:	return "expect message out";
-	case PHASE_MSGOUT:		return "message out";
-	case PHASE_STATUS:		return "status";
-	case PHASE_DONE:		return "done";
-	default:			return "???";
-	}
+	static const char *phases[] = {
+		[PHASE_IDLE]		= "idle",
+		[PHASE_SELECTION]	= "selection",
+		[PHASE_COMMAND]		= "command",
+		[PHASE_DATAOUT]		= "data out",
+		[PHASE_DATAIN]		= "data in",
+		[PHASE_MSGIN]		= "message in",
+		[PHASE_MSGIN_DISCONNECT]= "disconnect",
+		[PHASE_MSGOUT_EXPECT]	= "expect message out",
+		[PHASE_MSGOUT]		= "message out",
+		[PHASE_STATUS]		= "status",
+		[PHASE_DONE]		= "done",
+	};
+
+	if (info->scsi.phase < ARRAY_SIZE(phases) &&
+	    phases[info->scsi.phase])
+		return phases[info->scsi.phase];
+	return "???";
 }
 
 static char fas216_target(FAS216_Info *info)
@@ -228,56 +252,128 @@
 		return 'H';
 }
 
+static void
+fas216_do_log(FAS216_Info *info, char target, char *fmt, va_list ap)
+{
+	static char buf[1024];
+
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	printk("scsi%d.%c: %s", info->host->host_no, target, buf);
+}
+
+static void
+fas216_log_command(FAS216_Info *info, int level, Scsi_Cmnd *SCpnt, char *fmt, ...)
+{
+	va_list args;
+
+	if (level != 0 && !(level & level_mask))
+		return;
+
+	va_start(args, fmt);
+	fas216_do_log(info, '0' + SCpnt->target, fmt, args);
+	va_end(args);
+
+	printk("CDB: ");
+	print_command(SCpnt->cmnd);
+}
+
+static void
+fas216_log_target(FAS216_Info *info, int level, int target, char *fmt, ...)
+{
+	va_list args;
+
+	if (level != 0 && !(level & level_mask))
+		return;
+
+	if (target < 0)
+		target = 'H';
+	else
+		target += '0';
+
+	va_start(args, fmt);
+	fas216_do_log(info, target, fmt, args);
+	va_end(args);
+
+	printk("\n");
+}
+
+static void fas216_log(FAS216_Info *info, int level, char *fmt, ...)
+{
+	va_list args;
+
+	if (level != 0 && !(level & level_mask))
+		return;
+
+	va_start(args, fmt);
+	fas216_do_log(info, fas216_target(info), fmt, args);
+	va_end(args);
+
+	printk("\n");
+}
+
+#define PH_SIZE	32
+
+static struct { int stat, ssr, isr, ph; } ph_list[PH_SIZE];
+static int ph_ptr;
+
 static void add_debug_list(int stat, int ssr, int isr, int ph)
 {
-	list[ptr].stat = stat;
-	list[ptr].ssr = ssr;
-	list[ptr].isr = isr;
-	list[ptr].ph = ph;
+	ph_list[ph_ptr].stat = stat;
+	ph_list[ph_ptr].ssr = ssr;
+	ph_list[ph_ptr].isr = isr;
+	ph_list[ph_ptr].ph = ph;
+
+	ph_ptr = (ph_ptr + 1) & (PH_SIZE-1);
+}
+
+static struct { int command; void *from; } cmd_list[8];
+static int cmd_ptr;
+
+static void fas216_cmd(FAS216_Info *info, unsigned int command)
+{
+	cmd_list[cmd_ptr].command = command;
+	cmd_list[cmd_ptr].from = __builtin_return_address(0);
+
+	cmd_ptr = (cmd_ptr + 1) & 7;
 
-	ptr = (ptr + 1) & 7;
+	fas216_writeb(info, REG_CMD, command);
 }
 
 static void print_debug_list(void)
 {
 	int i;
 
-	i = ptr;
+	i = ph_ptr;
 
-	printk(KERN_ERR "SCSI IRQ trail: ");
+	printk(KERN_ERR "SCSI IRQ trail\n");
 	do {
-		printk("%02X:%02X:%02X:%1X ",
-			list[i].stat, list[i].ssr,
-			list[i].isr, list[i].ph);
+		printk(" %02x:%02x:%02x:%1x",
+			ph_list[i].stat, ph_list[i].ssr,
+			ph_list[i].isr, ph_list[i].ph);
+		i = (i + 1) & (PH_SIZE - 1);
+		if (((i ^ ph_ptr) & 7) == 0)
+			printk("\n");
+	} while (i != ph_ptr);
+	if ((i ^ ph_ptr) & 7)
+		printk("\n");
+
+	i = cmd_ptr;
+	printk(KERN_ERR "FAS216 commands: ");
+	do {
+		printk("%02x:%p ", cmd_list[i].command, cmd_list[i].from);
 		i = (i + 1) & 7;
-	} while (i != ptr);
+	} while (i != cmd_ptr);
 	printk("\n");
 }
 
 static void fas216_done(FAS216_Info *info, unsigned int result);
 
-/* Function: int fas216_clockrate(unsigned int clock)
- * Purpose : calculate correct value to be written into clock conversion
- *	     factor register.
- * Params  : clock - clock speed in MHz
- * Returns : CLKF_ value
- */
-static int fas216_clockrate(int clock)
-{
-	if (clock <= 10 || clock > 40) {
-		printk(KERN_CRIT
-		       "fas216: invalid clock rate: check your driver!\n");
-		clock = -1;
-	} else
-		clock = ((clock - 1) / 5 + 1) & 7;
-
-	return clock;
-}
-
-/* Function: unsigned short fas216_get_last_msg(FAS216_Info *info, int pos)
- * Purpose : retrieve a last message from the list, using position in fifo
- * Params  : info - interface to search
- *         : pos  - current fifo position
+/**
+ * fas216_get_last_msg - retrive last message from the list
+ * @info: interface to search
+ * @pos: current fifo position
+ *
+ * Retrieve a last message from the list, using position in fifo.
  */
 static inline unsigned short
 fas216_get_last_msg(FAS216_Info *info, int pos)
@@ -298,22 +394,21 @@
 			packed_msg = msg->msg[0];
 	}
 
-#ifdef DEBUG_MESSAGES
-	printk("Message: %04X found at position %02X\n",
-		packed_msg, pos);
-#endif
+	fas216_log(info, LOG_MESSAGES,
+		"Message: %04x found at position %02x\n", packed_msg, pos);
+
 	return packed_msg;
 }
 
-/* Function: int fas216_syncperiod(FAS216_Info *info, int ns)
- * Purpose : Calculate value to be loaded into the STP register
- *           for a given period in ns
- * Params  : info - state structure for interface connected to device
- *         : ns   - period in ns (between subsequent bytes)
- * Returns : Value suitable for REG_STP
+/**
+ * fas216_syncperiod - calculate STP register value
+ * @info: state structure for interface connected to device
+ * @ns: period in ns (between subsequent bytes)
+ *
+ * Calculate value to be loaded into the STP register for a given period
+ * in ns. Returns a value suitable for REG_STP.
  */
-static int
-fas216_syncperiod(FAS216_Info *info, int ns)
+static int fas216_syncperiod(FAS216_Info *info, int ns)
 {
 	int value = (info->ifcfg.clockrate * ns) / 1000;
 
@@ -327,23 +422,28 @@
 	return value & 31;
 }
 
-/* Function: void fas216_set_sync(FAS216_Info *info, int target)
- * Purpose : Correctly setup FAS216 chip for specified transfer period.
- * Params  : info   - state structure for interface
- *         : target - target
+/**
+ * fas216_set_sync - setup FAS216 chip for specified transfer period.
+ * @info: state structure for interface connected to device
+ * @target: target
+ *
+ * Correctly setup FAS216 chip for specified transfer period.
  * Notes   : we need to switch the chip out of FASTSCSI mode if we have
  *           a transfer period >= 200ns - otherwise the chip will violate
  *           the SCSI timings.
  */
-static void
-fas216_set_sync(FAS216_Info *info, int target)
+static void fas216_set_sync(FAS216_Info *info, int target)
 {
-	outb(info->device[target].sof, REG_SOF(info));
-	outb(info->device[target].stp, REG_STP(info));
+	unsigned int cntl3;
+
+	fas216_writeb(info, REG_SOF, info->device[target].sof);
+	fas216_writeb(info, REG_STP, info->device[target].stp);
+
+	cntl3 = info->scsi.cfg[2];
 	if (info->device[target].period >= (200 / 4))
-		outb(info->scsi.cfg[2] & ~CNTL3_FASTSCSI, REG_CNTL3(info));
-	else
-		outb(info->scsi.cfg[2], REG_CNTL3(info));
+		cntl3 = cntl3 & ~CNTL3_FASTSCSI;
+
+	fas216_writeb(info, REG_CNTL3, cntl3);
 }
 
 /* Synchronous transfer support
@@ -364,19 +464,20 @@
  *  Using the correct method assures compatibility with wide data
  *  transfers and future enhancements.
  *
- * We will always initiate a synchronous transfer negociation request on
+ * We will always initiate a synchronous transfer negotiation request on
  * every INQUIRY or REQUEST SENSE message, unless the target itself has
- * at some point performed a synchronous transfer negociation request, or
+ * at some point performed a synchronous transfer negotiation request, or
  * we have synchronous transfers disabled for this device.
  */
 
-/* Function: void fas216_handlesync(FAS216_Info *info, char *msg)
- * Purpose : Handle a synchronous transfer message from the target
- * Params  : info - state structure for interface
- *         : msg  - message from target
+/**
+ * fas216_handlesync - Handle a synchronous transfer message
+ * @info: state structure for interface
+ * @msg: message from target
+ *
+ * Handle a synchronous transfer message from the target
  */
-static void
-fas216_handlesync(FAS216_Info *info, char *msg)
+static void fas216_handlesync(FAS216_Info *info, char *msg)
 {
 	struct fas216_device *dev = &info->device[info->SCpnt->target];
 	enum { sync, async, none, reject } res = none;
@@ -392,7 +493,7 @@
 		 *  message with a MESSAGE REJECT message.
 		 *
 		 * Hence, if we get this condition, we disable
-		 * negociation for this device.
+		 * negotiation for this device.
 		 */
 		if (dev->sync_state == neg_inprogress) {
 			dev->sync_state = neg_invalid;
@@ -410,14 +511,14 @@
 			res = reject;
 			break;
 
-		/* We were not negociating a synchronous transfer,
-		 * but the device sent us a negociation request.
+		/* We were not negotiating a synchronous transfer,
+		 * but the device sent us a negotiation request.
 		 * Honour the request by sending back a SDTR
 		 * message containing our capability, limited by
 		 * the targets capability.
 		 */
 		default:
-			outb(CMD_SETATN, REG_CMD(info));
+			fas216_cmd(info, CMD_SETATN);
 			if (msg[4] > info->ifcfg.sync_max_depth)
 				msg[4] = info->ifcfg.sync_max_depth;
 			if (msg[3] < 1000 / info->ifcfg.clockrate)
@@ -436,7 +537,7 @@
 			res = sync;
 			break;
 
-		/* We initiated the synchronous transfer negociation,
+		/* We initiated the synchronous transfer negotiation,
 		 * and have successfully received a response from the
 		 * target.  The synchronous transfer agreement has been
 		 * reached.  Note: if the values returned are out of our
@@ -465,7 +566,7 @@
 		break;
 
 	case reject:
-		outb(CMD_SETATN, REG_CMD(info));
+		fas216_cmd(info, CMD_SETATN);
 		msgqueue_flush(&info->scsi.msgs);
 		msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
 		info->scsi.phase = PHASE_MSGOUT_EXPECT;
@@ -482,332 +583,277 @@
 	}
 }
 
-/* Function: void fas216_handlewide(FAS216_Info *info, char *msg)
- * Purpose : Handle a wide transfer message from the target
- * Params  : info - state structure for interface
- *         : msg  - message from target
+/**
+ * fas216_updateptrs - update data pointers after transfer suspended/paused
+ * @info: interface's local pointer to update
+ * @bytes_transferred: number of bytes transferred
+ *
+ * Update data pointers after transfer suspended/paused
  */
-static void
-fas216_handlewide(FAS216_Info *info, char *msg)
+static void fas216_updateptrs(FAS216_Info *info, int bytes_transferred)
 {
-	struct fas216_device *dev = &info->device[info->SCpnt->target];
-	enum { wide, bit8, none, reject } res = none;
+	Scsi_Pointer *SCp = &info->scsi.SCp;
 
-#ifdef SCSI2_WIDE
-	switch (msg[0]) {
-	case MESSAGE_REJECT:
-		/* Wide transfer request failed.
-		 * Note: SCSI II r10:
-		 *
-		 *  SCSI devices that are capable of wide
-		 *  data transfers shall not respond to a
-		 *  WDTR message with a MESSAGE REJECT message.
-		 *
-		 * Hence, if we get this condition, we never
-		 * reattempt negociation for this device.
-		 */
-		if (dev->wide_state == neg_inprogress) {
-			dev->wide_state = neg_invalid;
-			res = bit8;
-		}
-		break;
+	fas216_checkmagic(info);
 
-	case EXTENDED_MESSAGE:
-		switch (dev->wide_state) {
-		/* We don't accept wide data transfer requests.
-		 * Respond with a MESSAGE REJECT to prevent a
-		 * wide data transfer agreement from being reached.
-		 */
-		case neg_invalid:
-			res = reject;
-			break;
+	BUG_ON(bytes_transferred < 0);
 
-		/* We were not negociating a wide data transfer,
-		 * but the device sent is a negociation request.
-		 * Honour the request by sending back a WDTR
-		 * message containing our capability, limited by
-		 * the targets capability.
-		 */
-		default:
-			outb(CMD_SETATN, REG_CMD(info));
-			if (msg[3] > info->ifcfg.wide_max_size)
-				msg[3] = info->ifcfg.wide_max_size;
+	info->SCpnt->request_bufflen -= bytes_transferred;
 
-			msgqueue_flush(&info->scsi.msgs);
-			msgqueue_addmsg(&info->scsi.msgs, 4,
-					EXTENDED_MESSAGE, 2, EXTENDED_WDTR,
-					msg[3]);
-			info->scsi.phase = PHASE_MSGOUT_EXPECT;
-			res = wide;
+	while (bytes_transferred != 0) {
+		if (SCp->this_residual > bytes_transferred)
 			break;
-
-		/* We initiated the wide data transfer negociation,
-		 * and have successfully received a response from the
-		 * target.  The synchronous transfer agreement has been
-		 * reached.  Note: if the values returned are out of our
-		 * bounds, we must reject the message.
+		/*
+		 * We have used up this buffer.  Move on to the
+		 * next buffer.
 		 */
-		case neg_inprogress:
-			res = reject;
-			if (msg[3] <= info->ifcfg.wide_max_size) {
-				dev->wide_state = neg_complete;
-				res = wide;
-			}
-			break;
+		bytes_transferred -= SCp->this_residual;
+		if (!next_SCp(SCp) && bytes_transferred) {
+			printk(KERN_WARNING "scsi%d.%c: out of buffers\n",
+				info->host->host_no, '0' + info->SCpnt->target);
+			return;
 		}
 	}
-#else
-	res = reject;
-#endif
-
-	switch (res) {
-	case wide:
-		dev->wide_xfer = msg[3];
-		break;
-
-	case reject:
-		outb(CMD_SETATN, REG_CMD(info));
-		msgqueue_flush(&info->scsi.msgs);
-		msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
-		info->scsi.phase = PHASE_MSGOUT_EXPECT;
-
-	case bit8:
-		dev->wide_xfer = 0;
-		break;
 
-	case none:
-		break;
-	}
+	SCp->this_residual -= bytes_transferred;
+	if (SCp->this_residual)
+		SCp->ptr += bytes_transferred;
+	else
+		SCp->ptr = NULL;
 }
 
-/* Function: void fas216_updateptrs(FAS216_Info *info, int bytes_transferred)
- * Purpose : update data pointers after transfer suspended/paused
- * Params  : info              - interface's local pointer to update
- *           bytes_transferred - number of bytes transferred
+/**
+ * fas216_pio - transfer data off of/on to card using programmed IO
+ * @info: interface to transfer data to/from
+ * @direction: direction to transfer data (DMA_OUT/DMA_IN)
+ *
+ * Transfer data off of/on to card using programmed IO.
+ * Notes: this is incredibly slow.
  */
-static void
-fas216_updateptrs(FAS216_Info *info, int bytes_transferred)
+static void fas216_pio(FAS216_Info *info, fasdmadir_t direction)
 {
-	unsigned char *ptr;
-	unsigned int residual;
+	Scsi_Pointer *SCp = &info->scsi.SCp;
 
 	fas216_checkmagic(info);
 
-	ptr = info->scsi.SCp.ptr;
-	residual = info->scsi.SCp.this_residual;
-
-	info->SCpnt->request_bufflen -= bytes_transferred;
-
-	while (residual <= bytes_transferred && bytes_transferred) {
-		/* We have used up this buffer */
-		bytes_transferred -= residual;
-		if (info->scsi.SCp.buffers_residual) {
-			info->scsi.SCp.buffer++;
-			info->scsi.SCp.buffers_residual--;
-			ptr = (unsigned char *)info->scsi.SCp.buffer->address;
-			residual = info->scsi.SCp.buffer->length;
-		} else {
-			ptr = NULL;
-			residual = 0;
-		}
-	}
+	if (direction == DMA_OUT)
+		fas216_writeb(info, REG_FF, get_next_SCp_byte(SCp));
+	else
+		put_next_SCp_byte(SCp, fas216_readb(info, REG_FF));
 
-	residual -= bytes_transferred;
-	ptr += bytes_transferred;
+	if (SCp->this_residual == 0)
+		next_SCp(SCp);
+}
 
-	if (residual == 0)
-		ptr = NULL;
+static void fas216_set_stc(FAS216_Info *info, unsigned int length)
+{
+	fas216_writeb(info, REG_STCL, length);
+	fas216_writeb(info, REG_STCM, length >> 8);
+	fas216_writeb(info, REG_STCH, length >> 16);
+}
 
-	info->scsi.SCp.ptr = ptr;
-	info->scsi.SCp.this_residual = residual;
+static unsigned int fas216_get_ctc(FAS216_Info *info)
+{
+	return fas216_readb(info, REG_CTCL) +
+	       (fas216_readb(info, REG_CTCM) << 8) +
+	       (fas216_readb(info, REG_CTCH) << 16);
 }
 
-/* Function: void fas216_pio(FAS216_Info *info, fasdmadir_t direction)
- * Purpose : transfer data off of/on to card using programmed IO
- * Params  : info      - interface to transfer data to/from
- *           direction - direction to transfer data (DMA_OUT/DMA_IN)
- * Notes   : this is incredibly slow
+/**
+ * fas216_cleanuptransfer - clean up after a transfer has completed.
+ * @info: interface to clean up
+ *
+ * Update the data pointers according to the number of bytes transferred
+ * on the SCSI bus.
  */
-static void
-fas216_pio(FAS216_Info *info, fasdmadir_t direction)
+static void fas216_cleanuptransfer(FAS216_Info *info)
 {
-	unsigned int residual;
-	char *ptr;
+	unsigned long total, residual, fifo;
+	fasdmatype_t dmatype = info->dma.transfer_type;
 
-	fas216_checkmagic(info);
+	info->dma.transfer_type = fasdma_none;
 
-	residual = info->scsi.SCp.this_residual;
-	ptr = info->scsi.SCp.ptr;
+	/*
+	 * PIO transfers do not need to be cleaned up.
+	 */
+	if (dmatype == fasdma_pio || dmatype == fasdma_none)
+		return;
 
-	if (direction == DMA_OUT)
-		outb(*ptr++, REG_FF(info));
+	if (dmatype == fasdma_real_all)
+		total = info->SCpnt->request_bufflen;
 	else
-		*ptr++ = inb(REG_FF(info));
+		total = info->scsi.SCp.this_residual;
 
-	residual -= 1;
+	residual = fas216_get_ctc(info);
 
-	if (residual == 0) {
-		if (info->scsi.SCp.buffers_residual) {
-			info->scsi.SCp.buffer++;
-			info->scsi.SCp.buffers_residual--;
-			ptr = (unsigned char *)info->scsi.SCp.buffer->address;
-			residual = info->scsi.SCp.buffer->length;
-		} else {
-			ptr = NULL;
-			residual = 0;
-		}
-	}
+	fifo = fas216_readb(info, REG_CFIS) & CFIS_CF;
 
-	info->scsi.SCp.ptr = ptr;
-	info->scsi.SCp.this_residual = residual;
+	fas216_log(info, LOG_BUFFER, "cleaning up from previous "
+		   "transfer: length 0x%06x, residual 0x%x, fifo %d",
+		   total, residual, fifo);
+
+	/*
+	 * If we were performing Data-Out, the transfer counter
+	 * counts down each time a byte is transferred by the
+	 * host to the FIFO.  This means we must include the
+	 * bytes left in the FIFO from the transfer counter.
+	 */
+	if (info->scsi.phase == PHASE_DATAOUT)
+		residual += fifo;
+
+	fas216_updateptrs(info, total - residual);
 }
 
-/* Function: void fas216_starttransfer(FAS216_Info *info,
- *				       fasdmadir_t direction)
- * Purpose : Start a DMA/PIO transfer off of/on to card
- * Params  : info      - interface from which device disconnected from
- *           direction - transfer direction (DMA_OUT/DMA_IN)
+/**
+ * fas216_transfer - Perform a DMA/PIO transfer off of/on to card
+ * @info: interface from which device disconnected from
+ *
+ * Start a DMA/PIO transfer off of/on to card
  */
-static void
-fas216_starttransfer(FAS216_Info *info, fasdmadir_t direction, int flush_fifo)
+static void fas216_transfer(FAS216_Info *info)
 {
+	fasdmadir_t direction;
 	fasdmatype_t dmatype;
 
-	fas216_checkmagic(info);
-
-	info->scsi.phase = (direction == DMA_OUT) ?
-				PHASE_DATAOUT : PHASE_DATAIN;
-
-	if (info->dma.transfer_type != fasdma_none &&
-	    info->dma.transfer_type != fasdma_pio) {
-		unsigned long total, residual;
-
-		if (info->dma.transfer_type == fasdma_real_all)
-			total = info->SCpnt->request_bufflen;
-		else
-			total = info->scsi.SCp.this_residual;
-
-		residual = (inb(REG_CFIS(info)) & CFIS_CF) +
-			    inb(REG_CTCL(info)) +
-			    (inb(REG_CTCM(info)) << 8) +
-			    (inb(REG_CTCH(info)) << 16);
-		fas216_updateptrs(info, total - residual);
-	}
-	info->dma.transfer_type = fasdma_none;
+	fas216_log(info, LOG_BUFFER,
+		   "starttransfer: buffer %p length 0x%06x reqlen 0x%06x",
+		   info->scsi.SCp.ptr, info->scsi.SCp.this_residual,
+		   info->SCpnt->request_bufflen);
 
 	if (!info->scsi.SCp.ptr) {
-		printk("scsi%d.%c: null buffer passed to "
-			"fas216_starttransfer\n", info->host->host_no,
-			fas216_target(info));
+		fas216_log(info, LOG_ERROR, "null buffer passed to "
+			   "fas216_starttransfer");
+		print_SCp(&info->scsi.SCp, "SCp: ", "\n");
+		print_SCp(&info->SCpnt->SCp, "Cmnd SCp: ", "\n");
 		return;
 	}
 
-	/* flush FIFO */
-	if (flush_fifo)
-		outb(CMD_FLUSHFIFO, REG_CMD(info));
-
 	/*
-	 * Default to PIO mode or DMA mode if we have a synchronous
-	 * transfer agreement.
+	 * If we have a synchronous transfer agreement in effect, we must
+	 * use DMA mode.  If we are using asynchronous transfers, we may
+	 * use DMA mode or PIO mode.
 	 */
-	if (info->device[info->SCpnt->target].sof && info->dma.setup)
+	if (info->device[info->SCpnt->target].sof)
 		dmatype = fasdma_real_all;
 	else
 		dmatype = fasdma_pio;
 
+	if (info->scsi.phase == PHASE_DATAOUT)
+		direction = DMA_OUT;
+	else
+		direction = DMA_IN;
+
 	if (info->dma.setup)
 		dmatype = info->dma.setup(info->host, &info->scsi.SCp,
 					  direction, dmatype);
 	info->dma.transfer_type = dmatype;
 
+	if (dmatype == fasdma_real_all)
+		fas216_set_stc(info, info->SCpnt->request_bufflen);
+	else
+		fas216_set_stc(info, info->scsi.SCp.this_residual);
+
 	switch (dmatype) {
 	case fasdma_pio:
-		outb(0, REG_SOF(info));
-		outb(info->scsi.async_stp, REG_STP(info));
-		outb(info->scsi.SCp.this_residual, REG_STCL(info));
-		outb(info->scsi.SCp.this_residual >> 8, REG_STCM(info));
-		outb(info->scsi.SCp.this_residual >> 16, REG_STCH(info));
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		fas216_log(info, LOG_BUFFER, "PIO transfer");
+		fas216_writeb(info, REG_SOF, 0);
+		fas216_writeb(info, REG_STP, info->scsi.async_stp);
+		fas216_cmd(info, CMD_TRANSFERINFO);
 		fas216_pio(info, direction);
 		break;
 
 	case fasdma_pseudo:
-		outb(info->scsi.SCp.this_residual, REG_STCL(info));
-		outb(info->scsi.SCp.this_residual >> 8, REG_STCM(info));
-		outb(info->scsi.SCp.this_residual >> 16, REG_STCH(info));
-		outb(CMD_TRANSFERINFO | CMD_WITHDMA, REG_CMD(info));
+		fas216_log(info, LOG_BUFFER, "pseudo transfer");
+		fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA);
 		info->dma.pseudo(info->host, &info->scsi.SCp,
 				 direction, info->SCpnt->transfersize);
 		break;
 
 	case fasdma_real_block:
-		outb(info->scsi.SCp.this_residual, REG_STCL(info));
-		outb(info->scsi.SCp.this_residual >> 8, REG_STCM(info));
-		outb(info->scsi.SCp.this_residual >> 16, REG_STCH(info));
-		outb(CMD_TRANSFERINFO | CMD_WITHDMA, REG_CMD(info));
+		fas216_log(info, LOG_BUFFER, "block dma transfer");
+		fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA);
 		break;
 
 	case fasdma_real_all:
-		outb(info->SCpnt->request_bufflen, REG_STCL(info));
-		outb(info->SCpnt->request_bufflen >> 8, REG_STCM(info));
-		outb(info->SCpnt->request_bufflen >> 16, REG_STCH(info));
-		outb(CMD_TRANSFERINFO | CMD_WITHDMA, REG_CMD(info));
+		fas216_log(info, LOG_BUFFER, "total dma transfer");
+		fas216_cmd(info, CMD_TRANSFERINFO | CMD_WITHDMA);
 		break;
 
 	default:
-		printk(KERN_ERR "scsi%d.%d: invalid FAS216 DMA type\n",
-		       info->host->host_no, fas216_target(info));
+		fas216_log(info, LOG_BUFFER | LOG_ERROR,
+			   "invalid FAS216 DMA type");
 		break;
 	}
 }
 
-/* Function: void fas216_stoptransfer(FAS216_Info *info)
- * Purpose : Stop a DMA transfer onto / off of the card
- * Params  : info      - interface from which device disconnected from
+/**
+ * fas216_stoptransfer - Stop a DMA transfer onto / off of the card
+ * @info: interface from which device disconnected from
+ *
+ * Called when we switch away from DATA IN or DATA OUT phases.
  */
-static void
-fas216_stoptransfer(FAS216_Info *info)
+static void fas216_stoptransfer(FAS216_Info *info)
 {
 	fas216_checkmagic(info);
 
-	if (info->dma.transfer_type != fasdma_none &&
-	    info->dma.transfer_type != fasdma_pio) {
-		unsigned long total, residual;
+	if (info->dma.transfer_type == fasdma_real_all ||
+	    info->dma.transfer_type == fasdma_real_block)
+		info->dma.stop(info->host, &info->scsi.SCp);
 
-		if ((info->dma.transfer_type == fasdma_real_all ||
-		     info->dma.transfer_type == fasdma_real_block) &&
-		    info->dma.stop)
-			info->dma.stop(info->host, &info->scsi.SCp);
+	fas216_cleanuptransfer(info);
 
-		if (info->dma.transfer_type == fasdma_real_all)
-			total = info->SCpnt->request_bufflen;
-		else
-			total = info->scsi.SCp.this_residual;
+	if (info->scsi.phase == PHASE_DATAIN) {
+		unsigned int fifo;
 
-		residual = (inb(REG_CFIS(info)) & CFIS_CF) +
-			    inb(REG_CTCL(info)) +
-			    (inb(REG_CTCM(info)) << 8) +
-			    (inb(REG_CTCH(info)) << 16);
-		fas216_updateptrs(info, total - residual);
-		info->dma.transfer_type = fasdma_none;
+		/*
+		 * If we were performing Data-In, then the FIFO counter
+		 * contains the number of bytes not transferred via DMA
+		 * from the on-board FIFO.  Read them manually.
+		 */
+		fifo = fas216_readb(info, REG_CFIS) & CFIS_CF;
+		while (fifo && info->scsi.SCp.ptr) {
+			*info->scsi.SCp.ptr = fas216_readb(info, REG_FF);
+			fas216_updateptrs(info, 1);
+			fifo--;
+		}
+	} else {
+		/*
+		 * After a Data-Out phase, there may be unsent
+		 * bytes left in the FIFO.  Flush them out.
+		 */
+		fas216_cmd(info, CMD_FLUSHFIFO);
 	}
-	if (info->scsi.phase == PHASE_DATAOUT)
-		outb(CMD_FLUSHFIFO, REG_CMD(info));
 }
 
-/* Function: void fas216_disconnected_intr(FAS216_Info *info)
- * Purpose : handle device disconnection
- * Params  : info - interface from which device disconnected from
+static void fas216_aborttransfer(FAS216_Info *info)
+{
+	fas216_checkmagic(info);
+
+	if (info->dma.transfer_type == fasdma_real_all ||
+	    info->dma.transfer_type == fasdma_real_block)
+		info->dma.stop(info->host, &info->scsi.SCp);
+
+	info->dma.transfer_type = fasdma_none;
+	fas216_cmd(info, CMD_FLUSHFIFO);
+}
+
+static void fas216_kick(FAS216_Info *info);
+
+/**
+ * fas216_disconnected_intr - handle device disconnection
+ * @info: interface from which device disconnected from
+ *
+ * Handle device disconnection
  */
-static void
-fas216_disconnect_intr(FAS216_Info *info)
+static void fas216_disconnect_intr(FAS216_Info *info)
 {
+	unsigned long flags;
+
 	fas216_checkmagic(info);
 
-#ifdef DEBUG_CONNECT
-	printk("scsi%d.%c: disconnect phase=%02X\n", info->host->host_no,
-		fas216_target(info), info->scsi.phase);
-#endif
+	fas216_log(info, LOG_CONNECT, "disconnect phase=%02x",
+		   info->scsi.phase);
+
 	msgqueue_flush(&info->scsi.msgs);
 
 	switch (info->scsi.phase) {
@@ -817,11 +863,13 @@
 		break;
 
 	case PHASE_MSGIN_DISCONNECT:		/* message in - disconnecting		*/
-		outb(CMD_ENABLESEL, REG_CMD(info));
 		info->scsi.disconnectable = 1;
-		info->scsi.reconnected.tag = 0;
 		info->scsi.phase = PHASE_IDLE;
 		info->stats.disconnects += 1;
+		spin_lock_irqsave(&info->host_lock, flags);
+		if (info->scsi.phase == PHASE_IDLE)
+			fas216_kick(info);
+		spin_unlock_irqrestore(&info->host_lock, flags);
 		break;
 
 	case PHASE_DONE:			/* at end of command - complete		*/
@@ -845,295 +893,131 @@
 	}
 }
 
-/* Function: void fas216_reselected_intr(FAS216_Info *info)
- * Purpose : Start reconnection of a device
- * Params  : info - interface which was reselected
+/**
+ * fas216_reselected_intr - start reconnection of a device
+ * @info: interface which was reselected
+ *
+ * Start reconnection of a device
  */
 static void
 fas216_reselected_intr(FAS216_Info *info)
 {
-	unsigned char target, identify_msg, ok;
+	unsigned int cfis, i;
+	unsigned char msg[4];
+	unsigned char target, lun, tag;
 
 	fas216_checkmagic(info);
 
-	if ((info->scsi.phase == PHASE_SELECTION ||
-	     info->scsi.phase == PHASE_SELSTEPS) && info->SCpnt) {
-		Scsi_Cmnd *SCpnt = info->SCpnt;
+	if (info->scsi.phase == PHASE_SELECTION ||
+	    info->scsi.phase == PHASE_SELSTEPS) {
+		fas216_log(info, LOG_ERROR, "wrong phase in reselected_intr\n");
+		return;
+	}
 
-		info->origSCpnt = SCpnt;
-		info->SCpnt = NULL;
+	cfis = fas216_readb(info, REG_CFIS);
 
-		if (info->device[SCpnt->target].wide_state == neg_inprogress)
-			info->device[SCpnt->target].wide_state = neg_wait;
-		if (info->device[SCpnt->target].sync_state == neg_inprogress)
-			info->device[SCpnt->target].sync_state = neg_wait;
-	}
+	fas216_log(info, LOG_CONNECT, "reconnect phase=%02x cfis=%02x",
+		   info->scsi.phase, cfis);
 
-#ifdef DEBUG_CONNECT
-	printk("scsi%d.%c: reconnect phase=%02X\n", info->host->host_no,
-		fas216_target(info), info->scsi.phase);
-#endif
+	cfis &= CFIS_CF;
 
-	if ((inb(REG_CFIS(info)) & CFIS_CF) != 2) {
+	if (cfis < 2 || cfis > 4) {
 		printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n",
 			info->host->host_no);
-		outb(CMD_SETATN, REG_CMD(info));
-		outb(CMD_MSGACCEPTED, REG_CMD(info));
-		msgqueue_flush(&info->scsi.msgs);
-		msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
-		info->scsi.phase = PHASE_MSGOUT_EXPECT;
-		return;
-	}
-
-	target = inb(REG_FF(info));
-	identify_msg = inb(REG_FF(info));
-
-	ok = 1;
-	if (!(target & (1 << info->host->this_id))) {
-		printk(KERN_ERR "scsi%d.H: invalid host id on reselect\n", info->host->host_no);
-		ok = 0;
+		goto bad_message;
 	}
 
-	if (!(identify_msg & 0x80)) {
-		printk(KERN_ERR "scsi%d.H: no IDENTIFY message on reselect, got msg %02X\n",
-			info->host->host_no, identify_msg);
-		ok = 0;
-	}
+	for (i = 0; i < cfis; i++)
+		msg[i] = fas216_readb(info, REG_FF);
 
-	if (!ok) {
-		/*
-		 * Something went wrong - send an initiator error to
-		 * the target.
-		 */
-		outb(CMD_SETATN, REG_CMD(info));
-		outb(CMD_MSGACCEPTED, REG_CMD(info));
-		msgqueue_flush(&info->scsi.msgs);
-		msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
-		info->scsi.phase = PHASE_MSGOUT_EXPECT;
-		return;
-	}
+	if (!(msg[0] & (1 << info->host->this_id)) ||
+	    !(msg[1] & 0x80))
+		goto initiator_error;
 
-	target &= ~(1 << info->host->this_id);
-	switch (target) {
-	case   1:  target = 0; break;
-	case   2:  target = 1; break;
-	case   4:  target = 2; break;
-	case   8:  target = 3; break;
-	case  16:  target = 4; break;
-	case  32:  target = 5; break;
-	case  64:  target = 6; break;
-	case 128:  target = 7; break;
-	default:   target = info->host->this_id; break;
-	}
-
-	identify_msg &= 7;
-	info->scsi.reconnected.target = target;
-	info->scsi.reconnected.lun    = identify_msg;
-	info->scsi.reconnected.tag    = 0;
-
-	ok = 0;
-	if (info->scsi.disconnectable && info->SCpnt &&
-	    info->SCpnt->target == target && info->SCpnt->lun == identify_msg)
-		ok = 1;
+	target = msg[0] & ~(1 << info->host->this_id);
+	target = ffs(target) - 1;
+	lun = msg[1] & 7;
+	tag = 0;
 
-	if (!ok && queue_probetgtlun(&info->queues.disconnected, target, identify_msg))
-		ok = 1;
+	if (cfis >= 3) {
+		if (msg[2] != SIMPLE_QUEUE_TAG)
+			goto initiator_error;
 
-	msgqueue_flush(&info->scsi.msgs);
-	if (ok) {
-		info->scsi.phase = PHASE_RECONNECTED;
-		outb(target, REG_SDID(info));
-	} else {
-		/*
-		 * Our command structure not found - abort the
-		 * command on the target.  Since we have no
-		 * record of this command, we can't send
-		 * an INITIATOR DETECTED ERROR message.
-		 */
-		outb(CMD_SETATN, REG_CMD(info));
-		msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
-		info->scsi.phase = PHASE_MSGOUT_EXPECT;
+		tag = msg[3];
 	}
 
-	outb(CMD_MSGACCEPTED, REG_CMD(info));
-}
-
-/* Function: void fas216_finish_reconnect(FAS216_Info *info)
- * Purpose : finish reconnection sequence for device
- * Params  : info - interface which caused function done interrupt
- */
-static void
-fas216_finish_reconnect(FAS216_Info *info)
-{
-	fas216_checkmagic(info);
+	/* set up for synchronous transfers */
+	fas216_writeb(info, REG_SDID, target);
+	fas216_set_sync(info, target);
+	msgqueue_flush(&info->scsi.msgs);
 
-#ifdef DEBUG_CONNECT
-	printk("Connected: %1X %1X %02X, reconnected: %1X %1X %02X\n",
-		info->SCpnt->target, info->SCpnt->lun, info->SCpnt->tag,
-		info->scsi.reconnected.target, info->scsi.reconnected.lun,
-		info->scsi.reconnected.tag);
-#endif
+	fas216_log(info, LOG_CONNECT, "Reconnected: target %1x lun %1x tag %02x",
+		   target, lun, tag);
 
 	if (info->scsi.disconnectable && info->SCpnt) {
 		info->scsi.disconnectable = 0;
-		if (info->SCpnt->target == info->scsi.reconnected.target &&
-		    info->SCpnt->lun    == info->scsi.reconnected.lun &&
-		    info->SCpnt->tag    == info->scsi.reconnected.tag) {
-#ifdef DEBUG_CONNECT
-			printk("scsi%d.%c: reconnected",
-				info->host->host_no, fas216_target(info));
-#endif
+		if (info->SCpnt->target == target &&
+		    info->SCpnt->lun    == lun &&
+		    info->SCpnt->tag    == tag) {
+			fas216_log(info, LOG_CONNECT, "reconnected previously connected command");
 		} else {
 			queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt);
-#ifdef DEBUG_CONNECT
-			printk("scsi%d.%c: had to move command to disconnected queue\n",
-				info->host->host_no, fas216_target(info));
-#endif
+			fas216_log(info, LOG_CONNECT, "had to move command to disconnected queue");
 			info->SCpnt = NULL;
 		}
 	}
 	if (!info->SCpnt) {
 		info->SCpnt = queue_remove_tgtluntag(&info->queues.disconnected,
-					info->scsi.reconnected.target,
-					info->scsi.reconnected.lun,
-					info->scsi.reconnected.tag);
-#ifdef DEBUG_CONNECT
-		printk("scsi%d.%c: had to get command",
-			info->host->host_no, fas216_target(info));
-#endif
+					target, lun, tag);
+		fas216_log(info, LOG_CONNECT, "had to get command");
 	}
-	if (!info->SCpnt) {
-		outb(CMD_SETATN, REG_CMD(info));
-		msgqueue_flush(&info->scsi.msgs);
-#if 0
-		if (info->scsi.reconnected.tag)
-			msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, info->scsi.reconnected.tag);
-		else
-#endif
-			msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
-		info->scsi.phase = PHASE_MSGOUT_EXPECT;
-		info->scsi.aborting = 1;
-	} else {
+
+	if (info->SCpnt) {
 		/*
 		 * Restore data pointer from SAVED data pointer
 		 */
 		info->scsi.SCp = info->SCpnt->SCp;
-#ifdef DEBUG_CONNECT
-		printk(", data pointers: [%p, %X]",
-			info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
-#endif
-	}
-#ifdef DEBUG_CONNECT
-	printk("\n");
-#endif
-}
 
-static int fas216_wait_cmd(FAS216_Info *info, int cmd)
-{
-	int tout;
-	int stat;
-
-	outb(cmd, REG_CMD(info));
+		fas216_log(info, LOG_CONNECT, "data pointers: [%p, %X]",
+			info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+		info->scsi.phase = PHASE_MSGIN;
+	} else {
+		/*
+		 * Our command structure not found - abort the
+		 * command on the target.  Since we have no
+		 * record of this command, we can't send
+		 * an INITIATOR DETECTED ERROR message.
+		 */
+		fas216_cmd(info, CMD_SETATN);
 
-	for (tout = 1000; tout; tout -= 1) {
-		stat = inb(REG_STAT(info));
-		if (stat & STAT_INT)
-			break;
-		udelay(1);
+		if (tag)
+			msgqueue_addmsg(&info->scsi.msgs, 2, ABORT_TAG, tag);
+		else
+			msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
+		info->scsi.phase = PHASE_MSGOUT_EXPECT;
+		info->scsi.aborting = 1;
 	}
 
-	return stat;
-}
-
-static int fas216_get_msg_byte(FAS216_Info *info)
-{
-	int stat;
-
-	stat = fas216_wait_cmd(info, CMD_MSGACCEPTED);
-
-	if ((stat & STAT_INT) == 0)
-		goto timedout;
-
-	if ((stat & STAT_BUSMASK) != STAT_MESGIN)
-		goto unexpected_phase_change;
-
-	inb(REG_INST(info));
-
-	stat = fas216_wait_cmd(info, CMD_TRANSFERINFO);
-
-	if ((stat & STAT_INT) == 0)
-		goto timedout;
-
-	if ((stat & STAT_BUSMASK) != STAT_MESGIN)
-		goto unexpected_phase_change;
-
-	inb(REG_INST(info));
-
-	return inb(REG_FF(info));
-
-timedout:
-	printk("scsi%d.%c: timed out waiting for message byte\n",
-		info->host->host_no, fas216_target(info));
-	return -1;
-
-unexpected_phase_change:
-	printk("scsi%d.%c: unexpected phase change: status = %02X\n",
-		info->host->host_no, fas216_target(info), stat);
+	fas216_cmd(info, CMD_MSGACCEPTED);
+	return;
 
-	return -2;
+ initiator_error:
+	printk(KERN_ERR "scsi%d.H: error during reselection: bytes",
+		info->host->host_no);
+	for (i = 0; i < cfis; i++)
+		printk(" %02x", msg[i]);
+	printk("\n");
+ bad_message:
+	fas216_cmd(info, CMD_SETATN);
+	msgqueue_flush(&info->scsi.msgs);
+	msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+	info->scsi.phase = PHASE_MSGOUT_EXPECT;
+	fas216_cmd(info, CMD_MSGACCEPTED);
 }
 
-/* Function: void fas216_message(FAS216_Info *info)
- * Purpose : handle a function done interrupt from FAS216 chip
- * Params  : info - interface which caused function done interrupt
- */
-static void fas216_message(FAS216_Info *info)
+static void fas216_parse_message(FAS216_Info *info, unsigned char *message, int msglen)
 {
-	unsigned char *message = info->scsi.message;
-	unsigned int msglen = 1, i;
-	int msgbyte = 0;
-
-	fas216_checkmagic(info);
-
-	message[0] = inb(REG_FF(info));
-
-	if (message[0] == EXTENDED_MESSAGE) {
-		msgbyte = fas216_get_msg_byte(info);
-
-		if (msgbyte >= 0) {
-			message[1] = msgbyte;
-
-			for (msglen = 2; msglen < message[1] + 2; msglen++) {
-				msgbyte = fas216_get_msg_byte(info);
-
-				if (msgbyte >= 0)
-					message[msglen] = msgbyte;
-				else
-					break;
-			}
-		}
-	}
-
-	info->scsi.msglen = msglen;
-
-#ifdef DEBUG_MESSAGES
-	{
-		int i;
-
-		printk("scsi%d.%c: message in: ",
-			info->host->host_no, fas216_target(info));
-		for (i = 0; i < msglen; i++)
-			printk("%02X ", message[i]);
-		printk("\n");
-	}
-#endif
-
-	if (info->scsi.phase == PHASE_RECONNECTED) {
-		if (message[0] == SIMPLE_QUEUE_TAG)
-			info->scsi.reconnected.tag = message[1];
-		fas216_finish_reconnect(info);
-		info->scsi.phase = PHASE_MSGIN;
-	}
+	int i;
 
 	switch (message[0]) {
 	case COMMAND_COMPLETE:
@@ -1159,11 +1043,9 @@
 		 */
 		info->SCpnt->SCp = info->scsi.SCp;
 		info->SCpnt->SCp.sent_command = 0;
-#if defined (DEBUG_MESSAGES) || defined (DEBUG_CONNECT)
-		printk("scsi%d.%c: save data pointers: [%p, %X]\n",
-			info->host->host_no, fas216_target(info),
+		fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER,
+			"save data pointers: [%p, %X]",
 			info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
-#endif
 		break;
 
 	case RESTORE_POINTERS:
@@ -1174,11 +1056,9 @@
 		 * Restore current data pointer from SAVED data pointer
 		 */
 		info->scsi.SCp = info->SCpnt->SCp;
-#if defined (DEBUG_MESSAGES) || defined (DEBUG_CONNECT)
-		printk("scsi%d.%c: restore data pointers: [%p, %X]\n",
-			info->host->host_no, fas216_target(info),
+		fas216_log(info, LOG_CONNECT | LOG_MESSAGES | LOG_BUFFER,
+			"restore data pointers: [%p, 0x%x]",
 			info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
-#endif
 		break;
 
 	case DISCONNECT:
@@ -1197,13 +1077,8 @@
 			fas216_handlesync(info, message);
 			break;
 
-		case EXTENDED_MESSAGE | EXTENDED_WDTR << 8:
-			fas216_handlewide(info, message);
-			break;
-
 		default:
-			printk("scsi%d.%c: reject, last message %04X\n",
-				info->host->host_no, fas216_target(info),
+			fas216_log(info, 0, "reject, last message 0x%04x",
 				fas216_get_last_msg(info, info->scsi.msgin_fifo));
 		}
 		break;
@@ -1211,29 +1086,15 @@
 	case NOP:
 		break;
 
-	case SIMPLE_QUEUE_TAG:
-		if (msglen < 2)
-			goto unrecognised;
-
-		/* handled above - print a warning since this is untested */
-		printk("scsi%d.%c: reconnect queue tag %02X\n",
-			info->host->host_no, fas216_target(info),
-			message[1]);
-		break;
-
 	case EXTENDED_MESSAGE:
 		if (msglen < 3)
 			goto unrecognised;
 
 		switch (message[2]) {
-		case EXTENDED_SDTR:	/* Sync transfer negociation request/reply */
+		case EXTENDED_SDTR:	/* Sync transfer negotiation request/reply */
 			fas216_handlesync(info, message);
 			break;
 
-		case EXTENDED_WDTR:	/* Wide transfer negociation request/reply */
-			fas216_handlewide(info, message);
-			break;
-
 		default:
 			goto unrecognised;
 		}
@@ -1242,12 +1103,10 @@
 	default:
 		goto unrecognised;
 	}
-	outb(CMD_MSGACCEPTED, REG_CMD(info));
 	return;
 
 unrecognised:
-	printk("scsi%d.%c: unrecognised message, rejecting\n",
-		info->host->host_no, fas216_target(info));
+	fas216_log(info, 0, "unrecognised message, rejecting");
 	printk("scsi%d.%c: message was", info->host->host_no, fas216_target(info));
 	for (i = 0; i < msglen; i++)
 		printk("%s%02X", i & 31 ? " " : "\n  ", message[i]);
@@ -1258,19 +1117,138 @@
 	 * I can't use SETATN since the chip gives me an
 	 * invalid command interrupt when I do.  Weird.
 	 */
-outb(CMD_NOP, REG_CMD(info));
+fas216_cmd(info, CMD_NOP);
 fas216_dumpstate(info);
-	outb(CMD_SETATN, REG_CMD(info));
+	fas216_cmd(info, CMD_SETATN);
 	msgqueue_flush(&info->scsi.msgs);
 	msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
 	info->scsi.phase = PHASE_MSGOUT_EXPECT;
 fas216_dumpstate(info);
-	outb(CMD_MSGACCEPTED, REG_CMD(info));
 }
 
-/* Function: void fas216_send_command(FAS216_Info *info)
- * Purpose : send a command to a target after all message bytes have been sent
- * Params  : info - interface which caused bus service
+static int fas216_wait_cmd(FAS216_Info *info, int cmd)
+{
+	int tout;
+	int stat;
+
+	fas216_cmd(info, cmd);
+
+	for (tout = 1000; tout; tout -= 1) {
+		stat = fas216_readb(info, REG_STAT);
+		if (stat & (STAT_INT|STAT_PARITYERROR))
+			break;
+		udelay(1);
+	}
+
+	return stat;
+}
+
+static int fas216_get_msg_byte(FAS216_Info *info)
+{
+	unsigned int stat = fas216_wait_cmd(info, CMD_MSGACCEPTED);
+
+	if ((stat & STAT_INT) == 0)
+		goto timedout;
+
+	if ((stat & STAT_BUSMASK) != STAT_MESGIN)
+		goto unexpected_phase_change;
+
+	fas216_readb(info, REG_INST);
+
+	stat = fas216_wait_cmd(info, CMD_TRANSFERINFO);
+
+	if ((stat & STAT_INT) == 0)
+		goto timedout;
+
+	if (stat & STAT_PARITYERROR)
+		goto parity_error;
+
+	if ((stat & STAT_BUSMASK) != STAT_MESGIN)
+		goto unexpected_phase_change;
+
+	fas216_readb(info, REG_INST);
+
+	return fas216_readb(info, REG_FF);
+
+timedout:
+	fas216_log(info, LOG_ERROR, "timed out waiting for message byte");
+	return -1;
+
+unexpected_phase_change:
+	fas216_log(info, LOG_ERROR, "unexpected phase change: status = %02x", stat);
+	return -2;
+
+parity_error:
+	fas216_log(info, LOG_ERROR, "parity error during message in phase");
+	return -3;
+}
+
+/**
+ * fas216_message - handle a function done interrupt from FAS216 chip
+ * @info: interface which caused function done interrupt
+ *
+ * Handle a function done interrupt from FAS216 chip
+ */
+static void fas216_message(FAS216_Info *info)
+{
+	unsigned char *message = info->scsi.message;
+	unsigned int msglen = 1;
+	int msgbyte = 0;
+
+	fas216_checkmagic(info);
+
+	message[0] = fas216_readb(info, REG_FF);
+
+	if (message[0] == EXTENDED_MESSAGE) {
+		msgbyte = fas216_get_msg_byte(info);
+
+		if (msgbyte >= 0) {
+			message[1] = msgbyte;
+
+			for (msglen = 2; msglen < message[1] + 2; msglen++) {
+				msgbyte = fas216_get_msg_byte(info);
+
+				if (msgbyte >= 0)
+					message[msglen] = msgbyte;
+				else
+					break;
+			}
+		}
+	}
+
+	if (msgbyte == -3)
+		goto parity_error;
+
+#ifdef DEBUG_MESSAGES
+	{
+		int i;
+
+		printk("scsi%d.%c: message in: ",
+			info->host->host_no, fas216_target(info));
+		for (i = 0; i < msglen; i++)
+			printk("%02X ", message[i]);
+		printk("\n");
+	}
+#endif
+
+	fas216_parse_message(info, message, msglen);
+	fas216_cmd(info, CMD_MSGACCEPTED);
+	return;
+
+parity_error:
+	fas216_cmd(info, CMD_SETATN);
+	msgqueue_flush(&info->scsi.msgs);
+	msgqueue_addmsg(&info->scsi.msgs, 1, MSG_PARITY_ERROR);
+	info->scsi.phase = PHASE_MSGOUT_EXPECT;
+	fas216_cmd(info, CMD_MSGACCEPTED);
+	return;
+}
+
+/**
+ * fas216_send_command - send command after all message bytes have been sent
+ * @info: interface which caused bus service
+ *
+ * Send a command to a target after all message bytes have been sent
  */
 static void fas216_send_command(FAS216_Info *info)
 {
@@ -1278,22 +1256,24 @@
 
 	fas216_checkmagic(info);
 
-	outb(CMD_NOP|CMD_WITHDMA, REG_CMD(info));
-	outb(CMD_FLUSHFIFO, REG_CMD(info));
+	fas216_cmd(info, CMD_NOP|CMD_WITHDMA);
+	fas216_cmd(info, CMD_FLUSHFIFO);
 
 	/* load command */
 	for (i = info->scsi.SCp.sent_command; i < info->SCpnt->cmd_len; i++)
-		outb(info->SCpnt->cmnd[i], REG_FF(info));
+		fas216_writeb(info, REG_FF, info->SCpnt->cmnd[i]);
 
-	outb(CMD_TRANSFERINFO, REG_CMD(info));
+	fas216_cmd(info, CMD_TRANSFERINFO);
 
 	info->scsi.phase = PHASE_COMMAND;
 }
 
-/* Function: void fas216_send_messageout(FAS216_Info *info, int start)
- * Purpose : handle bus service to send a message
- * Params  : info - interface which caused bus service
- * Note    : We do not allow the device to change the data direction!
+/**
+ * fas216_send_messageout - handle bus service to send a message
+ * @info: interface which caused bus service
+ *
+ * Handle bus service to send a message.
+ * Note: We do not allow the device to change the data direction!
  */
 static void fas216_send_messageout(FAS216_Info *info, int start)
 {
@@ -1301,7 +1281,7 @@
 
 	fas216_checkmagic(info);
 
-	outb(CMD_FLUSHFIFO, REG_CMD(info));
+	fas216_cmd(info, CMD_FLUSHFIFO);
 
 	if (tot_msglen) {
 		struct message *msg;
@@ -1311,42 +1291,43 @@
 			int i;
 
 			for (i = start; i < msg->length; i++)
-				outb(msg->msg[i], REG_FF(info));
+				fas216_writeb(info, REG_FF, msg->msg[i]);
 
-			msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF);
+			msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF);
 			start = 0;
 		}
 	} else
-		outb(NOP, REG_FF(info));
+		fas216_writeb(info, REG_FF, NOP);
 
-	outb(CMD_TRANSFERINFO, REG_CMD(info));
+	fas216_cmd(info, CMD_TRANSFERINFO);
 
 	info->scsi.phase = PHASE_MSGOUT;
 }
 
-/* Function: void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
- * Purpose : handle a bus service interrupt from FAS216 chip
- * Params  : info - interface which caused bus service interrupt
- *           stat - Status register contents
- *           ssr  - SCSI Status register contents
+/**
+ * fas216_busservice_intr - handle bus service interrupt from FAS216 chip
+ * @info: interface which caused bus service interrupt
+ * @stat: Status register contents
+ * @is: SCSI Status register contents
+ *
+ * Handle a bus service interrupt from FAS216 chip
  */
-static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
+static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int is)
 {
 	fas216_checkmagic(info);
 
-#ifdef DEBUG_BUSSERVICE
-	printk("scsi%d.%c: bus service: stat=%02X ssr=%02X phase=%02X\n",
-		info->host->host_no, fas216_target(info), stat, ssr, info->scsi.phase);
-#endif
+	fas216_log(info, LOG_BUSSERVICE,
+		   "bus service: stat=%02x is=%02x phase=%02x",
+		   stat, is, info->scsi.phase);
 
 	switch (info->scsi.phase) {
 	case PHASE_SELECTION:
-		if ((ssr & IS_BITS) != 1)
+		if ((is & IS_BITS) != IS_MSGBYTESENT)
 			goto bad_is;
 		break;
 
 	case PHASE_SELSTEPS:
-		switch (ssr & IS_BITS) {
+		switch (is & IS_BITS) {
 		case IS_SELARB:
 		case IS_MSGBYTESENT:
 			goto bad_is;
@@ -1365,42 +1346,36 @@
 		break;
 	}
 
-	outb(CMD_NOP, REG_CMD(info));
+	fas216_cmd(info, CMD_NOP);
 
 #define STATE(st,ph) ((ph) << 3 | (st))
 	/* This table describes the legal SCSI state transitions,
 	 * as described by the SCSI II spec.
 	 */
 	switch (STATE(stat & STAT_BUSMASK, info->scsi.phase)) {
-						/* Reselmsgin   -> Data In	*/
-	case STATE(STAT_DATAIN, PHASE_RECONNECTED):
-		fas216_finish_reconnect(info);
 	case STATE(STAT_DATAIN, PHASE_SELSTEPS):/* Sel w/ steps -> Data In      */
-	case STATE(STAT_DATAIN, PHASE_DATAIN):  /* Data In      -> Data In      */
 	case STATE(STAT_DATAIN, PHASE_MSGOUT):  /* Message Out  -> Data In      */
 	case STATE(STAT_DATAIN, PHASE_COMMAND): /* Command      -> Data In      */
 	case STATE(STAT_DATAIN, PHASE_MSGIN):   /* Message In   -> Data In      */
-		fas216_starttransfer(info, DMA_IN, 0);
+		info->scsi.phase = PHASE_DATAIN;
+		fas216_transfer(info);
 		return;
 
+	case STATE(STAT_DATAIN, PHASE_DATAIN):  /* Data In      -> Data In      */
 	case STATE(STAT_DATAOUT, PHASE_DATAOUT):/* Data Out     -> Data Out     */
-		fas216_starttransfer(info, DMA_OUT, 0);
+		fas216_cleanuptransfer(info);
+		fas216_transfer(info);
 		return;
 
-						/* Reselmsgin   -> Data Out     */
-	case STATE(STAT_DATAOUT, PHASE_RECONNECTED):
-		fas216_finish_reconnect(info);
 	case STATE(STAT_DATAOUT, PHASE_SELSTEPS):/* Sel w/ steps-> Data Out     */
 	case STATE(STAT_DATAOUT, PHASE_MSGOUT): /* Message Out  -> Data Out     */
 	case STATE(STAT_DATAOUT, PHASE_COMMAND):/* Command      -> Data Out     */
 	case STATE(STAT_DATAOUT, PHASE_MSGIN):  /* Message In   -> Data Out     */
-		fas216_starttransfer(info, DMA_OUT, 1);
+		fas216_cmd(info, CMD_FLUSHFIFO);
+		info->scsi.phase = PHASE_DATAOUT;
+		fas216_transfer(info);
 		return;
 
-						/* Reselmsgin   -> Status       */
-	case STATE(STAT_STATUS, PHASE_RECONNECTED):
-		fas216_finish_reconnect(info);
-		goto status;
 	case STATE(STAT_STATUS, PHASE_DATAOUT): /* Data Out     -> Status       */
 	case STATE(STAT_STATUS, PHASE_DATAIN):  /* Data In      -> Status       */
 		fas216_stoptransfer(info);
@@ -1408,8 +1383,7 @@
 	case STATE(STAT_STATUS, PHASE_MSGOUT):  /* Message Out  -> Status       */
 	case STATE(STAT_STATUS, PHASE_COMMAND): /* Command      -> Status       */
 	case STATE(STAT_STATUS, PHASE_MSGIN):   /* Message In   -> Status       */
-	status:
-		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
+		fas216_cmd(info, CMD_INITCMDCOMPLETE);
 		info->scsi.phase = PHASE_STATUS;
 		return;
 
@@ -1419,32 +1393,59 @@
 	case STATE(STAT_MESGIN, PHASE_COMMAND):	/* Command	-> Message In	*/
 	case STATE(STAT_MESGIN, PHASE_SELSTEPS):/* Sel w/ steps -> Message In   */
 	case STATE(STAT_MESGIN, PHASE_MSGOUT):  /* Message Out  -> Message In   */
-		info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF;
-		outb(CMD_FLUSHFIFO, REG_CMD(info));
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF;
+		fas216_cmd(info, CMD_FLUSHFIFO);
+		fas216_cmd(info, CMD_TRANSFERINFO);
 		info->scsi.phase = PHASE_MSGIN;
 		return;
 
-						/* Reselmsgin   -> Message In   */
-	case STATE(STAT_MESGIN, PHASE_RECONNECTED):
 	case STATE(STAT_MESGIN, PHASE_MSGIN):
-		info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF;
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		info->scsi.msgin_fifo = fas216_readb(info, REG_CFIS) & CFIS_CF;
+		fas216_cmd(info, CMD_TRANSFERINFO);
 		return;
 
-						/* Reselmsgin   -> Command      */
-	case STATE(STAT_COMMAND, PHASE_RECONNECTED):
-		fas216_finish_reconnect(info);
 	case STATE(STAT_COMMAND, PHASE_MSGOUT): /* Message Out  -> Command      */
 	case STATE(STAT_COMMAND, PHASE_MSGIN):  /* Message In   -> Command      */
 		fas216_send_command(info);
 		info->scsi.phase = PHASE_COMMAND;
 		return;
-						/* Selection    -> Message Out  */
+
+
+	/*
+	 * Selection    -> Message Out
+	 */
 	case STATE(STAT_MESGOUT, PHASE_SELECTION):
 		fas216_send_messageout(info, 1);
 		return;
-						/* Any          -> Message Out  */
+
+	/*
+	 * Message Out  -> Message Out
+	 */
+	case STATE(STAT_MESGOUT, PHASE_SELSTEPS):
+	case STATE(STAT_MESGOUT, PHASE_MSGOUT):
+		/*
+		 * If we get another message out phase, this usually
+		 * means some parity error occurred.  Resend complete
+		 * set of messages.  If we have more than one byte to
+		 * send, we need to assert ATN again.
+		 */
+		if (info->device[info->SCpnt->target].parity_check) {
+			/*
+			 * We were testing... good, the device
+			 * supports parity checking.
+			 */
+			info->device[info->SCpnt->target].parity_check = 0;
+			info->device[info->SCpnt->target].parity_enabled = 1;
+			fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]);
+		}
+
+		if (msgqueue_msglength(&info->scsi.msgs) > 1)
+			fas216_cmd(info, CMD_SETATN);
+		/*FALLTHROUGH*/
+
+	/*
+	 * Any          -> Message Out
+	 */
 	case STATE(STAT_MESGOUT, PHASE_MSGOUT_EXPECT):
 		fas216_send_messageout(info, 0);
 		return;
@@ -1463,30 +1464,13 @@
 		printk(KERN_ERR "scsi%d.%c: "
 			"target trying to receive more command bytes\n",
 			info->host->host_no, fas216_target(info));
-		outb(CMD_SETATN, REG_CMD(info));
-		outb(15, REG_STCL(info));
-		outb(0, REG_STCM(info));
-		outb(0, REG_STCH(info));
-		outb(CMD_PADBYTES | CMD_WITHDMA, REG_CMD(info));
+		fas216_cmd(info, CMD_SETATN);
+		fas216_set_stc(info, 15);
+		fas216_cmd(info, CMD_PADBYTES | CMD_WITHDMA);
 		msgqueue_flush(&info->scsi.msgs);
 		msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
 		info->scsi.phase = PHASE_MSGOUT_EXPECT;
 		return;
-
-						/* Selection    -> Message Out  */
-	case STATE(STAT_MESGOUT, PHASE_SELSTEPS):
-	case STATE(STAT_MESGOUT, PHASE_MSGOUT): /* Message Out  -> Message Out  */
-		/* If we get another message out phase, this
-		 * usually means some parity error occurred.
-		 * Resend complete set of messages.  If we have
-		 * more than 1 byte to send, we need to assert
-		 * ATN again.
-		 */
-		if (msgqueue_msglength(&info->scsi.msgs) > 1)
-			outb(CMD_SETATN, REG_CMD(info));
-
-		fas216_send_messageout(info, 0);
-		return;
 	}
 
 	if (info->scsi.phase == PHASE_MSGIN_DISCONNECT) {
@@ -1494,11 +1478,11 @@
 			info->host->host_no, fas216_target(info),
 			fas216_bus_phase(stat));
 		msgqueue_flush(&info->scsi.msgs);
-		outb(CMD_SETATN, REG_CMD(info));
+		fas216_cmd(info, CMD_SETATN);
 		msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
 		info->scsi.phase = PHASE_MSGOUT_EXPECT;
 		info->scsi.aborting = 1;
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		fas216_cmd(info, CMD_TRANSFERINFO);
 		return;
 	}
 	printk(KERN_ERR "scsi%d.%c: bus phase %s after %s?\n",
@@ -1509,191 +1493,245 @@
 	return;
 
 bad_is:
-	printk("scsi%d.%c: bus service at step %d?\n",
-		info->host->host_no, fas216_target(info),
-		ssr & IS_BITS);
+	fas216_log(info, 0, "bus service at step %d?", is & IS_BITS);
+	fas216_dumpstate(info);
 	print_debug_list();
 
 	fas216_done(info, DID_ERROR);
 }
 
-/* Function: void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
- * Purpose : handle a function done interrupt from FAS216 chip
- * Params  : info - interface which caused function done interrupt
- *           stat - Status register contents
- *           ssr  - SCSI Status register contents
+/**
+ * fas216_funcdone_intr - handle a function done interrupt from FAS216 chip
+ * @info: interface which caused function done interrupt
+ * @stat: Status register contents
+ * @is: SCSI Status register contents
+ *
+ * Handle a function done interrupt from FAS216 chip
  */
-static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
+static void fas216_funcdone_intr(FAS216_Info *info, unsigned int stat, unsigned int is)
 {
-	int status, message;
+	unsigned int fifo_len = fas216_readb(info, REG_CFIS) & CFIS_CF;
 
 	fas216_checkmagic(info);
 
-#ifdef DEBUG_FUNCTIONDONE
-	printk("scsi%d.%c: function done: stat=%X ssr=%X phase=%02X\n",
-		info->host->host_no, fas216_target(info), stat, ssr, info->scsi.phase);
-#endif
+	fas216_log(info, LOG_FUNCTIONDONE,
+		   "function done: stat=%02x is=%02x phase=%02x",
+		   stat, is, info->scsi.phase);
+
 	switch (info->scsi.phase) {
 	case PHASE_STATUS:			/* status phase - read status and msg	*/
-		status = inb(REG_FF(info));
-		message = inb(REG_FF(info));
-		info->scsi.SCp.Message = message;
-		info->scsi.SCp.Status = status;
+		if (fifo_len != 2) {
+			fas216_log(info, 0, "odd number of bytes in FIFO: %d", fifo_len);
+		}
+		/*
+		 * Read status then message byte.
+		 */
+		info->scsi.SCp.Status = fas216_readb(info, REG_FF);
+		info->scsi.SCp.Message = fas216_readb(info, REG_FF);
 		info->scsi.phase = PHASE_DONE;
-		outb(CMD_MSGACCEPTED, REG_CMD(info));
+		fas216_cmd(info, CMD_MSGACCEPTED);
+		break;
+
+	case PHASE_IDLE:
+	case PHASE_SELECTION:
+	case PHASE_SELSTEPS:
 		break;
 
-	case PHASE_IDLE:			/* reselected?				*/
 	case PHASE_MSGIN:			/* message in phase			*/
-	case PHASE_RECONNECTED:			/* reconnected command			*/
 		if ((stat & STAT_BUSMASK) == STAT_MESGIN) {
-			info->scsi.msgin_fifo = inb(REG_CFIS(info)) & CFIS_CF;
+			info->scsi.msgin_fifo = fifo_len;
 			fas216_message(info);
 			break;
 		}
 
 	default:
-		printk("scsi%d.%c: internal phase %s for function done?"
-			"  What do I do with this?\n",
-			info->host->host_no, fas216_target(info),
+		fas216_log(info, 0, "internal phase %s for function done?"
+			"  What do I do with this?",
 			fas216_drv_phase(info));
 	}
 }
 
-/* Function: void fas216_intr(struct Scsi_Host *instance)
- * Purpose : handle interrupts from the interface to progress a command
- * Params  : instance - interface to service
+static void fas216_bus_reset(FAS216_Info *info)
+{
+	neg_t sync_state;
+	int i;
+
+	msgqueue_flush(&info->scsi.msgs);
+
+	sync_state = neg_invalid;
+
+#ifdef SCSI2_SYNC
+	if (info->ifcfg.capabilities & (FASCAP_DMA|FASCAP_PSEUDODMA))
+		sync_state = neg_wait;
+#endif
+
+	info->scsi.phase = PHASE_IDLE;
+	info->SCpnt = NULL; /* bug! */
+	memset(&info->scsi.SCp, 0, sizeof(info->scsi.SCp));
+
+	for (i = 0; i < 8; i++) {
+		info->device[i].disconnect_ok	= info->ifcfg.disconnect_ok;
+		info->device[i].sync_state	= sync_state;
+		info->device[i].period		= info->ifcfg.asyncperiod / 4;
+		info->device[i].stp		= info->scsi.async_stp;
+		info->device[i].sof		= 0;
+		info->device[i].wide_xfer	= 0;
+	}
+
+	info->rst_bus_status = 1;
+	wake_up(&info->eh_wait);
+}
+
+/**
+ * fas216_intr - handle interrupts to progress a command
+ * @info: interface to service
+ *
+ * Handle interrupts from the interface to progress a command
  */
-void fas216_intr(struct Scsi_Host *instance)
+void fas216_intr(FAS216_Info *info)
 {
-	FAS216_Info *info = (FAS216_Info *)instance->hostdata;
-	unsigned char isr, ssr, stat;
+	unsigned char inst, is, stat;
 
 	fas216_checkmagic(info);
 
-	stat = inb(REG_STAT(info));
-	ssr = inb(REG_IS(info));
-	isr = inb(REG_INST(info));
+	stat = fas216_readb(info, REG_STAT);
+	is = fas216_readb(info, REG_IS);
+	inst = fas216_readb(info, REG_INST);
 
-	add_debug_list(stat, ssr, isr, info->scsi.phase);
+	add_debug_list(stat, is, inst, info->scsi.phase);
 
 	if (stat & STAT_INT) {
-		if (isr & INST_BUSRESET) {
-			printk(KERN_DEBUG "scsi%d.H: bus reset detected\n", instance->host_no);
-			scsi_report_bus_reset(instance, 0);
-		} else if (isr & INST_ILLEGALCMD) {
-			printk(KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no);
+		if (inst & INST_BUSRESET) {
+			fas216_log(info, 0, "bus reset detected");
+			fas216_bus_reset(info);
+			scsi_report_bus_reset(info->host, 0);
+		} else if (inst & INST_ILLEGALCMD) {
+			fas216_log(info, LOG_ERROR, "illegal command given\n");
 			fas216_dumpstate(info);
-		} else if (isr & INST_DISCONNECT)
+			print_debug_list();
+		} else if (inst & INST_DISCONNECT)
 			fas216_disconnect_intr(info);
-		else if (isr & INST_RESELECTED)		/* reselected			*/
+		else if (inst & INST_RESELECTED)	/* reselected			*/
 			fas216_reselected_intr(info);
-		else if (isr & INST_BUSSERVICE)		/* bus service request		*/
-			fas216_busservice_intr(info, stat, ssr);
-		else if (isr & INST_FUNCDONE)		/* function done		*/
-			fas216_funcdone_intr(info, stat, ssr);
+		else if (inst & INST_BUSSERVICE)	/* bus service request		*/
+			fas216_busservice_intr(info, stat, is);
+		else if (inst & INST_FUNCDONE)		/* function done		*/
+			fas216_funcdone_intr(info, stat, is);
 		else
-		    	printk("scsi%d.%c: unknown interrupt received:"
-				" phase %s isr %02X ssr %02X stat %02X\n",
-				instance->host_no, fas216_target(info),
-				fas216_drv_phase(info), isr, ssr, stat);
+		    	fas216_log(info, 0, "unknown interrupt received:"
+				" phase %s inst %02X is %02X stat %02X",
+				fas216_drv_phase(info), inst, is, stat);
 	}
 }
 
-/* Function: void fas216_kick(FAS216_Info *info)
- * Purpose : kick a command to the interface - interface should be idle
- * Params  : info - our host interface to kick
- * Notes   : Interrupts are always disabled!
- */
-static void fas216_kick(FAS216_Info *info)
+static void __fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt)
 {
-	Scsi_Cmnd *SCpnt = NULL;
-	int tot_msglen, from_queue = 0, disconnect_ok;
+	int tot_msglen;
 
-	fas216_checkmagic(info);
+	/* following what the ESP driver says */
+	fas216_set_stc(info, 0);
+	fas216_cmd(info, CMD_NOP | CMD_WITHDMA);
 
-	/*
-	 * Obtain the next command to process.
-	 */
-	do {
-		if (info->reqSCpnt) {
-			SCpnt = info->reqSCpnt;
-			info->reqSCpnt = NULL;
-			break;
-		}
+	/* flush FIFO */
+	fas216_cmd(info, CMD_FLUSHFIFO);
 
-		if (info->origSCpnt) {
-			SCpnt = info->origSCpnt;
-			info->origSCpnt = NULL;
-			break;
+	/* load bus-id and timeout */
+	fas216_writeb(info, REG_SDID, BUSID(SCpnt->target));
+	fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout);
+
+	/* synchronous transfers */
+	fas216_set_sync(info, SCpnt->target);
+
+	tot_msglen = msgqueue_msglength(&info->scsi.msgs);
+
+#ifdef DEBUG_MESSAGES
+	{
+		struct message *msg;
+		int msgnr = 0, i;
+
+		printk("scsi%d.%c: message out: ",
+			info->host->host_no, '0' + SCpnt->target);
+		while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+			printk("{ ");
+			for (i = 0; i < msg->length; i++)
+				printk("%02x ", msg->msg[i]);
+			printk("} ");
 		}
+		printk("\n");
+	}
+#endif
 
-		/* retrieve next command */
-		if (!SCpnt) {
-			SCpnt = queue_remove_exclude(&info->queues.issue,
-						     info->busyluns);
-			from_queue = 1;
-			break;
+	if (tot_msglen == 1 || tot_msglen == 3) {
+		/*
+		 * We have an easy message length to send...
+		 */
+		struct message *msg;
+		int msgnr = 0, i;
+
+		info->scsi.phase = PHASE_SELSTEPS;
+
+		/* load message bytes */
+		while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
+			for (i = 0; i < msg->length; i++)
+				fas216_writeb(info, REG_FF, msg->msg[i]);
+			msg->fifo = tot_msglen - (fas216_readb(info, REG_CFIS) & CFIS_CF);
 		}
-	} while (0);
 
-	if (!SCpnt) /* no command pending - just exit */
-		return;
+		/* load command */
+		for (i = 0; i < SCpnt->cmd_len; i++)
+			fas216_writeb(info, REG_FF, SCpnt->cmnd[i]);
 
-	if (info->scsi.disconnectable && info->SCpnt) {
-		queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt);
-		info->scsi.disconnectable = 0;
-		info->SCpnt = NULL;
-		printk("scsi%d.%c: moved command to disconnected queue\n",
-			info->host->host_no, fas216_target(info));
+		if (tot_msglen == 1)
+			fas216_cmd(info, CMD_SELECTATN);
+		else
+			fas216_cmd(info, CMD_SELECTATN3);
+	} else {
+		/*
+		 * We have an unusual number of message bytes to send.
+		 *  Load first byte into fifo, and issue SELECT with ATN and
+		 *  stop steps.
+		 */
+		struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0);
+
+		fas216_writeb(info, REG_FF, msg->msg[0]);
+		msg->fifo = 1;
+
+		fas216_cmd(info, CMD_SELECTATNSTOP);
 	}
+}
+
+/*
+ * Decide whether we need to perform a parity test on this device.
+ * Can also be used to force parity error conditions during initial
+ * information transfer phase (message out) for test purposes.
+ */
+static int parity_test(FAS216_Info *info, int target)
+{
+#if 0
+	if (target == 3) {
+		info->device[target].parity_check = 0;
+		return 1;
+	}
+#endif
+	return info->device[target].parity_check;
+}
+
+static void fas216_start_command(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+{
+	int disconnect_ok;
 
 	/*
 	 * claim host busy
 	 */
 	info->scsi.phase = PHASE_SELECTION;
-	info->SCpnt = SCpnt;
 	info->scsi.SCp = SCpnt->SCp;
+	info->SCpnt = SCpnt;
 	info->dma.transfer_type = fasdma_none;
 
-#ifdef DEBUG_CONNECT
-	printk("scsi%d.%c: starting cmd %02X",
-		info->host->host_no, '0' + SCpnt->target,
-		SCpnt->cmnd[0]);
-#endif
-
-	if (from_queue) {
-#ifdef SCSI2_TAG
-		/*
-		 * tagged queuing - allocate a new tag to this command
-		 */
-		if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE &&
-		    SCpnt->cmnd[0] != INQUIRY) {
-		    SCpnt->device->current_tag += 1;
-			if (SCpnt->device->current_tag == 0)
-			    SCpnt->device->current_tag = 1;
-				SCpnt->tag = SCpnt->device->current_tag;
-		} else
-#endif
-			set_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns);
-
-		info->stats.removes += 1;
-		switch (SCpnt->cmnd[0]) {
-		case WRITE_6:
-		case WRITE_10:
-		case WRITE_12:
-			info->stats.writes += 1;
-			break;
-		case READ_6:
-		case READ_10:
-		case READ_12:
-			info->stats.reads += 1;
-			break;
-		default:
-			info->stats.miscs += 1;
-			break;
-		}
-	}
+	if (parity_test(info, SCpnt->target))
+		fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_PTE);
+	else
+		fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]);
 
 	/*
 	 * Don't allow request sense commands to disconnect.
@@ -1714,15 +1752,6 @@
 		msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag);
 
 	do {
-#ifdef SCSI2_WIDE
-		if (info->device[SCpnt->target].wide_state == neg_wait) {
-			info->device[SCpnt->target].wide_state = neg_inprogress;
-			msgqueue_addmsg(&info->scsi.msgs, 4,
-					EXTENDED_MESSAGE, 2, EXTENDED_WDTR,
-					info->ifcfg.wide_max_size);
-			break;
-		}
-#endif
 #ifdef SCSI2_SYNC
 		if ((info->device[SCpnt->target].sync_state == neg_wait ||
 		     info->device[SCpnt->target].sync_state == neg_complete) &&
@@ -1738,100 +1767,202 @@
 #endif
 	} while (0);
 
+	__fas216_start_command(info, SCpnt);
+}
+
+static void fas216_allocate_tag(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+{
+#ifdef SCSI2_TAG
+	/*
+	 * tagged queuing - allocate a new tag to this command
+	 */
+	if (SCpnt->device->tagged_queue && SCpnt->cmnd[0] != REQUEST_SENSE &&
+	    SCpnt->cmnd[0] != INQUIRY) {
+	    SCpnt->device->current_tag += 1;
+		if (SCpnt->device->current_tag == 0)
+		    SCpnt->device->current_tag = 1;
+			SCpnt->tag = SCpnt->device->current_tag;
+	} else
+#endif
+		set_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns);
+
+	info->stats.removes += 1;
+	switch (SCpnt->cmnd[0]) {
+	case WRITE_6:
+	case WRITE_10:
+	case WRITE_12:
+		info->stats.writes += 1;
+		break;
+	case READ_6:
+	case READ_10:
+	case READ_12:
+		info->stats.reads += 1;
+		break;
+	default:
+		info->stats.miscs += 1;
+		break;
+	}
+}
+
+static void fas216_do_bus_device_reset(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+{
+	struct message *msg;
+
+	/*
+	 * claim host busy
+	 */
+	info->scsi.phase = PHASE_SELECTION;
+	info->scsi.SCp = SCpnt->SCp;
+	info->SCpnt = SCpnt;
+	info->dma.transfer_type = fasdma_none;
+
+	fas216_log(info, LOG_ERROR, "sending bus device reset");
+
+	msgqueue_flush(&info->scsi.msgs);
+	msgqueue_addmsg(&info->scsi.msgs, 1, BUS_DEVICE_RESET);
+
 	/* following what the ESP driver says */
-	outb(0, REG_STCL(info));
-	outb(0, REG_STCM(info));
-	outb(0, REG_STCH(info));
-	outb(CMD_NOP | CMD_WITHDMA, REG_CMD(info));
+	fas216_set_stc(info, 0);
+	fas216_cmd(info, CMD_NOP | CMD_WITHDMA);
 
 	/* flush FIFO */
-	outb(CMD_FLUSHFIFO, REG_CMD(info));
+	fas216_cmd(info, CMD_FLUSHFIFO);
 
 	/* load bus-id and timeout */
-	outb(BUSID(SCpnt->target), REG_SDID(info));
-	outb(info->ifcfg.select_timeout, REG_STIM(info));
+	fas216_writeb(info, REG_SDID, BUSID(SCpnt->target));
+	fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout);
 
 	/* synchronous transfers */
 	fas216_set_sync(info, SCpnt->target);
 
-	tot_msglen = msgqueue_msglength(&info->scsi.msgs);
+	msg = msgqueue_getmsg(&info->scsi.msgs, 0);
 
-#ifdef DEBUG_MESSAGES
-	{
-		struct message *msg;
-		int msgnr = 0, i;
+	fas216_writeb(info, REG_FF, BUS_DEVICE_RESET);
+	msg->fifo = 1;
 
-		printk("scsi%d.%c: message out: ",
-			info->host->host_no, '0' + SCpnt->target);
-		while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
-			printk("{ ");
-			for (i = 0; i < msg->length; i++)
-				printk("%02x ", msg->msg[i]);
-			printk("} ");
+	fas216_cmd(info, CMD_SELECTATNSTOP);
+}
+
+/**
+ * fas216_kick - kick a command to the interface
+ * @info: our host interface to kick
+ *
+ * Kick a command to the interface, interface should be idle.
+ * Notes: Interrupts are always disabled!
+ */
+static void fas216_kick(FAS216_Info *info)
+{
+	Scsi_Cmnd *SCpnt = NULL;
+#define TYPE_OTHER	0
+#define TYPE_RESET	1
+#define TYPE_QUEUE	2
+	int where_from = TYPE_OTHER;
+
+	fas216_checkmagic(info);
+
+	/*
+	 * Obtain the next command to process.
+	 */
+	do {
+		if (info->rstSCpnt) {
+			SCpnt = info->rstSCpnt;
+			/* don't remove it */
+			where_from = TYPE_RESET;
+			break;
 		}
-		printk("\n");
-	}
-#endif
 
-	if (tot_msglen == 1 || tot_msglen == 3) {
-		/*
-		 * We have an easy message length to send...
-		 */
-		struct message *msg;
-		int msgnr = 0, i;
+		if (info->reqSCpnt) {
+			SCpnt = info->reqSCpnt;
+			info->reqSCpnt = NULL;
+			break;
+		}
 
-		info->scsi.phase = PHASE_SELSTEPS;
+		if (info->origSCpnt) {
+			SCpnt = info->origSCpnt;
+			info->origSCpnt = NULL;
+			break;
+		}
 
-		/* load message bytes */
-		while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) {
-			for (i = 0; i < msg->length; i++)
-				outb(msg->msg[i], REG_FF(info));
-			msg->fifo = tot_msglen - (inb(REG_CFIS(info)) & CFIS_CF);
+		/* retrieve next command */
+		if (!SCpnt) {
+			SCpnt = queue_remove_exclude(&info->queues.issue,
+						     info->busyluns);
+			where_from = TYPE_QUEUE;
+			break;
 		}
+	} while (0);
 
-		/* load command */
-		for (i = 0; i < SCpnt->cmd_len; i++)
-			outb(SCpnt->cmnd[i], REG_FF(info));
-
-		if (tot_msglen == 1)
-			outb(CMD_SELECTATN, REG_CMD(info));
-		else
-			outb(CMD_SELECTATN3, REG_CMD(info));
-	} else {
+	if (!SCpnt) {
 		/*
-		 * We have an unusual number of message bytes to send.
-		 *  Load first byte into fifo, and issue SELECT with ATN and
-		 *  stop steps.
+		 * no command pending, so enable reselection.
 		 */
-		struct message *msg = msgqueue_getmsg(&info->scsi.msgs, 0);
+		fas216_cmd(info, CMD_ENABLESEL);
+		return;
+	}
 
-		outb(msg->msg[0], REG_FF(info));
-		msg->fifo = 1;
+	/*
+	 * We're going to start a command, so disable reselection
+	 */
+	fas216_cmd(info, CMD_DISABLESEL);
+
+	if (info->scsi.disconnectable && info->SCpnt) {
+		fas216_log(info, LOG_CONNECT,
+			"moved command for %d to disconnected queue",
+			info->SCpnt->target);
+		queue_add_cmd_tail(&info->queues.disconnected, info->SCpnt);
+		info->scsi.disconnectable = 0;
+		info->SCpnt = NULL;
+	}
 
-		outb(CMD_SELECTATNSTOP, REG_CMD(info));
+	fas216_log_command(info, LOG_CONNECT | LOG_MESSAGES, SCpnt, "starting ");
+
+	switch (where_from) {
+	case TYPE_QUEUE:
+		fas216_allocate_tag(info, SCpnt);
+	case TYPE_OTHER:
+		fas216_start_command(info, SCpnt);
+		break;
+	case TYPE_RESET:
+		fas216_do_bus_device_reset(info, SCpnt);
+		break;
 	}
 
-#ifdef DEBUG_CONNECT
-	printk(", data pointers [%p, %X]\n",
+	fas216_log(info, LOG_CONNECT, "select: data pointers [%p, %X]",
 		info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
-#endif
-	/* should now get either DISCONNECT or (FUNCTION DONE with BUS SERVICE) intr */
+
+	/*
+	 * should now get either DISCONNECT or
+	 * (FUNCTION DONE with BUS SERVICE) interrupt
+	 */
+}
+
+/*
+ * Clean up from issuing a BUS DEVICE RESET message to a device.
+ */
+static void
+fas216_devicereset_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result)
+{
+	fas216_log(info, LOG_ERROR, "fas216 device reset complete");
+
+	info->rstSCpnt = NULL;
+	info->rst_dev_status = 1;
+	wake_up(&info->eh_wait);
 }
 
-/* Function: void fas216_rq_sns_done(info, SCpnt, result)
- * Purpose : Finish processing automatic request sense command
- * Params  : info   - interface that completed
- *	     SCpnt  - command that completed
- *	     result - driver byte of result
+/**
+ * fas216_rq_sns_done - Finish processing automatic request sense command
+ * @info: interface that completed
+ * @SCpnt: command that completed
+ * @result: driver byte of result
+ *
+ * Finish processing automatic request sense command
  */
 static void
 fas216_rq_sns_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result)
 {
-#ifdef DEBUG_CONNECT
-	printk("scsi%d.%c: request sense complete, result=%04X%02X%02X\n",
-		info->host->host_no, '0' + SCpnt->target, result,
-		SCpnt->SCp.Message, SCpnt->SCp.Status);
-#endif
+	fas216_log_target(info, LOG_CONNECT, SCpnt->target,
+		   "request sense complete, result=0x%04x%02x%02x",
+		   result, SCpnt->SCp.Message, SCpnt->SCp.Status);
 
 	if (result != DID_OK || SCpnt->SCp.Status != GOOD)
 		/*
@@ -1840,7 +1971,8 @@
 		 * confuse the higher levels.
 		 */
 		memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
-
+//printk("scsi%d.%c: sense buffer: ", info->host->host_no, '0' + SCpnt->target);
+//{ int i; for (i = 0; i < 32; i++) printk("%02x ", SCpnt->sense_buffer[i]); printk("\n"); }
 	/*
 	 * Note that we don't set SCpnt->result, since that should
 	 * reflect the status of the command that we were asked by
@@ -1850,11 +1982,13 @@
 	SCpnt->scsi_done(SCpnt);
 }
 
-/* Function: void fas216_std_done(info, SCpnt, result)
- * Purpose : Finish processing of standard command
- * Params  : info   - interface that completed
- *	     SCpnt  - command that completed
- *	     result - driver byte of result
+/**
+ * fas216_std_done - finish processing of standard command
+ * @info: interface that completed
+ * @SCpnt: command that completed
+ * @result: driver byte of result
+ *
+ * Finish processing of standard command
  */
 static void
 fas216_std_done(FAS216_Info *info, Scsi_Cmnd *SCpnt, unsigned int result)
@@ -1864,31 +1998,29 @@
 	SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 |
 			info->scsi.SCp.Status;
 
-#ifdef DEBUG_CONNECT
-	printk("scsi%d.%c: command complete, result=%08X, command=",
-		info->host->host_no, '0' + SCpnt->target, SCpnt->result);
-	print_command(SCpnt->cmnd);
-#endif
+	fas216_log_command(info, LOG_CONNECT, SCpnt,
+		"command complete, result=0x%08x ", SCpnt->result);
 
 	/*
-	 * If the driver detected an error, or the command
-	 * was request sense, then we're all done.
+	 * If the driver detected an error, we're all done.
 	 */
-	if (result != DID_OK || SCpnt->cmnd[0] == REQUEST_SENSE)
+	if (host_byte(SCpnt->result) != DID_OK ||
+	    msg_byte(SCpnt->result) != COMMAND_COMPLETE)
 		goto done;
 
 	/*
-	 * If the command returned CHECK_CONDITION status,
-	 * request the sense information.
+	 * If the command returned CHECK_CONDITION or COMMAND_TERMINATED
+	 * status, request the sense information.
 	 */
-	if (info->scsi.SCp.Status == CHECK_CONDITION)
+	if (status_byte(SCpnt->result) == CHECK_CONDITION ||
+	    status_byte(SCpnt->result) == COMMAND_TERMINATED)
 		goto request_sense;
 
 	/*
 	 * If the command did not complete with GOOD status,
 	 * we are all done here.
 	 */
-	if (info->scsi.SCp.Status != GOOD)
+	if (status_byte(SCpnt->result) != GOOD)
 		goto done;
 
 	/*
@@ -1902,24 +2034,38 @@
 		switch (SCpnt->cmnd[0]) {
 		case INQUIRY:
 		case START_STOP:
-//		case READ_CAPACITY:
 		case MODE_SENSE:
 			break;
 
 		default:
 			printk(KERN_ERR "scsi%d.%c: incomplete data transfer "
-				"detected: res=%08X ptr=%p len=%X command=",
+				"detected: res=%08X ptr=%p len=%X CDB: ",
 				info->host->host_no, '0' + SCpnt->target,
 				SCpnt->result, info->scsi.SCp.ptr,
 				info->scsi.SCp.this_residual);
 			print_command(SCpnt->cmnd);
+			SCpnt->result &= ~(255 << 16);
+			SCpnt->result |= DID_BAD_TARGET << 16;
+			goto request_sense;
 		}
 	}
 
-done:	SCpnt->scsi_done(SCpnt);
-	return;
+done:
+	if (SCpnt->scsi_done) {
+		SCpnt->scsi_done(SCpnt);
+		return;
+	}
+
+	panic("scsi%d.H: null scsi_done function in fas216_done",
+		info->host->host_no);
+
 
 request_sense:
+	if (SCpnt->cmnd[0] == REQUEST_SENSE)
+		goto done;
+
+	fas216_log_target(info, LOG_CONNECT, SCpnt->target,
+			  "requesting sense");
 	memset(SCpnt->cmnd, 0, sizeof (SCpnt->cmnd));
 	SCpnt->cmnd[0] = REQUEST_SENSE;
 	SCpnt->cmnd[1] = SCpnt->lun << 5;
@@ -1931,6 +2077,7 @@
 	SCpnt->SCp.this_residual = sizeof(SCpnt->sense_buffer);
 	SCpnt->SCp.Message = 0;
 	SCpnt->SCp.Status = 0;
+	SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer);
 	SCpnt->sc_data_direction = SCSI_DATA_READ;
 	SCpnt->use_sg = 0;
 	SCpnt->tag = 0;
@@ -1947,15 +2094,18 @@
 	info->reqSCpnt = SCpnt;
 }
 
-/* Function: void fas216_done(FAS216_Info *info, unsigned int result)
- * Purpose : complete processing for current command
- * Params  : info   - interface that completed
- *	     result - driver byte of result
+/**
+ * fas216_done - complete processing for current command
+ * @info: interface that completed
+ * @result: driver byte of result
+ *
+ * Complete processing for current command
  */
 static void fas216_done(FAS216_Info *info, unsigned int result)
 {
 	void (*fn)(FAS216_Info *, Scsi_Cmnd *, unsigned int);
 	Scsi_Cmnd *SCpnt;
+	unsigned long flags;
 
 	fas216_checkmagic(info);
 
@@ -1966,12 +2116,8 @@
 	info->SCpnt = NULL;
     	info->scsi.phase = PHASE_IDLE;
 
-	if (!SCpnt->scsi_done)
-		goto no_done;
-
 	if (info->scsi.aborting) {
-		printk("scsi%d.%c: uncaught abort - returning DID_ABORT\n",
-			info->host->host_no, fas216_target(info));
+		fas216_log(info, 0, "uncaught abort - returning DID_ABORT");
 		result = DID_ABORT;
 		info->scsi.aborting = 0;
 	}
@@ -1982,7 +2128,7 @@
 	 */
 	if (info->scsi.SCp.ptr && info->scsi.SCp.this_residual == 0) {
 		printk("scsi%d.%c: zero bytes left to transfer, but "
-		       "buffer pointer still valid: ptr=%p len=%08x command=",
+		       "buffer pointer still valid: ptr=%p len=%08x CDB: ",
 		       info->host->host_no, '0' + SCpnt->target,
 		       info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
 		info->scsi.SCp.ptr = NULL;
@@ -1994,29 +2140,33 @@
 	 * the sense information, fas216_kick will re-assert the busy
 	 * status.
 	 */
+	info->device[SCpnt->target].parity_check = 0;
 	clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns);
 
 	fn = (void (*)(FAS216_Info *, Scsi_Cmnd *, unsigned int))SCpnt->host_scribble;
 	fn(info, SCpnt, result);
 
-	if (info->scsi.irq != NO_IRQ)
-		fas216_kick(info);
+	if (info->scsi.irq != NO_IRQ) {
+		spin_lock_irqsave(&info->host_lock, flags);
+		if (info->scsi.phase == PHASE_IDLE)
+			fas216_kick(info);
+		spin_unlock_irqrestore(&info->host_lock, flags);
+	}
 	return;
 
 no_command:
 	panic("scsi%d.H: null command in fas216_done",
 		info->host->host_no);
-no_done:
-	panic("scsi%d.H: null scsi_done function in fas216_done",
-		info->host->host_no);
 }
 
-/* Function: int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
- * Purpose : queue a command for adapter to process.
- * Params  : SCpnt - Command to queue
- *	     done  - done function to call once command is complete
- * Returns : 0 - success, else error
- * Notes   : io_request_lock is held, interrupts are disabled.
+/**
+ * fas216_queue_command - queue a command for adapter to process.
+ * @SCpnt: Command to queue
+ * @done: done function to call once command is complete
+ *
+ * Queue a command for adapter to process.
+ * Returns: 0 on success, else error.
+ * Notes: io_request_lock is held, interrupts are disabled.
  */
 int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
 {
@@ -2025,61 +2175,20 @@
 
 	fas216_checkmagic(info);
 
-#ifdef DEBUG_CONNECT
-	printk("scsi%d.%c: received queuable command (%p) %02X\n",
-		SCpnt->host->host_no, '0' + SCpnt->target,
-		SCpnt, SCpnt->cmnd[0]);
-#endif
+	fas216_log_command(info, LOG_CONNECT, SCpnt,
+			   "received command (%p) ", SCpnt);
 
 	SCpnt->scsi_done = done;
 	SCpnt->host_scribble = (void *)fas216_std_done;
 	SCpnt->result = 0;
-	SCpnt->SCp.Message = 0;
-	SCpnt->SCp.Status = 0;
-
-	if (SCpnt->use_sg) {
-		unsigned long len = 0;
-		int buf;
-
-		SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer;
-		SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
-		SCpnt->SCp.ptr = (char *) SCpnt->SCp.buffer->address;
-		SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
-		/*
-		 * Calculate correct buffer length.  Some commands
-		 * come in with the wrong request_bufflen.
-		 */
-		for (buf = 0; buf <= SCpnt->SCp.buffers_residual; buf++)
-			len += SCpnt->SCp.buffer[buf].length;
-
-		if (SCpnt->request_bufflen != len)
-			printk(KERN_WARNING "scsi%d.%c: bad request buffer "
-			       "length %d, should be %ld\n", info->host->host_no,
-			       '0' + SCpnt->target, SCpnt->request_bufflen, len);
-		SCpnt->request_bufflen = len;
-	} else {
-		SCpnt->SCp.buffer = NULL;
-		SCpnt->SCp.buffers_residual = 0;
-		SCpnt->SCp.ptr = (unsigned char *)SCpnt->request_buffer;
-		SCpnt->SCp.this_residual = SCpnt->request_bufflen;
-	}
 
-	/*
-	 * If the upper SCSI layers pass a buffer, but zero length,
-	 * we aren't interested in the buffer pointer.
-	 */
-	if (SCpnt->SCp.this_residual == 0 && SCpnt->SCp.ptr) {
-#if 0
-		printk(KERN_WARNING "scsi%d.%c: zero length buffer passed for "
-		       "command ", info->host->host_no, '0' + SCpnt->target);
-		print_command(SCpnt->cmnd);
-#endif
-		SCpnt->SCp.ptr = NULL;
-	}
+	init_SCp(SCpnt);
 
 	info->stats.queues += 1;
 	SCpnt->tag = 0;
 
+	spin_lock(&info->host_lock);
+
 	/*
 	 * Add command into execute queue and let it complete under
 	 * whatever scheme we're using.
@@ -2090,15 +2199,21 @@
 	 * If we successfully added the command,
 	 * kick the interface to get it moving.
 	 */
-	if (result == 0 && (!info->SCpnt || info->scsi.disconnectable))
+	if (result == 0 && info->scsi.phase == PHASE_IDLE)
 		fas216_kick(info);
+	spin_unlock(&info->host_lock);
+
+	fas216_log_target(info, LOG_CONNECT, -1, "queue %s",
+		result ? "failure" : "success");
 
 	return result;
 }
 
-/* Function: void fas216_internal_done(Scsi_Cmnd *SCpnt)
- * Purpose : trigger restart of a waiting thread in fas216_command
- * Params  : SCpnt - Command to wake
+/**
+ * fas216_internal_done - trigger restart of a waiting thread in fas216_command
+ * @SCpnt: Command to wake
+ *
+ * Trigger restart of a waiting thread in fas216_command
  */
 static void fas216_internal_done(Scsi_Cmnd *SCpnt)
 {
@@ -2109,11 +2224,13 @@
 	info->internal_done = 1;
 }
 
-/* Function: int fas216_command(Scsi_Cmnd *SCpnt)
- * Purpose : queue a command for adapter to process.
- * Params  : SCpnt - Command to queue
- * Returns : scsi result code
- * Notes   : io_request_lock is held, interrupts are disabled.
+/**
+ * fas216_command - queue a command for adapter to process.
+ * @SCpnt: Command to queue
+ *
+ * Queue a command for adapter to process.
+ * Returns: scsi result code.
+ * Notes: io_request_lock is held, interrupts are disabled.
  */
 int fas216_command(Scsi_Cmnd *SCpnt)
 {
@@ -2125,8 +2242,7 @@
 	 * We should only be using this if we don't have an interrupt.
 	 * Provide some "incentive" to use the queueing code.
 	 */
-	if (info->scsi.irq != NO_IRQ)
-		BUG();
+	BUG_ON(info->scsi.irq != NO_IRQ);
 
 	info->internal_done = 0;
 	fas216_queue_command(SCpnt, fas216_internal_done);
@@ -2148,9 +2264,9 @@
 		 * and go to sleep if we know that the device is going
 		 * to be some time (eg, disconnected).
 		 */
-		if (inb(REG_STAT(info)) & STAT_INT) {
+		if (fas216_readb(info, REG_STAT) & STAT_INT) {
 			spin_lock_irq(&io_request_lock);
-			fas216_intr(info->host);
+			fas216_intr(info);
 			spin_unlock_irq(&io_request_lock);
 		}
 	}
@@ -2160,23 +2276,42 @@
 	return SCpnt->result;
 }
 
-enum res_abort {
-	res_failed,		/* unable to abort		*/
+/*
+ * Error handler timeout function.  Indicate that we timed out,
+ * and wake up any error handler process so it can continue.
+ */
+static void fas216_eh_timer(unsigned long data)
+{
+	FAS216_Info *info = (FAS216_Info *)data;
+
+	fas216_log(info, LOG_ERROR, "error handling timed out\n");
+
+	del_timer(&info->eh_timer);
+
+	if (info->rst_bus_status == 0)
+		info->rst_bus_status = -1;
+	if (info->rst_dev_status == 0)
+		info->rst_dev_status = -1;
+
+	wake_up(&info->eh_wait);
+}
+
+enum res_find {
+	res_failed,		/* not found			*/
 	res_success,		/* command on issue queue	*/
-	res_success_clear,	/* command marked tgt/lun busy	*/
 	res_hw_abort		/* command on disconnected dev	*/
 };
 
-/*
- * Prototype: enum res_abort fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt)
- * Purpose  : decide how to abort a command
- * Params   : SCpnt - command to abort
- * Returns  : abort status
+/**
+ * fas216_do_abort - decide how to abort a command
+ * @SCpnt: command to abort
+ *
+ * Decide how to abort a command.
+ * Returns: abort status
  */
-static enum res_abort
-fas216_do_abort(FAS216_Info *info, Scsi_Cmnd *SCpnt)
+static enum res_find fas216_find_command(FAS216_Info *info, Scsi_Cmnd *SCpnt)
 {
-	enum res_abort res = res_failed;
+	enum res_find res = res_failed;
 
 	if (queue_remove_cmd(&info->queues.issue, SCpnt)) {
 		/*
@@ -2224,19 +2359,22 @@
 		 * been set.
 		 */
 		info->origSCpnt = NULL;
+		clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns);
 		printk("waiting for execution ");
-		res = res_success_clear;
+		res = res_success;
 	} else
 		printk("unknown ");
 
 	return res;
 }
 
-/* Function: int fas216_eh_abort(Scsi_Cmnd *SCpnt)
- * Purpose : abort this command
- * Params  : SCpnt - command to abort
- * Returns : FAILED if unable to abort
- * Notes   : io_request_lock is taken, and irqs are disabled
+/**
+ * fas216_eh_abort - abort this command
+ * @SCpnt: command to abort
+ *
+ * Abort this command.
+ * Returns: FAILED if unable to abort
+ * Notes: io_request_lock is taken, and irqs are disabled
  */
 int fas216_eh_abort(Scsi_Cmnd *SCpnt)
 {
@@ -2247,22 +2385,15 @@
 
 	info->stats.aborts += 1;
 
+	printk(KERN_WARNING "scsi%d: abort command ", info->host->host_no);
+	print_command(SCpnt->data_cmnd);
+
 	print_debug_list();
 	fas216_dumpstate(info);
-	fas216_dumpinfo(info);
 
-	printk(KERN_WARNING "scsi%d: abort ", info->host->host_no);
-
-	switch (fas216_do_abort(info, SCpnt)) {
-	/*
-	 * We managed to find the command and cleared it out.
-	 * We do not expect the command to be executing on the
-	 * target, but we have set the busylun bit.
-	 */
-	case res_success_clear:
-		printk("clear ");
-		clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns);
+	printk(KERN_WARNING "scsi%d: abort %p ", info->host->host_no, SCpnt);
 
+	switch (fas216_find_command(info, SCpnt)) {
 	/*
 	 * We found the command, and cleared it out.  Either
 	 * the command is still known to be executing on the
@@ -2293,155 +2424,195 @@
 	return result;
 }
 
-/* Function: void fas216_reset_state(FAS216_Info *info)
- * Purpose : Initialise driver internal state
- * Params  : info - state to initialise
+/**
+ * fas216_eh_device_reset - Reset the device associated with this command
+ * @SCpnt: command specifing device to reset
+ *
+ * Reset the device associated with this command.
+ * Returns: FAILED if unable to reset.
+ * Notes: We won't be re-entered, so we'll only have one device
+ * reset on the go at one time.
  */
-static void fas216_reset_state(FAS216_Info *info)
+int fas216_eh_device_reset(Scsi_Cmnd *SCpnt)
 {
-	neg_t sync_state, wide_state;
-	int i;
+	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
+	unsigned long flags;
+	int i, res = FAILED, target = SCpnt->target;
 
-	fas216_checkmagic(info);
+	fas216_log(info, LOG_ERROR, "device reset for target %d", target);
 
-	/*
-	 * Clear out all stale info in our state structure
-	 */
-	memset(info->busyluns, 0, sizeof(info->busyluns));
-	msgqueue_flush(&info->scsi.msgs);
-	info->scsi.reconnected.target = 0;
-	info->scsi.reconnected.lun = 0;
-	info->scsi.reconnected.tag = 0;
-	info->scsi.disconnectable = 0;
-	info->scsi.aborting = 0;
-	info->scsi.phase = PHASE_IDLE;
-	info->scsi.async_stp =
-			fas216_syncperiod(info, info->ifcfg.asyncperiod);
+	spin_lock_irqsave(&info->host_lock, flags);
 
-	if (info->ifcfg.wide_max_size == 0)
-		wide_state = neg_invalid;
-	else
-#ifdef SCSI2_WIDE
-		wide_state = neg_wait;
-#else
-		wide_state = neg_invalid;
-#endif
+	do {
+		/*
+		 * If we are currently connected to a device, and
+		 * it is the device we want to reset, there is
+		 * nothing we can do here.  Chances are it is stuck,
+		 * and we need a bus reset.
+		 */
+		if (info->SCpnt && !info->scsi.disconnectable &&
+		    info->SCpnt->target == SCpnt->target)
+			break;
 
-	if (info->host->dma_channel == NO_DMA || !info->dma.setup)
-		sync_state = neg_invalid;
-	else
-#ifdef SCSI2_SYNC
-		sync_state = neg_wait;
-#else
-		sync_state = neg_invalid;
-#endif
+		/*
+		 * We're going to be resetting this device.  Remove
+		 * all pending commands from the driver.  By doing
+		 * so, we guarantee that we won't touch the command
+		 * structures except to process the reset request.
+		 */
+		queue_remove_all_target(&info->queues.issue, target);
+		queue_remove_all_target(&info->queues.disconnected, target);
+		if (info->origSCpnt && info->origSCpnt->target == target)
+			info->origSCpnt = NULL;
+		if (info->reqSCpnt && info->reqSCpnt->target == target)
+			info->reqSCpnt = NULL;
+		for (i = 0; i < 8; i++)
+			clear_bit(target * 8 + i, info->busyluns);
 
-	for (i = 0; i < 8; i++) {
-		info->device[i].disconnect_ok	= info->ifcfg.disconnect_ok;
-		info->device[i].sync_state	= sync_state;
-		info->device[i].wide_state	= wide_state;
-		info->device[i].period		= info->ifcfg.asyncperiod / 4;
-		info->device[i].stp		= info->scsi.async_stp;
-		info->device[i].sof		= 0;
-		info->device[i].wide_xfer	= 0;
-	}
+		/*
+		 * Hijack this SCSI command structure to send
+		 * a bus device reset message to this device.
+		 */
+		SCpnt->host_scribble = (void *)fas216_devicereset_done;
 
-	/*
-	 * Drain all commands on disconnected queue
-	 */
-	while (queue_remove(&info->queues.disconnected) != NULL);
+		info->rst_dev_status = 0;
+		info->rstSCpnt = SCpnt;
 
-	/*
-	 * Remove executing commands.
-	 */
-	info->SCpnt     = NULL;
-	info->reqSCpnt  = NULL;
-	info->origSCpnt = NULL;
-}
+		if (info->scsi.phase == PHASE_IDLE)
+			fas216_kick(info);
 
-/* Function: int fas216_eh_device_reset(Scsi_Cmnd *SCpnt)
- * Purpose : Reset the device associated with this command
- * Params  : SCpnt - command specifing device to reset
- * Returns : FAILED if unable to reset
- */
-int fas216_eh_device_reset(Scsi_Cmnd *SCpnt)
-{
-	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
+		mod_timer(&info->eh_timer, 30 * HZ);
+		spin_unlock_irqrestore(&info->host_lock, flags);
+
+		/*
+		 * Wait up to 30 seconds for the reset to complete.
+		 */
+		wait_event(info->eh_wait, info->rst_dev_status);
+
+		del_timer_sync(&info->eh_timer);
+		spin_lock_irqsave(&info->host_lock, flags);
+		info->rstSCpnt = NULL;
+
+		if (info->rst_dev_status == 1)
+			res = SUCCESS;
+	} while (0);
+
+	SCpnt->host_scribble = NULL;
+	spin_unlock_irqrestore(&info->host_lock, flags);
+
+	fas216_log(info, LOG_ERROR, "device reset complete: %s\n",
+		   res == SUCCESS ? "success" : "failed");
 
-	printk("scsi%d.%c: "__FUNCTION__": called\n",
-		info->host->host_no, '0' + SCpnt->target);
-	return FAILED;
+	return res;
 }
 
-/* Function: int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt)
- * Purpose : Reset the bus associated with the command
- * Params  : SCpnt - command specifing bus to reset
- * Returns : FAILED if unable to reset
- * Notes   : io_request_lock is taken, and irqs are disabled
+/**
+ * fas216_eh_bus_reset - Reset the bus associated with the command
+ * @SCpnt: command specifing bus to reset
+ *
+ * Reset the bus associated with the command.
+ * Returns: FAILED if unable to reset.
+ * Notes: Further commands are blocked.
  */
 int fas216_eh_bus_reset(Scsi_Cmnd *SCpnt)
 {
 	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
-	int result = FAILED;
+	unsigned long flags;
+	Scsi_Device *SDpnt;
 
 	fas216_checkmagic(info);
+	fas216_log(info, LOG_ERROR, "resetting bus");
 
 	info->stats.bus_resets += 1;
 
-	printk("scsi%d.%c: "__FUNCTION__": resetting bus\n",
-		info->host->host_no, '0' + SCpnt->target);
+	spin_lock_irqsave(&info->host_lock, flags);
 
 	/*
-	 * Attempt to stop all activity on this interface.
+	 * Stop all activity on this interface.
 	 */
-	outb(info->scsi.cfg[2], REG_CNTL3(info));
-	fas216_stoptransfer(info);
+	fas216_aborttransfer(info);
+	fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]);
 
 	/*
-	 * Clear any pending interrupts
+	 * Clear any pending interrupts.
 	 */
-	while (inb(REG_STAT(info)) & STAT_INT)
-		inb(REG_INST(info));
+	while (fas216_readb(info, REG_STAT) & STAT_INT)
+		fas216_readb(info, REG_INST);
+
+	info->rst_bus_status = 0;
 
 	/*
-	 * Reset the SCSI bus
+	 * For each attached hard-reset device, clear out
+	 * all command structures.  Leave the running
+	 * command in place.
 	 */
-	outb(CMD_RESETSCSI, REG_CMD(info));
-	udelay(5);
+	for (SDpnt = info->host->host_queue; SDpnt; SDpnt = SDpnt->next) {
+		int i;
+
+		if (SDpnt->soft_reset)
+			continue;
+
+		queue_remove_all_target(&info->queues.issue, SDpnt->id);
+		queue_remove_all_target(&info->queues.disconnected, SDpnt->id);
+		if (info->origSCpnt && info->origSCpnt->target == SDpnt->id)
+			info->origSCpnt = NULL;
+		if (info->reqSCpnt && info->reqSCpnt->target == SDpnt->id)
+			info->reqSCpnt = NULL;
+		info->SCpnt = NULL;
+
+		for (i = 0; i < 8; i++)
+			clear_bit(SDpnt->id * 8 + i, info->busyluns);
+	}
+
+	info->scsi.phase = PHASE_IDLE;
 
 	/*
-	 * Clear reset interrupt
+	 * Reset the SCSI bus.  Device cleanup happens in
+	 * the interrupt handler.
 	 */
-	if (inb(REG_STAT(info)) & STAT_INT &&
-	    inb(REG_INST(info)) & INST_BUSRESET)
-		result = SUCCESS;
+	fas216_cmd(info, CMD_RESETSCSI);
 
-	fas216_reset_state(info);
+	mod_timer(&info->eh_timer, jiffies + HZ);
+	spin_unlock_irqrestore(&info->host_lock, flags);
 
-	return result;
+	/*
+	 * Wait one second for the interrupt.
+	 */
+	wait_event(info->eh_wait, info->rst_bus_status);
+	del_timer_sync(&info->eh_timer);
+
+	fas216_log(info, LOG_ERROR, "bus reset complete: %s\n",
+		   info->rst_bus_status == 1 ? "success" : "failed");
+
+	return info->rst_bus_status == 1 ? SUCCESS : FAILED;
 }
 
-/* Function: void fas216_init_chip(FAS216_Info *info)
- * Purpose : Initialise FAS216 state after reset
- * Params  : info - state structure for interface
+/**
+ * fas216_init_chip - Initialise FAS216 state after reset
+ * @info: state structure for interface
+ *
+ * Initialise FAS216 state after reset
  */
 static void fas216_init_chip(FAS216_Info *info)
 {
-	outb(fas216_clockrate(info->ifcfg.clockrate), REG_CLKF(info));
-	outb(info->scsi.cfg[0], REG_CNTL1(info));
-	outb(info->scsi.cfg[1], REG_CNTL2(info));
-	outb(info->scsi.cfg[2], REG_CNTL3(info));
-	outb(info->ifcfg.select_timeout, REG_STIM(info));
-	outb(0, REG_SOF(info));
-	outb(info->scsi.async_stp, REG_STP(info));
-	outb(info->scsi.cfg[0], REG_CNTL1(info));
+	unsigned int clock = ((info->ifcfg.clockrate - 1) / 5 + 1) & 7;
+	fas216_writeb(info, REG_CLKF, clock);
+	fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]);
+	fas216_writeb(info, REG_CNTL2, info->scsi.cfg[1]);
+	fas216_writeb(info, REG_CNTL3, info->scsi.cfg[2]);
+	fas216_writeb(info, REG_STIM, info->ifcfg.select_timeout);
+	fas216_writeb(info, REG_SOF, 0);
+	fas216_writeb(info, REG_STP, info->scsi.async_stp);
+	fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]);
 }
 
-/* Function: int fas216_eh_host_reset(Scsi_Cmnd *SCpnt)
- * Purpose : Reset the host associated with this command
- * Params  : SCpnt - command specifing host to reset
- * Returns : FAILED if unable to reset
- * Notes   : io_request_lock is taken, and irqs are disabled
+/**
+ * fas216_eh_host_reset - Reset the host associated with this command
+ * @SCpnt: command specifing host to reset
+ *
+ * Reset the host associated with this command.
+ * Returns: FAILED if unable to reset.
+ * Notes: io_request_lock is taken, and irqs are disabled
  */
 int fas216_eh_host_reset(Scsi_Cmnd *SCpnt)
 {
@@ -2449,13 +2620,13 @@
 
 	fas216_checkmagic(info);
 
-	printk("scsi%d.%c: "__FUNCTION__": resetting host\n",
-		info->host->host_no, '0' + SCpnt->target);
+	printk("scsi%d.%c: %s: resetting host\n",
+		info->host->host_no, '0' + SCpnt->target, __FUNCTION__);
 
 	/*
 	 * Reset the SCSI chip.
 	 */
-	outb(CMD_RESETCHIP, REG_CMD(info));
+	fas216_cmd(info, CMD_RESETCHIP);
 
 	/*
 	 * Ugly ugly ugly!
@@ -2464,13 +2635,13 @@
 	 * IRQs after the sleep.
 	 */
 	spin_unlock_irq(&io_request_lock);
-	scsi_sleep(25*HZ/100);
+	scsi_sleep(50 * HZ/100);
 	spin_lock_irq(&io_request_lock);
 
 	/*
 	 * Release the SCSI reset.
 	 */
-	outb(CMD_NOP, REG_CMD(info));
+	fas216_cmd(info, CMD_NOP);
 
 	fas216_init_chip(info);
 
@@ -2502,56 +2673,56 @@
 	/*
 	 * Reset the chip.
 	 */
-	outb(CMD_RESETCHIP, REG_CMD(info));
+	fas216_writeb(info, REG_CMD, CMD_RESETCHIP);
 	udelay(50);
-	outb(CMD_NOP, REG_CMD(info));
+	fas216_writeb(info, REG_CMD, CMD_NOP);
 
 	/*
 	 * Check to see if control reg 2 is present.
 	 */
-	outb(0, REG_CNTL3(info));
-	outb(CNTL2_S2FE, REG_CNTL2(info));
+	fas216_writeb(info, REG_CNTL3, 0);
+	fas216_writeb(info, REG_CNTL2, CNTL2_S2FE);
 
 	/*
 	 * If we are unable to read back control reg 2
 	 * correctly, it is not present, and we have a
 	 * NCR53C90.
 	 */
-	if ((inb(REG_CNTL2(info)) & (~0xe0)) != CNTL2_S2FE)
+	if ((fas216_readb(info, REG_CNTL2) & (~0xe0)) != CNTL2_S2FE)
 		return TYPE_NCR53C90;
 
 	/*
 	 * Now, check control register 3
 	 */
-	outb(0, REG_CNTL2(info));
-	outb(0, REG_CNTL3(info));
-	outb(5, REG_CNTL3(info));
+	fas216_writeb(info, REG_CNTL2, 0);
+	fas216_writeb(info, REG_CNTL3, 0);
+	fas216_writeb(info, REG_CNTL3, 5);
 
 	/*
 	 * If we are unable to read the register back
 	 * correctly, we have a NCR53C90A
 	 */
-	if (inb(REG_CNTL3(info)) != 5)
+	if (fas216_readb(info, REG_CNTL3) != 5)
 		return TYPE_NCR53C90A;
 
 	/*
 	 * Now read the ID from the chip.
 	 */
-	outb(0, REG_CNTL3(info));
+	fas216_writeb(info, REG_CNTL3, 0);
 
-	outb(CNTL3_ADIDCHK, REG_CNTL3(info));
-	outb(0, REG_CNTL3(info));
+	fas216_writeb(info, REG_CNTL3, CNTL3_ADIDCHK);
+	fas216_writeb(info, REG_CNTL3, 0);
 
-	outb(CMD_RESETCHIP, REG_CMD(info));
-	udelay(5);
-	outb(CMD_WITHDMA | CMD_NOP, REG_CMD(info));
+	fas216_writeb(info, REG_CMD, CMD_RESETCHIP);
+	udelay(50);
+	fas216_writeb(info, REG_CMD, CMD_WITHDMA | CMD_NOP);
 
-	outb(CNTL2_ENF, REG_CNTL2(info));
-	outb(CMD_RESETCHIP, REG_CMD(info));
-	udelay(5);
-	outb(CMD_NOP, REG_CMD(info));
+	fas216_writeb(info, REG_CNTL2, CNTL2_ENF);
+	fas216_writeb(info, REG_CMD, CMD_RESETCHIP);
+	udelay(50);
+	fas216_writeb(info, REG_CMD, CMD_NOP);
 
-	rev     = inb(REG1_ID(info));
+	rev     = fas216_readb(info, REG_ID);
 	family  = rev >> 3;
 	rev    &= 7;
 
@@ -2577,33 +2748,106 @@
 	return TYPE_NCR53C9x;
 }
 
-/* Function: int fas216_init(struct Scsi_Host *instance)
- * Purpose : initialise FAS/NCR/AMD SCSI ic.
- * Params  : instance - a driver-specific filled-out structure
- * Returns : 0 on success
+/**
+ * fas216_reset_state - Initialise driver internal state
+ * @info: state to initialise
+ *
+ * Initialise driver internal state
  */
-int fas216_init(struct Scsi_Host *instance)
+static void fas216_reset_state(FAS216_Info *info)
 {
-	FAS216_Info *info = (FAS216_Info *)instance->hostdata;
-	int type;
+	int i;
+
+	fas216_checkmagic(info);
+
+	fas216_bus_reset(info);
 
-	info->magic_start = MAGIC;
-	info->magic_end   = MAGIC;
-	info->host        = instance;
-	info->scsi.cfg[0] = instance->this_id;
-	info->scsi.cfg[1] = CNTL2_ENF | CNTL2_S2FE;
-	info->scsi.cfg[2] = info->ifcfg.cntl3 | CNTL3_ADIDCHK | CNTL3_G2CB;
+	/*
+	 * Clear out all stale info in our state structure
+	 */
+	memset(info->busyluns, 0, sizeof(info->busyluns));
+	info->scsi.disconnectable = 0;
+	info->scsi.aborting = 0;
+
+	for (i = 0; i < 8; i++) {
+		info->device[i].parity_enabled	= 0;
+		info->device[i].parity_check	= 1;
+	}
+
+	/*
+	 * Drain all commands on disconnected queue
+	 */
+	while (queue_remove(&info->queues.disconnected) != NULL);
+
+	/*
+	 * Remove executing commands.
+	 */
+	info->SCpnt     = NULL;
+	info->reqSCpnt  = NULL;
+	info->rstSCpnt  = NULL;
+	info->origSCpnt = NULL;
+}
+
+/**
+ * fas216_init - initialise FAS/NCR/AMD SCSI structures.
+ * @host: a driver-specific filled-out structure
+ *
+ * Initialise FAS/NCR/AMD SCSI structures.
+ * Returns: 0 on success
+ */
+int fas216_init(struct Scsi_Host *host)
+{
+	FAS216_Info *info = (FAS216_Info *)host->hostdata;
+
+	info->magic_start    = MAGIC;
+	info->magic_end      = MAGIC;
+	info->host           = host;
+	info->scsi.cfg[0]    = host->this_id | CNTL1_PERE;
+	info->scsi.cfg[1]    = CNTL2_ENF | CNTL2_S2FE;
+	info->scsi.cfg[2]    = info->ifcfg.cntl3 |
+			       CNTL3_ADIDCHK | CNTL3_QTAG | CNTL3_G2CB | CNTL3_LBTM;
+	info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod);
+
+	info->rst_dev_status = -1;
+	info->rst_bus_status = -1;
+	init_waitqueue_head(&info->eh_wait);
+	init_timer(&info->eh_timer);
+	info->eh_timer.data  = (unsigned long)info;
+	info->eh_timer.function = fas216_eh_timer;
+	
+	spin_lock_init(&info->host_lock);
 
 	memset(&info->stats, 0, sizeof(info->stats));
 
 	msgqueue_initialise(&info->scsi.msgs);
 
 	if (!queue_initialise(&info->queues.issue))
-		return 1;
+		return -ENOMEM;
 
 	if (!queue_initialise(&info->queues.disconnected)) {
 		queue_free(&info->queues.issue);
-		return 1;
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/**
+ * fas216_add - initialise FAS/NCR/AMD SCSI ic.
+ * @host: a driver-specific filled-out structure
+ *
+ * Initialise FAS/NCR/AMD SCSI ic.
+ * Returns: 0 on success
+ */
+int fas216_add(struct Scsi_Host *host)
+{
+	FAS216_Info *info = (FAS216_Info *)host->hostdata;
+	int type;
+
+	if (info->ifcfg.clockrate <= 10 || info->ifcfg.clockrate > 40) {
+		printk(KERN_CRIT "fas216: invalid clock rate %u MHz\n",
+			info->ifcfg.clockrate);
+		return -EINVAL;
 	}
 
 	fas216_reset_state(info);
@@ -2622,78 +2866,53 @@
 	 * the resulting reset interrupt, so mask it
 	 * out.
 	 */
-	outb(info->scsi.cfg[0] | CNTL1_DISR, REG_CNTL1(info));
-	outb(CMD_RESETSCSI, REG_CMD(info));
+	fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0] | CNTL1_DISR);
+	fas216_writeb(info, REG_CMD, CMD_RESETSCSI);
 
 	/*
 	 * scsi standard says wait 250ms
 	 */
 	spin_unlock_irq(&io_request_lock);
-	scsi_sleep(25*HZ/100);
+	scsi_sleep(100*HZ/100);
 	spin_lock_irq(&io_request_lock);
 
-	outb(info->scsi.cfg[0], REG_CNTL1(info));
-	inb(REG_INST(info));
+	fas216_writeb(info, REG_CNTL1, info->scsi.cfg[0]);
+	fas216_readb(info, REG_INST);
 
 	fas216_checkmagic(info);
 
 	return 0;
 }
 
-/* Function: int fas216_release(struct Scsi_Host *instance)
- * Purpose : release all resources and put everything to bed for
- *           FAS/NCR/AMD SCSI ic.
- * Params  : instance - a driver-specific filled-out structure
- * Returns : 0 on success
- */
-int fas216_release(struct Scsi_Host *instance)
+void fas216_remove(struct Scsi_Host *host)
 {
-	FAS216_Info *info = (FAS216_Info *)instance->hostdata;
+	FAS216_Info *info = (FAS216_Info *)host->hostdata;
 
 	fas216_checkmagic(info);
 
-	outb(CMD_RESETCHIP, REG_CMD(info));
-	queue_free(&info->queues.disconnected);
-	queue_free(&info->queues.issue);
-
-	return 0;
+	fas216_writeb(info, REG_CMD, CMD_RESETCHIP);
 }
 
-/*
- * Function: int fas216_info(FAS216_Info *info, char *buffer)
- * Purpose : generate a string containing information about this
- *	     host.
- * Params  : info   - FAS216 host information
- *	     buffer - string buffer to build string
- * Returns : size of built string
+/**
+ * fas216_release - release all resources for FAS/NCR/AMD SCSI ic.
+ * @host: a driver-specific filled-out structure
+ *
+ * release all resources and put everything to bed for FAS/NCR/AMD SCSI ic.
  */
-int fas216_info(FAS216_Info *info, char *buffer)
+void fas216_release(struct Scsi_Host *host)
 {
-	char *p = buffer;
+	FAS216_Info *info = (FAS216_Info *)host->hostdata;
 
-	p += sprintf(p, "(%s) at port 0x%08lX ",
-		     info->scsi.type, info->host->io_port);
-
-	if (info->host->irq != NO_IRQ)
-		p += sprintf(p, "irq %d ", info->host->irq);
-	else
-		p += sprintf(p, "no irq ");
-
-	if (info->host->dma_channel != NO_DMA)
-		p += sprintf(p, "dma %d ", info->host->dma_channel);
-	else
-		p += sprintf(p, "no dma ");
-
-	return p - buffer;
+	queue_free(&info->queues.disconnected);
+	queue_free(&info->queues.issue);
 }
 
 int fas216_print_host(FAS216_Info *info, char *buffer)
 {
-	
 	return sprintf(buffer,
 			"\n"
 			"Chip    : %s\n"
-			" Address: 0x%08lX\n"
+			" Address: 0x%08lx\n"
 			" IRQ    : %d\n"
 			" DMA    : %d\n",
 			info->scsi.type, info->host->io_port,
@@ -2702,8 +2921,9 @@
 
 int fas216_print_stats(FAS216_Info *info, char *buffer)
 {
-	return sprintf(buffer,
-			"\n"
+	char *p = buffer;
+
+	p += sprintf(p, "\n"
 			"Command Statistics:\n"
 			" Queued     : %u\n"
 			" Issued     : %u\n"
@@ -2720,6 +2940,8 @@
 			info->stats.writes,	 info->stats.miscs,
 			info->stats.disconnects, info->stats.aborts,
 			info->stats.bus_resets,	 info->stats.host_resets);
+
+	return p - buffer;
 }
 
 int fas216_print_device(FAS216_Info *info, Scsi_Device *scd, char *buffer)
@@ -2738,7 +2960,9 @@
 			     scd->tagged_queue ? "en" : "dis",
 			     scd->current_tag);
 
-	p += sprintf(p, "\n  Transfers : %d-bit ",
+	p += sprintf(p, "%s\n", dev->parity_enabled ? "parity" : "");
+
+	p += sprintf(p, "  Transfers : %d-bit ",
 		     8 << dev->wide_xfer);
 
 	if (dev->sof)
@@ -2750,11 +2974,12 @@
 	return p - buffer;
 }
 
-EXPORT_SYMBOL(fas216_info);
 EXPORT_SYMBOL(fas216_init);
+EXPORT_SYMBOL(fas216_add);
 EXPORT_SYMBOL(fas216_queue_command);
 EXPORT_SYMBOL(fas216_command);
 EXPORT_SYMBOL(fas216_intr);
+EXPORT_SYMBOL(fas216_remove);
 EXPORT_SYMBOL(fas216_release);
 EXPORT_SYMBOL(fas216_eh_abort);
 EXPORT_SYMBOL(fas216_eh_device_reset);

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