diff -u --recursive --new-file v1.3.58/linux/Documentation/networking/vortex.txt linux/Documentation/networking/vortex.txt
--- v1.3.58/linux/Documentation/networking/vortex.txt	Thu Jan  1 02:00:00 1970
+++ linux/Documentation/networking/vortex.txt	Mon Jan 22 22:52:34 1996
@@ -0,0 +1,35 @@
+This document describes the usage and errata of the 3Com "Vortex" device
+driver for Linux.
+
+This driver supports the following hardware:
+	3c590, 3c592, 3c595, 3c597
+
+When loaded as a module the following variables may be set:
+ name	 type	description
+ debug	 int	The debug message level, 0 (no messages) to 6 (wordy).
+ options int[]	The media type override and card operation settings
+		(See list below.)
+
+An example of loading the vortex module is
+	insmod 3c59x.o debug=1 options=0,,12
+This sets the debug message level to minimal messages, sets the first card to
+the 10baseT transceiver, the second to the EEPROM-set transceiver, and the
+third card to operate in full-duplex mode using its 100baseTx transceiver.
+(Note: card ordering is set by the PCI BIOS.)
+
+Possible media type settings
+	0	10baseT
+	1	10Mbs AUI
+	2	undefined
+	3	10base2 (BNC)
+	4	100base-TX
+	5	100base-FX
+	6	MII (not yet available)
+	7	<Use default setting>
+	
+	8	Full-duplex bit
+		8 10baseT full-duplex
+		12 100baseTx full-duplex 
+	16	Bus-master enable bit (experimental use only!)
+
+Details of the device driver implementation are at the top of the source file.
diff -u --recursive --new-file v1.3.58/linux/Makefile linux/Makefile
--- v1.3.58/linux/Makefile	Tue Jan 23 21:15:36 1996
+++ linux/Makefile	Mon Jan 22 22:17:03 1996
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 3
-SUBLEVEL = 58
+SUBLEVEL = 59
 
 ARCH = i386
 
diff -u --recursive --new-file v1.3.58/linux/arch/alpha/kernel/Makefile linux/arch/alpha/kernel/Makefile
--- v1.3.58/linux/arch/alpha/kernel/Makefile	Tue Jan 23 21:15:36 1996
+++ linux/arch/alpha/kernel/Makefile	Tue Jan 23 20:49:51 1996
@@ -16,7 +16,7 @@
 
 O_TARGET := kernel.o
 O_OBJS   := entry.o traps.o process.o osf_sys.o irq.o signal.o setup.o \
-	    bios32.o ptrace.o time.o apecs.o lca.o ksyms.c
+	    bios32.o ptrace.o time.o apecs.o lca.o ksyms.o
 
 all: kernel.o head.o
 
diff -u --recursive --new-file v1.3.58/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v1.3.58/linux/arch/i386/defconfig	Tue Jan 23 21:15:36 1996
+++ linux/arch/i386/defconfig	Tue Jan 23 21:57:16 1996
@@ -6,7 +6,7 @@
 # Loadable module support
 #
 CONFIG_MODULES=y
-CONFIG_MODVERSIONS=y
+# CONFIG_MODVERSIONS is not set
 # CONFIG_KERNELD is not set
 
 #
@@ -88,6 +88,7 @@
 # CONFIG_EL1 is not set
 # CONFIG_EL2 is not set
 CONFIG_EL3=y
+# CONFIG_VORTEX is not set
 # CONFIG_NET_ISA is not set
 # CONFIG_NET_EISA is not set
 # CONFIG_NET_POCKET is not set
diff -u --recursive --new-file v1.3.58/linux/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- v1.3.58/linux/drivers/block/ll_rw_blk.c	Tue Dec 26 04:45:36 1995
+++ linux/drivers/block/ll_rw_blk.c	Mon Jan 22 22:16:06 1996
@@ -436,16 +436,19 @@
 	add_request(major+blk_dev,req);
 }
 
+/*
+ * Swap partitions are now read via brw_page.  ll_rw_page is an
+ * asynchronous function now --- we must call wait_on_page afterwards
+ * if synchronous IO is required.  
+ */
 void ll_rw_page(int rw, kdev_t dev, unsigned long page, char * buffer)
 {
-	struct request * req;
 	unsigned int major = MAJOR(dev);
-	unsigned long sector = page * (PAGE_SIZE / 512);
-	struct semaphore sem = MUTEX_LOCKED;
-
+	int block = page;
+	
 	if (major >= MAX_BLKDEV || !(blk_dev[major].request_fn)) {
 		printk("Trying to read nonexistent block-device %s (%ld)\n",
-		       kdevname(dev), sector);
+		       kdevname(dev), page);
 		return;
 	}
 	switch (rw) {
@@ -461,19 +464,10 @@
 		default:
 			panic("ll_rw_page: bad block dev cmd, must be R/W");
 	}
-	req = get_request_wait(NR_REQUEST, dev);
-/* fill up the request-info, and add it to the queue */
-	req->cmd = rw;
-	req->errors = 0;
-	req->sector = sector;
-	req->nr_sectors = PAGE_SIZE / 512;
-	req->current_nr_sectors = PAGE_SIZE / 512;
-	req->buffer = buffer;
-	req->sem = &sem;
-	req->bh = NULL;
-	req->next = NULL;
-	add_request(major+blk_dev,req);
-	down(&sem);
+	if (mem_map[MAP_NR(buffer)].locked)
+		panic ("ll_rw_page: page already locked");
+	mem_map[MAP_NR(buffer)].locked = 1;
+	brw_page(rw, (unsigned long) buffer, dev, &block, PAGE_SIZE, 0);
 }
 
 /* This function can be used to request a number of buffers from a block
diff -u --recursive --new-file v1.3.58/linux/drivers/char/apm_bios.c linux/drivers/char/apm_bios.c
--- v1.3.58/linux/drivers/char/apm_bios.c	Tue Jan 23 21:15:40 1996
+++ linux/drivers/char/apm_bios.c	Mon Jan 22 22:55:23 1996
@@ -20,6 +20,10 @@
  * January 1996, Rik Faith (faith@cs.unc.edu):
  *    Make /proc/apm easy to format (bump driver version)
  *
+ * History:
+ *    0.6b: first version in official kernel, Linux 1.3.46
+ *    0.7: changed /proc/apm format, Linux 1.3.58
+ *    0.8: fixed gcc 2.7.[12] compilation problems, Linux 1.3.59
  *
  * Reference:
  *
@@ -110,30 +114,41 @@
  */
 
 /*
- * define to have debug messages
+ * Define to have debug messages.
  */
 #undef APM_DEBUG
 
 /*
- * define to always call the APM BIOS busy routine even if the clock was
- * not slowed by the idle routine
+ * Define to always call the APM BIOS busy routine even if the clock was
+ * not slowed by the idle routine.
  */
 #define ALWAYS_CALL_BUSY
 
 /*
- * define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
- * should turn interrupts on before it does a 'hlt')
+ * Define to disable interrupts in APM BIOS calls (the CPU Idle BIOS call
+ * should turn interrupts on before it does a 'hlt').
  */
 #define APM_NOINTS
 
 /*
- * define to make the APM BIOS calls zero all data segment registers (do
- * that an incorrect BIOS implementation will cause a kernel panic if it
- * tries to write to arbitrary memory)
+ * Define to make the APM BIOS calls zero all data segment registers (do
+ * that if an incorrect BIOS implementation will cause a kernel panic if it
+ * tries to write to arbitrary memory).
  */
 #define APM_ZERO_SEGS
 
 /*
+ * Define to make all set_limit calls use 64k limits.  The APM 1.1 BIOS is
+ * supposed to provide limit information that it recognizes.  Many machines
+ * do this correctly, but many others do not restrict themselves to their
+ * claimed limit.  When this happens, they will cause a segmentation
+ * violation in the kernel at boot time.  Most BIOS's, however, will
+ * respect a 64k limit, so we use that.  If you want to be pedantic and
+ * hold your BIOS to its claims, then undefine this.
+ */
+#define APM_RELAX_SEGMENTS
+
+/*
  * Need to poll the APM BIOS every second
  */
 #define APM_CHECK_TIMEOUT	(HZ)
@@ -193,27 +208,27 @@
 #define APM_SET_CPU_IDLE(error) \
 	APM_BIOS_CALL(al) \
 	: "=a" (error) \
-	: "0" (0x5305) \
+	: "a" (0x5305) \
 	APM_BIOS_CALL_END
 #endif
 
 #define APM_SET_CPU_BUSY(error) \
 	APM_BIOS_CALL(al) \
 	: "=a" (error) \
-	: "0" (0x5306) \
+	: "a" (0x5306) \
 	APM_BIOS_CALL_END
 
 #define APM_SET_POWER_STATE(state, error) \
 	APM_BIOS_CALL(al) \
 	: "=a" (error) \
-	: "0" (0x5307), "b" (0x0001), "c" (state) \
+	: "a" (0x5307), "b" (0x0001), "c" (state) \
 	APM_BIOS_CALL_END
 
 #ifdef CONFIG_APM_DISPLAY_BLANK
 #define APM_SET_DISPLAY_POWER_STATE(state, error) \
 	APM_BIOS_CALL(al) \
 	: "=a" (error) \
-	: "0" (0x5307), "b" (0x01ff), "c" (state) \
+	: "a" (0x5307), "b" (0x01ff), "c" (state) \
 	APM_BIOS_CALL_END
 #endif
 
@@ -221,32 +236,32 @@
 #define APM_ENABLE_POWER_MANAGEMENT(device, error) \
 	APM_BIOS_CALL(al) \
 	: "=a" (error) \
-	: "0" (0x5308), "b" (device), "c" (1) \
+	: "a" (0x5308), "b" (device), "c" (1) \
 	APM_BIOS_CALL_END
 #endif
 
 #define APM_GET_POWER_STATUS(bx, cx, dx, error) \
 	APM_BIOS_CALL(al) \
 	: "=a" (error), "=b" (bx), "=c" (cx), "=d" (dx) \
-	: "0" (0x530a), "1" (1) \
+	: "a" (0x530a), "b" (1) \
 	APM_BIOS_CALL_END
 
 #define APM_GET_EVENT(event, error)	\
 	APM_BIOS_CALL(al) \
 	: "=a" (error), "=b" (event) \
-	: "0" (0x530b) \
+	: "a" (0x530b) \
 	APM_BIOS_CALL_END
 
 #define APM_DRIVER_VERSION(ver, ax, error) \
 	APM_BIOS_CALL(bl) \
 	: "=a" (ax), "=b" (error) \
-	: "0" (0x530e), "1" (0), "c" (ver) \
+	: "a" (0x530e), "b" (0), "c" (ver) \
 	APM_BIOS_CALL_END
 
 #define APM_ENGAGE_POWER_MANAGEMENT(device, error) \
 	APM_BIOS_CALL(al) \
 	: "=a" (error) \
-	: "0" (0x530f), "b" (device), "c" (1) \
+	: "a" (0x530f), "b" (device), "c" (1) \
 	APM_BIOS_CALL_END
 
 /*
@@ -292,7 +307,7 @@
 
 static struct timer_list	apm_timer;
 
-static char			driver_version[] = "0.7";/* no spaces */
+static char			driver_version[] = "0.8";/* no spaces */
 
 #ifdef APM_DEBUG
 static char *	apm_event_name[] = {
@@ -1025,13 +1040,18 @@
 		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
 		set_limit(gdt[APM_DS >> 3], 64 * 1024);
 	} else {
-		set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
-		/* This is not clear from the spec, but at least one
-		   machine needs CS_16 to be a 64k segment, and the DEC
-		   Hinote Ultra CT475 (and others?) needs DS to be a 64k
-		   segment. */
+#ifdef APM_RELAX_SEGMENTS
+		/* For ASUS motherboard, Award BIOS rev 110 (and others?) */
+		set_limit(gdt[APM_CS >> 3], 64 * 1024);
+		/* For some unknown machine. */
 		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
+		/* For the DEC Hinote Ultra CT475 (and others?) */
 		set_limit(gdt[APM_DS >> 3], 64 * 1024);
+#else
+		set_limit(gdt[APM_CS >> 3], apm_bios_info.cseg_len);
+		set_limit(gdt[APM_CS_16 >> 3], 64 * 1024);
+		set_limit(gdt[APM_DS >> 3], apm_bios_info.dseg_len);
+#endif
 		apm_bios_info.version = 0x0101;
 		error = apm_driver_version(&apm_bios_info.version);
 		if (error != 0)
diff -u --recursive --new-file v1.3.58/linux/drivers/char/busmouse.c linux/drivers/char/busmouse.c
--- v1.3.58/linux/drivers/char/busmouse.c	Sun Dec 17 11:43:17 1995
+++ linux/drivers/char/busmouse.c	Tue Jan 23 21:41:34 1996
@@ -65,7 +65,6 @@
 	char dx, dy;
 	unsigned char buttons;
 
-	MSE_INT_OFF();
 	outb(MSE_READ_X_LOW, MSE_CONTROL_PORT);
 	dx = (inb(MSE_DATA_PORT) & 0xf);
 	outb(MSE_READ_X_HIGH, MSE_CONTROL_PORT);
@@ -171,6 +170,7 @@
 	int dx;
 	int dy;
 	unsigned char buttons; 
+	/* long flags; */
 
 	if (count < 3)
 		return -EINVAL;
@@ -186,7 +186,8 @@
 	 * so paging in put_user() does not effect mouse tracking.
 	 */
 
-	MSE_INT_OFF();
+	/* save_flags(flags); cli(); */
+	disable_irq(mouse_irq);
 	dx = mouse.dx;
 	dy = mouse.dy;
 	if (dx < -127)
@@ -201,7 +202,8 @@
 	mouse.dx -= dx;
 	mouse.dy -= dy;
 	mouse.ready = 0;
-	MSE_INT_ON();
+	enable_irq(mouse_irq);
+	/* restore_flags(flags); */
 
 	put_user(buttons | 0x80, buffer);
 	put_user((char)dx, buffer + 1);
diff -u --recursive --new-file v1.3.58/linux/drivers/char/selection.h linux/drivers/char/selection.h
--- v1.3.58/linux/drivers/char/selection.h	Wed Nov  8 07:11:31 1995
+++ linux/drivers/char/selection.h	Wed Jan 24 00:21:21 1996
@@ -77,12 +77,9 @@
  * of the VC's backing store, or the "shadow screen" memory where the screen
  * contents are kept, as the TGA frame buffer is *not* char/attr cells.
  *
- * The "(unsigned long) addr < video_mem_term" tests for an Alpha kernel
- *  virtual address less than the end of the "shadow scrren" memory. This
- *  indicates we really want to write to the screen, so, we do... :-)
- *
- * NOTE: we must guarantee that video_mem_term is less than *any* VC's backing
- * store; to do that, we must allocate it earlier than any VC's are done.
+ * We must test for an Alpha kernel virtual address that falls within
+ *  the "shadow screen" memory. This condition indicates we really want 
+ *  to write to the screen, so, we do... :-)
  *
  * NOTE also: there's only *TWO* operations: to put/get a character/attribute.
  *  All the others needed by VGA support go away, as Not Applicable for TGA.
@@ -94,7 +91,8 @@
 	 * if so, then render the char/attr onto the real screen.
         */
         *addr = val;
-        if ((unsigned long)addr < video_mem_term) {
+        if ((unsigned long)addr < video_mem_term &&
+	    (unsigned long)addr >= video_mem_base) {
                 tga_blitc(val, (unsigned long) addr);
         }
 }
