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

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

diff -u --recursive --new-file v2.1.100/linux/drivers/acorn/scsi/fas216.c linux/drivers/acorn/scsi/fas216.c
@@ -3,8 +3,9 @@
  *
  * Copyright (C) 1997 Russell King
  *
- * Based in information in qlogicfas.c by Tom Zerucha, Michael Griffith, and
- * other sources.
+ * Based on information in qlogicfas.c by Tom Zerucha, Michael Griffith, and
+ * other sources, including:
+ *   the AMD Am53CF94 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:
@@ -18,6 +19,13 @@
  *  14-09-1997	RMK	Started disconnect support
  *  08-02-1998	RMK	Corrected real DMA support
  *  15-02-1998	RMK	Started sync xfer support
+ *  06-04-1998	RMK	Tightened conditions for printing incomplete
+ *			transfers
+ *  02-05-1998	RMK	Added extra checks in fas216_reset
+ *
+ * Todo:
+ *  - tighten up the MESSAGE_REJECT support.
+ *  - allow individual devices to enable sync xfers.
  */
 
 #include <linux/module.h>
@@ -31,6 +39,7 @@
 #include <linux/stat.h>
 
 #include <asm/delay.h>
+#include <asm/dma.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <asm/ecard.h>
@@ -43,7 +52,34 @@
 
 #define VER_MAJOR	0
 #define VER_MINOR	0
-#define VER_PATCH	2
+#define VER_PATCH	3
+
+#define SCSI2_TAG
+
+/* NOTE: SCSI2 Synchronous transfers *require* DMA according to
+ *  the data sheet.  This restriction is crazy, especially when
+ *  you only want to send 16 bytes!  What were the guys who
+ *  designed this chip on at that time?  Did they read the SCSI2
+ *  spec at all?  The following sections are taken from the SCSI2
+ *  standard (s2r10) concerning this:
+ *
+ * > IMPLEMENTORS NOTES:
+ * >   (1)  Re-negotiation at every selection is not recommended, since a
+ * >   significant performance impact is likely.
+ *
+ * >  The implied synchronous agreement shall remain in effect until a BUS DEVICE
+ * >  RESET message is received, until a hard reset condition occurs, or until one
+ * >  of the two SCSI devices elects to modify the agreement.  The default data
+ * >  transfer mode is asynchronous data transfer mode.  The default data transfer
+ * >  mode is entered at power on, after a BUS DEVICE RESET message, or after a hard
+ * >  reset condition.
+ *
+ *  In total, this means that once you have elected to use synchronous
+ *  transfers, you must always use DMA.
+ *
+ *  I was thinking that this was a good chip until I found this restriction ;(
+ */
+#define SCSI2_SYNC
 
 #undef NO_DISCONNECTS
 #undef DEBUG_CONNECT
@@ -51,9 +87,95 @@
 #undef DEBUG_FUNCTIONDONE
 #undef DEBUG_MESSAGES
 
-static char *fas216_bus_phase (int stat)
+#undef CHECK_STRUCTURE
+
+static struct { int stat, ssr, isr, ph; } list[8];
+static int ptr;
+
+static void fas216_dumpstate(FAS216_Info *info)
+{
+	printk("FAS216 registers:\n");
+	printk("    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)),  inb(REG_STAT(info)),
+		inb(REG_INST(info)), inb(REG_IS(info)),
+		inb(REG_CFIS(info)));
+	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)));
+}
+
+static void fas216_dumpinfo(FAS216_Info *info)
+{
+	static int used = 0;
+	int i;
+
+	if (used++)
+		return;
+
+	printk("FAS216_Info = \n");
+	printk("  { magic_start=%lX host=%p SCpnt=%p origSCpnt=%p\n",
+		info->magic_start, info->host, info->SCpnt,
+		info->origSCpnt);
+	printk("    scsi={ io_port=%X io_shift=%X irq=%X cfg={ %X %X %X %X }\n",
+		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("      msgs async_stp=%X last_message=%X disconnectable=%d aborting=%d }\n",
+		info->scsi.async_stp, info->scsi.last_message,
+		info->scsi.disconnectable, info->scsi.aborting);
+	printk("    stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X disconnects=%X aborts=%X resets=%X }\n",
+		info->stats.queues, info->stats.removes, info->stats.fins,
+		info->stats.reads, info->stats.writes, info->stats.miscs,
+		info->stats.disconnects, info->stats.aborts, info->stats.resets);
+	printk("    ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X }\n",
+		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 negstate=%X }\n",
+			i, info->busyluns[i], i,
+			info->device[i].disconnect_ok, info->device[i].stp,
+			info->device[i].sof, info->device[i].negstate);
+	}
+	printk("    dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n",
+		info->dma.transfer_type, info->dma.setup,
+		info->dma.pseudo, info->dma.stop);
+	printk("    internal_done=%X magic_end=%lX\n",
+		info->internal_done, info->magic_end);
+}
+
+#ifdef CHECK_STRUCTURE
+static void fas216_checkmagic(FAS216_Info *info, const char *func)
+{
+	int corruption = 0;
+	if (info->magic_start != MAGIC) {
+		printk(KERN_CRIT "FAS216 Error: magic at start corrupted\n");
+		corruption++;
+	}
+	if (info->magic_end != MAGIC) {
+		printk(KERN_CRIT "FAS216 Error: magic at end corrupted\n");
+		corruption++;
+	}
+	if (corruption) {
+		fas216_dumpinfo(info);
+		panic("scsi memory space corrupted in %s", func);
+	}
+}
+#else
+#define fas216_checkmagic(info,func)
+#endif
+
+static const char *fas216_bus_phase(int stat)
 {
-	static char *phases[] = {
+	static const char *phases[] = {
 		"DATA OUT", "DATA IN",
 		"COMMAND", "STATUS",
 		"MISC OUT", "MISC IN",
@@ -63,7 +185,26 @@
 	return phases[stat & STAT_BUSMASK];
 }
 
-static char fas216_target (FAS216_Info *info)
+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_MESSAGESENT:	return "message sent";
+	case PHASE_RECONNECTED:	return "reconnected";
+	case PHASE_DATAOUT:	return "data out";
+	case PHASE_DATAIN:	return "data in";
+	case PHASE_MSGOUT:	return "message out";
+	case PHASE_MSGIN:	return "message in";
+	case PHASE_AFTERMSGOUT:	return "after message out";
+	case PHASE_STATUS:	return "status";
+	case PHASE_DISCONNECT:	return "disconnect";
+	case PHASE_DONE:	return "done";
+	default:		return "???";
+	}
+}
+
+static char fas216_target(FAS216_Info *info)
 {
 	if (info->SCpnt)
 		return '0' + info->SCpnt->target;
@@ -71,15 +212,41 @@
 		return 'H';
 }
 
-static void fas216_done (FAS216_Info *info, unsigned int result);
+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;
+
+	ptr = (ptr + 1) & 7;
+}
+
+static void print_debug_list(void)
+{
+	int i;
+
+	i = ptr;
+
+	printk(KERN_ERR "SCSI state trail: ");
+	do {
+		printk("%02X:%02X:%02X:%02X ",
+			list[i].stat, list[i].ssr,
+			list[i].isr, list[i].ph);
+		i = (i + 1) & 7;
+	} while (i != ptr);
+	printk("\n");
+}
+
+static void fas216_done(FAS216_Info *info, unsigned int result);
 
-/* Function: int fas216_clockrate (unsigned int clock)
+/* 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)
+static int fas216_clockrate(int clock)
 {
 	if (clock <= 10 || clock > 40) {
 		printk(KERN_CRIT
@@ -102,6 +269,8 @@
 {
 	int value = (info->ifcfg.clockrate * ns) / 1000;
 
+	fas216_checkmagic(info, "fas216_syncperiod");
+
 	if (value < 4)
 		value = 4;
 	else if (value > 35)
@@ -110,16 +279,21 @@
 	return value & 31;
 }
 
-/* Function: void fas216_updateptrs (FAS216_Info *info, int bytes_transferred)
+/* 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
  */
 static void
-fas216_updateptrs (FAS216_Info *info, int bytes_transferred)
+fas216_updateptrs(FAS216_Info *info, int bytes_transferred)
 {
-	unsigned char *ptr = info->scsi.SCp.ptr;
-	unsigned int residual = info->scsi.SCp.this_residual;
+	unsigned char *ptr;
+	unsigned int residual;
+
+	fas216_checkmagic(info, "fas216_updateptrs");
+
+	ptr = info->scsi.SCp.ptr;
+	residual = info->scsi.SCp.this_residual;
 
 	info->SCpnt->request_bufflen -= bytes_transferred;
 
@@ -144,50 +318,61 @@
 	info->scsi.SCp.this_residual = residual;
 }
 
-/* Function: void fas216_pio (FAS216_Info *info, fasdmadir_t direction)
+/* 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
  */
 static void