diff -u --recursive --new-file v1.3.58/linux/drivers/net/3c59x.c linux/drivers/net/3c59x.c
--- v1.3.58/linux/drivers/net/3c59x.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/net/3c59x.c	Tue Jan 23 21:51:28 1996
@@ -0,0 +1,1033 @@
+/* 3c59x.c: A 3Com 3c590/3c595 "Vortex" ethernet driver for linux. */
+/*
+	Written 1995 by Donald Becker.
+
+	This software may be used and distributed according to the terms
+	of the GNU Public License, incorporated herein by reference.
+
+	This driver is for the 3Com "Vortex" series ethercards.  Members of
+	the series include the 3c590 PCI EtherLink III and 3c595-Tx PCI Fast
+	EtherLink.  It also works with the 10Mbs-only 3c590 PCI EtherLink III.
+
+	The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+	Center of Excellence in Space Data and Information Sciences
+	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+*/
+
+static char *version = "3c59x.c:v0.11 1/21/96 becker@cesdis.gsfc.nasa.gov\n";
+
+/* "Knobs" that turn on special features. */
+/* Use bus master transfers instead of programmed-I/O for the Tx process.
+   This is disabled by default! */
+#define VORTEX_BUS_MASTER
+
+/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
+#define VORTEX_DEBUG 1
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/timer.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* The total size is twice that of the original EtherLinkIII series: the
+   runtime register window, window 1, is now always mapped in. */
+#define VORTEX_TOTAL_SIZE 0x20
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry tc59x_drv =
+{"Vortex", vortex_pci_probe, VORTEX_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef VORTEX_DEBUG
+int vortex_debug = VORTEX_DEBUG;
+#else
+int vortex_debug = 1;
+#endif
+
+static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0, 0};
+static const char *product_names[] = {
+	"3c590 Vortex 10Mbps",
+	"3c595 Vortex 100baseTX",
+	"3c595 Vortex 100baseT4",
+	"3c595 Vortex 100base-MII",
+	"EISA Vortex 3c597",
+};
+#define DEMON_INDEX 5			/* Caution!  Must be consistent with above! */
+
+/*
+				Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the 3Com FastEtherLink, 3Com's PCI to
+10/100baseT adapter.  It also works with the 3c590, a similar product
+with only a 10Mbs interface.
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board.  The system BIOS should be set to assign the
+PCI INTA signal to an otherwise unused system IRQ line.  While it's
+physically possible to shared PCI interrupt lines, the 1.2.0 kernel doesn't
+support it.
+
+III. Driver operation
+
+The 3c59x series use an interface that's very similar to the previous 3c5x9
+series.  The primary interface is two programmed-I/O FIFOs, with an
+alternate single-contiguous-region bus-master transfer (see next).
+
+One extension that is advertised in a very large font is that the adapters
+are capable of being bus masters.  Unfortunately this capability is only for
+a single contiguous region making it less useful than the list of transfer
+regions available with the DEC Tulip or AMD PCnet.  Given the significant
+performance impact of taking an extra interrupt for each transfer, using
+DMA transfers is a win only with large blocks.
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control.  One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag.  The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+IV. Notes
+
+Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing both
+3c590 and 3c595 boards.
+The name "Vortex" is the internal 3Com project name for the PCI ASIC, and
+the not-yet-released (3/95) EISA version is called "Demon".  According to
+Terry these names come from rides at the local amusement park.
+
+The new chips support both ethernet (1.5K) and FDDI (4.5K) packet sizes!
+This driver only supports ethernet packets because of the skbuff allocation
+limit of 4K.
+*/
+
+#define TCOM_VENDOR_ID	0x10B7		/* 3Com's manufacturer's ID. */
+
+/* Operational defintions.
+   These are not used by other compilation units and thus are not
+   exported in a ".h" file.
+
+   First the windows.  There are eight register windows, with the command
+   and status registers available in each.
+   */
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+
+/* The top five bits written to EL3_CMD are a command, the lower
+   11 bits are the parameter, if applicable.
+   Note that 11 parameters bits was fine for ethernet, but the new chip
+   can handle FDDI lenght frames (~4500 octets) and now parameters count
+   32-bit 'Dwords' rather than octets. */
+
+enum vortex_cmd {
+	TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
+	TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
+	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+	SetTxThreshold = 18<<11, SetTxStart = 19<<11,
+	StartDMAUp = 20<<11, StartDMADown = (20<<11)+1, StatsEnable = 21<<11,
+	StatsDisable = 22<<11, StopCoax = 23<<11,};
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
+
+/* Bits in the general status register. */
+enum vortex_status {
+	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+	IntReq = 0x0040, StatsFull = 0x0080, DMADone = 1<<8,
+	DMAInProgress = 1<<11,			/* DMA controller is still busy.*/
+	CmdInProgress = 1<<12,			/* EL3_CMD is still busy.*/
+};
+
+/* Register window 1 offsets, the window used in normal operation.
+   On the Vortex this window is always mapped at offsets 0x10-0x1f. */
+enum Window1 {
+	TX_FIFO = 0x10,  RX_FIFO = 0x10,  RxErrors = 0x14,
+	RxStatus = 0x18,  Timer=0x1A, TxStatus = 0x1B,
+	TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */
+};
+enum Window0 {
+	Wn0EepromCmd = 10,		/* Window 0: EEPROM command register. */
+};
+enum Win0_EEPROM_bits {
+	EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
+	EEPROM_EWENB = 0x30,		/* Enable erasing/writing for 10 msec. */
+	EEPROM_EWDIS = 0x00,		/* Disable EWENB before 10 msec timeout. */
+};
+/* EEPROM locations. */
+enum eeprom_offset {
+	PhysAddr01=0, PhysAddr23=1, PhysAddr45=2, ModelID=3,
+	EtherLink3ID=7, IFXcvrIO=8, IRQLine=9,
+	NodeAddr01=10, NodeAddr23=11, NodeAddr45=12,
+	DriverTune=13, Checksum=15};
+
+enum Window3 {			/* Window 3: MAC/config bits. */
+	Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
+};
+union wn3_config {
+	int i;
+	struct w3_config_fields {
+		unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
+		int pad8:8;
+		unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
+		int pad24:8;
+	} u;
+};
+
+enum Window4 {
+	Wn4_Media = 0x0A,		/* Window 4: Various transcvr/media bits. */
+};
+enum Win4_Media_bits {
+	Media_TP = 0x00C0,		/* Enable link beat and jabber for 10baseT. */
+};
+enum Window7 {					/* Window 7: Bus Master control. */
+	Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
+};
+
+struct vortex_private {
+	char devname[8];			/* "ethN" string, also for kernel debug. */
+	const char *product_name;
+	struct device *next_module;
+	struct enet_statistics stats;
+#ifdef VORTEX_BUS_MASTER
+	struct sk_buff *tx_skb;		/* Packet being eaten by bus master ctrl.  */
+#endif
+	struct timer_list timer;	/* Media selection timer. */
+	int options;				/* User-settable driver options (none yet). */
+	unsigned int media_override:3, full_duplex:1, bus_master:1, autoselect:1;
+};
+
+static char *if_names[] = {
+	"10baseT", "10Mbs AUI", "undefined", "10base2",
+	"100baseTX", "100baseFX", "MII", "undefined"};
+
+static int vortex_scan(struct device *dev);
+static int vortex_found_device(struct device *dev, int ioaddr, int irq,
+							   int product_index, int options);
+static int vortex_probe1(struct device *dev);
+static int vortex_open(struct device *dev);
+static void vortex_timer(unsigned long arg);
+static int vortex_start_xmit(struct sk_buff *skb, struct device *dev);
+static int vortex_rx(struct device *dev);
+static void vortex_interrupt(int irq, struct pt_regs *regs);
+static int vortex_close(struct device *dev);
+static void update_stats(int addr, struct device *dev);
+static struct enet_statistics *vortex_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+
+/* Unlike the other PCI cards the 59x cards don't need a large contiguous
+   memory region, so making the driver a loadable module is feasible.
+
+   Unfortuneately maximizing the shared code between the integrated and
+   module version of the driver results in a complicated set of initialization
+   procedures.
+   init_module() -- modules /  tc59x_init()  -- built-in
+		The wrappers for vortex_scan()
+   vortex_scan()  		 The common routine that scans for PCI and EISA cards
+   vortex_found_device() Allocate a device structure when we find a card.
+					Different versions exist for modules and built-in.
+   vortex_probe1()		Fill in the device structure -- this is seperated
+					so that the modules code can put it in dev->init.
+*/
+/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
+/* Note: this is the only limit on the number of cards supported!! */
+int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,};
+
+#ifdef MODULE
+static int debug = -1;
+/* A list of all installed Vortex devices, for removing the driver module. */
+static struct device *root_vortex_dev = NULL;
+
+int
+init_module(void)
+{
+	int cards_found;
+
+	if (debug >= 0)
+		vortex_debug = debug;
+	if (vortex_debug)
+		printk(version);
+
+	root_vortex_dev = NULL;
+	cards_found = vortex_scan(0);
+	return cards_found < 0 ? cards_found : 0;
+}
+
+#else
+unsigned long tc59x_probe(struct device *dev)
+{
+	int cards_found = 0;
+
+	cards_found = vortex_scan(dev);
+
+	if (vortex_debug > 0  &&  cards_found)
+		printk(version);
+
+	return cards_found ? 0 : -ENODEV;
+}
+#endif  /* not MODULE */
+
+static int vortex_scan(struct device *dev)
+{
+	int cards_found = 0;
+
+	if (pcibios_present()) {
+		int pci_index;
+		for (pci_index = 0; pci_index < 8; pci_index++) {
+			unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
+			unsigned int pci_ioaddr;
+			unsigned short pci_command;
+			int index;
+
+			for (index = 0; product_ids[index]; index++) {
+				if ( ! pcibios_find_device(TCOM_VENDOR_ID, product_ids[index],
+										   pci_index, &pci_bus,
+										   &pci_device_fn))
+					break;
+			}
+			if ( ! product_ids[index])
+				break;
+
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+									 PCI_INTERRUPT_LINE, &pci_irq_line);
+			pcibios_read_config_dword(pci_bus, pci_device_fn,
+									  PCI_BASE_ADDRESS_0, &pci_ioaddr);
+			/* Remove I/O space marker in bit 0. */
+			pci_ioaddr &= ~3;
+
+#ifdef VORTEX_BUS_MASTER
+			/* Get and check the bus-master and latency values.
+			   Some PCI BIOSes fail to set the master-enable bit, and
+			   the latency timer must be set to the maximum value to avoid
+			   data corruption that occurs when the timer expires during
+			   a transfer.  Yes, it's a bug. */
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+									 PCI_COMMAND, &pci_command);
+			if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+				printk("  PCI Master Bit has not been set! Setting...\n");
+				pci_command |= PCI_COMMAND_MASTER;
+				pcibios_write_config_word(pci_bus, pci_device_fn,
+										  PCI_COMMAND, pci_command);
+			}
+			pcibios_read_config_byte(pci_bus, pci_device_fn,
+										 PCI_LATENCY_TIMER, &pci_latency);
+			if (pci_latency != 255) {
+				printk("  Overriding PCI latency timer (CFLT) setting of %d, new value is 255.\n", pci_latency);
+				pcibios_write_config_byte(pci_bus, pci_device_fn,
+										  PCI_LATENCY_TIMER, 255);
+			}
+#endif  /* VORTEX_BUS_MASTER */
+			vortex_found_device(dev, pci_ioaddr, pci_irq_line,
+							   index, options[cards_found]);
+			dev = 0;
+			cards_found++;
+		}
+	}
+
+	/* Now check all slots of the EISA bus. */
+	if (EISA_bus) {
+		static int ioaddr = 0x1000;
+		for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+			/* Check the standard EISA ID register for an encoded '3Com'. */
+			if (inw(ioaddr + 0xC80) != 0x6d50)
+				continue;
+			/* Check for a product that we support. */
+			if ((inw(ioaddr + 0xC82) & 0xFFF0) != 0x5970
+				&& (inw(ioaddr + 0xC82) & 0xFFF0) != 0x5920)
+				continue;
+			vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
+							   DEMON_INDEX, options[cards_found]);
+			dev = 0;
+			cards_found++;
+		}
+	}
+
+	return cards_found;
+}
+
+static int vortex_found_device(struct device *dev, int ioaddr, int irq,
+							   int product_index, int options)
+{
+	struct vortex_private *vp;
+
+#ifdef MODULE
+	/* Allocate and fill new device structure. */
+	int dev_size = sizeof(struct device) +
+		sizeof(struct vortex_private);
+	
+	dev = (struct device *) kmalloc(dev_size, GFP_KERNEL);
+	memset(dev, 0, dev_size);
+	dev->priv = ((void *)dev) + sizeof(struct device);
+	vp = (struct vortex_private *)dev->priv;
+	dev->name = vp->devname; /* An empty string. */
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+	dev->init = vortex_probe1;
+	vp->product_name = product_names[product_index];
+	vp->options = options;
+	if (options >= 0) {
+		vp->media_override = options & 7;
+		vp->full_duplex = (options & 8) ? 1 : 0;
+		vp->bus_master = (options & 16) ? 1 : 0;
+	} else {
+		vp->media_override = 7;
+		vp->full_duplex = 0;
+		vp->bus_master = 0;
+	}
+	ether_setup(dev);
+	vp->next_module = root_vortex_dev;
+	root_vortex_dev = dev;
+	if (register_netdev(dev) != 0)
+		return -EIO;
+#else  /* not a MODULE */
+	if (dev) {
+		dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL);
+		memset(dev->priv, 0, sizeof (struct vortex_private));
+	}
+	dev = init_etherdev(dev, sizeof(struct vortex_private));
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
+	vp  = (struct vortex_private *)dev->priv;
+	vp->product_name = product_names[product_index];
+	vp->options = options;
+	vortex_probe1(dev);
+#endif /* MODULE */
+	return 0;
+}
+
+static int vortex_probe1(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	int i;
+
+	printk("%s: 3Com %s at %#3x,", dev->name,
+		   vp->product_name, ioaddr);
+
+	/* Read the station address from the EEPROM. */
+	EL3WINDOW(0);
+	for (i = 0; i < 3; i++) {
+		short *phys_addr = (short *)dev->dev_addr;
+		int timer;
+		outw(EEPROM_Read + PhysAddr01 + i, ioaddr + Wn0EepromCmd);
+		/* Pause for at least 162 us. for the read to take place. */
+		for (timer = 0; timer < 162*4 + 400; timer++) {
+			SLOW_DOWN_IO;
+			if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
+				break;
+		}
+		phys_addr[i] = htons(inw(ioaddr + 12));
+	}
+	for (i = 0; i < 6; i++)
+		printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
+	printk(", IRQ %d\n", dev->irq);
+	/* Tell them about an invalid IRQ. */
+	if (vortex_debug && (dev->irq <= 0 || dev->irq > 15))
+		printk(" *** Warning: this IRQ is unlikely to work!\n");
+
+	{
+		char *ram_split[] = {"5:3", "3:1", "1:1", "invalid"};
+		union wn3_config config;
+		EL3WINDOW(3);
+		config.i = inl(ioaddr + Wn3_Config);
+		if (vortex_debug > 1)
+			printk("  Internal config register is %4.4x, transceivers %#x.\n",
+				   config.i, inw(ioaddr + Wn3_Options));
+		printk("  %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
+			   8 << config.u.ram_size,
+			   config.u.ram_width ? "word" : "byte",
+			   ram_split[config.u.ram_split],
+			   config.u.autoselect ? "autoselect/" : "",
+			   if_names[config.u.xcvr]);
+		dev->if_port = config.u.xcvr;
+		vp->autoselect = config.u.autoselect;
+	}
+
+	/* We do a request_region() only to register /proc/ioports info. */
+	request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name);
+
+	/* The 3c59x-specific entries in the device structure. */
+	dev->open = &vortex_open;
+	dev->hard_start_xmit = &vortex_start_xmit;
+	dev->stop = &vortex_close;
+	dev->get_stats = &vortex_get_stats;
+	dev->set_multicast_list = &set_multicast_list;
+#if defined (HAVE_SET_MAC_ADDR) && 0
+	dev->set_mac_address = &set_mac_address;
+#endif
+
+	return 0;
+}
+
+
+static int
+vortex_open(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	union wn3_config config;
+	int i;
+
+	/* Before initializing select the active media port. */
+	EL3WINDOW(3);
+	if (vp->full_duplex)
+		outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
+	config.i = inl(ioaddr + Wn3_Config);
+
+	if (vp->media_override != 7) {
+		if (vortex_debug > 1)
+			printk("%s: Media override to transceiver %d (%s).\n",
+				   dev->name, vp->media_override, if_names[vp->media_override]);
+		config.u.xcvr = vp->media_override;
+		dev->if_port = vp->media_override;
+		outl(config.i, ioaddr + Wn3_Config);
+	}
+
+	if (vortex_debug > 1) {
+		printk("%s: vortex_open() InternalConfig %8.8x.\n",
+			dev->name, config.i);
+	}
+
+	outw(TxReset, ioaddr + EL3_CMD);
+	for (i = 20; i >= 0 ; i--)
+		if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+			break;
+
+	outw(RxReset, ioaddr + EL3_CMD);
+	/* Wait a few ticks for the RxReset command to complete. */
+	for (i = 20; i >= 0 ; i--)
+		if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+			break;
+
+	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+
+	if (irq2dev_map[dev->irq] != NULL
+		|| (irq2dev_map[dev->irq] = dev) == NULL
+		|| dev->irq == 0
+		|| request_irq(dev->irq, &vortex_interrupt, 0, vp->product_name)) {
+		return -EAGAIN;
+	}
+
+	if (vortex_debug > 1) {
+		EL3WINDOW(4);
+		printk("%s: vortex_open() irq %d media status %4.4x.\n",
+			   dev->name, dev->irq, inw(ioaddr + Wn4_Media));
+	}
+
+	/* Set the station address and mask in window 2 each time opened. */
+	EL3WINDOW(2);
+	for (i = 0; i < 6; i++)
+		outb(dev->dev_addr[i], ioaddr + i);
+	for (; i < 12; i+=2)
+		outw(0, ioaddr + i);
+
+	if (dev->if_port == 3)
+		/* Start the thinnet transceiver. We should really wait 50ms...*/
+		outw(StartCoax, ioaddr + EL3_CMD);
+	else if (dev->if_port == 0) {
+		/* 10baseT interface, enabled link beat and jabber check. */
+		EL3WINDOW(4);
+		outw(inw(ioaddr + Wn4_Media) | Media_TP, ioaddr + Wn4_Media);
+	}
+
+	/* Switch to the stats window, and clear all stats by reading. */
+	outw(StatsDisable, ioaddr + EL3_CMD);
+	EL3WINDOW(6);
+	for (i = 0; i < 10; i++)	
+		inb(ioaddr + i);
+	inw(ioaddr + 10);
+	inw(ioaddr + 12);
+	/* New: On the Vortex we must also clear the BadSSD counter. */
+	EL3WINDOW(3);
+	inb(ioaddr + 12);
+
+	/* Switch to register set 7 for normal use. */
+	EL3WINDOW(7);
+
+	/* Accept b-case and phys addr only. */
+	outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+
+	dev->tbusy = 0;
+	dev->interrupt = 0;
+	dev->start = 1;
+
+	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+	/* Allow status bits to be seen. */
+	outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+	/* Ack all pending events, and set active indicator mask. */
+	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+		 ioaddr + EL3_CMD);
+	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+		 | DMADone, ioaddr + EL3_CMD);
+
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+
+	if (vp->autoselect) {
+		init_timer(&vp->timer);
+		vp->timer.expires = (14*HZ)/10; 			/* 1.4 sec. */
+		vp->timer.data = (unsigned long)dev;
+		vp->timer.function = &vortex_timer;    /* timer handler */
+		add_timer(&vp->timer);
+	}
+	return 0;
+}
+
+static void vortex_timer(unsigned long data)
+{
+	struct device *dev = (struct device *)data;
+	if (vortex_debug > 2)
+		printk("%s: Media selection timer tick happened.\n", dev->name);
+	/* ToDo: active media selection here! */
+}
+
+static int
+vortex_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+
+	/* Transmitter timeout, serious problems. */
+	if (dev->tbusy) {
+		int tickssofar = jiffies - dev->trans_start;
+		if (tickssofar < 40)
+			return 1;
+		printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+			   dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS));
+		vp->stats.tx_errors++;
+		/* Issue TX_RESET and TX_START commands. */
+		outw(TxReset, ioaddr + EL3_CMD);
+		{
+			int i;
+			for (i = 20; i >= 0 ; i--)
+				if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+					break;
+		}
+		outw(TxEnable, ioaddr + EL3_CMD);
+		dev->trans_start = jiffies;
+		dev->tbusy = 0;
+		return 0;
+	}
+
+	if (skb == NULL || skb->len <= 0) {
+		printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+			   dev->name);
+		dev_tint(dev);
+		return 0;
+	}
+
+	/* Block a timer-based transmit from overlapping.  This could better be
+	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+	   If this ever occurs the queue layer is doing something evil! */
+	if (set_bit(0, (void*)&dev->tbusy) != 0) {
+		printk("%s: Transmitter access conflict.\n", dev->name);
+		return 1;
+	}
+
+	/* Put out the doubleword header... */
+	outl(skb->len, ioaddr + TX_FIFO);
+#ifdef VORTEX_BUS_MASTER
+	if (vp->bus_master) {
+		/* Set the bus-master controller to transfer the packet. */
+		outl((int)(skb->data), ioaddr + Wn7_MasterAddr);
+		outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
+		vp->tx_skb = skb;
+		outw(StartDMADown, ioaddr + EL3_CMD);
+	} else {
+		/* ... and the packet rounded to a doubleword. */
+		outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+		dev_kfree_skb (skb, FREE_WRITE);
+		if (inw(ioaddr + TxFree) > 1536) {
+			dev->tbusy = 0;
+		} else
+			/* Interrupt us when the FIFO has room for max-sized packet. */
+			outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+	}
+#else
+	/* ... and the packet rounded to a doubleword. */
+	outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+	dev_kfree_skb (skb, FREE_WRITE);
+	if (inw(ioaddr + TxFree) > 1536) {
+		dev->tbusy = 0;
+	} else
+		/* Interrupt us when the FIFO has room for max-sized packet. */
+		outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+#endif  /* bus master */
+
+	dev->trans_start = jiffies;
+
+	/* Clear the Tx status stack. */
+	{
+		short tx_status;
+		int i = 4;
+
+		while (--i > 0	&&	(tx_status = inb(ioaddr + TxStatus)) > 0) {
+			if (tx_status & 0x3C) {		/* A Tx-disabling error occured.  */
+				if (vortex_debug > 2)
+				  printk("%s: Tx error, status %2.2x.\n",
+						 dev->name, tx_status);
+				if (tx_status & 0x04) vp->stats.tx_fifo_errors++;
+				if (tx_status & 0x38) vp->stats.tx_aborted_errors++;
+				if (tx_status & 0x30) {
+					int j;
+					outw(TxReset, ioaddr + EL3_CMD);
+					for (j = 20; j >= 0 ; j--)
+						if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+							break;
+				}
+				outw(TxEnable, ioaddr + EL3_CMD);
+			}
+			outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
+		}
+	}
+	return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+static void vortex_interrupt(int irq, struct pt_regs *regs)
+{
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+	struct vortex_private *lp;
+	int ioaddr, status;
+	int latency;
+	int i = 0;
+
+	if (dev == NULL) {
+		printk ("vortex_interrupt(): irq %d for unknown device.\n", irq);
+		return;
+	}
+
+	if (dev->interrupt)
+		printk("%s: Re-entering the interrupt handler.\n", dev->name);
+	dev->interrupt = 1;
+
+	ioaddr = dev->base_addr;
+	latency = inb(ioaddr + Timer);
+	lp = (struct vortex_private *)dev->priv;
+
+	status = inw(ioaddr + EL3_STATUS);
+
+	if (vortex_debug > 4)
+		printk("%s: interrupt, status %4.4x, timer %d.\n", dev->name,
+			   status, latency);
+	if ((status & 0xE000) != 0xE000) {
+		static int donedidthis=0;
+		/* Some interrupt controllers store a bogus interrupt from boot-time.
+		   Ignore a single early interrupt, but don't hang the machine for
+		   other interrupt problems. */
+		if (donedidthis++ > 1) {
+			printk("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
+				   dev->name, status, dev->start);
+			free_irq(dev->irq);
+		}
+	}
+
+	do {
+		if (vortex_debug > 5)
+				printk("%s: In interrupt loop, status %4.4x.\n",
+					   dev->name, status);
+		if (status & RxComplete)
+			vortex_rx(dev);
+
+		if (status & TxAvailable) {
+			if (vortex_debug > 5)
+				printk("	TX room bit was handled.\n");
+			/* There's room in the FIFO for a full-sized packet. */
+			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+			dev->tbusy = 0;
+			mark_bh(NET_BH);
+		}
+#ifdef VORTEX_BUS_MASTER
+		if (status & DMADone) {
+			outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
+			dev->tbusy = 0;
+			mark_bh(NET_BH);
+		}
+#endif
+		if (status & (AdapterFailure | RxEarly | StatsFull)) {
+			/* Handle all uncommon interrupts at once. */
+			if (status & RxEarly) {				/* Rx early is unused. */
+				vortex_rx(dev);
+				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+			}
+			if (status & StatsFull) { 	/* Empty statistics. */
+				static int DoneDidThat = 0;
+				if (vortex_debug > 4)
+					printk("%s: Updating stats.\n", dev->name);
+				update_stats(ioaddr, dev);
+				/* DEBUG HACK: Disable statistics as an interrupt source. */
+				/* This occurs when we have the wrong media type! */
+				if (DoneDidThat == 0  &&
+					inw(ioaddr + EL3_STATUS) & StatsFull) {
+					int win, reg;
+					printk("%s: Updating stats failed, disabling stats as an"
+						   " interrupt source.\n", dev->name);
+					for (win = 0; win < 8; win++) {
+						EL3WINDOW(win);
+						printk("\n Vortex window %d:", win);
+						for (reg = 0; reg < 16; reg++)
+							printk(" %2.2x", inb(ioaddr+reg));
+					}
+					EL3WINDOW(7);
+					outw(SetIntrEnb | 0x18, ioaddr + EL3_CMD);
+					DoneDidThat++;
+				}
+			}
+			if (status & AdapterFailure) {
+				/* Adapter failure requires Rx reset and reinit. */
+				outw(RxReset, ioaddr + EL3_CMD);
+				/* Set the Rx filter to the current state. */
+				outw(SetRxFilter | RxStation | RxBroadcast
+					 | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
+					 | (dev->flags & IFF_PROMISC ? RxProm : 0),
+					 ioaddr + EL3_CMD);
+				outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
+				outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+			}
+		}
+
+		if (++i > 10) {
+			printk("%s: Infinite loop in interrupt, status %4.4x.  "
+				   "Disabling functions (%4.4x).\n",
+				   dev->name, status, SetStatusEnb | ((~status) & 0xFE));
+			/* Disable all pending interrupts. */
+			outw(SetStatusEnb | ((~status) & 0xFE), ioaddr + EL3_CMD);
+			outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+			break;
+		}
+		/* Acknowledge the IRQ. */
+		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+
+	} while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
+
+	if (vortex_debug > 4)
+		printk("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
+
+	dev->interrupt = 0;
+	return;
+}
+
+static int
+vortex_rx(struct device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	int i;
+	short rx_status;
+
+	if (vortex_debug > 5)
+		printk("   In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
+	while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
+		if (rx_status & 0x4000) { /* Error, update stats. */
+			unsigned char rx_error = inb(ioaddr + RxErrors);
+			if (vortex_debug > 4)
+				printk(" Rx error: status %2.2x.\n", rx_error);
+			vp->stats.rx_errors++;
+			if (rx_error & 0x01)  vp->stats.rx_over_errors++;
+			if (rx_error & 0x02)  vp->stats.rx_length_errors++;
+			if (rx_error & 0x04)  vp->stats.rx_frame_errors++;
+			if (rx_error & 0x08)  vp->stats.rx_crc_errors++;
+			if (rx_error & 0x10)  vp->stats.rx_length_errors++;
+		} else {
+			/* The packet length: up to 4.5K!. */
+			short pkt_len = rx_status & 0x1fff;
+			struct sk_buff *skb;
+
+			skb = dev_alloc_skb(pkt_len + 5);
+			if (vortex_debug > 4)
+				printk("Receiving packet size %d status %4.4x.\n",
+					   pkt_len, rx_status);
+			if (skb != NULL) {
+				skb->dev = dev;
+				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
+				/* 'skb_put()' points to the start of sk_buff data area. */
+				insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len),
+					 (pkt_len + 3) >> 2);
+				skb->protocol = eth_type_trans(skb, dev);
+				netif_rx(skb);
+				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
+				/* Wait a limited time to go to next packet. */
+				for (i = 200; i >= 0; i--)
+					if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+						break;
+				vp->stats.rx_packets++;
+				continue;
+			} else if (vortex_debug)
+				printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+					   dev->name, pkt_len);
+		}
+		vp->stats.rx_dropped++;
+		outw(RxDiscard, ioaddr + EL3_CMD);
+		/* Wait a limited time to skip this packet. */
+		for (i = 200; i >= 0; i--)
+			if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+				break;
+	}
+
+	return 0;
+}
+
+static int
+vortex_close(struct device *dev)
+{
+	int ioaddr = dev->base_addr;
+
+	dev->start = 0;
+	dev->tbusy = 1;
+
+	if (vortex_debug > 1)
+		printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n",
+			   dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus));
+
+	/* Turn off statistics ASAP.  We update lp->stats below. */
+	outw(StatsDisable, ioaddr + EL3_CMD);
+
+	/* Disable the receiver and transmitter. */
+	outw(RxDisable, ioaddr + EL3_CMD);
+	outw(TxDisable, ioaddr + EL3_CMD);
+
+	if (dev->if_port == 3)
+		/* Turn off thinnet power.  Green! */
+		outw(StopCoax, ioaddr + EL3_CMD);
+	else if (dev->if_port == 0) {
+		/* Disable link beat and jabber, if_port may change ere next open(). */
+		EL3WINDOW(4);
+		outw(inw(ioaddr + Wn4_Media) & ~Media_TP, ioaddr + Wn4_Media);
+	}
+
+	free_irq(dev->irq);
+	/* Mmmm, we should diable all interrupt sources here. */
+	irq2dev_map[dev->irq] = 0;
+
+	update_stats(ioaddr, dev);
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+
+	return 0;
+}
+
+static struct enet_statistics *
+vortex_get_stats(struct device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+	update_stats(dev->base_addr, dev);
+	restore_flags(flags);
+	return &vp->stats;
+}
+
+/*  Update statistics.
+	Unlike with the EL3 we need not worry about interrupts changing
+	the window setting from underneath us, but we must still guard
+	against a race condition with a StatsUpdate interrupt updating the
+	table.  This is done by checking that the ASM (!) code generated uses
+	atomic updates with '+='.
+	*/
+static void update_stats(int ioaddr, struct device *dev)
+{
+	struct vortex_private *vp = (struct vortex_private *)dev->priv;
+
+	/* Unlike the 3c5x9 we need not turn off stats updates while reading. */
+	/* Switch to the stats window, and read everything. */
+	EL3WINDOW(6);
+	vp->stats.tx_carrier_errors		+= inb(ioaddr + 0);
+	vp->stats.tx_heartbeat_errors	+= inb(ioaddr + 1);
+	/* Multiple collisions. */		inb(ioaddr + 2);
+	vp->stats.collisions			+= inb(ioaddr + 3);
+	vp->stats.tx_window_errors		+= inb(ioaddr + 4);
+	vp->stats.rx_fifo_errors		+= inb(ioaddr + 5);
+	vp->stats.tx_packets			+= inb(ioaddr + 6);
+	vp->stats.tx_packets			+= (inb(ioaddr + 9)&15) << 8;
+	/* Rx packets	*/				inb(ioaddr + 7);   /* Must read to clear */
+	/* Tx deferrals */				inb(ioaddr + 8);
+	/* Don't bother with register 9, an extention of registers 6&7.
+	   If we do use the 6&7 values the atomic update assumption above
+	   is invalid. */
+	inw(ioaddr + 10);	/* Total Rx and Tx octets. */
+	inw(ioaddr + 12);
+	/* New: On the Vortex we must also clear the BadSSD counter. */
+	EL3WINDOW(3);
+	inb(ioaddr + 12);
+
+	/* We change back to window 7 (not 1) with the Vortex. */
+	EL3WINDOW(7);
+	return;
+}
+
+/* There are two version of set_multicast_list() to support both v1.2 and
+   v1.4 kernels. */
+static void
+set_multicast_list(struct device *dev)
+{
+	short ioaddr = dev->base_addr;
+
+	if ((dev->mc_list)  ||  (dev->flags & IFF_ALLMULTI)) {
+		outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
+		if (vortex_debug > 3) {
+			printk("%s: Setting Rx multicast mode, %d addresses.\n",
+				   dev->name, dev->mc_count);
+		}
+	} else if (dev->flags & IFF_PROMISC) {
+		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+			 ioaddr + EL3_CMD);
+	} else
+		outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+}
+
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+	struct device *next_dev;
+
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	while (root_vortex_dev) {
+		next_dev = ((struct vortex_private *)root_vortex_dev->priv)->next_module;
+		unregister_netdev(root_vortex_dev);
+		release_region(root_vortex_dev->base_addr, VORTEX_TOTAL_SIZE);
+		kfree(root_vortex_dev);
+		root_vortex_dev = next_dev;
+	}
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o 3c59x.o"
+ *  c-indent-level: 4
+ *  tab-width: 4
+ * End:
+ */
diff -u --recursive --new-file v1.3.58/linux/drivers/net/Config.in linux/drivers/net/Config.in
--- v1.3.58/linux/drivers/net/Config.in	Tue Jan 23 21:15:42 1996
+++ linux/drivers/net/Config.in	Mon Jan 22 22:52:34 1996
@@ -29,6 +29,7 @@
     tristate '3c507 support' CONFIG_EL16
   fi
   tristate '3c509/3c579 support' CONFIG_EL3
+  tristate '3c590 series (592/595/597) "Vortex" support' CONFIG_VORTEX
 fi
 bool 'Other ISA cards' CONFIG_NET_ISA
 if [ "$CONFIG_NET_ISA" = "y" ]; then
diff -u --recursive --new-file v1.3.58/linux/drivers/net/Makefile linux/drivers/net/Makefile
--- v1.3.58/linux/drivers/net/Makefile	Tue Jan 23 21:15:42 1996
+++ linux/drivers/net/Makefile	Mon Jan 22 22:52:34 1996
@@ -226,6 +226,14 @@
   endif
 endif
 
+ifeq ($(CONFIG_VORTEX),y)
+L_OBJS += 3c59x.o
+else
+  ifeq ($(CONFIG_VORTEX),m)
+  M_OBJS += 3c59x.o
+  endif
+endif
+
 ifeq ($(CONFIG_EEXPRESS),y)
 L_OBJS += eexpress.o
 else
diff -u --recursive --new-file v1.3.58/linux/drivers/net/Space.c linux/drivers/net/Space.c
--- v1.3.58/linux/drivers/net/Space.c	Sat Nov 25 19:04:40 1995
+++ linux/drivers/net/Space.c	Mon Jan 22 22:52:34 1996
@@ -68,6 +68,7 @@
 extern int ni65_probe(struct device *);
 extern int SK_init(struct device *);
 extern int seeq8005_probe(struct device *);
+extern int tc59x_probe(struct device *);
 
 /* Detachable devices ("pocket adaptors") */
 extern int atp_init(struct device *);
@@ -83,6 +84,9 @@
 	return 1;		/* ENXIO */
 
     if (1
+#if defined(CONFIG_VORTEX)
+	&& tc59x_probe(dev)
+#endif
 #if defined(CONFIG_SEEQ8005)
 	&& seeq8005_probe(dev)
 #endif
diff -u --recursive --new-file v1.3.58/linux/drivers/net/ibmtr.c linux/drivers/net/ibmtr.c
--- v1.3.58/linux/drivers/net/ibmtr.c	Thu Nov  9 11:23:49 1995
+++ linux/drivers/net/ibmtr.c	Mon Jan 22 22:16:17 1996
@@ -325,7 +325,7 @@
       if (intr==2) irq=10;
       if (intr==3) irq=11;
       while(!(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN));
-      ti->sram=(unsigned char *)((unsigned long)(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN) <<12);
+      ti->sram=(unsigned char *)((unsigned long)(*(ti->mmio + ACA_OFFSET + ACA_RW + RRR_EVEN)) <<12);
       ti->global_int_enable=PIOaddr+ADAPTINTREL;
       break;
       
diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/AM53C974.c linux/drivers/scsi/AM53C974.c
--- v1.3.58/linux/drivers/scsi/AM53C974.c	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/AM53C974.c	Tue Jan 23 21:52:43 1996
@@ -0,0 +1,2243 @@
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "AM53C974.h"
+#include "constants.h"
+#include "sd.h"
+
+/* AM53/79C974 (PCscsi) driver release 0.5
+ *
+ * The architecture and much of the code of this device
+ * driver was originally developed by Drew Eckhardt for
+ * the NCR5380. The following copyrights apply:
+ *  For the architecture and all pieces of code which can also be found 
+ *    in the NCR5380 device driver:
+ *   Copyright 1993, Drew Eckhardt
+ *	Visionary Computing 
+ *	(Unix and Linux consulting and custom programming)
+ * 	drew@colorado.edu
+ *	+1 (303) 666-5836
+ *
+ *  The AM53C974_nobios_detect code was origininally developed by
+ *   Robin Cutshaw (robin@xfree86.org) and is used here in a 
+ *   slightly modified form.
+ *
+ *  For the remaining code:
+ *    Copyright 1994, D. Frieauff
+ *    EMail: fri@rsx42sun0.dofn.de
+ *    Phone: x49-7545-8-2256 , x49-7541-42305
+ */
+
+/*
+ * $Log: AM53C974.c,v $
+ */
+
+#ifdef AM53C974_DEBUG
+ #define DEB(x) x
+ #ifdef AM53C974_DEBUG_KEYWAIT
+   #define KEYWAIT() AM53C974_keywait()
+  #else
+   #define KEYWAIT()
+  #endif
+ #ifdef AM53C974_DEBUG_INIT
+   #define DEB_INIT(x) x
+  #else
+   #define DEB_INIT(x)
+  #endif
+ #ifdef AM53C974_DEBUG_MSG
+   #define DEB_MSG(x) x
+  #else
+   #define DEB_MSG(x)
+  #endif
+ #ifdef AM53C974_DEB_RESEL
+   #define DEB_RESEL(x) x
+  #else
+   #define DEB_RESEL(x)
+  #endif
+ #ifdef AM53C974_DEBUG_QUEUE
+  #define DEB_QUEUE(x) x
+  #define LIST(x,y) {printk("LINE:%d   Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); }
+  #define REMOVE(w,x,y,z) {printk("LINE:%d   Removing: %p->%p  %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); }
+ #else
+  #define DEB_QUEUE(x)
+  #define LIST(x,y)
+  #define REMOVE(w,x,y,z)
+ #endif
+ #ifdef AM53C974_DEBUG_INFO
+   #define DEB_INFO(x) x
+  #else
+   #define DEB_INFO(x)
+  #endif
+ #ifdef AM53C974_DEBUG_LINKED
+   #define DEB_LINKED(x) x
+  #else
+   #define DEB_LINKED(x)
+  #endif
+ #ifdef AM53C974_DEBUG_INTR
+   #define DEB_INTR(x) x
+  #else
+   #define DEB_INTR(x)
+  #endif
+#else
+ #define DEB_INIT(x)
+ #define DEB(x)
+ #define DEB_QUEUE(x)
+ #define LIST(x,y)
+ #define REMOVE(w,x,y,z)
+ #define DEB_INFO(x)
+ #define DEB_LINKED(x)
+ #define DEB_INTR(x)
+ #define DEB_MSG(x)
+ #define DEB_RESEL(x)
+ #define KEYWAIT()
+#endif
+ #ifdef AM53C974_DEBUG_ABORT
+   #define DEB_ABORT(x) x
+  #else
+   #define DEB_ABORT(x)
+  #endif
+
+#ifdef VERBOSE_AM53C974_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define INSIDE(x,l,h) ( ((x) >= (l)) && ((x) <= (h)) )
+
+#ifdef AM53C974_DEBUG
+static void AM53C974_print_pci(struct Scsi_Host *instance);
+static void AM53C974_print_phase(struct Scsi_Host *instance);
+static void AM53C974_print_queues(struct Scsi_Host *instance);
+#endif /* AM53C974_DEBUG */
+static void AM53C974_print(struct Scsi_Host *instance);
+static void AM53C974_keywait(void);
+static int AM53C974_bios_detect(Scsi_Host_Template *tpnt);
+static int AM53C974_nobios_detect(Scsi_Host_Template *tpnt);
+static int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config);
+static void AM53C974_config_after_reset(struct Scsi_Host *instance);
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd);
+static __inline__ void run_main(void);
+static void AM53C974_main (void);
+static void AM53C974_intr(int irq, struct pt_regs *regs);
+static void AM53C974_intr_disconnect(struct Scsi_Host *instance); 
+static int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg);
+static __inline__ void AM53C974_set_async(struct Scsi_Host *instance, int target);
+static __inline__ void AM53C974_set_sync(struct Scsi_Host *instance, int target);
+static void AM53C974_information_transfer(struct Scsi_Host *instance, 
+                                          unsigned char statreg, unsigned char isreg,
+                                          unsigned char instreg, unsigned char cfifo,
+                                          unsigned char dmastatus);
+static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd, unsigned char msg);
+static void AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag);
+static void AM53C974_intr_reselect(struct Scsi_Host *instance, unsigned char statreg);
+static  __inline__ void AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
+                                              unsigned long length, char *data);
+static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus, 
+                               unsigned char statreg);
+static void AM53C974_intr_bus_reset(struct Scsi_Host *instance);
+
+static struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+static struct Scsi_Host *first_host = NULL;	/* Head of list of AMD boards */
+static volatile int main_running = 0;
+static int commandline_current = 0;
+override_t overrides[7] = { {-1, 0, 0, 0}, };   /* LILO overrides */
+
+#ifdef AM53C974_DEBUG
+static int deb_stop = 1;
+
+/**************************************************************************
+ * Function : void AM53C974_print_pci(struct Scsi_Host *instance)
+ *
+ * Purpose : dump the PCI registers for debugging purposes
+ *
+ * Input : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print_pci(struct Scsi_Host *instance)
+{
+int            i;
+unsigned short vendor_id, device_id, command, status, scratch[8];
+unsigned long  class_revision, base; 
+unsigned char  irq, cache_line_size, latency_timer, header_type;
+
+AM53C974_PCIREG_OPEN();
+
+for (i = 0; i < 8; i++) *(scratch + i) = AM53C974_PCIREG_READ_WORD(instance, PCI_SCRATCH_REG_0 + 2*i);
+vendor_id = AM53C974_PCIREG_READ_WORD(instance, PCI_VENDOR_ID);
+device_id = AM53C974_PCIREG_READ_WORD(instance, PCI_DEVICE_ID);
+command   = AM53C974_PCIREG_READ_WORD(instance, PCI_COMMAND);
+status    = AM53C974_PCIREG_READ_WORD(instance, PCI_STATUS);
+class_revision = AM53C974_PCIREG_READ_DWORD(instance, PCI_CLASS_REVISION);
+cache_line_size = AM53C974_PCIREG_READ_BYTE(instance, PCI_CACHE_LINE_SIZE);
+latency_timer = AM53C974_PCIREG_READ_BYTE(instance, PCI_LATENCY_TIMER);
+header_type = AM53C974_PCIREG_READ_BYTE(instance, PCI_HEADER_TYPE);
+base = AM53C974_PCIREG_READ_DWORD(instance, PCI_BASE_ADDRESS_0);
+irq = AM53C974_PCIREG_READ_BYTE(instance, PCI_INTERRUPT_LINE);
+
+AM53C974_PCIREG_CLOSE();
+
+
+printk("------------- start of PCI register dump -------------\n");
+printk("PCI_VENDOR_ID:       0x%x\n", vendor_id);
+printk("PCI_DEVICE_ID:       0x%x\n", device_id);
+printk("PCI_COMMAND:         0x%x\n", command);
+printk("PCI_STATUS:          0x%x\n", status);
+printk("PCI_CLASS_REVISION:  0x%lx\n", class_revision);
+printk("PCI_CACHE_LINE_SIZE: 0x%x\n", cache_line_size);
+printk("PCI_LATENCY_TIMER:   0x%x\n", latency_timer);
+printk("PCI_HEADER_TYPE:     0x%x\n", header_type);
+printk("PCI_BASE_ADDRESS_0:  0x%lx\n", base);
+printk("PCI_INTERRUPT_LINE:  %d\n", irq);
+for (i = 0; i < 8; i++) printk("PCI_SCRATCH_%d:       0x%x\n", i, scratch[i]);
+printk("------------- end of PCI register dump -------------\n\n");
+}
+
+static struct {
+    unsigned char value;
+    char *name;
+} phases[] = {
+{PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
+{PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
+{PHASE_RES_0, "RESERVED 0"}, {PHASE_RES_1, "RESERVED 1"}};
+
+/************************************************************************** 
+ * Function : void AM53C974_print_phase(struct Scsi_Host *instance)
+ *
+ * Purpose : print the current SCSI phase for debugging purposes
+ *
+ * Input : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print_phase(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+unsigned char statreg, latched;
+int           i;
+AM53C974_setio(instance);
+
+latched = (AM53C974_read_8(CNTLREG2)) & CNTLREG2_ENF;
+statreg = AM53C974_read_8(STATREG);
+for (i = 0; (phases[i].value != PHASE_RES_1) && 
+     (phases[i].value != (statreg & STATREG_PHASE)); ++i); 
+if (latched)
+   printk("scsi%d : phase %s, latched at end of last command\n", instance->host_no, phases[i].name);
+  else
+   printk("scsi%d : phase %s, real time\n", instance->host_no, phases[i].name);
+}
+
+/**************************************************************************
+ * Function : void AM53C974_print_queues(struct Scsi_Host *instance)
+ *
+ * Purpose : print commands in the various queues
+ *
+ * Inputs : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print_queues(struct Scsi_Host *instance)
+{
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd *ptr;
+
+printk("AM53C974: coroutine is%s running.\n", main_running ? "" : "n't");
+    
+cli();
+    
+if (!hostdata->connected) {
+   printk ("scsi%d: no currently connected command\n", instance->host_no); } 
+  else {
+   print_Scsi_Cmnd ((Scsi_Cmnd *)hostdata->connected); }
+if (!hostdata->sel_cmd) {
+   printk ("scsi%d: no currently arbitrating command\n", instance->host_no); } 
+  else {
+   print_Scsi_Cmnd ((Scsi_Cmnd *)hostdata->sel_cmd); }
+
+printk ("scsi%d: issue_queue ", instance->host_no);
+if (!hostdata->issue_queue)
+   printk("empty\n");
+  else {
+   printk(":\n");
+   for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *)ptr->host_scribble) 
+       print_Scsi_Cmnd (ptr); }
+
+printk ("scsi%d: disconnected_queue ", instance->host_no);
+if (!hostdata->disconnected_queue)
+   printk("empty\n");
+  else {
+   printk(":\n");
+   for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *)ptr->host_scribble) 
+       print_Scsi_Cmnd (ptr); }
+    
+sti();
+}
+
+#endif /* AM53C974_DEBUG */
+
+/**************************************************************************
+ * Function : void AM53C974_print(struct Scsi_Host *instance)
+ *
+ * Purpose : dump the chip registers for debugging purposes
+ *
+ * Input : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+unsigned long ctcreg, dmastc, dmaspa, dmawbc, dmawac;
+unsigned char cmdreg, statreg, isreg, cfireg, cntlreg[4], dmacmd, dmastatus;
+AM53C974_setio(instance);
+
+cli();
+ctcreg = AM53C974_read_8(CTCHREG) << 16;
+ctcreg |= AM53C974_read_8(CTCMREG) << 8;
+ctcreg |= AM53C974_read_8(CTCLREG);
+cmdreg = AM53C974_read_8(CMDREG);
+statreg = AM53C974_read_8(STATREG);
+isreg = AM53C974_read_8(ISREG);
+cfireg = AM53C974_read_8(CFIREG);
+cntlreg[0] = AM53C974_read_8(CNTLREG1);
+cntlreg[1] = AM53C974_read_8(CNTLREG2);
+cntlreg[2] = AM53C974_read_8(CNTLREG3);
+cntlreg[3] = AM53C974_read_8(CNTLREG4);
+dmacmd = AM53C974_read_8(DMACMD);
+dmastc = AM53C974_read_32(DMASTC);
+dmaspa = AM53C974_read_32(DMASPA);
+dmawbc = AM53C974_read_32(DMAWBC);
+dmawac = AM53C974_read_32(DMAWAC);
+dmastatus = AM53C974_read_8(DMASTATUS);
+sti();
+
+printk("AM53C974 register dump:\n");
+printk("IO base: 0x%04lx; CTCREG: 0x%04lx; CMDREG: 0x%02x; STATREG: 0x%02x; ISREG: 0x%02x\n",
+       io_port, ctcreg, cmdreg, statreg, isreg);
+printk("CFIREG: 0x%02x; CNTLREG1-4: 0x%02x; 0x%02x; 0x%02x; 0x%02x\n",
+        cfireg, cntlreg[0], cntlreg[1], cntlreg[2], cntlreg[3]);
+printk("DMACMD: 0x%02x; DMASTC: 0x%04lx; DMASPA: 0x%04lx\n", dmacmd, dmastc, dmaspa);
+printk("DMAWBC: 0x%04lx; DMAWAC: 0x%04lx; DMASTATUS: 0x%02x\n", dmawbc, dmawac, dmastatus);
+printk("---------------------------------------------------------\n");
+}
+
+/**************************************************************************
+* Function : void AM53C974_keywait(void)
+*
+* Purpose : wait until a key is pressed, if it was the 'r' key leave singlestep mode;
+*           this function is used for debugging only
+*
+* Input : none
+**************************************************************************/
+static void AM53C974_keywait(void)
+{
+#ifdef AM53C974_DEBUG
+int key;
+
+if (!deb_stop) return;
+#endif
+
+cli();
+while ((inb_p(0x64) & 0x01) != 0x01) ;
+#ifdef AM53C974_DEBUG
+key = inb(0x60);
+if (key == 0x93) deb_stop = 0;  /* don't stop if 'r' was pressed */
+#endif
+sti();
+}
+
+/**************************************************************************
+* Function : AM53C974_setup(char *str, int *ints)
+*
+* Purpose : LILO command line initialization of the overrides array,
+* 
+* Inputs : str - unused, ints - array of integer parameters with ints[0]
+*	    equal to the number of ints.
+*
+* NOTE : this function needs to be declared as an external function
+*         in init/main.c and included there in the bootsetups list
+***************************************************************************/
+void AM53C974_setup(char *str, int *ints)
+{
+if (ints[0] < 4) 
+   printk("AM53C974_setup: wrong number of parameters;\n correct syntax is: AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset\n");
+  else {
+   if (commandline_current < (sizeof(overrides) / sizeof(override_t))) {
+      if ((ints[1] < 0) || (ints[1] > 7) ||
+          (ints[2] < 0) || (ints[2] > 7) ||
+          (ints[1] == ints[2]) ||
+          (ints[3] < (DEF_CLK / MAX_PERIOD)) || (ints[3] > (DEF_CLK / MIN_PERIOD)) ||
+          (ints[4] < 0) || (ints[4] > MAX_OFFSET))
+         printk("AM53C974_setup: illegal parameter\n");
+        else {
+         overrides[commandline_current].host_scsi_id = ints[1];
+         overrides[commandline_current].target_scsi_id = ints[2];
+         overrides[commandline_current].max_rate = ints[3];
+         overrides[commandline_current].max_offset = ints[4];
+         commandline_current++; }
+      }
+     else
+      printk("AM53C974_setup: too many overrides\n");
+   }
+}
+
+#if defined (CONFIG_PCI)
+/**************************************************************************
+* Function : int AM53C974_bios_detect(Scsi_Host_Template *tpnt)
+*
+* Purpose : detects and initializes AM53C974 SCSI chips with PCI Bios
+*
+* Inputs : tpnt - host template
+* 
+* Returns : number of host adapters detected
+**************************************************************************/
+int AM53C974_bios_detect(Scsi_Host_Template *tpnt)
+{
+int count = 0;        /* number of boards detected */
+int pci_index;
+pci_config_t pci_config;
+
+for (pci_index = 0; pci_index <= 16; ++pci_index) {
+    unsigned char pci_bus, pci_device_fn;
+    if (pcibios_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SCSI, pci_index, &pci_bus, &pci_device_fn) != 0)
+       break;
+
+    pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &pci_config._vendor);
+    pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &pci_config._device);
+    pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_config._command);
+    pcibios_read_config_word(pci_bus, pci_device_fn, PCI_STATUS, &pci_config._status);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_CLASS_REVISION, &pci_config._class_revision);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_CACHE_LINE_SIZE, &pci_config._cache_line_size);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_config._latency_timer);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_HEADER_TYPE, &pci_config._header_type);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_BIST, &pci_config._bist);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &pci_config._base0);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &pci_config._base1);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &pci_config._base2);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_3, &pci_config._base3);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_4, &pci_config._base4);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_5, &pci_config._base5);
+    pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_ROM_ADDRESS, &pci_config._baserom);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pci_config._int_line);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_PIN, &pci_config._int_pin);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_MIN_GNT, &pci_config._min_gnt);
+    pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_MAX_LAT, &pci_config._max_lat);
+    pci_config._pcibus = 0xFFFFFFFF;
+    pci_config._cardnum = 0xFFFFFFFF;
+ 
+    /* check whether device is I/O mapped -- should be */
+    if (!(pci_config._command & PCI_COMMAND_IO)) continue;
+
+    /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility
+       to set the PCI Master Enable Bit if needed. 
+       (from Mark Stockton <marks@schooner.sys.hou.compaq.com>) */
+    if (!(pci_config._command & PCI_COMMAND_MASTER)) {
+       pci_config._command |= PCI_COMMAND_MASTER;
+       printk("PCI Master Bit has not been set. Setting...\n");
+       pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, pci_config._command); }
+
+    /* everything seems OK now, so initialize */
+    if (AM53C974_init(tpnt, pci_config)) count++ ;
+    }
+return (count);
+}
+#endif
+
+/**************************************************************************
+* Function : int AM53C974_nobios_detect(Scsi_Host_Template *tpnt)
+*
+* Purpose : detects and initializes AM53C974 SCSI chips using PCI config 2 
+*
+* Inputs : tpnt - host template
+* 
+* Returns : number of host adapters detected
+*
+* NOTE : This code assumes the controller on PCI bus 0.
+*
+* Origin: Robin Cutshaw (robin@xfree86.org)
+**************************************************************************/
+int AM53C974_nobios_detect(Scsi_Host_Template *tpnt)
+{
+int          count = 0;		/* number of boards detected */
+pci_config_t pci_config;
+
+/* first try PCI config method 1 */
+for (pci_config._pcibus = 0; pci_config._pcibus < 0x10; pci_config._pcibus++) {
+    for (pci_config._cardnum = 0; pci_config._cardnum < 0x20; pci_config._cardnum++) {
+        unsigned long config_cmd;
+	config_cmd = 0x80000000 | (pci_config._pcibus<<16) | (pci_config._cardnum<<11);
+
+        outl(config_cmd, 0xCF8);         /* ioreg 0 */
+        pci_config._device_vendor = inl(0xCFC);
+
+        if ((pci_config._vendor == PCI_VENDOR_ID_AMD) && (pci_config._device == PCI_DEVICE_ID_AMD_SCSI)) {
+           outl(config_cmd | PCI_COMMAND, 0xCF8); pci_config._status_command  = inl(0xCFC);
+           outl(config_cmd | PCI_CLASS_REVISION, 0xCF8); pci_config._class_revision = inl(0xCFC);
+           outl(config_cmd | PCI_CACHE_LINE_SIZE, 0xCF8); pci_config._bist_header_latency_cache = inl(0xCFC);
+           outl(config_cmd | PCI_BASE_ADDRESS_0, 0xCF8); pci_config._base0 = inl(0xCFC);
+           outl(config_cmd | PCI_BASE_ADDRESS_1, 0xCF8); pci_config._base1 = inl(0xCFC);
+           outl(config_cmd | PCI_BASE_ADDRESS_2, 0xCF8); pci_config._base2 = inl(0xCFC);
+           outl(config_cmd | PCI_BASE_ADDRESS_3, 0xCF8); pci_config._base3 = inl(0xCFC);
+           outl(config_cmd | PCI_BASE_ADDRESS_4, 0xCF8); pci_config._base4 = inl(0xCFC);
+           outl(config_cmd | PCI_BASE_ADDRESS_5, 0xCF8); pci_config._base5 = inl(0xCFC);
+           outl(config_cmd | PCI_ROM_ADDRESS, 0xCF8); pci_config._baserom = inl(0xCFC);
+           outl(config_cmd | PCI_INTERRUPT_LINE, 0xCF8); pci_config._max_min_ipin_iline = inl(0xCFC);
+
+           /* check whether device is I/O mapped -- should be */
+           if (!(pci_config._command & PCI_COMMAND_IO)) continue;
+
+           /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility
+              to set the PCI Master Enable Bit if needed. 
+              From Mark Stockton <marks@schooner.sys.hou.compaq.com> */
+           if (!(pci_config._command & PCI_COMMAND_MASTER)) {
+              pci_config._command |= PCI_COMMAND_MASTER;
+              printk("Config 1; PCI Master Bit has not been set. Setting...\n");
+              outl(config_cmd | PCI_COMMAND, 0xCF8); outw(pci_config._command, 0xCFC); }
+
+           /* everything seems OK now, so initialize */
+           if (AM53C974_init(tpnt, pci_config)) count++ ;
+           }
+        }
+    }
+outb(0, 0xCF8); /* is this really necessary? */
+
+/* try PCI config method 2, if no device was detected by method 1 */
+if (!count) {
+   AM53C974_PCIREG_OPEN();
+
+   pci_config._pcibus = 0xFFFFFFFF;
+   pci_config._cardnum = 0xFFFFFFFF;
+
+   for (pci_config._ioaddr = 0xC000; pci_config._ioaddr < 0xD000; pci_config._ioaddr += 0x0100) {
+       pci_config._device_vendor = inl(pci_config._ioaddr);
+
+       if ((pci_config._vendor == PCI_VENDOR_ID_AMD) && (pci_config._device == PCI_DEVICE_ID_AMD_SCSI)) {
+          pci_config._status_command = inl(pci_config._ioaddr + PCI_COMMAND);
+          pci_config._class_revision = inl(pci_config._ioaddr + PCI_CLASS_REVISION);
+          pci_config._bist_header_latency_cache = inl(pci_config._ioaddr + PCI_CACHE_LINE_SIZE);
+          pci_config._base0 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_0);
+          pci_config._base1 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_1);
+          pci_config._base2 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_2);
+          pci_config._base3 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_3);
+          pci_config._base4 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_4);
+          pci_config._base5 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_5);
+          pci_config._baserom = inl(pci_config._ioaddr + PCI_ROM_ADDRESS);
+          pci_config._max_min_ipin_iline = inl(pci_config._ioaddr + PCI_INTERRUPT_LINE);
+
+          /* check whether device is I/O mapped -- should be */
+          if (!(pci_config._command & PCI_COMMAND_IO)) continue;
+
+          /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility
+             to set the PCI Master Enable Bit if needed.
+             From Mark Stockton <marks@schooner.sys.hou.compaq.com> */
+          if (!(pci_config._command & PCI_COMMAND_MASTER)) {
+              pci_config._command |= PCI_COMMAND_MASTER;
+              printk("Config 2; PCI Master Bit has not been set. Setting...\n");
+              outw(pci_config._command, pci_config._ioaddr + PCI_COMMAND); }
+
+          /* everything seems OK now, so initialize */
+          if (AM53C974_init(tpnt, pci_config)) count++ ;
+          }
+       }
+   AM53C974_PCIREG_CLOSE();
+   }
+
+return(count);
+}
+
+/**************************************************************************
+* Function : int AM53C974_detect(Scsi_Host_Template *tpnt)
+*
+* Purpose : detects and initializes AM53C974 SCSI chips
+*
+* Inputs : tpnt - host template
+* 
+* Returns : number of host adapters detected
+**************************************************************************/
+int AM53C974_detect(Scsi_Host_Template *tpnt)
+{
+int count;        /* number of boards detected */
+
+#if defined (CONFIG_PCI)
+if (pcibios_present())
+   count = AM53C974_bios_detect(tpnt);
+  else
+#endif
+count = AM53C974_nobios_detect(tpnt);
+return (count);
+}
+
+/**************************************************************************
+* Function : int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config)
+*
+* Purpose : initializes instance and corresponding AM53/79C974 chip,
+*
+* Inputs : tpnt - template, pci_config - PCI configuration,
+* 
+* Returns : 1 on success, 0 on failure.
+* 
+* NOTE: If no override for the controller's SCSI id is given and AM53C974_SCSI_ID 
+*       is not defined we assume that the SCSI address of this controller is correctly
+*       set up by the BIOS (as reflected by contents of register CNTLREG1).
+*       This is the only BIOS assistance we need.
+**************************************************************************/
+static int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config)
+{
+AM53C974_local_declare();
+int                      i, j;
+struct Scsi_Host         *instance, *search;
+struct AM53C974_hostdata *hostdata;
+
+#ifdef AM53C974_OPTION_DEBUG_PROBE_ONLY
+   printk ("AM53C974: probe only enabled, aborting initialization\n");
+   return -1;
+#endif
+
+instance = scsi_register(tpnt, sizeof(struct AM53C974_hostdata));
+hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+instance->base = NULL;
+instance->io_port = pci_config._base0 & (pci_config._base0 & 0x1 ? 
+                                         0xFFFFFFFC : 0xFFFFFFF0);
+instance->irq = pci_config._int_line;
+instance->dma_channel = -1;
+AM53C974_setio(instance);
+
+#ifdef AM53C974_SCSI_ID
+instance->this_id = AM53C974_SCSI_ID;
+AM53C974_write_8(CNTLREG1, instance->this_id & CNTLREG1_SID);
+#else
+instance->this_id = AM53C974_read_8(CNTLREG1) & CNTLREG1_SID;
+if (instance->this_id != 7) 
+   printk("scsi%d: WARNING: unusual hostadapter SCSI id %d; please verify!\n", 
+          instance->host_no, instance->this_id);
+#endif
+
+for (i = 0; i < sizeof(hostdata->msgout); i++) {
+    hostdata->msgout[i] = NOP;
+    hostdata->last_message[i] = NOP; }
+for (i = 0; i < 8; i++) {
+    hostdata->busy[i] = 0;
+    hostdata->sync_per[i] = DEF_STP;
+    hostdata->sync_off[i] = 0;
+    hostdata->sync_neg[i] = 0;
+    hostdata->sync_en[i] = DEFAULT_SYNC_NEGOTIATION_ENABLED;
+    hostdata->max_rate[i] = DEFAULT_RATE;
+    hostdata->max_offset[i] = DEFAULT_SYNC_OFFSET; }
+
+/* overwrite defaults by LILO overrides */
+for (i = 0; i < commandline_current; i++) {
+    if (overrides[i].host_scsi_id == instance->this_id) {
+       j = overrides[i].target_scsi_id;
+       hostdata->sync_en[j] = 1;
+       hostdata->max_rate[j] = overrides[i].max_rate;
+       hostdata->max_offset[j] = overrides[i].max_offset; 
+       }
+    }
+
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->issue_queue = NULL;
+hostdata->disconnected_queue = NULL;
+hostdata->in_reset = 0;
+hostdata->aborted = 0;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+
+/* Set up an interrupt handler if we aren't already sharing an IRQ with another board */
+for (search = first_host; 
+     search && ( ((the_template != NULL) && (search->hostt != the_template)) || 
+                 (search->irq != instance->irq) || (search == instance) );
+     search = search->next);
+if (!search) {
+   if (request_irq(instance->irq, AM53C974_intr, SA_INTERRUPT, "AM53C974")) {
+      printk("scsi%d: IRQ%d not free, detaching\n", instance->host_no, instance->irq);
+      scsi_unregister(instance);
+      return -1; } 
+   }
+  else {
+   printk("scsi%d: using interrupt handler previously installed for scsi%d\n",
+	  instance->host_no, search->host_no); }
+
+if (!the_template) {
+   the_template = instance->hostt;
+   first_instance = instance; }
+
+/* do hard reset */
+AM53C974_write_8(CMDREG, CMDREG_RDEV);     /* reset device */
+udelay(5);
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); 
+AM53C974_write_8(CMDREG, CMDREG_RBUS);     /* reset SCSI bus */
+udelay(10);
+AM53C974_config_after_reset(instance);
+
+return(0);
+}
+
+/*********************************************************************
+* Function : AM53C974_config_after_reset(struct Scsi_Host *instance) *
+*                                                                    *
+* Purpose : initializes chip registers after reset                   *
+*                                                                    *
+* Inputs : instance - which AM53C974                                 *
+*                                                                    *
+* Returns : nothing                                                  *
+**********************************************************************/
+static void AM53C974_config_after_reset(struct Scsi_Host *instance)
+{
+AM53C974_local_declare(); 
+AM53C974_setio(instance);
+
+/* clear SCSI FIFO */
+AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+
+/* configure device */
+AM53C974_write_8(STIMREG, DEF_SCSI_TIMEOUT);
+AM53C974_write_8(STPREG, DEF_STP & STPREG_STP);
+AM53C974_write_8(SOFREG, (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
+AM53C974_write_8(CLKFREG, DEF_CLKF & CLKFREG_MASK);
+AM53C974_write_8(CNTLREG1, (DEF_ETM<<7) | CNTLREG1_DISR | (DEF_PERE<<4) | instance->this_id);
+AM53C974_write_8(CNTLREG2, (DEF_ENF<<6));
+AM53C974_write_8(CNTLREG3, (DEF_ADIDCHK<<7) | (DEF_FASTSCSI<<4) | (DEF_FASTCLK<<3));
+AM53C974_write_8(CNTLREG4, (DEF_GLITCH<<6) | (DEF_PWD<<5) | (DEF_RAE<<3) | (DEF_RADE<<2) | CNTLREG4_RES);
+}
+
+/***********************************************************************
+* Function : const char *AM53C974_info(struct Scsi_Host *instance)     *
+*                                                                      *
+* Purpose : return device driver information                           *
+*                                                                      *
+* Inputs : instance - which AM53C974                                   *
+*                                                                      *
+* Returns : info string                                                *
+************************************************************************/
+const char *AM53C974_info(struct Scsi_Host *instance)
+{
+static char       info[100];
+
+sprintf(info, "AM53/79C974 PCscsi driver rev. %d.%d; host I/O address: 0x%x; irq: %d\n", 
+        AM53C974_DRIVER_REVISION_MAJOR, AM53C974_DRIVER_REVISION_MINOR,
+        instance->io_port, instance->irq);
+return (info);
+}
+
+/************************************************************************** 
+* Function : int AM53C974_command (Scsi_Cmnd *SCpnt)                      *
+*                                                                         *
+* Purpose : the unqueued SCSI command function, replaced by the           *
+*           AM53C974_queue_command function                               *
+*                                                                         *
+* Inputs : SCpnt - pointer to command structure                           *
+*                                                                         *
+* Returns :status, see hosts.h for details                                *
+***************************************************************************/
+int AM53C974_command(Scsi_Cmnd *SCpnt)
+{
+DEB(printk("AM53C974_command called\n"));
+return 0;
+}
+
+/**************************************************************************
+* Function : void initialize_SCp(Scsi_Cmnd *cmd)                          *
+*                                                                         *
+* Purpose : initialize the saved data pointers for cmd to point to the    *
+*	    start of the buffer.                                          *                              
+*                                                                         *
+* Inputs : cmd - Scsi_Cmnd structure to have pointers reset.              *
+*                                                                         *
+* Returns : nothing                                                       *
+**************************************************************************/
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd)
+{
+if (cmd->use_sg) {
+   cmd->SCp.buffer = (struct scatterlist *)cmd->buffer;
+   cmd->SCp.buffers_residual = cmd->use_sg - 1;
+   cmd->SCp.ptr = (char *)cmd->SCp.buffer->address;
+   cmd->SCp.this_residual = cmd->SCp.buffer->length; }
+  else {
+   cmd->SCp.buffer = NULL;
+   cmd->SCp.buffers_residual = 0;
+   cmd->SCp.ptr = (char *)cmd->request_buffer;
+   cmd->SCp.this_residual = cmd->request_bufflen; }
+}
+
+/**************************************************************************
+* Function : run_main(void)                                               *
+*                                                                         *
+* Purpose : insure that the coroutine is running and will process our     *
+* 	    request.  main_running is checked/set here (in an inline      *
+*           function rather than in AM53C974_main itself to reduce the    *
+*           chances of stack overflow.                                    *
+*                                                                         *
+*                                                                         *
+* Inputs : none                                                           *
+*                                                                         *
+* Returns : nothing                                                       *
+**************************************************************************/
+static __inline__ void run_main(void)
+{
+cli();
+if (!main_running) {
+   /* main_running is cleared in AM53C974_main once it can't do 
+      more work, and AM53C974_main exits with interrupts disabled. */
+   main_running = 1;
+   AM53C974_main();
+   sti(); }
+  else 
+   sti();
+}
+
+/************************************************************************** 
+* Function : int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+*
+* Purpose : writes SCSI command into AM53C974 FIFO 
+*
+* Inputs : cmd - SCSI command, done - function called on completion, with
+*	a pointer to the command descriptor.
+* 
+* Returns : status, see hosts.h for details
+*
+* Side effects : 
+*      cmd is added to the per instance issue_queue, with minor 
+*	twiddling done to the host specific fields of cmd.  If the 
+*	main coroutine is not running, it is restarted.
+**************************************************************************/
+int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+struct Scsi_Host         *instance = cmd->host;
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd                *tmp;
+
+cli();
+DEB_QUEUE(printk(SEPARATOR_LINE));
+DEB_QUEUE(printk("scsi%d: AM53C974_queue_command called\n", instance->host_no));
+DEB_QUEUE(printk("cmd=%02x target=%02x lun=%02x bufflen=%d use_sg = %02x\n", 
+	   cmd->cmnd[0], cmd->target, cmd->lun, cmd->request_bufflen, cmd->use_sg));
+
+/* We use the host_scribble field as a pointer to the next command in a queue */
+cmd->host_scribble = NULL;
+cmd->scsi_done = done;
+cmd->result = 0;
+cmd->device->disconnect = 0;
+
+/* Insert the cmd into the issue queue. Note that REQUEST SENSE 
+ * commands are added to the head of the queue since any command will
+ * clear the contingent allegiance condition that exists and the 
+ * sense data is only guaranteed to be valid while the condition exists. */
+if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+   LIST(cmd, hostdata->issue_queue);
+   cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+   hostdata->issue_queue = cmd; }
+  else {
+   for (tmp = (Scsi_Cmnd *)hostdata->issue_queue; tmp->host_scribble; 
+	tmp = (Scsi_Cmnd *)tmp->host_scribble);
+   LIST(cmd, tmp);
+   tmp->host_scribble = (unsigned char *)cmd; }
+
+DEB_QUEUE(printk("scsi%d : command added to %s of queue\n", instance->host_no,
+	  (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"));
+
+/* Run the coroutine if it isn't already running. */
+run_main();
+return 0;
+}
+
+/**************************************************************************
+ * Function : AM53C974_main (void) 
+ *
+ * Purpose : AM53C974_main is a coroutine that runs as long as more work can 
+ *	be done on the AM53C974 host adapters in a system.  Both 
+ *	AM53C974_queue_command() and AM53C974_intr() will try to start it 
+ *	in case it is not running.
+ * 
+ * NOTE : AM53C974_main exits with interrupts *disabled*, the caller should 
+ *  reenable them.  This prevents reentrancy and kernel stack overflow.
+ **************************************************************************/  
+static void AM53C974_main(void)
+{
+AM53C974_local_declare(); 
+Scsi_Cmnd                *tmp, *prev;
+struct Scsi_Host         *instance;
+struct AM53C974_hostdata *hostdata;
+int                      done;
+
+/* We run (with interrupts disabled) until we're sure that none of 
+ * the host adapters have anything that can be done, at which point 
+ * we set main_running to 0 and exit. */
+
+do {
+   cli(); /* Freeze request queues */
+   done = 1;
+   for (instance = first_instance; instance && instance->hostt == the_template;
+        instance = instance->next) {
+       hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+       AM53C974_setio(instance);
+       /* start to select target if we are not connected and not in the 
+          selection process */
+       if (!hostdata->connected && !hostdata->sel_cmd) {
+          /* Search through the issue_queue for a command destined for a target 
+             that is not busy. */
+          for (tmp = (Scsi_Cmnd *)hostdata->issue_queue, prev = NULL; tmp; 
+               prev = tmp, tmp = (Scsi_Cmnd *)tmp->host_scribble) {
+	      /*  When we find one, remove it from the issue queue. */
+	      if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
+		 if (prev) {
+		    REMOVE(prev, (Scsi_Cmnd *)(prev->host_scribble), tmp,
+                           (Scsi_Cmnd *)(tmp->host_scribble));
+		    prev->host_scribble = tmp->host_scribble; } 
+                   else {
+		    REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble);
+		    hostdata->issue_queue = (Scsi_Cmnd *)tmp->host_scribble; }
+		 tmp->host_scribble = NULL;
+
+                 /* go into selection mode, disable reselection and wait for
+                    SO interrupt which will continue with the selection process */
+                 hostdata->selecting = 1;
+                 hostdata->sel_cmd = tmp;
+                 AM53C974_write_8(CMDREG, CMDREG_DSR); 
+                 break;                 
+		 } /* if target/lun is not busy */
+
+	      } /* for */
+          } /* if (!hostdata->connected) */
+         else {		
+          DEB(printk("main: connected; cmd = 0x%lx, sel_cmd = 0x%lx\n",
+                 (long)hostdata->connected, (long)hostdata->sel_cmd));
+	  }
+       } /* for instance */
+   } while (!done);
+main_running = 0;
+}
+
+/*********************************************************************
+* Function : AM53C974_intr(int irq, struct pt_regs *regs)            *
+*                                                                    *
+* Purpose : interrupt handler                                        *
+*                                                                    *
+* Inputs : irq - interrupt line, regs - ?                            *
+*                                                                    *
+* Returns : nothing                                                  *
+**********************************************************************/
+static void AM53C974_intr(int irq, struct pt_regs *regs)
+{
+AM53C974_local_declare(); 
+struct Scsi_Host         *instance;
+struct AM53C974_hostdata *hostdata;
+unsigned char            cmdreg, dmastatus, statreg, isreg, instreg, cfifo;
+
+/* find AM53C974 hostadapter responsible for this interrupt */
+for (instance = first_instance; instance; instance = instance->next)
+    if ((instance->irq == irq) && (instance->hostt == the_template)) goto FOUND;
+sti();
+return;
+
+/* found; now decode and process */
+FOUND:
+hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+dmastatus = AM53C974_read_8(DMASTATUS);
+
+DEB_INTR(printk(SEPARATOR_LINE));
+DEB_INTR(printk("AM53C974 interrupt; dmastatus=0x%02x\n", dmastatus));
+KEYWAIT();
+
+/*** DMA related interrupts ***/
+if (hostdata->connected && (dmastatus & (DMASTATUS_ERROR | DMASTATUS_PWDN | 
+                                         DMASTATUS_ABORT))) {
+   /* DMA error or POWERDOWN */
+   printk("scsi%d: DMA error or powerdown; dmastatus: 0x%02x\n",
+          instance->host_no, dmastatus);
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+   panic("scsi%d: cannot recover\n", instance->host_no); }
+
+if (hostdata->connected && (dmastatus & DMASTATUS_DONE)) {     
+   /* DMA transfer done */
+   unsigned long residual;
+   cli();
+   if (!(AM53C974_read_8(DMACMD) & DMACMD_DIR)) {
+      do {
+         dmastatus = AM53C974_read_8(DMASTATUS);
+         residual  = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+                    (AM53C974_read_8(CTCHREG) << 16);
+         residual += AM53C974_read_8(CFIREG) & CFIREG_CF;
+         } while (!(dmastatus & DMASTATUS_SCSIINT) && residual);
+      residual = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+                 (AM53C974_read_8(CTCHREG) << 16);
+      residual += AM53C974_read_8(CFIREG) & CFIREG_CF;
+      }
+     else
+      residual = 0;
+   hostdata->connected->SCp.ptr += hostdata->connected->SCp.this_residual - residual;
+   hostdata->connected->SCp.this_residual = residual;
+
+   AM53C974_write_8(DMACMD, DMACMD_IDLE);
+
+   /* if service request missed before, process it now (ugly) */
+   if (hostdata->dma_busy) {
+      hostdata->dma_busy = 0;
+      cmdreg = AM53C974_read_8(CMDREG);
+      statreg = AM53C974_read_8(STATREG);
+      isreg = AM53C974_read_8(ISREG);
+      instreg = AM53C974_read_8(INSTREG);
+      cfifo = AM53C974_cfifo();
+      AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo,
+                                    dmastatus); }
+   sti();
+   }
+   
+if (!(dmastatus & DMASTATUS_SCSIINT)) {
+   sti();
+   return; }
+
+/*** SCSI related interrupts ***/
+cmdreg = AM53C974_read_8(CMDREG);
+statreg = AM53C974_read_8(STATREG);
+isreg = AM53C974_read_8(ISREG);
+instreg = AM53C974_read_8(INSTREG);
+cfifo = AM53C974_cfifo();
+
+DEB_INTR(printk("scsi%d: statreg: 0x%02x; isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
+                instance->host_no, statreg, isreg, instreg, cfifo));
+
+if (statreg & STATREG_PE) {
+   /* parity error */
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+   printk("scsi%d : PARITY error\n", instance->host_no);
+   if (hostdata->connected) hostdata->sync_off[hostdata->connected->target] = 0; /* setup asynchronous transfer */
+   hostdata->aborted = 1; }
+
+if (statreg & STATREG_IOE) {
+   /* illegal operation error */
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+   printk("scsi%d : ILLEGAL OPERATION error\n", instance->host_no);
+   printk("cmdreg:  0x%02x; dmacmd:  0x%02x; statreg: 0x%02x; \n"
+          "isreg:   0x%02x; instreg: 0x%02x; cfifo:   0x%02x\n",
+           cmdreg, AM53C974_read_8(DMACMD), statreg, isreg, instreg, cfifo); }
+if (hostdata->in_reset && (instreg & INSTREG_SRST)) {
+   /* RESET INTERRUPT */
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+   DEB(printk("Bus reset interrupt received\n"));
+   AM53C974_intr_bus_reset(instance);
+   cli();
+   if (hostdata->connected) {
+      hostdata->connected->result = DID_RESET << 16;
+      hostdata->connected->scsi_done((Scsi_Cmnd *)hostdata->connected);
+      hostdata->connected = NULL; }
+     else { 
+      if (hostdata->sel_cmd) {
+         hostdata->sel_cmd->result = DID_RESET << 16;
+         hostdata->sel_cmd->scsi_done((Scsi_Cmnd *)hostdata->sel_cmd);
+         hostdata->sel_cmd = NULL; }
+      }
+   sti();
+   if (hostdata->in_reset == 1) goto EXIT;
+     else return;
+   }
+
+if (instreg & INSTREG_ICMD) {
+   /* INVALID COMMAND INTERRUPT */
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+   printk("scsi%d: Invalid command interrupt\n", instance->host_no);
+   printk("cmdreg:  0x%02x; dmacmd:  0x%02x; statreg: 0x%02x; dmastatus: 0x%02x; \n"
+          "isreg:   0x%02x; instreg: 0x%02x; cfifo:   0x%02x\n",
+           cmdreg, AM53C974_read_8(DMACMD), statreg, dmastatus, isreg, instreg, cfifo);
+   panic("scsi%d: cannot recover\n", instance->host_no); }
+
+if (instreg & INSTREG_DIS) {
+   /* DISCONNECT INTERRUPT */
+   DEB_INTR(printk("Disconnect interrupt received; "));
+   cli();
+   AM53C974_intr_disconnect(instance);
+   sti();
+   goto EXIT; }
+
+if (instreg & INSTREG_RESEL) {
+   /* RESELECTION INTERRUPT */
+   DEB_INTR(printk("Reselection interrupt received\n"));
+   cli();
+   AM53C974_intr_reselect(instance, statreg);
+   sti();
+   goto EXIT; }
+
+if (instreg & INSTREG_SO) {
+   DEB_INTR(printk("Successful operation interrupt received\n"));
+   if (hostdata->selecting) {
+      DEB_INTR(printk("DSR completed, starting select\n"));
+      cli();
+      AM53C974_select(instance, (Scsi_Cmnd *)hostdata->sel_cmd,
+                          (hostdata->sel_cmd->cmnd[0] == REQUEST_SENSE) ? 
+                                                  TAG_NONE : TAG_NEXT);
+      hostdata->selecting = 0;
+      AM53C974_set_sync(instance, hostdata->sel_cmd->target);
+      sti();
+      return; }
+
+   if (hostdata->sel_cmd != NULL) {
+      if ( ((isreg & ISREG_IS) != ISREG_OK_NO_STOP) &&
+           ((isreg & ISREG_IS) != ISREG_OK_STOP) ) {
+         /* UNSUCCESSFUL SELECTION */
+         DEB_INTR(printk("unsuccessful selection\n"));
+         cli();
+         hostdata->dma_busy = 0;
+	 LIST(hostdata->sel_cmd, hostdata->issue_queue);
+	 hostdata->sel_cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+         hostdata->issue_queue = hostdata->sel_cmd;
+         hostdata->sel_cmd = NULL;
+         hostdata->selecting = 0;
+         sti();
+         goto EXIT; }
+        else {
+         /* SUCCESSFUL SELECTION */
+         DEB(printk("successful selection; cmd=0x%02lx\n", (long)hostdata->sel_cmd));
+         cli();
+         hostdata->dma_busy = 0;
+         hostdata->disconnecting = 0;
+         hostdata->connected = hostdata->sel_cmd;
+         hostdata->sel_cmd = NULL;
+         hostdata->selecting = 0;
+#ifdef SCSI2
+         if (!hostdata->connected->device->tagged_queue)
+#endif    
+            hostdata->busy[hostdata->connected->target] |= (1 << hostdata->connected->lun);
+         /* very strange -- use_sg is sometimes nonzero for request sense commands !! */
+         if ((hostdata->connected->cmnd[0] == REQUEST_SENSE) && hostdata->connected->use_sg) {
+            DEB(printk("scsi%d: REQUEST_SENSE command with nonzero use_sg\n", instance->host_no));
+            KEYWAIT();
+            hostdata->connected->use_sg = 0; }
+         initialize_SCp((Scsi_Cmnd *)hostdata->connected);
+         hostdata->connected->SCp.phase = PHASE_CMDOUT;
+         AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+         sti();
+         return; }
+      }
+     else {
+      cli();
+      AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+      sti();
+      return; }
+   }
+    
+if (instreg & INSTREG_SR) {
+   DEB_INTR(printk("Service request interrupt received, "));
+   if (hostdata->connected) {
+      DEB_INTR(printk("calling information_transfer\n"));
+      cli();
+      AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+      sti(); }
+     else {
+      printk("scsi%d: weird: service request when no command connected\n", instance->host_no);
+      AM53C974_write_8(CMDREG, CMDREG_CFIFO); }   /* clear FIFO */
+   return;
+   }
+
+EXIT:
+  DEB_INTR(printk("intr: starting main\n"));
+  run_main();
+  DEB_INTR(printk("end of intr\n"));
+}
+
+/************************************************************************** 
+* Function : AM53C974_intr_disconnect(struct Scsi_Host *instance)
+*
+* Purpose : manage target disconnection
+*
+* Inputs : instance -- which AM53C974
+* 
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_intr_disconnect(struct Scsi_Host *instance) 
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd                *cmd;
+AM53C974_setio(instance);
+
+if (hostdata->sel_cmd != NULL) {
+   /* normal selection timeout, typical for nonexisting targets */
+   cmd = (Scsi_Cmnd *)hostdata->sel_cmd;
+   DEB_INTR(printk("bad target\n"));
+   cmd->result = DID_BAD_TARGET << 16;
+   goto EXIT_FINISHED; }
+
+if (!hostdata->connected) {
+   /* can happen if controller was reset, a device tried to reconnect,
+      failed and disconnects now */
+   AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+   return; }
+
+if (hostdata->disconnecting) {
+   /* target sent disconnect message, so we are prepared */
+   cmd = (Scsi_Cmnd *)hostdata->connected;
+   AM53C974_set_async(instance, cmd->target);
+   DEB_INTR(printk("scsi%d : disc. from cmnd %d for ta %d, lun %d\n",
+   	           instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+   if (cmd->device->disconnect) {
+      /* target wants to reselect later */
+      DEB_INTR(printk("ok, re-enabling selection\n"));
+      LIST(cmd,hostdata->disconnected_queue);
+      cmd->host_scribble = (unsigned char *)hostdata->disconnected_queue;
+      hostdata->disconnected_queue = cmd;
+      DEB_QUEUE(printk("scsi%d : command for target %d lun %d this %d was moved from connected to"
+   	               "  the disconnected_queue\n", instance->host_no, cmd->target,
+                        cmd->lun, hostdata->disconnected_queue->SCp.this_residual));
+      DEB_QUEUE(AM53C974_print_queues(instance));
+      goto EXIT_UNFINISHED; }
+     else {
+      /* target does not want to reselect later, we are really finished */
+#ifdef AM53C974_DEBUG
+      if (cmd->cmnd[0] == REQUEST_SENSE) {
+        int i;
+        printk("Request sense data dump:\n");
+        for (i = 0; i < cmd->request_bufflen; i++) {
+             printk("%02x ", *((char *)(cmd->request_buffer) + i));
+             if (i && !(i % 16)) printk("\n"); }
+        printk("\n"); }
+#endif
+      goto EXIT_FINISHED; } /* !cmd->device->disconnect */
+   } /* if (hostdata->disconnecting) */
+
+/* no disconnect message received; unexpected disconnection */
+cmd = (Scsi_Cmnd *)hostdata->connected;
+if (cmd) {
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+   AM53C974_set_async(instance, cmd->target);
+   printk("scsi%d: Unexpected disconnect; phase: %d; target: %d; this_residual: %d; buffers_residual: %d; message: %d\n",
+           instance->host_no, cmd->SCp.phase, cmd->target, cmd->SCp.this_residual, cmd->SCp.buffers_residual,
+           cmd->SCp.Message);
+   printk("cmdreg: 0x%02x; statreg: 0x%02x; isreg: 0x%02x; cfifo: 0x%02x\n",
+          AM53C974_read_8(CMDREG), AM53C974_read_8(STATREG), AM53C974_read_8(ISREG),
+           AM53C974_read_8(CFIREG) & CFIREG_CF);
+
+    if ((hostdata->last_message[0] == EXTENDED_MESSAGE) && 
+        (hostdata->last_message[2] == EXTENDED_SDTR)) {
+        /* sync. negotiation was aborted, setup asynchronous transfer with target */
+        hostdata->sync_off[cmd->target] = 0; }
+   if (hostdata->aborted || hostdata->msgout[0] == ABORT)
+      cmd->result = DID_ABORT << 16;
+     else
+      cmd->result = DID_ERROR << 16;
+   goto EXIT_FINISHED; }
+
+EXIT_FINISHED:
+hostdata->aborted = 0;
+hostdata->msgout[0] = NOP;
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+DEB(printk("disconnect; issue_queue: 0x%lx, disconnected_queue: 0x%lx\n", 
+       (long)hostdata->issue_queue, (long)hostdata->disconnected_queue));
+cmd->scsi_done(cmd);
+
+if (!hostdata->selecting) {
+   AM53C974_set_async(instance, cmd->target); 
+   AM53C974_write_8(CMDREG, CMDREG_ESR); } /* allow reselect */
+return;
+
+EXIT_UNFINISHED:
+hostdata->msgout[0] = NOP;
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->aborted = 0;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+DEB(printk("disconnect; issue_queue: 0x%lx, disconnected_queue: 0x%lx\n", 
+       (long)hostdata->issue_queue, (long)hostdata->disconnected_queue));
+if (!hostdata->selecting) {
+   AM53C974_set_async(instance, cmd->target); 
+   AM53C974_write_8(CMDREG, CMDREG_ESR); } /* allow reselect */
+return;
+}
+
+/************************************************************************** 
+* Function : int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg)
+*
+* Purpose : setup message string for sync. negotiation
+*
+* Inputs : instance -- which AM53C974
+*          target -- which SCSI target to deal with
+*          msg -- input message string
+* 
+* Returns : 0 if parameters accepted or 1 if not accepted
+*
+* Side effects: hostdata is changed
+*
+* Note: we assume here that fastclk is enabled
+**************************************************************************/
+static int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg) 
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+int                      period, offset, i, rate, rate_rem;
+AM53C974_setio(instance);
+
+period = (DEF_CLK * msg[3] * 8 + 1000) / 2000;
+if (period < MIN_PERIOD) {
+    period = MIN_PERIOD;
+    hostdata->msgout[3] = period / 4; }
+   else
+    if (period > MAX_PERIOD) {
+       period = MAX_PERIOD;
+       hostdata->msgout[3] = period / 4; }
+      else
+       hostdata->msgout[3] = msg[3];
+offset = msg[4]; 
+if (offset > MAX_OFFSET) offset = MAX_OFFSET;
+hostdata->msgout[4] = offset;
+hostdata->sync_per[target] = period;
+hostdata->sync_off[target] = offset;
+for (i = 0; i < 3; i++) hostdata->msgout[i] = msg[i];
+if ((hostdata->msgout[3] != msg[3]) || (msg[4] != offset)) return(1);
+
+rate = DEF_CLK / period;
+rate_rem = 10 * (DEF_CLK - period * rate) / period;
+
+if (offset)
+   printk("\ntarget %d: rate=%d.%d Mhz, synchronous, sync offset=%d bytes\n",
+          target, rate, rate_rem, offset);
+  else
+   printk("\ntarget %d: rate=%d.%d Mhz, asynchronous\n", target, rate, rate_rem);
+
+return(0);
+}
+
+/************************************************************************** 
+* Function : AM53C974_set_async(struct Scsi_Host *instance, int target)
+*
+* Purpose : put controller into async. mode
+*
+* Inputs : instance -- which AM53C974
+*          target -- which SCSI target to deal with
+* 
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_set_async(struct Scsi_Host *instance, int target)
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+AM53C974_write_8(STPREG, hostdata->sync_per[target]);
+AM53C974_write_8(SOFREG, (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
+}
+
+/************************************************************************** 
+* Function : AM53C974_set_sync(struct Scsi_Host *instance, int target)
+*
+* Purpose : put controller into sync. mode
+*
+* Inputs : instance -- which AM53C974
+*          target -- which SCSI target to deal with
+* 
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_set_sync(struct Scsi_Host *instance, int target)
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+AM53C974_write_8(STPREG, hostdata->sync_per[target]);
+AM53C974_write_8(SOFREG, (SOFREG_SO & hostdata->sync_off[target]) | 
+                (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
+}
+
+/***********************************************************************
+* Function : AM53C974_information_transfer(struct Scsi_Host *instance, *
+*                          unsigned char statreg, unsigned char isreg, *
+*                         unsigned char instreg, unsigned char cfifo,  *
+*                         unsigned char dmastatus)                     *
+*                                                                      *
+* Purpose : handle phase changes                                       *
+*                                                                      *
+* Inputs : instance - which AM53C974                                   *
+*          statreg - stus register                                     *
+*          isreg - internal state register                             *
+*          instreg - interrupt status register                         *
+*          cfifo - number of bytes in FIFO                             *
+*          dmastatus - dma status register                             *
+*                                                                      *
+* Returns : nothing                                                    *
+************************************************************************/
+static void AM53C974_information_transfer(struct Scsi_Host *instance, 
+                                          unsigned char statreg, unsigned char isreg,
+                                          unsigned char instreg, unsigned char cfifo,
+                                          unsigned char dmastatus)
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd                *cmd = (Scsi_Cmnd *)hostdata->connected;
+int                      ret, i, len, residual=-1;
+AM53C974_setio(instance);
+
+DEB_INFO(printk(SEPARATOR_LINE));
+switch (statreg & STATREG_PHASE) {	/* scsi phase */
+  case PHASE_DATAOUT:
+    DEB_INFO(printk("Dataout phase; cmd=0x%lx, sel_cmd=0x%lx, this_residual=%d, buffers_residual=%d\n",
+                    (long)hostdata->connected, (long)hostdata->sel_cmd, cmd->SCp.this_residual, cmd->SCp.buffers_residual));
+    cmd->SCp.phase = PHASE_DATAOUT;
+    goto PHASE_DATA_IO;
+
+  case PHASE_DATAIN:
+    DEB_INFO(printk("Datain phase; cmd=0x%lx, sel_cmd=0x%lx, this_residual=%d, buffers_residual=%d\n",
+                     (long)hostdata->connected, (long)hostdata->sel_cmd, cmd->SCp.this_residual, cmd->SCp.buffers_residual));
+    cmd->SCp.phase = PHASE_DATAIN;
+    PHASE_DATA_IO:
+    if (hostdata->aborted) {
+       AM53C974_write_8(DMACMD, DMACMD_IDLE);
+       AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+       AM53C974_write_8(CMDREG, CMDREG_SATN);
+       return; }
+    if ((!cmd->SCp.this_residual) && cmd->SCp.buffers_residual) {
+       cmd->SCp.buffer++;
+       cmd->SCp.buffers_residual--;
+       cmd->SCp.ptr = (unsigned char *)cmd->SCp.buffer->address;
+       cmd->SCp.this_residual = cmd->SCp.buffer->length; }
+    if (cmd->SCp.this_residual) {
+       if (!(AM53C974_read_8(DMACMD) & DMACMD_START)) {
+          hostdata->dma_busy = 0;
+          AM53C974_transfer_dma(instance, statreg & STATREG_IO,
+                                (unsigned long)cmd->SCp.this_residual,
+                                cmd->SCp.ptr); }
+         else
+          hostdata->dma_busy = 1;
+       }
+    return;
+
+  case PHASE_MSGIN:
+    DEB_INFO(printk("Message-In phase; cmd=0x%lx, sel_cmd=0x%lx\n",
+                    (long)hostdata->connected, (long)hostdata->sel_cmd));
+    AM53C974_set_async(instance, cmd->target);
+    if (cmd->SCp.phase == PHASE_DATAIN)
+       AM53C974_dma_blast(instance, dmastatus, statreg);
+    if ((cmd->SCp.phase == PHASE_DATAOUT) && (AM53C974_read_8(DMACMD) & DMACMD_START)) {
+       AM53C974_write_8(DMACMD, DMACMD_IDLE);
+       residual = cfifo + (AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+                          (AM53C974_read_8(CTCHREG) << 16));
+       cmd->SCp.ptr += cmd->SCp.this_residual - residual;
+       cmd->SCp.this_residual = residual;
+       if (cfifo) { AM53C974_write_8(CMDREG, CMDREG_CFIFO); cfifo = 0; }
+       }
+    if (cmd->SCp.phase == PHASE_STATIN) {
+       while ((AM53C974_read_8(CFIREG) & CFIREG_CF) < 2) ;
+       cmd->SCp.Status = AM53C974_read_8(FFREG);
+       cmd->SCp.Message = AM53C974_read_8(FFREG); 
+       DEB_INFO(printk("Message-In phase; status=0x%02x, message=0x%02x\n",
+                       cmd->SCp.Status, cmd->SCp.Message));
+       ret = AM53C974_message(instance, cmd, cmd->SCp.Message); }
+      else {
+       if (!cfifo) {
+          AM53C974_write_8(CMDREG, CMDREG_IT); 
+          AM53C974_poll_int();
+          cmd->SCp.Message = AM53C974_read_8(FFREG);
+          }
+       ret = AM53C974_message(instance, cmd, cmd->SCp.Message);
+       }
+    cmd->SCp.phase = PHASE_MSGIN;
+    AM53C974_set_sync(instance, cmd->target);
+    break;
+  case PHASE_MSGOUT:
+    DEB_INFO(printk("Message-Out phase; cfifo=%d; msgout[0]=0x%02x\n",
+                    AM53C974_read_8(CFIREG) & CFIREG_CF, hostdata->msgout[0]));
+    AM53C974_write_8(DMACMD, DMACMD_IDLE);
+    AM53C974_set_async(instance, cmd->target);
+    for (i = 0; i < sizeof(hostdata->last_message); i++) 
+        hostdata->last_message[i] = hostdata->msgout[i];
+    if ((hostdata->msgout[0] == 0) || INSIDE(hostdata->msgout[0], 0x02, 0x1F) || 
+        INSIDE(hostdata->msgout[0], 0x80, 0xFF)) 
+       len = 1;
+      else {
+       if (hostdata->msgout[0] == EXTENDED_MESSAGE) {
+#ifdef AM53C974_DEBUG_INFO
+          printk("Extended message dump:\n");
+          for (i = 0; i < hostdata->msgout[1] + 2; i++) {
+              printk("%02x ", hostdata->msgout[i]);
+              if (i && !(i % 16)) printk("\n"); }
+          printk("\n");
+#endif
+          len = hostdata->msgout[1] + 2; }
+         else
+          len = 2;
+       }
+    for (i = 0; i < len; i++) AM53C974_write_8(FFREG, hostdata->msgout[i]);
+    AM53C974_write_8(CMDREG, CMDREG_IT);
+    cmd->SCp.phase = PHASE_MSGOUT;
+    hostdata->msgout[0] = NOP;
+    AM53C974_set_sync(instance, cmd->target);
+    break;
+
+  case PHASE_CMDOUT:
+    DEB_INFO(printk("Command-Out phase\n"));
+    AM53C974_set_async(instance, cmd->target);
+    for (i = 0; i < cmd->cmd_len; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]);
+    AM53C974_write_8(CMDREG, CMDREG_IT);
+    cmd->SCp.phase = PHASE_CMDOUT;
+    AM53C974_set_sync(instance, cmd->target);
+    break;
+
+  case PHASE_STATIN:
+    DEB_INFO(printk("Status phase\n"));
+    if (cmd->SCp.phase == PHASE_DATAIN)
+       AM53C974_dma_blast(instance, dmastatus, statreg);
+    AM53C974_set_async(instance, cmd->target);
+    if (cmd->SCp.phase == PHASE_DATAOUT) {
+       unsigned long residual;
+
+       if (AM53C974_read_8(DMACMD) & DMACMD_START) {
+          AM53C974_write_8(DMACMD, DMACMD_IDLE);
+          residual = cfifo + (AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+                              (AM53C974_read_8(CTCHREG) << 16));
+          cmd->SCp.ptr += cmd->SCp.this_residual - residual;
+          cmd->SCp.this_residual = residual; }
+       if (cfifo) { AM53C974_write_8(CMDREG, CMDREG_CFIFO); cfifo = 0; }
+       }
+    cmd->SCp.phase = PHASE_STATIN;
+    AM53C974_write_8(CMDREG, CMDREG_ICCS);  /* command complete */
+    break;
+
+  case PHASE_RES_0:
+  case PHASE_RES_1:
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+    DEB_INFO(printk("Reserved phase\n"));
+    break;
+  }
+KEYWAIT();
+}
+
+/******************************************************************************
+* Function : int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+*                                 unsigned char msg)                
+*
+* Purpose : handle SCSI messages
+*
+* Inputs : instance -- which AM53C974
+*          cmd -- SCSI command the message belongs to
+*          msg -- message id byte
+* 
+* Returns : 1 on success, 0 on failure.
+**************************************************************************/
+static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+                            unsigned char msg)
+{
+AM53C974_local_declare(); 
+static unsigned char     extended_msg[10];
+unsigned char            statreg;
+int                      len, ret = 0;
+unsigned char            *p;
+#ifdef AM53C974_DEBUG_MSG
+int                      j;
+#endif
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+DEB_MSG(printk(SEPARATOR_LINE));
+
+/* Linking lets us reduce the time required to get the 
+ * next command out to the device, hopefully this will
+ * mean we don't waste another revolution due to the delays
+ * required by ARBITRATION and another SELECTION.
+ * In the current implementation proposal, low level drivers
+ * merely have to start the next command, pointed to by 
+ * next_link, done() is called as with unlinked commands. */
+switch (msg) {
+#ifdef LINKED
+  case LINKED_CMD_COMPLETE:
+  case LINKED_FLG_CMD_COMPLETE:
+    /* Accept message by releasing ACK */
+    DEB_LINKED(printk("scsi%d : target %d lun %d linked command complete.\n",
+			instance->host_no, cmd->target, cmd->lun));
+    /* Sanity check : A linked command should only terminate with
+     * one of these messages if there are more linked commands available. */
+    if (!cmd->next_link) {
+       printk("scsi%d : target %d lun %d linked command complete, no next_link\n"
+	       instance->host_no, cmd->target, cmd->lun);
+       hostdata->aborted = 1;
+       AM53C974_write_8(CMDREG, CMDREG_SATN);
+       AM53C974_write_8(CMDREG, CMDREG_MA);
+       break; }
+    if (hostdata->aborted) {
+       DEB_ABORT(printk("ATN set for cmnd %d upon reception of LINKED_CMD_COMPLETE or"
+                        "LINKED_FLG_CMD_COMPLETE message\n", cmd->cmnd[0]));
+       AM53C974_write_8(CMDREG, CMDREG_SATN); }
+    AM53C974_write_8(CMDREG, CMDREG_MA);
+
+    initialize_SCp(cmd->next_link);
+    /* The next command is still part of this process */
+    cmd->next_link->tag = cmd->tag;
+    cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
+    DEB_LINKED(printk("scsi%d : target %d lun %d linked request done, calling scsi_done().\n",
+		      instance->host_no, cmd->target, cmd->lun));
+    cmd->scsi_done(cmd);
+    cmd = hostdata->connected;
+    break;
+
+#endif /* def LINKED */
+
+  case ABORT:
+  case COMMAND_COMPLETE: 
+    DEB_MSG(printk("scsi%d: command complete message received; cmd %d for target %d, lun %d\n",
+	           instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+    hostdata->disconnecting = 1;
+    cmd->device->disconnect = 0;
+
+    /* I'm not sure what the correct thing to do here is : 
+     * 
+     * If the command that just executed is NOT a request 
+     * sense, the obvious thing to do is to set the result
+     * code to the values of the stored parameters.
+     * If it was a REQUEST SENSE command, we need some way 
+     * to differentiate between the failure code of the original
+     * and the failure code of the REQUEST sense - the obvious
+     * case is success, where we fall through and leave the result
+     * code unchanged.
+     * 
+     * The non-obvious place is where the REQUEST SENSE failed  */
+    if (cmd->cmnd[0] != REQUEST_SENSE) 
+       cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8); 
+      else if (cmd->SCp.Status != GOOD)
+       cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);		    
+    if (hostdata->aborted) {
+       AM53C974_write_8(CMDREG, CMDREG_SATN);
+       AM53C974_write_8(CMDREG, CMDREG_MA);
+       DEB_ABORT(printk("ATN set for cmnd %d upon reception of ABORT or"
+                        "COMMAND_COMPLETE message\n", cmd->cmnd[0]));
+       break; }
+    if ((cmd->cmnd[0] != REQUEST_SENSE) && (cmd->SCp.Status == CHECK_CONDITION)) {
+       DEB_MSG(printk("scsi%d : performing request sense\n", instance->host_no));
+       cmd->cmnd[0] = REQUEST_SENSE;
+       cmd->cmnd[1] &= 0xe0;
+       cmd->cmnd[2] = 0;
+       cmd->cmnd[3] = 0;
+       cmd->cmnd[4] = sizeof(cmd->sense_buffer);
+       cmd->cmnd[5] = 0;
+       cmd->SCp.buffer = NULL;
+       cmd->SCp.buffers_residual = 0;
+       cmd->SCp.ptr = (char *)cmd->sense_buffer;
+       cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
+       LIST(cmd,hostdata->issue_queue);
+       cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+       hostdata->issue_queue = (Scsi_Cmnd *)cmd;
+       DEB_MSG(printk("scsi%d : REQUEST SENSE added to head of issue queue\n",instance->host_no));
+       }
+
+    /* Accept message by clearing ACK */
+    AM53C974_write_8(CMDREG, CMDREG_MA);
+    break;
+		
+  case MESSAGE_REJECT:
+    DEB_MSG(printk("scsi%d: reject message received; cmd %d for target %d, lun %d\n",
+	           instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+    switch (hostdata->last_message[0]) {
+      case EXTENDED_MESSAGE:
+        if (hostdata->last_message[2] == EXTENDED_SDTR) {
+           /* sync. negotiation was rejected, setup asynchronous transfer with target */
+           printk("\ntarget %d: rate=%d Mhz, asynchronous (sync. negotiation rejected)\n",
+                  cmd->target,  DEF_CLK / DEF_STP);
+           hostdata->sync_off[cmd->target] = 0;
+           hostdata->sync_per[cmd->target] = DEF_STP; }
+        break;
+      case HEAD_OF_QUEUE_TAG:
+      case ORDERED_QUEUE_TAG:
+      case SIMPLE_QUEUE_TAG:
+        cmd->device->tagged_queue = 0;
+        hostdata->busy[cmd->target] |= (1 << cmd->lun);
+        break;
+      default:
+        break;
+      }
+    if (hostdata->aborted) AM53C974_write_8(CMDREG, CMDREG_SATN);
+    AM53C974_write_8(CMDREG, CMDREG_MA);
+    break;
+	
+  case DISCONNECT:
+    DEB_MSG(printk("scsi%d: disconnect message received; cmd %d for target %d, lun %d\n",
+	           instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+    cmd->device->disconnect = 1;
+    hostdata->disconnecting = 1;
+    AM53C974_write_8(CMDREG, CMDREG_MA); /* Accept message by clearing ACK */
+    break;
+		
+  case SAVE_POINTERS:
+  case RESTORE_POINTERS:
+    DEB_MSG(printk("scsi%d: save/restore pointers message received; cmd %d for target %d, lun %d\n",
+	           instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+    /* The SCSI data pointer is *IMPLICITLY* saved on a disconnect
+     * operation, in violation of the SCSI spec so we can safely 
+     * ignore SAVE/RESTORE pointers calls.
+     *
+     * Unfortunately, some disks violate the SCSI spec and 
+     * don't issue the required SAVE_POINTERS message before
+     * disconnecting, and we have to break spec to remain 
+     * compatible. */
+    if (hostdata->aborted) {
+       DEB_ABORT(printk("ATN set for cmnd %d upon reception of SAVE/REST. POINTERS message\n",
+                         cmd->cmnd[0]));
+       AM53C974_write_8(CMDREG, CMDREG_SATN); }
+    AM53C974_write_8(CMDREG, CMDREG_MA);
+    break;
+
+  case EXTENDED_MESSAGE:
+    DEB_MSG(printk("scsi%d: extended message received; cmd %d for target %d, lun %d\n",
+	           instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+   /* Extended messages are sent in the following format :
+    * Byte 	
+    * 0		  EXTENDED_MESSAGE == 1
+    * 1		  length (includes one byte for code, doesn't include first two bytes)
+    * 2 	  code
+    * 3..length+1 arguments
+    */
+   /* BEWARE!! THIS CODE IS EXTREMELY UGLY */
+    extended_msg[0] = EXTENDED_MESSAGE;
+    AM53C974_read_8(INSTREG) ; /* clear int */
+    AM53C974_write_8(CMDREG, CMDREG_MA);  /* ack. msg byte, then wait for SO */  
+    AM53C974_poll_int();
+    /* get length */
+    AM53C974_write_8(CMDREG, CMDREG_IT); 
+    AM53C974_poll_int();
+    AM53C974_write_8(CMDREG, CMDREG_MA);  /* ack. msg byte, then wait for SO */   
+    AM53C974_poll_int();
+    extended_msg[1] = len = AM53C974_read_8(FFREG); /* get length */
+    p = extended_msg+2;
+    /* read the remaining (len) bytes */
+    while (len) {
+      AM53C974_write_8(CMDREG, CMDREG_IT); 
+      AM53C974_poll_int();
+      if (len > 1) {
+         AM53C974_write_8(CMDREG, CMDREG_MA);  /* ack. msg byte, then wait for SO */   
+         AM53C974_poll_int(); }
+      *p = AM53C974_read_8(FFREG);
+      p++; len--; }
+
+#ifdef AM53C974_DEBUG_MSG
+    printk("scsi%d: received extended message: ", instance->host_no);
+    for (j = 0; j < extended_msg[1] + 2; j++) {
+        printk("0x%02x ", extended_msg[j]);
+        if (j && !(j % 16)) printk("\n"); }
+    printk("\n");
+#endif
+
+    /* check message */
+    if (extended_msg[2] == EXTENDED_SDTR)
+       ret = AM53C974_sync_neg(instance, cmd->target, extended_msg);
+    if (ret || hostdata->aborted) AM53C974_write_8(CMDREG, CMDREG_SATN);
+
+    AM53C974_write_8(CMDREG, CMDREG_MA); 
+    break;
+
+  default:
+    printk("scsi%d: unknown message 0x%02x received\n",instance->host_no, msg);
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+    /* reject message */
+    hostdata->msgout[0] = MESSAGE_REJECT; 
+    AM53C974_write_8(CMDREG, CMDREG_SATN);
+    AM53C974_write_8(CMDREG, CMDREG_MA);
+    return(0);
+    break;
+
+  } /* switch (msg) */
+KEYWAIT();
+return(1);
+}
+
+/************************************************************************** 
+* Function : AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+*
+* Purpose : try to establish nexus for the command;
+*           start sync negotiation via start stop and transfer the command in 
+*           cmdout phase in case of an inquiry or req. sense command with no 
+*           sync. neg. performed yet
+*
+* Inputs : instance -- which AM53C974
+*          cmd -- command which requires the selection
+*          tag -- tagged queueing
+* 
+* Returns : nothing
+*        
+* Note: this function initializes the selection process, which is continued 
+*       in the interrupt handler
+**************************************************************************/
+static void AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+unsigned char            cfifo, tmp[3];
+unsigned int             i, len, cmd_size = COMMAND_SIZE(cmd->cmnd[0]);
+AM53C974_setio(instance);
+
+cfifo = AM53C974_cfifo();
+if (cfifo) {
+   printk("scsi%d: select error; %d residual bytes in FIFO\n", instance->host_no, cfifo);
+   AM53C974_write_8(CMDREG, CMDREG_CFIFO); /* clear FIFO */
+   }                  
+
+tmp[0] = IDENTIFY(1, cmd->lun);
+
+#ifdef SCSI2
+if (cmd->device->tagged_queue && (tag != TAG_NONE)) {
+   tmp[1] = SIMPLE_QUEUE_TAG;
+   if (tag == TAG_NEXT) {
+      /* 0 is TAG_NONE, used to imply no tag for this command */
+      if (cmd->device->current_tag == 0) cmd->device->current_tag = 1;
+      cmd->tag = cmd->device->current_tag;
+      cmd->device->current_tag++; }
+     else  
+      cmd->tag = (unsigned char)tag;
+   tmp[2] = cmd->tag;
+   hostdata->last_message[0] = SIMPLE_QUEUE_TAG;
+   len = 3;
+   AM53C974_write_8(FFREG, tmp[0]);
+   AM53C974_write_8(FFREG, tmp[1]);
+   AM53C974_write_8(FFREG, tmp[2]);
+   }
+  else
+#endif /* def SCSI2 */
+   {
+   len = 1;
+   AM53C974_write_8(FFREG, tmp[0]);
+   cmd->tag = 0; }
+
+/* in case of an inquiry or req. sense command with no sync. neg performed yet, we start
+   sync negotiation via start stops and transfer the command in cmdout phase */
+if (((cmd->cmnd[0] == INQUIRY) || (cmd->cmnd[0] == REQUEST_SENSE)) &&
+    !(hostdata->sync_neg[cmd->target]) && hostdata->sync_en[cmd->target]) {
+   hostdata->sync_neg[cmd->target] = 1;
+   hostdata->msgout[0] = EXTENDED_MESSAGE;
+   hostdata->msgout[1] = 3;
+   hostdata->msgout[2] = EXTENDED_SDTR;
+   hostdata->msgout[3] = 250 / (int)hostdata->max_rate[cmd->target];
+   hostdata->msgout[4] = hostdata->max_offset[cmd->target];
+   len += 5; }
+
+AM53C974_write_8(SDIDREG, SDIREG_MASK & cmd->target);       /* setup dest. id  */
+AM53C974_write_8(STIMREG, DEF_SCSI_TIMEOUT);                /* setup timeout reg */
+switch (len) {
+  case 1:
+   for (i = 0; i < cmd_size; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]);
+   AM53C974_write_8(CMDREG, CMDREG_SAS);                    /* select with ATN, 1 msg byte */
+   hostdata->msgout[0] = NOP;
+   break;
+  case 3:
+   for (i = 0; i < cmd_size; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]);
+   AM53C974_write_8(CMDREG, CMDREG_SA3S);                   /* select with ATN, 3 msg bytes */
+   hostdata->msgout[0] = NOP;
+   break;
+  default:
+   AM53C974_write_8(CMDREG, CMDREG_SASS);   /* select with ATN, stop steps; continue in message out phase */
+   break;
+  }
+}
+
+/************************************************************************** 
+* Function : AM53C974_intr_select(struct Scsi_Host *instance, unsigned char statreg)
+*
+* Purpose : handle reselection 
+*
+* Inputs : instance -- which AM53C974
+*          statreg -- status register
+* 
+* Returns : nothing
+*
+* side effects: manipulates hostdata
+**************************************************************************/
+static void AM53C974_intr_reselect(struct Scsi_Host *instance, unsigned char statreg)
+{
+AM53C974_local_declare(); 
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+unsigned char            cfifo, msg[3], lun, t, target = 0;
+#ifdef SCSI2
+ unsigned                char tag;
+#endif
+Scsi_Cmnd                *tmp = NULL, *prev;
+AM53C974_setio(instance);
+
+cfifo = AM53C974_cfifo();
+
+if (hostdata->selecting) {
+   /* caught reselect interrupt in selection process;
+      put selecting command back into the issue queue and continue with the
+      reselecting command */
+   DEB_RESEL(printk("AM53C974_intr_reselect: in selection process\n"));
+   LIST(hostdata->sel_cmd, hostdata->issue_queue);
+   hostdata->sel_cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+   hostdata->issue_queue = hostdata->sel_cmd;
+   hostdata->sel_cmd = NULL;
+   hostdata->selecting = 0; }
+
+/* 2 bytes must be in the FIFO now */
+if (cfifo != 2) {
+   printk("scsi %d: error: %d bytes in fifo, 2 expected\n", instance->host_no, cfifo);
+   hostdata->aborted = 1;
+   goto EXIT_ABORT; }
+
+/* determine target which reselected */
+t = AM53C974_read_8(FFREG);
+if (!(t & (1 << instance->this_id))) {
+   printk("scsi %d: error: invalid host id\n", instance->host_no);
+   hostdata->aborted = 1;
+   goto EXIT_ABORT; }
+t ^= (1 << instance->this_id);
+target = 0; while (t != 1) { t >>= 1; target++; }
+DEB_RESEL(printk("scsi %d: reselect; target: %d\n", instance->host_no, target));
+      
+if (hostdata->aborted) goto EXIT_ABORT;
+
+if ((statreg & STATREG_PHASE) != PHASE_MSGIN) {
+   printk("scsi %d: error: upon reselection interrupt not in MSGIN\n", instance->host_no);
+   hostdata->aborted = 1;
+   goto EXIT_ABORT; }
+
+msg[0] = AM53C974_read_8(FFREG);
+if (!msg[0] & 0x80) {
+   printk("scsi%d: error: expecting IDENTIFY message, got ", instance->host_no);
+   print_msg(msg);
+   hostdata->aborted = 1;
+   goto EXIT_ABORT; }
+
+lun = (msg[0] & 0x07);
+
+/* We need to add code for SCSI-II to track which devices have
+ * I_T_L_Q nexuses established, and which have simple I_T_L
+ * nexuses so we can chose to do additional data transfer. */
+#ifdef SCSI2
+#error "SCSI-II tagged queueing is not supported yet"
+#endif
+
+/* Find the command corresponding to the I_T_L or I_T_L_Q  nexus we 
+ * just reestablished, and remove it from the disconnected queue. */
+for (tmp = (Scsi_Cmnd *)hostdata->disconnected_queue, prev = NULL; 
+     tmp; prev = tmp, tmp = (Scsi_Cmnd *)tmp->host_scribble) 
+    if ((target == tmp->target) && (lun == tmp->lun) 
+#ifdef SCSI2
+         && (tag == tmp->tag) 
+#endif
+       ) {
+       if (prev) {
+	  REMOVE(prev, (Scsi_Cmnd *)(prev->host_scribble), tmp,
+                 (Scsi_Cmnd *)(tmp->host_scribble));
+	  prev->host_scribble = tmp->host_scribble; } 
+         else {
+	  REMOVE(-1, hostdata->disconnected_queue, tmp, tmp->host_scribble);
+	  hostdata->disconnected_queue = (Scsi_Cmnd *)tmp->host_scribble; }
+       tmp->host_scribble = NULL;
+       hostdata->connected = tmp;
+       break; }
+
+if (!tmp) {
+#ifdef SCSI2
+   printk("scsi%d: warning : target %d lun %d tag %d not in disconnect_queue.\n",
+         instance->host_no, target, lun, tag);
+#else
+   printk("scsi%d: warning : target %d lun %d not in disconnect_queue.\n",
+         instance->host_no, target, lun);
+#endif
+   /* Since we have an established nexus that we can't do anything with, we must abort it. */
+   hostdata->aborted = 1;
+   DEB(AM53C974_keywait());
+   goto EXIT_ABORT; }
+  else
+   goto EXIT_OK;
+
+EXIT_ABORT: 
+AM53C974_write_8(CMDREG, CMDREG_SATN);
+AM53C974_write_8(CMDREG, CMDREG_MA);
+return;
+
+EXIT_OK:
+DEB_RESEL(printk("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
+	  instance->host_no, target, tmp->lun, tmp->tag));
+AM53C974_set_sync(instance, target);
+AM53C974_write_8(SDIDREG, SDIREG_MASK & target);       /* setup dest. id  */
+AM53C974_write_8(CMDREG, CMDREG_MA);
+hostdata->dma_busy = 0;
+hostdata->connected->SCp.phase = PHASE_CMDOUT;
+}
+
+/************************************************************************** 
+* Function : AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
+*                                  unsigned long length, char *data)
+*
+* Purpose : setup DMA transfer
+*
+* Inputs : instance -- which AM53C974
+*          dir -- direction flag, 0: write to device, read from memory; 
+*                                 1: read from device, write to memory
+*          length -- number of bytes to transfer to from buffer
+*          data -- pointer to data buffer
+* 
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
+                                             unsigned long length, char *data)
+{
+AM53C974_local_declare();
+AM53C974_setio(instance);
+
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D);  /* idle command */
+AM53C974_write_8(STCLREG, (unsigned char)(length & 0xff));
+AM53C974_write_8(STCMREG, (unsigned char)((length & 0xff00) >> 8));
+AM53C974_write_8(STCHREG, (unsigned char)((length & 0xff0000) >> 16));
+AM53C974_write_32(DMASTC, length & 0xffffff);
+AM53C974_write_32(DMASPA, (unsigned long)data);
+AM53C974_write_8(CMDREG, CMDREG_IT | CMDREG_DMA);
+AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D | DMACMD_START);
+}
+
+/************************************************************************** 
+* Function : AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
+*                               unsigned char statreg)
+*
+* Purpose : cleanup DMA transfer
+*
+* Inputs : instance -- which AM53C974
+*          dmastatus -- dma status register
+*          statreg -- status register
+* 
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
+                               unsigned char statreg)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+unsigned long            ctcreg;
+int                      dir = statreg & STATREG_IO;
+int                      cfifo, pio, i = 0;
+AM53C974_setio(instance);
+
+do {
+   cfifo = AM53C974_cfifo();
+   i++;
+   } while (cfifo && (i < 50000));
+pio = (i == 50000) ? 1: 0;
+
+if (statreg & STATREG_CTZ) { AM53C974_write_8(DMACMD, DMACMD_IDLE); return; }
+
+if (dmastatus & DMASTATUS_DONE) { AM53C974_write_8(DMACMD, DMACMD_IDLE); return; }
+
+AM53C974_write_8(DMACMD, ((dir << 7) & DMACMD_DIR) | DMACMD_BLAST);
+while(!(AM53C974_read_8(DMASTATUS) & DMASTATUS_BCMPLT)) ;
+AM53C974_write_8(DMACMD, DMACMD_IDLE);
+
+if (pio) {
+   /* transfer residual bytes via PIO */
+   unsigned char *wac = (unsigned char *)AM53C974_read_32(DMAWAC);
+   printk("pio mode, residual=%d\n", AM53C974_read_8(CFIREG) & CFIREG_CF);
+   while (AM53C974_read_8(CFIREG) & CFIREG_CF) *(wac++) = AM53C974_read_8(FFREG);
+   }
+
+ctcreg = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+         (AM53C974_read_8(CTCHREG) << 16);
+
+hostdata->connected->SCp.ptr += hostdata->connected->SCp.this_residual - ctcreg;
+hostdata->connected->SCp.this_residual = ctcreg;
+}
+
+/************************************************************************** 
+* Function : AM53C974_intr_bus_reset(struct Scsi_Host *instance)
+*
+* Purpose : handle bus reset interrupt
+*
+* Inputs : instance -- which AM53C974
+* 
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_intr_bus_reset(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+unsigned char cntlreg1;
+AM53C974_setio(instance);
+
+AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+
+cntlreg1 = AM53C974_read_8(CNTLREG1);
+AM53C974_write_8(CNTLREG1, cntlreg1 | CNTLREG1_DISR);
+}
+
+/**************************************************************************
+* Function : int AM53C974_abort(Scsi_Cmnd *cmd)
+*
+* Purpose : abort a command
+*
+* Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the 
+* 	host byte of the result field to, if zero DID_ABORTED is 
+*	used.
+*
+* Returns : 0 - success, -1 on failure.
+ **************************************************************************/
+int AM53C974_abort(Scsi_Cmnd *cmd)
+{
+AM53C974_local_declare();
+struct Scsi_Host         *instance = cmd->host;
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd                *tmp, **prev;
+
+#ifdef AM53C974_DEBUG
+   deb_stop = 1;
+#endif
+cli();
+AM53C974_setio(instance);
+
+DEB_ABORT(printk(SEPARATOR_LINE));
+DEB_ABORT(printk("scsi%d : AM53C974_abort called -- trouble starts!!\n", instance->host_no));
+DEB_ABORT(AM53C974_print(instance));
+DEB_ABORT(AM53C974_keywait());
+
+/* Case 1 : If the command is the currently executing command, 
+            we'll set the aborted flag and return control so that the
+            information transfer routine can exit cleanly. */
+if ((hostdata->connected == cmd) || (hostdata->sel_cmd == cmd)) {
+   DEB_ABORT(printk("scsi%d: aborting connected command\n", instance->host_no));
+   hostdata->aborted = 1;
+   hostdata->msgout[0] = ABORT;
+   sti();
+   return(SCSI_ABORT_PENDING); }
+
+/* Case 2 : If the command hasn't been issued yet,
+            we simply remove it from the issue queue. */
+for (prev = (Scsi_Cmnd **)&(hostdata->issue_queue), 
+     tmp = (Scsi_Cmnd *)hostdata->issue_queue; tmp; 
+     prev = (Scsi_Cmnd **)&(tmp->host_scribble),
+     tmp = (Scsi_Cmnd *)tmp->host_scribble) {
+    if (cmd == tmp) {
+       DEB_ABORT(printk("scsi%d : abort removed command from issue queue.\n", instance->host_no));
+       REMOVE(5, *prev, tmp, tmp->host_scribble);
+       (*prev) = (Scsi_Cmnd *)tmp->host_scribble;
+       tmp->host_scribble = NULL;
+       tmp->result = DID_ABORT << 16;
+       sti();
+       tmp->done(tmp);
+       return(SCSI_ABORT_SUCCESS); }
+#ifdef AM53C974_DEBUG_ABORT
+      else {
+       if (prev == (Scsi_Cmnd **)tmp) 
+          printk("scsi%d : LOOP\n", instance->host_no); 
+       }
+#endif
+    }
+ 
+/* Case 3 : If any commands are connected, we're going to fail the abort
+ *	    and let the high level SCSI driver retry at a later time or 
+ *	    issue a reset.
+ *
+ *	    Timeouts, and therefore aborted commands, will be highly unlikely
+ *          and handling them cleanly in this situation would make the common
+ *	    case of noresets less efficient, and would pollute our code.  So,
+ *	    we fail. */
+if (hostdata->connected || hostdata->sel_cmd) {
+   DEB_ABORT(printk("scsi%d : abort failed, other command connected.\n", instance->host_no));
+   sti();
+   return(SCSI_ABORT_NOT_RUNNING); }
+
+/* Case 4: If the command is currently disconnected from the bus, and 
+ * 	   there are no connected commands, we reconnect the I_T_L or 
+ *	   I_T_L_Q nexus associated with it, go into message out, and send 
+ *         an abort message. */
+for (tmp = (Scsi_Cmnd *)hostdata->disconnected_queue; tmp; 
+     tmp = (Scsi_Cmnd *)tmp->host_scribble) {
+     if (cmd == tmp) {
+        DEB_ABORT(printk("scsi%d: aborting disconnected command\n", instance->host_no));
+        hostdata->aborted = 1;
+        hostdata->msgout[0] = ABORT;
+        hostdata->selecting = 1;
+        hostdata->sel_cmd = tmp;
+        AM53C974_write_8(CMDREG, CMDREG_DSR);
+        sti();
+        return(SCSI_ABORT_PENDING); }
+     }
+
+/* Case 5 : If we reached this point, the command was not found in any of 
+ *	    the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke. */
+DEB_ABORT(printk("scsi%d : abort failed, command not found.\n", instance->host_no));
+sti();
+return(SCSI_ABORT_NOT_RUNNING);
+}
+
+/************************************************************************** 
+* Function : int AM53C974_reset(Scsi_Cmnd *cmd)
+*
+* Purpose : reset the SCSI controller and bus
+*
+* Inputs : cmd -- which command within the command block was responsible for the reset
+* 
+* Returns : status (SCSI_ABORT_SUCCESS)
+**************************************************************************/
+int AM53C974_reset(Scsi_Cmnd *cmd)
+{
+AM53C974_local_declare();
+int                      i;
+struct Scsi_Host         *instance = cmd->host;
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+cli();
+DEB(printk("AM53C974_reset called; "));
+
+printk("AM53C974_reset called\n");
+AM53C974_print(instance);
+AM53C974_keywait();
+
+/* do hard reset */
+AM53C974_write_8(CMDREG, CMDREG_RDEV);
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+hostdata->msgout[0] = NOP;
+for (i = 0; i < 8; i++) {
+    hostdata->busy[i] = 0;
+    hostdata->sync_per[i] = DEF_STP;
+    hostdata->sync_off[i] = 0;
+    hostdata->sync_neg[i] = 0; }
+hostdata->last_message[0] = NOP;
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->issue_queue = NULL;
+hostdata->disconnected_queue = NULL;
+hostdata->in_reset = 0;
+hostdata->aborted = 0;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+
+/* reset bus */
+AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); /* disable interrupt upon SCSI RESET */
+AM53C974_write_8(CMDREG, CMDREG_RBUS);     /* reset SCSI bus */
+udelay(40);
+AM53C974_config_after_reset(instance);
+
+sti();
+cmd->result = DID_RESET << 16;
+cmd->scsi_done(cmd);
+return SCSI_ABORT_SUCCESS;
+}
diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/AM53C974.h linux/drivers/scsi/AM53C974.h
--- v1.3.58/linux/drivers/scsi/AM53C974.h	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/AM53C974.h	Tue Jan 23 21:34:30 1996
@@ -0,0 +1,413 @@
+/* AM53/79C974 (PCscsi) driver release 0.5
+ *
+ * The architecture and much of the code of this device
+ * driver was originally developed by Drew Eckhardt for
+ * the NCR5380. The following copyrights apply:
+ *  For the architecture and all parts similar to the NCR5380:
+ *    Copyright 1993, Drew Eckhardt
+ *	Visionary Computing 
+ *	(Unix and Linux consulting and custom programming)
+ * 	drew@colorado.edu
+ *	+1 (303) 666-5836
+ *
+ *  The AM53C974_nobios_detect code was origininally developed by
+ *   Robin Cutshaw (robin@xfree86.org) and is used here in a 
+ *   modified form.
+ *
+ *  For the other parts:
+ *    Copyright 1994, D. Frieauff
+ *    EMail: fri@rsx42sun0.dofn.de
+ *    Phone: x49-7545-8-2256 , x49-7541-42305
+ */
+
+/*
+ * $Log: AM53C974.h,v $
+ */
+
+#ifndef AM53C974_H
+#define AM53C974_H
+
+#include <linux/scsicam.h>
+
+/***************************************************************************************
+* Default setting of the controller's SCSI id. Edit and uncomment this only if your    *
+* BIOS does not correctly initialize the controller's SCSI id.                         *
+* If you don't get a warning during boot, it is correctly initialized.                 *
+****************************************************************************************/
+/* #define AM53C974_SCSI_ID 7 */
+
+/***************************************************************************************
+* Default settings for sync. negotiation enable, transfer rate and sync. offset.       *
+* These settings can be replaced by LILO overrides (append) with the following syntax:          *
+* AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset                          *
+* Sync. negotiation is disabled by default and will be enabled for those targets which *
+* are specified in the LILO override                                                   *
+****************************************************************************************/
+#define DEFAULT_SYNC_NEGOTIATION_ENABLED 0 /* 0 or 1 */
+#define DEFAULT_RATE			 5 /* MHz, min: 3; max: 10 */
+#define DEFAULT_SYNC_OFFSET		 0 /* bytes, min: 0; max: 15; use 0 for async. mode */
+
+
+/* --------------------- don't edit below here  --------------------- */
+
+#define AM53C974_DRIVER_REVISION_MAJOR 0
+#define AM53C974_DRIVER_REVISION_MINOR 5
+#define SEPARATOR_LINE  \
+"--------------------------------------------------------------------------\n"
+
+/* debug control */
+/* #define AM53C974_DEBUG */
+/* #define AM53C974_DEBUG_MSG */
+/* #define AM53C974_DEBUG_KEYWAIT */
+/* #define AM53C974_DEBUG_INIT */
+/* #define AM53C974_DEBUG_QUEUE */
+/* #define AM53C974_DEBUG_INFO */
+/* #define AM53C974_DEBUG_LINKED */
+/* #define VERBOSE_AM53C974_DEBUG */
+/* #define AM53C974_DEBUG_INTR */
+/* #define AM53C974_DEB_RESEL */
+#define AM53C974_DEBUG_ABORT
+/* #define AM53C974_OPTION_DEBUG_PROBE_ONLY */
+
+/* special options/constants */
+#define DEF_CLK                 40   /* chip clock freq. in MHz */
+#define MIN_PERIOD               4   /* for negotiation: min. number of clocks per cycle */
+#define MAX_PERIOD              13   /* for negotiation: max. number of clocks per cycle */
+#define MAX_OFFSET              15   /* for negotiation: max. offset (0=async) */
+
+#define DEF_SCSI_TIMEOUT        245  /* STIMREG value, 40 Mhz */
+#define DEF_STP                 8    /* STPREG value assuming 5.0 MB/sec, FASTCLK, FASTSCSI */
+#define DEF_SOF_RAD             0    /* REQ/ACK deassertion delay */
+#define DEF_SOF_RAA             0    /* REQ/ACK assertion delay */
+#define DEF_ETM                 0    /* CNTLREG1, ext. timing mode */
+#define DEF_PERE                1    /* CNTLREG1, parity error reporting */
+#define DEF_CLKF                0    /* CLKFREG,  0=40 Mhz */
+#define DEF_ENF                 1    /* CNTLREG2, enable features */
+#define DEF_ADIDCHK             0    /* CNTLREG3, additional ID check */
+#define DEF_FASTSCSI            1    /* CNTLREG3, fast SCSI */
+#define DEF_FASTCLK             1    /* CNTLREG3, fast clocking, 5 MB/sec at 40MHz chip clk */
+#define DEF_GLITCH              1    /* CNTLREG4, glitch eater, 0=12ns, 1=35ns, 2=25ns, 3=off */
+#define DEF_PWD                 0    /* CNTLREG4, reduced power feature */
+#define DEF_RAE                 0    /* CNTLREG4, RAE active negation on REQ, ACK only */
+#define DEF_RADE                1    /* 1CNTLREG4, active negation on REQ, ACK and data */
+
+/*** PCI block ***/
+/* standard registers are defined in <linux/pci.h> */
+#ifndef PCI_VENDOR_ID_AMD
+#define PCI_VENDOR_ID_AMD	0x1022
+#define PCI_DEVICE_ID_AMD_SCSI  0x2020
+#endif
+#define PCI_BASE_MASK           0xFFFFFFE0
+#define PCI_COMMAND_PERREN      0x40
+#define PCI_SCRATCH_REG_0	0x40	/* 16 bits */
+#define PCI_SCRATCH_REG_1	0x42	/* 16 bits */
+#define PCI_SCRATCH_REG_2	0x44	/* 16 bits */
+#define PCI_SCRATCH_REG_3	0x46	/* 16 bits */
+#define PCI_SCRATCH_REG_4	0x48	/* 16 bits */
+#define PCI_SCRATCH_REG_5	0x4A	/* 16 bits */
+#define PCI_SCRATCH_REG_6	0x4C	/* 16 bits */
+#define PCI_SCRATCH_REG_7	0x4E	/* 16 bits */
+
+/*** SCSI block ***/
+#define CTCLREG		    	0x00	/* r	current transf. count, low byte    */
+#define CTCMREG		   	0x04	/* r 	current transf. count, middle byte */
+#define CTCHREG		    	0x38	/* r	current transf. count, high byte   */
+#define STCLREG		    	0x00	/* w	start transf. count, low byte      */
+#define STCMREG		    	0x04	/* w	start transf. count, middle byte   */
+#define STCHREG		    	0x38	/* w 	start transf. count, high byte     */
+#define FFREG		    	0x08	/* rw	SCSI FIFO reg.			   */
+#define STIMREG		    	0x14	/* w	SCSI timeout reg.		   */
+
+#define SDIDREG		    	0x10	/* w	SCSI destination ID reg.	   */
+#define SDIREG_MASK		0x07	/* mask					   */
+
+#define STPREG		    	0x18	/* w	synchronous transf. period reg.	   */
+#define STPREG_STP		0x1F	/* synchr. transfer period		   */
+
+#define CLKFREG		    	0x24	/* w	clock factor reg.		   */
+#define CLKFREG_MASK		0x07	/* mask					   */
+
+#define CMDREG		    	0x0C	/* rw	SCSI command reg.		   */
+#define CMDREG_DMA         	0x80    /* set DMA mode (set together with opcodes below) */
+#define CMDREG_IT          	0x10    /* information transfer 		   */
+#define CMDREG_ICCS		0x11	/* initiator command complete steps 	   */
+#define CMDREG_MA		0x12	/* message accepted 			   */
+#define CMDREG_TPB		0x98	/* transfer pad bytes, DMA mode only 	   */
+#define CMDREG_SATN		0x1A	/* set ATN 				   */
+#define CMDREG_RATN		0x1B	/* reset ATN 				   */
+#define CMDREG_SOAS		0x41	/* select without ATN steps 		   */
+#define CMDREG_SAS		0x42	/* select with ATN steps (1 msg byte)	   */
+#define CMDREG_SASS		0x43	/* select with ATN and stop steps 	   */
+#define CMDREG_ESR		0x44	/* enable selection/reselection 	   */
+#define CMDREG_DSR		0x45	/* disable selection/reselection 	   */
+#define CMDREG_SA3S		0x46	/* select with ATN 3 steps  (3 msg bytes)  */
+#define CMDREG_NOP		0x00	/* no operation 			   */
+#define CMDREG_CFIFO		0x01	/* clear FIFO 				   */
+#define CMDREG_RDEV		0x02	/* reset device 			   */
+#define CMDREG_RBUS		0x03	/* reset SCSI bus 			   */
+
+#define STATREG		    	0x10	/* r 	SCSI status reg.		   */
+#define STATREG_INT		0x80	/* SCSI interrupt condition detected	   */
+#define STATREG_IOE		0x40	/* SCSI illegal operation error detected   */
+#define STATREG_PE		0x20	/* SCSI parity error detected		   */
+#define STATREG_CTZ		0x10	/* CTC reg decremented to zero		   */
+#define STATREG_MSG		0x04	/* SCSI MSG phase (latched?)		   */
+#define STATREG_CD		0x02	/* SCSI C/D phase (latched?)		   */
+#define STATREG_IO		0x01	/* SCSI I/O phase (latched?)		   */
+#define STATREG_PHASE           0x07    /* SCSI phase mask 			   */
+
+#define INSTREG		    	0x14	/* r	interrupt status reg.		   */
+#define INSTREG_SRST		0x80	/* SCSI reset detected			   */
+#define INSTREG_ICMD		0x40	/* SCSI invalid command detected	   */
+#define INSTREG_DIS		0x20	/* target disconnected or sel/resel timeout*/
+#define INSTREG_SR		0x10	/* device on bus has service request       */
+#define INSTREG_SO		0x08	/* successful operation			   */
+#define INSTREG_RESEL		0x04	/* device reselected as initiator	   */
+
+#define ISREG		    	0x18	/* r	internal state reg.		   */
+#define ISREG_SOF		0x08	/* synchronous offset flag (act. low)	   */
+#define ISREG_IS		0x07	/* status of intermediate op.		   */
+#define ISREG_OK_NO_STOP        0x04    /* selection successful                    */
+#define ISREG_OK_STOP           0x01    /* selection successful                    */
+
+#define CFIREG		    	0x1C	/* r	current FIFO/internal state reg.   */
+#define CFIREG_IS		0xE0	/* status of intermediate op.		   */
+#define CFIREG_CF		0x1F	/* number of bytes in SCSI FIFO		   */
+
+#define SOFREG		    	0x1C	/* w	synchr. offset reg.		   */
+#define SOFREG_RAD		0xC0	/* REQ/ACK deassertion delay (sync.)	   */
+#define SOFREG_RAA		0x30	/* REQ/ACK assertion delay (sync.)	   */
+#define SOFREG_SO		0x0F	/* synch. offset (sync.)		   */
+
+#define CNTLREG1	    	0x20	/* rw	control register one		   */
+#define CNTLREG1_ETM		0x80	/* set extended timing mode		   */
+#define CNTLREG1_DISR		0x40	/* disable interrupt on SCSI reset	   */
+#define CNTLREG1_PERE		0x10	/* enable parity error reporting	   */
+#define CNTLREG1_SID		0x07	/* host adapter SCSI ID			   */
+
+#define CNTLREG2	    	0x2C	/* rw	control register two		   */
+#define CNTLREG2_ENF		0x40	/* enable features			   */
+
+#define CNTLREG3	    	0x30	/* rw	control register three		   */ 
+#define CNTLREG3_ADIDCHK	0x80	/* additional ID check			   */
+#define CNTLREG3_FASTSCSI	0x10	/* fast SCSI				   */
+#define CNTLREG3_FASTCLK	0x08	/* fast SCSI clocking			   */
+
+#define CNTLREG4	    	0x34	/* rw	control register four		   */ 
+#define CNTLREG4_GLITCH		0xC0	/* glitch eater				   */
+#define CNTLREG4_PWD		0x20	/* reduced power feature		   */
+#define CNTLREG4_RAE		0x08	/* write only, active negot. ctrl.	   */
+#define CNTLREG4_RADE		0x04	/* active negot. ctrl.			   */
+#define CNTLREG4_RES		0x10	/* reserved bit, must be 1		   */
+
+/*** DMA block ***/
+#define DMACMD		    	0x40	/* rw	command				   */
+#define DMACMD_DIR		0x80	/* transfer direction (1=read from device) */
+#define DMACMD_INTE_D		0x40	/* DMA transfer interrupt enable 	   */
+#define DMACMD_INTE_P		0x20	/* page transfer interrupt enable 	   */
+#define DMACMD_MDL		0x10	/* map to memory descriptor list 	   */
+#define DMACMD_DIAG		0x04	/* diagnostics, set to 0		   */
+#define DMACMD_IDLE 		0x00	/* idle cmd			 	   */
+#define DMACMD_BLAST		0x01	/* flush FIFO to memory		 	   */
+#define DMACMD_ABORT		0x02	/* terminate DMA		 	   */
+#define DMACMD_START		0x03	/* start DMA			 	   */
+
+#define DMASTATUS	      	0x54	/* r	status register			   */
+#define DMASTATUS_BCMPLT	0x20	/* BLAST complete			   */
+#define DMASTATUS_SCSIINT	0x10	/* SCSI interrupt pending		   */
+#define DMASTATUS_DONE		0x08	/* DMA transfer terminated		   */
+#define DMASTATUS_ABORT		0x04	/* DMA transfer aborted			   */
+#define DMASTATUS_ERROR		0x02	/* DMA transfer error			   */
+#define DMASTATUS_PWDN		0x02	/* power down indicator			   */
+
+#define DMASTC		    	0x44	/* rw	starting transfer count		   */
+#define DMASPA		    	0x48	/* rw	starting physical address	   */
+#define DMAWBC		    	0x4C	/* r	working byte counter		   */
+#define DMAWAC		    	0x50	/* r	working address counter		   */
+#define DMASMDLA	    	0x58	/* rw	starting MDL address		   */
+#define DMAWMAC		    	0x5C	/* r	working MDL counter		   */
+
+/*** SCSI phases ***/
+#define PHASE_MSGIN             0x07
+#define PHASE_MSGOUT            0x06
+#define PHASE_RES_1             0x05
+#define PHASE_RES_0             0x04
+#define PHASE_STATIN            0x03
+#define PHASE_CMDOUT            0x02
+#define PHASE_DATAIN            0x01
+#define PHASE_DATAOUT           0x00
+
+struct AM53C974_hostdata {
+    volatile unsigned       in_reset:1;          /* flag, says bus reset pending */
+    volatile unsigned       aborted:1;           /* flag, says aborted */
+    volatile unsigned       selecting:1;         /* selection started, but not yet finished */
+    volatile unsigned       disconnecting: 1;    /* disconnection started, but not yet finished */
+    volatile unsigned       dma_busy:1;          /* dma busy when service request for info transfer received */
+    volatile unsigned  char msgout[10];          /* message to output in MSGOUT_PHASE */
+    volatile unsigned  char last_message[10];	/* last message OUT */
+    volatile Scsi_Cmnd      *issue_queue;	/* waiting to be issued */
+    volatile Scsi_Cmnd      *disconnected_queue;	/* waiting for reconnect */
+    volatile Scsi_Cmnd      *sel_cmd;            /* command for selection */
+    volatile Scsi_Cmnd      *connected;		/* currently connected command */
+    volatile unsigned  char busy[8];		/* index = target, bit = lun */
+    unsigned  char sync_per[8];         /* synchronous transfer period (in effect) */
+    unsigned  char sync_off[8];         /* synchronous offset (in effect) */
+    unsigned  char sync_neg[8];         /* sync. negotiation performed (in effect) */
+    unsigned  char sync_en[8];          /* sync. negotiation performed (in effect) */
+    unsigned  char max_rate[8];         /* max. transfer rate (setup) */
+    unsigned  char max_offset[8];       /* max. sync. offset (setup), only valid if corresponding sync_en is nonzero */
+    };
+
+#define AM53C974 { \
+    NULL,              		/* pointer to next in list                      */  \
+    NULL,			/* long * usage_count				*/  \
+    NULL,                       /* struct proc_dir_entry *proc_dir              */ \
+    NULL,                       /* int (*proc_info)(char *, char **, off_t, int, int, int); */ \
+    "AM53C974",        		/* name                                         */  \
+    AM53C974_detect,   		/* int (* detect)(struct SHT *)                 */  \
+    NULL,              		/* int (*release)(struct Scsi_Host *)           */  \
+    AM53C974_info,     		/* const char *(* info)(struct Scsi_Host *)     */  \
+    AM53C974_command,  		/* int (* command)(Scsi_Cmnd *)                 */  \
+    AM53C974_queue_command,	/* int (* queuecommand)(Scsi_Cmnd *,                \
+                                           void (*done)(Scsi_Cmnd *))           */  \
+    AM53C974_abort,    		/* int (* abort)(Scsi_Cmnd *)                   */  \
+    AM53C974_reset,    		/* int (* reset)(Scsi_Cmnd *)                   */  \
+    NULL,                 	/* int (* slave_attach)(int, int)               */  \
+    scsicam_bios_param,		/* int (* bios_param)(Disk *, int, int[])       */  \
+    12,                 	/* can_queue                                    */  \
+    -1,                         /* this_id                                      */  \
+    SG_ALL,            		/* sg_tablesize                                 */  \
+    1,                 		/* cmd_per_lun                                  */  \
+    0,                 		/* present, i.e. how many adapters of this kind */  \
+    0,                 		/* unchecked_isa_dma                            */  \
+    DISABLE_CLUSTERING 		/* use_clustering                               */  \
+    }
+
+void AM53C974_setup(char *str, int *ints);
+int AM53C974_detect(Scsi_Host_Template *tpnt);
+int AM53C974_biosparm(Disk *disk, int dev, int *info_array);
+const char *AM53C974_info(struct Scsi_Host *);
+int AM53C974_command(Scsi_Cmnd *SCpnt);
+int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+int AM53C974_abort(Scsi_Cmnd *cmd);
+int AM53C974_reset (Scsi_Cmnd *cmd);
+
+#define AM53C974_local_declare()	unsigned long io_port
+#define AM53C974_setio(instance)	io_port = instance->io_port
+#define AM53C974_read_8(addr)           inb(io_port + (addr))
+#define AM53C974_write_8(addr,x)        outb((x), io_port + (addr))
+#define AM53C974_read_16(addr)          inw(io_port + (addr))
+#define AM53C974_write_16(addr,x)       outw((x), io_port + (addr))
+#define AM53C974_read_32(addr)          inl(io_port + (addr))
+#define AM53C974_write_32(addr,x)       outl((x), io_port + (addr))
+
+#define AM53C974_poll_int()             { do { statreg = AM53C974_read_8(STATREG); } \
+                                             while (!(statreg & STATREG_INT)) ; \
+                                          AM53C974_read_8(INSTREG) ; } /* clear int */
+#define AM53C974_cfifo()		(AM53C974_read_8(CFIREG) & CFIREG_CF)
+
+/* These are "special" values for the tag parameter passed to AM53C974_select. */
+#define TAG_NEXT	-1 	/* Use next free tag */
+#define TAG_NONE	-2	/* Establish I_T_L nexus instead of I_T_L_Q
+				 * even on SCSI-II devices */
+
+/************ LILO overrides *************/
+typedef struct _override_t {
+    int host_scsi_id;			/* SCSI id of the bus controller */
+    int target_scsi_id;                 /* SCSI id of target */
+    int max_rate;			/* max. transfer rate */
+    int max_offset;	                /* max. sync. offset, 0 = asynchronous */
+    } override_t;
+
+/************ PCI stuff *************/
+#define AM53C974_PCIREG_OPEN()                    outb(0xF1, 0xCF8); outb(0, 0xCFA)
+#define AM53C974_PCIREG_CLOSE()                   outb(0, 0xCF8)
+#define AM53C974_PCIREG_READ_BYTE(instance,a)     ( inb((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_READ_WORD(instance,a)     ( inw((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_READ_DWORD(instance,a)    ( inl((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_BYTE(instance,x,a)  ( outb((x), (a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_WORD(instance,x,a)  ( outw((x), (a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_DWORD(instance,x,a) ( outl((x), (a) + (instance)->io_port) )
+
+typedef struct _pci_config_t {
+    /* start of official PCI config space header */
+    union {
+        unsigned int device_vendor;
+	struct {
+	  unsigned short vendor;
+	  unsigned short device;
+ 	  } dv;
+        } dv_id;
+#define _device_vendor dv_id.device_vendor
+#define _vendor dv_id.dv.vendor
+#define _device dv_id.dv.device
+    union {
+        unsigned int status_command;
+	struct {
+	  unsigned short command;
+	  unsigned short status;
+	  } sc;
+        } stat_cmd;
+#define _status_command stat_cmd.status_command
+#define _command stat_cmd.sc.command
+#define _status  stat_cmd.sc.status
+    union {
+        unsigned int class_revision;
+	struct {
+	    unsigned char rev_id;
+	    unsigned char prog_if;
+	    unsigned char sub_class;
+	    unsigned char base_class;
+	} cr;
+    } class_rev;
+#define _class_revision class_rev.class_revision
+#define _rev_id     class_rev.cr.rev_id
+#define _prog_if    class_rev.cr.prog_if
+#define _sub_class  class_rev.cr.sub_class
+#define _base_class class_rev.cr.base_class
+    union {
+        unsigned int bist_header_latency_cache;
+	struct {
+	    unsigned char cache_line_size;
+	    unsigned char latency_timer;
+	    unsigned char header_type;
+	    unsigned char bist;
+	} bhlc;
+    } bhlc;
+#define _bist_header_latency_cache bhlc.bist_header_latency_cache
+#define _cache_line_size bhlc.bhlc.cache_line_size
+#define _latency_timer   bhlc.bhlc.latency_timer
+#define _header_type     bhlc.bhlc.header_type
+#define _bist            bhlc.bhlc.bist
+    unsigned int _base0;
+    unsigned int _base1;
+    unsigned int _base2;
+    unsigned int _base3;
+    unsigned int _base4;
+    unsigned int _base5;
+    unsigned int rsvd1;
+    unsigned int rsvd2;
+    unsigned int _baserom;
+    unsigned int rsvd3;
+    unsigned int rsvd4;
+    union {
+        unsigned int max_min_ipin_iline;
+	struct {
+	    unsigned char int_line;
+	    unsigned char int_pin;
+	    unsigned char min_gnt;
+	    unsigned char max_lat;
+	} mmii;
+    } mmii;
+#define _max_min_ipin_iline mmii.max_min_ipin_iline
+#define _int_line mmii.mmii.int_line
+#define _int_pin  mmii.mmii.int_pin
+#define _min_gnt  mmii.mmii.min_gnt
+#define _max_lat  mmii.mmii.max_lat
+    /* end of official PCI config space header */
+    unsigned short _ioaddr; /* config type 1 - private I/O addr    */
+    unsigned int _pcibus;  /* config type 2 - private bus id      */
+    unsigned int _cardnum; /* config type 2 - private card number */
+} pci_config_t;
+
+#endif /* AM53C974_H */
diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v1.3.58/linux/drivers/scsi/Config.in	Tue Jan 23 21:15:42 1996
+++ linux/drivers/scsi/Config.in	Tue Jan 23 21:35:41 1996
@@ -37,5 +37,6 @@
 dep_tristate '7000FASST SCSI support' CONFIG_SCSI_7000FASST $CONFIG_SCSI
 dep_tristate 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA $CONFIG_SCSI
 dep_tristate 'NCR53c406a SCSI support' CONFIG_SCSI_NCR53C406A $CONFIG_SCSI