-fas216_pio (FAS216_Info *info, fasdmadir_t direction)
+fas216_pio(FAS216_Info *info, fasdmadir_t direction)
 {
-	unsigned int length = info->scsi.SCp.this_residual;
-	char *ptr = info->scsi.SCp.ptr;
+	unsigned int residual;
+	char *ptr;
+	int correction;
+
+	fas216_checkmagic(info, "fas216_pio");
+
+	residual = info->scsi.SCp.this_residual;
+	ptr = info->scsi.SCp.ptr;
 
 	if (direction == DMA_OUT) {
-	    	while (length > 0) {
+	    	while (residual > 0) {
 			if ((inb(REG_CFIS(info)) & CFIS_CF) < 8) {
 				outb(*ptr++, REG_FF(info));
-				length -= 1;
+				residual -= 1;
 			} else if (inb(REG_STAT(info)) & STAT_INT)
 				break;
 		}
+		correction = inb(REG_CFIS(info)) & CFIS_CF;
 	} else {
-	    	while (length > 0) {
+	    	while (residual > 0) {
 			if ((inb(REG_CFIS(info)) & CFIS_CF) != 0) {
 				*ptr++ = inb(REG_FF(info));
-				length -= 1;
+				residual -= 1;
 			} else if (inb(REG_STAT(info)) & STAT_INT)
 				break;
 		}
+		correction = 0;
 	}
 
-	if (length == 0) {
+	ptr -= correction;
+	residual += correction;
+
+	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;
-			length = info->scsi.SCp.buffer->length;
+			residual = info->scsi.SCp.buffer->length;
 		} else {
 			ptr = NULL;
-			length = 0;
+			residual = 0;
 		}
 	}
 
 	info->scsi.SCp.ptr = ptr;
-	info->scsi.SCp.this_residual = length;
+	info->scsi.SCp.this_residual = residual;
 }
 
 /* Function: void fas216_starttransfer(FAS216_Info *info,
@@ -197,77 +382,81 @@
  *           direction - transfer direction (DMA_OUT/DMA_IN)
  */
 static void
-fas216_starttransfer(FAS216_Info *info, fasdmadir_t direction)
+fas216_starttransfer(FAS216_Info *info, fasdmadir_t direction, int flush_fifo)
 {
 	fasdmatype_t dmatype;
 
+	fas216_checkmagic(info, "fas216_starttransfer");
+
 	info->scsi.phase = (direction == DMA_OUT) ?
 				PHASE_DATAOUT : PHASE_DATAIN;
 
-	if (info->dma.transfer_type == fasdma_real_block ||
-	    info->dma.transfer_type == fasdma_real_all) {
+	if (info->dma.transfer_type != fasdma_none &&
+	    info->dma.transfer_type != fasdma_pio) {
 		unsigned long total, residual;
 
-		if (info->dma.transfer_type == fasdma_real_block)
-			total = info->scsi.SCp.this_residual;
-		else
+		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_updateptrs(info, total - residual);
 	}
+	info->dma.transfer_type = fasdma_none;
 
 	if (!info->scsi.SCp.ptr) {
-		printk ("scsi%d.%c: null buffer passed to "
+		printk("scsi%d.%c: null buffer passed to "
 			"fas216_starttransfer\n", info->host->host_no,
-			fas216_target (info));
+			fas216_target(info));
 		return;
 	}
 
-	dmatype = fasdma_none;
+	/* 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 (info->device[info->SCpnt->target].sof && info->dma.setup)
+		dmatype = fasdma_real_all;
+	else
+		dmatype = fasdma_pio;
+
 	if (info->dma.setup)
 		dmatype = info->dma.setup(info->host, &info->scsi.SCp,
-					  direction);
-
+					  direction, dmatype);
 	info->dma.transfer_type = dmatype;
 
 	switch (dmatype) {
-	case fasdma_none:
+	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_NOP | CMD_WITHDMA, REG_CMD(info));
 		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		fas216_pio (info, direction);
+		fas216_pio(info, direction);
 		break;
 
-	case fasdma_pseudo: {
-		int transferred;
-
+	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_NOP | CMD_WITHDMA, REG_CMD(info));
 		outb(CMD_TRANSFERINFO | CMD_WITHDMA, REG_CMD(info));
-
-		transferred =
-			info->dma.pseudo(info->host, &info->scsi.SCp,
-					 direction, info->SCpnt->transfersize);
-
-		fas216_updateptrs (info, transferred);
-		}
+		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_NOP | CMD_WITHDMA, REG_CMD(info));
-
 		outb(CMD_TRANSFERINFO | CMD_WITHDMA, REG_CMD(info));
 		break;
 
@@ -275,58 +464,66 @@
 		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_NOP | CMD_WITHDMA, REG_CMD(info));
-
 		outb(CMD_TRANSFERINFO | CMD_WITHDMA, REG_CMD(info));
 		break;
+
+	default:
+		printk(KERN_ERR "scsi%d.%d: invalid FAS216 DMA type\n",
+		       info->host->host_no, fas216_target(info));
+		break;
 	}
 }
 
-/* Function: void fas216_stoptransfer (FAS216_Info *info)
+/* 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
  */
 static void
-fas216_stoptransfer (FAS216_Info *info)
+fas216_stoptransfer(FAS216_Info *info)
 {
-	if (info->dma.transfer_type == fasdma_real_block ||
-	    info->dma.transfer_type == fasdma_real_all) {
+	fas216_checkmagic(info, "fas216_stoptransfer");
+
+	if (info->dma.transfer_type != fasdma_none &&
+	    info->dma.transfer_type != fasdma_pio) {
 		unsigned long total, residual;
 
-		if (info->dma.stop)
-			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);
 
-		if (info->dma.transfer_type == fasdma_real_block)
-			total = info->scsi.SCp.this_residual;
-		else
+		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);
-
+		fas216_updateptrs(info, total - residual);
 		info->dma.transfer_type = fasdma_none;
 	}
 }
 
-/* Function: void fas216_disconnected_intr (FAS216_Info *info)
+/* Function: void fas216_disconnected_intr(FAS216_Info *info)
  * Purpose : handle device disconnection
  * Params  : info - interface from which device disconnected from
  */
 static void
-fas216_disconnect_intr (FAS216_Info *info)
+fas216_disconnect_intr(FAS216_Info *info)
 {
+	fas216_checkmagic(info, "fas216_disconnected_intr");
+
 #ifdef DEBUG_CONNECT
 	printk("scsi%d.%c: disconnect phase=%02X\n", info->host->host_no,
-		fas216_target (info), info->scsi.phase);
+		fas216_target(info), info->scsi.phase);
 #endif
-	msgqueue_flush (&info->scsi.msgs);
+	msgqueue_flush(&info->scsi.msgs);
 
 	switch (info->scsi.phase) {
 	case PHASE_SELECTION:			/* while selecting - no target		*/
-		fas216_done (info, DID_NO_CONNECT);
+		fas216_done(info, DID_NO_CONNECT);
 		break;
 
 	case PHASE_DISCONNECT:			/* message in - disconnecting		*/
@@ -338,33 +535,36 @@
 		break;
 
 	case PHASE_DONE:			/* at end of command - complete		*/
-		fas216_done (info, DID_OK);
+		fas216_done(info, DID_OK);
 		break;
 
 	case PHASE_AFTERMSGOUT:			/* message out - possible ABORT message	*/
 		if (info->scsi.last_message == ABORT) {
 			info->scsi.aborting = 0;
-			fas216_done (info, DID_ABORT);
+			fas216_done(info, DID_ABORT);
 			break;
 		}
 
 	default:				/* huh?					*/
-		printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %d\n",
-			info->host->host_no, fas216_target (info), info->scsi.phase);
+		printk(KERN_ERR "scsi%d.%c: unexpected disconnect in phase %s\n",
+			info->host->host_no, fas216_target(info), fas216_drv_phase(info));
+		print_debug_list();
 		fas216_stoptransfer(info);
-		fas216_done (info, DID_ERROR);
+		fas216_done(info, DID_ERROR);
 		break;
 	}
 }
 
-/* Function: void fas216_reselected_intr (FAS216_Info *info)
+/* Function: void fas216_reselected_intr(FAS216_Info *info)
  * Purpose : Start reconnection of a device
  * Params  : info - interface which was reselected
  */
 static void
-fas216_reselected_intr (FAS216_Info *info)
+fas216_reselected_intr(FAS216_Info *info)
 {
-    unsigned char target, identify_msg, ok;
+	unsigned char target, identify_msg, ok;
+
+	fas216_checkmagic(info, "fas216_reselected_intr");
 
 	if (info->scsi.phase == PHASE_SELECTION && info->SCpnt) {
 		Scsi_Cmnd *SCpnt = info->SCpnt;
@@ -378,555 +578,733 @@
 
 #ifdef DEBUG_CONNECT
 	printk("scsi%d.%c: reconnect phase=%02X\n", info->host->host_no,
-		fas216_target (info), info->scsi.phase);
+		fas216_target(info), info->scsi.phase);
 #endif
 
-    msgqueue_flush (&info->scsi.msgs);
-
-    if ((inb(REG_CFIS(info)) & CFIS_CF) != 2) {
-	printk (KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n",
-		info->host->host_no);
-	outb(CMD_SETATN, REG_CMD(info));
-	msgqueue_addmsg (&info->scsi.msgs, 1, MESSAGE_REJECT);
-	info->scsi.phase = PHASE_MSGOUT;
-	outb(CMD_MSGACCEPTED, REG_CMD(info));
-	return;
-    }
+	msgqueue_flush(&info->scsi.msgs);
 
-    target = inb(REG_FF(info));
-    identify_msg = inb(REG_FF(info));
+	if ((inb(REG_CFIS(info)) & CFIS_CF) != 2) {
+		printk(KERN_ERR "scsi%d.H: incorrect number of bytes after reselect\n",
+			info->host->host_no);
+		outb(CMD_SETATN, REG_CMD(info));
+		msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+		info->scsi.phase = PHASE_MSGOUT;
+		outb(CMD_MSGACCEPTED, REG_CMD(info));
+		return;
+	}
 
-    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;
-    }
+	target = inb(REG_FF(info));
+	identify_msg = inb(REG_FF(info));
 