+bool 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974
 #dep_tristate 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG $CONFIG_SCSI
 endmenu
diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v1.3.58/linux/drivers/scsi/Makefile	Tue Jan 23 21:15:42 1996
+++ linux/drivers/scsi/Makefile	Tue Jan 23 21:34:30 1996
@@ -76,9 +76,9 @@
 ifeq ($(CONFIG_SCSI_ADVANSYS),y)
 L_OBJS += advansys.o
 else
-	ifeq ($(CONFIG_SCSI_ADVANSYS),m)
-	M_OBJS += advansys.o
-	endif
+  ifeq ($(CONFIG_SCSI_ADVANSYS),m)
+  M_OBJS += advansys.o
+  endif
 endif
 
 ifeq ($(CONFIG_SCSI_QLOGIC),y)
@@ -119,6 +119,10 @@
   ifeq ($(CONFIG_SCSI_AIC7XXX),m)
   M_OBJS += aic7xxx.o
   endif
+endif
+
+ifeq ($(CONFIG_SCSI_AM53C974),y)
+L_OBJS += AM53C974.o
 endif
 
 ifeq ($(CONFIG_SCSI_BUSLOGIC),y)
diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/README.AM53C974 linux/drivers/scsi/README.AM53C974
--- v1.3.58/linux/drivers/scsi/README.AM53C974	Thu Jan  1 02:00:00 1970
+++ linux/drivers/scsi/README.AM53C974	Tue Jan 23 21:34:30 1996
@@ -0,0 +1,232 @@
+SUBJECT
+-------
+AM53/79C974 PC-SCSI Driver
+
+
+DISCLAIMER
+----------
+***  THIS SHOULD BE CONSIDERED AS BETA SOFTWARE  ***
+***  USE AT YOUR OWN RISK!                       ***
+
+
+Copyright
+---------
+The architecture and much of the code of this device driver was 
+originally developed by Drew Eckhardt for the NCR5380. The 
+following copyrights apply:
+
+For the architecture and all pieces of code which can also be found 
+in the NCR5380 device driver:
+Copyright 1993, Drew Eckhardt
+  Visionary Computing 
+  (Unix and Linux consulting and custom programming)
+  drew@colorado.edu
+  +1 (303) 666-5836
+ 
+The AM53C974_nobios_detect code was origininally developed by
+Robin Cutshaw (robin@xfree86.org) and is used here in a 
+slightly modified form.
+ 
+For the remaining code:
+  Copyright 1994, D. Frieauff
+  EMail: fri@rsx42sun0.dofn.de
+  Phone: x49-7545-8-2256 , x49-7541-42305
+
+
+Version
+-------
+AM53/79C974 (PC-SCSI) Linux driver ALPHA release 0.5, 19 November 1995
+
+
+Changelog
+---------
+0.1 -> 0.2: 
+  - Extended message handling re-written to eliminate 'invalid message 17' bug
+  - Parameters of AM53C974_intr adapted
+  - Debug messages structured
+  - Spelling improved
+0.2 -> 0.3:
+  - README file updated -- please read this file up to the end!
+  - Automatic scanning of io_port and irq implemented; no need for BIOS32 
+    anymore
+  - Improved configuration (now via LILO parameter string)
+  - Cleanup of probing and initialisation code
+  - Improved sync. negotiation (can be setup individually for every device)
+  - Improved/ debugged code for reception of ext. messages
+0.3 -> 0.4:
+  - Improved PCI probing and initialisation code
+  - Compatibility changes for Linux 1.3.x
+0.4 -> 0.5:
+  - Compatibility changes for Linux 1.3.42
+
+Bugs & Todo
+-----------
+ - Add proc info function
+ - Support SCSI-2 tagged queuing
+ - Finalize abort code 
+
+Features
+--------
+This driver supports asynchronous and synchronous SCSI-I and SCSI-II devices.
+It is capable of transfer rate and synchronous negotiation (see below).
+The driver supportes scatter-gather. Transfers are DMA based, but do not 
+(yet) make use of the AM53/79C974 MDL mode.
+Max. transfer rate is 10MHz (whatever this is in real life). The transfer
+rate is negotiated with each device (see dmesg output).
+The AM53/79C974 has a 96-byte DMA FIFO to the PCI bus and a 16-byte SCSI
+FIFO. It provides active negation and glitch suppression functions.
+Burst DMA transfer rate is 132 MBytes/sec.
+
+
+Configuration
+-------------
+The following communication characteristics can be set individually  for every
+SCSI device on the bus:
+  - enable/disable sync. negotiation
+  - transfer rate
+  - asynchronous or synchronous communication
+  - in case of sync. communication, the sync. offset
+
+The sync. offset specifies the number of bytes that can be sent or 
+reveived from the SCSI bus without ACK resp. REQ signal.
+CAUTION: USING SYNCHRONOUS MODE ON LONG SCSI CABLES MAY CAUSE 
+         COMMUNICATION PROBLEMS LEADING TO LOSS OF DATA. 
+
+The default setting of the SCSI communication parameters is as follows:
+  - no negotiation
+  - 5.0 MHz transfer rate
+  - asynchronous mode
+  - zero offset
+The parameters can be modified by passing a string with the following syntax to 
+the kernel: AM53C974=host-scsi-id,target-scsi-id,max-rate,max-offset
+The parameters will be used by the driver as negotiation basis.
+The range of the rate parameter is 3 to 10 MHz.
+The range of the sync. offset parameter is 0 to 15 bytes. A value of 0 denotes 
+asynchronous comm. mode.
+If the target cannot cope with the specified transfer rate, sync. mode or sync.
+offset, the negotiation result will differ from the specified values.
+The negotiation result is printed out at the end of the negotiation process
+(to read it, use the dmesg program or the appropriate syslog).
+The parameter strings (blank separated) can be passed to the kernel at the
+LILO prompt, or as part of the LILO configuration file.
+
+For example, the string "AM53C974=7,2,8,15" would be interpreted as follows:
+ "For communication between the controller with SCSI-ID 7 and the device with
+ SCSI-ID 2 a transfer rate of 8MHz in synchronous mode with max. 15 bytes offset
+ should be negotiated".
+
+As an example, here my LILO configuration file:
+  boot = /dev/sda
+  compact
+  #prompt
+  delay = 50	# optional, for systems that boot very quickly
+  vga = normal	# force sane state
+  ramdisk = 0	# paranoia setting
+  root = current  # use "current" root
+  image = /usr/src/linux/arch/i386/boot/zImage
+    label = linux
+    append = "AM53C974=7,0,10,0 AM53C974=7,1,10,0 AM53C974=7,2,10,15 AM53C974=7,4,10,0 AM53C974=7,5,10,0"
+    read-only
+  other = /dev/sda4
+    label = os2
+  other = /dev/sdb3
+    loader = /boot/any_d.b
+    table = /dev/sdb
+    label = setup
+
+The same parameters at the LILO prompt:
+  LILO boot: linux AM53C974=7,0,10,0 AM53C974=7,1,10,0 AM53C974=7,2,10,15 AM53C974=7,4,10,0 AM53C974=7,5,10,0
+
+You can override parameters specified in the LILO configuration file by 
+parameters specified on the LILO command line.
+
+
+BIOS usage
+----------
+Version 0.4 of the driver will use the BIOS, if available. Otherwise
+it will try its internal PCI scan and access routines.
+The driver assumes that the controller's SCSI-ID (usually 7) has been
+correctly loaded by the BIOS into the controller's register during
+system boot. If the driver detects that the controller's SCSI ID is not 
+'7' it will print out a warning. If this happens to you please correct
+seeting of the controller's SCSI-ID. If it is wrong, then edit the 
+AM53C974_SCSI_ID definition in file AM53C974.h accordingly.
+
+
+Test environment
+----------------
+This driver was tested on a Compaq XL566 with the following SCSI configuration:
+2 x HP C2247 fixed disk (internal, rate=10MHz, async.)
+1 x Micropolis 1624 fixed disk (external, rate=8MHz, sync., offset=15 bytes)
+1 x Wangtek W5525ES cartridge streamer (internal, rate=5MHz, async.)
+1 x Toshiba XM-3301B CD-ROM (external, rate=5MHz, async.)
+
+
+Known problems
+--------------
+ - Compaq/Matsushita CD-ROM:
+   Use of this device with AM53C974 driver version 0.2 caused the kernel to
+   hang during Linux boot. If you encounter the problem, don't enable sync.
+   negotiation with the CD-ROM, i.e. simply don't specify comm. parameters 
+   for this device on the LILO commandline or configuration file. 
+   The driver will thus use its default for the CD-ROM, which is 5MHz 
+   transfer rate asynch and no sync. negotiation.
+ - Some disks cause problems.
+
+
+What to do if there is a SCSI problem possibly related to the driver
+--------------------------------------------------------------------
+Read Klaus Liedl's WWW page (http://www-c724.uibk.ac.at/XL/).
+In case this does not help:
+Send me a complete description of the problem, including your SCSI 
+configuration plus as much debugging information as possible. 
+Don't wait until I ask you for this information. To enable the 
+generation of debugging output, remove the comments from the following 
+definitions in the AM53C974.h file:
+    AM53C974_DEBUG
+    AM53C974_DEBUG_MSG
+    AM53C974_DEBUG_KEYWAIT
+    AM53C974_DEBUG_INFO
+    AM53C974_DEBUG_INTR
+With these definitions enabled, the driver will enter single-step mode during 
+Linux boot. Use the spacebar for stepping.
+Take note of at least the last 10 printout sections (marked by dashes) before 
+the crash/hangup or whatever happens and send me all of this information via 
+email. If the system can boot, use the syslogd daemon to record the debugging 
+output. Maybe you can use the ramdisk for this purpose too (if necessary, kindly
+ask K. Liedl (Klaus.Liedl@uibk.ac.at) for support, he knows how to do it -- 
+I never tried). Stay in email contact with me. Be aware that the following 
+weeks/months could be the worst of your life.
+Note: If single-stepping takes up too much time, you can try to let the driver 
+catch the problem by pressing the 'r' key. The driver will automatically enter
+single-step mode if it has detected something weird.
+
+
+Author's Contact Adress
+-----------------------
+Email: fri@rsx42sun0.dofn.de
+Phone: x49-7545-2256 (office), x49-7541-42305 (home)
+Home address: D. Frieauff, Stockerholzstr. 27, 88048 Friedrichshafen, Germany
+
+
+!!!! Important Notice !!!!
+-----------------------------
+- Klaus Liedl maintains an excellent WWW page about Linux on Compaq XL.
+  It includes an FAQ, lots of tips & tricks as well as downloadable 
+  boot disk images. The URL is: http://www-c724.uibk.ac.at/XL/
+- Volunteer wanted for further maintenance of this driver software. I
+  don't have the time anymore to do serious support as some of you will know.
+
+Literature
+----------
+ - AMD AM53C974 PC-SCSI Technical Manual, publication #18624B
+ - Amendment to the AMD AM53C974 PC-SCSI Technical Manual
+ - AMD AM79C974 PC-NET Datasheet, publication #18681
+ - Amendment to the AMD AM79C974 PC-NET Datasheet
+
+
+THANKS to
+---------
+ - Drew Eckhardt, Robin Cutshaw, K. Liedl, Robert J. Pappas, A. Grenier, 
+   Mark Stockton, David C. Niemi, Ben Craft, and many others who have helped
+
diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/aha152x.c linux/drivers/scsi/aha152x.c
--- v1.3.58/linux/drivers/scsi/aha152x.c	Sun Jan 14 16:30:11 1996
+++ linux/drivers/scsi/aha152x.c	Tue Jan 23 21:20:34 1996
@@ -20,9 +20,12 @@
  * General Public License for more details.
  *
  *