-    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;
-    }
+	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;
+	}
 
-    if (!ok) {
-	/*
-	 * Something went wrong - abort the command on
-	 * the target.  Should this be INITIATOR_ERROR ?
-	 */
-	outb(CMD_SETATN, REG_CMD(info));
-	msgqueue_addmsg (&info->scsi.msgs, 1, ABORT);
-	info->scsi.phase = PHASE_MSGOUT;
-	outb(CMD_MSGACCEPTED, REG_CMD(info));
-	return;
-    }
+	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;
+	}
 
-    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;
+	if (!ok) {
+		/*
+		 * Something went wrong - abort the command on
+		 * the target.  Should this be INITIATOR_ERROR ?
+		 */
+		outb(CMD_SETATN, REG_CMD(info));
+		msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
+		info->scsi.phase = PHASE_MSGOUT;
+		outb(CMD_MSGACCEPTED, REG_CMD(info));
+		return;
+	}
 
-    if (!ok && queue_probetgtlun (&info->queues.disconnected, target, identify_msg))
-	ok = 1;
+	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;
 
-    if (ok) {
-	info->scsi.phase = PHASE_RECONNECTED;
-	outb(target, REG_SDID(info));
-    } else {
-	/*
-	 * Our command structure not found - abort the command on the target
-	 * Should this be INITIATOR_ERROR ?
-	 */
-	outb(CMD_SETATN, REG_CMD(info));
-	msgqueue_addmsg (&info->scsi.msgs, 1, ABORT);
-	info->scsi.phase = PHASE_MSGOUT;
-    }
-    outb(CMD_MSGACCEPTED, REG_CMD(info));
+	ok = 0;
+	if (info->scsi.disconnectable && info->SCpnt &&
+	    info->SCpnt->target == target && info->SCpnt->lun == identify_msg)
+		ok = 1;
+
+	if (!ok && queue_probetgtlun(&info->queues.disconnected, target, identify_msg))
+		ok = 1;
+
+	if (ok) {
+		info->scsi.phase = PHASE_RECONNECTED;
+		outb(target, REG_SDID(info));
+	} else {
+		/*
+		 * Our command structure not found - abort the command on the target
+		 * Should this be INITIATOR_ERROR ?
+		 */
+		outb(CMD_SETATN, REG_CMD(info));
+		msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
+		info->scsi.phase = PHASE_MSGOUT;
+	}
+	outb(CMD_MSGACCEPTED, REG_CMD(info));
 }
 