- * $Id: aha152x.c,v 1.13 1996/01/09 02:15:53 fischer Exp $
+ * $Id: aha152x.c,v 1.14 1996/01/17 15:11:20 fischer Exp fischer $
  *
  * $Log: aha152x.c,v $
+ * Revision 1.14  1996/01/17  15:11:20  fischer
+ * - fixed lockup in MESSAGE IN phase after reconnection
+ *
  * Revision 1.13  1996/01/09  02:15:53  fischer
  * - some cleanups
  * - moved request_irq behind controller initialization
@@ -1402,8 +1405,6 @@
           aha152x_panic(shpnt, "unknown lun");
 	}
 
-      make_acklow(shpnt);
-      getphase(shpnt);
 
 #if defined(DEBUG_QUEUES)
       if(HOSTDATA(shpnt)->debug & debug_queues)
@@ -1430,6 +1431,8 @@
       CURRENT_SC->SCp.phase &= ~disconnected;
       restore_flags(flags);
 
+      make_acklow(shpnt);
+      if(getphase(shpnt)!=P_MSGI) {
       SETPORT(SIMODE0, 0);
       SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
 #if defined(DEBUG_RACE)
@@ -1438,6 +1441,7 @@
       SETBITS(DMACNTRL0, INTEN);
       return;
     }
+    }
   
   /* Check, if we aren't busy with a command */
   if(!CURRENT_SC)
@@ -1946,6 +1950,7 @@
 	    printk("d+, ");
 #endif
             append_SC(&DISCONNECTED_SC, CURRENT_SC);
+            CURRENT_SC->SCp.phase |= 1<<16;
             CURRENT_SC = NULL;
 	  restore_flags(flags);
 
diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/aha152x.h linux/drivers/scsi/aha152x.h
--- v1.3.58/linux/drivers/scsi/aha152x.h	Sun Jan 14 16:30:11 1996
+++ linux/drivers/scsi/aha152x.h	Tue Jan 23 21:20:34 1996
@@ -2,7 +2,7 @@
 #define _AHA152X_H
 
 /*
- * $Id: aha152x.h,v 1.13 1995/12/16 12:27:23 fischer Exp $
+ * $Id: aha152x.h,v 1.14 1996/01/17 15:13:36 fischer Exp fischer $
  */
 
 #if defined(__KERNEL__)
@@ -23,7 +23,7 @@
    (unless we support more than 1 cmd_per_lun this should do) */
 #define AHA152X_MAXQUEUE	7		
 
-#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.13 $"
+#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.14 $"
 
 extern struct proc_dir_entry proc_scsi_aha152x;
 