-/* Function: void fas216_finish_reconnect (FAS216_Info *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_finish_reconnect(FAS216_Info *info)
 {
+	fas216_checkmagic(info, "fas216_reconnect");
+
 #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);
+	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
 
-    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) {
+	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));
+			printk("scsi%d.%c: reconnected",
+				info->host->host_no, fas216_target(info));
 #endif
-	} else {
-	    queue_add_cmd_tail (&info->queues.disconnected, info->SCpnt);
+		} 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));
+			printk("scsi%d.%c: had to move command to disconnected queue\n",
+				info->host->host_no, fas216_target(info));
 #endif
-	    info->SCpnt = NULL;
+			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);
+	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));
+		printk("scsi%d.%c: had to get command",
+			info->host->host_no, fas216_target(info));
 #endif
-    }
-    if (!info->SCpnt) {
-	outb(CMD_SETATN, REG_CMD(info));
-	msgqueue_addmsg (&info->scsi.msgs, 1, ABORT);
-	info->scsi.phase = PHASE_MSGOUT;
-	info->scsi.aborting = 1;
-    } else {
-	/*
-	 * Restore data pointer from SAVED data pointer
-	 */
-	info->scsi.SCp = info->SCpnt->SCp;
+	}
+	if (!info->SCpnt) {
+		outb(CMD_SETATN, REG_CMD(info));
+		msgqueue_addmsg(&info->scsi.msgs, 1, ABORT);
+		info->scsi.phase = PHASE_MSGOUT;
+		info->scsi.aborting = 1;
+	} else {
+		/*
+		 * 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);
+		printk(", data pointers: [%p, %X]",
+			info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
 #endif
-    }
+	}
 #ifdef DEBUG_CONNECT
-    printk ("\n");
+	printk("\n");
 #endif
 }
 
-/* Function: void fas216_message (FAS216_Info *info)
+/* 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_message(FAS216_Info *info)
 {
-    unsigned char message[16];
-    unsigned int msglen = 1;
+	unsigned char message[16];
+	unsigned int msglen = 1;
+
+	fas216_checkmagic(info, "fas216_message");
+
+	message[0] = inb(REG_FF(info));
 
-    message[0] = inb(REG_FF(info));
+	if (message[0] == EXTENDED_MESSAGE) {
+		int tout;
+		outb(CMD_MSGACCEPTED, REG_CMD(info));
+		for (tout = 1000000; tout; tout--)
+			if (inb(REG_STAT(info)) & STAT_INT)
+				break;
+		inb(REG_INST(info));
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		for (tout = 1000000; tout; tout--)
+			if (inb(REG_STAT(info)) & STAT_INT)
+				break;
+		inb(REG_INST(info));
 
-    if (message[0] == EXTENDED_MESSAGE) {
-	message[1] = inb(REG_FF(info));
+		message[1] = inb(REG_FF(info));
 
-	for (msglen = 2; msglen < message[1]; msglen++)
-	    message[msglen] = inb(REG_FF(info));
-    }
+		for (msglen = 2; msglen < message[1] + 2; msglen++) {
+			outb(CMD_MSGACCEPTED, REG_CMD(info));
+			for (tout = 1000000; tout; tout--)
+				if (inb(REG_STAT(info)) & STAT_INT)
+					break;
+			inb(REG_INST(info));
+			outb(CMD_TRANSFERINFO, REG_CMD(info));
+			for (tout = 1000000; tout; tout--)
+				if (inb(REG_STAT(info)) & STAT_INT)
+					break;
+			inb(REG_INST(info));
+
+			message[msglen] = inb(REG_FF(info));
+		}
+	}
 
 #ifdef DEBUG_MESSAGES
-    {
-	int i;
+	{
+		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;
+	}
 
-	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;
-    }	
-
-    switch (message[0]) {
-    case COMMAND_COMPLETE:
-	printk ("fas216: command complete with no status in MESSAGE_IN?\n");
-	break;
+	switch (message[0]) {
+	case COMMAND_COMPLETE:
+		printk("fas216: command complete with no status in MESSAGE_IN?\n");
+		break;
 
-    case SAVE_POINTERS:
-	/*
-	 * Save current data pointer to SAVED data pointer
-	 */
-	info->SCpnt->SCp = info->scsi.SCp;
+	case SAVE_POINTERS:
+		/*
+		 * Save current data pointer to SAVED data pointer
+		 */
+		info->SCpnt->SCp = info->scsi.SCp;
 #if defined (DEBUG_MESSAGES) || defined (DEBUG_CONNECT)
-	printk ("scsi%d.%c: save data pointers: [%p, %X]\n",
-		info->host->host_no, fas216_target (info),
-		info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+		printk("scsi%d.%c: save data pointers: [%p, %X]\n",
+			info->host->host_no, fas216_target(info),
+			info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
 #endif
-	break;
+		break;
 
-    case RESTORE_POINTERS:
-	/*
-	 * Restore current data pointer from SAVED data pointer
-	 */
-	info->scsi.SCp = info->SCpnt->SCp;
+	case RESTORE_POINTERS:
+		/*
+		 * 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),
-		info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+		printk("scsi%d.%c: restore data pointers: [%p, %X]\n",
+			info->host->host_no, fas216_target(info),
+			info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
 #endif
-	break;
+		break;
+
+	case DISCONNECT:
+		info->scsi.phase = PHASE_DISCONNECT;
+		break;
+
+	case MESSAGE_REJECT:
+		printk("scsi%d.%c: reject, last message %04X\n",
+			info->host->host_no, fas216_target(info),
+			info->scsi.last_message);
+		break;
+
+	case SIMPLE_QUEUE_TAG:
+		/* handled above */
+		printk("scsi%d.%c: reconnect queue tag %02X\n",
+			info->host->host_no, fas216_target(info),
+			message[1]);
+		break;
+
+	case EXTENDED_MESSAGE:
+		switch (message[2]) {
+		case EXTENDED_SDTR:	/* Sync transfer negociation request/reply */
+			switch (info->device[info->SCpnt->target].negstate) {
+			case syncneg_invalid:
+				msgqueue_flush(&info->scsi.msgs);
+				outb(CMD_SETATN, REG_CMD(info));
+				msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+				info->scsi.phase = PHASE_MSGOUT;
+				break;
 
-    case DISCONNECT:
-	info->scsi.phase = PHASE_DISCONNECT;
-	break;
-
-    case MESSAGE_REJECT:
-	printk ("scsi%d.%c: reject, last message %04X\n",
-		info->host->host_no, fas216_target (info),
-		info->scsi.last_message);
-	break;
-
-    case SIMPLE_QUEUE_TAG:
-	/* handled above */
-	printk ("scsi%d.%c: reconnect queue tag %02X\n",
-		info->host->host_no, fas216_target (info),
-		message[1]);
-	break;
-
-    case EXTENDED_MESSAGE:
-	switch (message[2]) {
-	case EXTENDED_SDTR:	/* Sync transfer negociation request/reply */
+			default:
+				if (message[4] > info->ifcfg.sync_max_depth)
+					message[4] = info->ifcfg.sync_max_depth;
+				if (message[3] < 1000 / info->ifcfg.clockrate)
+					message[3] = 1000 / info->ifcfg.clockrate;
+
+				outb(CMD_SETATN, REG_CMD(info));
+				msgqueue_addmsg(&info->scsi.msgs, 5,
+						EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
+						message[3], message[4]);
+				info->scsi.phase = PHASE_MSGOUT;
+			case syncneg_sent:
+				info->device[info->SCpnt->target].negstate = syncneg_complete;
+				info->device[info->SCpnt->target].sof = message[4];
+				info->device[info->SCpnt->target].stp =
+					fas216_syncperiod(info, message[3] * 4);
+				printk(KERN_NOTICE "scsi%d.%c: using synchronous transfer, offset %d, %d ns\n",
+					info->host->host_no, fas216_target(info), message[4], message[3] * 4);
+				outb(info->device[info->SCpnt->target].sof, REG_SOF(info));
+				outb(info->device[info->SCpnt->target].stp, REG_STP(info));
+				break;
+			}
+			break;
+
+		case EXTENDED_WDTR:	/* Wide transfer negociation request/reply */
+			/* We don't do wide transfers - reject message */
+		default:
+			printk("scsi%d.%c: unrecognised extended message %02X, rejecting\n",
+				info->host->host_no, fas216_target(info),
+				message[2]);
+			msgqueue_flush(&info->scsi.msgs);
+			outb(CMD_SETATN, REG_CMD(info));
+			msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
+			info->scsi.phase = PHASE_MSGOUT;
+			break;
+		}
+		break;
 
-	case EXTENDED_WDTR:	/* Wide transfer negociation request/reply */
-		/* We don't do wide transfers - reject message */
 	default:
-		printk("scsi%d.%c: unrecognised extended message %02X, rejecting\n",
-			info->host->host_no, fas216_target (info),
-			message[2]);
-		msgqueue_flush (&info->scsi.msgs);
+		printk("scsi%d.%c: unrecognised message %02X, rejecting\n",
+			info->host->host_no, fas216_target(info),
+			message[0]);
+		msgqueue_flush(&info->scsi.msgs);
 		outb(CMD_SETATN, REG_CMD(info));
-		msgqueue_addmsg (&info->scsi.msgs, 1, MESSAGE_REJECT);
+		msgqueue_addmsg(&info->scsi.msgs, 1, MESSAGE_REJECT);
 		info->scsi.phase = PHASE_MSGOUT;
 		break;
 	}
-	break;
+	outb(CMD_MSGACCEPTED, REG_CMD(info));
+}
 
-    default:
-	printk ("scsi%d.%c: unrecognised message %02X, rejecting\n",
-		info->host->host_no, fas216_target (info),
-		message[0]);
-	msgqueue_flush (&info->scsi.msgs);
-	outb(CMD_SETATN, REG_CMD(info));
-	msgqueue_addmsg (&info->scsi.msgs, 1, MESSAGE_REJECT);
-	info->scsi.phase = PHASE_MSGOUT;
-	break;
-    }
-    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 void fas216_send_command(FAS216_Info *info)
+{
+	int i;
+
+	fas216_checkmagic(info, "fas216_send_command");
+
+	outb(CMD_NOP|CMD_WITHDMA, REG_CMD(info));
+	outb(CMD_FLUSHFIFO, REG_CMD(info));
+
+	/* load command */
+	for (i = 0; i < info->SCpnt->cmd_len; i++)
+		outb(info->SCpnt->cmnd[i], REG_FF(info));
+
+	outb(CMD_TRANSFERINFO, REG_CMD(info));
 }
 
-/* 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
+/* Function: int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
+ * Purpose : handle bus service in selection phase
+ * Params  : info - interface which caused bus service
+ * Returns : 0 if unable to service this interrupt
  */
-static void fas216_busservice_intr (FAS216_Info *info, unsigned int stat, unsigned int ssr)
+static int fas216_busservice_selection(FAS216_Info *info, unsigned int stat)
 {
-    int i;
-#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
-    switch (ssr & IS_BITS) {
-    case IS_COMPLETE:			/* last action completed		*/
-	outb(CMD_NOP, REG_CMD(info));
+	fas216_checkmagic(info, "fas216_busservice_selection");
 
-	switch (info->scsi.phase) {
-	case PHASE_SELECTION:		/* while selecting - selected target	*/
-	    switch (stat & STAT_BUSMASK) {
-	    case STAT_DATAOUT:	/* data out phase			*/
-		fas216_starttransfer (info, DMA_OUT);
-		break;
+	switch (stat & STAT_BUSMASK) {
+	case STAT_DATAOUT:	/* data out phase			*/
+		fas216_starttransfer(info, DMA_OUT, 1);
+		return 1;
 
-	    case STAT_DATAIN:		/* data in phase			*/
-		fas216_starttransfer (info, DMA_IN);
-		break;
+	case STAT_DATAIN:		/* data in phase		*/
+		fas216_starttransfer(info, DMA_IN, 0);
+		return 1;
 
-	    case STAT_STATUS:		/* status phase				*/
+	case STAT_STATUS:		/* status phase			*/
 		info->scsi.phase = PHASE_STATUS;
 		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		break;
+		return 1;
 
-	    case STAT_MESGIN:		/* message in phase			*/
+	case STAT_MESGIN:		/* message in phase		*/
 		info->scsi.phase = PHASE_MSGIN;
 		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		break;
+		return 1;
 
-	    default:			/* other				*/
-		printk ("scsi%d.%c: bus phase %s after connect?\n",
-			info->host->host_no, fas216_target (info),
-			fas216_bus_phase (stat));
-		break;
-	    }
-	    break;
+	case STAT_MESGOUT:{		/* message out phase		*/
+		char *msg;
+		int start = 1, msglen;
 
-	case PHASE_DATAIN:		/* while transfering data in		*/
-	    switch (stat & STAT_BUSMASK) {
-	    case STAT_DATAIN:		/* continue data in phase		*/
-		fas216_starttransfer (info, DMA_IN);
-		break;
+		/* load message bytes, but don't forget to miss the first
+		 * byte!
+		 */
+		while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
+			int i;
 
-	    case STAT_STATUS:
-		fas216_stoptransfer(info);
-		info->scsi.phase = PHASE_STATUS;
-		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		break;
+			for (i = start; i < msglen; i++)
+				outb(msg[i], REG_FF(info));
+			start = 0;
+		}
+		outb(CMD_TRANSFERINFO, REG_CMD(info));
+		info->scsi.phase = PHASE_MESSAGESENT;
+		return 1;
+	}
+	default:
+		return 0;
+	}
+}
 
-	    case STAT_MESGIN:		/* message in phase			*/
-		fas216_stoptransfer(info);
+/* Function: int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat)
+ * Purpose : handle bus service after the IDENTIFY message has been sent
+ * Params  : info - interface which caused bus service
+ * Returns : 0 if unable to service this interrupt
+ */
+static int fas216_busservice_messagesent(FAS216_Info *info, unsigned int stat)
+{
+	fas216_checkmagic(info, "fas216_busservice_messagesent");
+
+	switch (stat & STAT_BUSMASK) {
+	case STAT_MESGIN:		/* message in phase		*/
 		info->scsi.phase = PHASE_MSGIN;
+		outb(CMD_FLUSHFIFO, REG_CMD(info));
 		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		break;
+		return 1;
 
-	    default:
-		printk ("scsi%d.%c: bus phase %s after data in?\n",
-			info->host->host_no, fas216_target (info),
-			fas216_bus_phase (stat));
-	    }
-	    break;
-
-	case PHASE_DATAOUT:		/* while transfering data out		*/
-	    switch (stat & STAT_BUSMASK) {
-	    case STAT_DATAOUT:
-		fas216_starttransfer (info, DMA_OUT);
-		break;
+	case STAT_COMMAND:		/* command phase		*/
+		fas216_send_command(info);
+		return 1;
+
+	default:
+		return 0;
+	}
+}
 
-	    case STAT_STATUS:
+/* Function: int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat)
+ * Purpose : handle bus service in a data in/out phase.
+ * Params  : info - interface which caused bus service
+ * Returns : 0 if unable to service this interrupt
+ * Note    : We do not allow the device to change the data direction!
+ */
+static int fas216_busservice_dataphase(FAS216_Info *info, unsigned int stat)
+{
+	fas216_checkmagic(info, "fas216_busservice_dataphase");
+
+	switch (stat & STAT_BUSMASK) {
+	case STAT_DATAIN:		/* continue data in phase	*/
+		if (info->scsi.phase == PHASE_DATAIN) {
+			fas216_starttransfer(info, DMA_IN, 0);
+			return 1;
+		} else
+			return 0;
+
+	case STAT_DATAOUT:		/* continue data out phase	*/
+		if (info->scsi.phase == PHASE_DATAOUT) {
+			fas216_starttransfer(info, DMA_OUT, 0);
+			return 1;
+		} else
+			return 0;
+
+	case STAT_STATUS:		/* status in phase		*/
 		fas216_stoptransfer(info);
 		info->scsi.phase = PHASE_STATUS;
-		outb(CMD_FLUSHFIFO, REG_CMD(info));
 		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		break;
+		return 1;
 
-	    case STAT_MESGIN:		/* message in phase			*/
+	case STAT_MESGIN:		/* message in phase		*/
 		fas216_stoptransfer(info);
 		info->scsi.phase = PHASE_MSGIN;
-		outb(CMD_FLUSHFIFO, REG_CMD(info));
 		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		break;
+		return 1;
+
+	default:
+		return 0;
+	}
+}
+
+/* Function: int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat)
+ * Purpose : handle bus service in after a reconnection
+ * Params  : info - interface which caused bus service
+ * Returns : 0 if unable to service this interrupt
+ * Note    : We do not allow the device to change the data direction!
+ */
+static int fas216_busservice_reconnected(FAS216_Info *info, unsigned int stat)
+{
+	fas216_checkmagic(info, "fas216_busservice_reconnected");
 
-	    default:
-		printk ("scsi%d.%c: bus phase %s after data out?\n",
-			info->host->host_no, fas216_target (info),
-			fas216_bus_phase (stat));
-	    }
-	    break;
-
-	case PHASE_RECONNECTED:		/* newly reconnected device		*/
-	    /*
-	     * Command reconnected - if MESGIN, get message - it may be
-	     * the tag.  If not, get command out of the disconnected queue
-	     */
-	    switch (stat & STAT_BUSMASK) {
-	    case STAT_MESGIN:
+	switch (stat & STAT_BUSMASK) {
+	case STAT_MESGIN:
 		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		break;
+		return 1;
 
-	    case STAT_STATUS:
-		fas216_finish_reconnect (info);
+	case STAT_STATUS:
+		fas216_finish_reconnect(info);
 		info->scsi.phase = PHASE_STATUS;
 		outb(CMD_INITCMDCOMPLETE, REG_CMD(info));
-		break;
+		return 1;
 
-	    case STAT_DATAOUT:		/* data out phase			*/
-		fas216_finish_reconnect (info);
-		fas216_starttransfer (info, DMA_OUT);
-		break;
-
-	    case STAT_DATAIN:		/* data in phase			*/
-		fas216_finish_reconnect (info);
-		fas216_starttransfer (info, DMA_IN);
-		break;
-
-	    default:
-		printk ("scsi%d.%c: bus phase %s after reconnect?\n",
-			info->host->host_no, fas216_target (info),
-			fas216_bus_phase (stat));
-	    }
-	    break;
-
-	case PHASE_MSGIN:
-	    switch (stat & STAT_BUSMASK) {
-	    case STAT_MESGIN:
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		break;
+	case STAT_DATAOUT:		/* data out phase		*/
+		fas216_finish_reconnect(info);
+		fas216_starttransfer(info, DMA_OUT, 1);
+		return 1;
+
+	case STAT_DATAIN:		/* data in phase		*/
+		fas216_finish_reconnect(info);
+		fas216_starttransfer(info, DMA_IN, 0);
+		return 1;
+
+	default:
+		return 0;
+	}
+}
 
-	    default:
-		printk ("scsi%d.%c: bus phase %s after message in?\n",
-			info->host->host_no, fas216_target (info),
-			fas216_bus_phase (stat));
-	    }
-	    break;
-
-	case PHASE_MSGOUT:
-	    if ((stat & STAT_BUSMASK) != STAT_MESGOUT) {
-		printk ("scsi%d.%c: didn't manage MESSAGE OUT phase\n",
-			info->host->host_no, fas216_target (info));
-	    } else {
-		unsigned int msglen;
+/* Function: int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat)
+ * Purpose : handle bus service to send a message
+ * Params  : info - interface which caused bus service
+ * Returns : 0 if unable to service this interrupt
+ * Note    : We do not allow the device to change the data direction!
+ */
+static int fas216_busservice_messageout(FAS216_Info *info, unsigned int stat)
+{
+	fas216_checkmagic(info, "fas216_busservice_messageout");
 
-		msglen = msgqueue_msglength (&info->scsi.msgs);
+	if ((stat & STAT_BUSMASK) != STAT_MESGOUT) {
+		printk("scsi%d.%c: didn't manage MESSAGE OUT phase\n",
+		       info->host->host_no, fas216_target(info));
+		return 0;
+	} else {
+		unsigned int msglen = msgqueue_msglength(&info->scsi.msgs);
 
 		outb(CMD_FLUSHFIFO, REG_CMD(info));
 
 		if (msglen == 0)
-		    outb(NOP, REG_FF(info));
+			outb(NOP, REG_FF(info));
 		else {
-		    char *msg;
+			char *msg;
 
-		    while ((msg = msgqueue_getnextmsg (&info->scsi.msgs, &msglen)) != NULL) {
-			for (i = 0; i < msglen; i++)
-			    outb(msg[i], REG_FF(info));
-		    }
+			while ((msg = msgqueue_getnextmsg(&info->scsi.msgs, &msglen)) != NULL) {
+				int i;
+
+				for (i = 0; i < msglen; i++)
+					outb(msg[i], REG_FF(info));
+			}
 		}
 		outb(CMD_TRANSFERINFO, REG_CMD(info));
 		info->scsi.phase = PHASE_AFTERMSGOUT;
-	    }
-	    break;
+		return 1;
+	}
+}
 
-	case PHASE_AFTERMSGOUT:
-	    switch (stat & STAT_BUSMASK) {
-	    case STAT_MESGIN:
-		info->scsi.phase = PHASE_MSGIN;
-		outb(CMD_TRANSFERINFO, REG_CMD(info));
-		break;
+/* 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
+ */
+static void fas216_busservice_intr(FAS216_Info *info, unsigned int stat, unsigned int ssr)
+{
+	fas216_checkmagic(info, "fas216_busservice_intr");
+
+#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
+	switch (ssr & IS_BITS) {
+	case IS_MSGBYTESENT:		/* select with ATN and stop steps completed	*/
+	case IS_COMPLETE:			/* last action completed		*/
+		outb(CMD_NOP, REG_CMD(info));
 
-	    default:
-		printk ("scsi%d.%c: bus phase %s after message out\n",
-			info->host->host_no, fas216_target (info),
-			fas216_bus_phase (stat));
-	    }
-	    break;
-
-	case PHASE_DISCONNECT:
-	    printk ("scsi%d.%c: disconnect message received, but bus service %s?\n",
-		    info->host->host_no, fas216_target (info),
-		    fas216_bus_phase (stat));
-	    outb(CMD_SETATN, REG_CMD(info));
-	    msgqueue_addmsg (&info->scsi.msgs, 1, ABORT);
-	    info->scsi.phase = PHASE_MSGOUT;
-	    info->scsi.aborting = 1;
-	    outb(CMD_TRANSFERINFO, REG_CMD(info));
-	    break;
+		switch (info->scsi.phase) {
+		case PHASE_SELECTION:		/* while selecting - selected target	*/
+			if (!fas216_busservice_selection(info, stat))
+				printk("scsi%d.%c: bus phase %s after connect?\n",
+					info->host->host_no, fas216_target(info),
+					fas216_bus_phase(stat));
+			break;
+
+		case PHASE_MESSAGESENT:
+			if (!fas216_busservice_messagesent(info, stat))
+				printk("scsi%d.%c: bus phase %s after message sent?\n",
+					info->host->host_no, fas216_target(info),
+					fas216_bus_phase(stat));
+			break;
+
+		case PHASE_DATAIN:		/* while transfering data in		*/
+		case PHASE_DATAOUT:		/* while transfering data out		*/
+			if (!fas216_busservice_dataphase(info, stat))
+				printk("scsi%d.%c: bus phase %s after %s?\n",
+					info->host->host_no, fas216_target(info),
+					fas216_bus_phase(stat), fas216_drv_phase(info));
+			break;
+
+		case PHASE_RECONNECTED:		/* newly reconnected device		*/
+			/*
+			 * Command reconnected - if MESGIN, get message - it may be
+			 * the tag.  If not, get command out of the disconnected queue
+			 */
+			if (!fas216_busservice_reconnected(info, stat))
+				printk("scsi%d.%c: bus phase %s after reconnect?\n",
+					info->host->host_no, fas216_target(info),
+					fas216_bus_phase(stat));
+			break;
+
+		case PHASE_MSGIN:
+		case PHASE_AFTERMSGOUT:
+			switch (stat & STAT_BUSMASK) {
+			case STAT_MESGIN:
+				info->scsi.phase = PHASE_MSGIN;
+				outb(CMD_TRANSFERINFO, REG_CMD(info));
+				break;
+
+			case STAT_COMMAND:	/* command phase			*/
+				fas216_send_command(info);
+				info->scsi.phase = PHASE_SELECTION;
+				break;
+
+			default:
+				printk("scsi%d.%c: bus phase %s after %s?\n",
+					info->host->host_no, fas216_target(info),
+					fas216_bus_phase(stat),
+					fas216_drv_phase(info));
+			}
+			break;
+
+		case PHASE_MSGOUT:
+			if (!fas216_busservice_messageout(info, stat))
+				printk("scsi%d.%c: bus phase %s instead of message out?\n",
+					info->host->host_no, fas216_target(info),
+					fas216_bus_phase(stat));
+			break;
+
+		case PHASE_DISCONNECT:
+			printk("scsi%d.%c: disconnect message received, but bus service %s?\n",
+				info->host->host_no, fas216_target(info),
+				fas216_bus_phase(stat));
+			outb(CMD_SETATN, REG_CMD(info));
+			msgqueue_addmsg(&info->scsi.msgs, 1, INITIATOR_ERROR);
+			info->scsi.phase = PHASE_MSGOUT;
+			info->scsi.aborting = 1;
+			outb(CMD_TRANSFERINFO, REG_CMD(info));
+			break;
+
+		default:
+			printk("scsi%d.%c: internal phase %s for bus service?"
+				"  What do I do with this?\n",
+				info->host->host_no, fas216_target(info),
+				fas216_drv_phase(info));
+		}
+		break;
 
 	default:
-	    printk ("scsi%d.%c: internal phase %d for bus service?"
-		    "  What do I do with this?\n",
-		    info->host->host_no, fas216_target (info),
-		    info->scsi.phase);
+		printk("scsi%d.%c: bus service at step %d?\n",
+			info->host->host_no, fas216_target(info),
+			ssr & IS_BITS);
 	}
-	break;
-
-    default:
-	printk ("scsi%d.%c: bus service at step %d?\n",
-		info->host->host_no, fas216_target (info),
-		ssr & IS_BITS);
-    }
 }
 
-/* Function: void fas216_funcdone_intr (FAS216_Info *info, unsigned int stat, unsigned int ssr)
+/* 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
  */
-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 ssr)
 {
-    int status, message;
+	int status, message;
+
+	fas216_checkmagic(info, "fas216_funcdone_intr");
+
 #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);
+	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
-    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;
-	info->scsi.phase = PHASE_DONE;
-	outb(CMD_MSGACCEPTED, REG_CMD(info));
-	break;
+	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;
+		info->scsi.phase = PHASE_DONE;
+		outb(CMD_MSGACCEPTED, REG_CMD(info));
+		break;
 
-    case PHASE_IDLE:			/* reselected?				*/
-    case PHASE_MSGIN:			/* message in phase			*/
-    case PHASE_RECONNECTED:		/* reconnected command			*/
-	if ((stat & STAT_BUSMASK) == STAT_MESGIN) {
-	    fas216_message (info);
-	    break;
-	}
+	case PHASE_IDLE:			/* reselected?				*/
+	case PHASE_MSGIN:			/* message in phase			*/
+	case PHASE_RECONNECTED:			/* reconnected command			*/
+		if ((stat & STAT_BUSMASK) == STAT_MESGIN) {
+			fas216_message(info);
+			break;
+		}
 
-    default:
-	printk ("scsi%d.%c: internal phase %d for function done?"
-		"  What do I do with this?\n",
-		info->host->host_no, fas216_target (info),
-		info->scsi.phase);
-    }
+	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_drv_phase(info));
+	}
 }
 