diff -u --recursive --new-file v1.3.58/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v1.3.58/linux/drivers/scsi/hosts.c	Tue Jan 23 21:15:43 1996
+++ linux/drivers/scsi/hosts.c	Tue Jan 23 21:34:30 1996
@@ -125,6 +125,10 @@
 #include "NCR53c406a.h"
 #endif
 
+#ifdef CONFIG_SCSI_AM53C974
+#include "AM53C974.h"
+#endif
+
 #ifdef CONFIG_SCSI_DEBUG
 #include "scsi_debug.h"
 #endif
@@ -225,6 +229,9 @@
 #endif
 #ifdef CONFIG_SCSI_EATA
     EATA,
+#endif
+#ifdef CONFIG_SCSI_AM53C974
+    AM53C974,
 #endif
 #ifdef CONFIG_SCSI_DEBUG
     SCSI_DEBUG,
diff -u --recursive --new-file v1.3.58/linux/drivers/sound/sb16_dsp.c linux/drivers/sound/sb16_dsp.c
--- v1.3.58/linux/drivers/sound/sb16_dsp.c	Tue Jan 23 21:15:48 1996
+++ linux/drivers/sound/sb16_dsp.c	Tue Jan 23 22:31:47 1996
@@ -410,8 +410,8 @@
   audio_devs[my_dev]->dmachan1 = dsp_16bit ? dma16 : dma8;
   dsp_count = 0;
   dsp_cleanup ();
-  sb_dsp_command (0xd0);	/* Halt DMA until trigger() is called */
   trigger_bits = 0;
+  sb_dsp_command (0xd4);
   return 0;
 }
 
@@ -421,20 +421,14 @@
   audio_devs[my_dev]->dmachan1 = dsp_16bit ? dma16 : dma8;
   dsp_count = 0;
   dsp_cleanup ();
-  sb_dsp_command (0xd0);	/* Halt DMA until trigger() is called */
   trigger_bits = 0;
+  sb_dsp_command (0xd4);
   return 0;
 }
 
 static void
 sb16_dsp_trigger (int dev, int bits)
 {
-  if (bits != 0)
-    bits = 1;
-
-  if (bits == trigger_bits)	/* No change */
-    return;
-
   trigger_bits = bits;
 
   if (!bits)
diff -u --recursive --new-file v1.3.58/linux/drivers/sound/sb_dsp.c linux/drivers/sound/sb_dsp.c
--- v1.3.58/linux/drivers/sound/sb_dsp.c	Tue Jan 23 21:15:48 1996
+++ linux/drivers/sound/sb_dsp.c	Tue Jan 23 22:30:38 1996
@@ -1378,26 +1378,23 @@
   initialize_ProSonic16 ();
 }
 
-long
-sb_dsp_init (long mem_start, struct address_info *hw_config)
+static void
+dsp_get_vers (struct address_info *hw_config)
 {
   int             i;
-  int             ess_major = 0, ess_minor = 0;
 
-  int             mixer_type = 0;
+  unsigned long   flags;
 
+  save_flags (flags);
+  cli ();
   sb_osp = hw_config->osp;
   sbc_major = sbc_minor = 0;
-  sb_dsp_command (0xe1);	/*
-				 * Get version
-				 */
+  sb_dsp_command (0xe1);	/* Get version */
 
-  for (i = 1000; i; i--)
+  for (i = 100000; i; i--)
     {
       if (inb (DSP_DATA_AVAIL) & 0x80)
-	{			/*
-				 * wait for Data Ready
-				 */
+	{
 	  if (sbc_major == 0)
 	    sbc_major = inb (DSP_READ);
 	  else
@@ -1407,10 +1404,28 @@
 	    }
 	}
     }
+  restore_flags (flags);
+}
+
+long
+sb_dsp_init (long mem_start, struct address_info *hw_config)
+{
+  int             i;
+  int             ess_major = 0, ess_minor = 0;
+
+  int             mixer_type = 0;
+
+  dsp_get_vers (hw_config);
+
+  if (sbc_major == 0)
+    {
+      sb_reset_dsp ();
+      dsp_get_vers (hw_config);
+    }
 
   if (sbc_major == 0)
     {
-      printk ("\n\nFailed to get SB version (%x) - possible I/O conflict\n\n",
+      printk ("\n\nFailed to get SB version (%x) - possible I/O conflict?\n\n",
 	      inb (DSP_DATA_AVAIL));
       sbc_major = 1;
     }
diff -u --recursive --new-file v1.3.58/linux/fs/buffer.c linux/fs/buffer.c
--- v1.3.58/linux/fs/buffer.c	Tue Jan 23 21:15:49 1996
+++ linux/fs/buffer.c	Tue Jan 23 20:47:33 1996
@@ -998,13 +998,12 @@
 	unsigned long flags;
 	
 	save_flags(flags);
-	cli();
 	while (reuse_list) {
+		cli();
 		bh = reuse_list;
 		reuse_list = bh->b_next_free;
 		restore_flags(flags);
 		put_unused_buffer_head(bh);
-		cli();
 	}
 }
 
@@ -1062,7 +1061,7 @@
 	return NULL;
 }
 
-static int bread_page(unsigned long address, kdev_t dev, int b[], int size)
+int brw_page(int rw, unsigned long address, kdev_t dev, int b[], int size, int bmap)
 {
 	struct buffer_head *bh, *prev, *next, *arr[MAX_BUF_PER_PAGE];
 	int block, nr;
@@ -1085,35 +1084,55 @@
 		next->b_blocknr = block;
 		next->b_count = 1;
 		next->b_flushtime = 0;
-		clear_bit(BH_Dirty, &next->b_state);
-		clear_bit(BH_Req, &next->b_state);
 		set_bit(BH_Uptodate, &next->b_state);
-		
-		if (!block) {
+
+		/* When we use bmap, we define block zero to represent
+                   a hole.  ll_rw_page, however, may legitimately
+                   access block zero, and we need to distinguish the
+                   two cases. 
+		   */
+		if (bmap && !block) {
 			memset(next->b_data, 0, size);
+			next->b_count--;
 			continue;
 		}
 		tmp = get_hash_table(dev, block, size);
 		if (tmp) {
 			if (!buffer_uptodate(tmp)) {
-				ll_rw_block(READ, 1, &tmp);
+				if (rw == READ)
+					ll_rw_block(READ, 1, &tmp);
 				wait_on_buffer(tmp);
 			}
-			memcpy(next->b_data, tmp->b_data, size);
+			if (rw == READ) 
+				memcpy(next->b_data, tmp->b_data, size);
+			else {
+				memcpy(tmp->b_data, next->b_data, size);
+				set_bit(BH_Dirty, &tmp->b_state);
+			}
 			brelse(tmp);
+			next->b_count--;
 			continue;
 		}
-		clear_bit(BH_Uptodate, &next->b_state);
+		if (rw == READ)
+			clear_bit(BH_Uptodate, &next->b_state);
+		else
+			set_bit(BH_Dirty, &next->b_state);
 		arr[nr++] = next;
 	} while (prev = next, (next = next->b_this_page) != NULL);
 	prev->b_this_page = bh;
 	
 	if (nr)
-		ll_rw_block(READ, nr, arr);
+		ll_rw_block(rw, nr, arr);
 	else {
 		page->locked = 0;
 		page->uptodate = 1;
 		wake_up(&page->wait);
+		next = bh;
+		do {
+			next->b_next_free = reuse_list;
+			reuse_list = next;
+			next = next->b_this_page;
+		} while (next != bh);
 	}
 	++current->maj_flt;
 	return 0;
@@ -1138,6 +1157,64 @@
 		clear_bit(BH_Uptodate, &bh->b_state);
 }
 
+void unlock_buffer(struct buffer_head * bh)
+{
+	struct buffer_head *tmp;
+	unsigned long flags;
+	struct page *page;
+
+	clear_bit(BH_Lock, &bh->b_state);
+	wake_up(&bh->b_wait);
+
+	if (!test_bit(BH_FreeOnIO, &bh->b_state))
+		return;
+	page = mem_map + MAP_NR(bh->b_data);
+	if (!page->locked) {
+		printk ("Whoops: unlock_buffer: "
+			"async io complete on unlocked page\n");
+		return;
+	}
+	if (bh->b_count != 1) {
+		printk ("Whoops: unlock_buffer: b_count != 1 on async io.\n");
+		return;
+	}
+	/* Async buffer_heads are here only as labels for IO, and get
+           thrown away once the IO for this page is complete.  IO is
+           deemed complete once all buffers have been visited
+           (b_count==0) and are now unlocked. */
+	bh->b_count--;
+	for (tmp = bh; tmp=tmp->b_this_page, tmp!=bh; ) {
+		if (test_bit(BH_Lock, &tmp->b_state) || tmp->b_count)
+			return;
+	}
+
+	/* OK, go ahead and complete the async IO on this page. */
+	save_flags(flags);
+	page->locked = 0;
+	wake_up(&page->wait);
+	cli();
+	tmp = bh;
+	do {
+		if (!test_bit(BH_FreeOnIO, &tmp->b_state)) {
+			printk ("Whoops: unlock_buffer: "
+				"async IO mismatch on page.\n");
+			restore_flags(flags);
+			return;
+		}
+		tmp->b_next_free = reuse_list;
+		reuse_list = tmp;
+		clear_bit(BH_FreeOnIO, &tmp->b_state);
+		tmp = tmp->b_this_page;
+	} while (tmp != bh);
+	restore_flags(flags);
+	if (page->free_after) {
+		extern int nr_async_pages;
+		nr_async_pages--;
+		page->free_after = 0;
+		free_page(page_address(page));
+	}
+}
+
 /*
  * Generic "readpage" function for block devices that have the normal
  * bmap functionality. This is most of the block device filesystems.
@@ -1167,37 +1244,10 @@
 	/* IO start */
 	page->count++;
 	address = page_address(page);
-	bread_page(address, inode->i_dev, nr, inode->i_sb->s_blocksize);
+	brw_page(READ, address, inode->i_dev, nr, inode->i_sb->s_blocksize, 1);
 	free_page(address);
 	return 0;
 }
-
-#if 0
-/*
- * bwrite_page writes a page out to the buffer cache and/or the physical device.
- * It's used for mmap writes (the same way bread_page() is used for mmap reads).
- */
-void bwrite_page(unsigned long address, kdev_t dev, int b[], int size)
-{
-	struct buffer_head * bh[MAX_BUF_PER_PAGE];
-	int i, j;
-
- 	for (i=0, j=0; j<PAGE_SIZE ; i++, j+= size) {
-		bh[i] = NULL;
-		if (b[i])
-			bh[i] = getblk(dev, b[i], size);
-	}
- 	for (i=0, j=0; j<PAGE_SIZE ; i++, j += size, address += size) {
-		if (bh[i]) {
-			memcpy(bh[i]->b_data, (void *) address, size);
-			mark_buffer_uptodate(bh[i], 1);
-			mark_buffer_dirty(bh[i], 0);
-			brelse(bh[i]);
-		} else
-			memset((void *) address, 0, size); /* ???!?!! */
-	}	
-}
-#endif
 
 /*
  * Try to increase the number of buffers available: the size argument
diff -u --recursive --new-file v1.3.58/linux/fs/proc/procfs_syms.c linux/fs/proc/procfs_syms.c
--- v1.3.58/linux/fs/proc/procfs_syms.c	Tue Jan 23 21:15:51 1996
+++ linux/fs/proc/procfs_syms.c	Tue Jan 23 21:50:18 1996
@@ -1,8 +1,16 @@
-#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/proc_fs.h>
 
+/*
+ * This is all required so that if we load all of scsi as a module,
+ * that the scsi code will be able to talk to the /proc/scsi handling
+ * in the procfs.
+ */
+extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
+				off_t offset, int length, int inout);
+extern struct inode_operations proc_scsi_inode_operations;
+
 static struct symbol_table procfs_syms = {
 /* Should this be surrounded with "#ifdef CONFIG_MODULES" ? */
 #include <linux/symtab_begin.h>
@@ -12,11 +20,14 @@
 	X(generate_cluster),
 	X(proc_net_inode_operations),
 	X(proc_net),
-#ifdef CONFIG_SCSI /* Ugh... */
+
+	/*
+	 * This is required so that if we load scsi later, that the
+	 * scsi code can attach to /proc/scsi in the correct manner.
+	 */
 	X(proc_scsi),
 	X(proc_scsi_inode_operations),
 	X(dispatch_scsi_info_ptr),
-#endif
 #include <linux/symtab_end.h>
 };
 
diff -u --recursive --new-file v1.3.58/linux/fs/proc/root.c linux/fs/proc/root.c
--- v1.3.58/linux/fs/proc/root.c	Sun Jan 14 16:30:14 1996
+++ linux/fs/proc/root.c	Tue Jan 23 21:12:45 1996
@@ -297,11 +297,7 @@
 		64, &proc_self_inode_operations,
 	});
 	proc_register(&proc_root, &proc_net);
-
-#ifdef CONFIG_SCSI
 	proc_register(&proc_root, &proc_scsi);
-#endif
-
 	proc_register(&proc_root, &proc_sys_root);
 
 #ifdef CONFIG_DEBUG_MALLOC
diff -u --recursive --new-file v1.3.58/linux/include/linux/blk.h linux/include/linux/blk.h
--- v1.3.58/linux/include/linux/blk.h	Tue Jan 23 21:15:52 1996
+++ linux/include/linux/blk.h	Wed Jan 24 00:20:30 1996
@@ -13,14 +13,14 @@
 #define NR_REQUEST	64
 
 /*
- * This is used in the elevator algorithm: Note that
- * reads always go before writes. This is natural: reads
- * are much more time-critical than writes.
+ * This is used in the elevator algorithm.  We don't prioritise reads
+ * over writes any more --- although reads are more time-critical than
+ * writes, by treating them equally we increase filesystem throughput.
+ * This turns out to give better overall performance.  -- sct
  */
 #define IN_ORDER(s1,s2) \
-((s1)->cmd < (s2)->cmd || ((s1)->cmd == (s2)->cmd && \
 ((s1)->rq_dev < (s2)->rq_dev || (((s1)->rq_dev == (s2)->rq_dev && \
-(s1)->sector < (s2)->sector)))))
+(s1)->sector < (s2)->sector)))
 
 /*
  * These will have to be changed to be aware of different buffer
diff -u --recursive --new-file v1.3.58/linux/include/linux/fs.h linux/include/linux/fs.h
--- v1.3.58/linux/include/linux/fs.h	Tue Jan 23 21:15:52 1996
+++ linux/include/linux/fs.h	Mon Jan 22 22:16:07 1996
@@ -598,8 +598,8 @@
 extern int generic_readpage(struct inode *, struct page *);
 extern int generic_file_read(struct inode *, struct file *, char *, int);
 extern int generic_mmap(struct inode *, struct file *, struct vm_area_struct *);
+extern int brw_page(int, unsigned long, kdev_t, int [], int, int);
 
-extern void bwrite_page(unsigned long addr,kdev_t dev,int b[],int size);
 extern void put_super(kdev_t dev);
 unsigned long generate_cluster(kdev_t dev, int b[], int size);
 extern kdev_t ROOT_DEV;
diff -u --recursive --new-file v1.3.58/linux/include/linux/locks.h linux/include/linux/locks.h
--- v1.3.58/linux/include/linux/locks.h	Tue Jan 23 21:15:52 1996
+++ linux/include/linux/locks.h	Tue Jan 23 20:47:33 1996
@@ -4,6 +4,9 @@
 #ifndef _LINUX_MM_H
 #include <linux/mm.h>
 #endif
+#ifndef _LINUX_PAGEMAP_H
+#include <linux/pagemap.h>
+#endif
 
 /*
  * Unlocked, temporary IO buffer_heads gets moved to the reuse_list
@@ -29,39 +32,8 @@
 		__wait_on_buffer(bh);
 }
 
-extern inline void unlock_buffer(struct buffer_head * bh)
-{
-	struct buffer_head *tmp = bh;
-	int page_locked = 0;
-	unsigned long flags;
-	
-	clear_bit(BH_Lock, &bh->b_state);
-	wake_up(&bh->b_wait);
-	do {
-		if (test_bit(BH_Lock, &tmp->b_state)) {
-			page_locked = 1;
-			break;
-		}
-		tmp=tmp->b_this_page;
-	} while (tmp && tmp != bh);
-	save_flags(flags);
-	if (!page_locked) {
-		struct page *page = mem_map + MAP_NR(bh->b_data);
-		page->locked = 0;
-		wake_up(&page->wait);
-		tmp = bh;
-		cli();
-		do {
-			if (test_bit(BH_FreeOnIO, &tmp->b_state)) {
-				tmp->b_next_free = reuse_list;
-				reuse_list = tmp;
-				clear_bit(BH_FreeOnIO, &tmp->b_state);
-			}
-			tmp = tmp->b_this_page;
-		} while (tmp && tmp != bh);
-		restore_flags(flags);
-	}
-}
+void unlock_buffer(struct buffer_head *);
+
 
 /*
  * super-block locking. Again, interrupts may only unlock
diff -u --recursive --new-file v1.3.58/linux/include/linux/mm.h linux/include/linux/mm.h
--- v1.3.58/linux/include/linux/mm.h	Tue Jan 23 21:15:53 1996
+++ linux/include/linux/mm.h	Mon Jan 22 22:45:24 1996
@@ -122,7 +122,8 @@
 		 error:1,
 		 referenced:1,
 		 locked:1,
-		 unused:3,
+		 free_after:1,
+		 unused:2,
 		 reserved:1;
 	struct wait_queue *wait;
 	struct page *next;
diff -u --recursive --new-file v1.3.58/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v1.3.58/linux/include/linux/proc_fs.h	Tue Jan 23 21:15:53 1996
+++ linux/include/linux/proc_fs.h	Tue Jan 23 21:34:30 1996
@@ -118,6 +118,7 @@
 	PROC_SCSI_ULTRASTOR,
 	PROC_SCSI_7000FASST,
 	PROC_SCSI_EATA2X,
+	PROC_SCSI_AM53C974,
 	PROC_SCSI_SSC,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
diff -u --recursive --new-file v1.3.58/linux/include/linux/swap.h linux/include/linux/swap.h
--- v1.3.58/linux/include/linux/swap.h	Sun Jan 14 16:30:14 1996
+++ linux/include/linux/swap.h	Mon Jan 22 22:16:07 1996
@@ -12,6 +12,8 @@
 #define SWP_USED	1
 #define SWP_WRITEOK	3
 
+#define SWAP_CLUSTER_MAX 32
+
 struct swap_info_struct {
 	unsigned int flags;
 	kdev_t swap_device;
@@ -20,6 +22,8 @@
 	unsigned char * swap_lockmap;
 	int lowest_bit;
 	int highest_bit;
+	int cluster_next;
+	int cluster_nr;
 	int prio;			/* swap priority */
 	int pages;
 	unsigned long max;
@@ -28,6 +32,7 @@
 
 extern int nr_swap_pages;
 extern int nr_free_pages;
+extern int nr_async_pages;
 extern int min_free_pages;
 extern int free_pages_low;
 extern int free_pages_high;
@@ -40,17 +45,17 @@
 /* linux/ipc/shm.c */
 extern int shm_swap (int, unsigned long);
 
-/* linux/mm/swap_clock.c */
-extern int try_to_free_page(int, unsigned long);
+/* linux/mm/vmscan.c */
+extern int try_to_free_page(int, unsigned long, int);
 
-/* linux/mm/swap_io.c */
-extern void rw_swap_page(int, unsigned long, char *);
+/* linux/mm/page_io.c */
+extern void rw_swap_page(int, unsigned long, char *, int);
 #define read_swap_page(nr,buf) \
-	rw_swap_page(READ,(nr),(buf))
+	rw_swap_page(READ,(nr),(buf),1)
 #define write_swap_page(nr,buf) \
-	rw_swap_page(WRITE,(nr),(buf))
+	rw_swap_page(WRITE,(nr),(buf),1)
 
-/* linux/mm/swap_mman.c */
+/* linux/mm/page_alloc.c */
 extern void swap_in(struct task_struct *, struct vm_area_struct *,
 		    pte_t *, unsigned long, int);
 
diff -u --recursive --new-file v1.3.58/linux/init/main.c linux/init/main.c
--- v1.3.58/linux/init/main.c	Tue Jan 23 21:15:54 1996
+++ linux/init/main.c	Tue Jan 23 21:34:30 1996
@@ -70,6 +70,7 @@
 extern void aha152x_setup(char *str, int *ints);
 extern void aha1542_setup(char *str, int *ints);
 extern void aic7xxx_setup(char *str, int *ints);
+extern void AM53C974_setup(char *str, int *ints);
 extern void BusLogic_Setup(char *str, int *ints);
 extern void fdomain_setup(char *str, int *ints);
 extern void NCR53c406a_setup(char *str, int *ints);
@@ -238,6 +239,9 @@
 #endif
 #ifdef CONFIG_SCSI_BUSLOGIC
 	{ "BusLogic=", BusLogic_Setup},
+#endif
+#ifdef CONFIG_SCSI_AM53C974
+        { "AM53C974=", AM53C974_setup},
 #endif
 #ifdef CONFIG_SCSI_NCR53C406A
 	{ "ncr53c406a=", NCR53c406a_setup},
diff -u --recursive --new-file v1.3.58/linux/kernel/ksyms.c linux/kernel/ksyms.c
--- v1.3.58/linux/kernel/ksyms.c	Tue Jan 23 21:15:54 1996
+++ linux/kernel/ksyms.c	Tue Jan 23 21:12:17 1996
@@ -86,19 +86,6 @@
 #include <linux/smp.h>
 #endif
 
-#ifndef CONFIG_SCSI
-#if defined(CONFIG_PROC_FS)
-/*
- * This is all required so that if we load all of scsi as a module,
- * that the scsi code will be able to talk to the /proc/scsi handling
- * in the procfs.
- */
-extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
-				off_t offset, int length, int inout);
-extern struct inode_operations proc_scsi_inode_operations;
-#endif
-#endif
-
 extern char *get_options(char *str, int *ints);
 extern void set_device_ro(int dev,int flag);
 extern struct file_operations * get_blkfops(unsigned int);
@@ -210,6 +197,7 @@
 	X(__bforget),
 	X(ll_rw_block),
 	X(__wait_on_buffer),
+	X(unlock_buffer),
 	X(dcache_lookup),
 	X(dcache_add),
 	X(aout_core_dump),
@@ -435,14 +423,6 @@
 	 */
 	X(gendisk_head),
 	X(resetup_one_dev),
-#if defined(CONFIG_PROC_FS)
-	/*
-	 * This is required so that if we load scsi later, that the
-	 * scsi code can attach to /proc/scsi in the correct manner.
-	 */
-	X(proc_scsi_inode_operations),
-	X(dispatch_scsi_info_ptr),
-#endif
 #endif
 	/* Added to make file system as module */
 	X(set_writetime),
diff -u --recursive --new-file v1.3.58/linux/mm/filemap.c linux/mm/filemap.c
--- v1.3.58/linux/mm/filemap.c	Tue Jan 23 21:15:55 1996
+++ linux/mm/filemap.c	Tue Jan 23 20:47:33 1996
@@ -168,8 +168,7 @@
 	if (page) {
 		unsigned long addr;
 
-		if (!page->uptodate)
-			sleep_on(&page->wait);
+		wait_on_page(page);
 		addr = page_address(page);
 		memcpy((void *) ((pos & ~PAGE_MASK) + addr), buf, count);
 		free_page(addr);
diff -u --recursive --new-file v1.3.58/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v1.3.58/linux/mm/page_alloc.c	Sun Jan 14 16:30:16 1996
+++ linux/mm/page_alloc.c	Mon Jan 22 22:16:07 1996
@@ -181,7 +181,7 @@
 		return 0;
 	}
 	restore_flags(flags);
-	if (priority != GFP_BUFFER && try_to_free_page(priority, limit))
+	if (priority != GFP_BUFFER && try_to_free_page(priority, limit, 1))
 		goto repeat;
 	return 0;
 }
diff -u --recursive --new-file v1.3.58/linux/mm/page_io.c linux/mm/page_io.c
--- v1.3.58/linux/mm/page_io.c	Sun Jan 14 16:30:16 1996
+++ linux/mm/page_io.c	Mon Jan 22 22:16:07 1996
@@ -28,11 +28,12 @@
 
 static struct wait_queue * lock_queue = NULL;
 
-void rw_swap_page(int rw, unsigned long entry, char * buf)
+void rw_swap_page(int rw, unsigned long entry, char * buf, int wait)
 {
 	unsigned long type, offset;
 	struct swap_info_struct * p;
-
+	struct page *page;
+	
 	type = SWP_TYPE(entry);
 	if (type >= nr_swapfiles) {
 		printk("Internal error: bad swap-device\n");
@@ -58,8 +59,17 @@
 		kstat.pswpin++;
 	else
 		kstat.pswpout++;
+	page = mem_map + MAP_NR(buf);
+	wait_on_page(page);
 	if (p->swap_device) {
+		if (!wait) {
+			page->count++;
+			page->free_after = 1;
+			nr_async_pages++;
+		}
 		ll_rw_page(rw,p->swap_device,offset,buf);
+		if (wait)
+			wait_on_page(page);
 	} else if (p->swap_file) {
 		struct inode *swapf = p->swap_file;
 		unsigned int zones[PAGE_SIZE/512];
@@ -105,4 +115,3 @@
 		printk("rw_swap_page: lock already cleared\n");
 	wake_up(&lock_queue);
 }
-
diff -u --recursive --new-file v1.3.58/linux/mm/swap.c linux/mm/swap.c
--- v1.3.58/linux/mm/swap.c	Tue Jan 23 21:15:55 1996
+++ linux/mm/swap.c	Mon Jan 22 22:16:07 1996
@@ -42,6 +42,10 @@
 int free_pages_low = 30;
 int free_pages_high = 40;
 
+/* We track the number of pages currently being asynchronously swapped
+   out, so that we don't try to swap TOO many pages out at once */
+int nr_async_pages = 0;
+
 /*
  * Constants for the page aging mechanism: the maximum age (actually,
  * the maximum "youthfulness"); the quanta by which pages rejuvinate
diff -u --recursive --new-file v1.3.58/linux/mm/swapfile.c linux/mm/swapfile.c
--- v1.3.58/linux/mm/swapfile.c	Sun Jan 14 16:30:16 1996
+++ linux/mm/swapfile.c	Mon Jan 22 22:16:07 1996
@@ -31,6 +31,46 @@
 
 struct swap_info_struct swap_info[MAX_SWAPFILES];
 
+static inline int scan_swap_map(struct swap_info_struct *si)
+{
+	int offset;
+	/* 
+	 * We try to cluster swap pages by allocating them
+	 * sequentially in swap.  Once we've allocated
+	 * SWAP_CLUSTER_MAX pages this way, however, we resort to
+	 * first-free allocation, starting a new cluster.  This
+	 * prevents us from scattering swap pages all over the entire
+	 * swap partition, so that we reduce overall disk seek times
+	 * between swap pages.  -- sct */
+	if (si->cluster_nr) {
+		while (si->cluster_next <= si->highest_bit) {
+			offset = si->cluster_next++;
+			if (si->swap_map[offset])
+				continue;
+			if (test_bit(offset, si->swap_lockmap))
+				continue;
+			si->cluster_nr--;
+			goto got_page;
+		}
+	}
+	si->cluster_nr = SWAP_CLUSTER_MAX;
+	for (offset = si->lowest_bit; offset <= si->highest_bit ; offset++) {
+		if (si->swap_map[offset])
+			continue;
+		if (test_bit(offset, si->swap_lockmap))
+			continue;
+		si->lowest_bit = offset;
+got_page:
+		si->swap_map[offset] = 1;
+		nr_swap_pages--;
+		if (offset == si->highest_bit)
+			si->highest_bit--;
+		si->cluster_next = offset;
+		return offset;
+	}
+	return 0;
+}
+
 unsigned long get_swap_page(void)
 {
 	struct swap_info_struct * p;
@@ -44,26 +84,17 @@
 	while (1) {
 		p = &swap_info[type];
 		if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
-			for (offset = p->lowest_bit; offset <= p->highest_bit ; offset++) {
-				if (p->swap_map[offset])
-				  continue;
-				if (test_bit(offset, p->swap_lockmap))
-				  continue;
-				p->swap_map[offset] = 1;
-				nr_swap_pages--;
-				if (offset == p->highest_bit)
-				  p->highest_bit--;
-				p->lowest_bit = offset;
-				entry = SWP_ENTRY(type,offset);
-
-				type = swap_info[type].next;
-				if (type < 0 || p->prio != swap_info[type].prio) {
-				    swap_list.next = swap_list.head;
-				} else {
-				    swap_list.next = type;
-				}
-				return entry;
+			offset = scan_swap_map(p);
+			if (!offset)
+				continue;
+			entry = SWP_ENTRY(type,offset);
+			type = swap_info[type].next;
+			if (type < 0 || p->prio != swap_info[type].prio) {
+				swap_list.next = swap_list.head;
+			} else {
+				swap_list.next = type;
 			}
+			return entry;
 		}
 		type = p->next;
 		if (!wrapped) {
@@ -380,6 +411,7 @@
 	p->swap_lockmap = NULL;
 	p->lowest_bit = 0;
 	p->highest_bit = 0;
+	p->cluster_nr = 0;
 	p->max = 1;
 	p->next = -1;
 	if (swap_flags & SWAP_FLAG_PREFER) {
@@ -399,7 +431,8 @@
 
 	if (S_ISBLK(swap_inode->i_mode)) {
 		p->swap_device = swap_inode->i_rdev;
-
+		set_blocksize(p->swap_device, PAGE_SIZE);
+		
 		filp.f_inode = swap_inode;
 		filp.f_mode = 3; /* read write */
 		error = blkdev_open(swap_inode, &filp);
diff -u --recursive --new-file v1.3.58/linux/mm/vmscan.c linux/mm/vmscan.c
--- v1.3.58/linux/mm/vmscan.c	Tue Jan 23 21:15:55 1996
+++ linux/mm/vmscan.c	Mon Jan 22 22:16:08 1996
@@ -5,7 +5,7 @@
  *
  *  Swap reorganised 29.12.95, Stephen Tweedie.
  *  kswapd added: 7.1.96  sct
- *  Version: $Id: vmscan.c,v 1.3.2.3 1996/01/17 02:43:11 linux Exp $
+ *  Version: $Id: vmscan.c,v 1.4.2.2 1996/01/20 18:22:47 linux Exp $
  */
 
 #include <linux/mm.h>
@@ -68,7 +68,7 @@
  * have died while we slept).
  */
 static inline int try_to_swap_out(struct task_struct * tsk, struct vm_area_struct* vma,
-	unsigned long address, pte_t * page_table, unsigned long limit)
+	unsigned long address, pte_t * page_table, unsigned long limit, int wait)
 {
 	pte_t pte;
 	unsigned long entry;
@@ -114,7 +114,7 @@
 			set_pte(page_table, __pte(entry));
 			invalidate_page(vma, address);
 			tsk->nswap++;
-			write_swap_page(entry, (char *) page);
+			rw_swap_page(WRITE, entry, (char *) page, wait);
 		}
 		free_page(page);
 		return 1;	/* we slept: the process may not exist any more */
@@ -154,7 +154,7 @@
  */
 
 static inline int swap_out_pmd(struct task_struct * tsk, struct vm_area_struct * vma,
-	pmd_t *dir, unsigned long address, unsigned long end, unsigned long limit)
+	pmd_t *dir, unsigned long address, unsigned long end, unsigned long limit, int wait)
 {
 	pte_t * pte;
 	unsigned long pmd_end;
@@ -176,7 +176,7 @@
 	do {
 		int result;
 		tsk->swap_address = address + PAGE_SIZE;
-		result = try_to_swap_out(tsk, vma, address, pte, limit);
+		result = try_to_swap_out(tsk, vma, address, pte, limit, wait);
 		if (result)
 			return result;
 		address += PAGE_SIZE;
@@ -186,7 +186,7 @@
 }
 
 static inline int swap_out_pgd(struct task_struct * tsk, struct vm_area_struct * vma,
-	pgd_t *dir, unsigned long address, unsigned long end, unsigned long limit)
+	pgd_t *dir, unsigned long address, unsigned long end, unsigned long limit, int wait)
 {
 	pmd_t * pmd;
 	unsigned long pgd_end;
@@ -206,7 +206,7 @@
 		end = pgd_end;
 	
 	do {
-		int result = swap_out_pmd(tsk, vma, pmd, address, end, limit);
+		int result = swap_out_pmd(tsk, vma, pmd, address, end, limit, wait);
 		if (result)
 			return result;
 		address = (address + PMD_SIZE) & PMD_MASK;
@@ -216,7 +216,7 @@
 }
 
 static int swap_out_vma(struct task_struct * tsk, struct vm_area_struct * vma,
-	pgd_t *pgdir, unsigned long start, unsigned long limit)
+	pgd_t *pgdir, unsigned long start, unsigned long limit, int wait)
 {
 	unsigned long end;
 
@@ -227,7 +227,7 @@
 
 	end = vma->vm_end;
 	while (start < end) {
-		int result = swap_out_pgd(tsk, vma, pgdir, start, end, limit);
+		int result = swap_out_pgd(tsk, vma, pgdir, start, end, limit, wait);
 		if (result)
 			return result;
 		start = (start + PGDIR_SIZE) & PGDIR_MASK;
@@ -236,7 +236,7 @@
 	return 0;
 }
 
-static int swap_out_process(struct task_struct * p, unsigned long limit)
+static int swap_out_process(struct task_struct * p, unsigned long limit, int wait)
 {
 	unsigned long address;
 	struct vm_area_struct* vma;
@@ -257,7 +257,7 @@
 		address = vma->vm_start;
 
 	for (;;) {
-		int result = swap_out_vma(p, vma, pgd_offset(p->mm, address), address, limit);
+		int result = swap_out_vma(p, vma, pgd_offset(p->mm, address), address, limit, wait);
 		if (result)
 			return result;
 		vma = vma->vm_next;
@@ -269,7 +269,7 @@
 	return 0;
 }
 
-static int swap_out(unsigned int priority, unsigned long limit)
+static int swap_out(unsigned int priority, unsigned long limit, int wait)
 {
 	static int swap_task;
 	int loop, counter;
@@ -308,7 +308,7 @@
 		}
 		if (!--p->swap_cnt)
 			swap_task++;
-		switch (swap_out_process(p, limit)) {
+		switch (swap_out_process(p, limit, wait)) {
 			case 0:
 				if (p->swap_cnt)
 					swap_task++;
@@ -327,7 +327,7 @@
  * to be.  This works out OK, because we now do proper aging on page
  * contents. 
  */
-int try_to_free_page(int priority, unsigned long limit)
+int try_to_free_page(int priority, unsigned long limit, int wait)
 {
 	static int state = 0;
 	int i=6;
@@ -343,7 +343,7 @@
 				return 1;
 			state = 2;
 		default:
-			if (swap_out(i, limit))
+			if (swap_out(i, limit, wait))
 				return 1;
 			state = 0;
 		} while(i--);
@@ -359,7 +359,7 @@
 int kswapd(void *unused)
 {
 	int i;
-	char *revision="$Revision: 1.3.2.3 $", *s, *e;
+	char *revision="$Revision: 1.4.2.2 $", *s, *e;
 	
 	current->session = 1;
 	current->pgrp = 1;
@@ -400,7 +400,7 @@
 		swapstats.wakeups++;
 		/* Do the background pageout: */
 		for (i=0; i < kswapd_ctl.maxpages; i++)
-			try_to_free_page(GFP_KERNEL, ~0UL);
+			try_to_free_page(GFP_KERNEL, ~0UL, 0);
 	}
 }
 
@@ -410,8 +410,8 @@
 
 void swap_tick(void)
 {
-	if (nr_free_pages < free_pages_low ||
-	    (nr_free_pages < free_pages_high && 
+	if ((nr_free_pages + nr_async_pages) < free_pages_low ||
+	    ((nr_free_pages + nr_async_pages) < free_pages_high && 
 	     jiffies >= next_swap_jiffies)) {
 		if (!kswapd_awake && kswapd_ctl.maxpages > 0) {
 			wake_up(&kswapd_wait);
diff -u --recursive --new-file v1.3.58/linux/scripts/Configure linux/scripts/Configure
--- v1.3.58/linux/scripts/Configure	Tue Jan 23 21:15:56 1996
+++ linux/scripts/Configure	Tue Jan 23 21:41:34 1996
@@ -263,7 +263,8 @@
         while :; do
           readln "$1 ($2) [$def] " "$def"
           case "$ans" in
-             [1-9] | [1-9][0-9] | [1-9][0-9][0-9] | [1-9][0-9][0-9][0-9] ) 
+             -1 | [1-9] | [1-9][0-9] |\
+             [1-9][0-9][0-9] | [1-9][0-9][0-9][0-9] | [1-9][0-9][0-9][0-9][0-9]) 
                  define_int "$2" "$ans"
                  break;;
              * ) help "$2"
@@ -278,7 +279,7 @@
 #
 function define_hex () {
 	echo "$1=$2" >>$CONFIG
-	echo "#define $1 0x$2" >>$CONFIG_H
+	echo "#define $1 0x${2#*[x,X]}" >>$CONFIG_H
 	eval "$1=$2"
 }
 
@@ -289,10 +290,15 @@
 #
 function hex () {
 	def=$(eval echo "\${$2:-$3}")
+	def=${def#*[x,X]}
         while :; do
           readln "$1 ($2) [$def] " "$def"
+          ans=${ans#*[x,X]}
           case "$ans" in
-             [1-9] | [1-9][0-9] | [1-9][0-9][0-9] | [1-9][0-9][0-9][0-9] ) 
+             [0-9,a-f,A-F] |\
+             [0-9,a-f,A-F][0-9,a-f,A-F] |\
+             [0-9,a-f,A-F][0-9,a-f,A-F][0-9,a-f,A-F] |\
+             [0-9,a-f,A-F][0-9,a-f,A-F][0-9,a-f,A-F][0-9,a-f,A-F])\
                  define_hex "$2" "$ans"
                  break;;
              * ) help "$2"
diff -u --recursive --new-file v1.3.58/linux/scripts/depend.awk linux/scripts/depend.awk
--- v1.3.58/linux/scripts/depend.awk	Wed Nov  8 07:11:47 1995
+++ linux/scripts/depend.awk	Tue Jan 23 21:20:34 1996
@@ -37,6 +37,7 @@
 	hasdep=0
 	hasconfig=0
 	needsconfig=0
+	incomment=0
 	if(!(TOPDIR=ENVIRON["TOPDIR"])) {
 		print "Environment variable TOPDIR is not set"
 		exit 1
@@ -48,6 +49,30 @@
 	}
 }
 
+# eliminate comments
+{
+    # remove all comments fully contained on a single line
+	gsub("\\/\\*.*\\*\\/", "")
+	if (incomment) {
+		if ($0 ~ /\*\//) {
+			incomment = 0;
+			gsub(".*\\*\\/", "")
+		} else {
+			next
+		}
+	} else {
+		# start of multi-line comment
+		if ($0 ~ /\/\*/)
+		{
+			incomment = 1;
+			sub("\\/\\*.*", "")
+		} else if ($0 ~ /\*\//) {
+			incomment = 0;
+			sub(".*\\*\\/", "")
+		}
+	}
+}
+
 /^[ 	]*#[ 	]*if.*[^A-Za-z_]CONFIG_/ {
 	needsconfig=1
 	if (!hasconfig) {
@@ -64,6 +89,7 @@
 		hasdep=0
 		hasconfig=0
 		needsconfig=0
+		incomment=0
 		cmd=""
 		LASTFILE=FILENAME
 		depname=FILENAME