-/* Function: void fas216_intr (struct Scsi_Host *instance)
+/* Function: void fas216_intr(struct Scsi_Host *instance)
  * Purpose : handle interrupts from the interface to progress a command
  * Params  : instance - interface to service
  */
-void fas216_intr (struct Scsi_Host *instance)
+void fas216_intr(struct Scsi_Host *instance)
 {
-    FAS216_Info *info = (FAS216_Info *)instance->hostdata;
-    unsigned char isr, ssr, stat;
+	FAS216_Info *info = (FAS216_Info *)instance->hostdata;
+	unsigned char isr, ssr, stat;
+
+	fas216_checkmagic(info, "fas216_intr");
 
-    stat = inb(REG_STAT(info));
-    ssr = inb(REG_IS(info));
-    isr = inb(REG_INST(info));
-
-    if (isr & INST_BUSRESET)
-	printk ("scsi%d.H: fas216: bus reset detected\n", instance->host_no);
-    else if (isr & INST_ILLEGALCMD)
-	printk (KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no);
-    else if (isr & INST_DISCONNECT)
-	fas216_disconnect_intr (info);
-    else if (isr & 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
-    	printk ("scsi%d.%c: unknown interrupt received:"
-		" phase %d isr %02X ssr %02X stat %02X\n",
-		instance->host_no, fas216_target (info),
-		info->scsi.phase, isr, ssr, stat);
+	stat = inb(REG_STAT(info));
+	ssr = inb(REG_IS(info));
+	isr = inb(REG_INST(info));
+
+	add_debug_list(stat, ssr, isr, info->scsi.phase);
+
+	if (stat & STAT_INT) {
+		if (isr & INST_BUSRESET)
+			printk("scsi%d.H: fas216: bus reset detected\n", instance->host_no);
+		else if (isr & INST_ILLEGALCMD)
+			printk(KERN_CRIT "scsi%d.H: illegal command given\n", instance->host_no);
+		else if (isr & INST_DISCONNECT)
+			fas216_disconnect_intr(info);
+		else if (isr & 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
+		    	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);
+	}
 }
 
-/* Function: void fas216_kick (FAS216_Info *info)
+/* 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_kick(FAS216_Info *info)
 {
 	Scsi_Cmnd *SCpnt;
 	int i, msglen, from_queue = 0;
 
+	fas216_checkmagic(info, "fas216_kick");
+
 	if (info->origSCpnt) {
 		SCpnt = info->origSCpnt;
 		info->origSCpnt = NULL;
@@ -943,11 +1321,11 @@
 		return;
 
 	if (info->scsi.disconnectable && info->SCpnt) {
-		queue_add_cmd_tail (&info->queues.disconnected, 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));
+			info->host->host_no, fas216_target(info));
 	}
 
 	/*
@@ -1004,7 +1382,7 @@
 	}
 
 	/* build outgoing message bytes */
-	msgqueue_flush (&info->scsi.msgs);
+	msgqueue_flush(&info->scsi.msgs);
 	if (info->device[SCpnt->target].disconnect_ok)
 		msgqueue_addmsg(&info->scsi.msgs, 1, IDENTIFY(1, SCpnt->lun));
 	else
@@ -1015,7 +1393,8 @@
 		msgqueue_addmsg(&info->scsi.msgs, 2, SIMPLE_QUEUE_TAG, SCpnt->tag);
 
 	/* add synchronous negociation */
-	if (info->device[SCpnt->target].negstate == syncneg_start) {
+	if (SCpnt->cmnd[0] == REQUEST_SENSE &&
+	    info->device[SCpnt->target].negstate == syncneg_start) {
 		info->device[SCpnt->target].negstate = syncneg_sent;
 		msgqueue_addmsg(&info->scsi.msgs, 5,
 				EXTENDED_MESSAGE, 3, EXTENDED_SDTR,
@@ -1040,9 +1419,12 @@
 	outb(info->device[SCpnt->target].sof, REG_SOF(info));
 	outb(info->device[SCpnt->target].stp, REG_STP(info));
 
-	msglen = msgqueue_msglength (&info->scsi.msgs);
+	msglen = msgqueue_msglength(&info->scsi.msgs);
 
 	if (msglen == 1 || msglen == 3) {
+		/*
+		 * We have an easy message length to send...
+		 */
 		char *msg;
 
 		/* load message bytes */
@@ -1060,6 +1442,22 @@
 		else
 			outb(CMD_SELECTATN3, REG_CMD(info));
 	} else {
+		/*
+		 * We have an unusual number of message bytes to send.
+		 *  Load first byte into fifo, and issue SELECT with ATN and
+		 *  stop steps.
+		 * Note: we only peek at t his message - we need the rest
+		 * later on!
+		 */
+		int thismsg;
+		char *msg = msgqueue_peeknextmsg(&info->scsi.msgs, &thismsg);
+
+		if (!msg || thismsg < 1)
+			printk(KERN_CRIT "scsi%d.%c: no message to send, but %d bytes\n",
+				info->host->host_no, fas216_target(info), msglen);
+		else
+			outb(msg[0], REG_FF(info));
+
 		outb(CMD_SELECTATNSTOP, REG_CMD(info));
 	}
 
@@ -1070,79 +1468,95 @@
 	/* should now get either DISCONNECT or (FUNCTION DONE with BUS SERVICE) intr */
 }
 
-/* Function: void fas216_done (FAS216_Info *info, unsigned int result)
+/* Function: void fas216_done(FAS216_Info *info, unsigned int result)
  * Purpose : complete processing for command
  * Params  : info   - interface that completed
  *	     result - driver byte of result
  */
-static void fas216_done (FAS216_Info *info, unsigned int result)
+static void fas216_done(FAS216_Info *info, unsigned int result)
 {
-    Scsi_Cmnd *SCpnt = info->SCpnt;
+	Scsi_Cmnd *SCpnt;
 
-    if (info->scsi.aborting) {
-	printk ("scsi%d.%c: uncaught abort - returning DID_ABORT\n",
-		info->host->host_no, fas216_target (info));
-	result = DID_ABORT;
-	info->scsi.aborting = 0;
-    }
+	fas216_checkmagic(info, "fas216_done");
 
-    info->stats.fins += 1;
+	SCpnt = info->SCpnt;
 
-    if (SCpnt) {
-    	info->scsi.phase = PHASE_IDLE;
-	info->SCpnt = NULL;
+	if (info->scsi.aborting) {
+		printk("scsi%d.%c: uncaught abort - returning DID_ABORT\n",
+			info->host->host_no, fas216_target(info));
+		result = DID_ABORT;
+		info->scsi.aborting = 0;
+	}
+
+	info->stats.fins += 1;
+
+	if (SCpnt) {
+	    	info->scsi.phase = PHASE_IDLE;
+		info->SCpnt = NULL;
 
-	SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 |
+		SCpnt->result = result << 16 | info->scsi.SCp.Message << 8 |
 				info->scsi.SCp.Status;
 
-	/*
-	 * In theory, this should not happen, but just in case it does.
-	 */
-	if (info->scsi.SCp.ptr && result == DID_OK) {
-	    switch (status_byte (SCpnt->result)) {
-	    case CHECK_CONDITION:
-	    case COMMAND_TERMINATED:
-	    case BUSY:
-	    case QUEUE_FULL:
-	    case RESERVATION_CONFLICT:
-		break;
+		/*
+		 * In theory, this should not happen, but just in case it does.
+		 */
+		if (info->scsi.SCp.ptr && result == DID_OK) {
+			switch (SCpnt->cmnd[0]) {
+			case INQUIRY:
+			case START_STOP:
+			case READ_CAPACITY:
+				break;
 
-	    default:
-		printk (KERN_ERR "scsi%d.H: incomplete data transfer "
-			"detected: result=%08X command=",
-			info->host->host_no, SCpnt->result);
-		print_command (SCpnt->cmnd);
-	    }
-	}
+			default:
+				switch (status_byte(SCpnt->result)) {
+				case CHECK_CONDITION:
+				case COMMAND_TERMINATED:
+				case BUSY:
+				case QUEUE_FULL:
+				case RESERVATION_CONFLICT:
+					break;
+
+				default:
+					printk(KERN_ERR "scsi%d.H: incomplete data transfer "
+						"detected: res=%08X ptr=%p len=%X command=",
+						info->host->host_no, SCpnt->result,
+						info->scsi.SCp.ptr, info->scsi.SCp.this_residual);
+					print_command(SCpnt->cmnd);
+				}
+			}
+		}
 #ifdef DEBUG_CONNECT
-	printk ("scsi%d.%c: scsi command (%p) complete, result=%08X\n",
-		info->host->host_no, fas216_target (info),
-		SCpnt, SCpnt->result);
+		printk("scsi%d.%c: scsi command (%p) complete, result=%08X\n",
+			info->host->host_no, fas216_target(info),
+			SCpnt, SCpnt->result);
 #endif
 
-	if (!SCpnt->scsi_done)
-	    panic ("scsi%d.H: null scsi_done function in fas216_done", info->host->host_no);
+		if (!SCpnt->scsi_done)
+			panic("scsi%d.H: null scsi_done function in "
+				"fas216_done", info->host->host_no);
 
-	clear_bit (SCpnt->target * 8 + SCpnt->lun, info->busyluns);
+		clear_bit(SCpnt->target * 8 + SCpnt->lun, info->busyluns);
 
-	SCpnt->scsi_done (SCpnt);
-    } else
-	panic ("scsi%d.H: null command in fas216_done", info->host->host_no);
+		SCpnt->scsi_done(SCpnt);
+	} else
+		panic("scsi%d.H: null command in fas216_done", info->host->host_no);
 
-    if (info->scsi.irq != NO_IRQ)
-	fas216_kick (info);
+	if (info->scsi.irq != NO_IRQ)
+		fas216_kick(info);
 }
 
-/* Function: int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+/* 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
  */
-int fas216_queue_command (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+int fas216_queue_command(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
 {
 	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
 
+	fas216_checkmagic(info, "fas216_queue_command");
+
 #ifdef DEBUG_CONNECT
 	printk("scsi%d.%c: received queuable command (%p) %02X\n",
 		SCpnt->host->host_no, '0' + SCpnt->target,
@@ -1185,47 +1599,51 @@
 		/* add command into execute queue and let it complete under
 		 * the drivers interrupts.
 		 */
-		if (!queue_add_cmd_ordered (&info->queues.issue, SCpnt)) {
+		if (!queue_add_cmd_ordered(&info->queues.issue, SCpnt)) {
 			SCpnt->result = DID_ERROR << 16;
-			done (SCpnt);
+			done(SCpnt);
 		}
-		save_flags_cli (flags);
+		save_flags_cli(flags);
 		if (!info->SCpnt || info->scsi.disconnectable)
-			fas216_kick (info);
-		restore_flags (flags);
+			fas216_kick(info);
+		restore_flags(flags);
 	} else {
 		/* no interrupts to rely on - we'll have to handle the
 		 * command ourselves.  For now, we give up.
 		 */
 		SCpnt->result = DID_ERROR << 16;
-		done (SCpnt);
+		done(SCpnt);
 	}
 	return 0;
 }
 
-/* Function: void fas216_internal_done (Scsi_Cmnd *SCpnt)
+/* Function: void fas216_internal_done(Scsi_Cmnd *SCpnt)
  * Purpose : trigger restart of a waiting thread in fas216_command
  * Params  : SCpnt - Command to wake
  */
-static void fas216_internal_done (Scsi_Cmnd *SCpnt)
+static void fas216_internal_done(Scsi_Cmnd *SCpnt)
 {
 	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
 
+	fas216_checkmagic(info, "fas216_internal_done");
+
 	info->internal_done = 1;
 }
 
-/* Function: int fas216_command (Scsi_Cmnd *SCpnt)
+/* Function: int fas216_command(Scsi_Cmnd *SCpnt)
  * Purpose : queue a command for adapter to process.
  * Params  : SCpnt - Command to queue
  * Returns : scsi result code
  */
-int fas216_command (Scsi_Cmnd *SCpnt)
+int fas216_command(Scsi_Cmnd *SCpnt)
 {
 	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
 	unsigned long flags;
 
+	fas216_checkmagic(info, "fas216_command");
+
 	info->internal_done = 0;
-	fas216_queue_command (SCpnt, fas216_internal_done);
+	fas216_queue_command(SCpnt, fas216_internal_done);
 
 	/*
 	 * This wastes time, since we can't return until the command is
@@ -1233,13 +1651,13 @@
 	 * However, we must re-enable interrupts, or else we'll be
 	 * waiting forever.
 	 */
-	save_flags (flags);
-	sti ();
+	save_flags(flags);
+	sti();
 
 	while (!info->internal_done)
-		barrier ();
+		barrier();
 
-	restore_flags (flags);
+	restore_flags(flags);
 
 	return SCpnt->result;
 }
@@ -1262,7 +1680,7 @@
 		*SCpntp1 = NULL;
 
 		SCpnt->result = result;
-		SCpnt->scsi_done (SCpnt);
+		SCpnt->scsi_done(SCpnt);
 	}
 
 	if (SCpnt == *SCpntp2)
@@ -1309,28 +1727,33 @@
 	return FAILED;
 }
 
-/* Function: int fas216_abort (Scsi_Cmnd *SCpnt)
+/* Function: int fas216_abort(Scsi_Cmnd *SCpnt)
  * Purpose : abort a command if something horrible happens.
  * Params  : SCpnt - Command that is believed to be causing a problem.
  * Returns : one of SCSI_ABORT_ macros.
  */
-int fas216_abort (Scsi_Cmnd *SCpnt)
+int fas216_abort(Scsi_Cmnd *SCpnt)
 {
 	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
 	int result = SCSI_ABORT_SNOOZE;
 
+	fas216_checkmagic(info, "fas216_abort");
+
 	info->stats.aborts += 1;
 
+	print_debug_list();
+	fas216_dumpinfo(info);
+	fas216_dumpstate(info);
 	printk(KERN_WARNING "scsi%d: fas216_abort: ", info->host->host_no);
 
 	do {
 		/* If command is waiting in the issue queue, then we can
 		 * simply remove the command and return abort status
 		 */
-		if (queue_removecmd (&info->queues.issue, SCpnt)) {
+		if (queue_removecmd(&info->queues.issue, SCpnt)) {
 			SCpnt->result = DID_ABORT << 16;
-			SCpnt->scsi_done (SCpnt);
-			printk ("command on issue queue");
+			SCpnt->scsi_done(SCpnt);
+			printk("command on issue queue");
 			result = SCSI_ABORT_SUCCESS;
 			break;
 		}
@@ -1338,14 +1761,14 @@
 		/* If the command is on the disconencted queue, we need to
 		 * reconnect to the device
 		 */
-		if (queue_cmdonqueue (&info->queues.disconnected, SCpnt))
-			printk ("command on disconnected queue");
+		if (queue_cmdonqueue(&info->queues.disconnected, SCpnt))
+			printk("command on disconnected queue");
 
 		/* If the command is connected, we need to flag that the
 		 * command needs to be aborted
 		 */
 		if (info->SCpnt == SCpnt)
-			printk ("command executing");
+			printk("command executing");
 
 		/* If the command is pending for execution, then again
 		 * this is simple - we remove it and report abort status
@@ -1353,14 +1776,14 @@
 		if (info->origSCpnt == SCpnt) {
 			info->origSCpnt = NULL;
 			SCpnt->result = DID_ABORT << 16;
-			SCpnt->scsi_done (SCpnt);
-			printk ("command waiting for execution");
+			SCpnt->scsi_done(SCpnt);
+			printk("command waiting for execution");
 			result = SCSI_ABORT_SUCCESS;
 			break;
 		}
 	} while (0);
 
-	printk ("\n");
+	printk("\n");
 
 	return result;
 }
@@ -1371,12 +1794,15 @@
  */
 static void fas216_reset_state(FAS216_Info *info)
 {
+	syncneg_t negstate;
 	int i;
 
+	fas216_checkmagic(info, "fas216_reset_state");
+
 	/*
 	 * Clear out all stale info in our state structure
 	 */
-	memset (info->busyluns, 0, sizeof (info->busyluns));
+	memset(info->busyluns, 0, sizeof(info->busyluns));
 	msgqueue_flush(&info->scsi.msgs);
 	info->scsi.reconnected.target = 0;
 	info->scsi.reconnected.lun = 0;
@@ -1385,6 +1811,16 @@
 	info->scsi.last_message = 0;
 	info->scsi.aborting = 0;
 	info->scsi.phase = PHASE_IDLE;
+	info->scsi.async_stp = fas216_syncperiod(info, info->ifcfg.asyncperiod);
+
+	if (info->host->dma_channel == NO_DMA || !info->dma.setup)
+		negstate = syncneg_invalid;
+	else
+#ifdef SCSI2_SYNC
+		negstate = syncneg_start;
+#else
+		negstate = syncneg_invalid;
+#endif
 
 	for (i = 0; i < 8; i++) {
 #ifndef NO_DISCONNECTS
@@ -1392,13 +1828,9 @@
 #else
 		info->device[i].disconnect_ok = 0;
 #endif
-		info->device[i].stp = fas216_syncperiod(info, info->ifcfg.asyncperiod);
+		info->device[i].negstate = negstate;
+		info->device[i].stp = info->scsi.async_stp;
 		info->device[i].sof = 0;
-#ifdef SCSI2SYNC
-		info->device[i].negstate = syncneg_start;
-#else
-		info->device[i].negstate = syncneg_complete;
-#endif
 	}
 }
 
@@ -1408,17 +1840,19 @@
  */
 static void fas216_init_chip(FAS216_Info *info)
 {
+	fas216_checkmagic(info, "fas216_init_chip");
+
 	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(fas216_syncperiod(info, info->ifcfg.asyncperiod), REG_STP(info));
+	outb(info->scsi.async_stp, REG_STP(info));
 	outb(info->scsi.cfg[0], REG_CNTL1(info));
 }
 
-/* Function: int fas216_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+/* Function: int fas216_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
  * Purpose : resets the adapter if something horrible happens.
  * Params  : SCpnt - Command that is believed to be causing a problem.
  *	     reset_flags - flags indicating reset type that is believed
@@ -1426,51 +1860,72 @@
  * Returns : one of SCSI_RESET_ macros, or'd with the SCSI_RESET_*_RESET
  *           macros.
  */
-int fas216_reset (Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+int fas216_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
 {
 	FAS216_Info *info = (FAS216_Info *)SCpnt->host->hostdata;
 	Scsi_Cmnd *SCptr;
 	int result = 0;
 
+	fas216_checkmagic(info, "fas216_reset");
+
+	/*
+	 * Validate that command is actually on one of our queues if we're doing
+	 * an asynchronous reset
+	 */
+	if (reset_flags & SCSI_RESET_ASYNCHRONOUS &&
+	    SCpnt &&
+	    info->SCpnt != SCpnt &&
+	    info->origSCpnt != SCpnt &&
+	    !queue_cmdonqueue(&info->queues.disconnected, SCpnt) &&
+	    !queue_cmdonqueue(&info->queues.issue, SCpnt)) {
+		printk("scsi%d: fas216_reset: asynchronous reset for unknown command\n",
+		       info->host->host_no);
+		return SCSI_RESET_NOT_RUNNING;
+	}
+
 	info->stats.resets += 1;
 
+	print_debug_list();
 	printk(KERN_WARNING "scsi%d: fas216_reset: ", info->host->host_no);
+	if (SCpnt)
+		printk(" for target %d ", SCpnt->target);
 
 	outb(info->scsi.cfg[3], REG_CNTL3(info));
 
 	fas216_stoptransfer(info);
-	fas216_reset_state(info);
 
-	if (reset_flags & SCSI_RESET_SUGGEST_HOST_RESET) {
+	switch (reset_flags & (SCSI_RESET_SUGGEST_BUS_RESET | SCSI_RESET_SUGGEST_HOST_RESET)) {
+	case SCSI_RESET_SUGGEST_BUS_RESET:
+		outb(CMD_RESETSCSI, REG_CMD(info));
+		outb(CMD_NOP, REG_CMD(info));
+		result |= SCSI_RESET_BUS_RESET;
+		break;
+
+	case SCSI_RESET_SUGGEST_HOST_RESET:
 		outb(CMD_RESETCHIP, REG_CMD(info));
 		outb(CMD_NOP, REG_CMD(info));
 		result |= SCSI_RESET_HOST_RESET;
-	}
-
-	if (reset_flags & SCSI_RESET_SUGGEST_BUS_RESET) {
-	    	outb(CMD_RESETSCSI, REG_CMD(info));
-    		outb(CMD_NOP, REG_CMD(info));
-	    	result |= SCSI_RESET_BUS_RESET;
-	}
+		break;
 
-	if (!(reset_flags &
-	      (SCSI_RESET_SUGGEST_BUS_RESET|SCSI_RESET_SUGGEST_HOST_RESET))) {
+	default:
 		outb(CMD_RESETCHIP, REG_CMD(info));
 		outb(CMD_NOP, REG_CMD(info));
 		outb(CMD_RESETSCSI, REG_CMD(info));
 		result |= SCSI_RESET_HOST_RESET | SCSI_RESET_BUS_RESET;
+		break;
 	}
 
-	if (result & SCSI_RESET_HOST_RESET)
-		fas216_init_chip(info);
+	udelay(300);
+	fas216_reset_state(info);
+	fas216_init_chip(info);
 
 	/*
 	 * Signal all commands in progress have been reset
 	 */
-	fas216_reportstatus (&info->SCpnt, &SCpnt, DID_RESET << 16);
+	fas216_reportstatus(&info->SCpnt, &SCpnt, DID_RESET << 16);
 
-	while ((SCptr = queue_remove (&info->queues.disconnected)) != NULL)
-		fas216_reportstatus (&SCptr, &SCpnt, DID_RESET << 16);
+	while ((SCptr = queue_remove(&info->queues.disconnected)) != NULL)
+		fas216_reportstatus(&SCptr, &SCpnt, DID_RESET << 16);
 
 	if (SCpnt) {
 		/*
@@ -1483,42 +1938,45 @@
 		queue_removecmd(&info->queues.issue, SCpnt);
 
 		SCpnt->result = DID_RESET << 16;
-		SCpnt->scsi_done (SCpnt);
+		SCpnt->scsi_done(SCpnt);
 	}
 
-	printk ("\n");
+	printk("\n");
 
 	return result | SCSI_RESET_SUCCESS;
 }
 
-/* Function: int fas216_init (struct Scsi_Host *instance)
+/* 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
  */
-int fas216_init (struct Scsi_Host *instance)
+int fas216_init(struct Scsi_Host *instance)
 {
 	FAS216_Info *info = (FAS216_Info *)instance->hostdata;
 	unsigned long flags;
 	int target_jiffies;
 
+	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] = CNTL3_ADIDCHK | CNTL3_G2CB | CNTL3_FASTSCSI | CNTL3_FASTCLK;
+	info->scsi.cfg[2] = info->ifcfg.cntl3 | CNTL3_ADIDCHK | CNTL3_G2CB;
 	info->scsi.type = "unknown";
 	info->SCpnt = NULL;
 	fas216_reset_state(info);
 
-	memset (&info->stats, 0, sizeof (info->stats));
+	memset(&info->stats, 0, sizeof(info->stats));
 
-	msgqueue_initialise (&info->scsi.msgs);
+	msgqueue_initialise(&info->scsi.msgs);
 
-	if (!queue_initialise (&info->queues.issue))
+	if (!queue_initialise(&info->queues.issue))
 		return 1;
 
-	if (!queue_initialise (&info->queues.disconnected)) {
-		queue_free (&info->queues.issue);
+	if (!queue_initialise(&info->queues.disconnected)) {
+		queue_free(&info->queues.issue);
 		return 1;
 	}
 
@@ -1555,8 +2013,7 @@
 		break;
 	}
 
-	udelay (300);
-
+	udelay(300);
 	/* now for the real initialisation */
 	fas216_init_chip(info);
 
@@ -1565,32 +2022,39 @@
 
 	/* scsi standard says 250ms */
 	target_jiffies = jiffies + (25 * HZ) / 100;
-	save_flags (flags);
-	sti ();
+	save_flags(flags);
+	sti();
 
-	while (jiffies < target_jiffies) barrier ();
+	while (jiffies < target_jiffies) barrier();
 
-	restore_flags (flags);
+	restore_flags(flags);
 
 	outb(info->scsi.cfg[0], REG_CNTL1(info));
 	inb(REG_INST(info));
 
+	/* now for the real initialisation */
+	fas216_init_chip(info);
+
+	fas216_checkmagic(info, "fas216_init");
+
 	return 0;
 }
 
-/* Function: int fas216_release (struct Scsi_Host *instance)
+/* 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)
+int fas216_release(struct Scsi_Host *instance)
 {
 	FAS216_Info *info = (FAS216_Info *)instance->hostdata;
 
+	fas216_checkmagic(info, "fas216_release");
+
 	outb(CMD_RESETCHIP, REG_CMD(info));
-	queue_free (&info->queues.disconnected);
-	queue_free (&info->queues.issue);
+	queue_free(&info->queues.disconnected);
+	queue_free(&info->queues.issue);
 
 	return 0;
 }
@@ -1609,12 +2073,12 @@
 
 
 #ifdef MODULE
-int init_module (void)
+int init_module(void)
 {
 	return 0;
 }
 
-void cleanup_module (void)
+void cleanup_module(void)
 {
 }
 #endif

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov