diff -u --recursive --new-file v2.0.30/linux/CREDITS linux/CREDITS
--- v2.0.30/linux/CREDITS	Tue Apr  8 09:04:28 1997
+++ linux/CREDITS	Mon Aug  4 12:10:37 1997
@@ -466,7 +466,7 @@
 S: USA
 
 N: Philip Gladstone
-E: philipg@onsett.com
+E: philip@raptor.com
 D: Kernel / timekeeping stuff
 
 N: Michael A. Griffith
@@ -1125,9 +1125,9 @@
 S: Germany
 
 N: William E. Roadcap
-E: roadcapw@cfw.com
+E: roadcapw@titus.org
 W: http://www.cfw.com/~roadcapw
-D: Author of menu based configuration tool, Menuconfig.
+D: Author of ncurses based configuration tool, Menuconfig.
 S: 1407 Broad Street
 S: Waynesboro, Virginia  22980
 S: USA
diff -u --recursive --new-file v2.0.30/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.0.30/linux/Documentation/Configure.help	Tue Apr  8 08:47:45 1997
+++ linux/Documentation/Configure.help	Mon Aug  4 11:45:55 1997
@@ -124,18 +124,18 @@
   nothing to do with the loopback device used for network connections
   from the machine to itself.  Most users will answer N here.
 
-Enhanced IDE/MFM/RLL disk/cdrom/tape support
+Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support
 CONFIG_BLK_DEV_IDE 
-  This will use the full-featured IDE driver to control up to four IDE
-  interfaces, for a combination of up to eight IDE disk/cdrom/tape
-  drives.  Useful information about large (>540MB) IDE disks,
-  soundcard IDE ports, and other topics, is all contained in
-  Documentation/ide.txt.  If you have one or more IDE drives, say Y
-  here.  If your system has no IDE drives, or if memory requirements
-  are really tight, you could say N here, and select the Old harddisk
-  driver instead to save about 13kB of memory in the kernel.  To
-  fine-tune IDE drive/interface parameters for improved performance,
-  look for the hdparm package at
+  This will use the full-featured IDE driver to control up to four
+  IDE interfaces, for a combination of up to eight IDE
+  disk/cdrom/tape/floppy drives.  Useful information about large
+  (>540MB) IDE disks, soundcard IDE ports, and other topics, is all
+  contained in Documentation/ide.txt.  If you have one or more IDE
+  drives, say Y here.  If your system has no IDE drives, or if memory
+  requirements are really tight, you could say N here, and select
+  the Old harddisk driver instead to save about 13kB of memory in
+  the kernel.  To fine-tune IDE drive/interface parameters for
+  improved performance, look for the hdparm package at
   sunsite.unc.edu:/pub/Linux/kernel/patches/diskdrives/
 
 Old harddisk (MFM/RLL/IDE) driver
@@ -190,9 +190,32 @@
   ATAPI is a new protocol used by IDE TAPE and ATAPI drives,
   similar to the SCSI protocol.  At boot time, the TAPE drive will
   be identified along with other IDE devices, as "hdb" or "hdc",
-  or something similar.  Be sure to consult the drivers/block/ide-tape.c
+  or something similar, and will be mapped to a character device
+  such as "ht0".  Be sure to consult the drivers/block/ide-tape.c
   and Documentation/ide.txt files for usage information.
 
+Include IDE/ATAPI FLOPPY support (new)
+CONFIG_BLK_DEV_IDEFLOPPY
+  If you have an IDE floppy which uses the ATAPI protocol, say Y.
+  ATAPI is a new protocol used by IDE cdrom/tape/floppy drives,
+  similar to the SCSI protocol.  IDE floppy drives include the
+  LS-120 and the ATAPI ZIP (ATAPI PD-CD drives are not supported
+  by this driver; support for PD-CD drives is available through
+  the SCSI emulation).  At boot time, the FLOPPY drive will be
+  identified along with other IDE devices, as "hdb" or "hdc", or
+  something similar.
+
+SCSI emulation support
+CONFIG_BLK_DEV_IDESCSI
+  This will provide SCSI host adapter emulation for IDE ATAPI devices,
+  and will allow you to use a SCSI device driver instead of a native
+  ATAPI driver. This is useful if you have an ATAPI device for which
+  no native driver has been written (for example, an ATAPI PD-CD
+  drive); you can then use this emulation together with an appropriate
+  SCSI device driver. If both this SCSI emulation and native ATAPI
+  support are compiled into the kernel, the native support will be
+  used.  Normally, say N.
+
 Support removable IDE interfaces (PCMCIA)
 CONFIG_BLK_DEV_IDE_PCMCIA
   This option adds code to the IDE driver to handle hot insertion
@@ -589,10 +612,11 @@
 Intel 82371 PIIX (Triton I/II) DMA support
 CONFIG_BLK_DEV_TRITON
   If your PCI system uses an IDE harddrive (as opposed to SCSI, say)
-  and includes the Intel 430FX PCI Triton chipset, you will want to
-  enable this option to allow use of bus-mastering DMA data transfers.
-  Read the comments at the beginning of drivers/block/triton.c.  Check
-  the file Documentation/Changes for location and latest version of
+  and includes the Intel Triton I/II IDE interface chipset (i82371FB,
+  i82371SB or i82371AB), you will want to enable this option to allow
+  use of bus-mastering DMA data transfers. Read the comments at the
+  beginning of drivers/block/triton.c and Documentation/ide.txt.
+  Check the file Documentation/Changes for location and latest version of
   the hdparm utility. It is safe to say Y to this question.
 
 System V IPC
@@ -2417,19 +2441,18 @@
 
 AT&T WaveLAN & DEC RoamAbout DS support
 CONFIG_WAVELAN
-  These are cards for wireless ethernet-like networking. Supported are
-  AT&T GIS and NCR WaveLAN cards. If you want to use a card of this
-  type under Linux, say Y and read the Ethernet-HOWTO, available via
-  ftp (user: anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO. Some
-  more specific information is contained in
-  drivers/net/README.wavelan. This driver is also available as a
-  module ( = code which can be inserted in and removed from the
-  running kernel whenever you want). If you want to compile it as a
-  module, say M here and read Documentation/modules.txt as well as
-  Documentation/networking/net-modules.txt. If you plan to use more
-  than one network card under linux, read the
-  Multiple-Ethernet-mini-HOWTO, available from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+    The Lucent Wavelan (formerly NCR and AT&T ; or DEC RoamAbout DS)
+  is a Radio LAN (wireless ethernet-like) at 900 MHz and 2.4 GHz.
+    This driver support the ISA version of the Wavelan. A driver for
+  the pcmcia hardware is available in David Hinds's pcmcia package.
+    This driver is fairly stable and may be compiled as a module
+  (wavelan.o). It implements many nice feature and the Wireless
+  Extensions (you must get the Wireless tools from the net).
+    For documentation, refer to :
+      o the wavelan man page, wireless tools man pages
+      o wavelan.p.h and the source code
+      o Ethernet-HOWTO, Multiple-Ethernet-mini-HOWTO, Module-HOWTO
+      o More documentation to come when I will have the time :-)
 
 HP PCLAN+ (27247B and 27252A) support
 CONFIG_HPLAN_PLUS
diff -u --recursive --new-file v2.0.30/linux/Documentation/ide.txt linux/Documentation/ide.txt
--- v2.0.30/linux/Documentation/ide.txt	Wed Sep 25 01:12:11 1996
+++ linux/Documentation/ide.txt	Mon Aug  4 11:45:55 1997
@@ -14,6 +14,7 @@
 
 Major features of ide.c & ide-cd.c ("NEW!" marks changes since 1.2.13):
 
+NEW!	- support for IDE ATAPI *floppy* drives
 NEW!	- support for IDE ATAPI *tape* drives, courtesy of Gadi Oxman
 		(re-run MAKEDEV.ide to create the tape device entries in /dev/)
 NEW!	- support for up to *four* IDE interfaces on one or more IRQs
@@ -111,12 +112,12 @@
 Apparently many releases of Slackware 2.2/2.3 have incorrect entries
 in /dev for hdc* and hdd* -- this can also be corrected by running MAKEDEV.ide
 
-ide.c automatically probes for the primary and secondary interfaces,
+ide.c automatically probes for the standard four IDE interfaces,
 for the drives/geometries attached to those interfaces, and for the
-IRQ numbers being used by the interfaces (normally IRQ14 & IRQ15).
+IRQ numbers being used by the interfaces (normally 14, 15, 11 and 10).
 
-Interfaces beyond the first two are not normally probed for, but may be
-specified using kernel "command line" options.  For example,
+For special cases, interfaces may be specified using kernel "command line"
+options.  For example,
 
 	ide3=0x168,0x36e,10	/* ioports 0x168-0x16f,0x36e, irq 10 */
 
diff -u --recursive --new-file v2.0.30/linux/Documentation/locks.txt linux/Documentation/locks.txt
--- v2.0.30/linux/Documentation/locks.txt	Wed May 15 01:22:04 1996
+++ linux/Documentation/locks.txt	Sun Aug  3 13:59:07 1997
@@ -2,37 +2,30 @@
 
 		Andy Walker <andy@lysaker.kvaerner.no>
 
-			    15 May 1996
+			    12 May 1997
 
 
-What's New?
------------
+1. What's New?
+--------------
 
-Flock Emulation Warnings
-------------------------
-Many people will have noticed the ugly messages that the file locking
-code started generating with the release of kernel version 1.3.95. The
-messages look something like this:
+1.1 Broken Flock Emulation
+--------------------------
 
-    fcntl_setlk() called by process XX with broken flock() emulation
+The old flock(2) emulation in the kernel was swapped for proper BSD
+compatible flock(2) support in the 1.3.x series of kernels. With the
+release of the 2.1.x kernel series, support for the old emulation has
+been totally removed, so that we don't need to carry this baggage
+forever.
 
-This is a warning for people using older C libraries that those libraries
-are still calling the pre 1.3.x flock() emulation routines, instead of
-the real flock() system call. The old routines are quite badly broken,
-especially with respect to parent-child lock sharing, and can give bad
-results if, for example, sendmail attempts to use them.
+This should not cause problems for anybody, since everybody using a
+2.1.x kernel should have updated their C library to a suitable version
+anyway (see the file "linux/Documentation/Changes".)
 
-Fixed versions of the C libraries have been on public release for many
-months. The latest versions are 5.2.18 or 5.3.12 for ELF, and I believe
-somebody made a 4.7.6 release for people using a.out systems.
+1.2 Allow Mixed Locks Again
+---------------------------
 
-In 1.3.96 Linus decided to be lenient on the stragglers and changed the
-warning message so that the kernel will only complain five times and
-then shut up. That should make life more bearable even for people who,
-for some reason, don't want to upgrade.
-
-Sendmail Problems
------------------
+1.2.1 Typical Problems - Sendmail
+---------------------------------
 Because sendmail was unable to use the old flock() emulation, many sendmail
 installations use fcntl() instead of flock(). This is true of Slackware 3.0
 for example. This gave rise to some other subtle problems if sendmail was
@@ -42,23 +35,50 @@
 over time, or under a very heavy mail load, would eventually cause the kernel
 to lock solid with deadlocked processes.
 
-Disallow Mixed Locks
---------------------
-I have chosen the rather cruel solution of disallowing mixed locking styles
-on a given file at a given time. Attempts to lock a file with flock() when
-fcntl() locks exist, or vice versa, return with an error status of EBUSY.
-This seemed to be the only way to avoid all possible deadlock conditions,
-as flock() locks do not strictly have one owner process and so can't be
-checked for deadlocking in the usual manner.
-
-The process that created a lock with flock() might have forked multiple
-children and exited. Previously the parent process would have been marked
-as the owner of the lock, but deadlocks could just have easily occurred in
-one or more of the children, which we would not have been able to identify
-and avoid.
-
-Some programs may break (again, groan). In particular the aforementioned
-sendmail may have problems running in 'newaliases' mode. It will no longer
-deadlock though. Recompile sendmail to use flock() and your troubles will
-be over.
 
+1.2.2 The Solution
+------------------
+The solution I have chosen, after much experimentation and discussion,
+is to make flock() and fcntl() locks oblivious to each other. Both can
+exists, and neither will have any effect on the other.
+
+I wanted the two lock styles to be cooperative, but there were so many
+race and deadlock conditions that the current solution was the only
+practical one. It puts us in the same position as, for example, SunOS
+4.1.x and serveral other commercial Unices. The only OS's that support
+cooperative flock()/fcntl() are those that emulate flock() using
+fcntl(), with all the problems that implies.
+
+
+1.3 Mandatory Locking As A Mount Option
+---------------------------------------
+
+Mandatory locking, as described in 'Documentation/mandatory.txt' was prior
+to this release a general configuration option that was valid for all
+mounted filesystems. This had a number of inherent dangers, not the least
+of which was the ability to freeze an NFS server by asking it to read a
+file for which a mandatory lock existed.
+
+From this release of the kernel, mandatory locking can be turned on and off
+on a per-filesystem basis, using the mount options 'mand' and 'nomand'.
+The default is to disallow mandatory locking. The intention is that
+mandatory locking only be enabled on a local filesystem as the specific need
+arises.
+
+Until an updated version of mount(8) becomes available you may have to apply
+this patch to the mount sources (based on the version distributed with Rick
+Faiths util-linux-2.5 package):
+
+*** mount.c.orig	Sat Jun  8 09:14:31 1996
+--- mount.c	Sat Jun  8 09:13:02 1996
+***************
+*** 100,105 ****
+--- 100,107 ----
+    { "noauto",	0, MS_NOAUTO	},	/* Can  only be mounted explicitly */
+    { "user",	0, MS_USER	},	/* Allow ordinary user to mount */
+    { "nouser",	1, MS_USER	},	/* Forbid ordinary user to mount */
++   { "mand",	0, MS_MANDLOCK	},	/* Allow mandatory locks on this FS */
++   { "nomand",	1, MS_MANDLOCK	},	/* Forbid mandatory locks on this FS */
+    /* add new options here */
+  #ifdef MS_NOSUB
+    { "sub",	1, MS_NOSUB	},	/* allow submounts */
diff -u --recursive --new-file v2.0.30/linux/Documentation/mandatory.txt linux/Documentation/mandatory.txt
--- v2.0.30/linux/Documentation/mandatory.txt	Tue Apr 16 00:27:10 1996
+++ linux/Documentation/mandatory.txt	Sun Aug  3 13:59:07 1997
@@ -5,8 +5,8 @@
 			   15 April 1996
 
 
-What is  mandatory locking?
----------------------------
+1. What is  mandatory locking?
+------------------------------
 
 Mandatory locking is kernel enforced file locking, as opposed to the more usual
 cooperative file locking used to guarantee sequential access to files among
@@ -44,8 +44,8 @@
 borrowing the fcntl() locking scheme from System V. The mandatory locking
 scheme is defined by the System V Interface Definition (SVID) Version 3.
 
-Marking a file for mandatory locking
-------------------------------------
+2. Marking a file for mandatory locking
+---------------------------------------
 
 A file is marked as a candidate for mandatory by setting the group-id bit in
 its file mode but removing the group-execute bit. This is an otherwise
@@ -58,8 +58,8 @@
 refrain from clearing this bit. Similarly the kernel has been modified not
 to run mandatory lock candidates with setgid privileges.
 
-Available implementations
--------------------------
+3. Available implementations
+----------------------------
 
 I have considered the implementations of mandatory locking available with
 SunOS 4.1.x, Solaris 2.x and HP-UX 9.x.
@@ -93,8 +93,8 @@
 below are just as valid as any others, so long as the main points seem to
 agree. 
 
-Semantics
----------
+4. Semantics
+------------
 
 1. Mandatory locks can only be applied via the fcntl()/lockf() locking
    interface - in other words the System V/POSIX interface. BSD style
@@ -124,8 +124,8 @@
    that has any mandatory locks in effect will be rejected with the error status
    EAGAIN.
 
-Which system calls are affected?
---------------------------------
+5. Which system calls are affected?
+-----------------------------------
 
 Those which modify a file's contents, not just the inode. That gives read(),
 write(), readv(), writev(), open(), creat(), mmap(), truncate() and
@@ -142,8 +142,8 @@
 checking in my eagerness to get this code out the door. Please let me know, or
 better still fix the system calls yourself and submit a patch to me or Linus.
 
-Warning!
---------
+6. Warning!
+-----------
 
 Not even root can override a mandatory lock, so runaway process can wreak
 havoc if they lock crucial files. The way around it is to change the file
diff -u --recursive --new-file v2.0.30/linux/MAINTAINERS linux/MAINTAINERS
--- v2.0.30/linux/MAINTAINERS	Tue Apr  8 08:47:45 1997
+++ linux/MAINTAINERS	Mon Aug  4 11:45:55 1997
@@ -164,6 +164,12 @@
 L:	linux-eata@i-connect.net, linux-scsi@vger.rutgers.edu
 S:	Maintained
 
+FILE LOCKING (flock() and fcntl()/lockf())
+P:	Andy Walker
+M:	andy@lysaker.kvaerner.no
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 FRAME RELAY DLCI/FRAD (Sangoma drivers too)
 P:	Mike McLagan
 M:	mike.mclagan@linux.org
@@ -206,6 +212,12 @@
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
+IDE/ATAPI TAPE/FLOPPY DRIVERS
+P:	Gadi Oxman
+M:	Gadi Oxman <gadio@netvision.net.il>
+L:	linux-kernel@vger.rutgers.edu
+S:	Maintained
+
 ISDN SUBSYSTEM
 P:	Fritz Elfert
 M:	fritz@wuemaus.franken.de
@@ -318,7 +330,7 @@
 
 MENUCONFIG:
 P:	William Roadcap
-M:	roadcapw@cfw.com
+M:	roadcapw@titus.org
 L:	linux-kernel@vger.rutgers.edu
 S:	Maintained
 
diff -u --recursive --new-file v2.0.30/linux/Makefile linux/Makefile
--- v2.0.30/linux/Makefile	Mon Mar 17 14:58:22 1997
+++ linux/Makefile	Mon Aug  4 15:23:02 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 0
-SUBLEVEL = 30
+SUBLEVEL = 31
 
 ARCH = i386
 
@@ -319,7 +319,6 @@
 mrproper: clean
 	rm -f include/linux/autoconf.h include/linux/version.h
 	rm -f drivers/sound/local.h drivers/sound/.defines
-	rm -f drivers/scsi/aic7xxx_asm drivers/scsi/aic7xxx_seq.h
 	rm -f drivers/char/uni_hash.tbl drivers/char/conmakehash
 	rm -f .version .config* config.in config.old
 	rm -f scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp
diff -u --recursive --new-file v2.0.30/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
--- v2.0.30/linux/arch/alpha/defconfig	Tue Oct 29 17:42:40 1996
+++ linux/arch/alpha/defconfig	Sun Aug  3 13:59:07 1997
@@ -179,7 +179,6 @@
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-# CONFIG_LOCK_MANDATORY is not set
 # CONFIG_MINIX_FS is not set
 # CONFIG_EXT_FS is not set
 CONFIG_EXT2_FS=y
diff -u --recursive --new-file v2.0.30/linux/arch/alpha/kernel/osf_sys.c linux/arch/alpha/kernel/osf_sys.c
--- v2.0.30/linux/arch/alpha/kernel/osf_sys.c	Tue Aug 20 23:18:07 1996
+++ linux/arch/alpha/kernel/osf_sys.c	Sun Aug  3 10:58:38 1997
@@ -714,29 +714,25 @@
 osf_getsysinfo (unsigned long op, void * buffer, unsigned long nbytes,
 		int * start, void *arg)
 {
-    extern unsigned long rdfpcr (void);
-    unsigned long fpcw;
-
-    switch (op) {
-      case 45:	/* GSI_IEEE_FP_CONTROL */
-	  /* build and return current fp control word: */
-	  fpcw = current->tss.flags & IEEE_TRAP_ENABLE_MASK;
-	  fpcw |= ((rdfpcr() >> 52) << 17) & IEEE_STATUS_MASK;
-	  put_user(fpcw, (unsigned long *) buffer);
-	  return 0;
-
-      case 46:	/* GSI_IEEE_STATE_AT_SIGNAL */
-	  /*
-	   * Not sure anybody will ever use this weird stuff.  These
-	   * ops can be used (under OSF/1) to set the fpcr that should
-	   * be used when a signal handler starts executing.
-	   */
-	  break;
-
-      default:
-	  break;
-    }
-    return -EOPNOTSUPP;
+	switch (op) {
+	case 45:	/* GSI_IEEE_FP_CONTROL */
+		/* Return current sw control & status bits.  */
+		put_user(current->tss.flags & IEEE_SW_MASK,
+			 (unsigned long *)buffer);
+		return 0;
+
+	case 46:	/* GSI_IEEE_STATE_AT_SIGNAL */
+		/*
+		 * Not sure anybody will ever use this weird stuff.  These
+		 * ops can be used (under OSF/1) to set the fpcr that should
+		 * be used when a signal handler starts executing.
+		 */
+		break;
+
+	default:
+		break;
+	}
+	return -EOPNOTSUPP;
 }
 
 
@@ -744,25 +740,43 @@
 osf_setsysinfo (unsigned long op, void * buffer, unsigned long nbytes,
 		int * start, void *arg)
 {
-    unsigned long fpcw;
-
-    switch (op) {
-      case 14:	/* SSI_IEEE_FP_CONTROL */
-	  /* update trap enable bits: */
-	  fpcw = get_user((unsigned long *) buffer);
-	  current->tss.flags &= ~IEEE_TRAP_ENABLE_MASK;
-	  current->tss.flags |= (fpcw & IEEE_TRAP_ENABLE_MASK);
-	  return 0;
-
-      case 15:	/* SSI_IEEE_STATE_AT_SIGNAL */
-      case 16:	/* SSI_IEEE_IGNORE_STATE_AT_SIGNAL */
-	  /*
-	   * Not sure anybody will ever use this weird stuff.  These
-	   * ops can be used (under OSF/1) to set the fpcr that should
-	   * be used when a signal handler starts executing.
-	   */
-      default:
-	  break;
-    }
-    return -EOPNOTSUPP;
+	switch (op) {
+	case 14: {	/* SSI_IEEE_FP_CONTROL */
+		unsigned long sw, fpcw;
+
+		/*
+		 * Alpha Architecture Handbook 4.7.7.3:
+		 * To be fully IEEE compiant, we must track the current IEEE
+		 * exception state in software, because spurrious bits can be
+		 * set in the trap shadow of a software-complete insn.
+		 */
+
+		/* Update software trap enable bits.  */
+		sw = get_user((unsigned long *) buffer) & IEEE_SW_MASK;
+		current->tss.flags &= ~IEEE_SW_MASK;
+		current->tss.flags |= sw & IEEE_SW_MASK;
+
+		/* Update the real fpcr.  For exceptions that are disabled,
+		   but that we have seen, turn off exceptions in h/w.
+		   Otherwise leave them enabled so that we can update our
+		   software status mask.  */
+		fpcw = rdfpcr() & (~FPCR_MASK | FPCR_DYN_MASK);
+		fpcw |= ieee_sw_to_fpcr(sw | ((~sw & IEEE_STATUS_MASK) >> 16));
+		wrfpcr(fpcw);
+		return 0;
+	}
+
+	case 15:	/* SSI_IEEE_STATE_AT_SIGNAL */
+	case 16:	/* SSI_IEEE_IGNORE_STATE_AT_SIGNAL */
+		/*
+		 * Not sure anybody will ever use this weird stuff.  These
+		 * ops can be used (under OSF/1) to set the fpcr that should
+		 * be used when a signal handler starts executing.
+		 */
+		break;
+
+	default:
+		break;
+	}
+	return -EOPNOTSUPP;
 }
diff -u --recursive --new-file v2.0.30/linux/arch/alpha/kernel/ptrace.c linux/arch/alpha/kernel/ptrace.c
--- v2.0.30/linux/arch/alpha/kernel/ptrace.c	Wed Sep 11 07:57:13 1996
+++ linux/arch/alpha/kernel/ptrace.c	Mon Aug  4 15:41:59 1997
@@ -641,7 +641,7 @@
 		return;
 	current->exit_code = SIGTRAP;
 	current->state = TASK_STOPPED;
-	notify_parent(current);
+	notify_parent(current, SIGCHLD);
 	schedule();
 	/*
 	 * this isn't the same as continuing with a signal, but it will do
diff -u --recursive --new-file v2.0.30/linux/arch/alpha/kernel/signal.c linux/arch/alpha/kernel/signal.c
--- v2.0.30/linux/arch/alpha/kernel/signal.c	Tue Aug 20 23:18:07 1996
+++ linux/arch/alpha/kernel/signal.c	Mon Aug  4 15:41:48 1997
@@ -291,7 +291,7 @@
 		if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
 			current->exit_code = signr;
 			current->state = TASK_STOPPED;
-			notify_parent(current);
+			notify_parent(current, SIGCHLD);
 			schedule();
 			single_stepping |= ptrace_cancel_bpt(current);
 			if (!(signr = current->exit_code))
@@ -330,7 +330,7 @@
 				current->exit_code = signr;
 				if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & 
 						SA_NOCLDSTOP))
-					notify_parent(current);
+					notify_parent(current, SIGCHLD);
 				schedule();
 				single_stepping |= ptrace_cancel_bpt(current);
 				continue;
diff -u --recursive --new-file v2.0.30/linux/arch/alpha/math-emu/fp-emul.c linux/arch/alpha/math-emu/fp-emul.c
--- v2.0.30/linux/arch/alpha/math-emu/fp-emul.c	Thu Apr 11 23:49:30 1996
+++ linux/arch/alpha/math-emu/fp-emul.c	Sun Aug  3 10:58:38 1997
@@ -298,19 +298,29 @@
 	 *
 	 *	- Set the appropriate bits in the FPCR
 	 *	- If the specified exception is enabled in the FPCR,
-	 *	  return.  The caller (mxr_signal_handler) will dispatch
+	 *	  return.  The caller (entArith) will dispatch
 	 *	  the appropriate signal to the translated program.
+	 *
+	 * In addition, properly track the exception state in software
+	 * as described in Alpha Architecture Handbook 4.7.7.3.
 	 */
 	if (res) {
-		fpcr |= FPCR_SUM | res;
-		wrfpcr(fpcr);
-		if (((res & FPCR_INV) && (fpcw & IEEE_TRAP_ENABLE_INV)) ||
-		    ((res & FPCR_DZE) && (fpcw & IEEE_TRAP_ENABLE_DZE)) ||
-		    ((res & FPCR_OVF) && (fpcw & IEEE_TRAP_ENABLE_OVF)) ||
-		    ((res & FPCR_UNF) && (fpcw & IEEE_TRAP_ENABLE_UNF)) ||
-		    ((res & FPCR_INE) && (fpcw & IEEE_TRAP_ENABLE_INE)))
+		/* record exceptions in software control word.  */
+		fpcw |= res >> 35;
+		current->tss.flags = fpcw;
+
+		/* update hardware control register, disabling hardware
+		   exceptions for disabled software exceptions for which
+		   we have a status.  (no, really.)  */
+		fpcr &= (~FPCR_MASK | FPCR_DYN_MASK);
+		fpcr |= ieee_sw_to_fpcr(fpcw | (~fpcw & IEEE_STATUS_MASK)>>16);
+                wrfpcr(fpcr);
+
+		/* Do we generate a signal?  */
+		if (res >> 51 & fpcw & IEEE_TRAP_ENABLE_MASK)
 			return 0;
 	}
+
 	/*
 	 * Whoo-kay... we got this far, and we're not generating a signal
 	 * to the translated program.  All that remains is to write the
diff -u --recursive --new-file v2.0.30/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.0.30/linux/arch/i386/defconfig	Tue Oct 29 17:42:40 1996
+++ linux/arch/i386/defconfig	Mon Aug  4 14:59:17 1997
@@ -42,11 +42,13 @@
 # CONFIG_BLK_DEV_HD_IDE is not set
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_BLK_DEV_IDE_PCMCIA is not set
 CONFIG_BLK_DEV_CMD640=y
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 CONFIG_BLK_DEV_RZ1000=y
-# CONFIG_BLK_DEV_TRITON is not set
+CONFIG_BLK_DEV_TRITON=y
 # CONFIG_IDE_CHIPSETS is not set
 
 #
@@ -66,7 +68,10 @@
 CONFIG_INET=y
 # CONFIG_IP_FORWARD is not set
 # CONFIG_IP_MULTICAST is not set
+# CONFIG_SYN_COOKIES is not set
 # CONFIG_IP_ACCT is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
 
 #
 # (it is safe to leave these untouched)
@@ -129,7 +134,6 @@
 # Filesystems
 #
 # CONFIG_QUOTA is not set
-# CONFIG_LOCK_MANDATORY is not set
 CONFIG_MINIX_FS=y
 # CONFIG_EXT_FS is not set
 CONFIG_EXT2_FS=y
diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/bios32.c linux/arch/i386/kernel/bios32.c
--- v2.0.30/linux/arch/i386/kernel/bios32.c	Tue Apr  8 08:47:45 1997
+++ linux/arch/i386/kernel/bios32.c	Mon Aug  4 08:38:56 1997
@@ -1,6 +1,8 @@
 /*
  * bios32.c - BIOS32, PCI BIOS functions.
  *
+ * $Id: bios32.c,v 1.3.2.4 1997/08/02 22:24:23 mj Exp $
+ *
  * Sponsored by
  *	iX Multiuser Multitasking Magazine
  *	Hannover, Germany
@@ -52,6 +54,13 @@
  * Feb 3, 1997  : Set internal functions to static, save/restore flags
  *	avoid dead locks reading broken PCI BIOS, werner@suse.de 
  *
+ * Apr 26, 1997 : Fixed case when there is BIOS32, but not PCI BIOS
+ *	(mj@atrey.karlin.mff.cuni.cz)
+ *
+ * May 7,  1997 : Added some missing cli()'s. [mj]
+ * 
+ * Jun 20, 1997 : Corrected problems in "conf1" type accesses.
+ *      (paubert@iram.es)
  */
 
 #include <linux/config.h>
@@ -156,7 +165,7 @@
 	unsigned long entry;		/* %edx */
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%edi)"
 		: "=a" (return_code),
 		  "=b" (address),
@@ -171,10 +180,10 @@
 		case 0:
 			return address + entry;
 		case 0x80:	/* Not present */
-			printk("bios32_service(%ld) : not present\n", service);
+			printk("bios32_service(0x%lx) : not present\n", service);
 			return 0;
 		default: /* Shouldn't happen */
-			printk("bios32_service(%ld) : returned 0x%x, mail drew@colorado.edu\n",
+			printk("bios32_service(0x%lx) : returned 0x%x, mail drew@colorado.edu\n",
 				service, return_code);
 			return 0;
 	}
@@ -187,7 +196,7 @@
 } pci_indirect = { 0, KERNEL_CS };
 
 
-extern unsigned long check_pcibios(unsigned long memory_start, unsigned long memory_end)
+static int check_pcibios(void)
 {
 	unsigned long signature;
 	unsigned char present_status;
@@ -199,7 +208,7 @@
 	if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
 		pci_indirect.address = pcibios_entry;
 
-		save_flags(flags);
+		save_flags(flags); cli();
 		__asm__("lcall (%%edi)\n\t"
 			"jc 1f\n\t"
 			"xor %%ah, %%ah\n"
@@ -230,9 +239,10 @@
 		if (pcibios_entry) {
 			printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n",
 				major_revision, minor_revision, pcibios_entry);
+			return 1;
 		}
 	}
-	return memory_start;
+	return 0;
 }
 
 
@@ -243,7 +253,7 @@
 	unsigned long ret;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__ ("lcall (%%edi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -268,7 +278,7 @@
 	unsigned short ret;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%edi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -293,7 +303,7 @@
 	unsigned long bx = (bus << 8) | device_fn;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -315,7 +325,7 @@
 	unsigned long bx = (bus << 8) | device_fn;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -337,7 +347,7 @@
 	unsigned long bx = (bus << 8) | device_fn;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -359,7 +369,7 @@
 	unsigned long bx = (bus << 8) | device_fn;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -381,7 +391,7 @@
 	unsigned long bx = (bus << 8) | device_fn;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -403,7 +413,7 @@
 	unsigned long bx = (bus << 8) | device_fn;
 	unsigned long flags;
 
-	save_flags(flags);
+	save_flags(flags); cli();
 	__asm__("lcall (%%esi)\n\t"
 		"jc 1f\n\t"
 		"xor %%ah, %%ah\n"
@@ -444,21 +454,17 @@
 {
     unsigned int curr = 0;
     struct pci_dev *dev;
-    unsigned long flags;
 
-    save_flags(flags);
     for (dev = pci_devices; dev; dev = dev->next) {
 	if (dev->vendor == vendor && dev->device == device_id) {
 	    if (curr == index) {
 		*devfn = dev->devfn;
 		*bus = dev->bus->number;
-		restore_flags(flags);
 		return PCIBIOS_SUCCESSFUL;
 	    }
 	    ++curr;
 	}
     }
-    restore_flags(flags);
     return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
@@ -472,21 +478,17 @@
 {
     unsigned int curr = 0;
     struct pci_dev *dev;
-    unsigned long flags;
 
-    save_flags(flags);
     for (dev = pci_devices; dev; dev = dev->next) {
 	if (dev->class == class_code) {
 	    if (curr == index) {
 		*devfn = dev->devfn;
 		*bus = dev->bus->number;
-		restore_flags(flags);
 		return PCIBIOS_SUCCESSFUL;
 	    }
 	    ++curr;
 	}
     }
-    restore_flags(flags);
     return PCIBIOS_DEVICE_NOT_FOUND;
 }
 
@@ -500,18 +502,9 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
-    switch (where & 3) {
-	case 0: *value = inb(0xCFC);
-		break;
-	case 1: *value = inb(0xCFD);
-		break;
-	case 2: *value = inb(0xCFE);
-		break;
-	case 3: *value = inb(0xCFF);
-		break;
-    }
+    *value = inb(0xCFC + (where&3));
     restore_flags(flags);
     return PCIBIOS_SUCCESSFUL;
 }
@@ -521,12 +514,10 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
-    if (where & 2)
-    	*value = inw(0xCFE);
-    else
-    	*value = inw(0xCFC);
+    *value = inw(0xCFC + (where&2));
     restore_flags(flags);
     return PCIBIOS_SUCCESSFUL;    
 }
@@ -536,7 +527,8 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
     *value = inl(0xCFC);
     restore_flags(flags);
@@ -548,9 +540,9 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
-    outb(value, 0xCFC);
+    outb(value, 0xCFC + (where&3));
     restore_flags(flags);
     return PCIBIOS_SUCCESSFUL;
 }
@@ -560,9 +552,10 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    if (where&1) return PCIBIOS_BAD_REGISTER_NUMBER;
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
-    outw(value, 0xCFC);
+    outw(value, 0xCFC + (where&2));
     restore_flags(flags);
     return PCIBIOS_SUCCESSFUL;
 }
@@ -572,7 +565,8 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    if (where&3) return PCIBIOS_BAD_REGISTER_NUMBER;
+    save_flags(flags); cli();
     outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
     outl(value, 0xCFC);
     restore_flags(flags);
@@ -608,7 +602,7 @@
 
     if (device_fn & 0x80)
 	return PCIBIOS_DEVICE_NOT_FOUND;
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     *value = inb(IOADDR(device_fn,where));
@@ -624,7 +618,7 @@
 
     if (device_fn & 0x80)
 	return PCIBIOS_DEVICE_NOT_FOUND;
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     *value = inw(IOADDR(device_fn,where));
@@ -640,7 +634,7 @@
 
     if (device_fn & 0x80)
 	return PCIBIOS_DEVICE_NOT_FOUND;
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     *value = inl (IOADDR(device_fn,where));    
@@ -654,7 +648,7 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     outb (value, IOADDR(device_fn,where));
@@ -668,7 +662,7 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     outw (value, IOADDR(device_fn,where));
@@ -682,7 +676,7 @@
 {
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
     outb (FUNC(device_fn), 0xCF8);
     outb (bus, 0xCFA);
     outl (value, IOADDR(device_fn,where));    
@@ -714,7 +708,7 @@
     unsigned int tmp;
     unsigned long flags;
 
-    save_flags(flags);
+    save_flags(flags); cli();
 
     /*
      * check if configuration type 1 works
@@ -736,7 +730,7 @@
     outb (0x00, 0xCFB);
     outb (0x00, 0xCF8);
     outb (0x00, 0xCFA);
-    if (inb (0xCF8) == 0x00 && inb (0xCFC) == 0x00) {
+    if (inb (0xCF8) == 0x00 && inb (0xCFB) == 0x00) {
 	restore_flags(flags);
 	printk("pcibios_init: Using configuration type 2\n");
 	return &pci_direct_conf2;
@@ -883,7 +877,9 @@
 	 *
 	 */
 
-	for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) {
+	for (check = (union bios32 *) 0xe0000;
+	     check <= (union bios32 *) 0xffff0;
+	     ++check) {
 		if (check->fields.signature != BIOS32_SIGNATURE)
 			continue;
 		length = check->fields.length * 16;
@@ -908,13 +904,11 @@
 				bios32_entry = check->fields.entry;
 				printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
 				bios32_indirect.address = bios32_entry;
-				access_pci = &pci_bios_access;
 			}
 		}
 	}
-	if (bios32_entry) {
-		memory_start = check_pcibios (memory_start, memory_end);
-	}
+	if (bios32_entry && check_pcibios())
+ 		access_pci = &pci_bios_access;
 #endif
 	return memory_start;
 }
diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/ptrace.c linux/arch/i386/kernel/ptrace.c
--- v2.0.30/linux/arch/i386/kernel/ptrace.c	Wed Sep 11 07:57:13 1996
+++ linux/arch/i386/kernel/ptrace.c	Mon Aug  4 12:12:22 1997
@@ -535,7 +535,7 @@
 		return;
 	current->exit_code = SIGTRAP;
 	current->state = TASK_STOPPED;
-	notify_parent(current);
+	notify_parent(current, SIGCHLD);
 	schedule();
 	/*
 	 * this isn't the same as continuing with a signal, but it will do
diff -u --recursive --new-file v2.0.30/linux/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c
--- v2.0.30/linux/arch/i386/kernel/signal.c	Wed Dec 11 06:41:01 1996
+++ linux/arch/i386/kernel/signal.c	Mon Aug  4 12:12:51 1997
@@ -286,7 +286,7 @@
 		if ((current->flags & PF_PTRACED) && signr != SIGKILL) {
 			current->exit_code = signr;
 			current->state = TASK_STOPPED;
-			notify_parent(current);
+			notify_parent(current, SIGCHLD);
 			schedule();
 			if (!(signr = current->exit_code))
 				continue;
@@ -324,7 +324,7 @@
 				current->exit_code = signr;
 				if (!(current->p_pptr->sig->action[SIGCHLD-1].sa_flags & 
 						SA_NOCLDSTOP))
-					notify_parent(current);
+					notify_parent(current, SIGCHLD);
 				schedule();
 				continue;
 
diff -u --recursive --new-file v2.0.30/linux/drivers/block/Config.in linux/drivers/block/Config.in
--- v2.0.30/linux/drivers/block/Config.in	Mon Aug  5 00:13:50 1996
+++ linux/drivers/block/Config.in	Mon Aug  4 11:45:55 1997
@@ -5,7 +5,7 @@
 comment 'Floppy, IDE, and other block devices'
 
 tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
-bool 'Enhanced IDE/MFM/RLL disk/cdrom/tape support' CONFIG_BLK_DEV_IDE
+bool 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE
 comment 'Please see Documentation/ide.txt for help/info on IDE drives'
 if [ "$CONFIG_BLK_DEV_IDE" = "n" ]; then
   bool 'Old harddisk (MFM/RLL/IDE) driver' CONFIG_BLK_DEV_HD_ONLY
@@ -13,6 +13,8 @@
   bool '   Use old disk-only driver on primary interface' CONFIG_BLK_DEV_HD_IDE
   bool '   Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD
   bool '   Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE
+  bool '   Include IDE/ATAPI FLOPPY support (new)' CONFIG_BLK_DEV_IDEFLOPPY
+  bool '   SCSI emulation support' CONFIG_BLK_DEV_IDESCSI
   bool '   Support removable IDE interfaces (PCMCIA)' CONFIG_BLK_DEV_IDE_PCMCIA
   bool '   CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640
   if [ "$CONFIG_BLK_DEV_CMD640" = "y" ]; then
diff -u --recursive --new-file v2.0.30/linux/drivers/block/Makefile linux/drivers/block/Makefile
--- v2.0.30/linux/drivers/block/Makefile	Sat Aug 10 00:03:14 1996
+++ linux/drivers/block/Makefile	Mon Aug  4 11:45:55 1997
@@ -97,6 +97,10 @@
 L_OBJS += ide-tape.o
 endif
 
+ifeq ($(CONFIG_BLK_DEV_IDEFLOPPY),y)
+L_OBJS += ide-floppy.o
+endif
+
 ifeq ($(CONFIG_BLK_DEV_XD),y)
 L_OBJS += xd.o
 else
diff -u --recursive --new-file v2.0.30/linux/drivers/block/genhd.c linux/drivers/block/genhd.c
--- v2.0.30/linux/drivers/block/genhd.c	Tue Aug 20 23:18:07 1996
+++ linux/drivers/block/genhd.c	Mon Aug  4 11:45:55 1997
@@ -331,7 +331,7 @@
 				   && (q->sector & 63) == 1
 				   && (q->end_sector & 63) == 63) {
 					unsigned int heads = q->end_head + 1;
-					if (heads == 32 || heads == 64 || heads == 128) {
+					if (heads == 32 || heads == 64 || heads == 128 || heads == 255) {
 
 						(void) ide_xlate_1024(dev, heads, " [PTBL]");
 						break;
diff -u --recursive --new-file v2.0.30/linux/drivers/block/ide-cd.c linux/drivers/block/ide-cd.c
--- v2.0.30/linux/drivers/block/ide-cd.c	Tue Mar 11 13:28:36 1997
+++ linux/drivers/block/ide-cd.c	Mon Aug  4 11:45:55 1997
@@ -125,7 +125,8 @@
  *                          you must define IHAVEADOLPHIN)
  *                        Added identifier so new Sanyo CD-changer works
  *                        Better detection if door locking isn't supported 
- *                         
+ * 3.21  Jun 16,1997  -- Add work-around for GCD-R580B
+ *
  * NOTE: Direct audio reads will only work on some types of drive.
  * So far, i've received reports of success for Sony and Toshiba drives.
  *
@@ -1141,6 +1142,11 @@
 	/* Number of sectors to transfer. */
 	nsect = rq->nr_sectors;
 
+#if !STANDARD_ATAPI
+	if (nsect > drive->cdrom_info.max_sectors)
+		nsect = drive->cdrom_info.max_sectors;
+#endif /* not STANDARD_ATAPI */
+
 	/* Starting sector. */
 	sector = rq->sector;
 
@@ -2674,6 +2680,8 @@
 		CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
 
 #if ! STANDARD_ATAPI
+	drive->cdrom_info.max_sectors = 252;
+
 	CDROM_CONFIG_FLAGS (drive)->old_readcd = 0;
 	CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0;
 	CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 0;
@@ -2698,6 +2706,9 @@
 			/* Vertos 600 ESD. */
 			CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1;
 		}
+
+		else if (strcmp (drive->id->model, "GCD-R580B") == 0)
+			drive->cdrom_info.max_sectors = 124;
 
 		else if (strcmp (drive->id->model,
 				 "NEC CD-ROM DRIVE:260") == 0 &&
diff -u --recursive --new-file v2.0.30/linux/drivers/block/ide-floppy.c linux/drivers/block/ide-floppy.c
--- v2.0.30/linux/drivers/block/ide-floppy.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/block/ide-floppy.c	Mon Aug  4 11:45:55 1997
@@ -0,0 +1,1388 @@
+/*
+ * linux/drivers/block/ide-floppy.c	Version 0.7 - ALPHA	Aug   4, 1997
+ *
+ * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+/*
+ * IDE ATAPI floppy driver.
+ *
+ * The driver currently doesn't have any fancy features, just the bare
+ * minimum read/write support.
+ *
+ * Many thanks to Lode Leroy <Lode.Leroy@www.ibase.be>, who tested so many
+ * ALPHA patches to this driver on an EASYSTOR LS-120 ATAPI floppy drive.
+ *
+ * Ver 0.1   Oct 17 96   Initial test version, mostly based on ide-tape.c.
+ * Ver 0.2   Oct 31 96   Minor changes.
+ * Ver 0.3   Dec  2 96   Fixed error recovery bug.
+ * Ver 0.4   Jan 26 97   Add support for the HDIO_GETGEO ioctl.
+ * Ver 0.5   Feb 21 97   Add partitions support.
+ *                       Use the minimum of the LBA and CHS capacities.
+ *                       Avoid hwgroup->rq == NULL on the last irq.
+ *                       Fix potential null dereferencing with DEBUG_LOG.
+ * Ver 0.6   Jul  3 97   Limit max sectors per read/write command to 64.
+ * Ver 0.7   Aug  4 97   Increase irq timeout from 10 to 100 seconds.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/unaligned.h>
+#include <asm/bitops.h>
+
+/*
+ *	Main Linux ide driver include file
+ */
+#include "ide.h"
+
+/*
+ *	The following are used to debug the driver.
+ */
+#define IDEFLOPPY_DEBUG_LOG		0
+#define IDEFLOPPY_DEBUG_INFO		0
+#define IDEFLOPPY_DEBUG_BUGS		1
+
+/*
+ *	After each failed packet command we issue a request sense command
+ *	and retry the packet command IDEFLOPPY_MAX_PC_RETRIES times.
+ */
+#define IDEFLOPPY_MAX_PC_RETRIES	3
+
+/*
+ *	With each packet command, we allocate a buffer of
+ *	IDEFLOPPY_PC_BUFFER_SIZE bytes.
+ */
+#define IDEFLOPPY_PC_BUFFER_SIZE	256
+
+/*
+ *	In various places in the driver, we need to allocate storage
+ *	for packet commands and requests, which will remain valid while
+ *	we leave the driver to wait for an interrupt or a timeout event.
+ */
+#define IDEFLOPPY_PC_STACK		(10 + IDEFLOPPY_MAX_PC_RETRIES)
+
+/*
+ *	Some drives fail read/write requests with 64 or more sectors.
+ */
+#define IDEFLOPPY_MAX_SECTORS		64
+
+/*
+ *	Some drives require a longer irq timeout.
+ */
+#define IDEFLOPPY_WAIT_CMD		(10 * WAIT_CMD)
+
+/*
+ *	Our view of a packet command.
+ */
+typedef struct idefloppy_packet_command_s {
+	u8 c[12];				/* Actual packet bytes */
+	int retries;				/* On each retry, we increment retries */
+	int error;				/* Error code */
+	int request_transfer;			/* Bytes to transfer */
+	int actually_transferred;		/* Bytes actually transferred */
+	int buffer_size;			/* Size of our data buffer */
+	char *b_data;				/* Pointer which runs on the buffers */
+	int b_count;				/* Missing/Available data on the current buffer */
+	struct request *rq;			/* The corresponding request */
+	byte *buffer;				/* Data buffer */
+	byte *current_position;			/* Pointer into the above buffer */
+	void (*callback) (ide_drive_t *);	/* Called when this packet command is completed */
+	byte pc_buffer[IDEFLOPPY_PC_BUFFER_SIZE];	/* Temporary buffer */
+	unsigned int flags;			/* Status/Action bit flags */
+} idefloppy_pc_t;
+
+/*
+ *	Packet command flag bits.
+ */
+#define	PC_ABORT			0	/* Set when an error is considered normal - We won't retry */
+#define PC_DMA_RECOMMENDED		2	/* 1 when we prefer to use DMA if possible */
+#define	PC_DMA_IN_PROGRESS		3	/* 1 while DMA in progress */
+#define	PC_DMA_ERROR			4	/* 1 when encountered problem during DMA */
+#define	PC_WRITING			5	/* Data direction */
+
+/*
+ *	Removable Block Access Capabilities Page
+ */
+typedef struct {
+	unsigned	page_code	:6;	/* Page code - Should be 0x1b */
+	unsigned	reserved1_6	:1;	/* Reserved */
+	unsigned	ps		:1;	/* Should be 0 */
+	u8		page_length;		/* Page Length - Should be 0xa */
+	unsigned	reserved2	:6;
+	unsigned	srfp		:1;	/* Supports reporting progress of format */
+	unsigned	sflp		:1;	/* System floppy type device */
+	unsigned	tlun		:3;	/* Total logical units supported by the device */
+	unsigned	reserved3	:3;
+	unsigned	sml		:1;	/* Single / Multiple lun supported */
+	unsigned	ncd		:1;	/* Non cd optical device */
+	u8		reserved[8];
+} idefloppy_capabilities_page_t;
+
+/*
+ *	Flexible disk page.
+ */
+typedef struct {
+	unsigned	page_code	:6;	/* Page code - Should be 0x5 */
+	unsigned	reserved1_6	:1;	/* Reserved */
+	unsigned	ps		:1;	/* The device is capable of saving the page */
+	u8		page_length;		/* Page Length - Should be 0x1e */
+	u16		transfer_rate;		/* In kilobits per second */
+	u8		heads, sectors;		/* Number of heads, Number of sectors per track */
+	u16		sector_size;		/* Byes per sector */
+	u16		cyls;			/* Number of cylinders */
+	u8		reserved10[10];
+	u8		motor_delay;		/* Motor off delay */
+	u8		reserved21[7];
+	u16		rpm;			/* Rotations per minute */
+	u8		reserved30[2];
+} idefloppy_flexible_disk_page_t;
+ 
+/*
+ *	Format capacity
+ */
+typedef struct {
+	u8		reserved[3];
+	u8		length;			/* Length of the following descriptors in bytes */
+} idefloppy_capacity_header_t;
+
+typedef struct {
+	u32		blocks;			/* Number of blocks */
+	unsigned	dc		:2;	/* Descriptor Code */
+	unsigned	reserved	:6;
+	u8		length_msb;		/* Block Length (MSB)*/
+	u16		length;			/* Block Length */
+} idefloppy_capacity_descriptor_t;
+
+#define CAPACITY_INVALID	0x00
+#define CAPACITY_UNFORMATTED	0x01
+#define CAPACITY_CURRENT	0x02
+#define CAPACITY_NO_CARTRIDGE	0x03
+
+/*
+ *	Most of our global data which we need to save even as we leave the
+ *	driver due to an interrupt or a timer event is stored in a variable
+ *	of type idefloppy_floppy_t, defined below.
+ */
+typedef struct {
+	ide_drive_t *drive;
+
+	idefloppy_pc_t *pc;			/* Current packet command */
+	idefloppy_pc_t *failed_pc; 		/* Last failed packet command */
+	idefloppy_pc_t pc_stack[IDEFLOPPY_PC_STACK];/* Packet command stack */
+	int pc_stack_index;			/* Next free packet command storage space */
+	struct request rq_stack[IDEFLOPPY_PC_STACK];
+	int rq_stack_index;			/* We implement a circular array */
+
+	/*
+	 *	Last error information
+	 */
+	byte sense_key, asc, ascq;
+
+	/*
+	 *	Device information
+	 */
+	int blocks, block_size, bs_factor;			/* Current format */
+	idefloppy_capacity_descriptor_t capacity;		/* Last format capacity */
+	idefloppy_flexible_disk_page_t flexible_disk_page;	/* Copy of the flexible disk page */
+
+	unsigned int flags;			/* Status/Action flags */
+} idefloppy_floppy_t;
+
+/*
+ *	Floppy flag bits values.
+ */
+#define IDEFLOPPY_DRQ_INTERRUPT		0	/* DRQ interrupt device */
+#define IDEFLOPPY_MEDIA_CHANGED		1	/* Media may have changed */
+#define IDEFLOPPY_USE_READ12		2	/* Use READ12/WRITE12 or READ10/WRITE10 */
+
+/*
+ *	ATAPI floppy drive packet commands
+ */
+#define IDEFLOPPY_FORMAT_UNIT_CMD	0x04
+#define IDEFLOPPY_INQUIRY_CMD		0x12
+#define IDEFLOPPY_MODE_SELECT_CMD	0x55
+#define IDEFLOPPY_MODE_SENSE_CMD	0x5a
+#define IDEFLOPPY_READ10_CMD		0x28
+#define IDEFLOPPY_READ12_CMD		0xa8
+#define IDEFLOPPY_READ_CAPACITY_CMD	0x23
+#define IDEFLOPPY_REQUEST_SENSE_CMD	0x03
+#define IDEFLOPPY_PREVENT_REMOVAL_CMD	0x1e
+#define IDEFLOPPY_SEEK_CMD		0x2b
+#define IDEFLOPPY_START_STOP_CMD	0x1b
+#define IDEFLOPPY_TEST_UNIT_READY_CMD	0x00
+#define IDEFLOPPY_VERIFY_CMD		0x2f
+#define IDEFLOPPY_WRITE10_CMD		0x2a
+#define IDEFLOPPY_WRITE12_CMD		0xaa
+#define IDEFLOPPY_WRITE_VERIFY_CMD	0x2e
+
+/*
+ *	Defines for the mode sense command
+ */
+#define MODE_SENSE_CURRENT		0x00
+#define MODE_SENSE_CHANGEABLE		0x01
+#define MODE_SENSE_DEFAULT		0x02 
+#define MODE_SENSE_SAVED		0x03
+
+/*
+ *	Special requests for our block device strategy routine.
+ */
+#define	IDEFLOPPY_FIRST_RQ		90
+
+/*
+ * 	IDEFLOPPY_PC_RQ is used to queue a packet command in the request queue.
+ */
+#define	IDEFLOPPY_PC_RQ			90
+
+#define IDEFLOPPY_LAST_RQ		90
+
+/*
+ *	A macro which can be used to check if a given request command
+ *	originated in the driver or in the buffer cache layer.
+ */
+#define IDEFLOPPY_RQ_CMD(cmd) 		((cmd >= IDEFLOPPY_FIRST_RQ) && (cmd <= IDEFLOPPY_LAST_RQ))
+
+/*
+ *	Error codes which are returned in rq->errors to the higher part
+ *	of the driver.
+ */
+#define	IDEFLOPPY_ERROR_GENERAL		101
+
+/*
+ *	The ATAPI Status Register.
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned check		:1;	/* Error occurred */
+		unsigned idx		:1;	/* Reserved */
+		unsigned corr		:1;	/* Correctable error occurred */
+		unsigned drq		:1;	/* Data is request by the device */
+		unsigned dsc		:1;	/* Media access command finished */
+		unsigned reserved5	:1;	/* Reserved */
+		unsigned drdy		:1;	/* Ignored for ATAPI commands (ready to accept ATA command) */
+		unsigned bsy		:1;	/* The device has access to the command block */
+	} b;
+} idefloppy_status_reg_t;
+
+/*
+ *	The ATAPI error register.
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned ili		:1;	/* Illegal Length Indication */
+		unsigned eom		:1;	/* End Of Media Detected */
+		unsigned abrt		:1;	/* Aborted command - As defined by ATA */
+		unsigned mcr		:1;	/* Media Change Requested - As defined by ATA */
+		unsigned sense_key	:4;	/* Sense key of the last failed packet command */
+	} b;
+} idefloppy_error_reg_t;
+
+/*
+ *	ATAPI Feature Register
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned dma		:1;	/* Using DMA or PIO */
+		unsigned reserved321	:3;	/* Reserved */
+		unsigned reserved654	:3;	/* Reserved (Tag Type) */
+		unsigned reserved7	:1;	/* Reserved */
+	} b;
+} idefloppy_feature_reg_t;
+
+/*
+ *	ATAPI Byte Count Register.
+ */
+typedef union {
+	unsigned all			:16;
+	struct {
+		unsigned low		:8;	/* LSB */
+		unsigned high		:8;	/* MSB */
+	} b;
+} idefloppy_bcount_reg_t;
+
+/*
+ *	ATAPI Interrupt Reason Register.
+ */
+typedef union {
+	unsigned all			:8;
+	struct {
+		unsigned cod		:1;	/* Information transferred is command (1) or data (0) */
+		unsigned io		:1;	/* The device requests us to read (1) or write (0) */
+		unsigned reserved	:6;	/* Reserved */
+	} b;
+} idefloppy_ireason_reg_t;
+
+/*
+ *	ATAPI floppy Drive Select Register
+ */
+typedef union {	
+	unsigned all			:8;
+	struct {
+		unsigned sam_lun	:3;	/* Logical unit number */
+		unsigned reserved3	:1;	/* Reserved */
+		unsigned drv		:1;	/* The responding drive will be drive 0 (0) or drive 1 (1) */
+		unsigned one5		:1;	/* Should be set to 1 */
+		unsigned reserved6	:1;	/* Reserved */
+		unsigned one7		:1;	/* Should be set to 1 */
+	} b;
+} idefloppy_drivesel_reg_t;
+
+/*
+ *	ATAPI Device Control Register
+ */
+typedef union {			
+	unsigned all			:8;
+	struct {
+		unsigned zero0		:1;	/* Should be set to zero */
+		unsigned nien		:1;	/* Device interrupt is disabled (1) or enabled (0) */
+		unsigned srst		:1;	/* ATA software reset. ATAPI devices should use the new ATAPI srst. */
+		unsigned one3		:1;	/* Should be set to 1 */
+		unsigned reserved4567	:4;	/* Reserved */
+	} b;
+} idefloppy_control_reg_t;
+
+/*
+ *	The following is used to format the general configuration word of
+ *	the ATAPI IDENTIFY DEVICE command.
+ */
+struct idefloppy_id_gcw {	
+	unsigned packet_size		:2;	/* Packet Size */
+	unsigned reserved234		:3;	/* Reserved */
+	unsigned drq_type		:2;	/* Command packet DRQ type */
+	unsigned removable		:1;	/* Removable media */
+	unsigned device_type		:5;	/* Device type */
+	unsigned reserved13		:1;	/* Reserved */
+	unsigned protocol		:2;	/* Protocol type */
+};
+
+/*
+ *	INQUIRY packet command - Data Format
+ */
+typedef struct {
+	unsigned	device_type	:5;	/* Peripheral Device Type */
+	unsigned	reserved0_765	:3;	/* Peripheral Qualifier - Reserved */
+	unsigned	reserved1_6t0	:7;	/* Reserved */
+	unsigned	rmb		:1;	/* Removable Medium Bit */
+	unsigned	ansi_version	:3;	/* ANSI Version */
+	unsigned	ecma_version	:3;	/* ECMA Version */
+	unsigned	iso_version	:2;	/* ISO Version */
+	unsigned	response_format :4;	/* Response Data Format */
+	unsigned	reserved3_45	:2;	/* Reserved */
+	unsigned	reserved3_6	:1;	/* TrmIOP - Reserved */
+	unsigned	reserved3_7	:1;	/* AENC - Reserved */
+	u8		additional_length;	/* Additional Length (total_length-4) */
+	u8		rsv5, rsv6, rsv7;	/* Reserved */
+	u8		vendor_id[8];		/* Vendor Identification */
+	u8		product_id[16];		/* Product Identification */
+	u8		revision_level[4];	/* Revision Level */
+	u8		vendor_specific[20];	/* Vendor Specific - Optional */
+	u8		reserved56t95[40];	/* Reserved - Optional */
+						/* Additional information may be returned */
+} idefloppy_inquiry_result_t;
+
+/*
+ *	REQUEST SENSE packet command result - Data Format.
+ */
+typedef struct {
+	unsigned	error_code	:7;	/* Current error (0x70) */
+	unsigned	valid		:1;	/* The information field conforms to SFF-8070i */
+	u8		reserved1	:8;	/* Reserved */
+	unsigned	sense_key	:4;	/* Sense Key */
+	unsigned	reserved2_4	:1;	/* Reserved */
+	unsigned	ili		:1;	/* Incorrect Length Indicator */
+	unsigned	reserved2_67	:2;
+	u32		information __attribute__ ((packed));
+	u8		asl;			/* Additional sense length (n-7) */
+	u32		command_specific;	/* Additional command specific information */
+	u8		asc;			/* Additional Sense Code */
+	u8		ascq;			/* Additional Sense Code Qualifier */
+	u8		replaceable_unit_code;	/* Field Replaceable Unit Code */
+	u8		reserved[3];
+	u8		pad[2];			/* Padding to 20 bytes */
+} idefloppy_request_sense_result_t;
+
+/*
+ *	Pages of the SELECT SENSE / MODE SENSE packet commands.
+ */
+#define	IDEFLOPPY_CAPABILITIES_PAGE	0x1b
+#define IDEFLOPPY_FLEXIBLE_DISK_PAGE	0x05
+
+/*
+ *	Mode Parameter Header for the MODE SENSE packet command
+ */
+typedef struct {
+	u16		mode_data_length;	/* Length of the following data transfer */
+	u8		medium_type;		/* Medium Type */
+	unsigned	reserved3	:7;
+	unsigned	wp		:1;	/* Write protect */
+	u8		reserved[4];
+} idefloppy_mode_parameter_header_t;
+
+#define IDEFLOPPY_MIN(a,b)	((a)<(b) ? (a):(b))
+#define	IDEFLOPPY_MAX(a,b)	((a)>(b) ? (a):(b))
+
+/*
+ *	Too bad. The drive wants to send us data which we are not ready to accept.
+ *	Just throw it away.
+ */
+static void idefloppy_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+	while (bcount--)
+		IN_BYTE (IDE_DATA_REG);
+}
+
+#if IDEFLOPPY_DEBUG_BUGS
+static void idefloppy_write_zeros (ide_drive_t *drive, unsigned int bcount)
+{
+	while (bcount--)
+		OUT_BYTE (0, IDE_DATA_REG);
+}
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+/*
+ *	idefloppy_end_request is used to finish servicing a request.
+ *
+ *	For read/write requests, we will call ide_end_request to pass to the
+ *	next buffer.
+ */
+void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+	ide_drive_t *drive = hwgroup->drive;
+	idefloppy_floppy_t *floppy = drive->floppy;
+	struct request *rq = hwgroup->rq;
+	int error;
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "Reached idefloppy_end_request\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	switch (uptodate) {
+		case 0: error = IDEFLOPPY_ERROR_GENERAL; break;
+		case 1: error = 0; break;
+		default: error = uptodate;
+	}
+	if (error)
+		floppy->failed_pc = NULL;
+	if (!IDEFLOPPY_RQ_CMD (rq->cmd)) {
+		ide_end_request (uptodate, hwgroup);
+		return;
+	}
+	rq->errors = error;
+	ide_end_drive_cmd (drive, 0, 0);
+}
+
+static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+	struct request *rq = pc->rq;
+	struct buffer_head *bh = rq->bh;
+	int count;
+
+	while (bcount) {
+		if (pc->b_count == bh->b_size) {
+			idefloppy_end_request (1, HWGROUP(drive));
+			if ((bh = rq->bh) != NULL)
+				pc->b_count = 0;
+		}
+		if (bh == NULL) {
+			printk (KERN_ERR "%s: bh == NULL in idefloppy_input_buffers, bcount == %d\n", drive->name, bcount);
+			idefloppy_discard_data (drive, bcount);
+			return;
+		}
+		count = IDEFLOPPY_MIN (bh->b_size - pc->b_count, bcount);
+		atapi_input_bytes (drive, bh->b_data + pc->b_count, count);
+		bcount -= count; pc->b_count += count;
+		if (pc->b_count == bh->b_size) {
+			rq->sector += rq->current_nr_sectors;
+			rq->nr_sectors -= rq->current_nr_sectors;
+		}
+	}
+}
+
+static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount)
+{
+	struct request *rq = pc->rq;
+	struct buffer_head *bh = rq->bh;
+	int count;
+	
+	while (bcount) {
+		if (!pc->b_count) {
+			idefloppy_end_request (1, HWGROUP(drive));
+			if ((bh = rq->bh) != NULL) {
+				pc->b_data = bh->b_data;
+				pc->b_count = bh->b_size;
+			}
+		}
+		if (bh == NULL) {
+			printk (KERN_ERR "%s: bh == NULL in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount);
+			idefloppy_write_zeros (drive, bcount);
+			return;
+		}
+		count = IDEFLOPPY_MIN (pc->b_count, bcount);
+		atapi_output_bytes (drive, pc->b_data, count);
+		bcount -= count; pc->b_data += count; pc->b_count -= count;
+		if (!pc->b_count) {
+			rq->sector += rq->current_nr_sectors;
+			rq->nr_sectors -= rq->current_nr_sectors;
+		}
+	}
+}
+
+#ifdef CONFIG_BLK_DEV_TRITON
+static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+	struct request *rq = pc->rq;
+	struct buffer_head *bh = rq->bh;
+
+	while ((bh = rq->bh) != NULL)
+		idefloppy_end_request (1, HWGROUP(drive));
+}
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+/*
+ *	idefloppy_queue_pc_head generates a new packet command request in front
+ *	of the request queue, before the current request, so that it will be
+ *	processed immediately, on the next pass through the driver.
+ */
+static void idefloppy_queue_pc_head (ide_drive_t *drive,idefloppy_pc_t *pc,struct request *rq)
+{
+	ide_init_drive_cmd (rq);
+	rq->buffer = (char *) pc;
+	rq->cmd = IDEFLOPPY_PC_RQ;
+	(void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+static idefloppy_pc_t *idefloppy_next_pc_storage (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+
+	if (floppy->pc_stack_index==IDEFLOPPY_PC_STACK)
+		floppy->pc_stack_index=0;
+	return (&floppy->pc_stack[floppy->pc_stack_index++]);
+}
+
+static struct request *idefloppy_next_rq_storage (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+
+	if (floppy->rq_stack_index==IDEFLOPPY_PC_STACK)
+		floppy->rq_stack_index=0;
+	return (&floppy->rq_stack[floppy->rq_stack_index++]);
+}
+
+/*
+ *	idefloppy_analyze_error is called on each failed packet command retry
+ *	to analyze the request sense.
+ */
+static void idefloppy_analyze_error (ide_drive_t *drive,idefloppy_request_sense_result_t *result)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+
+	floppy->sense_key = result->sense_key; floppy->asc = result->asc; floppy->ascq = result->ascq;
+#if IDEFLOPPY_DEBUG_LOG
+	if (floppy->failed_pc)
+		printk (KERN_INFO "ide-floppy: pc = %x, sense key = %x, asc = %x, ascq = %x\n",floppy->failed_pc->c[0],result->sense_key,result->asc,result->ascq);
+	else
+		printk (KERN_INFO "ide-floppy: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+}
+
+static void idefloppy_request_sense_callback (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "ide-floppy: Reached idefloppy_request_sense_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+	if (!floppy->pc->error) {
+		idefloppy_analyze_error (drive,(idefloppy_request_sense_result_t *) floppy->pc->buffer);
+		idefloppy_end_request (1,HWGROUP (drive));
+	} else {
+		printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n");
+		idefloppy_end_request (0,HWGROUP (drive));
+	}
+}
+
+/*
+ *	General packet command callback function.
+ */
+static void idefloppy_pc_callback (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	idefloppy_end_request (floppy->pc->error ? 0:1, HWGROUP(drive));
+}
+
+/*
+ *	idefloppy_init_pc initializes a packet command.
+ */
+static void idefloppy_init_pc (idefloppy_pc_t *pc)
+{
+	memset (pc->c, 0, 12);
+	pc->retries = 0;
+	pc->flags = 0;
+	pc->request_transfer = 0;
+	pc->buffer = pc->pc_buffer;
+	pc->buffer_size = IDEFLOPPY_PC_BUFFER_SIZE;
+	pc->b_data = NULL;
+	pc->callback = &idefloppy_pc_callback;
+}
+
+static void idefloppy_create_request_sense_cmd (idefloppy_pc_t *pc)
+{
+	idefloppy_init_pc (pc);	
+	pc->c[0] = IDEFLOPPY_REQUEST_SENSE_CMD;
+	pc->c[4] = 255;
+	pc->request_transfer = 18;
+	pc->callback = &idefloppy_request_sense_callback;
+}
+
+/*
+ *	idefloppy_retry_pc is called when an error was detected during the
+ *	last packet command. We queue a request sense packet command in
+ *	the head of the request list.
+ */
+static void idefloppy_retry_pc (ide_drive_t *drive)
+{
+	idefloppy_pc_t *pc;
+	struct request *rq;
+	idefloppy_error_reg_t error;
+
+	error.all = IN_BYTE (IDE_ERROR_REG);
+	pc = idefloppy_next_pc_storage (drive);
+	rq = idefloppy_next_rq_storage (drive);
+	idefloppy_create_request_sense_cmd (pc);
+	idefloppy_queue_pc_head (drive, pc, rq);
+}
+
+/*
+ *	idefloppy_pc_intr is the usual interrupt handler which will be called
+ *	during a packet command.
+ */
+static void idefloppy_pc_intr (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_status_reg_t status;
+	idefloppy_bcount_reg_t bcount;
+	idefloppy_ireason_reg_t ireason;
+	idefloppy_pc_t *pc=floppy->pc;
+	struct request *rq = pc->rq;
+	unsigned int temp;
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_intr interrupt handler\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */	
+
+#ifdef CONFIG_BLK_DEV_TRITON
+	if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+		if (HWIF(drive)->dmaproc(ide_dma_status_bad, drive)) {
+			set_bit (PC_DMA_ERROR, &pc->flags);
+		} else {
+			pc->actually_transferred=pc->request_transfer;
+			idefloppy_update_buffers (drive, pc);
+		}
+		(void) (HWIF(drive)->dmaproc(ide_dma_abort, drive));	/* End DMA */
+#if IDEFLOPPY_DEBUG_LOG
+		printk (KERN_INFO "ide-floppy: DMA finished\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+	}
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+	status.all = GET_STAT();					/* Clear the interrupt */
+
+	if (!status.b.drq) {						/* No more interrupts */
+#if IDEFLOPPY_DEBUG_LOG
+		printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+		clear_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+
+		sti();
+
+		if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) {	/* Error detected */
+#if IDEFLOPPY_DEBUG_LOG
+			printk (KERN_INFO "ide-floppy: %s: I/O error, ",drive->name);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+			rq->errors++;
+			if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+				printk (KERN_ERR "ide-floppy: I/O error in request sense command\n");
+				ide_do_reset (drive);
+				return;
+			}
+			idefloppy_retry_pc (drive);				/* Retry operation */
+			return;
+		}
+		pc->error = 0;
+		if (floppy->failed_pc == pc)
+			floppy->failed_pc=NULL;
+		pc->callback(drive);			/* Command finished - Call the callback function */
+		return;
+	}
+#ifdef CONFIG_BLK_DEV_TRITON
+	if (clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+		printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n");
+		printk (KERN_ERR "ide-floppy: DMA disabled, reverting to PIO\n");
+		drive->using_dma=0;
+		ide_do_reset (drive);
+		return;
+	}
+#endif /* CONFIG_BLK_DEV_TRITON */
+	bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG);			/* Get the number of bytes to transfer */
+	bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG);			/* on this interrupt */
+	ireason.all=IN_BYTE (IDE_IREASON_REG);
+
+	if (ireason.b.cod) {
+		printk (KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n");
+		ide_do_reset (drive);
+		return;
+	}
+	if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) {	/* Hopefully, we will never get here */
+		printk (KERN_ERR "ide-floppy: We wanted to %s, ", ireason.b.io ? "Write":"Read");
+		printk (KERN_ERR "but the floppy wants us to %s !\n",ireason.b.io ? "Read":"Write");
+		ide_do_reset (drive);
+		return;
+	}
+	if (!test_bit (PC_WRITING, &pc->flags)) {			/* Reading - Check that we have enough space */
+		temp = pc->actually_transferred + bcount.all;
+		if ( temp > pc->request_transfer) {
+			if (temp > pc->buffer_size) {
+				printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n");
+				idefloppy_discard_data (drive,bcount.all);
+				ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD);
+				return;
+			}
+#if IDEFLOPPY_DEBUG_LOG
+			printk (KERN_NOTICE "ide-floppy: The floppy wants to send us more data than expected - allowing transfer\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+		}
+	}
+	if (test_bit (PC_WRITING, &pc->flags)) {
+		if (pc->buffer != NULL)
+			atapi_output_bytes (drive,pc->current_position,bcount.all);	/* Write the current buffer */
+		else
+			idefloppy_output_buffers (drive, pc, bcount.all);
+	} else {
+		if (pc->buffer != NULL)
+			atapi_input_bytes (drive,pc->current_position,bcount.all);	/* Read the current buffer */
+		else
+			idefloppy_input_buffers (drive, pc, bcount.all);
+	}
+	pc->actually_transferred+=bcount.all;				/* Update the current position */
+	pc->current_position+=bcount.all;
+
+	ide_set_handler (drive,&idefloppy_pc_intr,IDEFLOPPY_WAIT_CMD);		/* And set the interrupt handler again */
+}
+
+static void idefloppy_transfer_pc (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_ireason_reg_t ireason;
+
+	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+		printk (KERN_ERR "ide-floppy: Strange, packet command initiated yet DRQ isn't asserted\n");
+		return;
+	}
+	ireason.all=IN_BYTE (IDE_IREASON_REG);
+	if (!ireason.b.cod || ireason.b.io) {
+		printk (KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while issuing a packet command\n");
+		ide_do_reset (drive);
+		return;
+	}
+	ide_set_handler (drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD);	/* Set the interrupt routine */
+	atapi_output_bytes (drive, floppy->pc->c, 12);		/* Send the actual packet */
+}
+
+/*
+ *	Issue a packet command
+ */
+static void idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_bcount_reg_t bcount;
+	int dma_ok = 0;
+
+#if IDEFLOPPY_DEBUG_BUGS
+	if (floppy->pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD && pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) {
+		printk (KERN_ERR "ide-floppy: possible ide-floppy.c bug - Two request sense in serial were issued\n");
+	}
+#endif /* IDEFLOPPY_DEBUG_BUGS */
+
+	if (floppy->failed_pc == NULL && pc->c[0] != IDEFLOPPY_REQUEST_SENSE_CMD)
+		floppy->failed_pc=pc;
+	floppy->pc=pc;							/* Set the current packet command */
+
+	if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) {
+		/*
+		 *	We will "abort" retrying a packet command in case
+		 *	a legitimate error code was received.
+		 */
+		if (!test_bit (PC_ABORT, &pc->flags)) {
+			printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+				drive->name, pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+			pc->error = IDEFLOPPY_ERROR_GENERAL;		/* Giving up */
+		}
+		floppy->failed_pc=NULL;
+		pc->callback(drive);
+		return;
+	}
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "Retry number - %d\n",pc->retries);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	pc->retries++;
+	pc->actually_transferred=0;					/* We haven't transferred any data yet */
+	pc->current_position=pc->buffer;
+	bcount.all=pc->request_transfer;				/* Request to transfer the entire buffer at once */
+
+#ifdef CONFIG_BLK_DEV_TRITON
+	if (clear_bit (PC_DMA_ERROR, &pc->flags)) {
+		printk (KERN_WARNING "ide-floppy: DMA disabled, reverting to PIO\n");
+		drive->using_dma=0;
+	}
+	if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma)
+		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+	OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+	OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG);			/* Use PIO/DMA */
+	OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG);
+	OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG);
+	OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+
+#ifdef CONFIG_BLK_DEV_TRITON
+	if (dma_ok) {							/* Begin DMA, if necessary */
+		set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+	}
+#endif /* CONFIG_BLK_DEV_TRITON */
+
+	if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) {
+		ide_set_handler (drive, &idefloppy_transfer_pc, IDEFLOPPY_WAIT_CMD);
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);		/* Issue the packet command */
+	} else {
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
+		idefloppy_transfer_pc (drive);
+	}
+}
+
+static void idefloppy_rw_callback (ide_drive_t *drive)
+{
+#if IDEFLOPPY_DEBUG_LOG	
+	printk (KERN_INFO "ide-floppy: Reached idefloppy_rw_callback\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	idefloppy_end_request(1, HWGROUP(drive));
+	return;
+}
+
+static void idefloppy_create_prevent_cmd (idefloppy_pc_t *pc, int prevent)
+{
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "ide-floppy: creating prevent removal command, prevent = %d\n", prevent);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_PREVENT_REMOVAL_CMD;
+	pc->c[4] = prevent;
+}
+
+static void idefloppy_create_read_capacity_cmd (idefloppy_pc_t *pc)
+{
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_READ_CAPACITY_CMD;
+	pc->c[7] = 255;
+	pc->c[8] = 255;
+}
+
+/*
+ *	A mode sense command is used to "sense" floppy parameters.
+ */
+static void idefloppy_create_mode_sense_cmd (idefloppy_pc_t *pc, byte page_code, byte type)
+{
+	unsigned short length = sizeof (idefloppy_mode_parameter_header_t);
+	
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_MODE_SENSE_CMD;
+	pc->c[1] = 0;
+	pc->c[2] = page_code + (type << 6);
+
+	switch (page_code) {
+		case IDEFLOPPY_CAPABILITIES_PAGE:
+			length += 12;
+			break;
+		case IDEFLOPPY_FLEXIBLE_DISK_PAGE:
+			length += 32;
+			break;
+		default:
+			printk (KERN_ERR "ide-floppy: unsupported page code in create_mode_sense_cmd\n");
+	}
+	put_unaligned (htons (length), (unsigned short *) &pc->c[7]);
+	pc->request_transfer = length;
+}
+
+static void idefloppy_create_start_stop_cmd (idefloppy_pc_t *pc, int start)
+{
+	idefloppy_init_pc (pc);
+	pc->c[0] = IDEFLOPPY_START_STOP_CMD;
+	pc->c[4] = start;
+}
+
+static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector)
+{
+	int block = sector / floppy->bs_factor;
+	int blocks = IDEFLOPPY_MIN(rq->nr_sectors / floppy->bs_factor, IDEFLOPPY_MAX_SECTORS);
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk ("create_rw1%d_cmd: block == %d, blocks == %d\n",
+		2 * test_bit (IDEFLOPPY_USE_READ12, &floppy->flags), block, blocks);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	idefloppy_init_pc (pc);
+	if (test_bit (IDEFLOPPY_USE_READ12, &floppy->flags)) {
+		pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ12_CMD : IDEFLOPPY_WRITE12_CMD;
+		put_unaligned (htonl (blocks), (unsigned int *) &pc->c[6]);
+	} else {
+		pc->c[0] = rq->cmd == READ ? IDEFLOPPY_READ10_CMD : IDEFLOPPY_WRITE10_CMD;
+		put_unaligned (htons (blocks), (unsigned short *) &pc->c[7]);
+	}
+	put_unaligned (htonl (block), (unsigned int *) &pc->c[2]);
+	pc->callback = &idefloppy_rw_callback;
+	pc->rq = rq;
+	pc->b_data = rq->buffer;
+	pc->b_count = rq->cmd == READ ? 0 : rq->bh->b_size;
+	if (rq->cmd == WRITE)
+		set_bit (PC_WRITING, &pc->flags);
+	pc->buffer = NULL;
+	pc->request_transfer = pc->buffer_size = blocks * floppy->block_size;
+	set_bit (PC_DMA_RECOMMENDED, &pc->flags);
+}
+
+/*
+ *	idefloppy_do_request is our request handling function.	
+ */
+void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_pc_t *pc;
+
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+	printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	if (rq->errors >= ERROR_MAX) {
+		if (floppy->failed_pc != NULL)
+			printk (KERN_ERR "ide-floppy: %s: I/O error, pc = %2x, key = %2x, asc = %2x, ascq = %2x\n",
+				drive->name, floppy->failed_pc->c[0], floppy->sense_key, floppy->asc, floppy->ascq);
+		else
+			printk (KERN_ERR "ide-floppy: %s: I/O error\n", drive->name);
+		idefloppy_end_request (0, HWGROUP(drive));
+		return;
+	}
+	switch (rq->cmd) {
+		case READ:
+		case WRITE:
+			if (rq->sector % floppy->bs_factor || rq->nr_sectors % floppy->bs_factor) {
+				printk ("%s: unsupported r/w request size\n", drive->name);
+				idefloppy_end_request (0, HWGROUP(drive));
+				return;
+			}
+			pc = idefloppy_next_pc_storage (drive);
+			idefloppy_create_rw_cmd (floppy, pc, rq, block);
+			break;
+		case IDEFLOPPY_PC_RQ:
+			pc = (idefloppy_pc_t *) rq->buffer;
+			break;
+		default:
+			printk (KERN_ERR "ide-floppy: unsupported command %x in request queue\n", rq->cmd);
+			idefloppy_end_request (0,HWGROUP (drive));
+			return;
+	}
+	pc->rq = rq;
+	idefloppy_issue_pc (drive, pc);
+}
+
+/*
+ *	idefloppy_queue_pc_tail adds a special packet command request to the
+ *	tail of the request queue, and waits for it to be serviced.
+ */
+static int idefloppy_queue_pc_tail (ide_drive_t *drive,idefloppy_pc_t *pc)
+{
+	struct request rq;
+
+	ide_init_drive_cmd (&rq);
+	rq.buffer = (char *) pc;
+	rq.cmd = IDEFLOPPY_PC_RQ;
+	return ide_do_drive_cmd (drive, &rq, ide_wait);
+}
+
+/*
+ *	Look at the flexible disk page parameters. We will ignore the CHS
+ *	capacity parameters and use the LBA parameters instead.
+ */
+static int idefloppy_get_flexible_disk_page (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_pc_t pc;
+	idefloppy_mode_parameter_header_t *header;
+	idefloppy_flexible_disk_page_t *page;
+	int capacity, lba_capacity;
+
+	idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE, MODE_SENSE_CURRENT);
+	if (idefloppy_queue_pc_tail (drive,&pc)) {
+		printk (KERN_ERR "ide-floppy: Can't get flexible disk page parameters\n");
+		return 1;
+	}
+	header = (idefloppy_mode_parameter_header_t *) pc.buffer;
+	page = (idefloppy_flexible_disk_page_t *) (header + 1);
+
+	page->transfer_rate = ntohs (page->transfer_rate);
+	page->sector_size = ntohs (page->sector_size);
+	page->cyls = ntohs (page->cyls);
+	page->rpm = ntohs (page->rpm);
+	capacity = page->cyls * page->heads * page->sectors * page->sector_size;
+	if (memcmp (page, &floppy->flexible_disk_page, sizeof (idefloppy_flexible_disk_page_t)))
+		printk (KERN_INFO "%s: %dkB, %d/%d/%d CHS, %d kBps, %d sector size, %d rpm\n",
+			drive->name, capacity / 1024, page->cyls, page->heads, page->sectors,
+			page->transfer_rate / 8, page->sector_size, page->rpm);
+
+	floppy->flexible_disk_page = *page;
+	drive->bios_cyl = page->cyls;
+	drive->bios_head = page->heads;
+	drive->bios_sect = page->sectors;
+	lba_capacity = floppy->blocks * floppy->block_size;
+	if (capacity != lba_capacity) {
+		printk (KERN_NOTICE "%s: The drive reports both %d and %d bytes as its capacity\n",
+			drive->name, capacity, lba_capacity);
+		capacity = IDEFLOPPY_MIN(capacity, lba_capacity);
+		floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0;
+	}
+	return 0;
+}
+
+/*
+ *	Determine if a media is present in the floppy drive, and if so,
+ *	its LBA capacity.
+ */
+static int idefloppy_get_capacity (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_pc_t pc;
+	idefloppy_capacity_header_t *header;
+	idefloppy_capacity_descriptor_t *descriptor;
+	int i, descriptors, rc = 1, blocks, length;
+
+	drive->bios_cyl = 0;
+	drive->bios_head = drive->bios_sect = 0;
+	floppy->blocks = floppy->bs_factor = 0;
+	drive->part[0].nr_sects = 0;
+
+	idefloppy_create_read_capacity_cmd (&pc);
+	if (idefloppy_queue_pc_tail (drive, &pc)) {
+		printk (KERN_ERR "ide-floppy: Can't get floppy parameters\n");
+		return 1;
+	}
+	header = (idefloppy_capacity_header_t *) pc.buffer;
+	descriptors = header->length / sizeof (idefloppy_capacity_descriptor_t);
+	descriptor = (idefloppy_capacity_descriptor_t *) (header + 1);
+	for (i = 0; i < descriptors; i++, descriptor++) {
+		blocks = descriptor->blocks = ntohl (descriptor->blocks);
+		length = descriptor->length = ntohs (descriptor->length);
+		if (!i && descriptor->dc == CAPACITY_CURRENT) {
+			if (memcmp (descriptor, &floppy->capacity, sizeof (idefloppy_capacity_descriptor_t)))
+				printk (KERN_INFO "%s: %dkB, %d blocks, %d sector size\n", drive->name, blocks * length / 1024, blocks, length);
+			floppy->capacity = *descriptor;
+			if (!length || length % 512)
+				printk (KERN_ERR "%s: %d bytes block size not supported\n", drive->name, length);
+			else {
+				floppy->blocks = blocks;
+				floppy->block_size = length;
+				if ((floppy->bs_factor = length / 512) != 1)
+					printk (KERN_NOTICE "%s: warning: non 512 bytes block size not fully supported\n", drive->name);
+				rc = 0;
+			}
+		}
+#if IDEFLOPPY_DEBUG_INFO
+		if (!i) printk (KERN_INFO "Descriptor 0 Code: %d\n", descriptor->dc);
+		printk (KERN_INFO "Descriptor %d: %dkB, %d blocks, %d sector size\n", i, blocks * length / 1024, blocks, length);
+#endif /* IDEFLOPPY_DEBUG_INFO */
+	}
+	(void) idefloppy_get_flexible_disk_page (drive);
+	drive->part[0].nr_sects = floppy->blocks * floppy->bs_factor;
+	return rc;
+}
+
+/*
+ *	Our special ide-floppy ioctl's.
+ *
+ *	Currently there aren't any ioctl's.
+ */
+int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	return -EIO;
+}
+
+/*
+ *	Our open/release functions
+ */
+int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	idefloppy_pc_t pc;
+	
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "Reached idefloppy_open\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	MOD_INC_USE_COUNT;
+	if (drive->usage == 1) {
+		idefloppy_create_start_stop_cmd (&pc, 1);
+		(void) idefloppy_queue_pc_tail (drive, &pc);
+		if (idefloppy_get_capacity (drive)) {
+			drive->usage--;
+			MOD_DEC_USE_COUNT;
+			return -EIO;
+		}
+		set_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
+		idefloppy_create_prevent_cmd (&pc, 1);
+		(void) idefloppy_queue_pc_tail (drive, &pc);
+		check_disk_change(inode->i_rdev);
+	}
+	return 0;
+}
+
+void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	idefloppy_pc_t pc;
+	
+#if IDEFLOPPY_DEBUG_LOG
+	printk (KERN_INFO "Reached idefloppy_release\n");
+#endif /* IDEFLOPPY_DEBUG_LOG */
+
+	if (!drive->usage) {
+		invalidate_buffers (inode->i_rdev);
+		idefloppy_create_prevent_cmd (&pc, 0);
+		(void) idefloppy_queue_pc_tail (drive, &pc);
+	}
+	MOD_DEC_USE_COUNT;
+}
+
+/*
+ *	Check media change. Use a simple algorithm for now.
+ */
+int idefloppy_media_change (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	
+	return clear_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags);
+}
+
+/*
+ *	Return the current floppy capacity to ide.c.
+ */
+unsigned long idefloppy_capacity (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	unsigned long capacity = floppy->blocks * floppy->bs_factor;
+
+	return capacity;
+}
+
+/*
+ *	idefloppy_identify_device checks if we can support a drive,
+ *	based on the ATAPI IDENTIFY command results.
+ */
+int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id)
+{
+	struct idefloppy_id_gcw gcw;
+	idefloppy_floppy_t *floppy;
+#if IDEFLOPPY_DEBUG_INFO
+	unsigned short mask,i;
+	char buffer[80];
+#endif /* IDEFLOPPY_DEBUG_INFO */
+
+	*((unsigned short *) &gcw) = id->config;
+
+#if IDEFLOPPY_DEBUG_INFO
+	printk (KERN_INFO "Dumping ATAPI Identify Device floppy parameters\n");
+	switch (gcw.protocol) {
+		case 0: case 1: sprintf (buffer, "ATA");break;
+		case 2:	sprintf (buffer, "ATAPI");break;
+		case 3: sprintf (buffer, "Reserved (Unknown to ide-floppy)");break;
+	}
+	printk (KERN_INFO "Protocol Type: %s\n", buffer);
+	switch (gcw.device_type) {
+		case 0: sprintf (buffer, "Direct-access Device");break;
+		case 1: sprintf (buffer, "Streaming Tape Device");break;
+		case 2: case 3: case 4: sprintf (buffer, "Reserved");break;
+		case 5: sprintf (buffer, "CD-ROM Device");break;
+		case 6: sprintf (buffer, "Reserved");
+		case 7: sprintf (buffer, "Optical memory Device");break;
+		case 0x1f: sprintf (buffer, "Unknown or no Device type");break;
+		default: sprintf (buffer, "Reserved");
+	}
+	printk (KERN_INFO "Device Type: %x - %s\n", gcw.device_type, buffer);
+	printk (KERN_INFO "Removable: %s\n",gcw.removable ? "Yes":"No");	
+	switch (gcw.drq_type) {
+		case 0: sprintf (buffer, "Microprocessor DRQ");break;
+		case 1: sprintf (buffer, "Interrupt DRQ");break;
+		case 2: sprintf (buffer, "Accelerated DRQ");break;
+		case 3: sprintf (buffer, "Reserved");break;
+	}
+	printk (KERN_INFO "Command Packet DRQ Type: %s\n", buffer);
+	switch (gcw.packet_size) {
+		case 0: sprintf (buffer, "12 bytes");break;
+		case 1: sprintf (buffer, "16 bytes");break;
+		default: sprintf (buffer, "Reserved");break;
+	}
+	printk (KERN_INFO "Command Packet Size: %s\n", buffer);
+	printk (KERN_INFO "Model: %s\n",id->model);
+	printk (KERN_INFO "Firmware Revision: %s\n",id->fw_rev);
+	printk (KERN_INFO "Serial Number: %s\n",id->serial_no);
+	printk (KERN_INFO "Write buffer size(?): %d bytes\n",id->buf_size*512);
+	printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n");
+	printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n");
+	printk (KERN_INFO "IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n");
+	printk (KERN_INFO "IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n");
+	printk (KERN_INFO "ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n");
+	printk (KERN_INFO "PIO Cycle Timing Category: %d\n",id->tPIO);
+	printk (KERN_INFO "DMA Cycle Timing Category: %d\n",id->tDMA);
+	printk (KERN_INFO "Single Word DMA supported modes:\n");
+	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+		if (id->dma_1word & mask)
+			printk (KERN_INFO "   Mode %d%s\n", i, (id->dma_1word & (mask << 8)) ? " (active)" : "");
+	}
+	printk (KERN_INFO "Multi Word DMA supported modes:\n");
+	for (i=0,mask=1;i<8;i++,mask=mask << 1) {
+		if (id->dma_mword & mask)
+			printk (KERN_INFO "   Mode %d%s\n", i, (id->dma_mword & (mask << 8)) ? " (active)" : "");
+	}
+	if (id->field_valid & 0x0002) {
+		printk (KERN_INFO "Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None");
+		if (id->eide_dma_min == 0)
+			sprintf (buffer, "Not supported");
+		else
+			sprintf (buffer, "%d ns",id->eide_dma_min);
+		printk (KERN_INFO "Minimum Multi-word DMA cycle per word: %s\n", buffer);
+		if (id->eide_dma_time == 0)
+			sprintf (buffer, "Not supported");
+		else
+			sprintf (buffer, "%d ns",id->eide_dma_time);
+		printk (KERN_INFO "Manufacturer\'s Recommended Multi-word cycle: %s\n", buffer);
+		if (id->eide_pio == 0)
+			sprintf (buffer, "Not supported");
+		else
+			sprintf (buffer, "%d ns",id->eide_pio);
+		printk (KERN_INFO "Minimum PIO cycle without IORDY: %s\n", buffer);
+		if (id->eide_pio_iordy == 0)
+			sprintf (buffer, "Not supported");
+		else
+			sprintf (buffer, "%d ns",id->eide_pio_iordy);
+		printk (KERN_INFO "Minimum PIO cycle with IORDY: %s\n", buffer);
+	} else
+		printk (KERN_INFO "According to the device, fields 64-70 are not valid.\n");
+#endif /* IDEFLOPPY_DEBUG_INFO */
+
+	if (gcw.protocol != 2)
+		printk (KERN_ERR "ide-floppy: Protocol is not ATAPI\n");
+	else if (gcw.device_type != 0)
+		printk (KERN_ERR "ide-floppy: Device type is not set to floppy\n");
+	else if (!gcw.removable)
+		printk (KERN_ERR "ide-floppy: The removable flag is not set\n");
+	else if (gcw.drq_type == 3)
+		printk (KERN_ERR "ide-floppy: Sorry, DRQ type %d not supported\n", gcw.drq_type);
+	else if (gcw.packet_size != 0)
+		printk (KERN_ERR "ide-floppy: Packet size is not 12 bytes long\n");
+	else if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL)
+		printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name);
+	else {
+		drive->floppy = floppy;	
+		return 1;
+	}
+	printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name);
+	return 0;
+}
+
+/*
+ *	idefloppy_get_capabilities asks the floppy about its various
+ *	parameters.
+ */
+static void idefloppy_get_capabilities (ide_drive_t *drive)
+{
+	idefloppy_pc_t pc;
+	idefloppy_mode_parameter_header_t *header;
+	idefloppy_capabilities_page_t *capabilities;
+	
+	idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_CAPABILITIES_PAGE, MODE_SENSE_CURRENT);
+	if (idefloppy_queue_pc_tail (drive,&pc)) {
+		printk (KERN_ERR "ide-floppy: Can't get drive capabilities\n");
+		return;
+	}
+	header = (idefloppy_mode_parameter_header_t *) pc.buffer;
+	capabilities = (idefloppy_capabilities_page_t *) (header + 1);
+
+	if (!capabilities->sflp)
+		printk (KERN_INFO "%s: Warning - system floppy device bit is not set\n", drive->name);
+
+#if IDEFLOPPY_DEBUG_INFO
+	printk (KERN_INFO "Dumping the results of the MODE SENSE packet command\n");
+	printk (KERN_INFO "Mode Parameter Header:\n");
+	printk (KERN_INFO "Mode Data Length - %d\n",header->mode_data_length);
+	printk (KERN_INFO "Medium Type - %d\n",header->medium_type);
+	printk (KERN_INFO "WP - %d\n",header->wp);
+
+	printk (KERN_INFO "Capabilities Page:\n");
+	printk (KERN_INFO "Page code - %d\n",capabilities->page_code);
+	printk (KERN_INFO "Page length - %d\n",capabilities->page_length);
+	printk (KERN_INFO "PS - %d\n",capabilities->ps);
+	printk (KERN_INFO "System Floppy Type device - %s\n",capabilities->sflp ? "Yes":"No");
+	printk (KERN_INFO "Supports Reporting progress of Format - %s\n",capabilities->srfp ? "Yes":"No");
+	printk (KERN_INFO "Non CD Optical device - %s\n",capabilities->ncd ? "Yes":"No");
+	printk (KERN_INFO "Multiple LUN support - %s\n",capabilities->sml ? "Yes":"No");
+	printk (KERN_INFO "Total LUN supported - %s\n",capabilities->tlun ? "Yes":"No");
+#endif /* IDEFLOPPY_DEBUG_INFO */
+}
+
+/*
+ *	Driver initialization.
+ */
+void idefloppy_setup (ide_drive_t *drive)
+{
+	idefloppy_floppy_t *floppy = drive->floppy;
+	struct idefloppy_id_gcw gcw;
+
+	*((unsigned short *) &gcw) = drive->id->config;
+	drive->ready_stat = 0;
+	memset (floppy, 0, sizeof (idefloppy_floppy_t));
+	floppy->drive = drive;
+	floppy->pc = floppy->pc_stack;
+	if (gcw.drq_type == 1)
+		set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags);
+
+	idefloppy_get_capabilities (drive);
+	(void) idefloppy_get_capacity (drive);
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/block/ide.c linux/drivers/block/ide.c
--- v2.0.30/linux/drivers/block/ide.c	Tue Nov 19 06:21:06 1996
+++ linux/drivers/block/ide.c	Mon Aug  4 11:45:55 1997
@@ -1,5 +1,5 @@
 /*
- *  linux/drivers/block/ide.c	Version 5.52  Sep  24, 1996
+ *  linux/drivers/block/ide.c	Version 5.53  Jun  24, 1997
  *
  *  Copyright (C) 1994-1996  Linus Torvalds & authors (see below)
  */
@@ -261,6 +261,17 @@
  *			change delay_10ms() to delay_50ms() to fix problems
  * Version 5.52		fix incorrect invalidation of removable devices
  *			add "hdx=slow" command line option
+ * Version 5.53		add ATAPI floppy drive support
+ *			change default media for type 0 to floppy
+ *			add support for Exabyte Nest
+ *			add missing set_blocksize() in revalidate_disk()
+ *			handle bad status bit sequencing in ide_wait_stat()
+ *			support partition table translations with 255 heads
+ *			probe all interfaces by default
+ *			add probe for the i82371AB chipset
+ *			acknowledge media change on removable drives
+ *			add work-around for BMI drives
+ *			remove "LBA" from boot messages
  *
  *  Some additional driver compile-time options are in ide.h
  *
@@ -367,7 +378,6 @@
 
 	/* fill in any non-zero initial values */
 	hwif->index     = index;
-	hwif->noprobe	= (index > 1);
 	hwif->io_base	= default_io_base[index];
 	hwif->ctl_port	= hwif->io_base ? hwif->io_base+0x206 : 0x000;
 #ifdef CONFIG_BLK_DEV_HD
@@ -536,6 +546,29 @@
 }
 
 /*
+ * The following routines are mainly used by the ATAPI drivers.
+ *
+ * These routines will round up any request for an odd number of bytes,
+ * so if an odd bytecount is specified, be sure that there's at least one
+ * extra byte allocated for the buffer.
+ */
+void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+	++bytecount;
+	ide_input_data (drive, buffer, bytecount / 4);
+	if ((bytecount & 0x03) >= 2)
+		insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+}
+
+void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount)
+{
+	++bytecount;
+	ide_output_data (drive, buffer, bytecount / 4);
+	if ((bytecount & 0x03) >= 2)
+		outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+}
+
+/*
  * This should get invoked any time we exit the driver to
  * wait for an interrupt response from a drive.  handler() points
  * at the appropriate code to handle the next interrupt, and a
@@ -593,6 +626,10 @@
 
 	if (!drive->present)
 		return 0;
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+	if (drive->media == ide_floppy)
+		return idefloppy_capacity(drive);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
 	if (drive->media != ide_disk)
 		return 0x7fffffff;	/* cdrom or tape */
 	drive->select.b.lba = 0;
@@ -625,8 +662,13 @@
 		if (drive->present && drive->media == ide_tape)
 			idetape_setup(drive);
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+		if (drive->present && drive->media == ide_floppy)
+			idefloppy_setup(drive);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
 		drive->part[0].nr_sects = current_capacity(drive);
-		if (!drive->present || drive->media != ide_disk) {
+		if (!drive->present || (drive->media != ide_disk && drive->media != ide_floppy) ||
+		    !drive->part[0].nr_sects) {
 			drive->part[0].start_sect = -1; /* skip partition check */
 		}
 	}
@@ -1005,6 +1047,8 @@
 				rq->errors = ERROR_MAX;
 			else if (err & TRK0_ERR)	/* help it find track zero */
 				rq->errors |= ERROR_RECAL;
+			else if (err & MC_ERR)
+				drive->special.b.mc = 1;
 		}
 		if ((stat & DRQ_STAT) && rq->cmd != WRITE)
 			try_to_flush_leftover_data(drive);
@@ -1017,9 +1061,20 @@
 		if (drive->media == ide_tape) {
 			rq->errors = 0;
 			idetape_end_request(0, HWGROUP(drive));
-		}
-		else
+		} else
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+		if (drive->media == ide_floppy) {
+			rq->errors = 0;
+			idefloppy_end_request(0, HWGROUP(drive));
+		} else
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+		if (drive->media == ide_scsi) {
+			rq->errors = 0;
+			idescsi_end_request(0, HWGROUP(drive));
+		} else
+#endif /* CONFIG_BLK_DEV_IDESCSI */
  		ide_end_request(0, HWGROUP(drive));
 	}
 	else {
@@ -1230,6 +1285,19 @@
 }
 
 /*
+ * mc_intr() is invoked on completion of a WIN_ACKMC cmd.
+ */
+static void mc_intr (ide_drive_t *drive)
+{
+	byte stat = GET_STAT();
+
+	sti();
+	if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+		ide_error(drive, "mc_intr", stat);
+	drive->special.b.mc = 0;
+}
+
+/*
  * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
  */
 static void drive_cmd_intr (ide_drive_t *drive)
@@ -1265,7 +1333,7 @@
 #endif
 	if (s->b.set_geometry) {
 		s->b.set_geometry = 0;
-		if (drive->media == ide_disk) {
+		if (drive->media == ide_disk && !drive->no_geom) {
 			OUT_BYTE(drive->sect,IDE_SECTOR_REG);
 			OUT_BYTE(drive->cyl,IDE_LCYL_REG);
 			OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
@@ -1291,6 +1359,10 @@
 				ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
 		} else
 			drive->mult_req = 0;
+	} else if (s->b.mc) {
+		s->b.mc = 0;
+		if (drive->media == ide_disk && !IS_PROMISE_DRIVE)
+			ide_cmd(drive, WIN_ACKMC, drive->sect, &mc_intr);
 	} else if (s->all) {
 		int special = s->all;
 		s->all = 0;
@@ -1314,27 +1386,24 @@
 	byte stat;
 	unsigned long flags;
 
-test:
-	udelay(1);	/* spec allows drive 400ns to change "BUSY" */
-	if (OK_STAT((stat = GET_STAT()), good, bad))
-		return 0;	/* fast exit for most frequent case */
-	if (!(stat & BUSY_STAT)) {
-		ide_error(drive, "status error", stat);
-		return 1;
-	}
-
-	save_flags(flags);
-	sti();
-	timeout += jiffies;
-	do {
-		if (!((stat = GET_STAT()) & BUSY_STAT)) {
-			restore_flags(flags);
-			goto test;
+	udelay(1);	/* spec allows drive 400ns to assert "BUSY" */
+	if ((stat = GET_STAT()) & BUSY_STAT) {
+		save_flags(flags);
+		sti();
+		timeout += jiffies;
+		while ((stat = GET_STAT()) & BUSY_STAT) {
+			if (jiffies > timeout) {
+				restore_flags(flags);
+				ide_error(drive, "status timeout", stat);
+				return 1;
+			}
 		}
-	} while (jiffies <= timeout);
-
-	restore_flags(flags);
-	ide_error(drive, "status timeout", GET_STAT());
+		restore_flags(flags);
+	}
+	udelay(1);	/* allow status to settle, then read it again */
+	if (OK_STAT((stat = GET_STAT()), good, bad))
+		return 0;
+	ide_error(drive, "status error", stat);
 	return 1;
 }
 
@@ -1532,6 +1601,16 @@
 				idetape_do_request (drive, rq, block);
 				return;
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+			case ide_floppy:
+				idefloppy_do_request (drive, rq, block);
+				return;
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+			case ide_scsi:
+				idescsi_do_request (drive, rq, block);
+				return;
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 
 			default:
 				printk("%s: media type %d not supported\n",
@@ -1588,6 +1667,7 @@
 				if (rq != NULL && rq->rq_status != RQ_INACTIVE)
 					goto got_rq;
 			} while ((hwif = hwif->next) != hwgroup->next_hwif);
+			hwgroup->active = 0;
 			return;		/* no work left for this hwgroup */
 		}
 	got_rq:	
@@ -1612,6 +1692,7 @@
 	if (hwgroup->handler == NULL) {
 		ide_hwif_t *hgif = hwgroup->hwif;
 		ide_hwif_t *hwif = hgif;
+		hwgroup->active = 1;
 		do {
 			disable_irq(hwif->irq);
 		} while ((hwif = hwif->next) != hgif);
@@ -1859,13 +1940,8 @@
 	if (cur_rq == NULL || action == ide_preempt) {
 		rq->next = cur_rq;
 		bdev->current_request = rq;
-		if (action == ide_preempt) {
+		if (action == ide_preempt)
 			HWGROUP(drive)->rq = NULL;
-		} else
-		if (HWGROUP(drive)->rq == NULL) {  /* is this necessary (?) */
-			bdev->request_fn();
-			cli();
-		}
 	} else {
 		if (action == ide_wait || action == ide_end) {
 			while (cur_rq->next != NULL)	/* find end of list */
@@ -1874,6 +1950,10 @@
 		rq->next = cur_rq->next;
 		cur_rq->next = rq;
 	}
+	if (!HWGROUP(drive)->active) {
+		do_hwgroup_request(HWGROUP(drive));
+		cli();
+	}
 	if (action == ide_wait  && rq->rq_status != RQ_INACTIVE)
 		down(&sem);	/* wait for it to be serviced */
 	restore_flags(flags);
@@ -1901,6 +1981,14 @@
 	if (drive->media == ide_tape)
 		return idetape_blkdev_open (inode, filp, drive);
 #endif	/* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+	if (drive->media == ide_floppy)
+		return idefloppy_open (inode, filp, drive);
+#endif	/* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+	if (drive->media == ide_scsi)
+		return idescsi_open (inode, filp, drive);
+#endif	/* CONFIG_BLK_DEV_IDESCSI */
 	if (drive->removable && drive->usage == 1) {
 		byte door_lock[] = {WIN_DOORLOCK,0,0,0};
 		struct request rq;
@@ -1940,6 +2028,18 @@
 			return;
 		}
 #endif	/* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+		if (drive->media == ide_floppy) {
+			idefloppy_release (inode, file, drive);
+			return;
+		}
+#endif	/* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+		if (drive->media == ide_scsi) {
+			idescsi_ide_release (inode, file, drive);
+			return;
+		}
+#endif	/* CONFIG_BLK_DEV_IDESCSI */
 		if (drive->removable && !drive->usage) {
 			byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
 			struct request rq;
@@ -1985,13 +2085,14 @@
 			fsync_dev          (devp);
 			invalidate_inodes  (devp);
 			invalidate_buffers (devp);
+			set_blocksize(devp, 1024);
 		}
 		drive->part[p].start_sect = 0;
 		drive->part[p].nr_sects   = 0;
 	};
 
 	drive->part[0].nr_sects = current_capacity(drive);
-	if (drive->media != ide_disk)
+	if ((drive->media != ide_disk && drive->media != ide_floppy) || !drive->part[0].nr_sects)
 		drive->part[0].start_sect = -1;
 	resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit);
 
@@ -2029,7 +2130,7 @@
 		case HDIO_GETGEO:
 		{
 			struct hd_geometry *loc = (struct hd_geometry *) arg;
-			if (!loc || drive->media != ide_disk) return -EINVAL;
+			if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL;
 			err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
 			if (err) return err;
 			put_user(drive->bios_head, (byte *) &loc->heads);
@@ -2223,6 +2324,14 @@
 			if (drive->media == ide_tape)
 				return idetape_blkdev_ioctl(drive, inode, file, cmd, arg);
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+			if (drive->media == ide_floppy)
+				return idefloppy_ioctl(drive, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+			if (drive->media == ide_scsi)
+				return idescsi_ioctl(drive, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 			return -EPERM;
 	}
 }
@@ -2237,6 +2346,10 @@
 	if (drive->media == ide_cdrom)
 		return ide_cdrom_check_media_change (drive);
 #endif	/* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+	if (drive->media == ide_floppy)
+		return idefloppy_media_change (drive);
+#endif	/* CONFIG_BLK_DEV_IDEFLOPPY */
 	if (drive->removable) /* for disks */
 		return 1;	/* always assume it was changed */
 	return 0;
@@ -2307,6 +2420,9 @@
 	ide_fixstring (id->fw_rev,    sizeof(id->fw_rev),    bswap);
 	ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
 
+	if (strstr(id->model, "E X A B Y T E N E S T"))
+		return;
+
 #ifdef CONFIG_BLK_DEV_IDEATAPI
 	/*
 	 * Check for an ATAPI device
@@ -2322,7 +2438,22 @@
 		}
 #endif /* CONFIG_BLK_DEV_PROMISE */
 		switch (type) {
-			case 0:		/* Early cdrom models used zero */
+			case 0:
+				if (!strstr(id->model, "oppy") && !strstr(id->model, "poyp") && !strstr(id->model, "ZIP"))
+					printk("cdrom or floppy?, assuming ");
+				if (drive->media != ide_cdrom) {
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+					printk("FLOPPY drive\n");
+					drive->media = ide_floppy;
+					if (idefloppy_identify_device(drive, id))
+						drive->present = 1;
+					return;
+#else
+					printk("FLOPPY ");
+					break;
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+				}
+				/* Early cdrom models used zero */
 			case 5:
 #ifdef CONFIG_BLK_DEV_IDECD
 				printk ("CDROM drive\n");
@@ -2361,8 +2492,15 @@
 				printk("Type %d - Unknown device\n", type);
 				return;
 		}
+#ifdef CONFIG_BLK_DEV_IDESCSI
+		printk("drive - enabling SCSI emulation\n");
+		drive->media = ide_scsi;
+		drive->present = 1;
+		idescsi_setup(drive);
+#else
 		drive->present = 0;
 		printk("- not supported by this kernel\n");
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 		return;
 	}
 #endif /* CONFIG_BLK_DEV_IDEATAPI */
@@ -2381,7 +2519,7 @@
 		    return;
 		}
 	}
-	
+
 	drive->media = ide_disk;
 	/* Extract geometry if we did not already have one for the drive */
 	if (!drive->present) {
@@ -2431,9 +2569,13 @@
 
 	(void) current_capacity (drive); /* initialize LBA selection */
 
-	printk ("%s: %.40s, %ldMB w/%dkB Cache, %sCHS=%d/%d/%d",
+	if (!strncmp(id->model, "BMI ", 4) &&
+	    strstr(id->model, " ENHANCED IDE ") &&
+	    drive->select.b.lba)
+		drive->no_geom = 1;
+
+	printk ("%s: %.40s, %ldMB w/%dkB Cache, CHS=%d/%d/%d",
 	 drive->name, id->model, current_capacity(drive)/2048L, id->buf_size/2,
-	 drive->select.b.lba ? "LBA, " : "",
 	 drive->bios_cyl, drive->bios_head, drive->bios_sect);
 
 	drive->mult_count = 0;
@@ -2607,6 +2749,34 @@
 	return rc;
 }
 
+static void enable_nest (ide_drive_t *drive)
+{
+	unsigned long timeout;
+
+	printk("%s: enabling %s -- ", HWIF(drive)->name, drive->id->model);
+	SELECT_DRIVE(HWIF(drive), drive);
+	delay_50ms();
+	OUT_BYTE(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG);
+	timeout = jiffies + WAIT_WORSTCASE;
+	do {
+		if (jiffies > timeout) {
+			printk("failed (timeout)\n");
+			return;
+		}
+		delay_50ms();
+	} while (GET_STAT() & BUSY_STAT);
+	delay_50ms();
+	if (!OK_STAT(GET_STAT(), 0, BAD_STAT))
+		printk("failed (status = 0x%02x)\n", GET_STAT());
+	else
+		printk("success\n");
+	if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+		(void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
+#endif	/* CONFIG_BLK_DEV_IDEATAPI */
+	}
+}
+
 /*
  * probe_for_drive() tests for existence of a given drive using do_probe().
  *
@@ -2622,6 +2792,8 @@
 		(void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
 #endif	/* CONFIG_BLK_DEV_IDEATAPI */
 	}
+	if (drive->id && strstr(drive->id->model, "E X A B Y T E N E S T"))
+		enable_nest(drive);
 	if (!drive->present)
 		return 0;			/* drive not found */
 	if (drive->id == NULL) {		/* identification failed? */
@@ -3250,6 +3422,7 @@
 		else
 			hwgroup->drive = &hwif->drives[1];
 		hwgroup->poll_timeout = 0;
+		hwgroup->active = 0;
 		init_timer(&hwgroup->timer);
 		hwgroup->timer.function = &timer_expiry;
 		hwgroup->timer.data = (unsigned long) hwgroup;
@@ -3353,6 +3526,7 @@
 		 */
 		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
 		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1, &ide_init_triton, 0);
+		ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB, &ide_init_triton, 0);
 #endif /* CONFIG_BLK_DEV_TRITON */
 	}
 #endif /* CONFIG_PCI */
diff -u --recursive --new-file v2.0.30/linux/drivers/block/ide.h linux/drivers/block/ide.h
--- v2.0.30/linux/drivers/block/ide.h	Tue Mar 11 15:39:13 1997
+++ linux/drivers/block/ide.h	Mon Aug  4 15:03:06 1997
@@ -51,7 +51,8 @@
 #endif
 #endif  /* CONFIG_BLK_DEV_CMD640 */
 
-#if defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE)
+#if	defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE) || \
+	defined(CONFIG_BLK_DEV_IDEFLOPPY) || defined(CONFIG_BLK_DEV_IDESCSI)
 #define CONFIG_BLK_DEV_IDEATAPI 1
 #endif
 
@@ -110,6 +111,9 @@
 #define IDE_FEATURE_REG		IDE_ERROR_REG
 #define IDE_COMMAND_REG		IDE_STATUS_REG
 #define IDE_ALTSTATUS_REG	IDE_CONTROL_REG
+#define IDE_IREASON_REG		IDE_NSECTOR_REG
+#define IDE_BCOUNTL_REG		IDE_LCYL_REG
+#define IDE_BCOUNTH_REG		IDE_HCYL_REG
 
 #ifdef REALLY_FAST_IO
 #define OUT_BYTE(b,p)		outb((b),(p))
@@ -126,7 +130,7 @@
 #define BAD_W_STAT		(BAD_R_STAT  | WRERR_STAT)
 #define BAD_STAT		(BAD_R_STAT  | DRQ_STAT)
 #define DRIVE_READY		(READY_STAT  | SEEK_STAT)
-#define DATA_READY		(DRIVE_READY | DRQ_STAT)
+#define DATA_READY		(DRQ_STAT)
 
 /*
  * Some more useful definitions
@@ -285,6 +289,8 @@
 	/* The result of the last successful request sense command
 	   on this device. */
 	struct atapi_request_sense sense_data;
+
+	int max_sectors;
 };
 
 #endif /* CONFIG_BLK_DEV_IDECD */
@@ -293,7 +299,7 @@
  * Now for the data we need to maintain per-drive:  ide_drive_t
  */
 
-typedef enum {ide_disk, ide_cdrom, ide_tape} ide_media_t;
+typedef enum {ide_disk, ide_cdrom, ide_tape, ide_floppy, ide_scsi} ide_media_t;
 
 typedef union {
 	unsigned all			: 8;	/* all of the bits together */
@@ -302,7 +308,8 @@
 		unsigned recalibrate	: 1;	/* seek to cyl 0      */
 		unsigned set_multmode	: 1;	/* set multmode count */
 		unsigned set_tune	: 1;	/* tune interface for drive */
-		unsigned reserved	: 4;	/* unused */
+		unsigned mc		: 1;	/* acknowledge media change */
+		unsigned reserved	: 3;	/* unused */
 		} b;
 	} special_t;
 
@@ -335,7 +342,8 @@
 #if FAKE_FDISK_FOR_EZDRIVE
 	unsigned remap_0_to_1	: 1;	/* flag: partitioned with ezdrive */
 #endif /* FAKE_FDISK_FOR_EZDRIVE */
-	ide_media_t	media;		/* disk, cdrom, tape */
+	unsigned no_geom	: 1;	/* flag: do not set geometry */
+	ide_media_t	media;		/* disk, cdrom, tape, floppy */
 	select_t	select;		/* basic drive/head select reg value */
 	byte		ctl;		/* "normal" value for IDE_CONTROL_REG */
 	byte		ready_stat;	/* min status value for drive ready */
@@ -363,6 +371,12 @@
 #ifdef CONFIG_BLK_DEV_IDETAPE
 	idetape_tape_t	tape;		/* for ide-tape.c */
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+	void *floppy;			/* for ide-floppy.c */
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+#ifdef CONFIG_BLK_DEV_IDESCSI
+	void *scsi;			/* for ide-scsi.c */
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 	} ide_drive_t;
 
 /*
@@ -465,6 +479,7 @@
 	struct timer_list	timer;	/* failsafe timer */
 	struct request		wrq;	/* local copy of current write rq */
 	unsigned long		poll_timeout;	/* timeout value during long polls */
+	int			active; /* set when servicing requests */
 	} ide_hwgroup_t;
 
 /*
@@ -503,6 +518,12 @@
 void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
 
 /*
+ * This is used for (nearly) all ATAPI data transfers from/to the IDE interface
+ */
+void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
+void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount);
+
+/*
  * This is used on exit from the driver, to designate the next irq handler
  * and also to start the safety timer.
  */
@@ -692,6 +713,28 @@
 void idetape_register_chrdev (void);
 
 #endif /* CONFIG_BLK_DEV_IDETAPE */
+
+#ifdef CONFIG_BLK_DEV_IDEFLOPPY
+int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id);
+void idefloppy_setup (ide_drive_t *drive);
+void idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
+void idefloppy_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
+int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg);
+int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
+void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
+int idefloppy_media_change (ide_drive_t *drive);
+unsigned long idefloppy_capacity (ide_drive_t *drive);
+#endif /* CONFIG_BLK_DEV_IDEFLOPPY */
+
+#ifdef CONFIG_BLK_DEV_IDESCSI
+void idescsi_setup (ide_drive_t *drive);
+void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
+void idescsi_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
+int idescsi_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
+void idescsi_ide_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
+#endif /* CONFIG_BLK_DEV_IDESCSI */
 
 #ifdef CONFIG_BLK_DEV_TRITON
 void ide_init_triton (byte, byte);
diff -u --recursive --new-file v2.0.30/linux/drivers/isdn/isdn_common.c linux/drivers/isdn/isdn_common.c
--- v2.0.30/linux/drivers/isdn/isdn_common.c	Tue Nov 12 22:36:19 1996
+++ linux/drivers/isdn/isdn_common.c	Sun Aug  3 15:22:52 1997
@@ -1694,6 +1694,7 @@
 					q->next = p->next;
 				else
 					dev->infochain = (infostruct *) (p->next);
+				kfree(p);
 				return;
 			}
 			q = p;
diff -u --recursive --new-file v2.0.30/linux/drivers/net/auto_irq.c linux/drivers/net/auto_irq.c
--- v2.0.30/linux/drivers/net/auto_irq.c	Thu Feb 29 21:50:43 1996
+++ linux/drivers/net/auto_irq.c	Mon Aug  4 12:10:18 1997
@@ -54,17 +54,23 @@
 {
 	irq_number = irq;
 	set_bit(irq, (void *)&irq_bitmap);	/* irq_bitmap |= 1 << irq; */
-	disable_irq(irq);
+	/* This code used to disable the irq. However, the interrupt stub
+	 * would then re-enable the interrupt with (potentially) disastrous
+	 * consequences
+	 */
+	free_irq(irq, dev_id);
 	return;
 }
 
 int autoirq_setup(int waittime)
 {
-	int i, mask;
+	int i;
 	int timeout = jiffies + waittime;
 	int boguscount = (waittime*loops_per_sec) / 100;
 
 	irq_handled = 0;
+	irq_bitmap = 0;
+
 	for (i = 0; i < 16; i++) {
 		if (test_bit(i, &irqs_busy) == 0
 			&& request_irq(i, autoirq_probe, SA_INTERRUPT, "irq probe", NULL) == 0)
@@ -72,22 +78,15 @@
 	}
 	/* Update our USED lists. */
 	irqs_used |= ~irq_handled;
-	irq_number = 0;
-	irq_bitmap = 0;
 
 	/* Hang out at least <waittime> jiffies waiting for bogus IRQ hits. */
 	while (timeout > jiffies  &&  --boguscount > 0)
 		;
 
-	for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
-		if (irq_bitmap & irq_handled & mask) {
-			irq_handled &= ~mask;
-#ifdef notdef
-			printk(" Spurious interrupt on IRQ %d\n", i);
-#endif
-			free_irq(i, NULL);
-		}
-	}
+	irq_handled &= ~irq_bitmap;
+
+	irq_number = 0;	/* We are interested in new interrupts from now on */
+
 	return irq_handled;
 }
 
@@ -102,6 +101,8 @@
 	while (timeout > jiffies  &&  --boguscount > 0)
 		if (irq_number)
 			break;
+
+	irq_handled &= ~irq_bitmap;	/* This eliminates the already reset handlers */
 
 	/* Retract the irq handlers that we installed. */
 	for (i = 0; i < 16; i++) {
diff -u --recursive --new-file v2.0.30/linux/drivers/net/lance32.c linux/drivers/net/lance32.c
--- v2.0.30/linux/drivers/net/lance32.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/net/lance32.c	Mon Aug  4 12:11:05 1997
@@ -273,6 +273,9 @@
         outw(0x0002, ioaddr+LANCE_ADDR);
 	outw(0x0002, ioaddr+LANCE_BUS_IF);
 
+	/* Reset the LANCE - this should prevent any more interrupts from arriving */
+	inw(ioaddr+LANCE_RESET);
+
 	if (lance32_debug > 0)
 		printk(version);
 
@@ -451,7 +454,7 @@
 	/* Transmitter timeout, serious problems. */
 	if (dev->tbusy) {
 		int tickssofar = jiffies - dev->trans_start;
-		if (tickssofar < 20)
+		if (tickssofar < 60)		/* It can take this long to run through the 16 retries */
 			return 1;
 		outw(0, ioaddr+LANCE_ADDR);
 		printk("%s: transmit timed out, status %4.4x, resetting.\n",
@@ -469,8 +472,9 @@
 					   lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
 					   lp->rx_ring[i].msg_length);
 			for (i = 0 ; i < TX_RING_SIZE; i++)
-				printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+				printk("%s %08x %04x %04x %08x  ", i & 0x1 ? "" : "\n ",
 					   lp->tx_ring[i].base, -lp->tx_ring[i].length,
+					   lp->tx_ring[i].status,
 					   lp->tx_ring[i].misc);
 			printk("\n");
 		}
@@ -633,6 +637,9 @@
 				dirty_tx += TX_RING_SIZE;
 			}
 #endif
+
+			if (dev->tbusy)
+				dev->trans_start = jiffies; /* We are just starting the next transmit */
 
 			if (lp->tx_full && dev->tbusy
 				&& dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
diff -u --recursive --new-file v2.0.30/linux/drivers/net/new_tunnel.c linux/drivers/net/new_tunnel.c
--- v2.0.30/linux/drivers/net/new_tunnel.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/net/new_tunnel.c	Sun Aug  3 14:02:34 1997
@@ -267,6 +267,7 @@
 
 		/* Tack on our header */
 		new_skb->h.iph = (struct iphdr *) skb_push(new_skb, tunnel_hlen);
+		new_skb->mac.raw = new_skb->ip_hdr;
 
 		/* Free the old packet, we no longer need it */
 		dev_kfree_skb(skb, FREE_WRITE);
diff -u --recursive --new-file v2.0.30/linux/drivers/net/tulip.c linux/drivers/net/tulip.c
--- v2.0.30/linux/drivers/net/tulip.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/net/tulip.c	Mon Aug  4 14:51:40 1997
@@ -1,7 +1,6 @@
-/* tulip.c: A DEC 21040 ethernet driver for linux. */
+/* tulip.c: A DEC 21040-family ethernet driver for linux. */
 /*
-   NOTICE: this version works with kernels 1.1.82 and later only!
-	Written 1994,1995 by Donald Becker.
+	Written 1994-1997 by Donald Becker.
 
 	This software may be used and distributed according to the terms
 	of the GNU Public License, incorporated herein by reference.
@@ -13,44 +12,60 @@
 	Center of Excellence in Space Data and Information Sciences
 	   Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
 
-	Subscribe to linux-tulip@cesdis.gsfc.nasa.gov and linux-tulip-bugs@cesdis.gsfc.nasa.gov
-	for late breaking news and exciting develovements.
+	Support and updates available at
+	http://cesdis.gsfc.nasa.gov/linux/drivers/tulip.html
 */
 
-static char *version =
-"tulip.c:v0.10 8/11/95 becker@cesdis.gsfc.nasa.gov\n"
-"        +0.72 4/17/96 "
-"http://www.dsl.tutics.tut.ac.jp/~linux/tulip\n"
-"        +0.02 12/15/96 mjacob@feral.com (2.0.27)\n";
+static const char *version = "tulip.c:v0.78 7/25/97 becker@cesdis.gsfc.nasa.gov\n";
 
 /* A few user-configurable values. */
 
-/* Default to using 10baseT (i.e. AUI/10base2/100baseT port) port. */
-#define	TULIP_10TP_PORT		0
-#define	TULIP_100TP_PORT	1
-#define	TULIP_AUI_PORT		1
-#define	TULIP_BNC_PORT		2
-#define	TULIP_MAX_PORT		3
-#define	TULIP_AUTO_PORT		-1
+/* Set if the PCI BIOS detects the chips on a multiport board backwards. */
+#ifdef REVERSE_PROBE_ORDER
+static int reverse_probe = 1;
+#else
+static int reverse_probe = 0;
+#endif
 
-#ifndef	TULIP_PORT
-#define	TULIP_PORT			TULIP_10TP_PORT
+/* Keep the ring sizes a power of two for efficiency.
+   Making the Tx ring too large decreases the effectiveness of channel
+   bonding and packet priority.
+   There are no ill effects from too-large receive rings. */
+#define TX_RING_SIZE	16
+#define RX_RING_SIZE	16
+
+/* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */
+#define SKBUFF_RX_COPYBREAK 200
+
+/* The following example shows how to always use the 10base2 port. */
+#ifdef notdef
+#define TULIP_DEFAULT_MEDIA 1		/* 1 == 10base2 */
+#define TULIP_NO_MEDIA_SWITCH		/* Don't switch from this port */
 #endif
 
 /* Define to force full-duplex operation on all Tulip interfaces. */
 /* #define  TULIP_FULL_DUPLEX 1 */
 
-/* Define to fix port. */
-/* #define  TULIP_FIX_PORT 1 */
-
-/* Define to probe only first detected device */
-/*#define	TULIP_MAX_CARDS 1*/
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT  ((2000*HZ)/1000)
 
+#include <linux/config.h>
+#ifdef MODULE
+#ifdef MODVERSIONS
+#include <linux/modversions.h>
+#endif
 #include <linux/module.h>
+#include <linux/version.h>
+#else
+#define MOD_INC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
 
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/string.h>
+#include <linux/timer.h>
 #include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
@@ -58,8 +73,7 @@
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/bios32.h>
-#include <linux/delay.h>
-#include <asm/byteorder.h>
+#include <asm/processor.h>		/* Processor type for cache alignment. */
 #include <asm/bitops.h>
 #include <asm/io.h>
 #include <asm/dma.h>
@@ -68,35 +82,116 @@
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 
+/* Kernel compatibility defines, common to David Hind's PCMCIA package.
+   This is only in the support-all-kernels source code. */
+#include <linux/version.h>		/* Evil, but neccessary */
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10300
+#define RUN_AT(x) (x)			/* What to put in timer->expires.  */
+#define DEV_ALLOC_SKB(len) alloc_skb(len, GFP_ATOMIC)
+#define virt_to_bus(addr)  ((unsigned long)addr)
+#define bus_to_virt(addr) ((void*)addr)
+
+#else  /* 1.3.0 and later */
+#define RUN_AT(x) (jiffies + (x))
+#define DEV_ALLOC_SKB(len) dev_alloc_skb(len + 2)
+#endif
+
+#if defined (LINUX_VERSION_CODE) && LINUX_VERSION_CODE < 0x10338
+#ifdef MODULE
+#if !defined(CONFIG_MODVERSIONS) && !defined(__NO_VERSION__)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#else
+#undef MOD_INC_USE_COUNT
+#define MOD_INC_USE_COUNT
+#undef MOD_DEC_USE_COUNT
+#define MOD_DEC_USE_COUNT
+#endif
+#endif /* 1.3.38 */
+
+#if (LINUX_VERSION_CODE >= 0x10344)
+#define NEW_MULTICAST
+#include <linux/delay.h>
+#endif
+#if (LINUX_VERSION_CODE >= 0x20100)
+char kernel_version[] = UTS_RELEASE;
+#endif
+#ifdef SA_SHIRQ
+#define IRQ(irq, dev_id, pt_regs) (irq, dev_id, pt_regs)
+#else
+#define IRQ(irq, dev_id, pt_regs) (irq, pt_regs)
+#endif
+
+#if (LINUX_VERSION_CODE < 0x20123)
+#define test_and_set_bit(val, addr) set_bit(val, addr)
+#endif
+
+/* This my implementation of shared IRQs, now only used for 1.2.13. */
+#ifdef HAVE_SHARED_IRQ
+#define USE_SHARED_IRQ
+#include <linux/shared_irq.h>
+#endif
+
 /* The total size is unusually large: The 21040 aligns each of its 16
    longword-wide registers on a quadword boundary. */
 #define TULIP_TOTAL_SIZE 0x80
 
+#ifdef HAVE_DEVLIST
+struct netdev_entry tulip_drv =
+{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef TULIP_DEBUG
+int tulip_debug = TULIP_DEBUG;
+#else
+int tulip_debug = 1;
+#endif
+
 /*
 				Theory of Operation
 
 I. Board Compatibility
 
-This device driver is designed for the DECchip 21040 "Tulip", Digital's
-single-chip ethernet controller for PCI, as used on the SMC EtherPower
-ethernet adapter.  It also works with boards based the 21041 (new/experimental)
-and 21140 (10/100mbps).
+This device driver is designed for the DECchip "Tulip", Digital's
+single-chip ethernet controllers for PCI.  Supported members of the family
+are the 21040, 21041, 21140, 21140A and 21142.  These chips are used on
+many PCI boards including the SMC EtherPower series.
 
 
 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 kernel doesn't
-support it. 
+need to be set on the board.  The system BIOS preferably should assign the
+PCI INTA signal to an otherwise unused system IRQ line.
+Note: Kernel versions earlier than 1.3.73 do not support shared PCI
+interrupt lines.
 
 III. Driver operation
 
 IIIa. Ring buffers
+
 The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
-The current driver uses a statically allocated Rx ring of descriptors and
-buffers, and a list of the Tx buffers.
+This driver uses statically allocated rings of Rx and Tx descriptors, set at
+compile time by RX/TX_RING_SIZE.  This version of the driver allocates skbuffs
+for the Rx ring buffers at open() time and passes the skb->data field to the
+Tulip as receive data buffers.  When an incoming frame is less than
+SKBUFF_RX_COPYBREAK bytes long, a fresh skbuff is allocated and the frame is
+copied to the new skbuff.  When the incoming frame is larger, the skbuff is
+passed directly up the protocol stack and replaced by a newly allocated
+skbuff.
+
+The SKBUFF_RX_COPYBREAK value is chosen to trade-off the memory wasted by
+using a full-sized skbuff for small frames vs. the copying costs of larger
+frames.  For small frames the copying cost is negligible (esp. considering
+that we are pre-loading the cache with immediately useful header
+information).  For large frames the copying cost is non-trivial, and the
+larger copy might flush the cache of useful data.  A subtle aspect of this
+choice is that the Tulip only receives into longword aligned buffers, thus
+the IP header at offset 14 isn't longword aligned for further processing.
+Copied frames are put into the new skbuff at an offset of "+2", thus copying
+has the beneficial effect of aligning the IP header and preloading the
+cache.
 
 IIIC. Synchronization
 The driver runs as two independent, single-threaded flows of control.  One
@@ -120,592 +215,807 @@
 
 Thanks to Duke Kamstra of SMC for providing an EtherPower board.
 
+IVb. References
+
+http://cesdis.gsfc.nasa.gov/linux/misc/NWay.html
+http://www.digital.com  (search for current 21*4* datasheets and "21X4 SROM")
+http://www.national.com/pf/DP/DP83840.html
+
+IVc. Errata
+
 The DEC databook doesn't document which Rx filter settings accept broadcast
 packets.  Nor does it document how to configure the part to configure the
 serial subsystem for normal (vs. loopback) operation or how to have it
 autoswitch between internal 10baseT, SIA and AUI transceivers.
 
-The databook claims that CSR13, CSR14, and CSR15 should each be the last
-register of the set CSR12-15 written.   Hmmm, now how is that possible?
-*/
+The 21040 databook claims that CSR13, CSR14, and CSR15 should each be the last
+register of the set CSR12-15 written.  Hmmm, now how is that possible?  */
+
 
 /* A few values that may be tweaked. */
-/* Keep the ring sizes a power of two for efficiency. */
-#define TX_RING_SIZE	4
-#define RX_RING_SIZE	4
 #define PKT_BUF_SZ		1536			/* Size of each temporary Rx buffer.*/
 
-/* This is a mysterious value that can be written to CSR11 in the 21040
-   to detect a full-duplex frame.  No one knows what it should be, but if
-   left at its default value some 10base2(!) packets trigger a
-   full-duplex-request interrupt. */
+/* This is a mysterious value that can be written to CSR11 in the 21040 (only)
+   to support a pre-NWay full-duplex signaling mechanism using short frames.
+   No one knows what it should be, but if left at its default value some
+   10base2(!) packets trigger a full-duplex-request interrupt. */
 #define FULL_DUPLEX_MAGIC	0x6969
 
+#ifndef PCI_VENDOR_ID_DEC		/* Now defined in linux/pci.h */
+#define PCI_VENDOR_ID_DEC			0x1011
+#define PCI_DEVICE_ID_TULIP			0x0002		/* 21040. */
+#define PCI_DEVICE_ID_TULIP_FAST	0x0009		/* 21140. */
+#endif
+
+#ifndef PCI_DEVICE_ID_DEC_TULIP_PLUS
+#define PCI_DEVICE_ID_DEC_TULIP_PLUS	0x0014		/* 21041. */
+#endif
+
+#ifndef PCI_DEVICE_ID_DEC_TULIP_21142
+#define PCI_DEVICE_ID_DEC_TULIP_21142	0x0019
+#endif
+
 /* The rest of these values should never change. */
 
-#define PCI_DEVICE_ID_NONE	0xFFFF
-#define	ETHNAMSIZ	8
-#define	ROUND_UP(size, n)	((size + n - 1) & ~(n - 1))
+static void tulip_timer(unsigned long data);
+
+/* A table describing the chip types. */
+static struct tulip_chip_table {
+  int device_id;
+  char *chip_name;
+  int flags;
+  void (*media_timer)(unsigned long data);
+} tulip_tbl[] = {
+  { PCI_DEVICE_ID_DEC_TULIP, "DS21040 Tulip", 0, tulip_timer },
+  { PCI_DEVICE_ID_DEC_TULIP_PLUS, "DS21041 Tulip", 0, tulip_timer },
+  { PCI_DEVICE_ID_DEC_TULIP_FAST, "DS21140 Tulip", 0, tulip_timer }, /* + 21140A*/
+  { PCI_DEVICE_ID_DEC_TULIP_21142, "DS21142 Tulip", 0, tulip_timer }, /* + 21143 */
+  {0, 0, 0, 0},
+};
+/* This matches the table above. */
+enum chips { DC21040=0, DC21041=1, DC21140=2, DC21142=3, };
+
+static const char * const medianame[] = {
+  "10baseT", "10base2", "AUI", "100baseTx",
+  "10baseT-FD", "100baseTx-FD", "100baseT4", "100baseFx",
+  "100baseFx-FD", "MII 10baseT", "MII 10baseT-FD", "MII",
+  "", "MII 100baseTx", "MII 100baseTx-FD", "MII 100baseT4",
+};
+/* A full-duplex map for above. */
+static const char media_fd[] =
+{0,0,0,0,  0xff,0xff,0,0,  0xff,0,0xff,0x01, 0,0,0xff,0 };
+/* 21041 transceiver register settings: 10-T, 10-2, AUI, 10-T, 10T-FD*/
+static u16 t21041_csr13[] = { 0xEF05, 0xEF09, 0xEF09, 0xEF01, 0xEF09, };
+static u16 t21041_csr14[] = { 0x7F3F, 0xF7FD, 0xF7FD, 0x7F3F, 0x7F3D, };
+static u16 t21041_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
+
+static u16 t21142_csr13[] = { 0x0001, 0x0009, 0x0009, 0x0001, 0x0001, };
+static u16 t21142_csr14[] = { 0xFFFF, 0x0705, 0x0705, 0x7F3F, 0x7F3D, };
+static u16 t21142_csr15[] = { 0x0008, 0x0006, 0x000E, 0x0008, 0x0008, };
 
 /* Offsets to the Command and Status Registers, "CSRs".  All accesses
    must be longword instructions and quadword aligned. */
 enum tulip_offsets {
-				/* 21040		21041		21140		*/
-	CSR0=0,		/* BUS mode								*/
-    CSR1=0x08,	/* TX poll demand						*/
-	CSR2=0x10,	/* RX poll demand						*/
-	CSR3=0x18,	/* RX ring base addr					*/
-	CSR4=0x20,	/* TX ring base addr					*/
-	CSR5=0x28,	/* Status								*/
-	CSR6=0x30,	/* Command mode							*/
-	CSR7=0x38,	/* Interrupt Mask						*/
-	CSR8=0x40,	/* Missed frame counter					*/
-	CSR9=0x48,	/* Eth.addrROM	SROM mii	SROM mii	*/
-	CSR10=0x50,	/* Diagn.		boot ROM	-			*/
-	CSR11=0x58,	/* Full duplex	G.P. timer	G.P. timer	*/
-	CSR12=0x60,	/* SIA status				G.P.		*/
-	CSR13=0x68,	/* SIA connectivity			-			*/
-	CSR14=0x70,	/* SIA TX/RX				-			*/
-	CSR15=0x78	/* SIA general				watchdog	*/
-};
+	CSR0=0,    CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
+	CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
+	CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
 
-/* description of CSR0 bus mode register */
-#define	TBMOD_RESERVED		0xfff80000	/* I don't know */
-#define	TBMOD_RESET			0x00000001
-#define	TBMOD_BIGENDIAN		0x00000080
-/*
-	   Cache alignment bits 15:14	     Burst length 13:8
-    	0000	No alignment  0x00000000 unlimited		0800 8 longwords
-		4000	8  longwords		0100 1 longword		1000 16 longwords
-		8000	16 longwords		0200 2 longwords	2000 32 longwords
-		C000	32  longwords		0400 4 longwords
-*/
-#define	TBMOD_ALIGN0		0x00000000	/* no cache alignment */
-#define	TBMOD_ALIGN8		0x00004000	/* 8 longwords */
-#define	TBMOD_ALIGN16		0x00008000
-#define	TBMOD_ALIGN32		(TBMOD_ALIGN8|TBMOD_ALIGN16)
-#define	TBMOD_BURST0		0x00000000	/* unlimited=rx buffer size */
-#define	TBMOD_BURST1		0x00000100	/* 1 longwords */
-#define	TBMOD_BURST2		0x00000200
-#define	TBMOD_BURST4		0x00000400
-#define	TBMOD_BURST8		0x00000800
-#define	TBMOD_BURST16		0x00001000
-#define	TBMOD_BURST32		0x00002000
-
-/* description of CSR1 Tx poll demand register */
-/* description of CSR2 Rx poll demand register */
-#define	TPOLL_START			0x00000001	/* ? */
-#define	TPOLL_TRIGGER		0x00000000	/* ? */
-
-/* description of CSR5 status register from de4x5.h */
-#define	TSTAT_BUSERROR		0x03800000
-#define	TSTAT_SYSERROR		0x00002000
-#define	TSTAT_TxSTAT		0x00700000
-#define	TSTAT_RxSTAT		0x000e0000
-#define	TSTAT_LKFAIL		0x00001000
-#define	TSTAT_NORINTR		0x00010000	/* Normal interrupt */
-#define	TSTAT_ABNINTR		0x00008000	/* Abnormal interrupt */
-#define	TSTAT_RxMISSED		0x00000100	/* Rx frame missed */
-#define	TSTAT_RxUNABL		0x00000080
-#define	TSTAT_RxINTR		0x00000040
-#define	TSTAT_LKPASS		0x00000010
-#define	TSTAT_TEXPIRED		0x00000800	/* Timer Expired */
-#define	TSTAT_TxTOUT		0x00000008
-#define	TSTAT_TxUNABL		0x00000004
-#define	TSTAT_TxINTR		0x00000001
-#define	TSTAT_CLEARINTR		0x0001ffff	/* clear all interrupt sources */
-
-/* description of CSR6 command mode register */
-#define	TCMOD_SCRM			0x01000000	/* scrambler mode */
-#define	TCMOD_PCS			0x00800000	/* PCS function */
-#define	TCMOD_TxTHMODE		0x00400000	/* Tx threshold mode */
-#define	TCMOD_SW100TP		0x00040000  /* 21140: 100MB */
-#define	TCMOD_CAPTURE		0x00020000	/* capture effect */
-#define	TCMOD_FULLDUPLEX	0x00000200
-#define TCMOD_TH128			0x00008000	/* 10 - 128 bytes threshold */
-#define	TCMOD_TxSTART		0x00002000
-#define	TCMOD_RxSTART		0x00000002
-#define	TCMOD_ALLMCAST		0x00000080	/* pass all multicast */
-#define	TCMOD_PROMISC		0x00000040	/* promisc */
-#define	TCMOD_BOFFCOUNTER	0x00000020	/* backoff counter */
-#define	TCMOD_INVFILTER		0x00000010	/* invert filtering */
-#define	TCMOD_HONLYFILTER	0x00000004	/* hash only filtering */
-#define TCMOD_HPFILTER		0x00000001	/* hash/perfect Rx filtering */
-#define	TCMOD_MODEMASK		(TCMOD_ALLMCAST|TCMOD_PROMISC)
-#define	TCMOD_FILTERMASK	(TCMOD_HONLYFILTER|TCMOD_HPFILTER|TCMOD_INVFILTER)
-#define	TCMOD_TRxSTART		(TCMOD_TxSTART|TCMOD_RxSTART)
-#define	TCMOD_BASE			(TCMOD_CAPTURE|TCMOD_BOFFCOUNTER)
-#define	TCMOD_10TP			(TCMOD_TxTHMODE|TCMOD_BASE)
-#define	TCMOD_100TP			(TCMOD_SCRM|TCMOD_PCS|TCMOD_SW100TP|TCMOD_BASE)
-#define	TCMOD_AUTO			(TCMOD_SW100TP|TCMOD_TH128|TCMOD_10TP)
-
-/* description of CSR7 interrupt mask register */
-#define	TINTR_ENABLE		0xFFFFFFFF
-#define	TINTR_DISABLE		0x00000000
-
-/* description of CSR11 G.P. timer (21041/21140) register */
-#define	TGEPT_COUNT			0x0001FFFF
-
-/* description of CSR12 SIA status(2104x)/GP(21140) register */
-#define	TSIAS_CONERROR		0x00000002	/* connection error */
-#define	TSIAS_LNKERROR		0x00000004	/* link error */
-#define TSIAS_ACTERROR		0x00000200  /* port Rx activity */
-#define TSIAS_RxACTIVE		0x00000100  /* port Rx activity */
-
-#define	TGEPR_LK10NG		0x00000080	/* 10Mbps N.G. (R) */
-#define	TGEPR_LK100NG		0x00000040	/* 100Mbps N.G. (R) */
-#define	TGEPR_DETECT		0x00000020	/* detect signal (R) */
-#define	TGEPR_HALFDUPLEX	0x00000008	/* half duplex (W) */
-#define	TGEPR_PHYLOOPBACK	0x00000004	/* PHY loopback (W) */
-#define	TGEPR_FORCEALED		0x00000002	/* force activity LED on (W) */
-#define	TGEPR_FORCE100		0x00000001	/* force 100Mbps mode */
-
-/* description of CSR13 SIA connectivity register */
-#define	TSIAC_OUTEN			0x0000e000	/* 21041: Output enable */
-#define TSIAC_SELED			0x00000f00	/* 21041: AUI or TP with LEDs */
-#define	TSIAC_INEN			0x00001000	/* 21041: Input enable */
-#define	TSIAC_NO10TP		0x00000008	/* 10baseT(0) or not(1) */
-#define TSIAC_CONFIG		0x00000004	/* Configuration */
-#define	TSIAC_SWRESET		0x00000001	/* 21041: software reset */
-#define	TSIAC_RESET			0x00000000	/* reset */
-#define	TSIAC_C21041		(TSIAC_OUTEN|TSIAC_SELED|TSIAC_SWRESET)
-#define	TSIAC_C21040		TSIAC_CONFIG
-
-/* description of CSR14 SIA TX/RX register */
-#define	TSIAX_NO10TP		0x0000f73d
-#define	TSIAX_10TP			0x0000ff3f
-
-/* description of CSR15 SIA general register */
-#define	TSIAG_SWBNCAUI		0x00000008 /* BNC(0) or AUI(1) */
-#define	TSIAG_BNC			0x00000006
-#define	TSIAG_AUI			(TSIAG_BNC|TSIAG_SWBNCAUI)
-#define	TSIAG_10TP			0x00000000
-
-/* description of rx_ring.status */
-#define	TRING_OWN			0x80000000	/* Owned by chip */
-#define	TRING_CLEAR			0x00000000	/* clear */
-#define	TRING_ERROR			0x00008000	/* error summary */
-#define	TRING_ETxTO			0x00004000	/* Tx time out */
-#define	TRING_ELCOLL		0x00000200	/* late collision */
-#define	TRING_EFCOLL		0x00000100	/* fatal collision */
-#define	TRING_ELCARR		0x00000800	/* carrier lost */
-#define	TRING_ENCARR		0x00000400	/* no carrier */
-#define	TRING_ENOHB			0x00000080	/* heartbeat fail */
-#define	TRING_ELINK			0x00000004	/* link fail */
-#define	TRING_EUFLOW		0x00000002	/* underflow */
-
-#define	TRING_ELEN			0x00004000	/* length error */
-#define	TRING_FDESC			0x00000200	/* first descriptor */
-#define	TRING_LDESC			0x00000100	/* last descriptor */
-#define	TRING_ERUNT			0x00000800	/* runt frame */
-#define	TRING_ELONG			0x00000080	/* frame too long */
-#define	TRING_EWATCHDOG		0x00000010	/* receive watchdog */
-#define	TRING_EDRBIT		0x00000004	/* dribble bit */
-#define	TRING_ECRC			0x00000002	/* CRC error */
-#define	TRING_EOVERFLOW		0x00000001	/* overflow */
-
-#define	TRING_RxDESCMASK	(TRING_FDESC|TRING_LDESC)
-#define	TRING_RxLENGTH		(TRING_ERUNT|TRING_ELONG|TRING_EWATCHDOG)
-#define	TRING_RxFRAME		(TRING_EDRBIT)
-#define	TRING_RxCRC			(TRING_ECRC)
-#define	TRING_RxFIFO		(TRING_EOVERFLOW)
-#define	TRING_TxABORT		(TRING_ETxTO|TRING_EFCOLL|TRING_ELINK)
-#define	TRING_TxCARR		(TRING_ELCARR|TRING_ENCARR)
-#define	TRING_TxWINDOW		(TRING_ELCOLL)
-#define	TRING_TxFIFO		(TRING_EUFLOW)
-#define	TRING_TxHEARTBEAT	(TRING_ENOHB)
 /* The Tulip Rx and Tx buffer descriptors. */
 struct tulip_rx_desc {
 	s32 status;
 	s32 length;
-	u32 buffer1, buffer2;			/* We use only buffer 1.  */
+	u32 buffer1, buffer2;
 };
 
 struct tulip_tx_desc {
 	s32 status;
 	s32 length;
-	u32 buffer1, buffer2;			/* We use only buffer 1.  */
+	u32 buffer1, buffer2;				/* We use only buffer 1.  */
+};
+
+struct medialeaf {
+	u8 type;
+	u8 media;
+	unsigned char *leafdata;
+};
+
+struct mediatable {
+	u16 defaultmedia;
+	u8 leafcount, csr12dir;				/* General purpose pin directions. */
+	unsigned has_mii:1;
+	struct medialeaf mleaf[0];
+};
+
+struct mediainfo {
+	struct mediainfo *next;
+	int info_type;
+	int index;
+	struct non_mii { char media; unsigned char csr12val; char bitnum, flags;}  non_mii;
+	unsigned char *info;
 };
 
 struct tulip_private {
+	char devname[8];			/* Used only for kernel debugging. */
+	const char *product_name;
+	struct device *next_module;
 	struct tulip_rx_desc rx_ring[RX_RING_SIZE];
 	struct tulip_tx_desc tx_ring[TX_RING_SIZE];
 	/* The saved address of a sent-in-place packet/buffer, for skfree(). */
 	struct sk_buff* tx_skbuff[TX_RING_SIZE];
-	char rx_buffs[RX_RING_SIZE][PKT_BUF_SZ];
-	/* temporary Rx buffers. */
+	/* The addresses of receive-in-place skbuffs. */
+	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+	char *rx_buffs;				/* Address of temporary Rx buffers. */
+	int setup_frame[48];		/* Pseudo-Tx frame to init address table. */
+	int chip_id;
+	int revision;
 	struct enet_statistics stats;
-	int setup_frame[48];	/* Pseudo-Tx frame to init address table. */
-	void (*port_select)(struct device *dev);
-	int (*port_fail)(struct device *dev);
-	struct device *next_module;
-	char *signature;
+	struct timer_list timer;	/* Media selection timer. */
 	unsigned int cur_rx, cur_tx;		/* The next free ring entry */
 	unsigned int dirty_rx, dirty_tx;	/* The ring entries to be free()ed. */
 	unsigned int tx_full:1;				/* The Tx queue is full. */
 	unsigned int full_duplex:1;			/* Full-duplex operation requested. */
-	unsigned int port_fix:1;			/* Fix if_port to specified port. */
+	unsigned int default_port:4;		/* Last dev->if_port value. */
+	unsigned int media2:4;				/* Secondary monitored media port. */
+	unsigned int medialock:1;			/* Don't sense media type. */
+	unsigned int mediasense:1;			/* Media sensing in progress. */
+	unsigned int csr6;					/* Current CSR6 control settings. */
+	unsigned char eeprom[128];			/* Serial EEPROM contents. */
+	struct mediatable *mtable;
+	int cur_index;						/* Current media index. */
+	int pad0, pad1;						/* Used for 8-byte alignment */
 };
 
-struct eeprom {
-    union {
-		struct { /* broken EEPROM structure */
-			u_char addr[ETH_ALEN];
-		} ng;
-		struct { /* DEC EtherWorks
-					and other cards which have correct eeprom structure */
-			u_char dum1[20];
-			u_char addr[ETH_ALEN];
-		} ok;
-    } hw;
-#define	ng_addr	hw.ng.addr
-#define	ok_addr	hw.ok.addr
-#define	EE_SIGNLEN	14		/* should be 102 ? */
-	u_char sign[EE_SIGNLEN];
-};
+#ifdef MODULE
+/* Used to pass the full-duplex flag, etc. */
+static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
+#endif
 
-static int read_eeprom(int ioaddr, struct eeprom *eepp);
+static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
+								   int chip_id, int options);
+static void parse_eeprom(struct device *dev);
+static int read_eeprom(int ioaddr, int location);
+static int mdio_read(int ioaddr, int phy_id, int location);
+static void select_media(struct device *dev, int startup);
 static int tulip_open(struct device *dev);
+static void tulip_timer(unsigned long data);
+static void tulip_tx_timeout(struct device *dev);
 static void tulip_init_ring(struct device *dev);
 static int tulip_start_xmit(struct sk_buff *skb, struct device *dev);
 static int tulip_rx(struct device *dev);
-static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs);
 static int tulip_close(struct device *dev);
 static struct enet_statistics *tulip_get_stats(struct device *dev);
+#ifdef NEW_MULTICAST
 static void set_multicast_list(struct device *dev);
+#else
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
 
-#define	generic21140_fail	NULL
-static void generic21040_select(struct device *dev);
-static void generic21140_select(struct device *dev);
-static void generic21041_select(struct device *dev);
-static void auto21140_select(struct device *dev);
-static void cogent21140_select(struct device *dev);
-static int generic21040_fail(struct device *dev);
-static int generic21041_fail(struct device *dev);
+
 
 #ifdef MODULE
 /* A list of all installed Tulip devices, for removing the driver module. */
 static struct device *root_tulip_dev = NULL;
 #endif
 
-static struct {
-	void (*port_select)(struct device *dev);
-	int (*port_fail)(struct device *dev);
-	unsigned int vendor_id, device_id;
-	char *signature;
-	unsigned int array:1;
-} cardVendor[] = {
-	{generic21140_select, generic21140_fail,
-		 0x0000c000, PCI_DEVICE_ID_DEC_TULIP_FAST, "smc9332", 0},
-	{generic21041_select, generic21041_fail,
-		 0x0000c000, PCI_DEVICE_ID_DEC_TULIP_PLUS, "smc8432", 0},
-	{generic21040_select, generic21040_fail,
-		 0x0000c000, PCI_DEVICE_ID_DEC_TULIP, "old smc8432", 0},
-	{auto21140_select, generic21140_fail,
-		 0x0000f400, PCI_DEVICE_ID_DEC_TULIP_FAST, "LA100PCI", 0},
-	{cogent21140_select, generic21140_fail,
-		 0x00009200, PCI_DEVICE_ID_DEC_TULIP_FAST, "cogent_em110", 0},
-	{generic21040_select, generic21040_fail,
-		0x00009200, PCI_DEVICE_ID_DEC_TULIP, "cogent_em96x", 1},
-	{generic21140_select, generic21140_fail,
-		 0x0000f800, PCI_DEVICE_ID_DEC_TULIP_FAST, "DE500", 0},
-	{generic21041_select, generic21041_fail,
-		 0x0000f800, PCI_DEVICE_ID_DEC_TULIP_PLUS, "DE450", 0},
-	{generic21040_select, generic21040_fail,
-		 0x0000f800, PCI_DEVICE_ID_DEC_TULIP, "DE43x", 0},
-	{generic21040_select, generic21040_fail,
-		 0x0040c700, PCI_DEVICE_ID_DEC_TULIP, "EN9400", 0},
-	{generic21040_select, generic21040_fail,
-		 0x00c09500, PCI_DEVICE_ID_DEC_TULIP, "ZNYX312", 1},
-	{generic21040_select, generic21040_fail,
-		 0x08002b00, PCI_DEVICE_ID_DEC_TULIP, "QSILVER's", 0},
-	{generic21040_select, generic21040_fail,
-		 0, PCI_DEVICE_ID_DEC_TULIP, "21040", 0},
-	{generic21140_select, generic21140_fail,
-		 0, PCI_DEVICE_ID_DEC_TULIP_FAST, "21140", 0},
-	{generic21041_select, generic21041_fail,
-		 0, PCI_DEVICE_ID_DEC_TULIP_PLUS, "21041", 0},
-	{NULL, NULL, 0, 0, "Unknown", 0}
-};
+/* This 21040 probe no longer uses a large fixed contiguous Rx buffer region,
+   but now receives directly into full-sized skbuffs that are allocated
+   at open() time.
+   This allows the probe routine to use the old driver initialization
+   interface. */
 
-
-/* Serial EEPROM section.
-   A "bit" grungy, but we work our way through bit-by-bit :->. */
-/*  EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK	0x02	/* EEPROM shift clock. */
-#define EE_CS			0x01	/* EEPROM chip select. */
-#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
-#define EE_WRITE_0		0x01
-#define EE_WRITE_1		0x05
-#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
-#define EE_ENB			(0x4800 | EE_CS)
+int tulip_probe(struct device *dev)
+{
+	int cards_found = 0;
+	static int pci_index = 0;	/* Static, for multiple probe calls. */
 
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD	(5 << 6)
-#define EE_READ_CMD		(6 << 6)
-#define EE_ERASE_CMD	(7 << 6)
+	/* Ideally we would detect all network cards in slot order.  That would
+	   be best done a central PCI probe dispatch, which wouldn't work
+	   well with the current structure.  So instead we detect just the
+	   Tulip cards in slot order. */
+
+	if (pcibios_present()) {
+		unsigned char pci_bus, pci_device_fn;
+
+		for (;pci_index < 0xff; pci_index++) {
+			unsigned char pci_irq_line, pci_latency;
+			unsigned short pci_command, vendor, device;
+			unsigned int pci_ioaddr, chip_idx = 0;
+
+			if (pcibios_find_class
+				(PCI_CLASS_NETWORK_ETHERNET << 8,
+				 reverse_probe ? 0xfe - pci_index : pci_index,
+				 &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
+				if (reverse_probe)
+					continue;
+				else
+					break;
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+									 PCI_VENDOR_ID, &vendor);
+			pcibios_read_config_word(pci_bus, pci_device_fn,
+									 PCI_DEVICE_ID, &device);
+			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;
+
+			if (vendor != PCI_VENDOR_ID_DEC)
+				continue;
+
+			for (chip_idx = 0; tulip_tbl[chip_idx].chip_name; chip_idx++)
+				if (device == tulip_tbl[chip_idx].device_id)
+					break;
+			if (tulip_tbl[chip_idx].chip_name == 0) {
+				printk("Unknown Digital PCI ethernet chip type %4.4x detected:"
+					   " not configured.\n", device);
+				continue;
+			}
+			if (tulip_debug > 2)
+				printk("Found DEC PCI Tulip at I/O %#x, IRQ %d.\n",
+					   pci_ioaddr, pci_irq_line);
+
+			if (check_region(pci_ioaddr, TULIP_TOTAL_SIZE))
+				continue;
 
 #ifdef MODULE
-static int if_port=TULIP_AUTO_PORT;
-#ifdef TULIP_FULL_DUPLEX
-static int full_duplex=1;
+			dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx,
+						 options[cards_found]);
 #else
-static int full_duplex=0;
-#endif
+			dev = tulip_probe1(dev, pci_ioaddr, pci_irq_line, chip_idx,
+						 dev ? dev->mem_start : 0);
 #endif
 
-#define	tio_write(val, port)	outl(val, ioaddr + port)
-#define	tio_read(port)			inl(ioaddr + port)
+			if (dev) {
+			  /* Get and check the bus-master and latency values. */
+			  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 < 10) {
+				printk("  PCI latency timer (CFLT) is unreasonably low at %d."
+					   "  Setting to 64 clocks.\n", pci_latency);
+				pcibios_write_config_byte(pci_bus, pci_device_fn,
+										  PCI_LATENCY_TIMER, 64);
+			  } else if (tulip_debug > 1)
+				printk("  PCI latency timer (CFLT) is %#x.\n", pci_latency);
+			  /* Bring the 21143 out power-down mode. */
+			  if (device == PCI_DEVICE_ID_DEC_TULIP_21142)
+				pcibios_write_config_dword(pci_bus, pci_device_fn,
+										  	0x40, 0x40000000);
+			  dev = 0;
+			  cards_found++;
+			}
+		}
+	}
 
-static void inline
-tio_sia_write(u32 ioaddr, u32 val13, u32 val14, u32 val15)
-{
-	tio_write(0,CSR13);
-	tio_write(val15,CSR15);
-	tio_write(val14,CSR14);
-	tio_write(val13,CSR13);
+#if defined (MODULE)
+	return cards_found;
+#else
+	return 0;
+#endif
 }
 
-/*
-   card_type returns 1 if the card is 'etherarray'
-*/
-
-static int
-card_type(struct tulip_private *tp, int device_id, int vendor_id)
+static struct device *tulip_probe1(struct device *dev, int ioaddr, int irq,
+								   int chip_id, int options)
 {
-	int n;
+	static int did_version = 0;			/* Already printed version info. */
+	struct tulip_private *tp;
+	/* See note below on the multiport cards. */
+	static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
+	static int last_irq = 0;
+	int i;
+	unsigned short sum;
 
-	for (n = 0; cardVendor[n].device_id; n ++)
-		if (cardVendor[n].device_id == device_id
-			&& (cardVendor[n].vendor_id == vendor_id
-				|| cardVendor[n].vendor_id == 0)) break;
-	tp->port_select = cardVendor[n].port_select;
-	tp->port_fail = cardVendor[n].port_fail;
-	tp->signature = cardVendor[n].signature;
-	return(cardVendor[n].array ? 1: 0);
-}
+	if (tulip_debug > 0  &&  did_version++ == 0)
+		printk(version);
 
-static int
-read_eeprom(int ioaddr, struct eeprom *eepp)
-{
-    int i, n;
-    unsigned short val = 0;
-    int read_cmd = EE_READ_CMD;
-    u_char *p=(u_char *)eepp;
-
-	for (n = 0; n < sizeof(struct eeprom) / 2; n ++, read_cmd ++) {
-		tio_write(EE_ENB & ~EE_CS, CSR9);
-		tio_write(EE_ENB, CSR9);
-
-		/* Shift the read command bits out. */
-		for (i = 10; i >= 0; i--) {
-			short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
-			tio_write(EE_ENB | dataval, CSR9);
-			udelay(100);
-			tio_write(EE_ENB | dataval | EE_SHIFT_CLK, CSR9);
-			udelay(150);
-			tio_write(EE_ENB | dataval, CSR9);
-			udelay(250);
-		}
-		tio_write(EE_ENB, CSR9);
-
-		for (i = 16; i > 0; i--) {
-			tio_write(EE_ENB | EE_SHIFT_CLK, CSR9);
-			udelay(100);
-			val = (val << 1)
-				| ((tio_read(CSR9) & EE_DATA_READ) ? 1 : 0);
-			tio_write(EE_ENB, CSR9);
-			udelay(100);
-		}
-
-		/* Terminate the EEPROM access. */
-		tio_write(EE_ENB & ~EE_CS, CSR9);
-		*p ++ = val;
-		*p ++ = val >> 8;
-    }
-	/* broken eeprom ? */
-	p = (u_char *)eepp;
-	for (i = 0; i < 8; i ++)
-		if (p[i] != p[15 - i] || p[i] != p[16 + i]) return(0);
-	return(-1); /* broken */
-}
+	dev = init_etherdev(dev, 0);
 
-/* Is this required ? */
-static int
-generic21040_fail(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
+	printk("%s: DEC %s at %#3x,",
+		   dev->name, tulip_tbl[chip_id].chip_name, ioaddr);
 
-	return(tio_read(CSR12) & TSIAS_CONERROR);
-}
+	/* Stop the chip's Tx and Rx processes. */
+	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+	/* Clear the missed-packet counter. */
+	(volatile)inl(ioaddr + CSR8);
 
-static int
-generic21041_fail(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
-	u32 csr12 = tio_read(CSR12);
+	if (chip_id == DC21041) {
+		if (inl(ioaddr + CSR9) & 0x8000) {
+			printk(" 21040 compatible mode,");
+			chip_id = DC21040;
+		} else {
+			printk(" 21041 mode,");
+		}
+	}
 
-	return((!(csr12 & TSIAS_CONERROR)
-			|| !(csr12 & TSIAS_LNKERROR)) ? 0: 1);
-}
+	/* The station address ROM is read byte serially.  The register must
+	   be polled, waiting for the value to be read bit serially from the
+	   EEPROM.
+	   */
+	sum = 0;
+	if (chip_id == DC21040) {
+		outl(0, ioaddr + CSR9);		/* Reset the pointer with a dummy write. */
+		for (i = 0; i < 6; i++) {
+			int value, boguscnt = 100000;
+			do
+				value = inl(ioaddr + CSR9);
+			while (value < 0  && --boguscnt > 0);
+			dev->dev_addr[i] = value;
+			sum += value & 0xff;
+		}
+	} else {	/* Must be a new chip, with a serial EEPROM interface. */
+		/* We read the whole EEPROM, and sort it out later.  DEC has a
+		   specification _Digital Semiconductor 21X4 Serial ROM Format_
+		   but early vendor boards just put the address in the first six
+		   EEPROM locations. */
+		unsigned char ee_data[128];
+		int sa_offset = 0;
+
+		for (i = 0; i < sizeof(ee_data)/2; i++)
+			((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
+
+		/* Detect the simple EEPROM format by the duplicated station addr. */
+		for (i = 0; i < 8; i ++)
+			if (ee_data[i] != ee_data[16+i])
+				sa_offset = 20;
+		for (i = 0; i < 6; i ++) {
+			dev->dev_addr[i] = ee_data[i + sa_offset];
+			sum += ee_data[i + sa_offset];
+		}
+	}
+	/* On the Zynx 315 Etherarray and other multiport boards only the
+	   first Tulip has an EEPROM.
+	   The addresses of the subsequent ports are derived from the first.
+	   Many PCI BIOSes also incorrectly report the IRQ line, so we correct
+	   that here as well. */
+	if (sum == 0  || sum == 6*0xff) {
+		printk(" EEPROM not present,");
+		for (i = 0; i < 5; i++)
+			dev->dev_addr[i] = last_phys_addr[i];
+		dev->dev_addr[i] = last_phys_addr[i] + 1;
+		irq = last_irq;
+	}
+	for (i = 0; i < 6; i++)
+		printk(" %2.2x", last_phys_addr[i] = dev->dev_addr[i]);
+	printk(", IRQ %d\n", irq);
+	last_irq = irq;
 
-static void
-generic21040_select(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
-	const char *media;
+	/* We do a request_region() only to register /proc/ioports info. */
+	request_region(ioaddr, TULIP_TOTAL_SIZE, tulip_tbl[chip_id].chip_name);
 
-	dev->if_port &= 3;
-	switch (dev->if_port)
-	{
-	case TULIP_10TP_PORT:
-		media = "10baseT";
-		break;
-	case TULIP_AUI_PORT:
-		media = "AUI";
-		break;
-	case TULIP_BNC_PORT:
-		media = "BNC";
-		break;
-	default:
-		media = "unknown type";
-		break;
-	}
-	printk("%s: enabling %s port.\n", dev->name, media);
-	/* Set the full duplex match frame. */
-	tio_write(FULL_DUPLEX_MAGIC, CSR11);
-	tio_write(TSIAC_RESET, CSR13);
-	/* Reset the serial interface */
-	tio_write((dev->if_port ? TSIAC_NO10TP: 0) | TSIAC_C21040, CSR13);
-}
+	dev->base_addr = ioaddr;
+	dev->irq = irq;
 
-#if 0
-static void
-generic_timer(struct device *dev, u32 count)
-{
-	int ioaddr = dev->base_addr;
+	/* Make certain the data structures are quadword aligned. */
+	tp = (void *)(((long)kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA) + 7) & ~7);
+	memset(tp, 0, sizeof(*tp));
+	dev->priv = tp;
 
-	tio_write(count, CSR11);
-	while (tio_read(CSR11) & TGEPT_COUNT);
-}
+#ifdef MODULE
+	tp->next_module = root_tulip_dev;
+	root_tulip_dev = dev;
 #endif
 
-static void
-generic21041_select(struct device *dev)
-{
-	int ioaddr = dev->base_addr;
-	u32 tsiac = TSIAC_C21041;
-	u32 tsiax = TSIAX_10TP;
-	u32 tsiag = TSIAG_10TP;
-
-	switch(dev->if_port) {
-	case TULIP_AUI_PORT:
-		tsiac |= TSIAC_NO10TP;
-		tsiax = TSIAX_NO10TP;
-		tsiag = TSIAG_AUI;
+	tp->chip_id = chip_id;
+
+#ifdef TULIP_FULL_DUPLEX
+	tp->full_duplex = 1;
+#endif
+#ifdef TULIP_DEFAULT_MEDIA
+	tp->default_port = TULIP_DEFAULT_MEDIA;
+#endif
+#ifdef TULIP_NO_MEDIA_SWITCH
+	tp->medialock = 1;
+#endif
+
+	/* The lower four bits are the media type. */
+	if (options > 0) {
+		tp->full_duplex = (options & 16) ? 1 : 0;
+		tp->default_port = options & 15;
+		if (tp->default_port)
+		  tp->medialock = 1;
+	}
+
+	/* The Tulip-specific entries in the device structure. */
+	dev->open = &tulip_open;
+	dev->hard_start_xmit = &tulip_start_xmit;
+	dev->stop = &tulip_close;
+	dev->get_stats = &tulip_get_stats;
+#ifdef HAVE_MULTICAST
+	dev->set_multicast_list = &set_multicast_list;
+#endif
+
+	/* This is logically part of probe1(), but too complex to write inline. */
+	if (chip_id != DC21040)
+		parse_eeprom(dev);
+
+	/* Reset the xcvr interface and turn on heartbeat. */
+	switch (chip_id) {
+	case DC21041:
+		outl(0x00000000, ioaddr + CSR13);
+		outl(0xFFFFFFFF, ioaddr + CSR14);
+		outl(0x00000008, ioaddr + CSR15); /* Listen on AUI also. */
+		outl(inl(ioaddr + CSR6) | 0x200, ioaddr + CSR6);
+		outl(0x0000EF05, ioaddr + CSR13);
 		break;
-	case TULIP_BNC_PORT:
-		tsiac |= TSIAC_NO10TP;
-		tsiax = TSIAX_NO10TP;
-		tsiag = TSIAG_BNC;
+	case DC21140:  case DC21142:
+		if (tp->mtable)
+			outl(tp->mtable->csr12dir | 0x100, ioaddr + CSR12);
 		break;
-	default:
-		dev->if_port = TULIP_10TP_PORT;
+	case DC21040:
+		outl(0x00000000, ioaddr + CSR13);
+		outl(0x00000004, ioaddr + CSR13);
 		break;
 	}
-	tio_sia_write(ioaddr, tsiac, tsiax, tsiag);
-	if (dev->start)
-		printk("%s: enabling %s port.\n", dev->name,
-			   (dev->if_port == TULIP_AUI_PORT) ? "AUI":
-			   (dev->if_port == TULIP_BNC_PORT) ? "BNC": "10TP");
-}
 
-static void
-auto21140_select(struct device *dev)
-{
-	int i, ioaddr = dev->base_addr;
+	return dev;
+}
+
+/* Serial EEPROM section. */
+/* The main routine to parse the very complicated SROM structure.
+   Search www.digital.com for "21X4 SROM" to get details.
+   This code is very complex, and will require changes to support
+   additional cards, so I'll be verbose about what is going on.
+   */
+
+/* Known cards that have old-style EEPROMs. */
+static struct fixups {
+  char *name;
+  unsigned char addr0, addr1, addr2;
+  u16 newtable[32];				/* Max length below. */
+} eeprom_fixups[] = {
+  {"Asante", 0, 0, 0x94, {0x1e00, 0x0000, 0x0800, 0x0100, 0x018c,
+						  0x0000, 0x0000, 0xe078, 0x0001, 0x0050, 0x0018 }},
+  {"SMC9332DST", 0, 0, 0xC0, { 0x1e00, 0x0000, 0x0800, 0x021f,
+							   0x0000, 0x009E, /* 10baseT */
+							   0x0903, 0x006D, /* 100baseTx */ }},
+  {"Cogent EM100", 0, 0, 0x92, { 0x1e00, 0x0000, 0x0800, 0x013f,
+								 0x0103, 0x006D, /* 100baseTx */ }},
+  {"Maxtech NX-110", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x0313,
+							   0x1001, 0x009E, /* 10base2, CSR12 0x10*/
+							   0x0000, 0x009E, /* 10baseT */
+							   0x0303, 0x006D, /* 100baseTx, CSR12 0x03 */ }},
+  {"Accton EN1207", 0, 0, 0xE8, { 0x1e00, 0x0000, 0x0800, 0x031F,
+							0x1B01, 0x0000, /* 10base2,   CSR12 0x1B */
+							0x1B03, 0x006D, /* 100baseTx, CSR12 0x1B */ 
+							0x0B00, 0x009E, /* 10baseT,   CSR12 0x0B */
+   }},
+  {0, 0, 0, 0, {}}};
+
+static const char * block_name[] = {"21140 non-MII", "21140 MII PHY",
+ "21142 non-MII PHY", "21142 MII PHY", };
+
+#define EEPROM_SIZE 128
+static void parse_eeprom(struct device *dev)
+{
+	/* The last media info list parsed, for multiport boards.  */
+	static struct mediatable *last_mediatable = NULL;
+	static unsigned char *last_ee_data = NULL;
+	static controller_index = 0;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	unsigned char *ee_data = tp->eeprom;
+	int i;
 
-	/* kick port */
-	tio_write(TPOLL_TRIGGER, CSR1);
-	tio_write(TINTR_ENABLE, CSR7);
-	tio_write(TCMOD_AUTO|TCMOD_TRxSTART, CSR6);
-	dev->if_port = !(tio_read(CSR12) & TGEPR_FORCEALED);
-	printk("%s: probed %s port.\n",
-		   dev->name, dev->if_port ? "100TX" : "10TP");
-	tio_write((dev->if_port ? TGEPR_FORCE100: 0)
-			  | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);	
-	tio_write(TINTR_DISABLE, CSR7);
-	i = tio_read(CSR8) & 0xffff;
-	tio_write(TCMOD_AUTO, CSR6);
-}
+	{
+	  static int done_did_that = 0;
+	  if (done_did_that++ == 0)
+		printk("\n  THIS IS AN ALPHA TEST DRIVER.\n"
+			   "  The following verbose information is emitted for\n"
+			   "  bug reports on media selection.\n");
+	}
+	tp->mtable = 0;
+	for (i = 0; i < EEPROM_SIZE/2; i++)
+	  ((u16 *)ee_data)[i] = read_eeprom(ioaddr, i);
 
-static void
-cogent21140_select(struct device *dev)
-{
-	int ioaddr = dev->base_addr, csr6;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	dev->if_port &= 1;
-	csr6 = tio_read(CSR6) &
-		~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM);
-	/* Stop the transmit process. */
-	tio_write(csr6 | TCMOD_RxSTART, CSR6);
-	printk("%s: enabling %s port.\n",
-		   dev->name, dev->if_port ? "100baseTx" : "10baseT");
-	/* Turn on the output drivers */
-	tio_write(0x0000013F, CSR12);
-	tio_write((dev->if_port ? TGEPR_FORCE100: 0)
-			  | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);
-	tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP)
-			  | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6);
+	/* Detect an old-style (SA only) EEPROM layout:
+	   memcmp(eedata, eedata+16, 8). */
+	for (i = 0; i < 8; i ++)
+	  if (ee_data[i] != ee_data[16+i])
+		break;
+	if (i >= 8) {
+	  if (ee_data[0] == 0xff) {
+		if (last_mediatable) {
+		  controller_index++;
+		  printk("%s: Controller %d of multiport board.\n",
+				 dev->name, controller_index);
+		  tp->mtable = last_mediatable;
+		  ee_data = last_ee_data;
+		  goto subsequent_board;
+		} else
+		printk("%s:  Missing EEPROM, this device may not work correctly!\n",
+			   dev->name);
+		return;
+	  }
+	  /* Do a fix-up based on the vendor half of the station address prefix. */
+	  for (i = 0; eeprom_fixups[i].name; i++) {
+		if (dev->dev_addr[0] == eeprom_fixups[i].addr0
+			&&  dev->dev_addr[1] == eeprom_fixups[i].addr1
+			&&  dev->dev_addr[2] == eeprom_fixups[i].addr2) {
+		  if (dev->dev_addr[2] == 0xE8  &&  ee_data[0x1a] == 0x55)
+			  i++;			/* An Accton EN1207, not an outlaw Maxtech. */
+		  memcpy(ee_data + 26, eeprom_fixups[i].newtable,
+				 sizeof(eeprom_fixups[i].newtable));
+		  printk("\n%s: Old format EEPROM on '%s' board.  Using substitute"
+				 " media control info.\n",
+				 dev->name, eeprom_fixups[i].name);
+		  break;
+		}
+	  }
+	  if (eeprom_fixups[i].name == NULL) { /* No fixup found. */
+		printk("\n %s: Old style EEPROM -- no media selection information.\n",
+			   dev->name);
+		return;
+	  }
+	}
+	if (tulip_debug > 1) {
+	  printk("\nread_eeprom:");
+	  for (i = 0; i < 64; i++) {
+		printk("%c%4.4x", (i & 7) == 0 ? '\n':' ',
+			   read_eeprom(ioaddr, i));
+	  }
+	  printk("\n");
+	}
+	
+	controller_index = 0;
+	if (ee_data[19] > 1) {		/* Multiport board. */
+		last_ee_data = ee_data;
+	}
+subsequent_board:
+
+	if (tp->chip_id == DC21041) {
+		unsigned char *p = (void *)ee_data + ee_data[27 + controller_index*3];
+		short media = *(u16 *)p;
+		int count = p[2];
+
+		printk("%s:21041 Media information at %d, default media %4.4x"
+			   " (%s).\n", dev->name, ee_data[27], media,
+			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
+		for (i = 0; i < count; i++) {
+			unsigned char media_code = p[3 + i*7];
+			unsigned short *csrvals = (unsigned short *)&p[3 + i*7 + 1];
+			printk("%s:  21041 media %2.2x (%s),"
+				   " csr13 %4.4x csr14 %4.4x csr15 %4.4x.\n",
+				   dev->name, media_code & 15, medianame[media_code & 15],
+				   csrvals[0], csrvals[1], csrvals[2]);
+		}
+	} else {
+		unsigned char *p = (void *)ee_data + ee_data[27];
+		unsigned char csr12dir = 0;
+		int count;
+		struct mediatable *mtable;
+		short media = *((u16 *)p)++;
+
+		if (tp->chip_id == DC21140)
+			csr12dir = *p++;
+		count = *p++;
+		mtable = (struct mediatable *)
+			kmalloc(sizeof(struct mediatable) + count*sizeof(struct medialeaf),
+					GFP_KERNEL);
+		if (mtable == NULL)
+			return;				/* Horrible, impossible failure. */
+		last_mediatable = tp->mtable = mtable;
+		mtable->defaultmedia = media;
+		mtable->leafcount = count;
+		mtable->csr12dir = csr12dir;
+		mtable->has_mii = 0;
+
+		printk("%s: EEPROM default media type %s.\n", dev->name,
+			   media & 0x0800 ? "Autosense" : medianame[media & 15]);
+		for (i = 0; i < count; i++) {
+			struct medialeaf *leaf = &mtable->mleaf[i];
+			
+			if ((p[0] & 0x80) == 0) { /* 21140 Compact block. */
+				leaf->type = 0;
+				leaf->media = p[0] & 0x3f;
+				leaf->leafdata = p;
+				p += 4;
+			} else {
+				leaf->type = p[1];
+				if (p[1] & 1) {
+					mtable->has_mii = 1;
+					leaf->media = 11;
+				} else
+					leaf->media = p[2] & 0x0f;
+				leaf->leafdata = p + 2;
+				p += (p[0] & 0x3f) + 1;
+			}
+			if (tulip_debug > 1  &&  leaf->media == 11) {
+				unsigned char *bp = leaf->leafdata;
+				printk("%s:  MII interface PHY %d, setup/reset sequences"
+					   " %d/%d long, capabilities %2.2x %2.2x.\n",
+					   dev->name, bp[0], bp[1], bp[1 + bp[1]*2],
+					   bp[5 + bp[2 + bp[1]*2]*2], bp[4 + bp[2 + bp[1]*2]*2]);
+				if (tulip_debug > 2) { /* DEBUG only, should be > 3 */
+					int mii_reg;
+					printk("%s:  MII xcvr control registers:", dev->name);
+					for (mii_reg = 0; mii_reg < 32; mii_reg++)
+						printk(" %4.4x", mdio_read(ioaddr,bp[0], mii_reg));
+					printk(".\n");
+				}
+			}
+
+			printk("%s: Index #%d - Media %s (#%d) described by a %s (%d) block.\n",
+				   dev->name, i, medianame[leaf->media], leaf->media,
+				   block_name[leaf->type], leaf->type);
+		}
+	}
 }
+/* Reading a serial EEPROM is a "bit" grungy, but we work our way through:->.*/
 
-static void
-generic21140_select(struct device *dev)
-{
-	int ioaddr = dev->base_addr, csr6;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+/*  EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK	0x02	/* EEPROM shift clock. */
+#define EE_CS			0x01	/* EEPROM chip select. */
+#define EE_DATA_WRITE	0x04	/* EEPROM chip data in. */
+#define EE_WRITE_0		0x01
+#define EE_WRITE_1		0x05
+#define EE_DATA_READ	0x08	/* EEPROM chip data out. */
+#define EE_ENB			(0x4800 | EE_CS)
+
+/* Delay between EEPROM clock transitions.
+   The 1.2 code is a "nasty" timing loop, but PC compatible machines are
+   *supposed* to delay an ISA-compatible period for the SLOW_DOWN_IO macro.  */
+#ifdef _LINUX_DELAY_H
+#define eeprom_delay(nanosec)	udelay((nanosec + 999)/1000)
+#else
+#define eeprom_delay(nanosec)	do { int _i = 3; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+#endif
 
-	dev->if_port &= 1;
-	csr6 = tio_read(CSR6) &
-		~(TCMOD_10TP|TCMOD_100TP|TCMOD_TRxSTART|TCMOD_SCRM);
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD	(5 << 6)
+#define EE_READ_CMD		(6 << 6)
+#define EE_ERASE_CMD	(7 << 6)
 
-	/* Stop the transmit process. */
-	tio_write(csr6 | TCMOD_RxSTART, CSR6);
-	if (dev->start)
-		printk("%s: enabling %s port.\n",
-			   dev->name, dev->if_port ? "100TX" : "10TP");
-	tio_write((dev->if_port ? TCMOD_100TP: TCMOD_10TP)
-			  | TCMOD_TRxSTART | TCMOD_TH128 | csr6, CSR6);
-	tio_write((dev->if_port ? TGEPR_FORCE100: 0)
-			  | (tp->full_duplex ? 0:TGEPR_HALFDUPLEX), CSR12);
+static int read_eeprom(int ioaddr, int location)
+{
+	int i;
+	unsigned short retval = 0;
+	int ee_addr = ioaddr + CSR9;
+	int read_cmd = location | EE_READ_CMD;
+	
+	outl(EE_ENB & ~EE_CS, ee_addr);
+	outl(EE_ENB, ee_addr);
+	
+	/* Shift the read command bits out. */
+	for (i = 10; i >= 0; i--) {
+		short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+		outl(EE_ENB | dataval, ee_addr);
+		eeprom_delay(100);
+		outl(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay(150);
+		outl(EE_ENB | dataval, ee_addr);	/* Finish EEPROM a clock tick. */
+		eeprom_delay(250);
+	}
+	outl(EE_ENB, ee_addr);
+	
+	for (i = 16; i > 0; i--) {
+		outl(EE_ENB | EE_SHIFT_CLK, ee_addr);
+		eeprom_delay(100);
+		retval = (retval << 1) | ((inl(ee_addr) & EE_DATA_READ) ? 1 : 0);
+		outl(EE_ENB, ee_addr);
+		eeprom_delay(100);
+	}
+
+	/* Terminate the EEPROM access. */
+	outl(EE_ENB & ~EE_CS, ee_addr);
+	return retval;
+}
+
+/* Read and write the MII registers using software-generated serial
+   MDIO protocol.  It is just different enough from the EEPROM protocol
+   to not share code.  The maxium data clock rate is 2.5 Mhz. */
+#define MDIO_SHIFT_CLK	0x10000
+#define MDIO_DATA_WRITE 0x20000
+#define MDIO_ENB		0x40000
+#define MDIO_DATA_READ	0x80000
+static int mdio_read(int ioaddr, int phy_id, int location)
+{
+	int i;
+	int read_cmd = (0xf6 << 10) | (phy_id << 5) | location;
+	unsigned short retval = 0;
+	int ee_addr = ioaddr + CSR9;
+
+	/* Shift the read command bits out. */
+	for (i = 18; i >= 0; i--) {
+		int dataval =
+		  (read_cmd & (1 << i)) ? MDIO_DATA_WRITE : 0;
+
+		outl(MDIO_ENB | dataval, ee_addr);
+		eeprom_delay(100);
+		outl(MDIO_ENB | dataval | MDIO_SHIFT_CLK, ee_addr);
+		eeprom_delay(250);
+		outl(MDIO_ENB | dataval, ee_addr);
+		eeprom_delay(150);
+	}
+
+	for (i = 16; i > 0; i--) {
+		outl(MDIO_SHIFT_CLK, ee_addr);
+		eeprom_delay(250);
+		retval = (retval << 1) | ((inl(ee_addr) & MDIO_DATA_READ) ? 1 : 0);
+		outl(0, ee_addr);
+		eeprom_delay(250);
+	}
+	return retval;
 }
 
+
 static int
 tulip_open(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 	int ioaddr = dev->base_addr;
-	int i;
+	int i = 0;
 
-	/* Reset the chip, holding bit 0 set at least 10 PCI cycles. */
-	tio_write(tio_read(CSR0)|TBMOD_RESET, CSR0);
-	udelay(1000);
-	/* Deassert reset.  Set 8 longword cache alignment, 8 longword burst.
-	   -> Set 32 longword cache alignment, unlimited longword burst ?
+	/* On some chip revs we must set the MII/SYM port before the reset!? */
+	if (tp->mtable  &&  tp->mtable->has_mii)
+		outl(0x00040000, ioaddr + CSR6);
+
+	/* Reset the chip, holding bit 0 set at least 50 PCI cycles. */
+	outl(0x00000001, ioaddr + CSR0);
+#ifdef _LINUX_DELAY_H
+	udelay(2);
+#else
+	SLOW_DOWN_IO;
+#endif
+	/* Deassert reset.
+	   486: Set 8 longword cache alignment, 8 longword burst.
+	   586: Set 16 longword cache alignment, no burst limit.
+	   Cache alignment bits 15:14	     Burst length 13:8
+		0000	No alignment  0x00000000 unlimited		0800 8 longwords
+		4000	8  longwords		0100 1 longword		1000 16 longwords
+		8000	16 longwords		0200 2 longwords	2000 32 longwords
+		C000	32  longwords		0400 4 longwords
 	   Wait the specified 50 PCI cycles after a reset by initializing
 	   Tx and Rx queues and the address filter list. */
-	tio_write(tio_read(CSR0)|TBMOD_ALIGN32|TBMOD_BURST0, CSR0);
+#if defined(__alpha)
+	/* ToDo: Alpha setting could be better. */
+	outl(0x00200000 | 0xE000, ioaddr + CSR0);
+#else
+#if defined(MODULE)
+	/* When a module we don't have 'x86' to check. */
+	outl(0x00200000 | 0x4800, ioaddr + CSR0);
+#else
+	outl(0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000), ioaddr + CSR0);
+	if (x86 <= 4)
+	  printk("This is a 386/486 PCI system, setting cache alignment to %x.\n",
+			 0x00200000 | (x86 <= 4 ? 0x4800 : 0x8000));
+#endif
+#endif
 
-	if (request_irq(dev->irq, (void *)&tulip_interrupt, SA_SHIRQ,
-					tp->signature, dev))
+#ifdef SA_SHIRQ
+	if (request_irq(dev->irq, &tulip_interrupt, SA_SHIRQ,
+					tulip_tbl[tp->chip_id].chip_name, dev)) {
 		return -EAGAIN;
+	}
+#else
+	if (irq2dev_map[dev->irq] != NULL
+		|| (irq2dev_map[dev->irq] = dev) == NULL
+		|| dev->irq == 0
+		|| request_irq(dev->irq, &tulip_interrupt, 0,
+					   tulip_tbl[tp->chip_id].chip_name)) {
+		return -EAGAIN;
+	}
+#endif
+
+	if (tulip_debug > 1)
+		printk("%s: tulip_open() irq %d.\n", dev->name, dev->irq);
+
+	MOD_INC_USE_COUNT;
 
 	tulip_init_ring(dev);
 
+	/* This is set_rx_mode(), but without starting the transmitter. */
 	/* Fill the whole address filter table with our physical address. */
-	{ 
-		unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
+	{
+		u16 *eaddrs = (u16 *)dev->dev_addr;
 		int *setup_frm = tp->setup_frame, i;
 
 		/* You must add the broadcast address when doing perfect filtering! */
@@ -721,57 +1031,463 @@
 		/* Put the setup frame on the Tx list. */
 		tp->tx_ring[0].length = 0x08000000 | 192;
 		tp->tx_ring[0].buffer1 = virt_to_bus(tp->setup_frame);
-		tp->tx_ring[0].buffer2 = 0;
-		tp->tx_ring[0].status = TRING_OWN;
-		barrier();
-		tp->cur_tx++, tp->dirty_tx++;
+		tp->tx_ring[0].status = 0x80000000;
+
+		tp->cur_tx++;
 	}
 
-	tio_write(virt_to_bus(tp->rx_ring), CSR3);
-	tio_write(virt_to_bus(tp->tx_ring), CSR4);
+	outl(virt_to_bus(tp->rx_ring), ioaddr + CSR3);
+	outl(virt_to_bus(tp->tx_ring), ioaddr + CSR4);
+
+	if (dev->if_port == 0)
+	  dev->if_port = tp->default_port;
+	if (tp->chip_id == DC21041  &&  dev->if_port > 4)
+		/* Invalid: Select initial TP, autosense, autonegotiate.  */
+		dev->if_port = 4;
+
+	/* Allow selecting a default media. */
+	if (tp->mtable == NULL)
+		goto media_picked;
+	if (dev->if_port)
+		for (i = 0; i < tp->mtable->leafcount; i++)
+		  if (tp->mtable->mleaf[i].media ==
+			  (dev->if_port == 12 ? 0 : dev->if_port)) {
+			printk("%s: Using user-specified media %s.\n",
+				   dev->name, medianame[dev->if_port]);
+			goto media_picked;
+		  }
+	if ((tp->mtable->defaultmedia & 0x0800) == 0)
+		for (i = 0; i < tp->mtable->leafcount; i++)
+		  if (tp->mtable->mleaf[i].media == (tp->mtable->defaultmedia & 15)) {
+			printk("%s: Using EEPROM-set media %s.\n",
+				   dev->name, medianame[tp->mtable->mleaf[i].media]);
+			goto media_picked;
+		  }
+	for (i = tp->mtable->leafcount - 1;
+		 (media_fd[tp->mtable->mleaf[i].media] & 2) && i > 0; i--)
+	  ;
+media_picked:
+
+	tp->cur_index = i;
+	tp->csr6 = 0;
+	select_media(dev, 1);
+
+	/* Start the chip's Tx to process setup frame. */
+	outl(tp->csr6, ioaddr + CSR6);
+	outl(tp->csr6 | 0x2000, ioaddr + CSR6);
 
 	dev->tbusy = 0;
 	dev->interrupt = 0;
 	dev->start = 1;
-	/*
-	 * process setup frame completely prior to fiddling with media.
-	 */
-	tio_write((tio_read(CSR6) & ~TCMOD_PROMISC) | TCMOD_TxSTART, CSR6);
-	tio_write(TPOLL_TRIGGER, CSR1);
-	sti();
-	for (i = 0; i < 1000; i++) {
-		if (tp->tx_ring[0].status >= 0) {
+
+
+	/* Enable interrupts by setting the interrupt mask. */
+	outl(0x0001fbff, ioaddr + CSR7);
+	outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+	outl(0, ioaddr + CSR2);		/* Rx poll demand */
+
+	if (tulip_debug > 2) {
+		printk("%s: Done tulip_open(), CSR0 %8.8x, CSR5 %8.8x CSR13 %8.8x.\n",
+			   dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR5),
+			   inl(ioaddr + CSR13));
+	}
+	/* Set the timer to switch to check for link beat and perhaps switch
+	   to an alternate media type. */
+	init_timer(&tp->timer);
+	tp->timer.expires = RUN_AT((24*HZ)/10);			/* 2.4 sec. */
+	tp->timer.data = (unsigned long)dev;
+	tp->timer.function = &tulip_timer;				/* timer handler */
+	add_timer(&tp->timer);
+
+	return 0;
+}
+
+/* Set up the transceiver control registers for the selected media type. */
+static void select_media(struct device *dev, int startup)
+{
+	int ioaddr = dev->base_addr;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	struct mediatable *mtable = tp->mtable;
+	u32 new_csr6;
+	int i;
+
+	if (mtable) {
+		struct medialeaf *mleaf = &mtable->mleaf[tp->cur_index];
+		unsigned char *p = mleaf->leafdata;
+		switch (mleaf->type) {
+		case 0:					/* 21140 non-MII xcvr. */
+			if (tulip_debug > 1)
+				printk("%s: Using a 21140 non-MII transceiver with control"
+					   " setting %2.2x.\n",
+					   dev->name, p[1]);
+			dev->if_port = p[0];
+			if (startup)
+				outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+			outl(p[1], ioaddr + CSR12);
+			new_csr6 = 0x02000000 | ((p[2] & 0x71) << 18);
+			break;
+		case 1:
+			if (startup) {
+				outl(mtable->csr12dir | 0x100, ioaddr + CSR12);
+				dev->if_port = 11;
+				if (tulip_debug > 2)
+					printk("%s:  Doing a reset sequence of length %d.\n",
+						   dev->name, p[2 + p[1]]);
+				for (i = 0; i < p[2 + p[1]]; i++)
+					outl(p[3 + p[1] + i], ioaddr + CSR12);
+				if (tulip_debug > 2)
+					printk("%s  Doing a transceiver setup sequence of length %d.\n",
+						   dev->name, p[1]);
+				for (i = 0; i < p[1]; i++)
+					outl(p[2 + i], ioaddr + CSR12);
+			}
+			new_csr6 = 0x020C0000;
 			break;
+		case 2: case 4: {
+			u16 *setup = (u16*)&p[1];
+			dev->if_port = p[0] & 15;
+			if (tulip_debug > 1)
+				printk("%s: 21142 non-MII %s transceiver control %4.4x/%4.4x.\n",
+					   dev->name, medianame[dev->if_port], setup[0], setup[1]);
+			if (p[0] & 0x40) {	/* SIA (CSR13-15) setup values are provided. */
+				outl(0, ioaddr + CSR13);
+				outl(setup[1], ioaddr + CSR14);
+				outl(setup[2], ioaddr + CSR15);
+				outl(setup[0], ioaddr + CSR13);
+				setup += 3;
+			} else {
+				outl(0, ioaddr + CSR13);
+				outl(t21142_csr14[dev->if_port], ioaddr + CSR14);
+				outl(t21142_csr15[dev->if_port], ioaddr + CSR15);
+				outl(t21142_csr13[dev->if_port], ioaddr + CSR13);
+			}
+			outl(setup[0]<<16, ioaddr + CSR15);	/* Direction */
+			outl(setup[1]<<16, ioaddr + CSR15);	/* Data */
+			new_csr6 = 0x02000000;
+			break;
+		}
+		case 3: {
+			int init_length = p[1];
+			u16 * init_sequence = (u16*)(p + 2);
+			int reset_length = p[2 + init_length*2];
+			u16 * reset_sequence = (u16*)&p[3 + init_length*2];
+
+			dev->if_port = 11;
+			if (startup) {
+				if (tulip_debug > 2)
+					printk("%s:  Doing a 21142 reset sequence of length %d.\n",
+						   dev->name, reset_length);
+				for (i = 0; i < reset_length; i++)
+					outl(reset_sequence[i] << 16, ioaddr + CSR15);
+			}
+			if (tulip_debug > 2)
+				printk("%s: Doing a 21142 xcvr setup sequence of length %d.\n",
+					   dev->name, init_length);
+			for (i = 0; i < init_length; i++)
+				outl(init_sequence[i] << 16, ioaddr + CSR15);
+			new_csr6 = 0x020C0000 | (tp->full_duplex ? 0x0200 : 0);
+			break;
+		}
+		default:
+		  new_csr6 = 0x020C0000;
+		}
+		if (tulip_debug > 1)
+			printk("%s: Using media type %s, CSR12 is %2.2x.\n",
+				   dev->name, medianame[dev->if_port],
+				   inl(ioaddr + CSR12) & 0xff);
+	} else if (tp->chip_id == DC21140) {
+		/* Set media type to MII @ 100mbps: 0x020C0000 */
+		new_csr6 = 0x020C0000;
+		dev->if_port = 11;
+		if (tulip_debug > 1) {
+			printk("%s: Unknown media control, assuming MII, CSR12 %2.2x.\n",
+				   dev->name, inl(ioaddr + CSR12) & 0xff);
 		}
-		udelay(1000);
+	} else if (tp->chip_id == DC21041) {
+		if (tulip_debug > 1)
+			printk("%s: 21041 using media %s, CSR12 is %4.4x.\n",
+				   dev->name, medianame[dev->if_port & 15],
+				   inl(ioaddr + CSR12) & 0xffff);
+		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+		outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
+		outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
+		outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
+		new_csr6 = 0x80020000;
+	} else {					/* 21040 */
+		/* Turn on the xcvr interface. */
+		int csr12 = inl(ioaddr + CSR12);
+		if (tulip_debug > 1)
+			printk("%s: 21040 media type is %s, CSR12 is %2.2x.\n",
+				   dev->name, dev->if_port ? "AUI" : "10baseT", csr12);
+		new_csr6 = (dev->if_port ? 0x01860000 : 0x00420000);
+		/* Set the full duplux match frame. */
+		outl(FULL_DUPLEX_MAGIC, ioaddr + CSR11);
+		outl(0x00000000, ioaddr + CSR13); /* Reset the serial interface */
+		outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
 	}
-	if (i == 500) {
-		printk("%s: initial setup frame didn't complete.\n", dev->name);
-		dev->start = 0;
-		dev->tbusy = 1;
-		tio_write(TINTR_DISABLE, CSR7);
-		tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6);
-		tio_write(TSIAC_CONFIG, CSR13);
-		tio_write(0, CSR13);
-		free_irq(dev->irq, dev);
-		return (-EIO);
-	}
-	/*
-	 * Whack the chip to stop it and *then* do initial media setup.
-	 */
-	tio_write((tio_read(CSR6) & ~(TCMOD_PROMISC|TCMOD_TxSTART)), CSR6);
-	if (tp->port_select)
-		tp->port_select(dev);
-	/* Start the chip's Tx and Rx processes. */
-	tio_write(tio_read(CSR6) | TCMOD_TRxSTART
-			  | (tp->full_duplex ? TCMOD_FULLDUPLEX:0), CSR6);
-	/* Enable interrupts by setting the interrupt mask. */
-	tio_write(TINTR_ENABLE, CSR7);
 
-	MOD_INC_USE_COUNT;
-	return 0;
+	tp->csr6 = new_csr6 | (tp->csr6 & 0xfdff) | (tp->full_duplex ? 0x0200 : 0);
+	return;
 }
 
+static void tulip_timer(unsigned long data)
+{
+	struct device *dev = (struct device *)data;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int ioaddr = dev->base_addr;
+	u32 csr12 = inl(ioaddr + CSR12);
+	int next_tick = 0;
+
+	if (tulip_debug > 3) {
+		printk("%s: Media selection tick, status %8.8x mode %8.8x "
+			   "SIA %8.8x %8.8x %8.8x %8.8x.\n",
+			   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR6),
+			   csr12, inl(ioaddr + CSR13),
+			   inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+	}
+	switch (tp->chip_id) {
+	case DC21040:
+		if (csr12 & 0x0002) { /* Network error */
+			printk("%s: No 10baseT link beat found, switching to %s media.\n",
+				   dev->name, dev->if_port ? "10baseT" : "AUI");
+			dev->if_port ^= 1;
+			outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
+			dev->trans_start = jiffies;
+		}
+		break;
+	case DC21041:
+		if (tulip_debug > 2)
+			printk("%s: 21041 media tick  CSR12 %8.8x.\n",
+				   dev->name, csr12);
+		switch (dev->if_port) {
+		case 0: case 3: case 4:
+		  if (csr12 & 0x0004) { /*LnkFail */
+			/* 10baseT is dead.  Check for activity on alternate port. */
+			tp->mediasense = 1;
+			if (csr12 & 0x0200)
+				dev->if_port = 2;
+			else
+				dev->if_port = 1;
+			printk("%s: No 21041 10baseT link beat, Media switched to %s.\n",
+				   dev->name, medianame[dev->if_port]);
+			outl(0, ioaddr + CSR13); /* Reset */
+			outl(t21041_csr14[dev->if_port], ioaddr + CSR14);
+			outl(t21041_csr15[dev->if_port], ioaddr + CSR15);
+			outl(t21041_csr13[dev->if_port], ioaddr + CSR13);
+			next_tick = 10*HZ;			/* 2.4 sec. */
+		  } else
+			next_tick = 30*HZ;
+		  break;
+		case 1:					/* 10base2 */
+		case 2:					/* AUI */
+		  if (csr12 & 0x0100) {
+			next_tick = (30*HZ);			/* 30 sec. */
+			tp->mediasense = 0;
+		  } else if ((csr12 & 0x0004) == 0) {
+			printk("%s: 21041 media switched to 10baseT.\n", dev->name);
+			dev->if_port = 0;
+			select_media(dev, 0);
+			next_tick = (24*HZ)/10;				/* 2.4 sec. */
+		  } else if (tp->mediasense || (csr12 & 0x0002)) {
+			dev->if_port = 3 - dev->if_port; /* Swap ports. */
+			select_media(dev, 0);
+			next_tick = 20*HZ;
+		  } else {
+			next_tick = 20*HZ;
+		  }
+		  break;
+		}
+		break;
+	case DC21140:  case DC21142: {
+		struct medialeaf *mleaf;
+		unsigned char *p;
+		if (tp->mtable == NULL) {	/* No EEPROM info, use generic code. */
+			/* Assume this is like a SMC card, and check its link beat bit. */
+			if ((dev->if_port == 0 && (csr12 & 0x0080)) ||
+				(dev->if_port == 1 && (csr12 & 0x0040) == 0)) {
+				dev->if_port ^= 1;
+				/* Stop the transmit process. */
+				tp->csr6 = (dev->if_port ? 0x03860000 : 0x02420000);
+				outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+				printk("%s: link beat timed out, CSR12 is 0x%2.2x, switching to"
+					   " %s media.\n", dev->name,
+					   csr12 & 0xff,
+					   dev->if_port ? "100baseTx" : "10baseT");
+				outl(tp->csr6 | 0xA002, ioaddr + CSR6);
+				dev->trans_start = jiffies;
+				next_tick = (24*HZ)/10;
+			} else {
+				next_tick = 10*HZ;
+				if (tulip_debug > 2)
+					printk("%s: network media monitor 0x%2.2x, link"
+						   " beat detected as %s.\n", dev->name,
+						   csr12 & 0xff,
+						   dev->if_port ? "100baseTx" : "10baseT");
+			}
+			break;
+		}
+	  mleaf = &tp->mtable->mleaf[tp->cur_index];
+	  p = mleaf->leafdata;
+	  switch (mleaf->type) {
+	  case 0: case 4: {
+		/* Type 0 non-MII or #4 SYM transceiver.  Check the link beat bit. */
+		  s8 bitnum = p[mleaf->type == 4 ? 5 : 2];
+		  if (tulip_debug > 2)
+			  printk("%s: Transceiver monitor tick: CSR12=%#2.2x bit %d is"
+					 " %d, expecting %d.\n",
+					 dev->name, csr12, (bitnum >> 1) & 7,
+					 (csr12 & (1 << ((bitnum >> 1) & 7))) != 0,
+					 (bitnum >= 0));
+		  /* Check that the specified bit has the proper value. */
+		  if ((bitnum < 0) !=
+			  ((csr12 & (1 << ((bitnum >> 1) & 7))) != 0)) {
+			  if (tulip_debug > 1)
+				  printk("%s: Link beat detected for %s.\n", dev->name,
+						 medianame[mleaf->media]);
+			  break;
+		  }
+		  if (tp->medialock)
+			break;
+	  select_next_media:
+		  if (--tp->cur_index < 0) {
+			/* We start again, but should instead look for default. */
+			tp->cur_index = tp->mtable->leafcount - 1;
+		  }
+		  dev->if_port = tp->mtable->mleaf[tp->cur_index].media;
+		  if (media_fd[dev->if_port])
+			goto select_next_media; /* Skip FD entries. */
+		  if (tulip_debug > 1)
+			  printk("%s: No link beat on media %s,"
+					 " trying transceiver type %s.\n",
+					 dev->name, medianame[mleaf->media & 15],
+					 medianame[tp->mtable->mleaf[tp->cur_index].media]);
+		  select_media(dev, 0);
+		  /* Restart the transmit process. */
+		  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+		  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+		  next_tick = (24*HZ)/10;
+		  break;
+	  }
+	  case 1:
+		  printk(" %s: MII monitoring tick: CSR12 status %2.2x.\n",
+				 dev->name, csr12);
+		  /* Hack for D-Link: Full duplex indication is on bit 3. */
+		  if (dev->dev_addr[0] == 0  &&  dev->dev_addr[1] == 0x80
+			  && dev->dev_addr[2] == 0xC8) {
+			  /* The first message is for information only. */
+			  if (tp->full_duplex) {
+				  printk("%s: D-Link card in full-duplex mode, csr6 setting"
+						 " %8.8x.\n", dev->name, tp->csr6);
+			  } else if (csr12 & 0x08) {
+				  tp->full_duplex = 0;
+				  tp->csr6 &= ~0x0200;
+				  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+				  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+			  } else {
+				  tp->full_duplex = 1;
+				  tp->csr6 |= 0x0200;
+				  printk("%s: Switching D-Link card to full-duplex.\n", dev->name);
+				  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+				  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+			  }
+		  }
+		  break;
+	  case 2:					/* 21142 non-MII */
+	  case 3:					/* 21142 MII */
+		  next_tick = (24*HZ)/10;
+		  break;
+	  default:
+		  break;
+	  }
+	}
+	default:					/* Invalid chip type. */
+	  break;
+	}
+	if (next_tick) {
+		tp->timer.expires = RUN_AT(next_tick);
+		add_timer(&tp->timer);
+	}
+}
+
+static void tulip_tx_timeout(struct device *dev)
+{
+  struct tulip_private *tp = (struct tulip_private *)dev->priv;
+  int ioaddr = dev->base_addr;
+  int i;
+
+  if (tp->mtable && tp->mtable->has_mii) {
+	/* Do nothing -- the media monitor should handle this. */
+	if (tulip_debug > 1)
+	  printk("%s: Transmit timeout using MII device.\n", dev->name);
+  } else if (tp->chip_id == DC21040) {
+	  if (inl(ioaddr + CSR12) & 0x0002) {
+		  printk("%s: transmit timed out, switching to %s media.\n",
+				 dev->name, dev->if_port ? "10baseT" : "AUI");
+		  dev->if_port ^= 1;
+		  outl(dev->if_port ? 0x0000000C : 0x00000004, ioaddr + CSR13);
+	  }
+	  dev->trans_start = jiffies;
+	  return;
+  } else if (tp->chip_id == DC21140 || tp->chip_id == DC21142) {
+	/* Stop the transmit process. */
+	outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+	dev->if_port ^= 1;
+	printk("%s: 21140 transmit timed out, status %8.8x, SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
+		   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
+		   inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+	printk("%s: transmit timed out, switching to %s media.\n",
+		   dev->name, dev->if_port ? "100baseTx" : "10baseT");
+	outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+	tp->stats.tx_errors++;
+	dev->trans_start = jiffies;
+	return;
+  } else if (tp->chip_id == DC21041) {
+	u32 csr12 = inl(ioaddr + CSR12);
+
+	printk("%s: 21041 transmit timed out, status %8.8x, CSR12 %8.8x,"
+		   " CSR13 %8.8x, CSR14 %8.8x, resetting...\n",
+		   dev->name, inl(ioaddr + CSR5), csr12,
+		   inl(ioaddr + CSR13), inl(ioaddr + CSR14));
+	tp->mediasense = 1;
+	if (dev->if_port == 1 || dev->if_port == 2)
+		if (csr12 & 0x0004) {
+			dev->if_port = 2 - dev->if_port;
+		} else
+			dev->if_port = 0;
+	else
+		dev->if_port = 1;
+	select_media(dev, 0);
+	tp->stats.tx_errors++;
+	dev->trans_start = jiffies;
+	return;
+  } else
+	printk("%s: transmit timed out, status %8.8x, CSR12 %8.8x,"
+		   " resetting...\n",
+		   dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12));
+#ifndef __alpha__
+  printk("  Rx ring %8.8x: ", (int)tp->rx_ring);
+  for (i = 0; i < RX_RING_SIZE; i++)
+	printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
+  printk("\n  Tx ring %8.8x: ", (int)tp->tx_ring);
+  for (i = 0; i < TX_RING_SIZE; i++)
+	printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
+  printk("\n");
+#endif
+
+  /* Perhaps we should reinitialize the hardware here. */
+  dev->if_port = 0;
+  /* Stop and restart the chip's Tx processes . */
+  outl(tp->csr6 | 0x0002, ioaddr + CSR6);
+  outl(tp->csr6 | 0x2002, ioaddr + CSR6);
+  /* Trigger an immediate transmit demand. */
+  outl(0, ioaddr + CSR1);
+
+  dev->trans_start = jiffies;
+  tp->stats.tx_errors++;
+  return;
+}
+
+
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
 static void
 tulip_init_ring(struct device *dev)
@@ -784,85 +1500,62 @@
 	tp->dirty_rx = tp->dirty_tx = 0;
 
 	for (i = 0; i < RX_RING_SIZE; i++) {
-		tp->rx_ring[i].status = TRING_OWN;
+		tp->rx_ring[i].status = 0x80000000;	/* Owned by Tulip chip */
 		tp->rx_ring[i].length = PKT_BUF_SZ;
-		tp->rx_ring[i].buffer1 = virt_to_bus(tp->rx_buffs[i]);
+		{
+			/* Note the receive buffer must be longword aligned.
+			   dev_alloc_skb() provides 16 byte alignment.  But do *not*
+			   use skb_reserve() to align the IP header! */
+			struct sk_buff *skb;
+			skb = DEV_ALLOC_SKB(PKT_BUF_SZ);
+			tp->rx_skbuff[i] = skb;
+			if (skb == NULL)
+				break;			/* Bad news!  */
+			skb->dev = dev;			/* Mark as being used by this device. */
+#if LINUX_VERSION_CODE > 0x10300
+			tp->rx_ring[i].buffer1 = virt_to_bus(skb->tail);
+#else
+			tp->rx_ring[i].buffer1 = virt_to_bus(skb->data);
+#endif
+		}
 		tp->rx_ring[i].buffer2 = virt_to_bus(&tp->rx_ring[i+1]);
 	}
-	/* Mark the last entry as wrapping the ring. */ 
+	/* Mark the last entry as wrapping the ring. */
 	tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000;
 	tp->rx_ring[i-1].buffer2 = virt_to_bus(&tp->rx_ring[0]);
 
 	/* The Tx buffer descriptor is filled in as needed, but we
 	   do need to clear the ownership bit. */
 	for (i = 0; i < TX_RING_SIZE; i++) {
+		tp->tx_skbuff[i] = 0;
 		tp->tx_ring[i].status = 0x00000000;
+		tp->tx_ring[i].buffer2 = virt_to_bus(&tp->tx_ring[i+1]);
 	}
+	tp->tx_ring[i-1].buffer2 = virt_to_bus(&tp->tx_ring[0]);
 }
 
 static int
 tulip_start_xmit(struct sk_buff *skb, struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int ioaddr = dev->base_addr;
-	int entry, len;
-	unsigned long daddr;
+	int entry;
+	u32 flag;
 
-	/* Transmitter timeout, serious problems. */
-	if (dev->tbusy || (tp->port_fail && tp->port_fail(dev))) {
-		int tickssofar = jiffies - dev->trans_start;
-		int i;
-		if (tickssofar < 40) return(1);
-		if (tp->port_select) {
-			if (!tp->port_fix) dev->if_port ++;
-			tp->port_select(dev);
-			dev->trans_start = jiffies;
-			dev_kfree_skb(skb, FREE_WRITE);
-			return(0);
-		}
-		printk("%s: transmit timed out, status %8.8x,"
-			   "SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
-			   dev->name, tio_read(CSR5), tio_read(CSR12),
-			   tio_read(CSR13), tio_read(CSR14), tio_read(CSR15));
-#ifndef	__alpha__
-		printk("  Rx ring %8.8x: ", (int)tp->rx_ring);
-#endif
-		for (i = 0; i < RX_RING_SIZE; i++)
-			printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
-#ifndef	__alpha__
-		printk("\n  Tx ring %8.8x: ", (int)tp->tx_ring);
-#endif
-		for (i = 0; i < TX_RING_SIZE; i++)
-			printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
-		printk("\n");
-
-		tp->stats.tx_errors++;
-		/* Perhaps we should reinitialize the hardware here. */
-		dev->if_port = 0;
-		tio_write(TSIAC_CONFIG, CSR13);
-		/* Start the chip's Tx and Rx processes . */
-		tio_write(TCMOD_10TP | TCMOD_TRxSTART, CSR6);
-		/* Trigger an immediate transmit demand. */
-		tio_write(TPOLL_TRIGGER, CSR1);
-
-		dev->tbusy=0;
-		dev->trans_start = jiffies;
-		dev_kfree_skb(skb, FREE_WRITE);
-		return(0);
-	}
-
-	if (skb == NULL || (skb != (struct sk_buff *) -1 && skb->len <= 0)) {
+#ifndef final_version
+	if (skb == NULL || skb->len <= 0) {
 		printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
 			   dev->name);
 		dev_tint(dev);
-		return(0);
+		return 0;
 	}
+#endif
 
 	/* 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);
+	   done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+	if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+		if (jiffies - dev->trans_start < TX_TIMEOUT)
+			return 1;
+		tulip_tx_timeout(dev);
 		return 1;
 	}
 
@@ -872,43 +1565,49 @@
 	/* Calculate the next Tx descriptor entry. */
 	entry = tp->cur_tx % TX_RING_SIZE;
 
-	tp->tx_full = 1;
-	/*
-	 * If skb is == -1, then this is a funky setup_frame redo.
-	 */
-	if (skb == (struct sk_buff *) -1) {
-		daddr = virt_to_bus((char *)tp->setup_frame);
-		len = 192;
-		skb = NULL;
+	tp->tx_skbuff[entry] = skb;
+	tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data);
+
+	if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE/2) {/* Typical path */
+	  flag = 0x60000000; /* No interrupt */
+	  dev->tbusy = 0;
+	} else if (tp->cur_tx - tp->dirty_tx == TX_RING_SIZE/2) {
+	  flag = 0xe0000000; /* Tx-done intr. */
+	  dev->tbusy = 0;
+	} else if (tp->cur_tx - tp->dirty_tx < TX_RING_SIZE - 2) {
+	  flag = 0x60000000; /* No Tx-done intr. */
+	  dev->tbusy = 0;
 	} else {
-		daddr = virt_to_bus(skb->data);
-		len = skb->len;
+	  /* Leave room for set_rx_mode() to fill entries. */
+	  flag = 0xe0000000; /* Tx-done intr. */
+	  tp->tx_full = 1;
 	}
-	tp->tx_skbuff[entry] = skb;
-	tp->tx_ring[entry].length = len |
-		(entry == TX_RING_SIZE-1 ? 0xe2000000 : 0xe0000000);
-	tp->tx_ring[entry].buffer1 = daddr;
-	tp->tx_ring[entry].buffer2 = 0;
-	tp->tx_ring[entry].status = TRING_OWN;	/* Pass ownership to the chip. */
-	barrier();
+	if (entry == TX_RING_SIZE-1)
+		flag |= 0xe2000000;
 
+	tp->tx_ring[entry].length = skb->len | flag;
+	tp->tx_ring[entry].status = 0x80000000;	/* Pass ownership to the chip. */
 	tp->cur_tx++;
-
 	/* Trigger an immediate transmit demand. */
-	tio_write(TPOLL_TRIGGER, CSR1);
+	outl(0, dev->base_addr + CSR1);
 
 	dev->trans_start = jiffies;
 
-	return(0);
+	return 0;
 }
 
 /* The interrupt handler does all of the Rx thread work and cleans up
    after the Tx thread. */
-static void tulip_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static void tulip_interrupt IRQ(int irq, void *dev_instance, struct pt_regs *regs)
 {
-	struct device *dev = (struct device *)dev_id;
+#ifdef SA_SHIRQ		/* Use the now-standard shared IRQ implementation. */
+	struct device *dev = (struct device *)dev_instance;
+#else
+	struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
+
 	struct tulip_private *lp;
-	int csr5, ioaddr, boguscnt=10;
+	int csr5, ioaddr, boguscnt = 10;
 
 	if (dev == NULL) {
 		printk ("tulip_interrupt(): irq %d for unknown device.\n", irq);
@@ -923,33 +1622,46 @@
 	dev->interrupt = 1;
 
 	do {
-		csr5 = tio_read(CSR5);
+		csr5 = inl(ioaddr + CSR5);
 		/* Acknowledge all of the current interrupt sources ASAP. */
-		tio_write(csr5 & TSTAT_CLEARINTR, CSR5);
-		/* check interrupt ? */
-		if ((csr5 & (TSTAT_NORINTR|TSTAT_ABNINTR)) == 0) break;
+		outl(csr5 & 0x0001ffff, ioaddr + CSR5);
 
-		if (csr5 & TSTAT_RxINTR)			/* Rx interrupt */
+		if (tulip_debug > 4)
+			printk("%s: interrupt  csr5=%#8.8x new csr5=%#8.8x.\n",
+				   dev->name, csr5, inl(dev->base_addr + CSR5));
+
+		if ((csr5 & 0x00018000) == 0)
+			break;
+
+		if (csr5 & 0x0040)			/* Rx interrupt */
 			tulip_rx(dev);
 
-		if (csr5 & TSTAT_TxINTR) {		/* Tx-done interrupt */
-			int dirty_tx = lp->dirty_tx;
+		if (csr5 & 0x0007) {		/* Tx-done interrupt */
+			int dirty_tx;
 
-			while (dirty_tx < lp->cur_tx) {
+			for (dirty_tx = lp->dirty_tx; dirty_tx < lp->cur_tx; dirty_tx++) {
 				int entry = dirty_tx % TX_RING_SIZE;
 				int status = lp->tx_ring[entry].status;
 
 				if (status < 0)
 					break;			/* It still hasn't been Txed */
+				/* Check for Rx filter setup frames. */
+				if (lp->tx_skbuff[entry] == NULL)
+				  continue;
 
-				if (status & TRING_ERROR) {
+				if (status & 0x8000) {
 					/* There was an major error, log it. */
+#ifndef final_version
+					if (tulip_debug > 1)
+						printk("%s: Transmit error, Tx status %8.8x.\n",
+							   dev->name, status);
+#endif
 					lp->stats.tx_errors++;
-					if (status & TRING_TxABORT) lp->stats.tx_aborted_errors++;
-					if (status & TRING_TxCARR) lp->stats.tx_carrier_errors++;
-					if (status & TRING_TxWINDOW) lp->stats.tx_window_errors++;
-					if (status & TRING_TxFIFO) lp->stats.tx_fifo_errors++;
-					if ((status & TRING_TxHEARTBEAT) && !lp->full_duplex)
+					if (status & 0x4104) lp->stats.tx_aborted_errors++;
+					if (status & 0x0C00) lp->stats.tx_carrier_errors++;
+					if (status & 0x0200) lp->stats.tx_window_errors++;
+					if (status & 0x0002) lp->stats.tx_fifo_errors++;
+					if ((status & 0x0080) && lp->full_duplex == 0)
 						lp->stats.tx_heartbeat_errors++;
 #ifdef ETHER_STATS
 					if (status & 0x0100) lp->stats.collisions16++;
@@ -963,15 +1675,14 @@
 				}
 
 				/* Free the original skb. */
-				if (lp->tx_skbuff[entry] != NULL)
-					dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
-				dirty_tx++;
+				dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
+				lp->tx_skbuff[entry] = 0;
 			}
 
 #ifndef final_version
-			if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
-				printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
-					   dirty_tx, lp->cur_tx, lp->tx_full);
+			if (lp->cur_tx - dirty_tx > TX_RING_SIZE) {
+				printk("%s: Out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+					   dev->name, dirty_tx, lp->cur_tx, lp->tx_full);
 				dirty_tx += TX_RING_SIZE;
 			}
 #endif
@@ -988,34 +1699,50 @@
 		}
 
 		/* Log errors. */
-		if (csr5 & TSTAT_ABNINTR) {	/* Abnormal error summary bit. */
-			if (csr5 & TSTAT_TxTOUT) lp->stats.tx_errors++; /* Tx babble. */
-			if (csr5 & TSTAT_RxMISSED) { 		/* Missed a Rx frame. */
+		if (csr5 & 0x8000) {	/* Abnormal error summary bit. */
+			if (csr5 & 0x0008) lp->stats.tx_errors++; /* Tx babble. */
+			if (csr5 & 0x0020) { /* Tx FIFO underflow. */
+			  lp->csr6 |= 0x00200000;  /* Reconfigure to store-n-forward. */
+			  /* Restart the transmit process. */
+			  outl(lp->csr6 | 0x0002, ioaddr + CSR6);
+			  outl(lp->csr6 | 0x2002, ioaddr + CSR6);
+			}
+			if (csr5 & 0x0100) {		/* Missed a Rx frame. */
 				lp->stats.rx_errors++;
-				lp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff;
+				lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 			}
-			if (csr5 & TSTAT_TEXPIRED) {
+			if (csr5 & 0x0800) {
 				printk("%s: Something Wicked happened! %8.8x.\n",
 					   dev->name, csr5);
 				/* Hmmmmm, it's not clear what to do here. */
 			}
+			/* Clear all error sources, included undocumented ones! */
+			outl(0x000f7ba, ioaddr + CSR5);
 		}
 		if (--boguscnt < 0) {
 			printk("%s: Too much work at interrupt, csr5=0x%8.8x.\n",
 				   dev->name, csr5);
 			/* Clear all interrupt sources. */
-			tio_write(TSTAT_CLEARINTR, CSR5);
+			outl(0x0001ffff, ioaddr + CSR5);
 			break;
 		}
 	} while (1);
 
-	/* Special code for testing *only*. */
+	if (tulip_debug > 3)
+		printk("%s: exiting interrupt, csr5=%#4.4x.\n",
+			   dev->name, inl(ioaddr + CSR5));
+
+	/* Code that should never be run!  Perhaps remove after testing.. */
 	{
 		static int stopit = 10;
 		if (dev->start == 0  &&  --stopit < 0) {
 			printk("%s: Emergency stop, looping startup interrupt.\n",
 				   dev->name);
+#ifdef SA_SHIRQ
 			free_irq(irq, dev);
+#else
+			free_irq(irq);
+#endif
 		}
 	}
 
@@ -1028,59 +1755,100 @@
 {
 	struct tulip_private *lp = (struct tulip_private *)dev->priv;
 	int entry = lp->cur_rx % RX_RING_SIZE;
-	int i;
 
+	if (tulip_debug > 4)
+		printk(" In tulip_rx(), entry %d %8.8x.\n", entry,
+			   lp->rx_ring[entry].status);
 	/* If we own the next entry, it's a new packet. Send it up. */
 	while (lp->rx_ring[entry].status >= 0) {
 		int status = lp->rx_ring[entry].status;
 
-		if ((status & TRING_RxDESCMASK) != TRING_RxDESCMASK) {
-			printk("%s: Ethernet frame spanned multiple buffers,"
-				   "status %8.8x!\n", dev->name, status);
-		} else if (status & TRING_ERROR) {
+		if (tulip_debug > 4)
+			printk("  tulip_rx() status was %8.8x.\n", status);
+		if ((status & 0x0300) != 0x0300) {
+			if ((status & 0xffff) != 0x7fff) { /* Ingore earlier buffers. */
+			  printk("%s: Oversized Ethernet frame spanned multiple buffers,"
+					 " status %8.8x!\n", dev->name, status);
+			  lp->stats.rx_length_errors++;
+			}
+		} else if (status & 0x8000) {
 			/* There was a fatal error. */
 			lp->stats.rx_errors++; /* end of a packet.*/
-			if (status & TRING_RxLENGTH) lp->stats.rx_length_errors++;
-			if (status & TRING_RxFRAME) lp->stats.rx_frame_errors++;
-			if (status & TRING_RxCRC) lp->stats.rx_crc_errors++;
-			if (status & TRING_RxFIFO) lp->stats.rx_fifo_errors++;
+			if (status & 0x0890) lp->stats.rx_length_errors++;
+			if (status & 0x0004) lp->stats.rx_frame_errors++;
+			if (status & 0x0002) lp->stats.rx_crc_errors++;
+			if (status & 0x0001) lp->stats.rx_fifo_errors++;
 		} else {
 			/* Malloc up new buffer, compatible with net-2e. */
 			/* Omit the four octet CRC from the length. */
 			short pkt_len = (lp->rx_ring[entry].status >> 16) - 4;
 			struct sk_buff *skb;
+			int rx_in_place = 0;
 
-			skb = dev_alloc_skb(pkt_len + 2);
+			/* Check if the packet is long enough to just accept without
+			   copying to a properly sized skbuff. */
+			if (pkt_len > SKBUFF_RX_COPYBREAK) {
+				struct sk_buff *newskb;
+				char *temp;
+
+				/* Get a fresh skbuff to replace the filled one. */
+				newskb = DEV_ALLOC_SKB(PKT_BUF_SZ);
+				if (newskb == NULL) {
+					skb = 0;		/* No memory, drop the packet. */
+					goto memory_squeeze;
+				}
+				/* Pass up the skb already on the Rx ring. */
+				skb = lp->rx_skbuff[entry];
+				temp = skb_put(skb, pkt_len);
+				if (bus_to_virt(lp->rx_ring[entry].buffer1) != temp)
+					printk("%s: Warning -- the skbuff addresses do not match"
+						   " in tulip_rx: %p vs. %p / %p.\n", dev->name,
+						   bus_to_virt(lp->rx_ring[entry].buffer1),
+						   skb->head, temp);
+				rx_in_place = 1;
+				lp->rx_skbuff[entry] = newskb;
+				newskb->dev = dev;
+				/* Longword alignment required: do not skb_reserve(2)! */
+				lp->rx_ring[entry].buffer1 = virt_to_bus(newskb->tail);
+			} else
+				skb = DEV_ALLOC_SKB(pkt_len + 2);
+			memory_squeeze:
 			if (skb == NULL) {
-				printk("%s: Memory squeeze, deferring packet.\n",
-					   dev->name);
+				int i;
+				printk("%s: Memory squeeze, deferring packet.\n", dev->name);
 				/* Check that at least two ring entries are free.
 				   If not, free one and mark stats->rx_dropped++. */
-				for (i=0; i < RX_RING_SIZE; i++)
+				for (i = 0; i < RX_RING_SIZE; i++)
 					if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0)
 						break;
 
 				if (i > RX_RING_SIZE -2) {
 					lp->stats.rx_dropped++;
-					lp->rx_ring[entry].status = TRING_OWN;
+					lp->rx_ring[entry].status = 0x80000000;
 					lp->cur_rx++;
 				}
 				break;
 			}
 			skb->dev = dev;
-			skb_reserve(skb, 2);
-			memcpy(skb_put(skb, pkt_len),
-				   bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len);
-			/* Needed for 1.3.x */
-			skb->protocol = eth_type_trans(skb,dev);
+			if (! rx_in_place) {
+				skb_reserve(skb, 2);	/* 16 byte align the data fields */
+				memcpy(skb_put(skb, pkt_len),
+					   bus_to_virt(lp->rx_ring[entry].buffer1), pkt_len);
+			}
+#if LINUX_VERSION_CODE > 0x10300
+			skb->protocol = eth_type_trans(skb, dev);
+#else
+			skb->len = pkt_len;
+#endif
 			netif_rx(skb);
 			lp->stats.rx_packets++;
 		}
 
-		lp->rx_ring[entry].status = TRING_OWN;
+		lp->rx_ring[entry].status = 0x80000000;
 		entry = (++lp->cur_rx) % RX_RING_SIZE;
 	}
-	return(0);
+
+	return 0;
 }
 
 static int
@@ -1088,342 +1856,223 @@
 {
 	int ioaddr = dev->base_addr;
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
+	int i;
 
 	dev->start = 0;
 	dev->tbusy = 1;
 
+	if (tulip_debug > 1)
+		printk("%s: Shutting down ethercard, status was %2.2x.\n",
+			   dev->name, inl(ioaddr + CSR5));
+
 	/* Disable interrupts by clearing the interrupt mask. */
-	tio_write(TINTR_DISABLE, CSR7);
+	outl(0x00000000, ioaddr + CSR7);
 	/* Stop the chip's Tx and Rx processes. */
-	tio_write(tio_read(CSR6) & ~(TCMOD_TRxSTART), CSR6);
-	/* Leave the card in 10baseT state. */
-	tio_write(TSIAC_CONFIG, CSR13);
+	outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+	/* 21040 -- Leave the card in 10baseT state. */
+	if (tp->chip_id == DC21040)
+		outl(0x00000004, ioaddr + CSR13);
 
-	tp->stats.rx_missed_errors += tio_read(CSR8) & 0xffff;
+	tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 
-	tio_write(0, CSR13);
-/*	tio_write(0, CSR8);	wake up chip ? */
+	del_timer(&tp->timer);
 
+#ifdef SA_SHIRQ
 	free_irq(dev->irq, dev);
+#else
+	free_irq(dev->irq);
+	irq2dev_map[dev->irq] = 0;
+#endif
 
-	MOD_DEC_USE_COUNT;
-	return(0);
-}
+	/* Free all the skbuffs in the Rx queue. */
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		struct sk_buff *skb = tp->rx_skbuff[i];
+		tp->rx_skbuff[i] = 0;
+		tp->rx_ring[i].status = 0;		/* Not owned by Tulip chip. */
+		tp->rx_ring[i].length = 0;
+		tp->rx_ring[i].buffer1 = 0xBADF00D0; /* An invalid address. */
+		if (skb) {
+#if LINUX_VERSION_CODE < 0x20100
+			skb->free = 1;
+#endif
+			dev_kfree_skb(skb, FREE_WRITE);
+		}
+	}
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (tp->tx_skbuff[i])
+			dev_kfree_skb(tp->tx_skbuff[i], FREE_WRITE);
+		tp->tx_skbuff[i] = 0;
+	}
 
-static int
-tulip_config(struct device *dev, struct ifmap *map)
-{
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 
-	if (map->port == 0xff) return(-EINVAL);
-	dev->if_port = map->port;
-	tp->port_fix = 1;
-	if (tp->port_select) tp->port_select(dev);
-	return(0);
+	MOD_DEC_USE_COUNT;
+
+	return 0;
 }
 
 static struct enet_statistics *
 tulip_get_stats(struct device *dev)
 {
 	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	/*	short ioaddr = dev->base_addr;*/
+	int ioaddr = dev->base_addr;
+
+	if (dev->start)
+		tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
 
-	return(&tp->stats);
+	return &tp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+   Note that we only use exclusion around actually queueing the
+   new frame, not around filling tp->setup_frame.  This is non-deterministic
+   when re-entered but still correct. */
+
+/* The little-endian AUTODIN32 ethernet CRC calculation.
+   N.B. Do not use for bulk data, use a table-based routine instead.
+   This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
+{
+	unsigned int crc = 0xffffffff;	/* Initial value. */
+	while(--length >= 0) {
+		unsigned char current_octet = *data++;
+		int bit;
+		for (bit = 8; --bit >= 0; current_octet >>= 1) {
+			if ((crc ^ current_octet) & 1) {
+				crc >>= 1;
+				crc ^= ethernet_polynomial_le;
+			} else
+				crc >>= 1;
+		}
+	}
+	return crc;
 }
 
-/*
- *	Set or clear the multicast filter for this adaptor.
- */
 
-static void set_multicast_list(struct device *dev)
+static void
+#ifdef NEW_MULTICAST
+set_multicast_list(struct device *dev)
+#else
+static void set_multicast_list(struct device *dev, int num_addrs, void *addrs);
+#endif
 {
-	short ioaddr = dev->base_addr;
-	int csr6 = tio_read(CSR6) & ~(TCMOD_MODEMASK|TCMOD_FILTERMASK);
+	int ioaddr = dev->base_addr;
+	int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+	struct tulip_private *tp = (struct tulip_private *)dev->priv;
 
-	if (dev->flags&IFF_PROMISC) 
-	{			/* Set promiscuous. why ALLMULTI ? */
-		tio_write(csr6 | TCMOD_PROMISC | TCMOD_ALLMCAST, CSR6);
-		/* Log any net taps. */
+	tp->csr6 &= ~0x00D5;
+	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+		outl(csr6 | 0x00C0, ioaddr + CSR6);
+		/* Unconditionally log net taps. */
 		printk("%s: Promiscuous mode enabled.\n", dev->name);
-	}
-	else if (dev->mc_count > 14 || (dev->flags&IFF_ALLMULTI)) 
-	{
+		tp->csr6 |= 0xC0;
+	} else if ((dev->mc_count > 1000)  ||  (dev->flags & IFF_ALLMULTI)) {
 		/* Too many to filter perfectly -- accept all multicasts. */
-		tio_write(csr6 | TCMOD_ALLMCAST, CSR6);
-	}
-	else
-	{
-		struct tulip_private *tp = (struct tulip_private *)dev->priv;
-		struct dev_mc_list *dmi=dev->mc_list;
-		int *setup_frm = tp->setup_frame;
-		unsigned short *eaddrs;
+		outl(csr6 | 0x0080, ioaddr + CSR6);
+		tp->csr6 |= 0x80;
+	} else {
+		u32 *setup_frm = tp->setup_frame;
+		struct dev_mc_list *mclist;
+		u16 *eaddrs;
+		u32 tx_flags;
 		int i;
 
-		/* We have < 15 addresses that we can use the wonderful
-		   16 address perfect filtering of the Tulip.  Note that only
-		   the low shortword of setup_frame[] is valid. */
-		tio_write(csr6 | 0x0000, CSR6);
-		for (i = 0; i < dev->mc_count; i ++) {
-			eaddrs=(unsigned short *)dmi->dmi_addr;
-			dmi=dmi->next;
+		if (dev->mc_count > 14) { /* Must use a multicast hash table. */
+		  u16 hash_table[32];
+		  memset(hash_table, 0, sizeof(hash_table));
+		  for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+			   i++, mclist = mclist->next)
+			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
+					hash_table);
+		  /* Copy the hash table to the setup frame.
+			 NOTE that only the LOW SHORTWORD of setup_frame[] is valid!
+			 This code may require tweaking for non-x86 architectures!  */
+		  for (i = 0; i < 32; i++)
+			*setup_frm++ = hash_table[i];
+		  setup_frm += 7;
+		  tx_flags = 0x08400000 | 192;
+		  /* Too clever: i > 15 for fall-though. */
+		} else {
+		  /* We have <= 15 addresses so we can use the wonderful
+			 16 address perfect filtering of the Tulip. */
+		  for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
+			   i++, mclist = mclist->next) {
+			/* Note that only the low shortword of setup_frame[] is valid!
+			   This code may require tweaking for non-x86 architectures! */
+			eaddrs = (u16 *)mclist->dmi_addr;
 			*setup_frm++ = *eaddrs++;
 			*setup_frm++ = *eaddrs++;
 			*setup_frm++ = *eaddrs++;
+		  }
+		  /* Fill the rest of the table with our physical address.
+			 Once again, only the low shortword or setup_frame[] is valid! */
+		  *setup_frm++ = 0xffff;
+		  *setup_frm++ = 0xffff;
+		  *setup_frm++ = 0xffff;
+		  tx_flags = 0x08000000 | 192;
 		}
-		/* Fill the rest of the table with our physical address. */
-		eaddrs = (unsigned short *)dev->dev_addr;
-		/* Always accept broadcast packets */
-		*setup_frm++ = 0xffff;
-		*setup_frm++ = 0xffff;
-		*setup_frm++ = 0xffff;
+		eaddrs = (u16 *)dev->dev_addr;
 		do {
-			*setup_frm++ = eaddrs[0];
-			*setup_frm++ = eaddrs[1];
-			*setup_frm++ = eaddrs[2];
+		  *setup_frm++ = eaddrs[0];
+		  *setup_frm++ = eaddrs[1];
+		  *setup_frm++ = eaddrs[2];
 		} while (++i < 15);
-
 		/* Now add this frame to the Tx list. */
-		tulip_start_xmit((struct sk_buff *) -1, dev);
-	}
-}
-
-int
-tulip_hwinit(struct device *dev, int ioaddr,
-			 int irq, int device_id)
-{
-	/* See note below on the Znyx 315 etherarray. */
-	static unsigned char last_phys_addr[6] = {0x00, 'L', 'i', 'n', 'u', 'x'};
-	static int last_irq;
-	char detect_mesg[80], *mesgp=detect_mesg;
-	struct tulip_private *tp = (struct tulip_private *)dev->priv;
-	int i;
-	unsigned short sum, bitsum;
-
-	if (check_region(ioaddr, TULIP_TOTAL_SIZE) != 0) {
-		printk("tulip_hwinit: region already allocated at %#3x.\n",
-			   ioaddr);
-		return(-1);
-	}
-
-	mesgp += sprintf(mesgp, "(DEC 21%d4%d Tulip",
-					 device_id == PCI_DEVICE_ID_DEC_TULIP_FAST,
-					 device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS);
-
-	/* Stop the chip's Tx and Rx processes. */
-	tio_write(tio_read(CSR6) & ~TCMOD_TRxSTART, CSR6);
-	/* Clear the missed-packet counter. */
-	i = tio_read(CSR8) & 0xffff;
-
-	if (device_id == PCI_DEVICE_ID_DEC_TULIP_PLUS
-	    && (tio_read(CSR9) & 0x8000)) {
-		mesgp += sprintf(mesgp, " treat as 21040");
-	    device_id = PCI_DEVICE_ID_DEC_TULIP;
-	}
-	
-	/* The station address ROM is read byte serially.  The register must
-	   be polled, waiting for the value to be read bit serially from the
-	   EEPROM.
-	   */
-	sum = 0;
-	if (device_id == PCI_DEVICE_ID_DEC_TULIP) {
-		tio_write(0, CSR9);
-	    /* Reset the pointer with a dummy write. */
-	    bitsum = 0xff;
-	    for (i = 0; i < 6; i++) {
-			int value, boguscnt = 100000;
-			do
-				value = tio_read(CSR9);
-			while (value < 0  && --boguscnt > 0);
-			dev->dev_addr[i] = value;
-			sum += value & 0xFF;
-			bitsum &= value;
-	    }
-	} else {
-	    /* Must be a 21140/21041, with a serial EEPROM interface. */
-	    struct eeprom eep;
-	    u_char *addr;
-
-	    if (read_eeprom(ioaddr, &eep) < 0) {
-			addr = eep.ng_addr;/* broken EEPROM structure */
-	    } else {
-			addr = eep.ok_addr;/* DEC EtherWorks */
-	    }
-	    for (i = 0; i < ETH_ALEN; i++) {
-			sum += addr[i];
-			dev->dev_addr[i] = addr[i];
-	    }
-	}
-	/* Make certain the data structures are quadword aligned. */
-
-	mesgp += sprintf(mesgp, ") at %#3x, ", ioaddr);
-
-	/* On the Zynx 315 etherarray boards only the first Tulip has an EEPROM.
-	   The addresses of the subsequent ports are derived from the first. */
-	if (sum == 0) {
-		for (i = 0; i < ETH_ALEN - 1; i++)
-			dev->dev_addr[i] = last_phys_addr[i];
-		dev->dev_addr[i] = last_phys_addr[i] + 1;
-		irq = last_irq;
-	}
-	for (i = 0; i < ETH_ALEN - 1; i++)
-		mesgp += sprintf(mesgp, "%2.2x:", dev->dev_addr[i]);
-	mesgp += sprintf(mesgp, "%2.2x, IRQ %d\n",
-					 last_phys_addr[i] = dev->dev_addr[i], irq);
-	last_irq = irq;
-
-	/* copy ethernet address */
-	if (card_type(tp, device_id,
-				  htonl((*(int*)dev->dev_addr) & 0xFFFFFF)))
-		for (i = 0; i < ETH_ALEN - 1; i++)
-			last_phys_addr[i] = dev->dev_addr[i];
-	/* We do a request_region() only to register /proc/ioports info. */
-	request_region(ioaddr, TULIP_TOTAL_SIZE, tp->signature);
-
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
-
-	/* The Tulip-specific entries in the device structure. */
-	dev->open = &tulip_open;
-	dev->hard_start_xmit = &tulip_start_xmit;
-	dev->stop = &tulip_close;
-	dev->get_stats = &tulip_get_stats;
-	dev->set_config = &tulip_config;
-	dev->set_multicast_list = &set_multicast_list;
-
-#ifdef	MODULE
-	if (if_port == TULIP_AUTO_PORT)
-		if_port = TULIP_PORT;
-	else
-		tp->port_fix = 1;
-	dev->if_port = if_port;
-	tp->full_duplex = full_duplex;
-	tp->next_module = root_tulip_dev;
-	root_tulip_dev = dev;
-#else
-#ifdef TULIP_FULL_DUPLEX
-	tp->full_duplex = 1;
-#endif
-	dev->if_port = TULIP_PORT;
-#endif
-#ifdef	TULIP_FIX_PORT
-	tp->port_fix = 1;
-#endif
-
-	printk("%s: %s %s", dev->name, tp->signature, detect_mesg);
-
-	/* Reset the xcvr interface and turn on heartbeat. */
-	tio_write(TSIAC_RESET, CSR13);
-	tio_write(TSIAC_CONFIG, CSR13);
-
-	return(0);
-}
-
-int tulip_probe(struct device *dev)
-{
-	static struct device *tulip_head=NULL;
-	u_char pci_bus, pci_device_fn, pci_latency, pci_irq;
-	u_int pci_ioaddr;
-	u_short pci_command, vendor_id, device_id;
-	u_int pci_chips[] = {
-		PCI_DEVICE_ID_DEC_TULIP,
-		PCI_DEVICE_ID_DEC_TULIP_FAST,
-		PCI_DEVICE_ID_DEC_TULIP_PLUS,
-		PCI_DEVICE_ID_NONE
-	};
-	int num=0, cno;
-	static int pci_index = 0;
-
-    if (!pcibios_present()) return(-ENODEV);
-
-	for (; pci_index < 0xff; pci_index++) {
-		if (pcibios_find_class(PCI_CLASS_NETWORK_ETHERNET << 8, pci_index,
-							   &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL)
-				break;
-
-		/* get vendor id */
-		pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID,
-								 &vendor_id);
-		/* get IRQ */
-		pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE,
-								 &pci_irq);
-
-		/* get device id */
-		pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID,
-								 &device_id);
-
-		/* get IO address */
-		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;
-		if (vendor_id != PCI_VENDOR_ID_DEC)
-				continue;
-
-		for (cno = 0; pci_chips[cno] != PCI_DEVICE_ID_NONE; cno++)
-				if (device_id == pci_chips[cno])
-						break;
-		if (pci_chips[cno] == PCI_DEVICE_ID_NONE) {
-			printk("Unknown Digital PCI ethernet chip type %4.4x detected:"
-				   " not configured.\n", device_id);
-			continue;
-		}
-		dev = init_etherdev(NULL, ROUND_UP(sizeof(struct device) +
-										   sizeof (struct tulip_private) +
-										   ETHNAMSIZ, 8));
-		if (dev == NULL)
-				break;
+		if (tp->cur_tx - tp->dirty_tx > TX_RING_SIZE - 2) {
+			/* Same setup recently queued, we need not add it. */
+		} else {
+			unsigned long flags;
+			unsigned int entry;
+			
+			save_flags(flags); cli();
+			entry = tp->cur_tx++ % TX_RING_SIZE;
+
+			if (entry != 0) {
+			  /* Avoid a chip errata by prefixing a dummy entry. */
+			  tp->tx_skbuff[entry] = 0;
+			  tp->tx_ring[entry].length =
+				(entry == TX_RING_SIZE-1) ? 0x02000000 : 0;
+			  tp->tx_ring[entry].buffer1 = 0;
+			  tp->tx_ring[entry].status = 0x80000000;
+			  entry = tp->cur_tx++ % TX_RING_SIZE;
+			}
 
-		if (!tulip_head) {
-			printk(version);
-			tulip_head = dev;
-		}
-
-		/* Get and check the bus-master and latency values. */
-		pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND,
-								 &pci_command);
-		if ((pci_command & PCI_COMMAND_MASTER) == 0) {
-			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 < 10) {
-			printk("  PCI latency timer (CFLT) is"
-				   " unreasonably low at %d."
-				   "  Setting to 100 clocks.\n", pci_latency);
-			pcibios_write_config_byte(pci_bus, pci_device_fn,
-									  PCI_LATENCY_TIMER, 100);
-		}
-
-		if (tulip_hwinit(dev, pci_ioaddr, pci_irq, pci_chips[cno]) < 0) {
-			continue;
-		}
-		num++;
-#ifdef	TULIP_MAX_CARDS
-		if (num >= TULIP_MAX_CARDS) return(0);
-#endif
+			tp->tx_skbuff[entry] = 0;
+			/* Put the setup frame on the Tx list. */
+			if (entry == TX_RING_SIZE-1)
+			  tx_flags |= 0x02000000;		/* Wrap ring. */
+			tp->tx_ring[entry].length = tx_flags;
+			tp->tx_ring[entry].buffer1 = virt_to_bus(tp->setup_frame);
+			tp->tx_ring[entry].status = 0x80000000;
+			if (tp->cur_tx - tp->dirty_tx >= TX_RING_SIZE - 2) {
+				dev->tbusy = 1;
+				tp->tx_full = 1;
+			}
+			restore_flags(flags);
+			/* Trigger an immediate transmit demand. */
+			outl(0, ioaddr + CSR1);
+		}
+		outl(csr6 | 0x0000, ioaddr + CSR6);
 	}
-	return(num > 0 ? 0: -ENODEV);
 }
-
+
 #ifdef MODULE
 
-/* The parameters that may be passed in... */
-/* This driver does nothing with options yet.  It will later be used to
-   pass the full-duplex flag, etc. */
-int debug = -1;
+/* An additional parameter that may be passed in... */
+static int debug = -1;
 
 int
 init_module(void)
 {
+	int cards_found;
+
+	if (debug >= 0)
+		tulip_debug = debug;
+
 	root_tulip_dev = NULL;
-	return tulip_probe(NULL);
+	cards_found = tulip_probe(0);
+
+	return cards_found ? 0 : -ENODEV;
 }
 
 void
@@ -1433,21 +2082,21 @@
 
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	while (root_tulip_dev) {
-		next_dev =
-		   ((struct tulip_private *) root_tulip_dev->priv)->next_module;
+		next_dev = ((struct tulip_private *)root_tulip_dev->priv)->next_module;
 		unregister_netdev(root_tulip_dev);
 		release_region(root_tulip_dev->base_addr, TULIP_TOTAL_SIZE);
 		kfree(root_tulip_dev);
 		root_tulip_dev = next_dev;
 	}
 }
-#endif /* MODULE */
 
+#endif  /* MODULE */
 
 /*
  * Local variables:
- *  compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c tulip.c"
+ *  compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c tulip.c"
  *  c-indent-level: 4
+ *  c-basic-offset: 4
  *  tab-width: 4
  * End:
  */
diff -u --recursive --new-file v2.0.30/linux/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- v2.0.30/linux/drivers/net/wavelan.c	Thu Mar  6 10:03:51 1997
+++ linux/drivers/net/wavelan.c	Sun Aug  3 15:17:45 1997
@@ -112,7 +112,7 @@
  * Read from card's Host Adaptor Status Register.
  */
 static inline u_short
-hasr_read(u_short	ioaddr)
+hasr_read(u_long	ioaddr)
 {
   return(inw(HASR(ioaddr)));
 } /* hasr_read */
@@ -122,7 +122,7 @@
  * Write to card's Host Adapter Command Register.
  */
 static inline void
-hacr_write(u_short	ioaddr,
+hacr_write(u_long	ioaddr,
 	   u_short	hacr)
 {
   outw(hacr, HACR(ioaddr));
@@ -134,7 +134,7 @@
  * those times when it is needed.
  */
 static inline void
-hacr_write_slow(u_short	ioaddr,
+hacr_write_slow(u_long	ioaddr,
 		u_short	hacr)
 {
   hacr_write(ioaddr, hacr);
@@ -147,7 +147,7 @@
  * Set the channel attention bit.
  */
 static inline void
-set_chan_attn(u_short	ioaddr,
+set_chan_attn(u_long	ioaddr,
 	      u_short	hacr)
 {
   hacr_write(ioaddr, hacr | HACR_CA);
@@ -158,7 +158,7 @@
  * Reset, and then set host adaptor into default mode.
  */
 static inline void
-wv_hacr_reset(u_short	ioaddr)
+wv_hacr_reset(u_long	ioaddr)
 {
   hacr_write_slow(ioaddr, HACR_RESET);
   hacr_write(ioaddr, HACR_DEFAULT);
@@ -169,7 +169,7 @@
  * Set the i/o transfer over the ISA bus to 8 bits mode
  */
 static inline void
-wv_16_off(u_short	ioaddr,
+wv_16_off(u_long	ioaddr,
 	  u_short	hacr)
 {
   hacr &= ~HACR_16BITS;
@@ -181,7 +181,7 @@
  * Set the i/o transfer over the ISA bus to 8 bits mode
  */
 static inline void
-wv_16_on(u_short	ioaddr,
+wv_16_on(u_long		ioaddr,
 	 u_short	hacr)
 {
   hacr |= HACR_16BITS;
@@ -196,7 +196,7 @@
 wv_ints_off(device *	dev)
 {
   net_local *	lp = (net_local *)dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_long	x;
 
   x = wv_splhi();
@@ -215,7 +215,7 @@
 wv_ints_on(device *	dev)
 {
   net_local *	lp = (net_local *)dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_long	x;
 
   x = wv_splhi();
@@ -239,7 +239,7 @@
  * Read bytes from the PSA.
  */
 static void
-psa_read(u_short	ioaddr,
+psa_read(u_long		ioaddr,
 	 u_short	hacr,
 	 int		o,	/* offset in PSA */
 	 u_char *	b,	/* buffer to fill */
@@ -262,7 +262,7 @@
  * Write the Paramter Storage Area to the WaveLAN card's memory
  */
 static void
-psa_write(u_short	ioaddr,
+psa_write(u_long	ioaddr,
 	  u_short	hacr,
 	  int		o,	/* Offset in psa */
 	  u_char *	b,	/* Buffer in memory */
@@ -329,7 +329,7 @@
  * Write 1 byte to the MMC.
  */
 static inline void
-mmc_out(u_short		ioaddr,
+mmc_out(u_long		ioaddr,
 	u_short		o,
 	u_char		d)
 {
@@ -347,7 +347,7 @@
  * We start by the end because it is the way it should be !
  */
 static inline void
-mmc_write(u_short	ioaddr,
+mmc_write(u_long	ioaddr,
 	  u_char	o,
 	  u_char *	b,
 	  int		n)
@@ -365,7 +365,7 @@
  * Optimised version for 1 byte, avoid using memory...
  */
 static inline u_char
-mmc_in(u_short	ioaddr,
+mmc_in(u_long	ioaddr,
        u_short	o)
 {
   while(inw(HASR(ioaddr)) & HASR_MMC_BUSY)
@@ -386,7 +386,7 @@
  * We start by the end because it is the way it should be !
  */
 static inline void
-mmc_read(u_short	ioaddr,
+mmc_read(u_long		ioaddr,
 	 u_char		o,
 	 u_char *	b,
 	 int		n)
@@ -400,11 +400,27 @@
 
 /*------------------------------------------------------------------*/
 /*
+ * Get the type of encryption available...
+ */
+static inline int
+mmc_encr(u_long		ioaddr)	/* i/o port of the card */
+{
+  int	temp;
+
+  temp = mmc_in(ioaddr, mmroff(0, mmr_des_avail));
+  if((temp != MMR_DES_AVAIL_DES) && (temp != MMR_DES_AVAIL_AES))
+    return 0;
+  else
+    return temp;
+}
+
+/*------------------------------------------------------------------*/
+/*
  * Wait for the frequency EEprom to complete a command...
  * I hope this one will be optimally inlined...
  */
 static inline void
-fee_wait(u_short	ioaddr,	/* i/o port of the card */
+fee_wait(u_long		ioaddr,	/* i/o port of the card */
 	 int		delay,	/* Base delay to wait for */
 	 int		number)	/* Number of time to wait */
 {
@@ -420,7 +436,7 @@
  * Read bytes from the Frequency EEprom (frequency select cards).
  */
 static void
-fee_read(u_short	ioaddr,	/* i/o port of the card */
+fee_read(u_long		ioaddr,	/* i/o port of the card */
 	 u_short	o,	/* destination offset */
 	 u_short *	b,	/* data buffer */
 	 int		n)	/* number of registers */
@@ -445,6 +461,8 @@
     }
 }
 
+#ifdef WIRELESS_EXT	/* If wireless extension exist in the kernel */
+
 /*------------------------------------------------------------------*/
 /*
  * Write bytes from the Frequency EEprom (frequency select cards).
@@ -453,7 +471,7 @@
  * Jean II
  */
 static void
-fee_write(u_short	ioaddr,	/* i/o port of the card */
+fee_write(u_long	ioaddr,	/* i/o port of the card */
 	  u_short	o,	/* destination offset */
 	  u_short *	b,	/* data buffer */
 	  int		n)	/* number of registers */
@@ -528,6 +546,7 @@
   fee_wait(ioaddr, 10, 100);
 #endif	/* EEPROM_IS_PROTECTED */
 }
+#endif	/* WIRELESS_EXT */
 
 /************************ I82586 SUBROUTINES *************************/
 /*
@@ -540,7 +559,7 @@
  * Why inlining this function make it fail ???
  */
 static /*inline*/ void
-obram_read(u_short	ioaddr,
+obram_read(u_long	ioaddr,
 	   u_short	o,
 	   u_char *	b,
 	   int		n)
@@ -554,7 +573,7 @@
  * Write bytes to the on-board RAM.
  */
 static inline void
-obram_write(u_short	ioaddr,
+obram_write(u_long	ioaddr,
 	    u_short	o,
 	    u_char *	b,
 	    int		n)
@@ -571,7 +590,7 @@
 wv_ack(device *		dev)
 {
   net_local *	lp = (net_local *)dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_short	scb_cs;
   int		i;
 
@@ -614,7 +633,7 @@
 		   const char *	str)
 {
   net_local *	lp = (net_local *)dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_short	scb_cmd;
   ach_t		cb;
   int		i;
@@ -660,7 +679,7 @@
  */
 static inline int
 wv_config_complete(device *	dev,
-		   u_short	ioaddr,
+		   u_long	ioaddr,
 		   net_local *	lp)
 {
   unsigned short	mcs_addr;
@@ -722,7 +741,7 @@
  */
 static int
 wv_complete(device *	dev,
-	    u_short	ioaddr,
+	    u_long	ioaddr,
 	    net_local *	lp)
 {
   int	nreaped = 0;
@@ -992,7 +1011,7 @@
 static void
 wv_mmc_show(device *	dev)
 {
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   net_local *	lp = (net_local *)dev->priv;
   mmr_t		m;
 
@@ -1077,7 +1096,7 @@
  * Print the last block of the i82586 memory
  */
 static void
-wv_scb_show(unsigned short	ioaddr)
+wv_scb_show(u_long	ioaddr)
 {
   scb_t		scb;
 
@@ -1162,7 +1181,7 @@
 	       int		i,
 	       u_short		p)
 {
-  unsigned short	ioaddr;
+  u_long		ioaddr;
   ac_tx_t		actx;
 
   ioaddr = dev->base_addr;
@@ -1529,7 +1548,7 @@
  * (called in wavelan_ioctl)
  */
 static inline int
-wv_set_frequency(u_short	ioaddr,	/* i/o port of the card */
+wv_set_frequency(u_long		ioaddr,	/* i/o port of the card */
 		 iw_freq *	frequency)
 {
   const int	BAND_NUM = 10;	/* Number of bands */
@@ -1550,7 +1569,7 @@
     }
 
   /* Setting by channel (same as wfreqsel) */
-  /* Warning : each channel is 11MHz wide, so some of the channels
+  /* Warning : each channel is 22MHz wide, so some of the channels
    * will interfere... */
   if((frequency->e == 0) &&
      (frequency->m >= 0) && (frequency->m < BAND_NUM))
@@ -1729,7 +1748,7 @@
  * Give the list of available frequencies
  */
 static inline int
-wv_frequency_list(u_short	ioaddr,	/* i/o port of the card */
+wv_frequency_list(u_long	ioaddr,	/* i/o port of the card */
 		  iw_freq *	list,	/* List of frequency to fill */
 		  int		max)	/* Maximum number of frequencies */
 {
@@ -1826,7 +1845,7 @@
 	      struct ifreq *	rq,	/* Data passed */
 	      int		cmd)	/* Ioctl number */
 {
-  unsigned short	ioaddr = dev->base_addr;
+  u_long		ioaddr = dev->base_addr;
   net_local *		lp = (net_local *)dev->priv;	/* lp is not unused */
   struct iwreq *	wrq = (struct iwreq *) rq;
   psa_t			psa;
@@ -1866,9 +1885,7 @@
 	  m.w.mmw_netw_id_h = (wrq->u.nwid.nwid & 0xFF00) >> 8;
 	  mmc_write(ioaddr, (char *)&m.w.mmw_netw_id_l - (char *)&m,
 		    (unsigned char *)&m.w.mmw_netw_id_l, 2);
-	  m.w.mmw_loopt_sel = 0x00;
-	  mmc_write(ioaddr, (char *)&m.w.mmw_loopt_sel - (char *)&m,
-		    (unsigned char *)&m.w.mmw_loopt_sel, 1);
+	  mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), 0x00);
 	}
       else
 	{
@@ -1878,10 +1895,8 @@
 		    (char *)&psa.psa_nwid_select - (char *)&psa,
 		    (unsigned char *)&psa.psa_nwid_select, 1);
 
-	  /* Disable nwid in the mmc (no check) */
-	  m.w.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
-	  mmc_write(ioaddr, (char *)&m.w.mmw_loopt_sel - (char *)&m,
-		    (unsigned char *)&m.w.mmw_loopt_sel, 1);
+	  /* Disable nwid in the mmc (no filtering) */
+	  mmc_out(ioaddr, mmwoff(0, mmw_loopt_sel), MMW_LOOPT_SEL_DIS_NWID);
 	}
       break;
 
@@ -1950,6 +1965,82 @@
       wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F;
       break;
 
+     case SIOCSIWENCODE:
+       /* Set encryption key */
+       if(!mmc_encr(ioaddr))
+	 {
+	   ret = -EOPNOTSUPP;
+	   break;
+	 }
+
+       if(wrq->u.encoding.method)
+	 {	/* enable encryption */
+	   int		i;
+	   long long	key = wrq->u.encoding.code;
+
+	   for(i = 7; i >= 0; i--)
+	     {
+	       psa.psa_encryption_key[i] = key & 0xFF;
+	       key >>= 8;
+	     }
+           psa.psa_encryption_select = 1;
+	   psa_write(ioaddr, lp->hacr,
+		     (char *) &psa.psa_encryption_select - (char *) &psa,
+		     (unsigned char *) &psa.psa_encryption_select, 8+1);
+
+           mmc_out(ioaddr, mmwoff(0, mmw_encr_enable),
+		   MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE);
+           mmc_write(ioaddr, mmwoff(0, mmw_encr_key),
+		     (unsigned char *) &psa.psa_encryption_key, 8);
+	 }
+       else
+	 {	/* disable encryption */
+	   psa.psa_encryption_select = 0;
+	   psa_write(ioaddr, lp->hacr,
+		     (char *) &psa.psa_encryption_select - (char *) &psa,
+		     (unsigned char *) &psa.psa_encryption_select, 1);
+
+	   mmc_out(ioaddr, mmwoff(0, mmw_encr_enable), 0);
+	 }
+       break;
+
+     case SIOCGIWENCODE:
+       /* Read the encryption key */
+       if(!mmc_encr(ioaddr))
+	 {
+	   ret = -EOPNOTSUPP;
+	   break;
+	 }
+
+       /* only super-user can see encryption key */
+       if(!suser())
+	 {
+	   ret = -EPERM;
+	   break;
+	 }
+       else
+	 {
+	   int		i;
+	   long long	key = 0;
+
+	   psa_read(ioaddr, lp->hacr,
+		    (char *) &psa.psa_encryption_select - (char *) &psa,
+		    (unsigned char *) &psa.psa_encryption_select, 1+8);
+	   for(i = 0; i < 8; i++)
+	     {
+	       key <<= 8;
+	       key += psa.psa_encryption_key[i];
+	     }
+	   wrq->u.encoding.code = key;
+
+	   /* encryption is enabled */
+	   if(psa.psa_encryption_select)
+	     wrq->u.encoding.method = mmc_encr(ioaddr);
+	   else
+	     wrq->u.encoding.method = 0;
+	 }
+       break;
+
     case SIOCGIWRANGE:
       /* Basic checking... */
       if(wrq->u.data.pointer != (caddr_t) 0)
@@ -2207,7 +2298,7 @@
 static iw_stats *
 wavelan_get_wireless_stats(device *	dev)
 {
-  unsigned short	ioaddr = dev->base_addr;
+  u_long		ioaddr = dev->base_addr;
   net_local *		lp = (net_local *) dev->priv;
   mmr_t			m;
   iw_stats *		wstats;
@@ -2281,7 +2372,7 @@
 	       int		sksize)
 {
   net_local *		lp = (net_local *) dev->priv;
-  u_short		ioaddr = dev->base_addr;
+  u_long		ioaddr = dev->base_addr;
   struct sk_buff *	skb;
 
 #ifdef DEBUG_RX_TRACE
@@ -2369,7 +2460,7 @@
 static inline void
 wv_receive(device *	dev)
 {
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   net_local *	lp = (net_local *)dev->priv;
   int		nreaped = 0;
 
@@ -2555,7 +2646,7 @@
 		short	length)
 {
   net_local *		lp = (net_local *) dev->priv;
-  u_short		ioaddr = dev->base_addr;
+  u_long		ioaddr = dev->base_addr;
   unsigned short	txblock;
   unsigned short	txpred;
   unsigned short	tx_addr;
@@ -2760,7 +2851,7 @@
 static inline int
 wv_mmc_init(device *	dev)
 {
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   net_local *	lp = (net_local *)dev->priv;
   psa_t		psa;
   mmw_t		m;
@@ -2789,6 +2880,9 @@
       /* As NWID is not set : no NWID checking */
       psa.psa_nwid_select = 0;
 
+      /* Disable encryption */
+      psa.psa_encryption_select = 0;
+
       /* Set to standard values
        * 0x04 for AT,
        * 0x01 for MCA,
@@ -2806,7 +2900,7 @@
 #ifdef USE_PSA_CONFIG
       /* Write the psa */
       psa_write(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa,
-		(unsigned char *)psa.psa_nwid, 3);
+		(unsigned char *)psa.psa_nwid, 4);
       psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
 		(unsigned char *)&psa.psa_thr_pre_set, 1);
       psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa,
@@ -2828,6 +2922,14 @@
   else
     m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
 
+  memcpy(&m.mmw_encr_key, &psa.psa_encryption_key, 
+	 sizeof(m.mmw_encr_key));
+
+  if(psa.psa_encryption_select)
+    m.mmw_encr_enable = MMW_ENCR_ENABLE_EN | MMW_ENCR_ENABLE_MODE;
+  else
+    m.mmw_encr_enable = 0;
+
   m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
   m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
 
@@ -2919,7 +3021,7 @@
 wv_ru_start(device *	dev)
 {
   net_local *	lp = (net_local *) dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_short	scb_cs;
   fd_t		fd;
   rbd_t		rbd;
@@ -3013,7 +3115,7 @@
 wv_cu_start(device *	dev)
 {
   net_local *	lp = (net_local *) dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   int		i;
   u_short	txblock;
   u_short	first_nop;
@@ -3114,7 +3216,7 @@
 wv_82586_start(device *	dev)
 {
   net_local *	lp = (net_local *) dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   scp_t		scp;		/* system configuration pointer */
   iscp_t	iscp;		/* intermediate scp */
   scb_t		scb;		/* system control block */
@@ -3244,7 +3346,7 @@
 wv_82586_config(device *	dev)
 {
   net_local *		lp = (net_local *) dev->priv;
-  u_short		ioaddr = dev->base_addr;
+  u_long		ioaddr = dev->base_addr;
   unsigned short	txblock;
   unsigned short	txpred;
   unsigned short	tx_addr;
@@ -3440,7 +3542,7 @@
 wv_82586_stop(device *	dev)
 {
   net_local *	lp = (net_local *) dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_short	scb_cmd;
 
 #ifdef DEBUG_CONFIG_TRACE
@@ -3475,7 +3577,7 @@
 wv_hw_reset(device *	dev)
 {
   net_local *	lp = (net_local *)dev->priv;
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
 
 #ifdef DEBUG_CONFIG_TRACE
   printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name,
@@ -3520,7 +3622,7 @@
  * (called in wavelan_probe() and init_module())
  */
 static int
-wv_check_ioaddr(u_short		ioaddr,
+wv_check_ioaddr(u_long		ioaddr,
 		u_char *	mac)
 {
   int		i;		/* Loop counter */
@@ -3567,7 +3669,7 @@
 		  struct pt_regs *	regs)
 {
   device *	dev;
-  u_short	ioaddr;
+  u_long	ioaddr;
   net_local *	lp;
   u_short	hasr;
   u_short	status;
@@ -3714,7 +3816,7 @@
 {
   device *		dev;
   net_local *		lp;
-  unsigned short	ioaddr;
+  u_long		ioaddr;
   unsigned long		x;
   unsigned int		nreaped;
 
@@ -3909,7 +4011,7 @@
 static int
 wavelan_config(device *	dev)
 {
-  u_short	ioaddr = dev->base_addr;
+  u_long	ioaddr = dev->base_addr;
   u_char	irq_mask;
   int		irq;
   net_local *	lp;
diff -u --recursive --new-file v2.0.30/linux/drivers/net/wavelan.p.h linux/drivers/net/wavelan.p.h
--- v2.0.30/linux/drivers/net/wavelan.p.h	Thu Mar  6 10:03:51 1997
+++ linux/drivers/net/wavelan.p.h	Sun Aug  3 15:17:45 1997
@@ -35,6 +35,12 @@
 
 /* ------------------------ SPECIFIC NOTES ------------------------ */
 /*
+ * wavelan.o is darn too big
+ * -------------------------
+ *	That's true ! There is a very simple way to reduce the driver
+ *	object by 33% (yes !). Comment out the following line :
+ *		#include <linux/wireless.h>
+ *
  * MAC address and hardware detection :
  * ----------------------------------
  *	The detection code of the wavelan chech that the first 3
@@ -80,14 +86,12 @@
  * caracteristics of the hardware in a standard way and support for
  * applications for taking advantage of it (like Mobile IP).
  *
- * By default, these wireless extensions are disabled, because they
- * need a patch to the Linux Kernel. This simple patch may be found
- * with the driver + some utilities to access those wireless
- * extensions (iwconfig...). Hopefully, those wireless extensions will
- * make their way in the kernel someday.
+ * You will need to enable the CONFIG_NET_RADIO define in the kernel
+ * configuration to enable the wireless extensions (this is the one
+ * giving access to the radio network device choice).
  *
- * You also will need to enable the CONFIG_NET_RADIO in the kernel
- * configuration to enable the wireless extensions.
+ * It might also be a good idea as well to fetch the wireless tools to
+ * configure the device and play a bit.
  */
 
 /* ---------------------------- FILES ---------------------------- */
@@ -161,6 +165,7 @@
  *	Ajay Bakre (bakre@paul.rutgers.edu),
  *	Donald Becker (becker@cesdis.gsfc.nasa.gov),
  *	Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com),
+ *	Brent Elphick <belphick@uwaterloo.ca>,
  *	Anders Klemets (klemets@it.kth.se),
  *	Vladimir V. Kolpakov (w@stier.koenig.ru),
  *	Marc Meertens (Marc.Meertens@Utrecht.NCR.com),
@@ -185,6 +190,7 @@
  *	John Rosenberg (johnr@cs.usyd.edu.au),
  *	George Rossi (george@phm.gov.au),
  *	Arthur Scott (arthur@cs.usyd.edu.au),
+ *	Stanislav Sinyagin <stas@isf.ru>
  *	Peter Storey,
  * for their assistance and advice.
  *
@@ -260,9 +266,13 @@
  *	- Remove extern kerword for wavelan_probe()
  *	- Level threshold is now a standard wireless extension (version 4 !)
  *
+ * Changes made for release in 2.1.36 :
+ * ----------------------------------
+ *	- Encryption setting from Brent Elphick (thanks a lot !)
+ *	- 'ioaddr' to 'u_long' for the Alpha (thanks to Stanislav Sinyagin)
+ *
  * Wishes & dreams :
  * ---------------
- *	- Encryption stuff
  *	- Roaming
  */
 
@@ -345,7 +355,7 @@
 /************************ CONSTANTS & MACROS ************************/
 
 #ifdef DEBUG_VERSION_SHOW
-static const char	*version	= "wavelan.c : v15 (wireless extensions) 12/2/97\n";
+static const char	*version	= "wavelan.c : v16 (wireless extensions) 17/4/97\n";
 #endif
 
 /* Watchdog temporisation */
@@ -442,69 +452,65 @@
 	wv_psa_to_irq(u_char);
 /* ------------------- HOST ADAPTER SUBROUTINES ------------------- */
 static inline u_short		/* data */
-	hasr_read(u_short);	/* Read the host interface : base address */
+	hasr_read(u_long);	/* Read the host interface : base address */
 static inline void
-	hacr_write(u_short,	/* Write to host interface : base address */
+	hacr_write(u_long,	/* Write to host interface : base address */
 		   u_short),	/* data */
-	hacr_write_slow(u_short,
+	hacr_write_slow(u_long,
 		   u_short),
-	set_chan_attn(u_short,	/* ioaddr */
+	set_chan_attn(u_long,	/* ioaddr */
 		      u_short),	/* hacr */
-	wv_hacr_reset(u_short),	/* ioaddr */
-	wv_16_off(u_short,	/* ioaddr */
+	wv_hacr_reset(u_long),	/* ioaddr */
+	wv_16_off(u_long,	/* ioaddr */
 		  u_short),	/* hacr */
-	wv_16_on(u_short,	/* ioaddr */
+	wv_16_on(u_long,	/* ioaddr */
 		 u_short),	/* hacr */
 	wv_ints_off(device *),
 	wv_ints_on(device *);
 /* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
 static void
-	psa_read(u_short,	/* Read the Parameter Storage Area */
+	psa_read(u_long,	/* Read the Parameter Storage Area */
 		 u_short,	/* hacr */
 		 int,		/* offset in PSA */
 		 u_char *,	/* buffer to fill */
 		 int),		/* size to read */
-	psa_write(u_short, 	/* Write to the PSA */
+	psa_write(u_long, 	/* Write to the PSA */
 		  u_short,	/* hacr */
 		  int,		/* Offset in psa */
 		  u_char *,	/* Buffer in memory */
 		  int);		/* Length of buffer */
 static inline void
-	mmc_out(u_short,	/* Write 1 byte to the Modem Manag Control */
+	mmc_out(u_long,		/* Write 1 byte to the Modem Manag Control */
 		u_short,
 		u_char),
-	mmc_write(u_short,	/* Write n bytes to the MMC */
+	mmc_write(u_long,	/* Write n bytes to the MMC */
 		  u_char,
 		  u_char *,
 		  int);
 static inline u_char		/* Read 1 byte from the MMC */
-	mmc_in(u_short,
+	mmc_in(u_long,
 	       u_short);
 static inline void
-	mmc_read(u_short,	/* Read n bytes from the MMC */
+	mmc_read(u_long,	/* Read n bytes from the MMC */
 		 u_char,
 		 u_char *,
 		 int),
-	fee_wait(u_short,	/* Wait for frequency EEprom : base address */
+	fee_wait(u_long,	/* Wait for frequency EEprom : base address */
 		 int,		/* Base delay to wait for */
 		 int);		/* Number of time to wait */
 static void
-	fee_read(u_short,	/* Read the frequency EEprom : base address */
+	fee_read(u_long,	/* Read the frequency EEprom : base address */
 		 u_short,	/* destination offset */
 		 u_short *,	/* data buffer */
-		 int),		/* number of registers */
-	fee_write(u_short,	/* Write to frequency EEprom : base address */
-		  u_short,	/* destination offset */
-		  u_short *,	/* data buffer */
-		  int);		/* number of registers */
+		 int);		/* number of registers */
 /* ---------------------- I82586 SUBROUTINES ----------------------- */
 static /*inline*/ void
-	obram_read(u_short,	/* ioaddr */
+	obram_read(u_long,	/* ioaddr */
 		   u_short,	/* o */
 		   u_char *,	/* b */
 		   int);	/* n */
 static inline void
-	obram_write(u_short,	/* ioaddr */
+	obram_write(u_long,	/* ioaddr */
 		    u_short,	/* o */
 		    u_char *,	/* b */
 		    int);	/* n */
@@ -514,11 +520,11 @@
 	wv_synchronous_cmd(device *,
 			   const char *),
 	wv_config_complete(device *,
-			   u_short,
+			   u_long,
 			   net_local *);
 static int
 	wv_complete(device *,
-		    u_short,
+		    u_long,
 		    net_local *);
 static inline void
 	wv_82586_reconfig(device *);
@@ -560,7 +566,7 @@
 	wv_82586_stop(device *);
 static int
 	wv_hw_reset(device *),		/* Reset the wavelan hardware */
-	wv_check_ioaddr(u_short,	/* ioaddr */
+	wv_check_ioaddr(u_long,		/* ioaddr */
 			u_char *);	/* mac address (read) */
 /* ---------------------- INTERRUPT HANDLING ---------------------- */
 static void
diff -u --recursive --new-file v2.0.30/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.0.30/linux/drivers/pci/pci.c	Mon Mar 31 13:23:23 1997
+++ linux/drivers/pci/pci.c	Mon Aug  4 11:45:55 1997
@@ -241,6 +241,7 @@
 	DEVICE(	INTEL,		INTEL_82371SB_1,"82371SB Natoma/Triton II PIIX3"),
 	DEVICE( INTEL,		INTEL_82371SB_2,"82371SB Natoma/Triton II PIIX3"),
 	DEVICE( INTEL,		INTEL_82437VX,	"82437VX Triton II"),
+	DEVICE( INTEL,		INTEL_82371AB,	"82371AB 430TX PIIX4"),
 	DEVICE( INTEL,		INTEL_P6,	"Orion P6"),
 	DEVICE( ADAPTEC,	ADAPTEC_7850,	"AIC-7850"),
 	DEVICE( ADAPTEC,	ADAPTEC_7855,	"AIC-7855"),
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.0.30/linux/drivers/scsi/Config.in	Fri Feb 28 15:14:18 1997
+++ linux/drivers/scsi/Config.in	Mon Aug  4 14:57:13 1997
@@ -18,7 +18,14 @@
 dep_tristate 'Adaptec AHA152X/2825 support' CONFIG_SCSI_AHA152X $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1542 support' CONFIG_SCSI_AHA1542 $CONFIG_SCSI
 dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
-dep_tristate 'Adaptec AHA274X/284X/294X support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
+dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
+if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
+    bool '   Enable tagged command queueing' CONFIG_AIC7XXX_TAGGED_QUEUEING Y
+    int  '   Maximum number of commands per LUN' CONFIG_AIC7XXX_CMDS_PER_LUN 8
+    bool '   Enable SCB paging' CONFIG_AIC7XXX_PAGE_ENABLE N
+    bool '   Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N
+    int  '   delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 15
+fi
 dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
 dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
 dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.0.30/linux/drivers/scsi/Makefile	Fri Feb 28 15:14:18 1997
+++ linux/drivers/scsi/Makefile	Mon Aug  4 14:56:15 1997
@@ -348,6 +348,11 @@
   endif
 endif
 
+
+ifeq ($(CONFIG_BLK_DEV_IDESCSI),y)
+L_OBJS += ide-scsi.o
+endif
+
 include $(TOPDIR)/Rules.make
 
 BusLogic.o: BusLogic.c FlashPoint.c
@@ -359,12 +364,8 @@
 aha152x.o: aha152x.c
 	$(CC) $(CFLAGS) $(AHA152X) -c aha152x.c 
 
-aic7xxx_asm:	aic7xxx_asm.c
-	$(HOSTCC) -o $@ aic7xxx_asm.c
-
-aic7xxx.c: aic7xxx_seq.h
-aic7xxx_seq.h:	aic7xxx_asm aic7xxx.seq
-	./aic7xxx_asm -o $@ aic7xxx.seq
+aic7xxx.o: aic7xxx.c aic7xxx_seq.h aic7xxx_reg.h
+	$(CC) $(CFLAGS) -c -o $@ aic7xxx.c
 
 seagate.o: seagate.c
 	$(CC) $(CFLAGS) -DARBITRATE -DSLOW_HANDSHAKE -DFAST32 -c seagate.c 
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx/aic7xxx.reg linux/drivers/scsi/aic7xxx/aic7xxx.reg
--- v2.0.30/linux/drivers/scsi/aic7xxx/aic7xxx.reg	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx.reg	Thu Jul 31 12:37:17 1997
@@ -0,0 +1,1120 @@
+/*
+ * Aic7xxx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	$Id: aic7xxx.reg,v 1.4 1997/06/27 19:38:39 gibbs Exp $
+ */
+
+/*
+ * This file is processed by the aic7xxx_asm utility for use in assembling
+ * firmware for the aic7xxx family of SCSI host adapters as well as to generate
+ * a C header file for use in the kernel portion of the Aic7xxx driver.
+ *
+ * All page numbers refer to the Adaptec AIC-7770 Data Book available from
+ * Adaptec's Technical Documents Department 1-800-934-2766
+ */
+
+/*
+ * SCSI Sequence Control (p. 3-11).
+ * Each bit, when set starts a specific SCSI sequence on the bus
+ */
+register SCSISEQ {
+	address			0x000
+	access_mode RW
+	bit	TEMODE		0x80
+	bit	ENSELO		0x40
+	bit	ENSELI		0x20
+	bit	ENRSELI		0x10
+	bit	ENAUTOATNO	0x08
+	bit	ENAUTOATNI	0x04
+	bit	ENAUTOATNP	0x02
+	bit	SCSIRSTO	0x01
+}
+
+/*
+ * SCSI Transfer Control 0 Register (pp. 3-13).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL0 {
+	address			0x001
+	access_mode RW
+	bit	DFON		0x80
+	bit	DFPEXP		0x40
+	bit	FAST20		0x20
+	bit	CLRSTCNT	0x10
+	bit	SPIOEN		0x08
+	bit	SCAMEN		0x04
+	bit	CLRCHN		0x02
+}
+
+/*
+ * SCSI Transfer Control 1 Register (pp. 3-14,15).
+ * Controls the SCSI module data path.
+ */
+register SXFRCTL1 {
+	address			0x002
+	access_mode RW
+	bit	BITBUCKET	0x80
+	bit	SWRAPEN		0x40
+	bit	ENSPCHK		0x20
+	mask	STIMESEL	0x18
+	bit	ENSTIMER	0x04
+	bit	ACTNEGEN	0x02
+	bit	STPWEN		0x01	/* Powered Termination */
+}
+
+/*
+ * SCSI Control Signal Read Register (p. 3-15).
+ * Reads the actual state of the SCSI bus pins
+ */
+register SCSISIGI {
+	address			0x003
+	access_mode RO
+	bit	CDI		0x80
+	bit	IOI		0x40
+	bit	MSGI		0x20
+	bit	ATNI		0x10
+	bit	SELI		0x08
+	bit	BSYI		0x04
+	bit	REQI		0x02
+	bit	ACKI		0x01
+/*
+ * Possible phases in SCSISIGI
+ */
+	mask	PHASE_MASK	CDI|IOI|MSGI
+	mask	P_DATAOUT	0x00
+	mask	P_DATAIN	IOI
+	mask	P_COMMAND	CDI
+	mask	P_MESGOUT	CDI|MSGI
+	mask	P_STATUS	CDI|IOI
+	mask	P_MESGIN	CDI|IOI|MSGI
+}
+
+/*
+ * SCSI Control Signal Write Register (p. 3-16).
+ * Writing to this register modifies the control signals on the bus.  Only
+ * those signals that are allowed in the current mode (Initiator/Target) are
+ * asserted.
+ */
+register SCSISIGO {
+	address			0x003
+	access_mode WO
+	bit	CDO		0x80
+	bit	IOO		0x40
+	bit	MSGO		0x20
+	bit	ATNO		0x10
+	bit	SELO		0x08
+	bit	BSYO		0x04
+	bit	REQO		0x02
+	bit	ACKO		0x01
+/*
+ * Possible phases to write into SCSISIG0
+ */
+	mask	PHASE_MASK	CDI|IOI|MSGI
+	mask	P_DATAOUT	0x00
+	mask	P_DATAIN	IOI
+	mask	P_COMMAND	CDI
+	mask	P_MESGOUT	CDI|MSGI
+	mask	P_STATUS	CDI|IOI
+	mask	P_MESGIN	CDI|IOI|MSGI
+}
+
+/* 
+ * SCSI Rate Control (p. 3-17).
+ * Contents of this register determine the Synchronous SCSI data transfer
+ * rate and the maximum synchronous Req/Ack offset.  An offset of 0 in the
+ * SOFS (3:0) bits disables synchronous data transfers.  Any offset value
+ * greater than 0 enables synchronous transfers.
+ */
+register SCSIRATE {
+	address			0x004
+	access_mode RW
+	bit	WIDEXFER	0x80		/* Wide transfer control */
+	mask	SXFR		0x70		/* Sync transfer rate */
+	mask	SOFS		0x0f		/* Sync offset */
+}
+
+/*
+ * SCSI ID (p. 3-18).
+ * Contains the ID of the board and the current target on the
+ * selected channel.
+ */
+register SCSIID	{
+	address			0x005
+	access_mode RW
+	mask	TID		0xf0		/* Target ID mask */
+	mask	OID		0x0f		/* Our ID mask */
+}
+
+/*
+ * SCSI Latched Data (p. 3-19).
+ * Read/Write latches used to transfer data on the SCSI bus during
+ * Automatic or Manual PIO mode.  SCSIDATH can be used for the
+ * upper byte of a 16bit wide asynchronouse data phase transfer.
+ */
+register SCSIDATL {
+	address			0x006
+	access_mode RW
+}
+
+register SCSIDATH {
+	address			0x007
+	access_mode RW
+}
+
+/*
+ * SCSI Transfer Count (pp. 3-19,20)
+ * These registers count down the number of bytes transferred
+ * across the SCSI bus.  The counter is decremented only once
+ * the data has been safely transferred.  SDONE in SSTAT0 is
+ * set when STCNT goes to 0
+ */ 
+register STCNT {
+	address			0x008
+	size	3
+	access_mode RW
+}
+
+/*
+ * Clear SCSI Interrupt 0 (p. 3-20)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+register CLRSINT0 {
+	address			0x00b
+	access_mode WO
+	bit	CLRSELDO	0x40
+	bit	CLRSELDI	0x20
+	bit	CLRSELINGO	0x10
+	bit	CLRSWRAP	0x08
+	bit	CLRSPIORDY	0x02
+}
+
+/*
+ * SCSI Status 0 (p. 3-21)
+ * Contains one set of SCSI Interrupt codes
+ * These are most likely of interest to the sequencer
+ */
+register SSTAT0	{
+	address			0x00b
+	access_mode RO
+	bit	TARGET		0x80		/* Board acting as target */
+	bit	SELDO		0x40		/* Selection Done */
+	bit	SELDI		0x20		/* Board has been selected */
+	bit	SELINGO		0x10		/* Selection In Progress */
+	bit	SWRAP		0x08		/* 24bit counter wrap */
+	bit	SDONE		0x04		/* STCNT = 0x000000 */
+	bit	SPIORDY		0x02		/* SCSI PIO Ready */
+	bit	DMADONE		0x01		/* DMA transfer completed */
+}
+
+/*
+ * Clear SCSI Interrupt 1 (p. 3-23)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+register CLRSINT1 {
+	address			0x00c
+	access_mode WO
+	bit	CLRSELTIMEO	0x80
+	bit	CLRATNO		0x40
+	bit	CLRSCSIRSTI	0x20
+	bit	CLRBUSFREE	0x08
+	bit	CLRSCSIPERR	0x04
+	bit	CLRPHASECHG	0x02
+	bit	CLRREQINIT	0x01
+}
+
+/*
+ * SCSI Status 1 (p. 3-24)
+ */
+register SSTAT1	{
+	address			0x00c
+	access_mode RO
+	bit	SELTO		0x80
+	bit	ATNTARG 	0x40
+	bit	SCSIRSTI	0x20
+	bit	PHASEMIS	0x10
+	bit	BUSFREE		0x08
+	bit	SCSIPERR	0x04
+	bit	PHASECHG	0x02
+	bit	REQINIT		0x01
+}
+
+/*
+ * SCSI Status 2 (pp. 3-25,26)
+ */
+register SSTAT2 {
+	address			0x00d
+	access_mode RO
+	bit	OVERRUN		0x80
+	mask	SFCNT		0x1f
+}
+
+/*
+ * SCSI Status 3 (p. 3-26)
+ */
+register SSTAT3 {
+	address			0x00e
+	access_mode RO
+	mask	SCSICNT		0xf0
+	mask	OFFCNT		0x0f
+}
+
+/*
+ * SCSI Test Control (p. 3-27)
+ */
+register SCSITEST {
+	address			0x00f
+	access_mode RW
+	bit	RQAKCNT		0x04
+	bit	CNTRTEST	0x02
+	bit	CMODE		0x01
+}
+
+/*
+ * SCSI Interrupt Mode 1 (p. 3-28)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE0 to interrupt via the IRQ pin.
+ */
+register SIMODE0 {
+	address			0x010
+	access_mode RW
+	bit	ENSELDO		0x40
+	bit	ENSELDI		0x20
+	bit	ENSELINGO	0x10
+	bit	ENSWRAP		0x08
+	bit	ENSDONE		0x04
+	bit	ENSPIORDY	0x02
+	bit	ENDMADONE	0x01
+}
+
+/*
+ * SCSI Interrupt Mode 1 (pp. 3-28,29)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+register SIMODE1 {
+	address			0x011
+	access_mode RW
+	bit	ENSELTIMO	0x80
+	bit	ENATNTARG	0x40
+	bit	ENSCSIRST	0x20
+	bit	ENPHASEMIS	0x10
+	bit	ENBUSFREE	0x08
+	bit	ENSCSIPERR	0x04
+	bit	ENPHASECHG	0x02
+	bit	ENREQINIT	0x01
+}
+
+/*
+ * SCSI Data Bus (High) (p. 3-29)
+ * This register reads data on the SCSI Data bus directly.
+ */
+register SCSIBUSL {
+	address			0x012
+	access_mode RO
+}
+
+register SCSIBUSH {
+	address			0x013
+	access_mode RO
+}
+
+/*
+ * SCSI/Host Address (p. 3-30)
+ * These registers hold the host address for the byte about to be
+ * transferred on the SCSI bus.  They are counted up in the same
+ * manner as STCNT is counted down.  SHADDR should always be used
+ * to determine the address of the last byte transferred since HADDR
+ * can be skewed by write ahead.
+ */
+register SHADDR {
+	address			0x014
+	size	4
+	access_mode RO
+}
+
+/*
+ * Selection Timeout Timer (p. 3-30)
+ */
+register SELTIMER {
+	address			0x018
+	access_mode RW
+	bit	STAGE6		0x20
+	bit	STAGE5		0x10
+	bit	STAGE4		0x08
+	bit	STAGE3		0x04
+	bit	STAGE2		0x02
+	bit	STAGE1		0x01
+}
+
+/*
+ * Selection/Reselection ID (p. 3-31)
+ * Upper four bits are the device id.  The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+register SELID {
+	address			0x019
+	access_mode RW
+	mask	SELID_MASK	0xf0
+	bit	ONEBIT		0x08
+}
+
+/*
+ * SCSI Block Control (p. 3-32)
+ * Controls Bus type and channel selection.  In a twin channel configuration
+ * addresses 0x00-0x1e are gated to the appropriate channel based on this
+ * register.  SELWIDE allows for the coexistence of 8bit and 16bit devices
+ * on a wide bus.
+ */
+register SBLKCTL {
+	address			0x01f
+	access_mode RW
+	bit	DIAGLEDEN	0x80	/* Aic78X0 only */
+	bit	DIAGLEDON	0x40	/* Aic78X0 only */
+	bit	AUTOFLUSHDIS	0x20
+	bit	SELBUSB		0x08
+	bit	SELWIDE		0x02
+}
+
+/*
+ * Sequencer Control (p. 3-33)
+ * Error detection mode and speed configuration
+ */
+register SEQCTL {
+	address			0x060
+	access_mode RW
+	bit	PERRORDIS	0x80
+	bit	PAUSEDIS	0x40
+	bit	FAILDIS		0x20
+	bit	FASTMODE	0x10
+	bit	BRKADRINTEN	0x08
+	bit	STEP		0x04
+	bit	SEQRESET	0x02
+	bit	LOADRAM		0x01
+}
+
+/*
+ * Sequencer RAM Data (p. 3-34)
+ * Single byte window into the Scratch Ram area starting at the address
+ * specified by SEQADDR0 and SEQADDR1.  To write a full word, simply write
+ * four bytes in sucessesion.  The SEQADDRs will increment after the most
+ * significant byte is written
+ */
+register SEQRAM {
+	address			0x061
+	access_mode RW
+}
+
+/*
+ * Sequencer Address Registers (p. 3-35)
+ * Only the first bit of SEQADDR1 holds addressing information
+ */
+register SEQADDR0 {
+	address			0x062
+	access_mode RW
+}
+
+register SEQADDR1 {
+	address			0x063
+	access_mode RW
+	mask	SEQADDR1_MASK	0x01
+}
+
+/*
+ * Accumulator
+ * We cheat by passing arguments in the Accumulator up to the kernel driver
+ */
+register ACCUM {
+	address			0x064
+	access_mode RW
+	accumulator
+}
+
+register SINDEX	{
+	address			0x065
+	access_mode RW
+	sindex
+}
+
+register DINDEX {
+	address			0x066
+	access_mode RW
+}
+
+register ALLONES {
+	address			0x069
+	access_mode RO
+	allones
+}
+
+register ALLZEROS {
+	address			0x06a
+	access_mode RO
+	allzeros
+}
+
+register NONE {
+	address			0x06a
+	access_mode WO
+	none
+}
+
+register FLAGS {
+	address			0x06b
+	access_mode RO
+	bit	ZERO		0x02
+	bit	CARRY		0x01
+}
+
+register SINDIR	{
+	address			0x06c
+	access_mode RO
+}
+
+register DINDIR	 {
+	address			0x06d
+	access_mode WO
+}
+
+register FUNCTION1 {
+	address			0x06e
+	access_mode RW
+}
+
+register STACK {
+	address			0x06f
+	access_mode RO
+}
+
+/*
+ * Board Control (p. 3-43)
+ */
+register BCTL {
+	address			0x084
+	access_mode RW
+	bit	ACE		0x08
+	bit	ENABLE		0x01
+}
+
+/*
+ * On the aic78X0 chips, Board Control is replaced by the DSCommand
+ * register (p. 4-64)
+ */
+register DSCOMMAND {
+	address			0x084
+	access_mode RW
+	bit	CACHETHEN	0x80	/* Cache Threshold enable */
+	bit	DPARCKEN	0x40	/* Data Parity Check Enable */
+	bit	MPARCKEN	0x20	/* Memory Parity Check Enable */
+	bit	EXTREQLCK	0x10	/* External Request Lock */
+}
+
+/*
+ * Bus On/Off Time (p. 3-44)
+ */
+register BUSTIME {
+	address			0x085
+	access_mode RW
+	mask	BOFF		0xf0
+	mask	BON		0x0f
+}
+
+/*
+ * Bus Speed (p. 3-45)
+ */
+register BUSSPD {
+	address			0x086
+	access_mode RW
+	mask	DFTHRSH		0xc0
+	mask	STBOFF		0x38
+	mask	STBON		0x07
+	mask	DFTHRSH_100	0xc0
+}
+
+/*
+ * Host Control (p. 3-47) R/W
+ * Overall host control of the device.
+ */
+register HCNTRL {
+	address			0x087
+	access_mode RW
+	bit	POWRDN		0x40
+	bit	SWINT		0x10
+	bit	IRQMS		0x08
+	bit	PAUSE		0x04
+	bit	INTEN		0x02
+	bit	CHIPRST		0x01
+	bit	CHIPRSTACK	0x01
+}
+
+/*
+ * Host Address (p. 3-48)
+ * This register contains the address of the byte about
+ * to be transferred across the host bus.
+ */
+register HADDR {
+	address			0x088
+	size	4
+	access_mode RW
+}
+
+register HCNT {
+	address			0x08c
+	size	3
+	access_mode RW
+}
+
+/*
+ * SCB Pointer (p. 3-49)
+ * Gate one of the four SCBs into the SCBARRAY window.
+ */
+register SCBPTR {
+	address			0x090
+	access_mode RW
+}
+
+/*
+ * Interrupt Status (p. 3-50)
+ * Status for system interrupts
+ */
+register INTSTAT {
+	address			0x091
+	access_mode RW
+	bit	BRKADRINT 0x08
+	bit	SCSIINT	  0x04
+	bit	CMDCMPLT  0x02
+	bit	SEQINT    0x01
+	mask	BAD_PHASE	SEQINT		/* unknown scsi bus phase */
+	mask	SEND_REJECT	0x10|SEQINT	/* sending a message reject */
+	mask	NO_IDENT	0x20|SEQINT	/* no IDENTIFY after reconnect*/
+	mask	NO_MATCH	0x30|SEQINT	/* no cmd match for reconnect */
+	mask	EXTENDED_MSG	0x40|SEQINT	/* Extended message received */
+	mask	NO_MATCH_BUSY	0x50|SEQINT	/* Couldn't find BUSY SCB */
+	mask	REJECT_MSG	0x60|SEQINT	/* Reject message received */
+	mask	BAD_STATUS	0x70|SEQINT	/* Bad status from target */
+	mask	RESIDUAL	0x80|SEQINT	/* Residual byte count != 0 */
+	mask	ABORT_CMDCMPLT	0x91		/*
+						 * Command tagged for abort
+						 * completed successfully.
+						 */
+	mask	AWAITING_MSG	0xa0|SEQINT	/*
+						 * Kernel requested to specify
+                                                 * a message to this target
+                                                 * (command was null), so tell
+                                                 * it that it can fill the
+                                                 * message buffer.
+                                                 */
+	mask	MSG_BUFFER_BUSY	0xc0|SEQINT	/*
+						 * Sequencer wants to use the
+						 * message buffer, but it
+						 * already contains a message
+						 */
+	mask	MSGIN_PHASEMIS	0xd0|SEQINT	/*
+						 * Target changed phase on us
+						 * when we were expecting
+						 * another msgin byte.
+						 */
+	mask	DATA_OVERRUN	0xe0|SEQINT	/*
+						 * Target attempted to write
+						 * beyond the bounds of its
+						 * command.
+						 */
+
+	mask	SEQINT_MASK	0xf0|SEQINT	/* SEQINT Status Codes */
+	mask	INT_PEND  (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT)
+}
+
+/*
+ * Hard Error (p. 3-53)
+ * Reporting of catastrophic errors.  You usually cannot recover from
+ * these without a full board reset.
+ */
+register ERROR {
+	address			0x092
+	access_mode RO
+	bit	PARERR		0x08
+	bit	ILLOPCODE	0x04
+	bit	ILLSADDR	0x02
+	bit	ILLHADDR	0x01
+}
+
+/*
+ * Clear Interrupt Status (p. 3-52)
+ */
+register CLRINT {
+	address			0x092
+	access_mode WO
+	bit	CLRBRKADRINT	0x08
+	bit	CLRSCSIINT      0x04
+	bit	CLRCMDINT 	0x02
+	bit	CLRSEQINT 	0x01
+}
+
+register DFCNTRL {
+	address			0x093
+	access_mode RW
+	bit	WIDEODD		0x40
+	bit	SCSIEN		0x20
+	bit	SDMAEN		0x10
+	bit	SDMAENACK	0x10
+	bit	HDMAEN		0x08
+	bit	HDMAENACK	0x08
+	bit	DIRECTION	0x04
+	bit	FIFOFLUSH	0x02
+	bit	FIFORESET	0x01
+}
+
+register DFSTATUS {
+	address			0x094
+	access_mode RO
+	bit	DWORDEMP	0x20
+	bit	MREQPEND	0x10
+	bit	HDONE		0x08
+	bit	DFTHRESH	0x04
+	bit	FIFOFULL	0x02
+	bit	FIFOEMP		0x01
+}
+
+register DFDAT {
+	address			0x099
+	access_mode RW
+}
+
+/*
+ * SCB Auto Increment (p. 3-59)
+ * Byte offset into the SCB Array and an optional bit to allow auto
+ * incrementing of the address during download and upload operations
+ */
+register SCBCNT {
+	address			0x09a
+	access_mode RW
+	bit	SCBAUTO		0x80
+	mask	SCBCNT_MASK	0x1f
+}
+
+/*
+ * Queue In FIFO (p. 3-60)
+ * Input queue for queued SCBs (commands that the seqencer has yet to start)
+ */
+register QINFIFO {
+	address			0x09b
+	access_mode RW
+}
+
+/*
+ * Queue In Count (p. 3-60)
+ * Number of queued SCBs
+ */
+register QINCNT	{
+	address			0x09c
+	access_mode RO
+}
+
+/*
+ * Queue Out FIFO (p. 3-61)
+ * Queue of SCBs that have completed and await the host
+ */
+register QOUTFIFO {
+	address			0x09d
+	access_mode WO
+}
+
+/*
+ * Queue Out Count (p. 3-61)
+ * Number of queued SCBs in the Out FIFO
+ */
+register QOUTCNT {
+	address			0x09e
+	access_mode RO
+}
+
+/*
+ * SCB Definition (p. 5-4)
+ */
+scb {
+	address			0x0a0
+	SCB_CONTROL {
+		size	1
+		bit	MK_MESSAGE      0x80
+		bit	DISCENB         0x40
+		bit	TAG_ENB		0x20
+		bit	MUST_DMAUP_SCB	0x10
+		bit	ABORT_SCB	0x08
+		bit	DISCONNECTED	0x04
+		mask	SCB_TAG_TYPE	0x03
+	}
+	SCB_TCL {
+		size	1
+		bit	SELBUSB		0x08
+		mask	TID		0xf0
+		mask	LID		0x07
+	}
+	SCB_TARGET_STATUS {
+		size	1
+	}
+	SCB_SGCOUNT {
+		size	1
+	}
+	SCB_SGPTR {
+		size	4
+	}
+	SCB_RESID_SGCNT {
+		size	1
+	}
+	SCB_RESID_DCNT	{
+		size	3
+	}
+	SCB_DATAPTR {
+		size	4
+	}
+	SCB_DATACNT {
+		size	3
+	}
+	SCB_LINKED_NEXT {
+		size	1
+	}
+	SCB_CMDPTR {
+		size	4
+	}
+	SCB_CMDLEN {
+		size	1
+	}
+	SCB_TAG {
+		size	1
+	}
+	SCB_NEXT {
+		size	1
+	}
+	SCB_PREV {
+		size	1
+	}
+	SCB_BUSYTARGETS {
+		size	4
+	}
+}
+
+const	SG_SIZEOF	0x08		/* sizeof(struct ahc_dma) */
+
+/* --------------------- AHA-2840-only definitions -------------------- */
+
+register SEECTL_2840 {
+	address			0x0c0
+	access_mode RW
+	bit	CS_2840		0x04
+	bit	CK_2840		0x02
+	bit	DO_2840		0x01
+}
+
+register STATUS_2840 {
+	address			0x0c1
+	access_mode RW
+	bit	EEPROM_TF	0x80
+	mask	BIOS_SEL	0x60
+	mask	ADSEL		0x1e
+	bit	DI_2840		0x01
+}
+
+/* --------------------- AIC-7870-only definitions -------------------- */
+
+register DSPCISTATUS {
+	address			0x086
+}
+
+register BRDCTL	{
+	address			0x01d
+	bit	BRDDAT7		0x80
+	bit	BRDDAT6		0x40
+	bit	BRDDAT5		0x20
+	bit	BRDSTB		0x10
+	bit	BRDCS		0x08
+	bit	BRDRW		0x04
+	bit	BRDCTL1		0x02
+	bit	BRDCTL0		0x01
+}
+
+/*
+ * Serial EEPROM Control (p. 4-92 in 7870 Databook)
+ * Controls the reading and writing of an external serial 1-bit
+ * EEPROM Device.  In order to access the serial EEPROM, you must
+ * first set the SEEMS bit that generates a request to the memory
+ * port for access to the serial EEPROM device.  When the memory
+ * port is not busy servicing another request, it reconfigures
+ * to allow access to the serial EEPROM.  When this happens, SEERDY
+ * gets set high to verify that the memory port access has been
+ * granted.  
+ *
+ * After successful arbitration for the memory port, the SEECS bit of 
+ * the SEECTL register is connected to the chip select.  The SEECK, 
+ * SEEDO, and SEEDI are connected to the clock, data out, and data in 
+ * lines respectively.  The SEERDY bit of SEECTL is useful in that it 
+ * gives us an 800 nsec timer.  After a write to the SEECTL register, 
+ * the SEERDY goes high 800 nsec later.  The one exception to this is 
+ * when we first request access to the memory port.  The SEERDY goes 
+ * high to signify that access has been granted and, for this case, has 
+ * no implied timing.
+ *
+ * See 93cx6.c for detailed information on the protocol necessary to 
+ * read the serial EEPROM.
+ */
+register SEECTL {
+	address			0x01e
+	bit	EXTARBACK	0x80
+	bit	EXTARBREQ	0x40
+	bit	SEEMS		0x20
+	bit	SEERDY		0x10
+	bit	SEECS		0x08
+	bit	SEECK		0x04
+	bit	SEEDO		0x02
+	bit	SEEDI		0x01
+}
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+/* These offsets are either to values that are initialized by the board's
+ * BIOS or are specified by the sequencer code.
+ *
+ * The host adapter card (at least the BIOS) uses 20-2f for SCSI
+ * device information, 32-33 and 5a-5f as well. As it turns out, the
+ * BIOS trashes 20-2f, writing the synchronous negotiation results
+ * on top of the BIOS values, so we re-use those for our per-target
+ * scratchspace (actually a value that can be copied directly into
+ * SCSIRATE).  The kernel driver will enable synchronous negotiation
+ * for all targets that have a value other than 0 in the lower four
+ * bits of the target scratch space.  This should work regardless of
+ * whether the bios has been installed.
+ */
+
+scratch_ram {
+	address			0x020
+
+	/*
+	 * 1 byte per target starting at this address for configuration values
+	 */
+	TARG_SCRATCH {
+		size		16
+	}
+	ULTRA_ENB {
+		size		2
+	}
+	/*
+	 * Bit vector of targets that have disconnection disabled.
+	 */
+	DISC_DSB {
+		size		2
+	}
+	/*
+	 * Length of pending message
+	 */
+	MSG_LEN	{
+		size		1
+	}
+	/* We reserve 8bytes to store outgoing messages */
+	MSG_OUT {
+		size		8
+	}
+	/* Parameters for DMA Logic */
+	DMAPARAMS {
+		size		1
+		bit	WIDEODD		0x40
+		bit	SCSIEN		0x20
+		bit	SDMAEN		0x10
+		bit	SDMAENACK	0x10
+		bit	HDMAEN		0x08
+		bit	HDMAENACK	0x08
+		bit	DIRECTION	0x04
+		bit	FIFOFLUSH	0x02
+		bit	FIFORESET	0x01
+	}
+	/*
+	 * Number of SCBs supported by
+	 * this card.
+	 */
+	SCBCOUNT {
+		size		1
+	}
+	/*
+	 * Two's complement of SCBCOUNT
+	 */
+	COMP_SCBCOUNT {
+		size		1
+	}
+	/*
+	 * Mask of bits to test against
+	 * when looking at the Queue Count
+	 * registers.  Works around a bug
+	 * on aic7850 chips. 
+	 */
+	QCNTMASK {
+		size		1
+	}
+	SEQ_FLAGS {
+		size		1
+		bit	RESELECTED	0x80
+		bit	IDENTIFY_SEEN	0x40
+		bit	TAGGED_SCB	0x20
+		bit	DPHASE		0x10
+		bit	PAGESCBS	0x04
+		bit	WIDE_BUS	0x02
+		bit	TWIN_BUS	0x01
+	}
+	/*
+	 * Temporary storage for the
+	 * target/channel/lun of a
+	 * reconnecting target
+	 */
+	SAVED_TCL {
+		size		1
+	}
+	SG_COUNT {
+		size		1
+	}
+	/* working value of SG pointer */
+	SG_NEXT	{
+		size		4
+	}
+	/*
+	 * head of list of SCBs awaiting
+	 * selection
+	 */
+	WAITING_SCBH {
+		size		1
+	}
+	SAVED_LINKPTR {
+		size		1
+	}
+	SAVED_SCBPTR {
+		size		1
+	}
+	/*
+	 * The sequencer will stick the frist byte of any rejected message here
+	 * so we can see what is getting thrown away.
+	 */
+	REJBYTE	{
+		size		1
+	}
+	/*
+	 * The last bus phase as seen by the sequencer. 
+	 */
+	LASTPHASE {
+		size		1
+		bit	CDI		0x80
+		bit	IOI		0x40
+		bit	MSGI		0x20
+		mask	PHASE_MASK	CDI|IOI|MSGI
+		mask	P_DATAOUT	0x00
+		mask	P_DATAIN	IOI
+		mask	P_COMMAND	CDI
+		mask	P_MESGOUT	CDI|MSGI
+		mask	P_STATUS	CDI|IOI
+		mask	P_MESGIN	CDI|IOI|MSGI
+		mask	P_BUSFREE	0x01
+	}
+	MSGIN_EXT_LEN {
+		size		1
+	}
+	MSGIN_EXT_OPCODE {
+		size		1
+	}
+	/*
+	 * location 3, stores the last
+	 * byte of an extended message if
+	 * it passes the two bytes of space
+	 * we allow now.  This byte isn't
+	 * used for anything, it just makes
+	 * the code shorter for tossing
+	 * extra bytes.
+	 */
+	MSGIN_EXT_BYTES	{
+		size		3
+	}
+	/*
+	 * head of list of SCBs that are
+	 * disconnected.  Used for SCB
+	 * paging.
+	 */
+	DISCONNECTED_SCBH {
+		size		1
+	}
+	/*
+	 * head of list of SCBs that are
+	 * not in use.  Used for SCB paging.
+	 */
+	FREE_SCBH {
+		size		1
+	}
+	HSCB_ADDR {
+		size		4
+	}
+	CUR_SCBID {
+		size		1
+	}
+	ARG_1 {
+		size		1
+		mask	SEND_MSG	0x80
+		mask	SEND_SENSE	0x40
+		mask	SEND_REJ	0x20
+		alias	RETURN_1
+	}
+	/*
+	 * These are reserved registers in the card's scratch ram.  Some of
+	 * the values are specified in the AHA2742 technical reference manual
+	 * and are initialized by the BIOS at boot time.
+	 */
+	SCSICONF {
+		address		0x05a
+		size		1
+		bit	RESET_SCSI	0x40
+	}
+	HOSTCONF {
+		address		0x05d
+		size		1
+	}
+	HA_274_BIOSCTRL	{
+		address		0x05f
+		size		1
+		mask	BIOSMODE		0x30
+		mask	BIOSDISABLED		0x30	
+		bit	CHANNEL_B_PRIMARY	0x08
+	}
+}
+
+const SCB_LIST_NULL	0xff
+
+
+/* WDTR Message values */
+const BUS_8_BIT		0x00
+const BUS_16_BIT		0x01
+const BUS_32_BIT		0x02
+const MAX_OFFSET_8BIT		0x0f
+const MAX_OFFSET_16BIT	0x08
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq
--- v2.0.30/linux/drivers/scsi/aic7xxx/aic7xxx.seq	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx.seq	Thu Jul 31 12:37:17 1997
@@ -0,0 +1,1147 @@
+/*
+ * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	$Id: aic7xxx.seq,v 1.74 1997/06/27 19:38:42 gibbs Exp $
+ */
+
+#include <aic7xxx.reg>
+#include <scsi_message.h>
+
+/*
+ * A few words on the waiting SCB list:
+ * After starting the selection hardware, we check for reconnecting targets
+ * as well as for our selection to complete just in case the reselection wins
+ * bus arbitration.  The problem with this is that we must keep track of the
+ * SCB that we've already pulled from the QINFIFO and started the selection
+ * on just in case the reselection wins so that we can retry the selection at
+ * a later time.  This problem cannot be resolved by holding a single entry
+ * in scratch ram since a reconnecting target can request sense and this will
+ * create yet another SCB waiting for selection.  The solution used here is to 
+ * use byte 27 of the SCB as a psuedo-next pointer and to thread a list
+ * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB indexes, 
+ * SCB_LIST_NULL is 0xff which is out of range.  An entry is also added to
+ * this list everytime a request sense occurs or after completing a non-tagged
+ * command for which a second SCB has been queued.  The sequencer will
+ * automatically consume the entries.
+ */
+
+/*
+ * We assume that the kernel driver may reset us at any time, even in the
+ * middle of a DMA, so clear DFCNTRL too.
+ */
+reset:
+	clr	SCSISIGO;		/* De-assert BSY */
+	/* Always allow reselection */
+	mvi	SCSISEQ, ENRSELI|ENAUTOATNP;
+	call	clear_target_state;
+poll_for_work:
+	test	SSTAT0,SELDO	jnz select;
+	test	SSTAT0,SELDI	jnz reselect;
+	test	SCSISEQ, ENSELO	jnz poll_for_work;
+.if ( TWIN_CHANNEL )
+	/*
+	 * Twin channel devices cannot handle things like SELTO
+	 * interrupts on the "background" channel.  So, if we
+	 * are selecting, keep polling the current channel util
+	 * either a selection or reselection occurs.
+	 */
+	xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
+	test	SSTAT0,SELDO	jnz select;
+	test	SSTAT0,SELDI	jnz reselect;
+	test	SCSISEQ, ENSELO	jnz poll_for_work;
+	xor	SBLKCTL,SELBUSB;	/* Toggle back */
+.endif
+	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
+test_queue:
+	/* Has the driver posted any work for us? */
+	mov	A, QCNTMASK;
+	test	QINCNT,A	jz poll_for_work;
+
+/*
+ * We have at least one queued SCB now and we don't have any 
+ * SCBs in the list of SCBs awaiting selection.  If we have
+ * any SCBs available for use, pull the tag from the QINFIFO
+ * and get to work on it.
+ */
+.if ( SCB_PAGING )
+	mov	ALLZEROS	call	get_free_or_disc_scb;
+	cmp	SINDEX, SCB_LIST_NULL	je poll_for_work;
+.endif
+dequeue_scb:
+	mov	CUR_SCBID,QINFIFO;
+.if !( SCB_PAGING )
+	/* In the non-paging case, the SCBID == hardware SCB index */
+	mov	SCBPTR, CUR_SCBID;
+.endif
+dma_queued_scb:
+/*
+ * DMA the SCB from host ram into the current SCB location.
+ */
+	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+	mov	CUR_SCBID	call dma_scb;
+
+/*
+ * See if there is not already an active SCB for this target.  This code
+ * locks out on a per target basis instead of target/lun.  Although this
+ * is not ideal for devices that have multiple luns active at the same
+ * time, it is faster than looping through all SCB's looking for active
+ * commands.  We also don't have enough spare SCB space for us to store the
+ * SCBID of the currently busy transaction for each target/lun making it
+ * impossible to link up the SCBs.
+ */
+test_busy:
+	test	SCB_CONTROL, TAG_ENB|ABORT_SCB jnz start_scb;
+	mvi	SEQCTL, PAUSEDIS|FASTMODE;
+	mov	SAVED_SCBPTR, SCBPTR;
+	mov	SCB_TCL		call	index_untagged_scb;
+	mov	ARG_1, SINDIR;			/*
+						 * ARG_1 should
+						 * now have the SCB ID of
+						 * any active, non-tagged,
+						 * command for this target.
+						 */
+	cmp	ARG_1, SCB_LIST_NULL je make_busy;
+.if ( SCB_PAGING )
+	/*
+	 * Put this SCB back onto the free list.  It
+	 * may be necessary to satisfy the search for
+	 * the active SCB.
+	 */
+	mov	SCBPTR, SAVED_SCBPTR;
+	call	add_scb_to_free_list;
+	/* Find the active SCB */
+	mov	ALLZEROS	call findSCB;
+	/*
+	 * If we couldn't find it, tell the kernel.  This should
+	 * never happen.
+	 */
+	cmp	SINDEX, SCB_LIST_NULL	jne paged_busy_link;
+	mvi	INTSTAT, NO_MATCH_BUSY;
+paged_busy_link:
+	/* Link us in */
+	mov	SCB_LINKED_NEXT, CUR_SCBID;
+	/* Put it back on the disconnected list */
+	call	add_scb_to_disc_list;
+	mvi	SEQCTL, FASTMODE;
+	jmp	poll_for_work;
+.else
+simple_busy_link:
+	mov	SCBPTR, ARG_1;
+	mov	SCB_LINKED_NEXT, CUR_SCBID;
+	mvi	SEQCTL, FASTMODE;
+	jmp	poll_for_work;
+.endif
+make_busy:
+	mov	DINDIR, CUR_SCBID;
+	mov	SCBPTR, SAVED_SCBPTR;
+	mvi	SEQCTL, FASTMODE;
+
+start_scb:
+	/*
+	 * Place us on the waiting list in case our selection
+	 * doesn't win during bus arbitration.
+	 */
+	mov	SCB_NEXT,WAITING_SCBH;
+	mov	WAITING_SCBH, SCBPTR;
+start_waiting:
+	/*
+	 * Pull the first entry off of the waiting SCB list
+	 * We don't have to "test_busy" because only transactions that
+	 * have passed that test can be in the WAITING_SCB list.
+	 */
+	mov	SCBPTR, WAITING_SCBH;
+	call	start_selection;
+	jmp	poll_for_work;
+
+start_selection:
+.if ( TWIN_CHANNEL )
+	and	SINDEX,~SELBUSB,SBLKCTL;/* Clear the channel select bit */
+	and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
+	or	SINDEX,A;
+	mov	SBLKCTL,SINDEX;		/* select channel */
+.endif
+initialize_scsiid:
+	and	A, TID, SCB_TCL;	/* Get target ID */
+	and	SCSIID, OID;		/* Clear old target */
+	or	SCSIID, A;
+	mvi	SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
+/*
+ * Reselection has been initiated by a target. Make a note that we've been
+ * reselected, but haven't seen an IDENTIFY message from the target yet.
+ */
+reselect:
+	clr	MSG_LEN;	/* Don't have anything in the mesg buffer */
+	mvi	CLRSINT0, CLRSELDI;
+	/* XXX test for and handle ONE BIT condition */
+	and	SAVED_TCL, SELID_MASK, SELID;
+	or	SEQ_FLAGS,RESELECTED;
+	jmp	select2;
+
+/*
+ * After the selection, remove this SCB from the "waiting SCB"
+ * list.  This is achieved by simply moving our "next" pointer into
+ * WAITING_SCBH.  Our next pointer will be set to null the next time this
+ * SCB is used, so don't bother with it now.
+ */
+select:
+	/* Turn off the selection hardware */
+	mvi	SCSISEQ, ENRSELI|ENAUTOATNP;	/*
+						 * ATN on parity errors
+						 * for "in" phases
+						 */
+	mvi	CLRSINT0, CLRSELDO;
+	mov	SCBPTR, WAITING_SCBH;
+	mov	WAITING_SCBH,SCB_NEXT;
+	mov	SAVED_TCL, SCB_TCL;
+/*
+ * As soon as we get a successful selection, the target should go
+ * into the message out phase since we have ATN asserted.  Prepare
+ * the message to send.
+ *
+ * Messages are stored in scratch RAM starting with a length byte
+ * followed by the message itself.
+ */
+
+mk_identify:
+	and	MSG_OUT,0x7,SCB_TCL;	/* lun */
+	and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
+	or	MSG_OUT,A;		/* or in disconnect privledge */
+	or	MSG_OUT,MSG_IDENTIFYFLAG;
+	mvi	MSG_LEN, 1;
+
+/*
+ * Send a tag message if TAG_ENB is set in the SCB control block.
+ * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
+ */
+mk_tag:
+	test	SCB_CONTROL,TAG_ENB jz  mk_message;
+	and	MSG_OUT[1],TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+	mov	MSG_OUT[2],SCB_TAG;
+	add	MSG_LEN,2;	/* update message length */
+
+/*
+ * Interrupt the driver, and allow it to tweak the message buffer
+ * if it asks.
+ */
+mk_message:
+	test	SCB_CONTROL,MK_MESSAGE  jz select2;
+	mvi     INTSTAT,AWAITING_MSG;
+
+select2:
+	mvi	CLRSINT1,CLRBUSFREE;
+	or	SIMODE1, ENBUSFREE;		/*
+						 * We aren't expecting a
+						 * bus free, so interrupt
+						 * the kernel driver if it
+						 * happens.
+						 */
+/*
+ * Initialize Ultra mode setting and clear the SCSI channel.
+ */
+	or	SXFRCTL0, CLRSTCNT|SPIOEN|CLRCHN;
+.if ( ULTRA )
+ultra:
+	mvi	SINDEX, ULTRA_ENB+1;
+	test	SAVED_TCL, 0x80		jnz ultra_2;	/* Target ID > 7 */
+	dec	SINDEX;
+ultra_2:
+	mov     FUNCTION1,SAVED_TCL;
+	mov     A,FUNCTION1;
+	test	SINDIR, A	jz ndx_dtr;
+	or	SXFRCTL0, FAST20;
+.endif
+ 
+/*
+ * Initialize SCSIRATE with the appropriate value for this target.
+ * The SCSIRATE settings for each target are stored in an array
+ * based at TARG_SCRATCH.
+ */
+ndx_dtr:
+	shr	A,4,SAVED_TCL;
+	test	SBLKCTL,SELBUSB	jz ndx_dtr_2;
+	or	SAVED_TCL, SELBUSB; /* Add the channel bit while we're here */
+	or	A,0x08;			/* Channel B entries add 8 */
+ndx_dtr_2:
+	add	SINDEX,TARG_SCRATCH,A;
+	mov	SCSIRATE,SINDIR;
+
+
+/*
+ * Main loop for information transfer phases.  If BSY is false, then
+ * we have a bus free condition, expected or not.  Otherwise, wait
+ * for the target to assert REQ before checking MSG, C/D and I/O
+ * for the bus phase.
+ *
+ */
+ITloop:
+	test	SSTAT1,REQINIT		jz ITloop;
+	test	SSTAT1, SCSIPERR	jnz ITloop;
+
+	and	A,PHASE_MASK,SCSISIGI;
+	mov	LASTPHASE,A;
+	mov	SCSISIGO,A;
+
+	cmp	ALLZEROS,A	je p_dataout;
+	cmp	A,P_DATAIN	je p_datain;
+	cmp	A,P_COMMAND	je p_command;
+	cmp	A,P_MESGOUT	je p_mesgout;
+	cmp	A,P_STATUS	je p_status;
+	cmp	A,P_MESGIN	je p_mesgin;
+
+	mvi	INTSTAT,BAD_PHASE;	/* unknown phase - signal driver */
+	jmp	ITloop;			/* Try reading the bus again. */
+
+await_busfree:
+	and	SIMODE1, ~ENBUSFREE;
+	call	clear_target_state;
+	mov	NONE, SCSIDATL;		/* Ack the last byte */
+	test	SSTAT1,REQINIT|BUSFREE	jz .;
+	test	SSTAT1, BUSFREE jnz poll_for_work;
+	mvi	INTSTAT, BAD_PHASE;
+	
+clear_target_state:
+	clr	DFCNTRL;
+	clr	SCSIRATE;		/*
+					 * We don't know the target we will
+					 * connect to, so default to narrow
+					 * transfers to avoid parity problems.
+					 */
+	and	SXFRCTL0, ~FAST20;	
+	mvi	LASTPHASE, P_BUSFREE;
+	/* clear target specific flags */
+	and	SEQ_FLAGS,~(RESELECTED|IDENTIFY_SEEN|TAGGED_SCB|DPHASE) ret;
+
+p_dataout:
+	mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET;
+	jmp	data_phase_init;
+
+/*
+ * If we re-enter the data phase after going through another phase, the
+ * STCNT may have been cleared, so restore it from the residual field.
+ */
+data_phase_reinit:
+	mvi	DINDEX, STCNT;
+	mvi	SCB_RESID_DCNT	call bcopy_3;
+	jmp	data_phase_loop;
+
+p_datain:
+	mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+data_phase_init:
+	call	assert;			/*
+					 * Ensure entering a data
+					 * phase is okay - seen identify, etc.
+					 */
+
+	test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
+
+	/*
+	 * Initialize the DMA address and counter from the SCB.
+	 * Also set SG_COUNT and SG_NEXT in memory since we cannot
+	 * modify the values in the SCB itself until we see a
+	 * save data pointers message.
+	 */
+	mvi	DINDEX, HADDR;
+	mvi	SCB_DATAPTR	call bcopy_7;
+
+	call	set_stcnt_from_hcnt;
+
+	mov	SG_COUNT,SCB_SGCOUNT;
+
+	mvi	DINDEX, SG_NEXT;
+	mvi	SCB_SGPTR	call bcopy_4;
+
+data_phase_loop:
+/* Guard against overruns */
+	test	SG_COUNT, 0xff jnz data_phase_inbounds;
+/*
+ * Turn on 'Bit Bucket' mode, set the transfer count to
+ * 16meg and let the target run until it changes phase.
+ * When the transfer completes, notify the host that we
+ * had an overrun.
+ */
+	or	SXFRCTL1,BITBUCKET;
+	mvi	HCNT[0], 0xff;
+	mvi	HCNT[1], 0xff;
+	mvi	HCNT[2], 0xff;
+	call	set_stcnt_from_hcnt;
+
+data_phase_inbounds:
+/* If we are the last SG block, ensure wideodd is off. */
+	cmp	SG_COUNT,0x01 jne data_phase_wideodd;
+	and	DMAPARAMS, ~WIDEODD;
+data_phase_wideodd:
+	mov	DMAPARAMS  call dma;
+
+/* Go tell the host about any overruns */
+	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun;
+
+/* Exit if we had an underrun.  dma clears SINDEX in this case. */
+	test	SINDEX,0xff	jz data_phase_finish;
+
+/*
+ * Advance the scatter-gather pointers if needed 
+ */
+sg_advance:
+	dec	SG_COUNT;	/* one less segment to go */
+
+	test	SG_COUNT, 0xff	jz data_phase_finish; /* Are we done? */
+
+	clr	A;			/* add sizeof(struct scatter) */
+	add	SG_NEXT[0],SG_SIZEOF;
+	adc	SG_NEXT[1],A;
+
+/*
+ * Load a struct scatter and set up the data address and length.
+ * If the working value of the SG count is nonzero, then
+ * we need to load a new set of values.
+ *
+ * This, like all DMA's, assumes little-endian host data storage.
+ */
+sg_load:
+	mvi	DINDEX, HADDR;
+	mvi	SG_NEXT	call bcopy_4;
+
+	mvi	HCNT[0],SG_SIZEOF;
+	clr	HCNT[1];
+	clr	HCNT[2];
+
+	or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+
+	call	dma_finish;
+
+/*
+ * Copy data from FIFO into SCB data pointer and data count.  This assumes
+ * that the SG segments are of the form:
+ *
+ * struct ahc_dma_seg {
+ *	u_int32_t	addr;		four bytes, little-endian order
+ *	u_int32_t	len;		four bytes, little endian order
+ * };
+ */
+	mvi	HADDR	call dfdat_in_7;
+
+/* Load STCNT as well.  It is a mirror of HCNT */
+	call	set_stcnt_from_hcnt;
+	test	SSTAT1,PHASEMIS	jz data_phase_loop;
+
+data_phase_finish:
+/*
+ * After a DMA finishes, save the SG and STCNT residuals back into the SCB
+ * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
+ * were transferred on the SCSI (as opposed to the host) bus.
+ */
+	mov	SCB_RESID_DCNT[0],STCNT[0];
+	mov	SCB_RESID_DCNT[1],STCNT[1];
+	mov	SCB_RESID_DCNT[2],STCNT[2];
+	mov	SCB_RESID_SGCNT, SG_COUNT;
+
+	/* We have seen a data phase */
+	or	SEQ_FLAGS, DPHASE;
+
+	jmp	ITloop;
+
+data_phase_overrun:
+/*
+ * Turn off BITBUCKET mode and notify the host
+ */
+	and	SXFRCTL1, ~BITBUCKET;
+	mvi	INTSTAT,DATA_OVERRUN;
+	jmp	ITloop;
+
+/*
+ * Command phase.  Set up the DMA registers and let 'er rip.
+ */
+p_command:
+	call	assert;
+
+/*
+ * Load HADDR and HCNT.
+ */
+	mvi	DINDEX, HADDR;
+	mvi	SCB_CMDPTR	call bcopy_5;
+	clr	HCNT[1];
+	clr	HCNT[2];
+
+	call	set_stcnt_from_hcnt;
+
+	mvi	(SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
+	jmp	ITloop;
+
+/*
+ * Status phase.  Wait for the data byte to appear, then read it
+ * and store it into the SCB.
+ */
+p_status:
+	call	assert;
+
+	mov	SCB_TARGET_STATUS, SCSIDATL;
+	jmp	ITloop;
+
+/*
+ * Message out phase.  If there is not an active message, but the target
+ * took us into this phase anyway, build a no-op message and send it.
+ */
+p_mesgout:
+	test	MSG_LEN, 0xff	jnz  p_mesgout_start;
+	mvi	MSG_NOOP	call mk_mesg;	/* build NOP message */
+p_mesgout_start:
+/*
+ * Set up automatic PIO transfer from MSG_OUT.  Bit 3 in
+ * SXFRCTL0 (SPIOEN) is already on.
+ */
+	mvi	SINDEX,MSG_OUT;
+	mov	DINDEX,MSG_LEN;
+
+/*
+ * When target asks for a byte, drop ATN if it's the last one in
+ * the message.  Otherwise, keep going until the message is exhausted.
+ * ATN must be dropped *at least* 90ns before we ack the last byte, so
+ * the code is aranged to execute two instructions before the byte is
+ * transferred to give a good margin of safety
+ *
+ * Keep an eye out for a phase change, in case the target issues
+ * a MESSAGE REJECT.
+ */
+p_mesgout_loop:
+	test	SSTAT1, REQINIT		jz p_mesgout_loop;
+	test	SSTAT1, SCSIPERR	jnz p_mesgout_loop;
+	and	LASTPHASE, PHASE_MASK, SCSISIGI;
+	cmp	LASTPHASE, P_MESGOUT jne p_mesgout_done;
+p_mesgout_testretry:
+	test	DINDEX,0xff	jnz p_mesgout_dropatn;
+	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
+	jmp	p_mesgout_start;
+/*
+ * If the next bus phase after ATN drops is a message out, it means
+ * that the target is requesting that the last message(s) be resent.
+ */
+p_mesgout_dropatn:
+	cmp	DINDEX,1	jne p_mesgout_outb;	/* last byte? */
+	mvi	CLRSINT1,CLRATNO;			/* drop ATN */
+p_mesgout_outb:
+	dec	DINDEX;
+	mov	SCSIDATL,SINDIR;
+	jmp	p_mesgout_loop;
+
+p_mesgout_done:
+	mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
+	clr	MSG_LEN;		/* no active msg */
+	jmp	ITloop;
+
+/*
+ * Message in phase.  Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+	mvi	ACCUM		call inb_first;	/* read the 1st message byte */
+	mov	REJBYTE,A;			/* save it for the driver */
+
+	test	A,MSG_IDENTIFYFLAG	jnz mesgin_identify;
+	cmp	A,MSG_DISCONNECT	je mesgin_disconnect;
+	cmp	A,MSG_SAVEDATAPOINTER	je mesgin_sdptrs;
+	cmp	ALLZEROS,A		je mesgin_complete;
+	cmp	A,MSG_RESTOREPOINTERS	je mesgin_rdptrs;
+	cmp	A,MSG_EXTENDED		je mesgin_extended;
+	cmp	A,MSG_MESSAGE_REJECT	je mesgin_reject;
+	cmp	A,MSG_NOOP		je mesgin_done;
+
+rej_mesgin:
+/*
+ * We have no idea what this message in is, so we issue a message reject
+ * and hope for the best.  In any case, rejection should be a rare
+ * occurrence - signal the driver when it happens.
+ */
+	mvi	INTSTAT,SEND_REJECT;		/* let driver know */
+
+	mvi	MSG_MESSAGE_REJECT	call mk_mesg;
+
+mesgin_done:
+	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
+	jmp	ITloop;
+
+
+mesgin_complete:
+/*
+ * We got a "command complete" message, so put the SCB_TAG into the QOUTFIFO,
+ * and trigger a completion interrupt.  Before doing so, check to see if there
+ * is a residual or the status byte is something other than NO_ERROR (0).  In
+ * either of these conditions, we upload the SCB back to the host so it can
+ * process this information.  In the case of a non zero status byte, we 
+ * additionally interrupt the kernel driver synchronously, allowing it to
+ * decide if sense should be retrieved.  If the kernel driver wishes to request
+ * sense, it will fill the kernel SCB with a request sense command and set
+ * RETURN_1 to SEND_SENSE.  If RETURN_1 is set to SEND_SENSE we redownload
+ * the SCB, and process it as the next command by adding it to the waiting list.
+ * If the kernel driver does not wish to request sense, it need only clear
+ * RETURN_1, and the command is allowed to complete normally.  We don't bother
+ * to post to the QOUTFIFO in the error cases since it would require extra
+ * work in the kernel driver to ensure that the entry was removed before the
+ * command complete code tried processing it.
+ */
+
+/*
+ * First check for residuals
+ */
+	test	SCB_RESID_SGCNT,0xff	jnz upload_scb;
+	test	SCB_TARGET_STATUS,0xff	jz status_ok;	/* Good Status? */
+upload_scb:
+	mvi	DMAPARAMS, FIFORESET;
+	mov	SCB_TAG		call dma_scb;
+check_status:
+	test	SCB_TARGET_STATUS,0xff	jz status_ok;	/* Just a residual? */
+	mvi	INTSTAT,BAD_STATUS;			/* let driver know */
+	cmp	RETURN_1, SEND_SENSE	jne status_ok;
+	/* This SCB becomes the next to execute as it will retrieve sense */
+	mov	SCB_LINKED_NEXT, SCB_TAG;
+	jmp	dma_next_scb;
+
+status_ok:
+/* First, mark this target as free. */
+	test	SCB_CONTROL,TAG_ENB jnz complete;	/*
+							 * Tagged commands
+							 * don't busy the
+							 * target.
+							 */
+	mov	SAVED_SCBPTR, SCBPTR;
+	mov	SAVED_LINKPTR, SCB_LINKED_NEXT;
+	mov	SCB_TCL	call index_untagged_scb;
+	mov	DINDIR, SAVED_LINKPTR;
+	mov	SCBPTR, SAVED_SCBPTR;
+
+complete:
+	/* Post the SCB and issue an interrupt */
+	mov	QOUTFIFO,SCB_TAG;
+	mvi	INTSTAT,CMDCMPLT;
+	test	SCB_CONTROL, ABORT_SCB jz dma_next_scb;
+	mvi	INTSTAT, ABORT_CMDCMPLT;
+
+dma_next_scb:
+	cmp	SCB_LINKED_NEXT, SCB_LIST_NULL	je add_to_free_list;
+.if !( SCB_PAGING )
+	/* Only DMA on top of ourselves if we are the SCB to download */
+	mov	A, SCB_LINKED_NEXT;
+	cmp	SCB_TAG, A	je dma_next_scb2;
+	call	add_scb_to_free_list;
+	mov	SCBPTR, A;
+	jmp	add_to_waiting_list;
+.endif
+dma_next_scb2:
+	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+	mov	SCB_LINKED_NEXT		call dma_scb;
+add_to_waiting_list:
+	mov	SCB_NEXT,WAITING_SCBH;
+	mov	WAITING_SCBH, SCBPTR;
+	/*
+	 * Prepare our selection hardware before the busfree so we have a
+	 * high probability of winning arbitration.
+	 */
+	call	start_selection;
+	jmp	await_busfree;
+add_to_free_list:
+	call	add_scb_to_free_list;
+	jmp	await_busfree;
+
+/*
+ * Is it an extended message?  Copy the message to our message buffer and
+ * notify the host.  The host will tell us whether to reject this message,
+ * respond to it with the message that the host placed in our message buffer,
+ * or simply to do nothing.
+ */
+mesgin_extended:
+	mvi	MSGIN_EXT_LEN	 call inb_next;
+	mov	A, MSGIN_EXT_LEN;
+mesgin_extended_loop:
+	mov	DINDEX	call	inb_next;
+	dec	A;
+	cmp	DINDEX, MSGIN_EXT_BYTES+3 jne mesgin_extended_loop_test;
+	dec	DINDEX;		/* dump by repeatedly filling the last byte */
+mesgin_extended_loop_test:
+	test	A, 0xFF		jnz mesgin_extended_loop;
+mesgin_extended_intr:
+	mvi	INTSTAT,EXTENDED_MSG;		/* let driver know */
+	cmp	RETURN_1,SEND_REJ je rej_mesgin;
+	cmp	RETURN_1,SEND_MSG jne mesgin_done;
+/* The kernel has setup a message to be sent */
+	or	SCSISIGO,ATNO,LASTPHASE;	/* turn on ATNO */
+	jmp	mesgin_done;
+
+/*
+ * Is it a disconnect message?  Set a flag in the SCB to remind us
+ * and await the bus going free.
+ */
+mesgin_disconnect:
+	or	SCB_CONTROL,DISCONNECTED;
+.if ( SCB_PAGING )
+	call	add_scb_to_disc_list;
+.endif
+	jmp	await_busfree;
+
+/*
+ * Save data pointers message:
+ * Copying RAM values back to SCB, for Save Data Pointers message, but
+ * only if we've actually been into a data phase to change them.  This
+ * protects against bogus data in scratch ram and the residual counts
+ * since they are only initialized when we go into data_in or data_out.
+ */
+mesgin_sdptrs:
+	test	SEQ_FLAGS, DPHASE	jz mesgin_done;
+	mov	SCB_SGCOUNT,SG_COUNT;
+
+	/* The SCB SGPTR becomes the next one we'll download */
+	mvi	DINDEX, SCB_SGPTR;
+	mvi	SG_NEXT	call bcopy_4;
+	
+	/* The SCB DATAPTR0 becomes the current SHADDR */
+	mvi	DINDEX, SCB_DATAPTR;
+	mvi	SHADDR		call bcopy_4;
+
+/*
+ * Use the residual number since STCNT is corrupted by any message transfer.
+ */
+	mvi	SCB_RESID_DCNT	call	bcopy_3;
+
+	jmp	mesgin_done;
+
+/*
+ * Restore pointers message?  Data pointers are recopied from the
+ * SCB anytime we enter a data phase for the first time, so all
+ * we need to do is clear the DPHASE flag and let the data phase
+ * code do the rest.
+ */
+mesgin_rdptrs:
+	and	SEQ_FLAGS, ~DPHASE;		/*
+						 * We'll reload them
+						 * the next time through
+						 * the dataphase.
+						 */
+	jmp	mesgin_done;
+
+/*
+ * Identify message?  For a reconnecting target, this tells us the lun
+ * that the reconnection is for - find the correct SCB and switch to it,
+ * clearing the "disconnected" bit so we don't "find" it by accident later.
+ */
+mesgin_identify:
+	test	A,0x78	jnz rej_mesgin;	/*!DiscPriv|!LUNTAR|!Reserved*/
+	and	A,0x07;			/* lun in lower three bits */
+	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */
+	mov	SAVED_TCL call index_untagged_scb;
+	mov	ARG_1, SINDIR;
+.if ( SCB_PAGING )
+	cmp	ARG_1,SCB_LIST_NULL	jne use_findSCB;
+.else
+	cmp	ARG_1,SCB_LIST_NULL	je snoop_tag;
+	/* Directly index the SCB */
+	mov	SCBPTR,ARG_1;
+	test	SCB_CONTROL,DISCONNECTED jz not_found;
+	jmp	setup_SCB;
+.endif
+/*
+ * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
+ * If we get one, we use the tag returned to find the proper
+ * SCB.  With SCB paging, this requires using findSCB for both tagged
+ * and non-tagged transactions since the SCB may exist in any slot.
+ * If we're not using SCB paging, we can use the tag as the direct
+ * index to the SCB.
+ */
+snoop_tag:
+	mov	NONE,SCSIDATL;		/* ACK Identify MSG */
+snoop_tag_loop:
+	test	SSTAT1,REQINIT		jz snoop_tag_loop;
+	test	SSTAT1, SCSIPERR	jnz snoop_tag_loop;
+	and	LASTPHASE, PHASE_MASK, SCSISIGI;
+	cmp	LASTPHASE, P_MESGIN	jne not_found;
+	cmp	SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+	or	SEQ_FLAGS, TAGGED_SCB;
+	mvi	ARG_1	call inb_next;	/* tag value */
+/*
+ * See if the tag is in range.  The tag is < SCBCOUNT if we add
+ * the complement of SCBCOUNT to the incomming tag and there is
+ * no carry.
+ */
+	mov	A,COMP_SCBCOUNT;
+	add	SINDEX,A,ARG_1;
+	jc	not_found;
+
+.if ! ( SCB_PAGING )
+index_by_tag:
+	mov	SCBPTR,ARG_1;
+	mov	A, SAVED_TCL;
+	cmp	SCB_TCL,A		jne not_found;
+	test	SCB_CONTROL,TAG_ENB	jz  not_found;
+	test	SCB_CONTROL,DISCONNECTED jz not_found;
+.else
+/*
+ * Ensure that the SCB the tag points to is for an SCB transaction
+ * to the reconnecting target.
+ */
+use_findSCB:
+	mov	ALLZEROS	call findSCB;	  /* Have to search */
+	cmp	SINDEX, SCB_LIST_NULL	je not_found;
+.endif
+setup_SCB:
+	and	SCB_CONTROL,~DISCONNECTED;
+	or	SEQ_FLAGS,IDENTIFY_SEEN;	  /* make note of IDENTIFY */
+	jmp	mesgin_done;
+
+not_found:
+	mvi	INTSTAT, NO_MATCH;
+	mvi	MSG_BUS_DEV_RESET	call mk_mesg;
+	jmp	mesgin_done;
+
+/*
+ * Message reject?  Let the kernel driver handle this.  If we have an 
+ * outstanding WDTR or SDTR negotiation, assume that it's a response from 
+ * the target selecting 8bit or asynchronous transfer, otherwise just ignore 
+ * it since we have no clue what it pertains to.
+ */
+mesgin_reject:
+	mvi	INTSTAT, REJECT_MSG;
+	jmp	mesgin_done;
+
+/*
+ * [ ADD MORE MESSAGE HANDLING HERE ]
+ */
+
+/*
+ * Locking the driver out, build a one-byte message passed in SINDEX
+ * if there is no active message already.  SINDEX is returned intact.
+ */
+mk_mesg:
+	mvi	SEQCTL, PAUSEDIS|FASTMODE;
+	test	MSG_LEN,0xff	jz mk_mesg1;	/* Should always succeed */
+	
+	/*
+	 * Hmmm.  For some reason the mesg buffer is in use.
+	 * Tell the driver.  It should look at SINDEX to find
+	 * out what we wanted to use the buffer for and resolve
+	 * the conflict.
+	 */
+	mvi	SEQCTL,FASTMODE;
+	mvi	INTSTAT,MSG_BUFFER_BUSY;
+
+mk_mesg1:
+	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
+	mvi	MSG_LEN,1;		/* length = 1 */
+	mov	MSG_OUT,SINDEX;		/* 1-byte message */
+	mvi	SEQCTL,FASTMODE	ret;
+
+/*
+ * Functions to read data in Automatic PIO mode.
+ *
+ * According to Adaptec's documentation, an ACK is not sent on input from
+ * the target until SCSIDATL is read from.  So we wait until SCSIDATL is
+ * latched (the usual way), then read the data byte directly off the bus
+ * using SCSIBUSL.  When we have pulled the ATN line, or we just want to
+ * acknowledge the byte, then we do a dummy read from SCISDATL.  The SCSI
+ * spec guarantees that the target will hold the data byte on the bus until
+ * we send our ACK.
+ *
+ * The assumption here is that these are called in a particular sequence,
+ * and that REQ is already set when inb_first is called.  inb_{first,next}
+ * use the same calling convention as inb.
+ */
+
+inb_next:
+	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
+inb_next_wait:
+	/*
+	 * If there is a parity error, wait for the kernel to
+	 * see the interrupt and prepare our message response
+	 * before continuing.
+	 */
+	test	SSTAT1, REQINIT	jz inb_next_wait;
+	test	SSTAT1, SCSIPERR jnz inb_next_wait;
+	and	LASTPHASE, PHASE_MASK, SCSISIGI;
+	cmp	LASTPHASE, P_MESGIN jne mesgin_phasemis;
+inb_first:
+	mov	DINDEX,SINDEX;
+	mov	DINDIR,SCSIBUSL	ret;		/*read byte directly from bus*/
+inb_last:
+	mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/
+
+mesgin_phasemis:
+/*
+ * We expected to receive another byte, but the target changed phase
+ */
+	mvi	INTSTAT, MSGIN_PHASEMIS;
+	jmp	ITloop;
+
+/*
+ * DMA data transfer.  HADDR and HCNT must be loaded first, and
+ * SINDEX should contain the value to load DFCNTRL with - 0x3d for
+ * host->scsi, or 0x39 for scsi->host.  The SCSI channel is cleared
+ * during initialization.
+ */
+dma:
+	mov	DFCNTRL,SINDEX;
+dma_loop:
+	test	SSTAT0,DMADONE	jnz dma_dmadone;
+	test	SSTAT1,PHASEMIS	jz dma_loop;	/* ie. underrun */
+dma_phasemis:
+	test	SSTAT0,SDONE	jnz dma_checkfifo;
+	mov	SINDEX,ALLZEROS;		/* Notify caller of phasemiss */
+
+/*
+ * We will be "done" DMAing when the transfer count goes to zero, or
+ * the target changes the phase (in light of this, it makes sense that
+ * the DMA circuitry doesn't ACK when PHASEMIS is active).  If we are
+ * doing a SCSI->Host transfer, the data FIFO should be flushed auto-
+ * magically on STCNT=0 or a phase change, so just wait for FIFO empty
+ * status.
+ */
+dma_checkfifo:
+	test	DFCNTRL,DIRECTION	jnz dma_fifoempty;
+dma_fifoflush:
+	test	DFSTATUS,FIFOEMP	jz dma_fifoflush;
+
+dma_fifoempty:
+	/* Don't clobber an inprogress host data transfer */
+	test	DFSTATUS, MREQPEND	jnz dma_fifoempty;
+/*
+ * Now shut the DMA enables off and make sure that the DMA enables are 
+ * actually off first lest we get an ILLSADDR.
+ */
+dma_dmadone:
+	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+dma_halt:
+	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN)	jnz dma_halt; 
+return:
+	ret;
+
+/*
+ * Assert that if we've been reselected, then we've seen an IDENTIFY
+ * message.
+ */
+assert:
+	test	SEQ_FLAGS,RESELECTED	jz return;	/* reselected? */
+	test	SEQ_FLAGS,IDENTIFY_SEEN	jnz return;	/* seen IDENTIFY? */
+
+	mvi	INTSTAT,NO_IDENT 	ret;	/* no - tell the kernel */
+
+.if ( SCB_PAGING )
+/*
+ * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
+ * or by the SCBIDn ARG_1.  The search begins at the SCB index passed in
+ * via SINDEX.  If the SCB cannot be found, SINDEX will be SCB_LIST_NULL,
+ * otherwise, SCBPTR is set to the proper SCB.
+ */
+findSCB:
+	mov	SCBPTR,SINDEX;			/* switch to next SCB */
+	mov	A, ARG_1;			/* Tag passed in ARG_1 */
+	cmp	SCB_TAG,A	jne findSCB_loop;
+	test	SCB_CONTROL,DISCONNECTED jnz foundSCB;/*should be disconnected*/
+findSCB_loop:
+	inc	SINDEX;
+	mov	A,SCBCOUNT;
+	cmp	SINDEX,A	jne findSCB;
+/*
+ * We didn't find it.  If we're paging, pull an SCB and DMA down the
+ * one we want.  If we aren't paging or the SCB we dma down has the
+ * abort flag set, return not found.
+ */
+	mov	ALLZEROS	call	get_free_or_disc_scb;
+	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+	mov	ARG_1	call dma_scb;
+	test	SCB_RESID_SGCNT, 0xff jz . + 2;
+	or	SCB_CONTROL, MUST_DMAUP_SCB;
+	test	SCB_CONTROL, ABORT_SCB jz return;
+find_error:
+	mvi	SINDEX, SCB_LIST_NULL ret;
+foundSCB:
+	test	SCB_CONTROL, ABORT_SCB jnz find_error;
+rem_scb_from_disc_list:
+/* Remove this SCB from the disconnection list */
+	cmp	SCB_NEXT,SCB_LIST_NULL je unlink_prev;
+	mov	SAVED_LINKPTR, SCB_PREV;
+	mov	SCBPTR, SCB_NEXT;
+	mov	SCB_PREV, SAVED_LINKPTR;
+	mov	SCBPTR, SINDEX;
+unlink_prev:
+	cmp	SCB_PREV,SCB_LIST_NULL	je rHead;/* At the head of the list */
+	mov	SAVED_LINKPTR, SCB_NEXT;
+	mov	SCBPTR, SCB_PREV;
+	mov	SCB_NEXT, SAVED_LINKPTR;
+	mov	SCBPTR, SINDEX ret;
+rHead:
+	mov	DISCONNECTED_SCBH,SCB_NEXT ret;
+.else
+	ret;
+.endif
+
+set_stcnt_from_hcnt:
+	mov	STCNT[0], HCNT[0];
+	mov	STCNT[1], HCNT[1];
+	mov	STCNT[2], HCNT[2] ret;
+
+bcopy_7:
+	mov	DINDIR, SINDIR;
+	mov	DINDIR, SINDIR;
+bcopy_5:
+	mov	DINDIR, SINDIR;
+bcopy_4:
+	mov	DINDIR, SINDIR;
+bcopy_3:
+	mov	DINDIR, SINDIR;
+	mov	DINDIR, SINDIR;
+	mov	DINDIR, SINDIR ret;
+
+dma_scb:
+	/*
+	 * SCB index is in SINDEX.  Determine the physical address in
+	 * the host where this SCB is located and load HADDR with it.
+	 */
+	shr	DINDEX, 3, SINDEX;
+	shl	A, 5, SINDEX;
+	add	HADDR[0], A, HSCB_ADDR[0];
+	mov	A, DINDEX;
+	adc	HADDR[1], A, HSCB_ADDR[1];
+	clr	A;
+	adc	HADDR[2], A, HSCB_ADDR[2];
+	adc	HADDR[3], A, HSCB_ADDR[3];
+	/* Setup Count */
+	mvi	HCNT[0], 28;
+	clr	HCNT[1];
+	clr	HCNT[2];
+	mov	DFCNTRL, DMAPARAMS;
+	test	DMAPARAMS, DIRECTION	jnz dma_scb_fromhost;
+	/* Fill it with the SCB data */
+copy_scb_tofifo:
+	mvi	SINDEX, SCB_CONTROL;
+	add	A, 28, SINDEX;
+copy_scb_tofifo_loop:
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	cmp	SINDEX, A jne copy_scb_tofifo_loop;
+	or	DFCNTRL, HDMAEN|FIFOFLUSH;
+dma_scb_fromhost:
+	call	dma_finish;
+	/* If we were putting the SCB, we are done */
+	test	DMAPARAMS, DIRECTION	jz	return;
+	mvi	SCB_CONTROL  call dfdat_in_7;
+	call	dfdat_in_7_continued;
+	call	dfdat_in_7_continued;
+	jmp	dfdat_in_7_continued;
+dfdat_in_7:
+	mov     DINDEX,SINDEX;
+dfdat_in_7_continued:
+	mov	DINDIR,DFDAT;
+	mov	DINDIR,DFDAT;
+	mov	DINDIR,DFDAT;
+	mov	DINDIR,DFDAT;
+	mov	DINDIR,DFDAT;
+	mov	DINDIR,DFDAT;
+	mov	DINDIR,DFDAT ret;
+
+/*
+ * Wait for DMA from host memory to data FIFO to complete, then disable
+ * DMA and wait for it to acknowledge that it's off.
+ */
+dma_finish:
+	test	DFSTATUS,HDONE	jz dma_finish;
+	/* Turn off DMA */
+	and	DFCNTRL, ~HDMAEN;
+	test	DFCNTRL, HDMAEN jnz .;
+	ret;
+
+index_untagged_scb:
+	mov	DINDEX, SINDEX;
+	shr	DINDEX, 4;
+	and	DINDEX, 0x03;			/* Bottom two bits of tid */
+	add	DINDEX, SCB_BUSYTARGETS;
+	shr	A, 6, SINDEX;			/* Target ID divided by 4 */
+	test	SINDEX, SELBUSB jz index_untagged_scb2;
+	add	A, 2;				/* Add 2 positions */
+index_untagged_scb2:
+	mov	SCBPTR, A;			/*
+						 * Select the SCB with this 
+						 * target's information.
+						 */
+	mov	SINDEX, DINDEX	ret;
+
+add_scb_to_free_list:
+	mov	SCB_NEXT, FREE_SCBH;
+	mvi	SCB_TAG, SCB_LIST_NULL;
+	mov	FREE_SCBH, SCBPTR ret;
+
+.if ( SCB_PAGING )
+get_free_or_disc_scb:
+	cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
+	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
+return_error:
+	mvi	SINDEX, SCB_LIST_NULL	ret;
+dequeue_disc_scb:
+	mov	SCBPTR, DISCONNECTED_SCBH;
+/*
+ * If we have a residual, then we are in the middle of some I/O
+ * and we have to send this SCB back up to the kernel so that the
+ * saved data pointers and residual information isn't lost.
+ */
+	test	SCB_CONTROL, MUST_DMAUP_SCB jz . + 3;
+	and	SCB_CONTROL, ~MUST_DMAUP_SCB;
+	jmp	dma_up_scb;
+	test	SCB_RESID_SGCNT,0xff	jnz dma_up_scb;
+	cmp	SCB_LINKED_NEXT, SCB_LIST_NULL je unlink_disc_scb;
+dma_up_scb:
+	mvi	DMAPARAMS, FIFORESET;
+	mov	SCB_TAG		call dma_scb;
+unlink_disc_scb:
+	/* jmp instead of call since we want to return anyway */
+	mov	SCBPTR	jmp rem_scb_from_disc_list;
+dequeue_free_scb:
+	mov	SCBPTR, FREE_SCBH;
+	mov	FREE_SCBH, SCB_NEXT ret;
+
+add_scb_to_disc_list:
+/*
+ * Link this SCB into the DISCONNECTED list.  This list holds the
+ * candidates for paging out an SCB if one is needed for a new command.
+ * Modifying the disconnected list is a critical(pause dissabled) section.
+ */
+	mvi	SCB_PREV, SCB_LIST_NULL;
+	mov	SCB_NEXT, DISCONNECTED_SCBH;
+	mov	DISCONNECTED_SCBH, SCBPTR;
+	cmp	SCB_NEXT,SCB_LIST_NULL je return;
+	mov	SCBPTR,SCB_NEXT;
+	mov	SCB_PREV,DISCONNECTED_SCBH;
+	mov	SCBPTR,DISCONNECTED_SCBH ret;
+.endif
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx/scsi_message.h linux/drivers/scsi/aic7xxx/scsi_message.h
--- v2.0.30/linux/drivers/scsi/aic7xxx/scsi_message.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/scsi_message.h	Thu Jul 31 12:37:17 1997
@@ -0,0 +1,41 @@
+/*
+ * SCSI messages definitions.
+ */
+
+/* Messages (1 byte) */		     /* I/T (M)andatory or (O)ptional */
+#define MSG_CMDCOMPLETE		0x00 /* M/M */
+#define MSG_EXTENDED		0x01 /* O/O */
+#define MSG_SAVEDATAPOINTER	0x02 /* O/O */
+#define MSG_RESTOREPOINTERS	0x03 /* O/O */
+#define MSG_DISCONNECT		0x04 /* O/O */
+#define MSG_INITIATOR_DET_ERR	0x05 /* M/M */
+#define MSG_ABORT		0x06 /* O/M */
+#define MSG_MESSAGE_REJECT	0x07 /* M/M */
+#define MSG_NOOP		0x08 /* M/M */
+#define MSG_PARITY_ERROR	0x09 /* M/M */
+#define MSG_LINK_CMD_COMPLETE	0x0a /* O/O */
+#define MSG_LINK_CMD_COMPLETEF	0x0b /* O/O */
+#define MSG_BUS_DEV_RESET	0x0c /* O/M */
+#define MSG_ABORT_TAG		0x0d /* O/O */
+#define MSG_CLEAR_QUEUE		0x0e /* O/O */
+#define MSG_INIT_RECOVERY	0x0f /* O/O */
+#define MSG_REL_RECOVERY	0x10 /* O/O */
+#define MSG_TERM_IO_PROC	0x11 /* O/O */
+
+/* Messages (2 byte) */
+#define MSG_SIMPLE_Q_TAG	0x20 /* O/O */
+#define MSG_HEAD_OF_Q_TAG	0x21 /* O/O */
+#define MSG_ORDERED_Q_TAG	0x22 /* O/O */
+#define MSG_IGN_WIDE_RESIDUE	0x23 /* O/O */
+
+/* Identify message */		     /* M/M */	
+#define MSG_IDENTIFYFLAG	0x80 
+#define MSG_IDENTIFY(lun, disc)	(((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun))
+#define MSG_ISIDENTIFY(m)	((m) & MSG_IDENTIFYFLAG)
+
+/* Extended messages (opcode and length) */
+#define MSG_EXT_SDTR		0x01
+#define MSG_EXT_SDTR_LEN	0x03
+
+#define MSG_EXT_WDTR		0x03
+#define MSG_EXT_WDTR_LEN	0x02
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx/sequencer.h linux/drivers/scsi/aic7xxx/sequencer.h
--- v2.0.30/linux/drivers/scsi/aic7xxx/sequencer.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/sequencer.h	Thu Jul 31 12:37:17 1997
@@ -0,0 +1,102 @@
+/*
+ * Instruction formats for the sequencer program downloaded to
+ * Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *      $Id: sequencer.h,v 1.2 1997/06/27 19:38:52 gibbs Exp $
+ */
+
+#if defined(__KERNEL__)
+typedef unsigned char u_int8_t;
+#endif
+
+struct ins_format1 {
+	u_int8_t immediate;
+	u_int8_t source;
+	u_int8_t destination;
+	u_int8_t opcode_ret;
+};
+
+struct ins_format2 {
+	u_int8_t shift_control;
+	u_int8_t source;
+	u_int8_t destination;
+	u_int8_t opcode_ret;
+#define RETURN_BIT 0x01
+};
+
+struct ins_format3 {
+	u_int8_t immediate;
+	u_int8_t source;
+	u_int8_t address;
+	u_int8_t opcode_addr;
+#define ADDR_HIGH_BIT 0x01
+};
+
+struct instruction {
+	union {
+		struct ins_format1 format1;
+		struct ins_format2 format2;
+		struct ins_format3 format3;
+		u_int8_t	   bytes[4];
+	} format;
+	u_int	srcline;
+	struct symbol *patch_label;
+	struct {
+		struct instruction *stqe_next; /* next element */
+	} links;
+};
+
+#define	AIC_OP_OR	0x0
+#define	AIC_OP_AND	0x1
+#define AIC_OP_XOR	0x2
+#define	AIC_OP_ADD	0x3
+#define	AIC_OP_ADC	0x4
+#define	AIC_OP_ROL	0x5
+
+#define	AIC_OP_JMP	0x8
+#define AIC_OP_JC	0x9
+#define AIC_OP_JNC	0xa
+#define AIC_OP_CALL	0xb
+#define	AIC_OP_JNE	0xc
+#define	AIC_OP_JNZ	0xd
+#define	AIC_OP_JE	0xe
+#define	AIC_OP_JZ	0xf
+
+/* Pseudo Ops */
+#define	AIC_OP_SHL	0x10
+#define	AIC_OP_SHR	0x20
+#define	AIC_OP_ROR	0x30
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- v2.0.30/linux/drivers/scsi/aic7xxx.c	Tue Apr  8 08:47:46 1997
+++ linux/drivers/scsi/aic7xxx.c	Thu Jul 31 12:37:17 1997
@@ -1,5 +1,3 @@
-#define EXPERIMENTAL_FLAGS 0
-
 /*+M*************************************************************************
  * Adaptec AIC7xxx device driver for Linux.
  *
@@ -29,21 +27,73 @@
  * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
  * ANSI SCSI-2 specification (draft 10c), ...
  *
- * ----------------------------------------------------------------
- *  Modified to include support for wide and twin bus adapters,
- *  DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ * --------------------------------------------------------------------------
+ *
+ *  Modifications by Daniel M. Eischen (deischen@iworks.InterWorks.org):
+ *
+ *  Substantially modified to include support for wide and twin bus
+ *  adapters, DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
  *  SCB paging, and other rework of the code.
  *
- *  Parts of this driver are based on the FreeBSD driver by Justin
- *  T. Gibbs.
+ *  Parts of this driver were also based on the FreeBSD driver by
+ *  Justin T. Gibbs.  His copyright follows:
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994-1997 Justin Gibbs.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions, and the following disclaimer,
+ *    without modification, immediately at the beginning of the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Where this Software is combined with software released under the terms of 
+ * the GNU Public License ("GPL") and the terms of the GPL would require the 
+ * combined work to also be released under the terms of the GPL, the terms
+ * and conditions of this License will apply in addition to those of the
+ * GPL with the exception of any terms or conditions of this License that
+ * conflict with, or are expressly prohibited by, the GPL.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *      $Id: aic7xxx.c,v 1.119 1997/06/27 19:39:18 gibbs Exp $
+ *---------------------------------------------------------------------------
+ *
+ *  Thanks also go to (in alphabetical order) the following:
+ *
+ *    Rory Bolt     - Sequencer bug fixes
+ *    Jay Estabrook - Initial DEC Alpha support
+ *    Doug Ledford  - Much needed abort/reset bug fixes
+ *    Kai Makisara  - DMAing of SCBs
  *
  *  A Boot time option was also added for not resetting the scsi bus.
  *
- *    Form:  aic7xxx=extended,no_reset
+ *    Form:  aic7xxx=extended
+ *           aic7xxx=no_reset
+ *           aic7xxx=ultra
+ *           aic7xxx=irq_trigger:[0,1]  # 0 edge, 1 level
+ *           aic7xxx=verbose
  *
- *    -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 07/07/96
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
  *
- *  $Id: aic7xxx.c,v 4.0 1996/10/13 08:23:42 deang Exp $
+ *  $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
  *-M*************************************************************************/
 
 #ifdef MODULE
@@ -67,7 +117,11 @@
 #include "scsi.h"
 #include "hosts.h"
 #include "aic7xxx.h"
+
+#include "aic7xxx/sequencer.h"
+#include "aic7xxx/scsi_message.h"
 #include "aic7xxx_reg.h"
+#include "aic7xxx_seq.h"
 #include <linux/stat.h>
 #include <linux/malloc.h>	/* for kmalloc() */
 
@@ -81,14 +135,18 @@
 
 struct proc_dir_entry proc_scsi_aic7xxx = {
     PROC_SCSI_AIC7XXX, 7, "aic7xxx",
-    S_IFDIR | S_IRUGO | S_IXUGO, 2
+    S_IFDIR | S_IRUGO | S_IXUGO, 2,
+    0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
 };
 
-#define AIC7XXX_C_VERSION  "$Revision: 4.0 $"
+#define AIC7XXX_C_VERSION  "$Revision: 4.1 $"
 
 #define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
-#define MIN(a,b)        ((a < b) ? a : b)
+#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
+#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
 #define ALL_TARGETS -1
+#define ALL_CHANNELS '\0'
+#define ALL_LUNS -1
 #ifndef TRUE
 #  define TRUE 1
 #endif
@@ -107,11 +165,8 @@
  *     support because all PCI dependent code is bracketed with
  *     "#ifdef CONFIG_PCI ... #endif CONFIG_PCI".
  *
- *   o Twin bus support - this has been tested and does work.
- *
- *   o DMAing of SCBs - thanks to Kai Makisara, this now works.
- *     This define is now taken out and DMAing of SCBs is always
- *     performed (8/12/95 - DE).
+ *   o Twin bus support - this has been tested and does work.  It is
+ *     not an option anymore.
  *
  *   o Tagged queueing - this driver is capable of tagged queueing
  *     but I am unsure as to how well the higher level driver implements
@@ -140,16 +195,16 @@
  *     LUN using its own heuristic based on the number of available
  *     SCBs.
  *
- *   o 3985 support - The 3985 adapter is much like the 3940, but
- *     has three 7870 controllers as opposed to two for the 3940.
- *     It will get probed and recognized as three different adapters,
- *     but all three controllers can share the same external bank of
- *     255 SCBs.  If you enable AIC7XXX_SHARE_SCBS, then the driver
- *     will attempt to share the common bank of SCBs between the three
- *     controllers of the 3985.  This is experimental and hasn't
- *     been tested.  By default, we do not share the bank of SCBs,
- *     and force the controllers to use their own internal bank of
- *     16 SCBs.  Please let us know if sharing the SCB array works.
+ *   o 3985 support - The 3985 adapter is much like the 3940, but has
+ *     three 7870 controllers as opposed to two for the 3940.  It will
+ *     be probed and recognized as three different adapters, but all
+ *     three controllers can share the same external bank of 255 SCBs.
+ *     If you enable AIC7XXX_USE_EXT_SCBRAM, then the driver will attempt
+ *     to use and share the common bank of SCBs between the three
+ *     controllers of the 3985.  This is experimental and hasn't been
+ *     been tested.  By default, we do not use external SCB RAM, and
+ *     force the controllers to use their own internal bank of 16 SCBs.
+ *     Please let us know if using the external SCB array works.
  *
  *   o SCB paging support - SCB paging is enabled by defining
  *     AIC7XXX_PAGE_ENABLE.  Support for this was taken from the
@@ -162,43 +217,54 @@
  *  Note that sharing of IRQs is not an option any longer.  Linux supports
  *  it so we support it.
  *
- *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 06/30/96
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 01/26/96
  */
 
-/* Uncomment this for testing twin bus support. */
-#define AIC7XXX_TWIN_SUPPORT
-
 /* Uncomment this for tagged queueing. */
-/* #define AIC7XXX_TAGGED_QUEUEING */
+#ifdef CONFIG_AIC7XXX_TAGGED_QUEUEING
+#define AIC7XXX_TAGGED_QUEUEING
+#endif
 
 /*
  * You can try raising me if tagged queueing is enabled, or lowering
  * me if you only have 4 SCBs.
  */
-/* #define AIC7XXX_CMDS_PER_LUN 8 */
+#ifdef CONFIG_AIC7XXX_CMDS_PER_LUN
+#define AIC7XXX_CMDS_PER_LUN CONFIG_AIC7XXX_CMDS_PER_LUN
+#endif
 
 /* Set this to the delay in seconds after SCSI bus reset. */
+#ifdef CONFIG_AIC7XXX_RESET_DELAY
+#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY
+#else
 #define AIC7XXX_RESET_DELAY 15
+#endif
 
 /*
- * Uncomment the following define for collection of SCSI transfer statistics
- * for the /proc filesystem.
+ * Control collection of SCSI transfer statistics for the /proc filesystem.
  *
  * NOTE: Do NOT enable this when running on kernels version 1.2.x and below.
  * NOTE: This does affect performance since it has to maintain statistics.
  */
-/* #define AIC7XXX_PROC_STATS */
+#ifdef CONFIG_AIC7XXX_PROC_STATS
+#define AIC7XXX_PROC_STATS
+#endif
 
 /*
- * Uncomment the following to enable SCB paging.
+ * Enable SCB paging.
  */
-/* #define AIC7XXX_PAGE_ENABLE */
+#ifdef CONFIG_AIC7XXX_PAGE_ENABLE
+#define AIC7XXX_PAGE_ENABLE
+#endif
 
 /*
- * Uncomment the following to enable sharing of the external bank
- * of 255 SCBs for the 3985.
+ * Uncomment the following to enable use of the external bank
+ * of 255 SCBs.  For 3985 adapters, this will also enable sharing
+ * of the SCB array across all three controllers.
  */
-#define AIC7XXX_SHARE_SCBS
+#ifdef CONFIG_AIC7XXX_USE_EXT_SCBRAM
+#define AIC7XXX_USE_EXT_SCBRAM
+#endif
 
 /*
  * For debugging the abort/reset code.
@@ -211,6 +277,87 @@
 #define AIC7XXX_DEBUG
 
 /*
+ * Set this for defining the number of tagged commands on a device
+ * by device, and controller by controller basis.  The first set
+ * of tagged commands will be used for the first detected aic7xxx
+ * controller, the second set will be used for the second detected
+ * aic7xxx controller, and so on.  These values will *only* be used
+ * for targets that are tagged queueing capable; these values will
+ * be ignored in all other cases.  The tag_commands is an array of
+ * 16 to allow for wide and twin adapters.  Twin adapters will use
+ * indexes 0-7 for channel 0, and indexes 8-15 for channel 1.
+ *
+ * *** Determining commands per LUN ***
+ * 
+ * When AIC7XXX_CMDS_PER_LUN is not defined, the driver will use its
+ * own algorithm to determine the commands/LUN.  If SCB paging is
+ * enabled, the commands/LUN is 8.  When SCB paging is not enabled,
+ * then commands/LUN is 8 for adapters with 16 or more hardware SCBs
+ * and 4 commands/LUN for adapters with 3 or 4 SCBs.
+ *
+ */
+/* #define AIC7XXX_TAGGED_QUEUEING_BY_DEVICE */
+
+#ifdef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
+typedef struct
+{
+  char tag_commands[16];   /* Allow for wide/twin channel adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Make a define that will tell the driver to use it's own algorithm
+ * for determining commands/LUN (see Determining commands per LUN
+ * above).
+ */
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+/*
+ * Modify this as you see fit for your system.  By setting tag_commands
+ * to 0, the driver will use it's own algorithm for determining the
+ * number of commands to use (see above).  When -1, the driver will
+ * not enable tagged queueing for that particular device.  When positive
+ * (> 0) the values in the array are used for the queue_depth.  Note
+ * that the maximum value for an entry is 127.
+ *
+ * In this example, the first line will enable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter and tells the driver
+ * to use it's own algorithm for determining commands/LUN.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (1, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to use its own algorithm for ID 1.
+ *
+ * The third line is the same as the first line.
+ *
+ * The fourth line disables tagged queueing for devices 0 and 3.  It
+ * enables tagged queueing for the other IDs, with 16 commands/LUN
+ * for IDs 1 and 4, 127 commands/LUN for ID 8, and 4 commands/LUN for
+ * IDs 2, 5-7, and 9-15.
+ */
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+  {DEFAULT_TAG_COMMANDS},
+  {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, -1, 4, 4, 4}},
+  {DEFAULT_TAG_COMMANDS},
+  {{-1, 16, 4, -1, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+#endif
+
+/*
+ * Don't define this unless you have problems with the driver
+ * interrupt handler.  The old method would register the drivers
+ * interrupt handler as a "fast" type interrupt handler that would
+ * lock out other interrupts.  Since this driver can spend a lot
+ * of time in the interrupt handler, this is _not_ a good idea.
+ * It also conflicts with some of the more common ethernet drivers
+ * that don't use fast interrupts.  Currently, Linux does not allow
+ * IRQ sharing unless both drivers can agree on the type of interrupt
+ * handler.
+ */
+/* #define AIC7XXX_OLD_ISR_TYPE */
+
+
+/*
  * Controller type and options
  */
 typedef enum {
@@ -232,14 +379,15 @@
   AIC_7882,	/* PCI  aic7882 on 3940 Ultra */
   AIC_7883,	/* PCI  aic7883 on 3985 Ultra */
   AIC_7884	/* PCI  aic7884 on 294x Ultra Differential */
-} aha_type;
+} aha_chip_type;
 
 typedef enum {
   AIC_777x,	/* AIC-7770 based */
-  AIC_785x,	/* AIC-7850 based */
+  AIC_785x,	/* AIC-7850 based (3 SCBs)*/
+  AIC_786x,	/* AIC-7860 based (7850 ultra) */
   AIC_787x,	/* AIC-7870 based */
-  AIC_788x	/* AIC-7880 based */
-} aha_chip_type;
+  AIC_788x	/* AIC-7880 based (ultra) */
+} aha_chip_class_type;
 
 typedef enum {
   AIC_SINGLE,  /* Single Channel */
@@ -269,24 +417,24 @@
  * Don't forget to change this when changing the types!
  */
 static const char *board_names[] = {
-  "<AIC-7xxx Unknown>",		/* AIC_NONE */
-  "AIC-7770",			/* AIC_7770 */
-  "AHA-2740",			/* AIC_7771 */
-  "AHA-2840",			/* AIC_284x */
-  "AIC-7850",			/* AIC_7850 */
-  "AIC-7855",			/* AIC_7855 */
-  "AIC-7850 Ultra",		/* AIC_7860 */
-  "AHA-2940A Ultra",		/* AIC_7861 */
-  "AIC-7870",			/* AIC_7870 */
-  "AHA-2940",			/* AIC_7871 */
-  "AHA-3940",			/* AIC_7872 */
-  "AHA-3985",			/* AIC_7873 */
-  "AHA-2940 Differential",	/* AIC_7874 */
-  "AIC-7880 Ultra",		/* AIC_7880 */
-  "AHA-2940 Ultra",		/* AIC_7881 */
-  "AHA-3940 Ultra",		/* AIC_7882 */
-  "AHA-3985 Ultra",		/* AIC_7883 */
-  "AHA-2940 Ultra Differential"	/* AIC_7884 */
+  "AIC-7xxx Unknown",		                        /* AIC_NONE */
+  "Adaptec AIC-7770 SCSI host adapter",			/* AIC_7770 */
+  "Adaptec AHA-274X SCSI host adapter",			/* AIC_7771 */
+  "Adaptec AHA-284X SCSI host adapter",			/* AIC_284x */
+  "Adaptec AIC-7850 SCSI host adapter",			/* AIC_7850 */
+  "Adaptec AIC-7855 SCSI host adapter",			/* AIC_7855 */
+  "Adaptec AIC-7860 Ultra SCSI host adapter",		/* AIC_7860 */
+  "Adaptec AHA-2940A Ultra SCSI host adapter",		/* AIC_7861 */
+  "Adaptec AIC-7870 SCSI host adapter",			/* AIC_7870 */
+  "Adaptec AHA-294X SCSI host adapter",			/* AIC_7871 */
+  "Adaptec AHA-394X SCSI host adapter",			/* AIC_7872 */
+  "Adaptec AHA-398X SCSI host adapter",			/* AIC_7873 */
+  "Adaptec AHA-2944 SCSI host adapter",	                /* AIC_7874 */
+  "Adaptec AIC-7880 Ultra SCSI host adapter",		/* AIC_7880 */
+  "Adaptec AHA-294X Ultra SCSI host adapter",		/* AIC_7881 */
+  "Adaptec AHA-394X Ultra SCSI host adapter",		/* AIC_7882 */
+  "Adaptec AHA-398X Ultra SCSI host adapter",		/* AIC_7883 */
+  "Adaptec AHA-2944 Ultra SCSI host adapter"	        /* AIC_7884 */
 };
 
 /*
@@ -310,12 +458,17 @@
  */
 #define DID_RETRY_COMMAND DID_ERROR
 
+#define HSCSIID        0x07
+#define HWSCSIID       0x0F
+#define SCSI_RESET     0x040
+
 /*
  * EISA/VL-bus stuff
  */
 #define MINSLOT		1
 #define MAXSLOT		15
 #define SLOTBASE(x)	((x) << 12)
+#define BASE_TO_SLOT(x) ((x) >> 12)
 
 /*
  * Standard EISA Host ID regs  (Offset from slot base)
@@ -334,14 +487,6 @@
 #define INTDEF		0x5C		/* Interrupt Definition Register */
 
 /*
- * Some defines for the HCNTRL register.
- */
-#define	REQ_PAUSE	IRQMS | INTEN | PAUSE
-#define	UNPAUSE_274X	IRQMS | INTEN
-#define	UNPAUSE_284X	INTEN
-#define	UNPAUSE_294X	IRQMS | INTEN
-
-/*
  * AIC-78X0 PCI registers
  */
 #define	CLASS_PROGIF_REVID	0x08
@@ -377,7 +522,7 @@
  * each word, while the C56 and C66 (4096 bits) use 8 bits to
  * address each word.
  */
-typedef enum {c46 = 6, c56_66 = 8} seeprom_chip_type;
+typedef enum {C46 = 6, C56_66 = 8} seeprom_chip_type;
 
 /*
  *
@@ -417,15 +562,15 @@
 /*
  * Host Adapter Control Bits
  */
-/* UNUSED               0x0001 */
+#define CFAUTOTERM      0x0001          /* Perform Auto termination */
 #define CFULTRAEN       0x0002          /* Ultra SCSI speed enable (Ultra cards) */
 #define CF284XSELTO     0x0003          /* Selection timeout (284x cards) */
 #define CF284XFIFO      0x000C          /* FIFO Threshold (284x cards) */
-#define CFSTERM         0x0004          /* SCSI low byte termination (non-wide cards) */
+#define CFSTERM         0x0004          /* SCSI low byte termination */
 #define CFWSTERM        0x0008          /* SCSI high byte termination (wide card) */
 #define CFSPARITY	0x0010		/* SCSI parity */
 #define CF284XSTERM	0x0020		/* SCSI low byte termination (284x cards) */
-#define CFRESETB	0x0040		/* reset SCSI bus at IC initialization */
+#define CFRESETB	0x0040		/* reset SCSI bus at boot */
 /* UNUSED		0xFF80 */
   unsigned short adapter_control;	/* word 17 */
 
@@ -448,35 +593,17 @@
   unsigned short checksum;		/* word 31 */
 };
 
-
-#define SCSI_RESET 0x040
-
-/*
- * Pause the sequencer and wait for it to actually stop - this
- * is important since the sequencer can disable pausing for critical
- * sections.
- */
-#define PAUSE_SEQUENCER(p) \
-  outb(p->pause, HCNTRL + p->base);			\
-  while ((inb(HCNTRL + p->base) & PAUSE) == 0)		\
-    ;							\
-
-/*
- * Unpause the sequencer. Unremarkable, yet done often enough to
- * warrant an easy way to do it.
- */
-#define UNPAUSE_SEQUENCER(p) \
-  outb(p->unpause, HCNTRL + p->base)
-
-/*
- * Restart the sequencer program from address zero
- */
-#define RESTART_SEQUENCER(p) \
-  do {							\
-    outb(SEQRESET | FASTMODE, SEQCTL + p->base);	\
-  } while (inb(SEQADDR0 + p->base) != 0 &&		\
-	   inb(SEQADDR1 + p->base) != 0);		\
-  UNPAUSE_SEQUENCER(p);
+#define SELBUS_MASK		0x0a
+#define 	SELNARROW	0x00
+#define 	SELBUSB		0x08
+#define SINGLE_BUS		0x00
+
+#define SCB_TARGET(scb)         \
+       (((scb)->hscb->target_channel_lun & TID) >> 4)
+#define SCB_LUN(scb)            \
+       ((scb)->hscb->target_channel_lun & LID)
+#define SCB_IS_SCSIBUS_B(scb)   \
+       (((scb)->hscb->target_channel_lun & SELBUSB) != 0)
 
 /*
  * If an error occurs during a data transfer phase, run the command
@@ -540,12 +667,6 @@
 static int aic7xxx_spurious_count;
 
 /*
- * The driver keeps up to four scb structures per card in memory. Only the
- * first 25 bytes of the structure are valid for the hardware, the rest used
- * for driver level bookkeeping.
- */
-
-/*
  * As of Linux 2.1, the mid-level SCSI code uses virtual addresses
  * in the scatter-gather lists.  We need to convert the virtual
  * addresses to physical addresses.
@@ -558,20 +679,28 @@
 /*
  * Maximum number of SG segments these cards can support.
  */
-#define	MAX_SG 256
+#define	AIC7XXX_MAX_SG 27
 
-struct aic7xxx_scb {
+/*
+ * The maximum number of SCBs we could have for ANY type
+ * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
+ * SEQUENCER CODE IF THIS IS MODIFIED!
+ */
+#define AIC7XXX_MAXSCB	255
+
+
+struct aic7xxx_hwscb {
 /* ------------    Begin hardware supported fields    ---------------- */
 /* 0*/  unsigned char control;
 /* 1*/  unsigned char target_channel_lun;       /* 4/1/3 bits */
 /* 2*/  unsigned char target_status;
 /* 3*/  unsigned char SG_segment_count;
-/* 4*/  unsigned char SG_list_pointer[4] __attribute__ ((packed));
+/* 4*/  unsigned int  SG_list_pointer;
 /* 8*/  unsigned char residual_SG_segment_count;
-/* 9*/  unsigned char residual_data_count[3] __attribute__ ((packed));
-/*12*/  unsigned char data_pointer[4] __attribute__ ((packed));
-/*16*/  unsigned int  data_count __attribute__ ((packed)); /* must be 32 bits */
-/*20*/  unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed));
+/* 9*/  unsigned char residual_data_count[3];
+/*12*/  unsigned int  data_pointer;
+/*16*/  unsigned int  data_count;
+/*20*/  unsigned int  SCSI_cmd_pointer;
 /*24*/  unsigned char SCSI_cmd_length;
 /*25*/	u_char tag;			/* Index into our kernel SCB array.
 					 * Also used as the tag for tagged I/O
@@ -579,29 +708,47 @@
 #define SCB_PIO_TRANSFER_SIZE	26 	/* amount we need to upload/download
 					 * via PIO to initialize a transaction.
 					 */
-/*26*/  u_char next;                    /* Used to thread SCBs awaiting selection
+/*26*/  unsigned char next;             /* Used to thread SCBs awaiting selection
                                          * or disconnected down in the sequencer.
                                          */
-	/*-----------------end of hardware supported fields----------------*/
-	Scsi_Cmnd          *cmd;	/* Scsi_Cmnd for this scb */
-        struct aic7xxx_scb *q_next;     /* next scb in queue */
-#define SCB_FREE               0x00
-#define SCB_ACTIVE             0x01
-#define SCB_ABORTED            0x02
-#define SCB_DEVICE_RESET       0x04
-#define SCB_IMMED              0x08
-#define SCB_SENSE              0x10
-#define SCB_QUEUED_FOR_DONE    0x40
-#define SCB_PAGED_OUT          0x80
-#define SCB_WAITINGQ           0x100
-#define SCB_ASSIGNEDQ          0x200
-#define SCB_SENTORDEREDTAG     0x400
-#define SCB_IN_PROGRESS        (SCB_ACTIVE | SCB_PAGED_OUT | \
-                                SCB_WAITINGQ | SCB_ASSIGNEDQ)
-	int                 state;          /* current state of scb */
-	unsigned int        position;       /* Position in scb array */
-	struct hw_scatterlist  sg_list[MAX_SG]; /* SG list in adapter format */
-	unsigned char       sense_cmd[6];   /* Allocate 6 characters for sense command */
+/*27*/  unsigned char prev;
+/*28*/  unsigned int pad;               /*
+                                         * Unused by the kernel, but we require
+                                         * the padding so that the array of
+                                         * hardware SCBs is alligned on 32 byte
+                                         * boundaries so the sequencer can index
+                                         */
+};
+
+typedef enum {
+	SCB_FREE		= 0x0000,
+	SCB_ACTIVE		= 0x0001,
+	SCB_ABORTED		= 0x0002,
+	SCB_DEVICE_RESET	= 0x0004,
+	SCB_SENSE		= 0x0008,
+	SCB_TIMEDOUT		= 0x0010,
+	SCB_QUEUED_FOR_DONE	= 0x0020,
+	SCB_RECOVERY_SCB	= 0x0040,
+	SCB_WAITINGQ		= 0x0080,
+	SCB_ASSIGNEDQ		= 0x0100,
+	SCB_SENTORDEREDTAG	= 0x0200,
+	SCB_MSGOUT_SDTR		= 0x0400,
+	SCB_MSGOUT_WDTR		= 0x0800,
+	SCB_ABORT		= 0x1000,
+	SCB_QUEUED_ABORT	= 0x2000
+} scb_flag_type;
+
+struct aic7xxx_scb {
+        struct aic7xxx_hwscb  *hscb;          /* corresponding hardware scb */
+	Scsi_Cmnd             *cmd;	      /* Scsi_Cmnd for this scb */
+        struct aic7xxx_scb    *q_next;        /* next scb in queue */
+	scb_flag_type          flags;         /* current state of scb */
+	struct hw_scatterlist *sg_list;       /* SG list in adapter format */
+        unsigned char          sg_count;
+	unsigned char          sense_cmd[6];  /*
+                                               * Allocate 6 characters for
+                                               * sense command.
+                                               */
 };
 
 /*
@@ -626,20 +773,17 @@
 generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
 
 typedef struct {
+  struct aic7xxx_hwscb *hscbs;
   scb_queue_type free_scbs;        /*
                                     * SCBs assigned to free slot on
                                     * card (no paging required)
                                     */
-  int            numscbs;          /* current number of scbs */
-  int            activescbs;       /* active scbs */
-} scb_usage_type;
-
-/*
- * The maximum number of SCBs we could have for ANY type
- * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
- * SEQUENCER CODE IF THIS IS MODIFIED!
- */
-#define AIC7XXX_MAXSCB	255
+  unsigned char  numscbs;          /* current number of scbs */
+  unsigned char  maxhscbs;         /* hardware scbs */
+  unsigned char  maxscbs;          /* max scbs including pageable scbs */
+  struct aic7xxx_scb   *scb_array[AIC7XXX_MAXSCB];
+  unsigned int   reserve[100];
+} scb_data_type;
 
 /*
  * Define a structure used for each host adapter, only one per IRQ.
@@ -647,17 +791,26 @@
 struct aic7xxx_host {
   struct Scsi_Host        *host;             /* pointer to scsi host */
   int                      host_no;          /* SCSI host number */
+  int                      instance;         /* aic7xxx instance number */
+  int                      scsi_id;          /* host adapter SCSI ID */
+  int                      scsi_id_b;        /*   channel B for twin adapters */
+  int                      irq;              /* IRQ for this adapter */
   int                      base;             /* card base address */
-  int                      maxhscbs;         /* hardware SCBs */
-  int                      maxscbs;          /* max SCBs (including pageable) */
-#define A_SCANNED              0x0001
-#define B_SCANNED              0x0002
-#define EXTENDED_TRANSLATION   0x0004
-#define HAVE_SEEPROM           0x0008
-#define ULTRA_ENABLED          0x0010
-#define PAGE_ENABLED           0x0020
-#define IN_ISR                 0x0040
-#define USE_DEFAULTS           0x0080
+  unsigned int             mbase;            /* I/O memory address */
+  volatile unsigned char  *maddr;            /* memory mapped address */
+#define A_SCANNED               0x0001
+#define B_SCANNED               0x0002
+#define EXTENDED_TRANSLATION    0x0004
+#define FLAGS_CHANNEL_B_PRIMARY 0x0008
+#define MULTI_CHANNEL           0x0010
+#define ULTRA_ENABLED           0x0020
+#define PAGE_ENABLED            0x0040
+#define USE_DEFAULTS            0x0080
+#define BIOS_ENABLED            0x0100
+#define IN_ISR                  0x0200
+#define IN_TIMEOUT              0x0400
+#define SHARED_SCBDATA          0x0800
+#define HAVE_SEEPROM            0x1000
   unsigned int             flags;
   unsigned int             isr_count;        /* Interrupt count */
   unsigned short           needsdtr_copy;    /* default config */
@@ -668,36 +821,22 @@
   unsigned short           wdtr_pending;
   unsigned short           orderedtag;
   unsigned short           discenable;	     /* Targets allowed to disconnect */
-  aha_type                 type;             /* card type */
-  aha_chip_type            chip_type;        /* chip base type */
+  aha_chip_type            chip_type;        /* card type */
+  aha_chip_class_type      chip_class;
   aha_bus_type             bus_type;         /* normal/twin/wide bus */
-  char *                   mbase;            /* I/O memory address */
-  unsigned char            chan_num;         /* for 3940/3985, channel number */
+  unsigned char            chan_num;         /* for 39xx, channel number */
   unsigned char            unpause;          /* unpause value for HCNTRL */
   unsigned char            pause;            /* pause value for HCNTRL */
   unsigned char            qcntmask;
-  struct seeprom_config    seeprom;
+  unsigned char            qfullcount;
+  unsigned char            curqincnt;
   struct Scsi_Host        *next;             /* allow for multiple IRQs */
-  struct aic7xxx_scb      *scb_array[AIC7XXX_MAXSCB];  /* active commands */
-  struct aic7xxx_scb      *pagedout_ntscbs[16];  /*
-                                                  * paged-out, non-tagged scbs
-                                                  * indexed by target.
-                                                  */
-  scb_queue_type           page_scbs;        /*
-                                              * SCBs that will require paging
-                                              * before use (no assigned slot)
-                                              */
+  unsigned char            activescbs;       /* active scbs */
   scb_queue_type           waiting_scbs;     /*
-                                              * SCBs waiting to be paged and
-                                              * started.
-                                              */
-  scb_queue_type           assigned_scbs;    /*
-                                              * SCBs that were waiting but have
-                                              * have now been assigned a slot
-                                              * by aic7xxx_free_scb
+                                              * SCBs waiting for space in
+                                              * the QINFIFO.
                                               */
-  scb_usage_type           scb_usage;
-  scb_usage_type          *scb_link;
+  scb_data_type           *scb_data;
 
   struct aic7xxx_cmd_queue {
     Scsi_Cmnd *head;
@@ -709,6 +848,7 @@
 #define  BUS_DEVICE_RESET_PENDING       0x02
     int  flags;
     int  commands_sent;
+    int  active_cmds;
   } device_status[16];
 #ifdef AIC7XXX_PROC_STATS
   /*
@@ -734,34 +874,10 @@
 #endif /* AIC7XXX_PROC_STATS */
 };
 
-struct aic7xxx_host_config {
-  int              irq;        /* IRQ number */
-  int              mbase;      /* memory base address*/
-  int              base;       /* I/O base address*/
-  int              maxhscbs;   /* hardware SCBs */
-  int              maxscbs;    /* max SCBs (including pageable) */
-  int              unpause;    /* unpause value for HCNTRL */
-  int              pause;      /* pause value for HCNTRL */
-  int              scsi_id;    /* host SCSI ID */
-  int              scsi_id_b;  /* host SCSI ID B channel for twin cards */
-  unsigned int     flags;      /* used the same as struct aic7xxx_host flags */
-  int              chan_num;   /* for 3940/3985, channel number */
-  unsigned char    busrtime;   /* bus release time */
-  unsigned char    bus_speed;  /* bus speed */
-  unsigned char    qcntmask;
-  aha_type         type;       /* card type */
-  aha_chip_type    chip_type;  /* chip base type */
-  aha_bus_type     bus_type;   /* normal/twin/wide bus */
-  aha_status_type  bios;       /* BIOS is enabled/disabled */
-  aha_status_type  parity;     /* bus parity enabled/disabled */
-  aha_status_type  low_term;   /* bus termination low byte */
-  aha_status_type  high_term;  /* bus termination high byte (wide cards only) */
-};
-
 /*
  * Valid SCSIRATE values. (p. 3-17)
- * Provides a mapping of transfer periods in ns to the proper value to
- * stick in the scsiscfr reg to use that transfer rate.
+ * Provides a mapping of transfer periods in ns/4 to the proper value to
+ * stick in the SCSIRATE reg to use that transfer rate.
  */
 static struct {
   short period;
@@ -770,17 +886,17 @@
   short rate;
   const char *english;
 } aic7xxx_syncrates[] = {
-  {  50,  0x100,  "20.0"  },
-  {  62,  0x110,  "16.0"  },
-  {  75,  0x120,  "13.4"  },
-  { 100,  0x000,  "10.0"  },
-  { 125,  0x010,   "8.0"  },
-  { 150,  0x020,   "6.67" },
-  { 175,  0x030,   "5.7"  },
-  { 200,  0x040,   "5.0"  },
-  { 225,  0x050,   "4.4"  },
-  { 250,  0x060,   "4.0"  },
-  { 275,  0x070,   "3.6"  }
+  { 12,  0x100,  "20.0"  },
+  { 15,  0x110,  "16.0"  },
+  { 18,  0x120,  "13.4"  },
+  { 25,  0x000,  "10.0"  },
+  { 31,  0x010,   "8.0"  },
+  { 37,  0x020,   "6.67" },
+  { 43,  0x030,   "5.7"  },
+  { 50,  0x040,   "5.0"  },
+  { 56,  0x050,   "4.4"  },
+  { 62,  0x060,   "4.0"  },
+  { 68,  0x070,   "3.6"  }
 };
 
 static int num_aic7xxx_syncrates =
@@ -789,166 +905,51 @@
 #ifdef CONFIG_PCI
 static int number_of_3940s = 0;
 static int number_of_3985s = 0;
-#ifdef AIC7XXX_SHARE_SCBS
-static scb_usage_type *shared_3985_scbs = NULL;
-#endif
-#endif CONFIG_PCI
+#endif /* CONFIG_PCI */
 
 #ifdef AIC7XXX_DEBUG
 
-static void
-debug_config(struct aic7xxx_host_config *p)
-{
-  int scsi_conf;
-  unsigned char brelease;
-  unsigned char dfthresh;
-
-  static int DFT[] = { 0, 50, 75, 100 };
-  static int SST[] = { 256, 128, 64, 32 };
-  static const char *BUSW[] = { "", "-TWIN", "-WIDE" };
-
-  scsi_conf = inb(SCSICONF + p->base);
-
-  /*
-   * Scale the Data FIFO Threshhold and the Bus Release Time; they are
-   * stored in formats compatible for writing to sequencer registers.
-   */
-  dfthresh = p->bus_speed  >> 6;
-
-  if (p->chip_type == AIC_777x)
-  {
-    brelease = p->busrtime >> 2;
-  }
-  else
-  {
-    brelease = p->busrtime;
-  }
-  if (brelease == 0)
-  {
-    brelease = 2;
-  }
-
-  switch (p->type)
-  {
-    case AIC_7770:
-    case AIC_7771:
-      printk("%s%s AT EISA SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
-             p->base >> 12);
-      break;
-
-    case AIC_284x:
-      printk("%s%s AT VLB SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
-             p->base >> 12);
-      break;
-
-    case AIC_7850:
-    case AIC_7855:
-    case AIC_7860:
-    case AIC_7861:
-    case AIC_7870:
-    case AIC_7871:
-    case AIC_7872:
-    case AIC_7873:
-    case AIC_7874:
-    case AIC_7880:
-    case AIC_7881:
-    case AIC_7882:
-    case AIC_7883:
-    case AIC_7884:
-      printk("%s%s (PCI-bus), I/O 0x%x, Mem 0x%x:\n", board_names[p->type],
-             BUSW[p->bus_type], p->base, p->mbase);
-      break;
-
-    default:
-      panic("aic7xxx: (debug_config) internal error.\n");
-  }
-
-  printk("    irq %d\n"
-	 "    bus release time %d bclks\n"
-	 "    data fifo threshold %d%%\n",
-	 p->irq,
-	 brelease,
-	 DFT[dfthresh]);
-
-  printk("    SCSI CHANNEL A:\n"
-	 "        scsi id %d\n"
-	 "        scsi selection timeout %d ms\n"
-	 "        scsi bus reset at power-on %sabled\n",
-	 (p->bus_type & AIC_WIDE) ? (scsi_conf & 0x0f) : (scsi_conf & 0x07),
-	 SST[(scsi_conf >> 3) & 0x03],
-	 (scsi_conf & 0x40) ? "en" : "dis");
-
-  if ((p->chip_type == AIC_777x) && (p->parity == AIC_UNKNOWN))
-  {
-    /*
-     * Set the parity for 7770 based cards.
-     */
-    p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED;
-  }
-  if (p->parity != AIC_UNKNOWN)
-  {
-    printk("        scsi bus parity %sabled\n",
-	   (p->parity == AIC_ENABLED) ? "en" : "dis");
-  }
-
-  if ((p->type == AIC_7770) || (p->type == AIC_7771))
-  {
-    p->low_term = (scsi_conf & 0x80) ? AIC_ENABLED : AIC_DISABLED;
-  }
-  if (p->low_term != AIC_UNKNOWN)
-  {
-    printk("        scsi bus termination (low byte) %sabled\n",
-	  (p->low_term == AIC_ENABLED) ? "en" : "dis");
-  }
-  if ((p->bus_type == AIC_WIDE) && (p->high_term != AIC_UNKNOWN))
-  {
-    printk("        scsi bus termination (high byte) %sabled\n",
-	  (p->high_term == AIC_ENABLED) ? "en" : "dis");
-  }
-}
-
 #if 0
 static void
 debug_scb(struct aic7xxx_scb *scb)
 {
-  printk("control 0x%x, tcl 0x%x, sg_count %d, sg_ptr 0x%x, cmdp 0x%x, cmdlen %d\n",
-         scb->control, scb->target_channel_lun, scb->SG_segment_count,
-         (scb->SG_list_pointer[3] << 24) | (scb->SG_list_pointer[2] << 16) |
-         (scb->SG_list_pointer[1] << 8) | scb->SG_list_pointer[0],
-         (scb->SCSI_cmd_pointer[3] << 24) | (scb->SCSI_cmd_pointer[2] << 16) |
-         (scb->SCSI_cmd_pointer[1] << 8) | scb->SCSI_cmd_pointer[0],
-         scb->SCSI_cmd_length);
-  printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n",
-         (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status,
-         scb->residual_SG_segment_count,
-         ((scb->residual_data_count[2] << 16) |
-          (scb->residual_data_count[1] <<  8) |
-          (scb->residual_data_count[0]));
-  printk("data ptr 0x%x, data count %d, next waiting %d\n",
-         (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) |
-         (scb->data_pointer[1] << 8) | scb->data_pointer[0],
-         scb->data_count, scb->next_waiting);
-  printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n",
-         (unsigned long) scb->next, (unsigned long) scb->cmd, scb->state,
-         scb->position);
+  struct aic7xxx_hwscb *hscb = scb->hscb;
+
+  printk("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n",
+    scb,
+    hscb->control,
+    hscb->target_channel_lun,
+    hscb->SCSI_cmd_length,
+    hscb->SCSI_cmd_pointer );
+  printk("        datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n",
+    hscb->data_count,
+    hscb->data_pointer,
+    hscb->SG_segment_count,
+    hscb->SG_list_pointer);
+  printk("        sg_addr:%lx sg_len:%ld\n",
+    hscb->sg_list[0].address,
+    hscb->sg_list[0].length);
 }
 #endif
 
 #else
-#  define debug_config(x)
 #  define debug_scb(x)
 #endif AIC7XXX_DEBUG
 
-#define TCL_OF_SCB(x)  (((x)->target_channel_lun >> 4) & 0xf),  \
-                       (((x)->target_channel_lun >> 3) & 0x01), \
-                       ((x)->target_channel_lun & 0x07)
+#define TCL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf),  \
+                        (((scb->hscb)->target_channel_lun >> 3) & 0x01), \
+                        ((scb->hscb)->target_channel_lun & 0x07)
 
-#define TARGET_INDEX(x)  ((x)->target | ((x)->channel << 3))
+#define TC_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 4) & 0xf),  \
+                       (((scb->hscb)->target_channel_lun >> 3) & 0x01)
+
+#define CHAN_TO_INT(chan) ((chan) == 'A' ? 0 : 1)
+
+#define TARGET_INDEX(cmd)  ((cmd)->target | ((cmd)->channel << 3))
 
 /*
  * XXX - these options apply unilaterally to _all_ 274x/284x/294x
- *       cards in the system. This should be fixed, but then,
- *       does anyone really have more than one in a machine?
+ *       cards in the system.  This should be fixed.
  */
 static unsigned int aic7xxx_extended = 0;    /* extended translation on? */
 static unsigned int aic7xxx_no_reset = 0;    /* no resetting of SCSI bus */
@@ -958,6 +959,53 @@
                                               *  1 use level triggered
                                               */
 static int aic7xxx_enable_ultra = 0;         /* enable ultra SCSI speeds */
+static int aic7xxx_verbose = 0;	             /* verbose messages */
+
+
+/****************************************************************************
+ *
+ * These functions are not used yet, but when we do memory mapped
+ * IO, we'll use them then.
+ *
+ ***************************************************************************/
+static inline unsigned char
+aic_inb(struct aic7xxx_host *p, long port)
+{
+  if (p->maddr != NULL)
+    return (p->maddr[port]);
+  else
+    return (inb(p->base + port));
+}
+
+static inline void
+aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
+{
+  if (p->maddr != NULL)
+    p->maddr[port] = val;
+  else
+    outb(val, p->base + port);
+}
+
+static inline void
+aic_outsb(struct aic7xxx_host *p, long port, unsigned char *valp, size_t size)
+{
+  if (p->maddr != NULL)
+  {
+    __asm __volatile("
+      cld;
+    1:  lodsb;
+      movb %%al,(%0);
+      loop 1b"      :
+              :
+      "r" ((p)->maddr + (port)),
+      "S" ((valp)), "c" ((size))  :
+      "%esi", "%ecx", "%eax");
+  }
+  else
+  {
+    outsb(p->base + port, valp, size);
+  }
+}
 
 /*+F*************************************************************************
  * Function:
@@ -982,6 +1030,7 @@
     { "no_reset",    &aic7xxx_no_reset },
     { "irq_trigger", &aic7xxx_irq_trigger },
     { "ultra",       &aic7xxx_enable_ultra },
+    { "verbose",     &aic7xxx_verbose },
     { NULL,          NULL }
   };
 
@@ -1007,162 +1056,343 @@
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_loadseq
+ *   pause_sequencer
  *
  * Description:
- *   Load the sequencer code into the controller memory.
+ *   Pause the sequencer and wait for it to actually stop - this
+ *   is important since the sequencer can disable pausing for critical
+ *   sections.
  *-F*************************************************************************/
-static void
-aic7xxx_loadseq(int base)
+static inline void
+pause_sequencer(struct aic7xxx_host *p)
 {
-  static unsigned char seqprog[] = {
-    /*
-     * Each sequencer instruction is 29 bits
-     * long (fill in the excess with zeroes)
-     * and has to be loaded from least -> most
-     * significant byte, so this table has the
-     * byte ordering reversed.
-     */
-#   include "aic7xxx_seq.h"
-  };
-
-  /*
-   * When the AIC-7770 is paused (as on chip reset), the
-   * sequencer address can be altered and a sequencer
-   * program can be loaded by writing it, byte by byte, to
-   * the sequencer RAM port - the Adaptec documentation
-   * recommends using REP OUTSB to do this, hence the inline
-   * assembly. Since the address autoincrements as we load
-   * the program, reset it back to zero afterward. Disable
-   * sequencer RAM parity error detection while loading, and
-   * make sure the LOADRAM bit is enabled for loading.
-   */
-  outb(PERRORDIS | SEQRESET | LOADRAM, SEQCTL + base);
-
-  outsb(SEQRAM + base, seqprog, sizeof(seqprog));
-
-  /*
-   * WARNING!  This is a magic sequence!  After extensive
-   * experimentation, it seems that you MUST turn off the
-   * LOADRAM bit before you play with SEQADDR again, else
-   * you will end up with parity errors being flagged on
-   * your sequencer program. (You would also think that
-   * turning off LOADRAM and setting SEQRESET to reset the
-   * address to zero would work, but you need to do it twice
-   * for it to take effect on the address. Timing problem?)
-   */
-  do {
-    /*
-     * Actually, reset it until
-     * the address shows up as
-     * zero just to be safe..
-     */
-    outb(SEQRESET | FASTMODE, SEQCTL + base);
-  } while ((inb(SEQADDR0 + base) != 0) && (inb(SEQADDR1 + base) != 0));
+  outb(p->pause, p->base + HCNTRL);
+  while ((inb(p->base + HCNTRL) & PAUSE) == 0)
+  {
+    ;
+  }
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_delay
+ *   unpause_sequencer
  *
  * Description:
- *   Delay for specified amount of time.
+ *   Unpause the sequencer. Unremarkable, yet done often enough to
+ *   warrant an easy way to do it.
  *-F*************************************************************************/
-static void
-aic7xxx_delay(int seconds)
+static inline void
+unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
 {
-  unsigned long i;
-
-  i = jiffies + (seconds * HZ);  /* compute time to stop */
-
-  while (jiffies < i)
+  if (unpause_always ||
+      ((inb(p->base + INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0))
   {
-    ;  /* Do nothing! */
+    outb(p->unpause, p->base + HCNTRL);
   }
 }
 
 /*+F*************************************************************************
  * Function:
- *   rcs_version
+ *   restart_sequencer
  *
  * Description:
- *   Return a string containing just the RCS version number from either
- *   an Id or Revision RCS clause.
+ *   Restart the sequencer program from address zero.  This assumes
+ *   that the sequencer is already paused.
  *-F*************************************************************************/
-const char *
-rcs_version(const char *version_info)
+static inline void
+restart_sequencer(struct aic7xxx_host *p)
 {
-  static char buf[10];
-  char *bp, *ep;
+  /* Set the sequencer address to 0. */
+  outb(0, p->base + SEQADDR0);
+  outb(0, p->base + SEQADDR1);
 
-  bp = NULL;
-  strcpy(buf, "????");
-  if (!strncmp(version_info, "$Id: ", 5))
-  {
-    if ((bp = strchr(version_info, ' ')) != NULL)
-    {
-      bp++;
-      if ((bp = strchr(bp, ' ')) != NULL)
-      {
-	bp++;
-      }
-    }
-  }
-  else
+  /*
+   * Reset and unpause the sequencer.  The reset is suppose to
+   * start the sequencer running, but we do an unpause to make
+   * sure.
+   */
+  outb(SEQRESET | FASTMODE, p->base + SEQCTL);
+
+  unpause_sequencer(p, /*unpause_always*/ TRUE);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_next_patch
+ *
+ * Description:
+ *   Find the next patch to download.
+ *-F*************************************************************************/
+static struct patch *
+aic7xxx_next_patch(struct patch *cur_patch, int options, int instrptr)
+{
+  while (cur_patch != NULL)
   {
-    if (!strncmp(version_info, "$Revision: ", 11))
+    if ((((cur_patch->options & options) != 0) && (cur_patch->negative == FALSE))
+      || (((cur_patch->options & options) == 0) && (cur_patch->negative == TRUE))
+      || (instrptr >= cur_patch->end))
     {
-      if ((bp = strchr(version_info, ' ')) != NULL)
+      /*
+       * Either we want to keep this section of code, or we have consumed
+       * this patch.  Skip to the next patch.
+       */
+      cur_patch++;
+      if (cur_patch->options == 0)
       {
-	bp++;
+        /* Out of patches. */
+        cur_patch = NULL;
       }
     }
-  }
-
-  if (bp != NULL)
-  {
-    if ((ep = strchr(bp, ' ')) != NULL)
+    else
     {
-      register int len = ep - bp;
-
-      strncpy(buf, bp, len);
-      buf[len] = '\0';
+      /* Found an OK patch. */
+      break;
     }
   }
-
-  return buf;
+  return (cur_patch);
 }
 
+
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_info
+ *   aic7xxx_download_instr
  *
  * Description:
- *   Return a string describing the driver.
+ *   Find the next patch to download.
  *-F*************************************************************************/
-const char *
-aic7xxx_info(struct Scsi_Host *notused)
+static void
+aic7xxx_download_instr(struct aic7xxx_host *p, int options, int instrptr)
 {
-  static char buffer[128];
+  unsigned char opcode;
+  struct ins_format3 *instr;
 
-  strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
-  strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
-  strcat(buffer, "/");
-  strcat(buffer, rcs_version(AIC7XXX_H_VERSION));
-  strcat(buffer, "/");
-  strcat(buffer, rcs_version(AIC7XXX_SEQ_VER));
+  instr = (struct ins_format3 *) &seqprog[instrptr * 4];
+  /* Pull the opcode */
+  opcode = instr->opcode_addr >> 1;
+  switch (opcode)
+  {
+    case AIC_OP_JMP:
+    case AIC_OP_JC:
+    case AIC_OP_JNC:
+    case AIC_OP_CALL:
+    case AIC_OP_JNE:
+    case AIC_OP_JNZ:
+    case AIC_OP_JE:
+    case AIC_OP_JZ:
+    {
+      int address_offset;
+      struct ins_format3 new_instr;
+      unsigned int address;
+      struct patch *patch;
+      int i;
+
+      address_offset = 0;
+      new_instr = *instr;  /* Strucure copy */
+      address = new_instr.address;
+      address |= (new_instr.opcode_addr & ADDR_HIGH_BIT) << 8;
+      for (i = 0; i < NUMBER(patches); i++)
+      {
+        patch = &patches[i];
+        if ((((patch->options & options) == 0) && (patch->negative == FALSE)) ||
+            (((patch->options & options) != 0) && (patch->negative == TRUE)))
+        {
+          if (address >= patch->end)
+          {
+            address_offset += patch->end - patch->begin;
+          }
+        }
+      }
+      address -= address_offset;
+      new_instr.address = address &0xFF;
+      new_instr.opcode_addr &= ~ADDR_HIGH_BIT;
+      new_instr.opcode_addr |= (address >> 8) & ADDR_HIGH_BIT;
+      outsb(p->base + SEQRAM, &new_instr.immediate, 4);
+      break;
+    }
 
-  return buffer;
-}
+    case AIC_OP_OR:
+    case AIC_OP_AND:
+    case AIC_OP_XOR:
+    case AIC_OP_ADD:
+    case AIC_OP_ADC:
+    case AIC_OP_ROL:
+      outsb(p->base + SEQRAM, &instr->immediate, 4);
+      break;
+
+    default:
+      panic("aic7xxx: Unknown opcode encountered in sequencer program.");
+      break;
+  }
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_loadseq
+ *
+ * Description:
+ *   Load the sequencer code into the controller memory.
+ *-F*************************************************************************/
+static void
+aic7xxx_loadseq(struct aic7xxx_host *p)
+{
+  int options;
+  struct patch *cur_patch;
+  int i;
+  int downloaded;
+
+  if (aic7xxx_verbose)
+  {
+    printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
+  }
+  options = 1;  /* Code for all options. */
+  downloaded = 0;
+  if ((p->flags & ULTRA_ENABLED) != 0)
+    options |= ULTRA;
+  if (p->bus_type == AIC_TWIN)
+    options |= TWIN_CHANNEL;
+  if (p->scb_data->maxscbs > p->scb_data->maxhscbs)
+    options |= SCB_PAGING;
+
+  cur_patch = patches;
+  outb(PERRORDIS | LOADRAM, p->base + SEQCTL);
+  outb(0, p->base + SEQADDR0);
+  outb(0, p->base + SEQADDR1);
+
+  for (i = 0; i < sizeof(seqprog) / 4;  i++)
+  {
+    cur_patch = aic7xxx_next_patch(cur_patch, options, i);
+    if (cur_patch && (cur_patch->begin <= i) && (cur_patch->end > i))
+    {
+      /* Skip this instruction for this configuration. */
+      continue;
+    }
+    aic7xxx_download_instr(p, options, i);
+    downloaded++;
+  }
+
+  outb(FASTMODE, p->base + SEQCTL);
+  outb(0, p->base + SEQADDR0);
+  outb(0, p->base + SEQADDR1);
+
+  if (aic7xxx_verbose)
+  {
+     printk(" %d instructions downloaded\n", downloaded);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_delay
+ *
+ * Description:
+ *   Delay for specified amount of time.  We use udelay because the timer
+ *   interrupt is not guaranteed to be enabled.  This will cause an
+ *   infinite loop since jiffies (clock ticks) is not updated.
+ *-F*************************************************************************/
+static void
+aic7xxx_delay(int seconds)
+{
+  int i;
+
+  /*                        
+   * Call udelay() for 1 millisecond inside a loop for  
+   * the requested amount of seconds.
+   */
+  for (i=0; i < seconds*1000; i++)
+  {
+    udelay(1000);  /* Delay for 1 millisecond. */
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   rcs_version
+ *
+ * Description:
+ *   Return a string containing just the RCS version number from either
+ *   an Id or Revision RCS clause.
+ *-F*************************************************************************/
+const char *
+rcs_version(const char *version_info)
+{
+  static char buf[10];
+  char *bp, *ep;
+
+  bp = NULL;
+  strcpy(buf, "????");
+  if (!strncmp(version_info, "$Id: ", 5))
+  {
+    if ((bp = strchr(version_info, ' ')) != NULL)
+    {
+      bp++;
+      if ((bp = strchr(bp, ' ')) != NULL)
+      {
+	bp++;
+      }
+    }
+  }
+  else
+  {
+    if (!strncmp(version_info, "$Revision: ", 11))
+    {
+      if ((bp = strchr(version_info, ' ')) != NULL)
+      {
+	bp++;
+      }
+    }
+  }
+
+  if (bp != NULL)
+  {
+    if ((ep = strchr(bp, ' ')) != NULL)
+    {
+      register int len = ep - bp;
+
+      strncpy(buf, bp, len);
+      buf[len] = '\0';
+    }
+  }
+
+  return buf;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_info
+ *
+ * Description:
+ *   Return a string describing the driver.
+ *-F*************************************************************************/
+const char *
+aic7xxx_info(struct Scsi_Host *notused)
+{
+  static char buffer[128];
+
+  strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
+  strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
+  strcat(buffer, "/");
+  strcat(buffer, rcs_version(AIC7XXX_H_VERSION));
+#if 0
+  strcat(buffer, "/");
+  strcat(buffer, rcs_version(AIC7XXX_SEQ_VER));
+#endif
+
+  return buffer;
+}
 
 /*+F*************************************************************************
  * Function:
  *   aic7xxx_length
  *
  * Description:
- *   How much data should be transferred for this SCSI command? Stop
- *   at segment sg_last if it's a scatter-gather command so we can
- *   compute underflow easily.
+ *   How much data should be transferred for this SCSI command?  Assume
+ *   all segments are to be transferred except for the last sg_last
+ *   segments.  This will allow us to compute underflow easily.  To
+ *   calculate the total length of the command, use sg_last = 0.  To
+ *   calculate the length of all but the last 2 SG segments, use
+ *   sg_last = 2.
  *-F*************************************************************************/
 static unsigned
 aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
@@ -1176,7 +1406,7 @@
 
   if (cmd->use_sg)
   {
-    for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++)
+    for (i = length = 0; i < segments; i++)
     {
       length += sg[i].length;
     }
@@ -1198,9 +1428,9 @@
  *-F*************************************************************************/
 static void
 aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
-    short period, unsigned char offset, int target, char channel)
+    unsigned char *period, unsigned char *offset, int target, char channel)
 {
-  int i;
+  int i = num_aic7xxx_syncrates;
   unsigned long ultra_enb_addr;
   unsigned char ultra_enb, sxfrctl0;
 
@@ -1208,11 +1438,11 @@
    * If the offset is 0, then the device is requesting asynchronous
    * transfers.
    */
-  if (offset != 0)
+  if ((*period >= aic7xxx_syncrates[i].period) && *offset != 0)
   {
     for (i = 0; i < num_aic7xxx_syncrates; i++)
     {
-      if ((aic7xxx_syncrates[i].period - period) >= 0)
+      if (*period <= aic7xxx_syncrates[i].period)
       {
         /*
          * Watch out for Ultra speeds when ultra is not enabled and
@@ -1228,99 +1458,57 @@
            */
           continue;
         }
-        *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F);
+        *scsirate = (aic7xxx_syncrates[i].rate & 0xF0) | (*offset & 0x0F);
+        *period = aic7xxx_syncrates[i].period;
 
-        /*
-         * Ensure Ultra mode is set properly for this target.
-         */
-        ultra_enb_addr = ULTRA_ENB;
-        if ((channel == 'B') || (target > 7))
+        if (aic7xxx_verbose)
         {
-          ultra_enb_addr++;
+          printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
+                 "offset %d.\n", p->host_no, target, channel,
+                 aic7xxx_syncrates[i].english, *offset);
         }
-        ultra_enb = inb(p->base + ultra_enb_addr);
-        sxfrctl0 = inb(p->base + SXFRCTL0);
-        if (aic7xxx_syncrates[i].rate & ULTRA_SXFR)
-        {
-          ultra_enb |= 0x01 << (target & 0x07);
-          sxfrctl0 |= ULTRAEN;
-        }
-        else
-        {
-          ultra_enb &= ~(0x01 << (target & 0x07));
-          sxfrctl0 &= ~ULTRAEN;
-        }
-        outb(ultra_enb, p->base + ultra_enb_addr);
-        outb(sxfrctl0, p->base + SXFRCTL0);
-
-        printk("scsi%d: Target %d, channel %c, now synchronous at %sMHz, "
-               "offset %d.\n", p->host_no, target, channel,
-               aic7xxx_syncrates[i].english, offset);
-        return;
+        break;
       }
     }
   }
 
-  /*
-   * Default to asynchronous transfer
-   */
-  *scsirate = 0;
-  printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
-         p->host_no, target, channel);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_putscb
- *
- * Description:
- *   Transfer a SCB to the controller.
- *-F*************************************************************************/
-static inline void
-aic7xxx_putscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int base = p->base;
-
-  outb(SCBAUTO, SCBCNT + base);
+  if (i >= num_aic7xxx_syncrates)
+  {
+    /*
+     * Use asynchronous transfers.
+     */
+    *scsirate = 0;
+    *period = 0;
+    *offset = 0;
+    if (aic7xxx_verbose)
+    {
+      printk("scsi%d: Target %d, channel %c, using asynchronous transfers.\n",
+             p->host_no, target, channel);
+    }
+  }
 
   /*
-   * By turning on the SCB auto increment, any reference
-   * to the SCB I/O space postincrements the SCB address
-   * we're looking at. So turn this on and dump the relevant
-   * portion of the SCB to the card.
-   *
-   * We can do 16bit transfers on all but 284x.
+   * Ensure Ultra mode is set properly for this target.
    */
-  if (p->type == AIC_284x)
+  ultra_enb_addr = ULTRA_ENB;
+  if ((channel == 'B') || (target > 7))
+  {
+    ultra_enb_addr++;
+  }
+  ultra_enb = inb(p->base + ultra_enb_addr);
+  sxfrctl0 = inb(p->base + SXFRCTL0);
+  if ((*scsirate != 0) && (aic7xxx_syncrates[i].rate & ULTRA_SXFR))
   {
-    outsb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
+    ultra_enb |= 0x01 << (target & 0x07);
+    sxfrctl0 |= FAST20;
   }
   else
   {
-    outsl(SCBARRAY + base, scb, (SCB_PIO_TRANSFER_SIZE + 3) / 4);
+    ultra_enb &= ~(0x01 << (target & 0x07));
+    sxfrctl0 &= ~FAST20;
   }
-
-  outb(0, SCBCNT + base);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_getscb
- *
- * Description:
- *   Get a SCB from the controller.
- *-F*************************************************************************/
-static inline void
-aic7xxx_getscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int base = p->base;
-
-  /*
-   * This is almost identical to aic7xxx_putscb().
-   */
-  outb(SCBAUTO, SCBCNT + base);
-  insb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
-  outb(0, SCBCNT + base);
+  outb(ultra_enb, p->base + ultra_enb_addr);
+  outb(sxfrctl0, p->base + SXFRCTL0);
 }
 
 /*+F*************************************************************************
@@ -1374,6 +1562,47 @@
 
 /*+F*************************************************************************
  * Function:
+ *   scbq_remove
+ *
+ * Description:
+ *   Removes an SCB from the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_remove(scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+  if (queue->head == scb)
+  {
+    /* At beginning of queue, remove from head. */
+    scbq_remove_head(queue);
+  }
+  else
+  {
+    struct aic7xxx_scb *curscb = queue->head;
+
+    /*
+     * Search until the next scb is the one we're looking for, or
+     * we run out of queue.
+     */
+    while ((curscb != NULL) && (curscb->q_next != scb))
+    {
+      curscb = curscb->q_next;
+    }
+    if (curscb != NULL)
+    {
+      /* Found it. */
+      curscb->q_next = scb->q_next;
+      if (scb->q_next == NULL)
+      {
+        /* Update the tail when removing the tail. */
+        queue->tail = curscb;
+      }
+    }
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
  *   scbq_insert_tail
  *
  * Description:
@@ -1403,23 +1632,87 @@
  *   to be reset and all devices on that channel must be aborted.
  *-F*************************************************************************/
 static int
-aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
+aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel,
+    int lun, unsigned char tag)
 {
-  int targ = (scb->target_channel_lun >> 4) & 0x0F;
-  char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+  int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
+  char chan = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+  int slun = scb->hscb->target_channel_lun & 0x07;
+  int match;
 
 #ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (match_scb) comparing target/channel %d/%c to scb %d/%c\n",
-         target, channel, targ, chan);
+  printk("scsi%d: (targ %d/chan %c) matching scb to (targ %d/chan %c)\n",
+         scb->cmd->device->host->host_no, target, channel, targ, chan);
 #endif
-  if (target == ALL_TARGETS)
+  match = ((chan == channel) || (channel == ALL_CHANNELS));
+  if (match != 0)
+    match = ((targ == target) || (target == ALL_TARGETS));
+  if (match != 0)
+    match = ((lun == slun) || (lun == ALL_LUNS));
+  if (match != 0)
+    match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+
+  return (match);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_add_curscb_to_free_list
+ *
+ * Description:
+ *   Adds the current scb (in SCBPTR) to the list of free SCBs.
+ *-F*************************************************************************/
+static void
+aic7xxx_add_curscb_to_free_list(struct aic7xxx_host *p)
+{
+  /*
+   * Invalidate the tag so that aic7xxx_find_scb doesn't think
+   * it's active
+   */
+  outb(SCB_LIST_NULL, p->base + SCB_TAG);
+
+  outb(inb(p->base + FREE_SCBH), p->base + SCB_NEXT);
+  outb(inb(p->base + SCBPTR), p->base + FREE_SCBH);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_rem_scb_from_disc_list
+ *
+ * Description:
+ *   Removes the current SCB from the disconnected list and adds it
+ *   to the free list.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_rem_scb_from_disc_list(struct aic7xxx_host *p, unsigned char scbptr)
+{
+  unsigned char next;
+  unsigned char prev;
+
+  outb(scbptr, p->base + SCBPTR);
+  next = inb(p->base + SCB_NEXT);
+  prev = inb(p->base + SCB_PREV);
+
+  outb(0, p->base + SCB_CONTROL);
+
+  aic7xxx_add_curscb_to_free_list(p);
+
+  if (prev != SCB_LIST_NULL)
   {
-    return (chan == channel);
+    outb(prev, p->base + SCBPTR);
+    outb(next, p->base + SCB_NEXT);
   }
   else
   {
-    return ((chan == channel) && (targ == target));
+    outb(next, p->base + DISCONNECTED_SCBH);
   }
+
+  if (next != SCB_LIST_NULL)
+  {
+    outb(next, p->base + SCBPTR);
+    outb(prev, p->base + SCB_PREV);
+  }
+  return next;
 }
 
 /*+F*************************************************************************
@@ -1427,51 +1720,93 @@
  *   aic7xxx_busy_target
  *
  * Description:
- *   Set the specified target active.
+ *   Set the specified target busy.
  *-F*************************************************************************/
 static void
-aic7xxx_busy_target(unsigned char target, char channel, int base)
+aic7xxx_busy_target(struct aic7xxx_host *p, unsigned char target,
+    char channel, unsigned char scbid)
+{
+  unsigned char active_scb;
+  unsigned char info_scb;
+  unsigned int  scb_offset;
+
+  info_scb = target / 4;
+  if (channel == 'B')
+    info_scb = info_scb + 2;
+
+  active_scb = inb(p->base + SCBPTR);
+  outb(info_scb, p->base + SCBPTR);
+  scb_offset = SCB_BUSYTARGETS + (target & 0x03);
+  outb(scbid, p->base + scb_offset);
+  outb(active_scb, p->base + SCBPTR);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_index_busy_target
+ *
+ * Description:
+ *   Returns the index of the busy target, and optionally sets the
+ *   target inactive.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char target,
+    char channel, int unbusy)
 {
-  unsigned char active;
-  unsigned long active_port = ACTIVE_A + base;
+  unsigned char active_scb;
+  unsigned char info_scb;
+  unsigned char busy_scbid;
+  unsigned int  scb_offset;
+
+  info_scb = target / 4;
+  if (channel == 'B')
+    info_scb = info_scb + 2;
 
-  if ((target > 0x07) || (channel == 'B'))
+  active_scb = inb(p->base + SCBPTR);
+  outb(info_scb, p->base + SCBPTR);
+  scb_offset = SCB_BUSYTARGETS + (target & 0x03);
+  busy_scbid = inb(p->base + scb_offset);
+  if (unbusy)
   {
-    /*
-     * targets on the Second channel or above id 7 store info in byte two
-     * of ACTIVE
-     */
-    active_port++;
+    outb(SCB_LIST_NULL, p->base + scb_offset);
   }
-  active = inb(active_port);
-  active |= (0x01 << (target & 0x07));
-  outb(active, active_port);
+  outb(active_scb, p->base + SCBPTR);
+  return (busy_scbid);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_unbusy_target
+ *   aic7xxx_find_scb
  *
  * Description:
- *   Set the specified target inactive.
+ *   Look through the SCB array of the card and attempt to find the
+ *   hardware SCB that corresponds to the passed in SCB.  Return
+ *   SCB_LIST_NULL if unsuccessful.  This routine assumes that the
+ *   card is already paused.
  *-F*************************************************************************/
-static void
-aic7xxx_unbusy_target(unsigned char target, char channel, int base)
+static unsigned char
+aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  unsigned char active;
-  unsigned long active_port = ACTIVE_A + base;
+  unsigned char saved_scbptr;
+  unsigned char curindex;
 
-  if ((target > 0x07) || (channel == 'B'))
+  saved_scbptr = inb(p->base + SCBPTR);
+  curindex = 0;
+  for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
   {
-    /*
-     * targets on the Second channel or above id 7 store info in byte two
-     * of ACTIVE
-     */
-    active_port++;
+    outb(curindex, p->base + SCBPTR);
+    if (inb(p->base + SCB_TAG) == scb->hscb->tag)
+    {
+      break;
+    }
   }
-  active = inb(active_port);
-  active &= ~(0x01 << (target & 0x07));
-  outb(active, active_port);
+  outb(saved_scbptr, p->base + SCBPTR);
+  if (curindex >= p->scb_data->maxhscbs)
+  {
+    curindex = SCB_LIST_NULL;
+  }
+
+  return (curindex);
 }
 
 /*+F*************************************************************************
@@ -1479,68 +1814,60 @@
  *   aic7xxx_allocate_scb
  *
  * Description:
- *   Get a free SCB either from one already assigned to a hardware
- *   slot, or one that will require an SCB to be paged out before
- *   use.  If there are none, attempt to allocate a new one.
+ *   Get an SCB from the free list or by allocating a new one.
  *-F*************************************************************************/
 static struct aic7xxx_scb *
 aic7xxx_allocate_scb(struct aic7xxx_host *p)
 {
-  struct aic7xxx_scb *scbp = NULL;
-  int maxscbs;
+  struct aic7xxx_scb   *scbp = NULL;
+  struct aic7xxx_hwscb *hscbp = NULL;
+#ifdef AGRESSIVE
+  long processor_flags;
+
+  save_flags(processor_flags);
+  cli();
+#endif
 
-  scbp = p->scb_link->free_scbs.head;
+  scbp = p->scb_data->free_scbs.head;
   if (scbp != NULL)
   {
-    scbq_remove_head(&p->scb_link->free_scbs);
+    scbq_remove_head(&p->scb_data->free_scbs);
   }
   else
   {
-    /*
-     * This should always be NULL if paging is not enabled.
-     */
-    scbp = p->page_scbs.head;
-    if (scbp != NULL)
-    {
-      scbq_remove_head(&p->page_scbs);
-    }
-    else
+    if (p->scb_data->numscbs < p->scb_data->maxscbs)
     {
-      /*
-       * Set limit the SCB allocation to the maximum number of
-       * hardware SCBs if paging is not enabled; otherwise use
-       * the maximum (255).
-       */
-      if (p->flags & PAGE_ENABLED)
-        maxscbs = p->maxscbs;
-      else
-        maxscbs = p->maxhscbs;
-      if (p->scb_link->numscbs < maxscbs)
-      {
-        int scb_index = p->scb_link->numscbs;
-        int scb_size = sizeof(struct aic7xxx_scb);
-
-        p->scb_array[scb_index] = kmalloc(scb_size, GFP_ATOMIC | GFP_DMA);
-        scbp = (p->scb_array[scb_index]);
-        if (scbp != NULL)
-        {
-          memset(scbp, 0, sizeof(*scbp));
-          scbp->tag = scb_index;
-          if (scb_index < p->maxhscbs)
-            scbp->position = scb_index;
-          else
-	    scbp->position = SCB_LIST_NULL;
-          p->scb_link->numscbs++;
-        }
+      int scb_index = p->scb_data->numscbs;
+      int scb_size = sizeof(struct aic7xxx_scb) +
+                     sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG;
+
+      scbp = kmalloc(scb_size, GFP_ATOMIC);
+      if (scbp != NULL)
+      {
+        memset(scbp, 0, sizeof(struct aic7xxx_scb));
+        hscbp = &p->scb_data->hscbs[scb_index];
+        scbp->hscb = hscbp;
+        scbp->sg_list = (struct hw_scatterlist *) &scbp[1];
+        memset(hscbp, 0, sizeof(struct aic7xxx_hwscb));
+        hscbp->tag = scb_index;
+        p->scb_data->numscbs++;
+        /*
+         * Place in the scb array; never is removed
+         */
+        p->scb_data->scb_array[scb_index] = scbp;
       }
     }
   }
+#ifdef AIC7XXX_DEBUG
   if (scbp != NULL)
   {
-#ifdef AIC7XXX_DEBUG
-    p->scb_link->activescbs++;
-#endif
+    p->activescbs++;
   }
+#endif
+
+#ifdef AGRESSIVE
+  restore_flags(processor_flags);
+#endif
   return (scbp);
 }
 
@@ -1580,6 +1907,7 @@
     cmd = p->completeq.head;
     p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
     cmd->host_scribble = NULL;
+    p->device_status[TARGET_INDEX(cmd)].active_cmds--;
     cmd->scsi_done(cmd);
   }
   p->completeq.tail = NULL;
@@ -1590,53 +1918,29 @@
  *   aic7xxx_free_scb
  *
  * Description:
- *   Free the scb and update the page, waiting, free scb lists.
+ *   Free the scb and insert into the free scb list.
  *-F*************************************************************************/
 static void
 aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  struct aic7xxx_scb *wscb;
+  struct aic7xxx_hwscb *hscb;
+  long flags;
+
+  hscb = scb->hscb;
+  save_flags(flags);
+  cli();
 
-  scb->state = SCB_FREE;
+  scb->flags = SCB_FREE;
   scb->cmd = NULL;
-  scb->control = 0;
-  scb->state = 0;
+  hscb->control = 0;
+  hscb->target_status = 0;
 
-  if (scb->position == SCB_LIST_NULL)
-  {
-    scbq_insert_head(&p->page_scbs, scb);
-  }
-  else
-  {
-    /*
-     * If there are any SCBS on the waiting queue, assign the slot of this
-     * "freed" SCB to the first one.  We'll run the waiting queues after
-     * all command completes for a particular interrupt are completed or
-     * when we start another command.
-     */
-    wscb = p->waiting_scbs.head;
-    if (wscb != NULL)
-    {
-      scbq_remove_head(&p->waiting_scbs);
-      wscb->position = scb->position;
-      scbq_insert_tail(&p->assigned_scbs, wscb);
-      wscb->state = (wscb->state & ~SCB_WAITINGQ) | SCB_ASSIGNEDQ;
-
-      /* 
-       * The "freed" SCB will need to be assigned a slot before being
-       * used, so put it in the page_scbs queue.
-       */
-      scb->position = SCB_LIST_NULL;
-      scbq_insert_head(&p->page_scbs, scb);
-    }
-    else
-    {
-      scbq_insert_head(&p->scb_link->free_scbs, scb);
-    }
+  scbq_insert_head(&p->scb_data->free_scbs, scb);
 #ifdef AIC7XXX_DEBUG
-    p->scb_link->activescbs--;  /* For debugging purposes. */
+  p->activescbs--;  /* For debugging purposes. */
 #endif
-  }
+
+  restore_flags(flags);
 }
 
 /*+F*************************************************************************
@@ -1651,68 +1955,113 @@
 {
   Scsi_Cmnd *cmd = scb->cmd;
 
+  if (scb->flags & SCB_RECOVERY_SCB)
+  {
+    p->flags &= ~IN_TIMEOUT;
+  }
+  if (cmd->result == DID_OK)
+  {
+    if (scb->flags & SCB_ABORTED)
+    {
+      cmd->result = (DID_RESET << 16);
+    }
+  }
+  if ((scb->flags & (SCB_MSGOUT_WDTR | SCB_MSGOUT_SDTR)) != 0)
+  {
+    unsigned short mask;
+
+    mask = 0x01 << TARGET_INDEX(scb->cmd);
+    if (scb->flags & SCB_MSGOUT_WDTR)
+    {
+      p->wdtr_pending &= ~mask;
+    }
+    if (scb->flags & SCB_MSGOUT_SDTR)
+    {
+      p->sdtr_pending &= ~mask;
+    }
+  }
   aic7xxx_free_scb(p, scb);
   aic7xxx_queue_cmd_complete(p, cmd);
 
+#ifdef AIC7XXX_PROC_STATS
+  {
+    int actual;
+
+    /*
+     * XXX: we should actually know how much actually transferred
+     * XXX: for each command, but apparently that's too difficult.
+     */
+    actual = aic7xxx_length(cmd, 0);
+    if (!(scb->flags & (SCB_ABORTED | SCB_SENSE)) && (actual > 0)
+        && (aic7xxx_error(cmd) == 0))
+    {
+      struct aic7xxx_xferstats *sp;
+      long *ptr;
+      int x;
+
+      sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
+      sp->xfers++;
+
+      if (cmd->request.cmd == WRITE)
+      {
+        sp->w_total++;
+        sp->w_total512 += (actual >> 9);
+        ptr = sp->w_bins;
+      }
+      else
+      {
+        sp->r_total++;
+        sp->r_total512 += (actual >> 9);
+        ptr = sp->r_bins;
+      }
+      for (x = 9; x <= 17; x++)
+      {
+        if (actual < (1 << x))
+        {
+          ptr[x - 9]++;
+          break;
+        }
+      }
+      if (x > 17)
+      {
+        ptr[x - 9]++;
+      }
+    }
+  }
+#endif /* AIC7XXX_PROC_STATS */
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_done_aborted_scbs
+ *   aic7xxx_run_done_queue
  *
  * Description:
- *   Calls the scsi_done() for the Scsi_Cmnd of each scb in the
- *   aborted list, and adds each scb to the free list.
+ *   Calls the aic7xxx_done() for the Scsi_Cmnd of each scb in the
+ *   aborted list, and adds each scb to the free list.  If complete
+ *   is TRUE, we also process the commands complete list.
  *-F*************************************************************************/
 static void
-aic7xxx_done_aborted_scbs(struct aic7xxx_host *p)
+aic7xxx_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
 {
-  Scsi_Cmnd *cmd;
   struct aic7xxx_scb *scb;
   int i;
 
-  for (i = 0; i < p->scb_link->numscbs; i++)
+  for (i = 0; i < p->scb_data->numscbs; i++)
   {
-    scb = (p->scb_array[i]);
-    if (scb->state & SCB_QUEUED_FOR_DONE)
+    scb = p->scb_data->scb_array[i];
+    if (scb->flags & SCB_QUEUED_FOR_DONE)
     {
 #ifdef AIC7XXX_DEBUG_ABORT
-      printk("aic7xxx: (done_aborted_scbs) Aborting scb %d, TCL=%d/%d/%d\n",
-      scb->position, TCL_OF_SCB(scb));
+      printk("(scsi%d:%d:%d) Aborting scb %d\n",
+             p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
 #endif
-      /*
-       * Process the command after marking the scb as free
-       * and adding it to the free list.
-       */
-      cmd = scb->cmd;
-      p->device_status[TARGET_INDEX(cmd)].flags = 0;
-      aic7xxx_free_scb(p, scb);
-      cmd->scsi_done(cmd);  /* call the done function */
+      aic7xxx_done(p, scb);
     }
   }
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_add_waiting_scb
- *
- * Description:
- *   Add this SCB to the head of the "waiting for selection" list.
- *-F*************************************************************************/
-static void
-aic7xxx_add_waiting_scb(u_long base, struct aic7xxx_scb *scb)
-{
-  unsigned char next;
-  unsigned char curscb;
-
-  curscb = inb(SCBPTR + base);
-  next = inb(WAITING_SCBH + base);
-
-  outb(scb->position, SCBPTR + base);
-  outb(next, SCB_NEXT + base);
-  outb(scb->position, WAITING_SCBH + base);
-
-  outb(curscb, SCBPTR + base);
+  if (complete)
+  {
+    aic7xxx_done_cmds_complete(p);
+  }
 }
 
 /*+F*************************************************************************
@@ -1725,26 +2074,23 @@
  *-F*************************************************************************/
 static unsigned char
 aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
-    unsigned char prev)
+    unsigned char scbpos, unsigned char prev)
 {
   unsigned char curscb, next;
-  int target = (scb->target_channel_lun >> 4) & 0x0F;
-  char channel = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
-  int base = p->base;
 
   /*
    * Select the SCB we want to abort and pull the next pointer out of it.
    */
-  curscb = inb(SCBPTR + base);
-  outb(scb->position, SCBPTR + base);
-  next = inb(SCB_NEXT + base);
+  curscb = inb(p->base + SCBPTR);
+  outb(scbpos, p->base + SCBPTR);
+  next = inb(p->base + SCB_NEXT);
 
   /*
    * Clear the necessary fields
    */
-  outb(0, SCB_CONTROL + base);
-  outb(SCB_LIST_NULL, SCB_NEXT + base);
-  aic7xxx_unbusy_target(target, channel, base);
+  outb(0, p->base + SCB_CONTROL);
+
+  aic7xxx_add_curscb_to_free_list(p);
 
   /*
    * Update the waiting list
@@ -1754,22 +2100,23 @@
     /*
      * First in the list
      */
-    outb(next, WAITING_SCBH + base);
+    outb(next, p->base + WAITING_SCBH);
   }
   else
   {
     /*
      * Select the scb that pointed to us and update its next pointer.
      */
-    outb(prev, SCBPTR + base);
-    outb(next, SCB_NEXT + base);
+    outb(prev, p->base + SCBPTR);
+    outb(next, p->base + SCB_NEXT);
   }
   /*
    * Point us back at the original scb position and inform the SCSI
    * system that the command has been aborted.
    */
-  outb(curscb, SCBPTR + base);
-  scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+  outb(curscb, p->base + SCBPTR);
+  scb->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+  scb->flags &= ~SCB_ACTIVE;
   scb->cmd->result = (DID_RESET << 16);
 
   return (next);
@@ -1777,6 +2124,75 @@
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_search_qinfifo
+ *
+ * Description:
+ *   Search the queue-in FIFO for matching SCBs and conditionally
+ *   requeue.  Returns the number of matching SCBs.
+ *-F*************************************************************************/
+static int
+aic7xxx_search_qinfifo(struct aic7xxx_host *p, int target, char channel,
+    int lun, unsigned char tag, int flags, int requeue)
+{
+  unsigned char saved_queue[AIC7XXX_MAXSCB];
+  int      queued = inb(p->base + QINCNT) & p->qcntmask;
+  int      i;
+  int      found;
+  struct aic7xxx_scb *scbp;
+  scb_queue_type removed_scbs;
+
+  found = 0;
+  scbq_init (&removed_scbs);
+  for (i = 0; i < (queued - found); i++)
+  {
+    saved_queue[i] = inb(p->base + QINFIFO);
+    scbp = p->scb_data->scb_array[saved_queue[i]];
+    if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+    {
+       /*
+        * We found an scb that needs to be removed.
+        */
+       if (requeue)
+       {
+         scbq_insert_head(&removed_scbs, scbp);
+       }
+       else
+       {
+         scbp->flags = flags;
+         scbp->flags &= ~SCB_ACTIVE;
+         /*
+          * XXX - Don't know what error to use here.
+          */
+         aic7xxx_error(scbp->cmd) = DID_RESET;
+       }
+       i--;
+       found++;
+    }
+  }
+  /* Now put the saved scbs back. */
+  for (queued = 0; queued < i; queued++)
+    outb(saved_queue[queued], p->base + QINFIFO);
+
+  if (requeue)
+  {
+    scbp = removed_scbs.head;
+    while (scbp != NULL)
+    {
+      scbq_remove_head(&removed_scbs);
+      /*
+       * XXX - Shouldn't we be adding this to the free list?
+       */
+      scbq_insert_head(&p->waiting_scbs, scbp);
+      scbp->flags |= SCB_WAITINGQ;
+      scbp = removed_scbs.head;
+    }
+  }
+
+  return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_reset_device
  *
  * Description:
@@ -1784,131 +2200,280 @@
  *   all active and queued scbs for that target/channel.
  *-F*************************************************************************/
 static int
-aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel)
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
+                     int lun, unsigned char tag)
 {
-  int base = p->base;
-  struct aic7xxx_scb *scb;
+  struct aic7xxx_scb *scbp;
   unsigned char active_scb;
   int i = 0;
-  int found = 0;
+  int found;
 
   /*
    * Restore this when we're done
    */
-  active_scb = inb(SCBPTR + base);
+  active_scb = inb(p->base + SCBPTR);
 
 #ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (reset_device) target/channel %d/%c, active_scb %d\n",
-         target, channel, active_scb);
+  printk("(scsi%d:%d:%d) Reset device, active_scb %d\n",
+         p->host_no, target, CHAN_TO_INT(channel), active_scb);
 #endif
+
   /*
-   * Search the QINFIFO.
+   * Deal with the busy target and linked next issues.
    */
   {
-    int saved_queue[AIC7XXX_MAXSCB];
-    int queued = inb(QINCNT + base) & p->qcntmask;
+    int min_target, max_target;
+    unsigned char busy_scbid;
 
-    for (i = 0; i < (queued - found); i++)
+    /* Make all targets 'relative' to bus A. */
+    if (target == ALL_TARGETS)
     {
-      saved_queue[i] = inb(QINFIFO + base);
-      outb(saved_queue[i], SCBPTR + base);
-      scb = (p->scb_array[inb(SCB_TAG + base)]);
-      if (aic7xxx_match_scb(scb, target, channel))
+      switch (channel)
       {
-        /*
-         * We found an scb that needs to be aborted.
-         */
-#ifdef AIC7XXX_DEBUG_ABORT
-        printk("aic7xxx: (reset_device) aborting SCB %d, TCL=%d/%d/%d\n",
-               saved_queue[i], TCL_OF_SCB(scb));
-#endif
-        scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
-        scb->cmd->result = (DID_RESET << 16);
-        outb(0, SCB_CONTROL + base);
-        i--;
-        found++;
+        case 'A':
+  	  min_target = 0;
+  	  max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
+  	  break;
+        case 'B':
+  	  min_target = 8;
+  	  max_target = 15;
+  	  break;
+        case ALL_CHANNELS:
+        default:
+  	  min_target = 0;
+  	  max_target = (p->bus_type == AIC_SINGLE) ? 7 : 15;
+  	  break;
       }
     }
-    /*
-     * Now put the saved scbs back.
-     */
-    for (queued = 0; queued < i; queued++)
+    else
+    { 
+      min_target = target + channel == 'B' ? 8 : 0;
+      max_target = min_target;
+    }
+
+    for (i = min_target; i <= max_target; i++)
     {
-      outb(saved_queue[queued], QINFIFO + base);
+      busy_scbid = aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/FALSE);
+      if (busy_scbid < p->scb_data->numscbs)
+      {
+  	struct aic7xxx_scb *busy_scb;
+  	struct aic7xxx_scb *next_scb;
+  	unsigned char next_scbid;
+
+  	busy_scb = p->scb_data->scb_array[busy_scbid];
+  
+  	next_scbid = busy_scb->hscb->data_count >> 24;
+
+  	if (next_scbid == SCB_LIST_NULL)
+        {
+  	  busy_scbid = aic7xxx_find_scb(p, busy_scb);
+
+  	  if (busy_scbid != SCB_LIST_NULL)
+          {
+  	    outb(busy_scbid, p->base + SCBPTR);
+  	    next_scbid = inb(p->base + SCB_LINKED_NEXT);
+  	  }
+  	}
+
+  	if (aic7xxx_match_scb(busy_scb, target, channel, lun, tag))
+        {
+  	  aic7xxx_index_busy_target(p, i, 'A', /*unbusy*/TRUE);
+  	}
+
+  	if (next_scbid != SCB_LIST_NULL)
+        {
+  	  next_scb = p->scb_data->scb_array[next_scbid];
+  	  if (aic7xxx_match_scb(next_scb, target, channel, lun, tag))
+          {
+  	    continue;
+          }
+  	  /* Requeue for later processing */
+  	  scbq_insert_head(&p->waiting_scbs, next_scb);
+  	  next_scb->flags |= SCB_WAITINGQ;
+  	}
+      }
     }
   }
 
+  found = aic7xxx_search_qinfifo(p, target, channel, lun, tag,
+      SCB_ABORTED | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE);
+
   /*
    * Search waiting for selection list.
    */
   {
-    unsigned char next, prev;
+    unsigned char next, prev, scb_index;
 
-    next = inb(WAITING_SCBH + base);  /* Start at head of list. */
+    next = inb(p->base + WAITING_SCBH);  /* Start at head of list. */
     prev = SCB_LIST_NULL;
 
     while (next != SCB_LIST_NULL)
     {
-      outb(next, SCBPTR + base);
-      scb = (p->scb_array[inb(SCB_TAG + base)]);
-      /*
-       * Select the SCB.
-       */
-      if (aic7xxx_match_scb(scb, target, channel))
+      outb(next, p->base + SCBPTR);
+      scb_index = inb(p->base + SCB_TAG);
+      if (scb_index >= p->scb_data->numscbs)
+      {
+        panic("aic7xxx: Waiting List inconsistency; SCB index=%d, numscbs=%d\n",
+              scb_index, p->scb_data->numscbs);
+      }
+      scbp = p->scb_data->scb_array[scb_index];
+      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
       {
-        next = aic7xxx_abort_waiting_scb(p, scb, prev);
+        unsigned char linked_next;
+
+        next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
+        linked_next = inb(p->base + SCB_LINKED_NEXT);
+        if (linked_next != SCB_LIST_NULL)
+        {
+          struct aic7xxx_scb *next_scb;
+          /*
+           * Requeue the waiting SCB via the waiting list.
+           */
+          next_scb = p->scb_data->scb_array[linked_next];
+          if (! aic7xxx_match_scb(next_scb, target, channel, lun, tag))
+          {
+            scbq_insert_head(&p->waiting_scbs, next_scb);
+            next_scb->flags |= SCB_WAITINGQ;
+          }
+        }
         found++;
       }
       else
       {
         prev = next;
-        next = inb(SCB_NEXT + base);
+        next = inb(p->base + SCB_NEXT);
       }
     }
   }
 
   /*
-   * Go through the entire SCB array now and look for commands for
-   * for this target that are active.  These are other (most likely
-   * tagged) commands that were disconnected when the reset occurred.
+   * Go through disconnected list and remove any entries we have queued
+   * for completion, zeroing their control byte too.
    */
-  for (i = 0; i < p->scb_link->numscbs; i++)
   {
-    scb = (p->scb_array[i]);
-    if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel))
+    unsigned char next, prev, scb_index;
+
+    next = inb(p->base + DISCONNECTED_SCBH);
+    prev = SCB_LIST_NULL;
+
+    while (next != SCB_LIST_NULL)
     {
-      /*
-       * Ensure the target is "free"
-       */
-      aic7xxx_unbusy_target(target, channel, base);
-      if (! (scb->state & SCB_PAGED_OUT))
+      outb(next, p->base + SCBPTR);
+      scb_index = inb(p->base + SCB_TAG);
+      if (scb_index > p->scb_data->numscbs)
       {
-        outb(scb->position, SCBPTR + base);
-        outb(0, SCB_CONTROL + base);
+        panic("aic7xxx: Disconnected List inconsistency, SCB index = %d, "
+              "num scbs = %d.\n", scb_index, p->scb_data->numscbs);
       }
-      scb->state |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
-      scb->cmd->result = (DID_RESET << 16);
+      scbp = p->scb_data->scb_array[scb_index];
+      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      {
+        next = aic7xxx_rem_scb_from_disc_list(p, next);
+      }
+      else
+      {
+        prev = next;
+        next = inb(p->base + SCB_NEXT);
+      }
+    }
+  }
+
+  /*
+   * Go through the hardware SCB array looking for commands that
+   * were active but not on any list.
+   */
+  for (i = 0; i < p->scb_data->maxhscbs; i++)
+  {
+    unsigned char scbid;
+
+    outb(i, p->base + SCBPTR);
+    scbid = inb(p->base + SCB_TAG);
+    if (scbid < p->scb_data->numscbs)
+    {
+      scbp = p->scb_data->scb_array[scbid];
+      if (aic7xxx_match_scb(scbp, target, channel, lun, tag))
+      {
+        aic7xxx_add_curscb_to_free_list(p);
+      }
+    }
+  }
+
+  /*
+   * Go through the entire SCB array now and look for commands for
+   * for this target that are stillactive.  These are other (most likely
+   * tagged) commands that were disconnected when the reset occurred.
+   */
+  for (i = 0; i < p->scb_data->numscbs; i++)
+  {
+    scbp = p->scb_data->scb_array[i];
+    if (((scbp->flags & SCB_ACTIVE) != 0) &&
+        aic7xxx_match_scb(scbp, target, channel, lun, tag))
+    {
+      scbp->flags |= SCB_ABORTED | SCB_QUEUED_FOR_DONE;
+      scbp->flags &= ~SCB_ACTIVE;
+      aic7xxx_error(scbp->cmd) = DID_RESET;
+
       found++;
+
+      if ((scbp->flags & SCB_WAITINGQ) != 0)
+      {
+        scbq_remove(&p->waiting_scbs, scbp);
+        scbp->flags &= ~SCB_WAITINGQ;
+      }
     }
   }
 
-  outb(active_scb, SCBPTR + base);
+  outb(active_scb, p->base + SCBPTR);
   return (found);
 }
 
 /*+F*************************************************************************
  * Function:
+ *   aic7xxx_clear_intstat
+ *
+ * Description:
+ *   Clears the interrupt status.
+ *-F*************************************************************************/
+static void
+aic7xxx_clear_intstat(struct aic7xxx_host *p)
+{
+  /* Clear any interrupt conditions this may have caused. */
+  outb(CLRSELDO | CLRSELDI | CLRSELINGO, p->base + CLRSINT0);
+  outb(CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
+       CLRPHASECHG | CLRREQINIT, p->base + CLRSINT1);
+  outb(CLRSCSIINT, p->base + CLRINT);
+}
+
+/*+F*************************************************************************
+ * Function:
  *   aic7xxx_reset_current_bus
  *
  * Description:
  *   Reset the current SCSI bus.
  *-F*************************************************************************/
 static void
-aic7xxx_reset_current_bus(int base)
+aic7xxx_reset_current_bus(struct aic7xxx_host *p)
 {
-  outb(SCSIRSTO, SCSISEQ + base);
+  unsigned char scsiseq;
+
+  /* Disable reset interrupts. */
+  outb(inb(p->base + SIMODE1) & ~ENSCSIRST, p->base + SIMODE1);
+
+  /* Turn on the bus reset. */
+  scsiseq = inb(p->base + SCSISEQ);
+  outb(scsiseq | SCSIRSTO, p->base + SCSISEQ);
+
+  udelay(1000);
+
+  /* Turn off the bus reset. */
+  outb(scsiseq & ~SCSIRSTO, p->base + SCSISEQ);
+
+  aic7xxx_clear_intstat(p);
+
+  /* Re-enable reset interrupts. */
+  outb(inb(p->base + SIMODE1) | ENSCSIRST, p->base + SIMODE1);
+
   udelay(1000);
-  outb(0, SCSISEQ + base);
 }
 
 /*+F*************************************************************************
@@ -1921,25 +2486,24 @@
 static int
 aic7xxx_reset_channel(struct aic7xxx_host *p, char channel, int initiate_reset)
 {
-  int base = p->base;
-  unsigned char sblkctl;
-  char cur_channel;
   unsigned long offset, offset_max;
   int found;
+  unsigned char sblkctl;
+  char cur_channel;
 
+  pause_sequencer(p);
   /*
-   * Clean up all the state information for the
-   * pending transactions on this bus.
+   * Clean up all the state information for the pending transactions
+   * on this bus.
    */
-  found = aic7xxx_reset_device(p, ALL_TARGETS, channel);
+  found = aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
 
   if (channel == 'B')
   {
     p->needsdtr |= (p->needsdtr_copy & 0xFF00);
     p->sdtr_pending &= 0x00FF;
-    outb(0, ACTIVE_B + base);
-    offset = TARG_SCRATCH + base + 8;
-    offset_max = TARG_SCRATCH + base + 16;
+    offset = TARG_SCRATCH + 8;
+    offset_max = TARG_SCRATCH + 16;
   }
   else
   {
@@ -1949,132 +2513,100 @@
       p->needwdtr = p->needwdtr_copy;
       p->sdtr_pending = 0x0;
       p->wdtr_pending = 0x0;
-      outb(0, ACTIVE_A + base);
-      outb(0, ACTIVE_B + base);
-      offset = TARG_SCRATCH + base;
-      offset_max = TARG_SCRATCH + base + 16;
+      offset = TARG_SCRATCH;
+      offset_max = TARG_SCRATCH + 16;
     }
     else
     {
+      /* Channel A */
       p->needsdtr |= (p->needsdtr_copy & 0x00FF);
       p->sdtr_pending &= 0xFF00;
-      outb(0, ACTIVE_A + base);
-      offset = TARG_SCRATCH + base;
-      offset_max = TARG_SCRATCH + base + 8;
+      offset = TARG_SCRATCH;
+      offset_max = TARG_SCRATCH + 8;
     }
   }
+
   while (offset < offset_max)
   {
     /*
-     * Revert to async/narrow transfers
-     * until we renegotiate.
+     * Revert to async/narrow transfers until we renegotiate.
      */
     u_char targ_scratch;
-    targ_scratch = inb(offset);
+
+    targ_scratch = inb(p->base + offset);
     targ_scratch &= SXFR;
-    outb(targ_scratch, offset);
+    outb(targ_scratch, p->base + offset);
     offset++;
   }
 
   /*
    * Reset the bus and unpause/restart the controller
    */
-
-  /*
-   * Case 1: Command for another bus is active
-   */
-  sblkctl = inb(SBLKCTL + base);
+  sblkctl = inb(p->base + SBLKCTL);
   cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
   if (cur_channel != channel)
   {
+    /*
+     * Case 1: Command for another bus is active
+     */
 #ifdef AIC7XXX_DEBUG_ABORT
-    printk("aic7xxx: (reset_channel) Stealthily resetting channel %c\n",
-           channel);
+    printk("scsi%d: Stealthily resetting channel %c\n",
+           p->host_no, channel);
 #endif
     /*
-     * Stealthily reset the other bus without upsetting the current bus
+     * Stealthily reset the other bus without upsetting the current bus.
      */
-    outb(sblkctl ^ SELBUSB, SBLKCTL + base);
+    outb(sblkctl ^ SELBUSB, p->base + SBLKCTL);
+    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
     if (initiate_reset)
     {
-      aic7xxx_reset_current_bus(base);
+      aic7xxx_reset_current_bus(p);
+      /*
+       * Cause the mid-level SCSI code to delay any further 
+       * queueing by the bus settle time for us.
+       */
+      p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
     }
-    outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
-    outb(CLRSCSIINT, CLRINT + base);
-    outb(sblkctl, SBLKCTL + base);
-
-    UNPAUSE_SEQUENCER(p);
+    outb(0, p->base + SCSISEQ);
+    aic7xxx_clear_intstat(p);
+    outb(sblkctl, p->base + SBLKCTL);
+    unpause_sequencer(p, /* unpause_always */ FALSE);
   }
-  /*
-   * Case 2: A command from this bus is active or we're idle
-   */
   else
   {
+    /*
+     * Case 2: A command from this bus is active or we're idle.
+     */
 #ifdef AIC7XXX_DEBUG_ABORT
-    printk("aic7xxx: (reset_channel) Resetting current channel %c\n",
-           channel);
+    printk("scsi%d: Resetting current channel %c\n",
+           p->host_no, channel);
 #endif
+    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
     if (initiate_reset)
     {
-      aic7xxx_reset_current_bus(base);
+      aic7xxx_reset_current_bus(p);
+      /*
+       * Cause the mid-level SCSI code to delay any further 
+       * queueing by the bus settle time for us.
+       */
+#if 0
+      p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
+#endif
     }
-    outb(CLRSCSIRSTI | CLRSELTIMEO, CLRSINT1 + base);
-    outb(CLRSCSIINT, CLRINT + base);
-    RESTART_SEQUENCER(p);
+    outb(0, p->base + SCSISEQ);
+    aic7xxx_clear_intstat(p);
+    restart_sequencer(p);
 #ifdef AIC7XXX_DEBUG_ABORT
-    printk("aic7xxx: (reset_channel) Channel reset, sequencer restarted\n");
+    printk("scsi%d: Channel reset, sequencer restarted\n", p->host_no);
 #endif
   }
 
   /*
-   * Cause the mid-level SCSI code to delay any further 
-   * queueing by the bus settle time for us.
-   */
-  p->host->last_reset = (jiffies + (AIC7XXX_RESET_DELAY * HZ));
-
-  /*
    * Now loop through all the SCBs that have been marked for abortion,
    * and call the scsi_done routines.
    */
-  aic7xxx_done_aborted_scbs(p);
-  return found;
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_page_scb
- *
- * Description:
- *   Swap in_scbp for out_scbp down in the cards SCB array.
- *   We assume that the SCB for out_scbp is already selected in SCBPTR.
- *
- *-F*************************************************************************/
-static inline void
-aic7xxx_page_scb(struct aic7xxx_host *p, struct aic7xxx_scb *out_scbp,
-    struct aic7xxx_scb *in_scbp)
-{
-  int index;
-
-  /* Page-out */
-#if 0
-printk("aic7xxx: Paging out target %d SCB and paging in target %d SCB\n",
-       out_scbp->cmd->target, in_scbp->cmd->target);
-#endif
-  aic7xxx_getscb(p, out_scbp);
-  out_scbp->state |= SCB_PAGED_OUT;
-  if (!(out_scbp->control & TAG_ENB))
-  {
-    /* Stick in non-tagged array */
-    index = (out_scbp->target_channel_lun >> 4) | 
-            (out_scbp->target_channel_lun & SELBUSB);
-    p->pagedout_ntscbs[index] = out_scbp;
-  }
-
-  /* Page-in */
-  in_scbp->position = out_scbp->position;
-  out_scbp->position = SCB_LIST_NULL;
-  aic7xxx_putscb(p, in_scbp);
-  in_scbp->state &= ~SCB_PAGED_OUT;
+  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+  return (found);
 }
 
 /*+F*************************************************************************
@@ -2082,1159 +2614,1326 @@
  *   aic7xxx_run_waiting_queues
  *
  * Description:
- *   Scan the assigned_scbs and waiting_scbs queues.  For scbs in the
- *   assigned_scbs queue, we download and start them.  For scbs in the
- *   waiting_scbs queue, we page in as many as we can being careful
- *   not to cause a deadlock for a reconnecting target.
- *
+ *   Scan the awaiting_scbs queue downloading and starting as many
+ *   scbs as we can.
  *-F*************************************************************************/
 static inline void
 aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
 {
   struct aic7xxx_scb *scb;
-  u_char cur_scb, intstat;
-  u_long base = p->base;
-  long flags;
 
-  if ((p->assigned_scbs.head == NULL) && (p->waiting_scbs.head == NULL))
+  if (p->waiting_scbs.head == NULL)
     return;
 
-  save_flags(flags);
-  cli();
-
-  PAUSE_SEQUENCER(p);
-  cur_scb = inb(SCBPTR + base);
-  intstat = inb(INTSTAT + base);
-
+  pause_sequencer(p);
   /*
    * First handle SCBs that are waiting but have been assigned a slot.
    */
-  scb = p->assigned_scbs.head;
-  while (scb != NULL)
-  {
-    scbq_remove_head(&(p->assigned_scbs));
-    outb(scb->position, SCBPTR + base);
-    aic7xxx_putscb(p, scb);
-    /* Mark this as an active command. */
-    scb->state = (scb->state & ~SCB_ASSIGNEDQ) | SCB_ACTIVE;
-    outb(scb->position, QINFIFO + base);
-    scb = p->assigned_scbs.head;
-  }
-
-  /* Now deal with SCBs that require paging. */
   scb = p->waiting_scbs.head;
-  if (scb != NULL)
+  while (scb != NULL)
   {
-    u_char disc_scb = inb(DISCONNECTED_SCBH + base);
-    u_char active = inb(FLAGS + base) & (SELECTED | IDENTIFY_SEEN);
-    int count = 0;
-    u_char next_scb;
-
-    while (scb != NULL)
+    if (p->curqincnt >= p->qfullcount)
     {
-      /* Attempt to page this SCB in */
-      if (disc_scb == SCB_LIST_NULL)
-        break;
-
-      /*
-       * Advance disc_scb to the next one in the list.
-       */
-      outb(disc_scb, SCBPTR + base);
-      next_scb = inb(SCB_NEXT + base); 
-
-      /*
-       * We have to be careful about when we allow an SCB to be paged out. 
-       * There must always be at least one slot availible for a reconnecting
-       * target in case it references an SCB that has been paged out.  Our
-       * heuristic is that either the disconnected list has at least two
-       * entries in it or there is one entry and the sequencer is activily
-       * working on an SCB which implies that it will either complete or
-       * disconnect before another reconnection can occur.
-       */
-      if ((next_scb != SCB_LIST_NULL) || active)
+      p->curqincnt = inb(p->base + QINCNT) & p->qcntmask;
+      if (p->curqincnt >= p->qfullcount)
       {
-        u_char out_scbi;
-        struct aic7xxx_scb *out_scbp;
-
-        scbq_remove_head(&(p->waiting_scbs));
-
-        /*
-         * Find the in-core SCB for the one we're paging out.
-         */
-        out_scbi = inb(SCB_TAG + base); 
-        out_scbp = (p->scb_array[out_scbi]);
-
-        /* Do the page out and mark the paged in SCB as active. */
-        aic7xxx_page_scb(p, out_scbp, scb);
-
-        /* Mark this as an active command. */
-        scb->state = (scb->state & ~SCB_WAITINGQ) | SCB_ACTIVE;
-
-        /* Queue the command */
-        outb(scb->position, QINFIFO + base);
-        count++;
-
-        /* Advance to the next disconnected SCB */
-        disc_scb = next_scb;
-        scb = p->waiting_scbs.head;
+        break;
       }
-      else
-        scb = NULL;
     }
 
-    if (count)
+    /*
+     * We have some space.
+     */
+    scbq_remove_head(&(p->waiting_scbs));
+    scb->flags &= ~SCB_WAITINGQ;
+
+    outb(scb->hscb->tag, p->base + QINFIFO);
+
+    if ((p->flags & PAGE_ENABLED) != 0)
     {
-      /* 
-       * Update the head of the disconnected list.
+      /*
+       * We only care about this statistic when paging
+       * since it's impossible to overflow the qinfifo
+       * in the non-paging case.
        */
-      outb(disc_scb, DISCONNECTED_SCBH + base);
-      if (disc_scb != SCB_LIST_NULL)
-      {
-        outb(disc_scb, SCBPTR + base);
-        outb(SCB_LIST_NULL, SCB_PREV + base);
-      }
+      p->curqincnt++;
     }
+    scb = p->waiting_scbs.head;
   }
-  /* Restore old position */
-  outb(cur_scb, SCBPTR + base);
 
-  /*
-   * Guard against unpausing the sequencer if there is an interrupt
-   * waiting to happen.
-   */
-  if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
-  {
-    UNPAUSE_SEQUENCER(p);
-  }
+  unpause_sequencer(p, FALSE);
+}
 
-  restore_flags(flags);
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_construct_sdtr
+ *
+ * Description:
+ *   Constucts a synchronous data transfer message in the message
+ *   buffer on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_sdtr(struct aic7xxx_host *p, int start_byte,
+    unsigned char period, unsigned char offset)
+{
+  outb(MSG_EXTENDED,     p->base + MSG_OUT + start_byte);
+  outb(MSG_EXT_SDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
+  outb(MSG_EXT_SDTR,     p->base + MSG_OUT + 2 + start_byte);
+  outb(period,           p->base + MSG_OUT + 3 + start_byte);
+  outb(offset,           p->base + MSG_OUT + 4 + start_byte);
+  outb(start_byte + 5,   p->base + MSG_LEN);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_isr
+ *   aic7xxx_construct_wdtr
  *
  * Description:
- *   SCSI controller interrupt handler.
+ *   Constucts a wide data transfer message in the message buffer
+ *   on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_wdtr(struct aic7xxx_host *p, int start_byte,
+    unsigned char bus_width)
+{
+  outb(MSG_EXTENDED,     p->base + MSG_OUT + start_byte);
+  outb(MSG_EXT_WDTR_LEN, p->base + MSG_OUT + 1 + start_byte);
+  outb(MSG_EXT_WDTR,     p->base + MSG_OUT + 2 + start_byte);
+  outb(bus_width,        p->base + MSG_OUT + 3 + start_byte);
+  outb(start_byte + 4,   p->base + MSG_LEN);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_calc_residual
  *
- *   NOTE: Since we declared this using SA_INTERRUPT, interrupts should
- *         be disabled all through this function unless we say otherwise.
+ * Description:
+ *   Calculate the residual data not yet transferred.
  *-F*************************************************************************/
 static void
-aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
 {
-  int base, intstat, actual, scb_index, run_aborted_queue = FALSE;
-  struct aic7xxx_host *p;
-  struct aic7xxx_scb *scb = NULL;
-  short         transfer;
-  unsigned char ha_flags, scsi_id, bus_width;
-  unsigned char offset, rate, scratch, scratch_offset;
-  unsigned char max_offset, rej_byte;
-  unsigned short target_mask;
-  char channel;
-  unsigned int addr; /* must be 32 bits */
+  struct aic7xxx_hwscb *hscb;
   Scsi_Cmnd *cmd;
+  int actual;
 
-  p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
+  cmd = scb->cmd;
+  hscb = scb->hscb;
 
   /*
-   * Search for the host with a pending interrupt.  If we can't find
-   * one, then we've encountered a spurious interrupt.
+   *  Don't destroy valid residual information with
+   *  residual coming from a check sense operation.
    */
-  while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND))
+  if (((scb->hscb->control & DISCONNECTED) == 0) &&
+      (scb->flags & SCB_SENSE) == 0)
   {
-    if (p->next == NULL)
-    {
-      p = NULL;
-    }
-    else
+    /*
+     *  We had an underflow. At this time, there's only
+     *  one other driver that bothers to check for this,
+     *  and cmd->underflow seems to be set rather half-
+     *  heartedly in the higher-level SCSI code.
+     */
+    actual = aic7xxx_length(cmd, hscb->residual_SG_segment_count);
+
+    actual -= (hscb->residual_data_count[2] << 16) |
+              (hscb->residual_data_count[1] <<  8) |
+              hscb->residual_data_count[0];
+
+    if (actual < cmd->underflow)
     {
-      p = (struct aic7xxx_host *) p->next->hostdata;
+      printk(KERN_WARNING "(scsi%d:%d:%d) Underflow - "
+             "Wanted at least %u, got %u, residual SG count %d.\n",
+             p->host_no, TC_OF_SCB(scb), cmd->underflow, actual,
+             hscb->residual_SG_segment_count);
+      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+      aic7xxx_status(cmd) = hscb->target_status;
     }
   }
 
-  if (p == NULL)
-    return;
-
   /*
-   * Keep track of interrupts for /proc/scsi
+   * Clean out the residual information in the SCB for the
+   * next consumer.
    */
-  p->isr_count++;
+  hscb->residual_data_count[2] = 0;
+  hscb->residual_data_count[1] = 0;
+  hscb->residual_data_count[0] = 0;
+  hscb->residual_SG_segment_count = 0;
+}
 
-  if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_device_reset
+ *
+ * Description:
+ *   Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_device_reset(struct aic7xxx_host *p, int target, char channel)
+{
+  unsigned short targ_mask;
+  unsigned char  targ_scratch;
+  int scratch_offset = target;
+  int found;
+
+  if (channel == 'B')
   {
-    /*
-     * We must only have one card at this IRQ and it must have been
-     * added to the board data before the spurious interrupt occurred.
-     * It is sufficient that we check isr_count and not the spurious
-     * interrupt count.
-     */
-    printk("aic7xxx: (aic7xxx_isr) Encountered spurious interrupt.\n");
-    return;
+    scratch_offset += 8;
   }
-
-  base = p->base;
+  targ_mask = (0x01 << scratch_offset);
   /*
-   * Handle all the interrupt sources - especially for SCSI
-   * interrupts, we won't get a second chance at them.
+   * Go back to async/narrow transfers and renegotiate.
    */
-  intstat = inb(INTSTAT + base);
+  p->needsdtr |= p->needsdtr_copy & targ_mask;
+  p->needwdtr |= p->needwdtr_copy & targ_mask;
+  p->sdtr_pending &= ~targ_mask;
+  p->wdtr_pending &= ~targ_mask;
+  targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+  targ_scratch &= SXFR;
+  outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+  found = aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+  printk(KERN_WARNING "(scsi%d:%d:%d) Bus Device Reset delivered, "
+         "%d SCBs aborted.\n", p->host_no, target, CHAN_TO_INT(channel), found);
+  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+}
 
-  /*
-   * Indicate that we're in the interrupt handler.
-   */
-  p->flags |= IN_ISR;
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_seqint
+ *
+ * Description:
+ *   Interrupt handler for sequencer interrupts (SEQINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_seqint(struct aic7xxx_host *p, unsigned char intstat)
+{
+  struct aic7xxx_scb *scb;
+  unsigned short target_mask;
+  unsigned char target, scratch_offset;
+  char channel;
 
-  if (intstat & BRKADRINT)
+  if ((inb(p->base + SEQ_FLAGS) & RESELECTED) != 0)
   {
-    int i;
-    unsigned char errno = inb(ERROR + base);
-
-    printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
-    for (i = 0; i < NUMBER(hard_error); i++)
-    {
-      if (errno & hard_error[i].errno)
-      {
-        printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
-      }
-    }
-    panic("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
-          inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base));
+    target = (inb(p->base + SELID) >> 4) & 0x0F;
   }
-
-  if (intstat & SEQINT)
+  else
   {
-    /*
-     * Although the sequencer is paused immediately on
-     * a SEQINT, an interrupt for a SCSIINT condition will
-     * unpaused the sequencer before this point.
-     */
-    PAUSE_SEQUENCER(p);
+    target = (inb(p->base + SCSIID) >> 4) & 0x0F;
+  }
+  scratch_offset = target;
+  channel = 'A';
+  if (inb(p->base + SBLKCTL) & SELBUSB)
+  {
+    channel = 'B';
+    scratch_offset += 8;
+  }
+  target_mask = (0x01 << scratch_offset);
 
-    scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
-    scratch_offset = scsi_id;
-    channel = 'A';
-    if (inb(SBLKCTL + base) & SELBUSB)
-    {
-      channel = 'B';
-      scratch_offset += 8;
-    }
-    target_mask = (0x01 << scratch_offset);
+  switch (intstat & SEQINT_MASK)
+  {
+    case NO_MATCH:
+      {
+        /*
+         * This could be for a normal abort request.  Figure out
+         * which SCB we were trying to find and only give an error
+         * if we didn't ask for this to happen.
+         */
+        unsigned char scb_index;
+        unsigned char busy_scbid;
+        unsigned char arg1;
+
+        busy_scbid = aic7xxx_index_busy_target(p, target, channel,
+            /*unbusy*/ FALSE);
+        arg1 = inb(p->base + ARG_1);
 
-    switch (intstat & SEQINT_MASK)
-    {
-      case NO_MATCH:
-    	if (p->flags & PAGE_ENABLED)
+        if (arg1 == SCB_LIST_NULL)
         {
-    	  /* SCB Page-in request */
-    	  struct aic7xxx_scb *outscb;
-    	  u_char arg_1 = inb(ARG_1 + base);
-          int use_disconnected = FALSE;
-
-          /*
-           * The sequencer expects this value upon return.  Assume
-           * we will find the paged out SCB and set the value now.
-           * If we don't, and one of the methods used to acquire an
-           * SCB calls aic7xxx_done(), we will end up in our queue
-           * routine and unpause the sequencer without giving it the
-           * correct return value, which causes a hang.
-           */
-    	  outb(SCB_PAGEDIN, RETURN_1 + base);
-    	  if (arg_1 == SCB_LIST_NULL)
-          {
-    	    /* Non-tagged command */
-    	    int index = scsi_id;
-            if (channel == 'B')
-            {
-              index |= SELBUSB;
-            }
-    	    scb = p->pagedout_ntscbs[index];
-    	  }
-    	  else
-    	    scb = (p->scb_array[arg_1]);
+          /* untagged request */
+          scb_index = busy_scbid;
+        }
+        else
+        {
+          scb_index = arg1;
+        }
 
-          if (!(scb->state & SCB_PAGED_OUT))
+        if (scb_index < p->scb_data->numscbs)
+        {
+          scb = p->scb_data->scb_array[scb_index];
+          if (scb->hscb->control & ABORT_SCB)
           {
-  	    printk(KERN_WARNING "scsi%d: No active paged-out SCB for reconnecting "
-  	  	  "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
-    		  p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-    	    aic7xxx_unbusy_target(scsi_id, channel, base);
-  	    outb(CLRSELTIMEO, CLRSINT1 + base);
-            outb(0, RETURN_1 + base);
+            /*
+             * We expected this.  Let the busfree handler take care
+             * of this when we the abort is finially sent.  Set
+             * IDENTIFY_SEEN so that the busfree handler knows that
+             * there is an SCB to cleanup.
+             */
+            outb(inb(p->base + SEQ_FLAGS) | IDENTIFY_SEEN, p->base + SEQ_FLAGS);
+            printk(KERN_INFO "(scsi%d:%d:%d) reconnect SCB abort successful\n",
+                   p->host_no, TC_OF_SCB(scb));
             break;
           }
+        }
+        printk(KERN_WARNING "(scsi%d:%d:%d) No active SCB for reconnecting "
+               "target - Issuing BUS DEVICE RESET.\n",
+               p->host_no, target, CHAN_TO_INT(channel));
+
+        printk(KERN_WARNING "      SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
+               inb(p->base + SAVED_TCL), arg1,
+               (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+        aic7xxx_handle_device_reset(p, target, channel);
+      }
+      break;
 
-    	  /*
-    	   * Now to pick the SCB to page out.  Either take a free SCB, an
-           * assigned SCB, an SCB that just completed, or the first one
-           * on the disconnected SCB list.
-    	   */
-    	  if (p->scb_link->free_scbs.head != NULL)
-          {
-    	    outscb = p->scb_link->free_scbs.head;
-    	    scbq_remove_head(&p->scb_link->free_scbs);
-    	    scb->position = outscb->position;
-    	    outscb->position = SCB_LIST_NULL;
-    	    scbq_insert_head(&p->page_scbs, outscb);
-    	    outb(scb->position, SCBPTR + base);
-    	    aic7xxx_putscb(p, scb);
-    	    scb->state &= ~SCB_PAGED_OUT;
-    	  }
-    	  else if (p->assigned_scbs.head != NULL)
-          {
-            outscb = p->assigned_scbs.head;
-            scbq_remove_head(&p->assigned_scbs);
-            scb->position = outscb->position;
-            outscb->position = SCB_LIST_NULL;
-            scbq_insert_head(&p->waiting_scbs, outscb);
-            outscb->state = (outscb->state & ~SCB_ASSIGNEDQ) | SCB_WAITINGQ;
-            outb(scb->position, SCBPTR + base);
-    	    aic7xxx_putscb(p, scb);
-            scb->state &= ~SCB_PAGED_OUT;
-          }
-          else if (intstat & CMDCMPLT)
+    case NO_MATCH_BUSY:
+      {
+        /*
+         * XXX - Leave this as a panic for the time being since it
+         * indicates a bug in the timeout code for this to happen.
+         */
+        unsigned char scb_index;
+
+        scb_index = inb(p->base + CUR_SCBID);
+        scb = p->scb_data->scb_array[scb_index];
+
+        panic("scsi%d:  Target %d, channel %c, Target busy link failure, "
+              "but busy SCB exists!\n",
+              p->host_no, target, channel);
+      }
+      break;
+
+    case SEND_REJECT:
+      {
+        unsigned char rej_byte;
+
+        rej_byte = inb(p->base + REJBYTE);
+        printk(KERN_WARNING "(scsi%d:%d:%d) Rejecting unknown message (0x%x) "
+               "received from target, SEQ_FLAGS=0x%x\n",
+               p->host_no, target, CHAN_TO_INT(channel), rej_byte,
+               inb(p->base + SEQ_FLAGS));
+      }
+      break;
+
+    case NO_IDENT:
+      {
+        /*
+         * The reconnecting target either did not send an identify
+         * message, or did, but we didn't find and SCB to match and
+         * before it could respond to our ATN/abort, it hit a dataphase.
+         * The only safe thing to do is to blow it away with a bus
+         * reset.
+         */
+        int found;
+
+        printk(KERN_WARNING "(scsi%d:%d:%d): Target did not send an IDENTIFY "
+               "message; LASTPHASE 0x%x, SAVED_TCL 0x%x\n",
+               p->host_no, target, CHAN_TO_INT(channel),
+               inb(p->base + LASTPHASE), inb(p->base + SAVED_TCL));
+
+        found = aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
+
+        printk(KERN_WARNING "scsi%d: Issued channel %c bus reset; "
+     	       "%d SCBs aborted\n", p->host_no, channel, found);
+      }
+      break;
+
+    case BAD_PHASE:
+      if (inb(p->base + LASTPHASE) == P_BUSFREE)
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d): Missed busfree.\n",
+               p->host_no, CHAN_TO_INT(channel), target);
+        restart_sequencer(p);
+      }
+      else
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d): Unknown scsi bus phase, attempting "
+               "to continue\n", p->host_no, CHAN_TO_INT(channel), target);
+      }
+      break;
+
+    case EXTENDED_MSG:
+      {
+	unsigned char message_length;
+	unsigned char message_code;
+        unsigned char scb_index;
+
+	message_length = inb(p->base + MSGIN_EXT_LEN);
+	message_code = inb(p->base + MSGIN_EXT_OPCODE);
+        scb_index = inb(p->base + SCB_TAG);
+        scb = p->scb_data->scb_array[scb_index];
+
+	switch (message_code)
+	{
+          case MSG_EXT_SDTR:
           {
-            int scb_index;
+            unsigned char period;
+            unsigned char offset;
+            unsigned char saved_offset;
+            unsigned char targ_scratch;
+            unsigned char max_offset;
+            unsigned char rate;
 
-            outb(CLRCMDINT, CLRINT + base);
-            scb_index = inb(QOUTFIFO + base);
-            if (!(inb(QOUTCNT + base) & p->qcntmask))
+            if (message_length != MSG_EXT_SDTR_LEN)
             {
-              intstat &= ~CMDCMPLT;
-            }
-            outscb = (p->scb_array[scb_index]);
-            if (!(outscb->state & SCB_ACTIVE))
-            {
-	      printk(KERN_WARNING "scsi%d: No command for completed SCB %d "
-	             "during NO_MATCH interrupt\n", scb_index, p->host_no);
-              use_disconnected = TRUE;
+              outb(SEND_REJ, p->base + RETURN_1);
+              break;
             }
+
+            period = inb(p->base + MSGIN_EXT_BYTES);
+            saved_offset = inb(p->base + MSGIN_EXT_BYTES + 1);
+            targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+            if (targ_scratch & WIDEXFER)
+              max_offset = MAX_OFFSET_16BIT;
             else
+              max_offset = MAX_OFFSET_8BIT;
+            offset = MIN(saved_offset, max_offset);
+
+            aic7xxx_scsirate(p, &rate, &period, &offset, target, channel);
+
+            /*
+             * Preserve the WideXfer flag.
+             */
+            targ_scratch = rate | (targ_scratch & WIDEXFER);
+
+            /*
+             * Update both the target scratch area and current SCSIRATE.
+             */
+            outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+            outb(targ_scratch, p->base + SCSIRATE);
+
+            /*
+             * See if we initiated Sync Negotiation and didn't have
+             * have to fall down to async transfers.
+             */
+            if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
             {
-              scb->position = outscb->position;
-              outscb->position = SCB_LIST_NULL;
-              outb(scb->position, SCBPTR + base);
-              aic7xxx_putscb(p, scb);
-              scb->state &= ~SCB_PAGED_OUT;
-              outscb->cmd->result |= (aic7xxx_error(outscb->cmd) << 16);
-              if ((outscb->cmd->flags & WAS_SENSE) && 
-                 !(outscb->cmd->flags & ASKED_FOR_SENSE))
+              /* We started it. */
+              if (saved_offset == offset)
               {
-                /*
-                 * Got sense information.
-                 */
-	        outscb->cmd->flags &= ASKED_FOR_SENSE;
+        	/*
+        	 * Don't send an SDTR back to the target.
+        	 */
+        	outb(0, p->base + RETURN_1);
+              }
+              else
+              {
+        	/* We went too low - force async. */
+        	outb(SEND_REJ, p->base + RETURN_1);
               }
-              p->device_status[TARGET_INDEX(outscb->cmd)].flags
-                |= DEVICE_SUCCESS;
-              aic7xxx_done(p, outscb);
             }
+            else
+            {
+              /*
+               * Send our own SDTR in reply.
+               *
+               * We want to see this message as we don't expect a target
+               * to send us a SDTR request first.
+               */
+              printk(KERN_WARNING "scsi%d: Sending SDTR!!\n", p->host_no);
+              aic7xxx_construct_sdtr(p, /* start byte */ 0, period, offset);
+              outb(SEND_MSG, p->base + RETURN_1);
+            }
+            /*
+             * Clear the flags.
+             */
+            p->needsdtr &= ~target_mask;
+            break;
           }
-          else
-          {
-            use_disconnected = TRUE;
-          }
-          if (use_disconnected)
+
+          case MSG_EXT_WDTR:
           {
-    	    u_char tag;
-    	    u_char next;
-    	    u_char disc_scb = inb(DISCONNECTED_SCBH + base);
-    	    if (disc_scb != SCB_LIST_NULL)
+            unsigned char scratch, bus_width;
+
+            if (message_length != MSG_EXT_WDTR_LEN)
             {
-    	      outb(disc_scb, SCBPTR + base);
-    	      tag = inb(SCB_TAG + base);
-    	      outscb = (p->scb_array[tag]);
-    	      next = inb(SCB_NEXT + base);
-    	      if (next != SCB_LIST_NULL)
-              {
-    	        outb(next, SCBPTR + base);
-    	        outb(SCB_LIST_NULL, SCB_PREV + base);
-    	        outb(disc_scb, SCBPTR + base);
-    	      }
-    	      outb(next, DISCONNECTED_SCBH + base);
-    	      aic7xxx_page_scb(p, outscb, scb);
-    	    }
-            else if (inb(QINCNT + base) & p->qcntmask)
+              outb(SEND_REJ, p->base + RETURN_1);
+              break;
+            }
+
+            bus_width = inb(p->base + MSGIN_EXT_BYTES);
+            scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+            if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
             {
-              /* Pull one of our queued commands as a last resort. */
-              disc_scb = inb(QINFIFO + base);
-              outb(disc_scb, SCBPTR + base);
-              tag = inb(SCB_TAG + base);
-              outscb = (p->scb_array[tag]);
-              if ((outscb->control & 0x23) != TAG_ENB)
+              /*
+               * Don't send an WDTR back to the target, since we asked first.
+               */
+              outb(0, p->base + RETURN_1);
+              switch (bus_width)
               {
-                /*
-                 * This is not a simple tagged command so its position
-                 * in the queue matters.  Take the command at the end of
-                 * the queue instead.
-                 */
-                int i;
-                int saved_queue[AIC7XXX_MAXSCB];
-                int queued = inb(QINCNT + base) & p->qcntmask;
-
-                /* Count the command we removed already */
-                saved_queue[0] = disc_scb;
-                queued++;
-
-                /* Empty the input queue. */
-                for (i = 1; i < queued; i++)
-                {
-                  saved_queue[i] = inb(QINFIFO + base);
-                }
-
-                /* Put everyone back but the last entry. */
-                queued--;
-                for (i = 0; i < queued; i++)
-                {
-                  outb(saved_queue[i], QINFIFO + base);
-                }
-
-                outb(saved_queue[queued], SCBPTR + base);
-                tag = inb(SCB_TAG + base);
-                outscb = (p->scb_array[tag]);
+        	case BUS_8_BIT:
+        	  scratch &= 0x7F;
+        	  break;
+
+        	case BUS_16_BIT:
+                  if (aic7xxx_verbose)
+                  {
+        	    printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
+  	  	         "bit transfers.\n", p->host_no, target, channel);
+                  }
+        	  scratch |= WIDEXFER;
+        	  break;
+
+        	case BUS_32_BIT:
+        	  outb(SEND_REJ, p->base + RETURN_1);
+                  /* No verbose here!  We want to see this condition. */
+        	  printk(KERN_WARNING "scsi%d: Target %d, channel %c, "
+  			"requesting 32 bit transfers, rejecting...\n",
+                	 p->host_no, target, channel);
+        	  break;
+
+        	default:
+        	  break;
               }
-              scb->position = outscb->position;
-              outscb->position = SCB_LIST_NULL;
-              scbq_insert_head(&p->waiting_scbs, outscb);
-              outscb->state |= SCB_WAITINGQ;
-              aic7xxx_putscb(p, scb);
-              scb->state &= ~SCB_PAGED_OUT;
             }
             else
             {
-  	      printk(KERN_WARNING "scsi%d: Page-in request with no candidates "
-  	  	    "target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
-    		    p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-              aic7xxx_unbusy_target(scsi_id, channel, base);
-              outb(CLRSELTIMEO, CLRSINT1 + base);
-              outb(0, RETURN_1 + base);
+              /*
+               * Send our own WDTR in reply.
+               */
+              switch (bus_width)
+              {
+        	case BUS_8_BIT:
+        	  scratch &= 0x7F;
+        	  break;
+
+        	case BUS_32_BIT:
+        	case BUS_16_BIT:
+        	  if (p->bus_type == AIC_WIDE)
+        	  {
+                    printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 "
+  			   "bit transfers.\n", p->host_no, target, channel);
+                    bus_width = BUS_16_BIT;
+                    scratch |= WIDEXFER;
+        	  }
+        	  else
+        	  {
+                    bus_width = BUS_8_BIT;
+                    scratch &= 0x7F;  /* XXX - FreeBSD doesn't do this. */
+        	  }
+        	  break;
+
+        	default:
+        	  break;
+              }
+              aic7xxx_construct_wdtr(p, /* start byte */ 0, bus_width);
+              outb(SEND_MSG, p->base + RETURN_1);
             }
-          }
-    	}
-    	else
-        {
-  	  printk(KERN_WARNING "scsi%d: No active SCB for reconnecting "
-  	  	"target %d, channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
-    		p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-    	  aic7xxx_unbusy_target(scsi_id, channel, base);
-    	  outb(0, SCB_CONTROL + base);
-  	  outb(CLRSELTIMEO, CLRSINT1 + base);
-          outb(0, RETURN_1 + base);
-        }
-  	break;
-
-      case BAD_PHASE:
-	panic("scsi%d: Unknown scsi bus phase.\n", p->host_no);
-	break;
-
-      case SEND_REJECT:
-        rej_byte = inb(REJBYTE + base);
-        if ((rej_byte & 0xF0) == 0x20)
-        {
-          scb_index = inb(SCB_TAG + base);
-          scb = (p->scb_array[scb_index]);
-          printk(KERN_WARNING "scsi%d: Tagged message received without identify."
-                 "Disabling tagged commands for target %d channel %c.\n",
-                  p->host_no, scsi_id, channel);
-          scb->cmd->device->tagged_supported = 0;
-          scb->cmd->device->tagged_queue = 0;
-        }
-        else
-        {
-          printk(KERN_WARNING "scsi%d: Rejecting unknown message (0x%x) received "
-                 "from target %d channel %c.\n",
-                 p->host_no, rej_byte, scsi_id, channel);
-        }
-	break;
-
-      case NO_IDENT:
-	panic("scsi%d: Target %d, channel %c, did not send an IDENTIFY "
-	      "message. SAVED_TCL 0x%x.\n",
-              p->host_no, scsi_id, channel, inb(SAVED_TCL + base));
-	break;
+            p->needwdtr &= ~target_mask;
+            outb(scratch, p->base + TARG_SCRATCH + scratch_offset);
+            outb(scratch, p->base + SCSIRATE);
+            break;
+	  }  /* case MSG_EXT_WDTR */
 
-      case SDTR_MSG:
-	/*
-	 * Help the sequencer to translate the negotiated
-	 * transfer rate. Transfer is 1/4 the period
-	 * in ns as is returned by the sync negotiation
-	 * message. So, we must multiply by four.
-	 */
-	transfer = (inb(ARG_1 + base) << 2);
-	offset = inb(ACCUM + base);
-	scratch = inb(TARG_SCRATCH + base + scratch_offset);
+          default:
+            /*
+             * Unknown extended message - reject it.
+             */
+            outb(SEND_REJ, p->base + RETURN_1);
+            break;
+	}  /* switch (message_code) */
+      }  /* case EXTENDED_MSG */
+      break;
+
+    case REJECT_MSG:
+      {
 	/*
-	 * The maximum offset for a wide device is 0x08; for a
-	 * 8-bit bus device the maximum offset is 0x0F.
+	 * What we care about here is if we had an outstanding SDTR
+	 * or WDTR message for this target. If we did, this is a
+	 * signal that the target is refusing negotiation.
 	 */
-	if (scratch & WIDEXFER)
+	unsigned char targ_scratch;
+        unsigned char scb_index;
+
+        scb_index = inb(p->base + SCB_TAG);
+        scb = p->scb_data->scb_array[scb_index];
+	targ_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+
+	if ((scb->flags & SCB_MSGOUT_WDTR) != 0)
 	{
-	  max_offset = 0x08;
+          /*
+           * note 8bit xfers and clear flag
+           */
+          targ_scratch &= 0x7F;
+          p->needwdtr &= ~target_mask;
+          printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
+  		 "negotiation; using 8 bit transfers.\n",
+  		 p->host_no, target, channel);
 	}
 	else
 	{
-	  max_offset = 0x0F;
-	}
-	aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset),
-                         scsi_id, channel);
-	/*
-	 * Preserve the wide transfer flag.
-	 */
-	scratch = rate | (scratch & WIDEXFER);
-	outb(scratch, TARG_SCRATCH + base + scratch_offset);
-	outb(scratch, SCSIRATE + base);
-	if ((scratch & 0x0F) == 0)
-	{
-          /*
-           * One of two things happened.  Either the device requested
-           * asynchronous data transfers, or it requested a synchronous
-           * data transfer rate that was so low that asynchronous
-           * transfers are faster (not to mention the controller won't
-           * support them).  In both cases the synchronous data transfer
-           * rate and the offset are set to 0 indicating asynchronous
-           * transfers.
-           *
-           * If the device requested an asynchronous transfer, then
-           * accept the request.  If the device is being forced to
-           * asynchronous data transfers and this is the first time
-           * we've seen the request, accept the request.  If we've
-           * already seen the request, then attempt to force
-           * asynchronous data transfers by rejecting the message.
-           */
-          if ((offset == 0) || (p->sdtr_pending & target_mask))
+          if ((scb->flags & SCB_MSGOUT_SDTR) != 0)
           {
             /*
-             * Device requested asynchronous transfers or we're
-             * forcing asynchronous transfers for the first time.
+             * note asynch xfers and clear flag
              */
-            outb(0, RETURN_1 + base);
-          }
-          else
-          {
-            /*
-	     * The first time in forcing asynchronous transfers
-             * failed, so we try sending a reject message.
-	     */
-	    outb(SEND_REJ, RETURN_1 + base);
+            targ_scratch &= 0xF0;
+            p->needsdtr &= ~target_mask;
+            printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
+  		   "synchronous negotiation; using asynchronous transfers.\n",
+  		   p->host_no, target, channel);
           }
+          /*
+           * Otherwise, we ignore it.
+           */
 	}
-	else
-	{
-	  /*
-	   * See if we initiated Sync Negotiation
-	   */
-	  if (p->sdtr_pending & target_mask)
-	  {
-	    /*
-	     * Don't send an SDTR back to the target.
-	     */
-	    outb(0, RETURN_1 + base);
-	  }
-	  else
-	  {
-	    /*
-	     * Send our own SDTR in reply.
-	     */
-	    printk("aic7xxx: Sending SDTR!!\n");
-	    outb(SEND_SDTR, RETURN_1 + base);
-	  }
-	}
-	/*
-	 * Clear the flags.
-	 */
-	p->needsdtr &= ~target_mask;
-	p->sdtr_pending &= ~target_mask;
-	break;
-
-      case WDTR_MSG:
-      {
-	bus_width = inb(ARG_1 + base);
-	printk(KERN_INFO "scsi%d: Received MSG_WDTR, Target %d, channel %c "
-	       "needwdtr(0x%x).\n", p->host_no, scsi_id, channel, p->needwdtr);
-	scratch = inb(TARG_SCRATCH + base + scratch_offset);
-
-	if (p->wdtr_pending & target_mask)
-	{
-	  /*
-	   * Don't send an WDTR back to the target, since we asked first.
-	   */
-	  outb(0, RETURN_1 + base);
-	  switch (bus_width)
-	  {
-	    case BUS_8_BIT:
-	      scratch &= 0x7F;
-	      break;
-
-	    case BUS_16_BIT:
-	      printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
-                     "transfers.\n", p->host_no, scsi_id, channel);
-	      scratch |= 0x80;
-	      break;
-
-	    case BUS_32_BIT:
-	      outb(SEND_REJ, RETURN_1 + base);
-	      printk(KERN_INFO "scsi%d: Target %d, channel %c, requesting 32 bit "
-                     "transfers, rejecting...\n", p->host_no, scsi_id, channel);
-	      break;
-	  }
-	}
-	else
-	{
-	  /*
-	   * Send our own WDTR in reply.
-	   */
-	  printk(KERN_INFO "scsi%d: Will send WDTR!!\n", p->host_no);
-	  switch (bus_width)
-	  {
-	    case BUS_8_BIT:
-	      scratch &= 0x7F;
-	      break;
-
-	    case BUS_32_BIT:
-	      /*
-               * Negotiate 16 bits.
-               */
-	      bus_width = BUS_16_BIT;
-	      /* Yes, we mean to fall thru here. */
-
-	    case BUS_16_BIT:
-	      printk(KERN_INFO "scsi%d: Target %d, channel %c, using 16 bit "
-                     "transfers.\n", p->host_no, scsi_id, channel);
-	      scratch |= 0x80;
-	      break;
-	  }
-	  outb(bus_width | SEND_WDTR, RETURN_1 + base);
-	}
-	p->needwdtr &= ~target_mask;
-	p->wdtr_pending &= ~target_mask;
-	outb(scratch, TARG_SCRATCH + base + scratch_offset);
-	outb(scratch, SCSIRATE + base);
-	break;
+        outb(targ_scratch, p->base + TARG_SCRATCH + scratch_offset);
+        outb(targ_scratch, p->base + SCSIRATE);
       }
+      break;
 
-      case REJECT_MSG:
+    case BAD_STATUS:
       {
-	/*
-	 * What we care about here is if we had an
-	 * outstanding SDTR or WDTR message for this
-	 * target. If we did, this is a signal that
-	 * the target is refusing negotiation.
+	unsigned char scb_index;
+	struct aic7xxx_hwscb *hscb;
+	Scsi_Cmnd *cmd;
+
+	/* The sequencer will notify us when a command has an error that
+	 * would be of interest to the kernel.  This allows us to leave
+	 * the sequencer running in the common case of command completes
+	 * without error.  The sequencer will have DMA'd the SCB back
+	 * up to us, so we can reference the drivers SCB array.
 	 */
+	scb_index = inb(p->base + SCB_TAG);
+	scb = p->scb_data->scb_array[scb_index];
+	hscb = scb->hscb;
 
-	scratch = inb(TARG_SCRATCH + base + scratch_offset);
-
-	if (p->wdtr_pending & target_mask)
-	{
-	  /*
-	   * note 8bit xfers and clear flag
-	   */
-	  scratch &= 0x7F;
-	  p->needwdtr &= ~target_mask;
-	  p->wdtr_pending &= ~target_mask;
-	  printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing WIDE "
-                 "negotiation; using 8 bit transfers.\n",
-                 p->host_no, scsi_id, channel);
-	}
-	else
-	{
-	  if (p->sdtr_pending & target_mask)
-	  {
-	    /*
-	     * note asynch xfers and clear flag
-	     */
-	    scratch &= 0xF0;
-	    p->needsdtr &= ~target_mask;
-	    p->sdtr_pending &= ~target_mask;
-	    printk(KERN_WARNING "scsi%d: Target %d, channel %c, refusing "
-                   "synchronous negotiation; using asynchronous transfers.\n",
-                   p->host_no, scsi_id, channel);
-	  }
-	  /*
-	   * Otherwise, we ignore it.
-	   */
-	}
-	outb(scratch, TARG_SCRATCH + base + scratch_offset);
-	outb(scratch, SCSIRATE + base);
-	break;
-      }
-
-      case BAD_STATUS:
-        /* The sequencer will notify us when a command has an error that
-         * would be of interest to the kernel.  This allows us to leave
-         * the sequencerrunning in the common case of command completes
-         * without error.
-         */
-
-	scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-	outb(0, RETURN_1 + base);   /* CHECK_CONDITION may change this */
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+	/*
+	 * Set the default return value to 0 indicating not to send
+	 * sense.  The sense code will change this if needed and this
+	 * reduces code duplication.
+	 */
+	outb(0, p->base + RETURN_1);
+	if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
 	{
-	  printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-		 "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
-		 intstat, scb_index, scb->state, (unsigned long) scb->cmd);
+          printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
+        	 "SEQINT 0x%x, scb %d, flags 0x%x, cmd 0x%x.\n", p->host_no,
+        	 intstat, scb_index, scb->flags, (unsigned int) scb->cmd);
 	}
 	else
 	{
-	  cmd = scb->cmd;
-          scb->target_status = inb(SCB_TARGET_STATUS + base);
-	  aic7xxx_status(cmd) = scb->target_status;
+          cmd = scb->cmd;
+  	  hscb->target_status = inb(p->base + SCB_TARGET_STATUS);
+          aic7xxx_status(cmd) = hscb->target_status;
 
-	  cmd->result |= scb->target_status;
+          cmd->result |= hscb->target_status;
 
-	  switch (status_byte(scb->target_status))
-	  {
-	    case GOOD:
-              printk(KERN_WARNING "aic7xxx: Interrupted for status of GOOD???\n");
-	      break;
-
-	    case CHECK_CONDITION:
-	      if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE))
-	      {
-                unsigned char tcl;
-		unsigned int  req_buf; /* must be 32 bits */
+          switch (status_byte(hscb->target_status))
+          {
+            case GOOD:
+  	      printk(KERN_WARNING "(scsi%d:%d:%d) Interrupted for status of "
+                     "GOOD???\n", p->host_no, TC_OF_SCB(scb));
+              break;
 
-                tcl = scb->target_channel_lun;
+            case CHECK_CONDITION:
+              if ((aic7xxx_error(cmd) == 0) && !(scb->flags & SCB_SENSE))
+              {
+        	unsigned int addr;    /* must be 32 bits */
+        	/*
+        	 * XXX - How do we save the residual (if there is one).
+        	 */
+                aic7xxx_calculate_residual(p, scb);
+
+        	/*
+  		 * Send a sense command to the requesting target.
+        	 * XXX - revisit this and get rid of the memcopys.
+  		 */
+        	memcpy((void *) scb->sense_cmd, (void *) generic_sense,
+        	       sizeof(generic_sense));
+
+        	scb->sense_cmd[1] = (cmd->lun << 5);
+        	scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
+
+        	scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
+        	scb->sg_list[0].length = sizeof(cmd->sense_buffer);
+        	cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
 
-		/*
-                 * Send a sense command to the requesting target.
+                /*
+                 * XXX - We should allow disconnection, but can't as it
+                 * might allow overlapped tagged commands.
                  */
-		cmd->flags |= WAS_SENSE;
-		memcpy((void *) scb->sense_cmd, (void *) generic_sense,
-		       sizeof(generic_sense));
-
-		scb->sense_cmd[1] = (cmd->lun << 5);
-		scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
-
-		scb->sg_list[0].address = VIRT_TO_BUS(&cmd->sense_buffer);
-		scb->sg_list[0].length = sizeof(cmd->sense_buffer);
-		req_buf = VIRT_TO_BUS(&scb->sg_list[0]);
-		cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
-
-                scb->control = scb->control & DISCENB;
-		scb->target_channel_lun = tcl;
-		addr = VIRT_TO_BUS(scb->sense_cmd);
-		scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
-		memcpy(scb->SCSI_cmd_pointer, &addr,
-		       sizeof(scb->SCSI_cmd_pointer));
-		scb->SG_segment_count = 1;
-		memcpy(scb->SG_list_pointer, &req_buf,
-		       sizeof(scb->SG_list_pointer));
-                scb->data_count = scb->sg_list[0].length;
-		memcpy(scb->data_pointer, &(scb->sg_list[0].address),
-		       sizeof(scb->data_pointer));
+  		/* hscb->control &= DISCENB; */
+                hscb->control = 0;
+        	hscb->target_status = 0;
+        	hscb->SG_segment_count = 1;
+
+        	addr = VIRT_TO_BUS(&scb->sg_list[0]);
+        	memcpy(&hscb->SG_list_pointer, &addr,
+        	       sizeof(hscb->SG_list_pointer));
+
+        	memcpy(&hscb->data_pointer, &(scb->sg_list[0].address),
+        	       sizeof(hscb->data_pointer));
+        	/* Maintain SCB_LINKED_NEXT */
+        	hscb->data_count &= 0xFF000000;
+  		hscb->data_count |= scb->sg_list[0].length;
+
+        	addr = VIRT_TO_BUS(scb->sense_cmd);
+        	memcpy(&hscb->SCSI_cmd_pointer, &addr,
+        	       sizeof(hscb->SCSI_cmd_pointer));
+        	hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
 
-                aic7xxx_putscb(p, scb);
+                scb->sg_count = hscb->SG_segment_count;
+        	scb->flags |= SCB_SENSE;
                 /*
-                 * Ensure that the target is "BUSY" so we don't get overlapping
-                 * commands if we happen to be doing tagged I/O.
+                 * Ensure the target is busy since this will be an
+                 * an untagged request.
                  */
-		aic7xxx_busy_target(scsi_id, channel, base);
+                aic7xxx_busy_target(p, target, channel, hscb->tag);
+        	outb(SEND_SENSE, p->base + RETURN_1);
+              }  /* first time sense, no errors */
+  	      else
+  	      {
+        	if (aic7xxx_error(cmd) == 0)
+  		{
+        	  aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+  		}
+  	      }
+              break;
 
-                aic7xxx_add_waiting_scb(base, scb);
-		outb(SEND_SENSE, RETURN_1 + base);
-	      }  /* first time sense, no errors */
-              else
+            case QUEUE_FULL:
+#ifdef NOT_YET
+              if (scb->hscb->control & TAG_ENB)
+              {
+        	if (cmd->device->queue_depth > 2)
+        	{
+                  cmd->device->queue_depth--;  /* Not correct */
+                  printk(KERN_WARNING "(scsi%d:%d:%d) Tagged queue depth "
+                	 "reduced to %d\n", p->host_no,
+                	 TC_OF_SCB(scb), cmd->device->queue_depth);
+        	}
+        	/*
+        	 * XXX - Requeue this unconditionally?
+        	 */
+
+        	/*
+        	 * We'd like to be able to give the SCB some more time
+        	 * (untimeout, then timeout).
+        	 */
+        	break;
+              }
+#endif
+              printk(KERN_WARNING "(scsi%d:%d:%d) Queue full received; "
+                     "queue depth %d, active %d\n", p->host_no,
+                     TC_OF_SCB(scb), cmd->device->queue_depth,
+                     p->device_status[TARGET_INDEX(cmd)].active_cmds);
+
+              /* Else treat this as if it was a BUSY condition. */
+              scb->hscb->target_status = (BUSY << 1) |
+                  (scb->hscb->target_status & 0x01);
+              /* Fall through to the BUSY case. */
+
+            case BUSY:
+              printk(KERN_WARNING "(scsi%d:%d:%d) Target busy\n",
+                     p->host_no, TC_OF_SCB(scb));
+              if (!aic7xxx_error(cmd))
               {
-                cmd->flags &= ~ASKED_FOR_SENSE;
-	        if (aic7xxx_error(cmd) == 0)
-                {
-		  aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-                }
+  		/*
+        	 * The mid-level SCSI code should be fixed to
+        	 * retry the command at a later time instead of
+        	 * trying right away.
+        	 */
+        	aic7xxx_error(cmd) = DID_BUS_BUSY | (SUGGEST_RETRY << 8);
               }
-	      break;
+              udelay(1000);  /*  A small pause (1ms) to help the drive */
+              break;
 
-	    case BUSY:
-	      printk(KERN_WARNING "scsi%d: Target busy, TCL=0x%x.\n",
-                     p->host_no, scb->target_channel_lun);
-	      if (!aic7xxx_error(cmd))
-	      {
-                /* The error code here used to be DID_BUS_BUSY,
-                 * but after extensive testing, it has been determined
-                 * that a DID_BUS_BUSY return is a waste of time.  If
-                 * the problem is something that will go away, then it
-                 * will, if it isn't, then you don't want the endless
-                 * looping that you get with a DID_BUS_BUSY.  Better
-                 * to be on the safe side and specify an error condition
-                 * that will eventually lead to a reset or abort of some
-                 * sort instead of an endless loop.
-                 */
-	        aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-	      }
-	      break;
-
-	    case QUEUE_FULL:
-	      printk(KERN_WARNING "scsi%d: Queue full.\n", p->host_no);
-              scb->state |= SCB_ASSIGNEDQ;
-              scbq_insert_tail(&p->assigned_scbs, scb);
-	      break;
-
-	    default:
-	      printk(KERN_WARNING "scsi%d: Unexpected target status 0x%x.\n",
-		     p->host_no, scb->target_status);
-	      if (!aic7xxx_error(cmd))
-	      {
-		aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-	      }
-	      break;
-	  }  /* end switch */
+            default:
+              printk(KERN_WARNING "(scsi%d:%d:%d) Unexpected target "
+                     "status 0x%x.\n", p->host_no,
+        	     TC_OF_SCB(scb), scb->hscb->target_status);
+              if (!aic7xxx_error(cmd))
+              {
+        	aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+              }
+              break;
+          }  /* end switch */
 	}  /* end else of */
-	break;
-
-      case RESIDUAL:
-	scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-	{
-	  printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-		 "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
-		 intstat, scb_index, scb->state, (unsigned long) scb->cmd);
-	}
-	else
-	{
-	  cmd = scb->cmd;
-	  /*
-	   *  Don't destroy valid residual information with
-	   *  residual coming from a check sense operation.
-	   */
-	  if (!(cmd->flags & WAS_SENSE))
-	  {
-	    /*
-	     *  We had an underflow. At this time, there's only
-	     *  one other driver that bothers to check for this,
-	     *  and cmd->underflow seems to be set rather half-
-	     *  heartedly in the higher-level SCSI code.
-	     */
-	    actual = aic7xxx_length(cmd, scb->residual_SG_segment_count);
-
-	    actual -= (inb(SCB_RESID_DCNT2 + base) << 16) |
-		      (inb(SCB_RESID_DCNT1 + base) <<  8) |
-		      inb(SCB_RESID_DCNT0 + base);
-
-	    if (actual < cmd->underflow)
-	    {
-	      printk(KERN_WARNING "scsi%d: Target %d underflow - "
-		     "Wanted at least %u, got %u, residual SG count %d.\n",
-		     p->host_no, cmd->target, cmd->underflow, actual,
-                     inb(SCB_RESID_SGCNT + base));
-	      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-	      aic7xxx_status(cmd) = scb->target_status;
-	    }
-	  }
-	}
-	break;
+      }
+      break;
 
-      case ABORT_TAG:
-	scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-	{
-	  printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-		 "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx\n", p->host_no,
-		 intstat, scb_index, scb->state, (unsigned long) scb->cmd);
-	}
-	else
-	{
-	  cmd = scb->cmd;
-	  /*
-	   * We didn't receive a valid tag back from the target
-	   * on a reconnect.
-	   */
-	  printk("scsi%d: Invalid tag received on target %d, channel %c, "
-                 "lun %d - Sending ABORT_TAG.\n", p->host_no,
-		  scsi_id, channel, cmd->lun & 0x07);
+    case AWAITING_MSG:
+      {
+	unsigned char scb_index;
+        unsigned char message_offset;
 
-	  cmd->result = (DID_RETRY_COMMAND << 16);
-          aic7xxx_done(p, scb);
-	}
-	break;
+	scb_index = inb(p->base + SCB_TAG);
+	scb = p->scb_data->scb_array[scb_index];
 
-      case AWAITING_MSG:
-	scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-	if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+	/*
+	 * This SCB had a MK_MESSAGE set in its control byte informing
+	 * the sequencer that we wanted to send a special message to
+	 * this target.
+	 */
+        message_offset = inb(p->base + MSG_LEN);
+	if (scb->flags & SCB_DEVICE_RESET)
 	{
-	  printk(KERN_WARNING "scsi%d: Referenced SCB not valid during "
-		 "SEQINT 0x%x, scb %d, state 0x%x, cmd 0x%lx.\n", p->host_no,
-		 intstat, scb_index, scb->state, (unsigned long) scb->cmd);
+          outb(MSG_BUS_DEV_RESET, p->base + MSG_OUT);
+          outb(1, p->base + MSG_LEN);
+          printk(KERN_INFO "(scsi%d:%d:%d) Bus device reset sent\n",
+        	 p->host_no, TC_OF_SCB(scb));
 	}
-	else
+        else if (scb->flags & SCB_ABORT)
+        {
+          if ((scb->hscb->control & TAG_ENB) != 0)
+          {
+            outb(MSG_ABORT_TAG, p->base + MSG_OUT + message_offset);
+          }
+          else
+          {
+            outb(MSG_ABORT, p->base + MSG_OUT + message_offset);
+          }
+          outb(message_offset + 1, p->base + MSG_LEN);
+          printk(KERN_WARNING "(scsi%d:%d:%d): Abort message sent.\n",
+                 p->host_no, TC_OF_SCB(scb));
+        }
+	else if (scb->flags & SCB_MSGOUT_WDTR)
 	{
-	  /*
-	   * This SCB had a zero length command, informing the sequencer
-	   * that we wanted to send a special message to this target.
-	   * We only do this for BUS_DEVICE_RESET messages currently.
-	   */
-	   if (scb->state & SCB_DEVICE_RESET)
-	   {
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk ("aic7xxx: (isr) sending bus device reset to target %d\n",
-          scsi_id);
-#endif
-	     outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
-	     outb(1, MSG_LEN + base);
-	   }
-	   else
-	   {
-	     panic("scsi%d: AWAITING_SCB for an SCB that does "
-		   "not have a waiting message.\n", p->host_no);
-	   }
-	}
-	break;
-
-      case IMMEDDONE:
-        scb_index = inb(SCB_TAG + base);
-	scb = (p->scb_array[scb_index]);
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: received IMMEDDONE for target %d, scb %d, state %d\n",
-         scsi_id, scb_index, scb->state);
-#endif
-        if (scb->state & SCB_DEVICE_RESET)
+          aic7xxx_construct_wdtr(p, message_offset, BUS_16_BIT);
+        }
+        else if (scb->flags & SCB_MSGOUT_SDTR)
         {
-          int found;
+          unsigned char target_scratch;
+          unsigned short ultra_enable;
+          int i, sxfr;
 
           /*
-           * Go back to async/narrow transfers and renegotiate.
+           * Pull the user defined setting from scratch RAM.
            */
-          aic7xxx_unbusy_target(scsi_id, channel, base);
-          p->needsdtr |= (p->needsdtr_copy & target_mask);
-          p->needwdtr |= (p->needwdtr_copy & target_mask);
-          p->sdtr_pending &= ~target_mask;
-          p->wdtr_pending &= ~target_mask;
-          scratch = inb(TARG_SCRATCH + base + scratch_offset);
-          scratch &= SXFR;
-          outb(scratch, TARG_SCRATCH + base + scratch_offset);
-          found = aic7xxx_reset_device(p, (int) scsi_id, channel);
-          printk(KERN_INFO "scsi%d: Bus Device Reset delivered, %d SCBs "
-                 "aborted.\n", p->host_no, found);
-          /* Indicate that we want to call aic7xxx_done_aborted_scbs() */
-          run_aborted_queue = TRUE;
+          target_scratch = inb(p->base + TARG_SCRATCH + scratch_offset);
+          sxfr = target_scratch & SXFR;
+          ultra_enable = inb(p->base + ULTRA_ENB) |
+              (inb(p->base + ULTRA_ENB + 1) << 8);
+          if (ultra_enable & target_mask)
+          {
+            sxfr |= 0x100;
+          }
+          for (i = 0; i < num_aic7xxx_syncrates; i++)
+          {
+            if (sxfr == aic7xxx_syncrates[i].rate)
+            break;
+          }
+          aic7xxx_construct_sdtr(p, message_offset,
+                                 aic7xxx_syncrates[i].period,
+                                 target_scratch & WIDEXFER ?
+                                 MAX_OFFSET_16BIT : MAX_OFFSET_8BIT);
         }
-        else
+        else 
         {
-          panic("scsi%d: Immediate complete for unknown operation.\n",
-                p->host_no);
-        }
-        break;
+          panic("aic7xxx: AWAITING_MSG for an SCB that does "
+                "not have a waiting message.");
+	}
+      }
+      break;
 
-      case DATA_OVERRUN:
+    case DATA_OVERRUN:
       {
-        unsigned int overrun;
-
-        scb = (p->scb_array[inb(base + SCB_TAG)]);
-        overrun = inb(base + STCNT0) | (inb(base + STCNT1) << 8) |
-                  (inb(base + STCNT2) << 16);
-        overrun =0x00FFFFFF - overrun;
-        printk(KERN_WARNING "scsi%d: data overrun of %d bytes detected; forcing "
-               "a retry.\n", p->host_no, overrun);
-        aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
-        break;
+	unsigned char scb_index = inb(p->base + SCB_TAG);
+        unsigned char lastphase = inb(p->base + LASTPHASE);
+	unsigned int i, overrun;
+
+	scb = (p->scb_data->scb_array[scb_index]);
+	overrun = inb(p->base + STCNT) | (inb(p->base + STCNT + 1) << 8) |
+  		  (inb(p->base + STCNT + 2) << 16);
+	overrun = 0x00FFFFFF - overrun;
+	printk(KERN_WARNING "(scsi%d:%d:%d) Data overrun of %d bytes detected "
+               "in %s phase, tag %d; forcing a retry.\n",
+               p->host_no, TC_OF_SCB(scb), overrun,
+               lastphase == P_DATAIN ? "Data-In" : "Data-Out",
+               scb->hscb->tag);
+        printk(KERN_WARNING "%s seen Data Phase.  Length = %d, NumSGs = %d.\n",
+               inb(p->base + SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+               aic7xxx_length(scb->cmd, 0), scb->sg_count);
+        for (i = 0; i < scb->sg_count; i++)
+        {
+          printk(KERN_INFO "     sg[%d] - Addr 0x%x : Length %d\n",
+                 i, scb->sg_list[i].address, scb->sg_list[i].length);
+        }
+	/*
+	 * XXX - What do we really want to do on an overrun?  The
+	 *       mid-level SCSI code should handle this, but for now,
+	 *       we'll just indicate that the command should retried.
+	 */
+	aic7xxx_error(scb->cmd) = DID_RETRY_COMMAND;
       }
+      break;
 
-#if AIC7XXX_NOT_YET
-      /* XXX Fill these in later */
-      case MESG_BUFFER_BUSY:
-        break;
-      case MSGIN_PHASEMIS:
-        break;
-#endif
+/* #if AIC7XXX_NOT_YET */
+    /* XXX Fill these in later */
+    case MSG_BUFFER_BUSY:
+      printk("aic7xxx: Message buffer busy.\n");
+      break;
+    case MSGIN_PHASEMIS:
+      printk("aic7xxx: Message-in phasemis.\n");
+      break;
+/*#endif */
+
+    case ABORT_CMDCMPLT:
+      /* This interrupt serves to pause the sequencer until we can clean
+       * up the QOUTFIFO allowing us to handle any abort SCBs that may
+       * completed yet still have an SCB in the QINFIFO or waiting for
+       * selection queue.  By the time we get here, we should have
+       * already cleaned up the queues, so all we need to do is unpause
+       * the sequencer.
+       */
+      break;
+
+    default:		   /* unknown */
+      printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
+             p->host_no, intstat, inb(p->base + SCSISIGI));
+      break;
+  }
+
+  /*
+   * Clear the sequencer interrupt and unpause the sequencer.
+   */
+  outb(CLRSEQINT, p->base + CLRINT);
+  unpause_sequencer(p, /* unpause always */ TRUE);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_scsiint
+ *
+ * Description:
+ *   Interrupt handler for SCSI interrupts (SCSIINT).
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat)
+{
+  unsigned char scb_index;
+  unsigned char status;
+  struct aic7xxx_scb *scb;
+
+  scb_index = inb(p->base + SCB_TAG);
+  status = inb(p->base + SSTAT1);
 
-      default:               /* unknown */
-	printk(KERN_WARNING "scsi%d: SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
-	       p->host_no, intstat, inb(SCSISIGI + base));
-	break;
+  if (scb_index < p->scb_data->numscbs)
+  {
+    scb = p->scb_data->scb_array[scb_index];
+    if ((scb->flags & SCB_ACTIVE) == 0)
+    {
+      scb = NULL;
     }
+  }
+  else
+  {
+    scb = NULL;
+  }
 
+  if ((status & SCSIRSTI) != 0)
+  {
+    char channel;
+
+    channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
+
+    printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
+           p->host_no, channel);
     /*
-     * Clear the sequencer interrupt and unpause the sequencer.
+     * Go through and abort all commands for the channel, but do not
+     * reset the channel again.
      */
-    outb(CLRSEQINT, CLRINT + base);
-    UNPAUSE_SEQUENCER(p);
+    aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
+    scb = NULL;
   }
-
-  if (intstat & SCSIINT)
+  else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
   {
-    int status = inb(SSTAT1 + base);
-    scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
-    channel = 'A';
-    if (inb(SBLKCTL + base) & SELBUSB)
-    {
-      channel = 'B';
+    /*
+     * First look at what phase we were last in.  If it's message-out,
+     * chances are pretty good that the bus free was in response to
+     * one of our abort requests.
+     */
+    unsigned char lastphase = inb(p->base + LASTPHASE);
+    unsigned char target = (inb(p->base + SAVED_TCL) >> 4) & 0x0F;
+    char channel = (inb(p->base + SBLKCTL) & SELBUSB) ? 'B' : 'A';
+    int printerror = TRUE;
+
+    outb(0, p->base + SCSISEQ);
+    if (lastphase == P_MESGOUT)
+    {
+      unsigned char sindex;
+      unsigned char message;
+
+      sindex = inb(p->base + SINDEX);
+      message = inb(p->base + sindex - 1);
+
+      if (message == MSG_ABORT)
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort completed.\n",
+                   p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), SCB_LIST_NULL);
+        aic7xxx_run_done_queue(p, /* complete */ TRUE);
+        scb = NULL;
+        printerror = 0;
+      }
+      else if (message == MSG_ABORT_TAG)
+      {
+        printk(KERN_WARNING "(scsi%d:%d:%d) SCB %d abort Tag completed.\n",
+                   p->host_no, TC_OF_SCB(scb), scb->hscb->tag);
+        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), scb->hscb->tag);
+        aic7xxx_run_done_queue(p, /* complete */ TRUE);
+        scb = NULL;
+        printerror = 0;
+      }
+      else if (message == MSG_BUS_DEV_RESET)
+      {
+        aic7xxx_handle_device_reset(p, target, channel);
+        scb = NULL;
+        printerror = 0;
+      }
     }
+    if (printerror != 0)
+    {
+      if (scb != NULL)
+      {
+        unsigned char tag;
 
-    scb_index = inb(SCB_TAG + base);
-    scb = (p->scb_array[scb_index]);
-    if (status & SCSIRSTI)
+        if ((scb->hscb->control & TAG_ENB) != 0)
+        {
+          tag = scb->hscb->tag;
+        }
+        else
+        {
+          tag = SCB_LIST_NULL;
+        }
+        aic7xxx_reset_device(p, target, channel, SCB_LUN(scb), tag);
+      }
+      else
+      {
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+      }
+      printk(KERN_WARNING "scsi%d: Unexpected busfree, LASTPHASE = 0x%x, "
+             "SEQADDR = 0x%x\n", p->host_no, lastphase,
+             (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+    }
+    outb(inb(p->base + SIMODE1) & ~ENBUSFREE, p->base + SIMODE1);
+    outb(CLRBUSFREE, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    restart_sequencer(p);
+  }
+  else if ((status & SELTO) != 0)
+  {
+    unsigned char scbptr;
+    unsigned char nextscb;
+    Scsi_Cmnd *cmd;
+
+    scbptr = inb(p->base + WAITING_SCBH);
+    outb(scbptr, p->base + SCBPTR);
+    scb_index = inb(p->base + SCB_TAG);
+
+    scb = NULL;
+    if (scb_index < p->scb_data->numscbs)
     {
-      PAUSE_SEQUENCER(p);
-      printk(KERN_WARNING "scsi%d: SCSIINT - Someone reset channel %c.\n",
-             p->host_no, channel);
-      /*
-       * Go through and abort all commands for the channel, but do not
-       * reset the channel again.
-       */
-      aic7xxx_reset_channel(p, channel, FALSE);
-      run_aborted_queue = TRUE;
+      scb = p->scb_data->scb_array[scb_index];
+      if ((scb->flags & SCB_ACTIVE) == 0)
+      {
+        scb = NULL;
+      }
+    }
+    if (scb == NULL)
+    {
+      printk(KERN_WARNING "scsi%d: Referenced SCB %d not valid during SELTO.\n",
+             p->host_no, scb_index);
+      printk(KERN_WARNING "        SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x "
+             "SSTAT1 = 0x%x\n", inb(p->base + SCSISEQ),
+             inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
+             inb(p->base + SSTAT0), inb(p->base + SSTAT1));
     }
-    else if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+    else
     {
-      printk(KERN_WARNING "scsi%d: SCSIINT - No command for SCB.\n", p->host_no);
       /*
-       * Turn off the interrupt and set status to zero, so that it
-       * falls through the rest of the SCSIINT code.
+       * XXX - If we queued an abort tag, go clean up the disconnected list.
+       */
+      cmd = scb->cmd;
+      cmd->result = (DID_TIME_OUT << 16);
+
+      /*
+       * Clear an pending messages for the timed out
+       * target and mark the target as free.
+       */
+      outb(0, p->base + MSG_LEN);
+      aic7xxx_index_busy_target(p, cmd->target,
+          cmd->channel ? 'B': 'A', /*unbusy*/ TRUE);
+      outb(0, p->base + SCB_CONTROL);
+
+      /*
+       * Shift the waiting for selection queue forward
+       */
+      nextscb = inb(p->base + SCB_NEXT);
+      outb(nextscb, p->base + WAITING_SCBH);
+
+      /*
+       * Put this SCB back on the free list.
        */
-      outb(status, CLRSINT1 + base);
-      UNPAUSE_SEQUENCER(p);
-      outb(CLRSCSIINT, CLRINT + base);
+      aic7xxx_add_curscb_to_free_list(p);
+    }
+    /*
+     * Stop the selection.
+     */
+    outb(0, p->base + SCSISEQ);
+    outb(CLRSELTIMEO | CLRBUSFREE, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    restart_sequencer(p);
+  }
+  else if (scb == NULL)
+  {
+    printk(KERN_WARNING "scsi%d: aic7xxx_isr - referenced scb not valid "
+           "during scsiint 0x%x scb(%d)\n"
+           "      SIMODE0 0x%x, SIMODE1 0x%x, SSTAT0 0x%x, SEQADDR 0x%x\n",
+           p->host_no, status, scb_index, inb(p->base + SIMODE0),
+           inb(p->base + SIMODE1), inb(p->base + SSTAT0),
+           (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+    /*
+     * Turn off the interrupt and set status to zero, so that it
+     * falls through the rest of the SCSIINT code.
+     */
+    outb(status, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    unpause_sequencer(p, /* unpause always */ TRUE);
+    scb = NULL;
+  }
+  else if (status & SCSIPERR)
+  {
+    /*
+     * Determine the bus phase and queue an appropriate message.
+     */
+    char  *phase;
+    Scsi_Cmnd *cmd;
+    unsigned char mesg_out = MSG_NOOP;
+    unsigned char lastphase = inb(p->base + LASTPHASE);
+
+    cmd = scb->cmd;
+    switch (lastphase)
+    {
+      case P_DATAOUT:
+        phase = "Data-Out";
+        break;
+      case P_DATAIN:
+        phase = "Data-In";
+        mesg_out = MSG_INITIATOR_DET_ERR;
+        break;
+      case P_COMMAND:
+        phase = "Command";
+        break;
+      case P_MESGOUT:
+        phase = "Message-Out";
+        break;
+      case P_STATUS:
+        phase = "Status";
+        mesg_out = MSG_INITIATOR_DET_ERR;
+        break;
+      case P_MESGIN:
+        phase = "Message-In";
+        mesg_out = MSG_PARITY_ERROR;
+        break;
+      default:
+        phase = "unknown";
+        break;
+    }
+
+    /*
+     * A parity error has occurred during a data
+     * transfer phase. Flag it and continue.
+     */
+    printk(KERN_WARNING "(scsi%d:%d:%d) Parity error during phase %s.\n",
+           p->host_no, TC_OF_SCB(scb), phase);
+
+    /*
+     * We've set the hardware to assert ATN if we get a parity
+     * error on "in" phases, so all we need to do is stuff the
+     * message buffer with the appropriate message.  "In" phases
+     * have set mesg_out to something other than MSG_NOP.
+     */
+    if (mesg_out != MSG_NOOP)
+    {
+      outb(mesg_out, p->base + MSG_OUT);
+      outb(1, p->base + MSG_LEN);
       scb = NULL;
     }
-    else if (status & SCSIPERR)
+    else
     {
-      char  *phase;
-      unsigned char mesg_out = MSG_NOP;
-      unsigned char lastphase = inb(LASTPHASE + base);
+      /*
+       * Should we allow the target to make this decision for us?
+       */
+      cmd->result = DID_RETRY_COMMAND << 16;
+    }
+    outb(CLRSCSIPERR, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+  }
+  else
+  {
+    /*
+     * We don't know what's going on. Turn off the
+     * interrupt source and try to continue.
+     */
+    printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
+    outb(status, p->base + CLRSINT1);
+    outb(CLRSCSIINT, p->base + CLRINT);
+    unpause_sequencer(p, /* unpause always */ TRUE);
+    scb = NULL;
+  }
+  if (scb != NULL)
+  {
+    aic7xxx_done(p, scb);
+    aic7xxx_done_cmds_complete(p);
+  }
+}
 
-      cmd = scb->cmd;
-      switch (lastphase)
-      {
-        case P_DATAOUT:
-          phase = "Data-Out";
-          break;
-        case P_DATAIN:
-          phase = "Data-In";
-          mesg_out = MSG_INITIATOR_DET_ERROR;
-          break;
-        case P_COMMAND:
-          phase = "Command";
-          break;
-        case P_MESGOUT:
-          phase = "Message-Out";
-          break;
-        case P_STATUS:
-          phase = "Status";
-          mesg_out = MSG_INITIATOR_DET_ERROR;
-          break;
-        case P_MESGIN:
-          phase = "Message-In";
-          mesg_out = MSG_MSG_PARITY_ERROR;
-          break;
-        default:
-          phase = "unknown";
-          break;
-      }
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_isr
+ *
+ * Description:
+ *   SCSI controller interrupt handler.
+ *
+ *   NOTE: Since we declared this using SA_INTERRUPT, interrupts should
+ *         be disabled all through this function unless we say otherwise.
+ *-F*************************************************************************/
+static void
+aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct aic7xxx_host *p;
+  unsigned char intstat;
+  unsigned long flags;
 
-      /*
-       * A parity error has occurred during a data
-       * transfer phase. Flag it and continue.
-       */
-      printk(KERN_WARNING "scsi%d: Parity error during phase %s on target %d, "
-             "channel %d, lun %d.\n", p->host_no, phase,
-             cmd->target, cmd->channel & 0x01, cmd->lun & 0x07);
+  p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
 
-      /*
-       * We've set the hardware to assert ATN if we get a parity
-       * error on "in" phases, so all we need to do is stuff the
-       * message buffer with the appropriate message. In phases
-       * have set mesg_out to something other than MSG_NOP.
-       */
-      if (mesg_out != MSG_NOP)
-      {
-        outb(mesg_out, MSG0 + base);
-        outb(1, MSG_LEN + base);
-        cmd->result = DID_PARITY << 16;
-      }
-      else
-      {
-        /*
-         * Should we allow the target to make this decision for us?
-         */
-        cmd->result = DID_RETRY_COMMAND << 16;
-      }
-      aic7xxx_done(p, scb);
+  /*
+   * Search for the host with a pending interrupt.  If we can't find
+   * one, then we've encountered a spurious interrupt.
+   */
+  while ((p != NULL) && !(inb(p->base + INTSTAT) & INT_PEND))
+  {
+    if (p->next == NULL)
+    {
+      p = NULL;
     }
-    else if (status & SELTO)
+    else
     {
-      unsigned char waiting;
+      p = (struct aic7xxx_host *) p->next->hostdata;
+    }
+  }
 
-      cmd = scb->cmd;
+  if (p == NULL)
+    return;
 
-      cmd->result = (DID_TIME_OUT << 16);
-      /*
-       * Clear an pending messages for the timed out
-       * target and mark the target as free.
-       */
-      ha_flags = inb(FLAGS + base);
-      outb(0, MSG_LEN + base);
-      aic7xxx_unbusy_target(scsi_id, channel, base);
-      /*
-       * Stop the selection.
-       */
-      outb(0, SCSISEQ + base);
-      outb(0, SCB_CONTROL + base);
-      outb(CLRSELTIMEO, CLRSINT1 + base);
-      outb(CLRSCSIINT, CLRINT + base);
+  /*
+   * Handle all the interrupt sources - especially for SCSI
+   * interrupts, we won't get a second chance at them.
+   */
+  intstat = inb(p->base + INTSTAT);
 
-      /*
-       * Shift the waiting for selection queue forward
-       */
-      waiting = inb(WAITING_SCBH + base);
-      outb(waiting, SCBPTR + base);
-      waiting = inb(SCB_NEXT + base);
-      outb(waiting, WAITING_SCBH + base);
+  /*
+   * Keep track of interrupts for /proc/scsi
+   */
+  p->isr_count++;
 
-      RESTART_SEQUENCER(p);
-      aic7xxx_done(p, scb);
-    }
-    else if (!(status & BUSFREE))
+  if (!(p->flags & A_SCANNED) && (p->isr_count == 1))
+  {
+    /*
+     * We must only have one card at this IRQ and it must have been
+     * added to the board data before the spurious interrupt occurred.
+     * It is sufficient that we check isr_count and not the spurious
+     * interrupt count.
+     */
+    printk("scsi%d: Encountered spurious interrupt.\n", p->host_no);
+    if (intstat)
     {
-      /*
-       * We don't know what's going on. Turn off the
-       * interrupt source and try to continue.
-       */
-      printk(KERN_WARNING "aic7xxx: SSTAT1(0x%x).\n", status);
-      outb(status, CLRSINT1 + base);
-      UNPAUSE_SEQUENCER(p);
-      outb(CLRSCSIINT, CLRINT + base);
+      /* Try clearing all interrupts. */
+      outb(CLRBRKADRINT | CLRSCSIINT | CLRCMDINT | CLRSEQINT, p->base + CLRINT);
     }
+    return;
+  }
+
+  if (p->flags & IN_ISR)
+  {
+    printk(KERN_WARNING "scsi%d: Warning!! Interrupt routine called reentrantly!\n",
+           p->host_no);
+    return;
   }
 
-  if (run_aborted_queue)
-    aic7xxx_done_aborted_scbs(p);
+  /*
+   * Indicate that we're in the interrupt handler.
+   */
+  save_flags(flags);
+  cli();
+  p->flags |= IN_ISR;
 
   if (intstat & CMDCMPLT)
   {
-    int complete;
+    struct aic7xxx_scb *scb = NULL;
+    Scsi_Cmnd *cmd;
+    unsigned char qoutcnt;
+    unsigned char scb_index;
+    int i, interrupts_cleared = 0;
 
     /*
      * The sequencer will continue running when it
      * issues this interrupt. There may be >1 commands
      * finished, so loop until we've processed them all.
      */
-    do {
-      complete = inb(QOUTFIFO + base);
+    qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
 
-      scb = (p->scb_array[complete]);
-      if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
-      {
-	printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d.\n"
-	       "       QOUTCNT %d, QINCNT %d, SCB state 0x%x, cmd 0x%lx, "
-               "pos(%d).\n", p->host_no, complete, inb(QOUTCNT + base),
-               inb(QINCNT + base), scb->state, (unsigned long) scb->cmd,
-               scb->position);
-	outb(CLRCMDINT, CLRINT + base);
-	continue;
-      }
-      cmd = scb->cmd;
-      cmd->result |= (aic7xxx_error(cmd) << 16);
-      if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
+#if 1
+  if (qoutcnt >= p->qfullcount - 1)
+    printk(KERN_WARNING "aic7xxx: Command complete near Qfull count, "
+           "qoutcnt = %d.\n", qoutcnt);
+#endif
+    while (qoutcnt > 0)
+    {
+      for (i = 0; i < qoutcnt; i++)
       {
-        /*
-         * Got sense information.
-         */
-	cmd->flags &= ASKED_FOR_SENSE;
+        scb_index = inb(p->base + QOUTFIFO);
+        scb = p->scb_data->scb_array[scb_index];
+        if (scb == NULL)
+        {
+	  printk(KERN_WARNING "scsi%d: CMDCMPLT with invalid SCB index %d, "
+	         "QOUTCNT %d, QINCNT %d\n", p->host_no, scb_index,
+                 inb(p->base + QOUTCNT), inb(p->base + QINCNT));
+          continue;
+        }
+        else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+        {
+	  printk(KERN_WARNING "scsi%d: CMDCMPLT without command for SCB %d, "
+	         "QOUTCNT %d, QINCNT %d, SCB flags 0x%x, cmd 0x%lx\n",
+                 p->host_no, scb_index, inb(p->base + QOUTCNT),
+                 inb(p->base + QINCNT), scb->flags, (unsigned long) scb->cmd);
+	  continue;
+        }
+        cmd = scb->cmd;
+        if (scb->hscb->residual_SG_segment_count != 0)
+        {
+          aic7xxx_calculate_residual(p, scb);
+        }
+        if ((scb->flags & SCB_QUEUED_ABORT) != 0)
+        {
+          /*
+           * Have to clean up any possible entries in the
+           * waiting queue and the QINFIFO.
+           */
+          int target;
+          char channel;
+          int lun;
+          unsigned char tag;
+
+          tag = SCB_LIST_NULL;
+          target = cmd->target;
+          lun = cmd->lun;
+          channel = (scb->hscb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+          if (scb->hscb->control & TAG_ENB)
+          {
+            tag = scb->hscb->tag;
+          }
+          aic7xxx_reset_device(p, target, channel, lun, tag);
+          /*
+           * Run the done queue, but don't complete the commands; we
+           * do this once at the end of the loop.
+           */
+          aic7xxx_run_done_queue(p, /*complete*/ FALSE);
+        }
+        cmd->result |= (aic7xxx_error(cmd) << 16);
+        p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
+        aic7xxx_done(p, scb);
       }
-      p->device_status[TARGET_INDEX(cmd)].flags |= DEVICE_SUCCESS;
-
       /*
        * Clear interrupt status before checking the output queue again.
        * This eliminates a race condition whereby a command could
@@ -3242,56 +3941,152 @@
        * so notification of the command being complete never made it
        * back up to the kernel.
        */
-      outb(CLRCMDINT, CLRINT + base);
-      aic7xxx_done(p, scb);
+      outb(CLRCMDINT, p->base + CLRINT);
+      interrupts_cleared++;
+      qoutcnt = inb(p->base + QOUTCNT) & p->qcntmask;
+    }
 
-#ifdef AIC7XXX_PROC_STATS
-      /*
-       * XXX: we should actually know how much actually transferred
-       * XXX: for each command, but apparently that's too difficult.
-       */
-      actual = aic7xxx_length(cmd, 0);
-      if (!(cmd->flags & WAS_SENSE) && (actual > 0))
+    if (interrupts_cleared == 0)
+    {
+      outb(CLRCMDINT, p->base + CLRINT);
+    }
+
+    aic7xxx_done_cmds_complete(p);
+  }
+
+  if (intstat & BRKADRINT)
+  {
+    int i;
+    unsigned char errno = inb(p->base + ERROR);
+
+    printk(KERN_ERR "scsi%d: BRKADRINT error(0x%x):\n", p->host_no, errno);
+    for (i = 0; i < NUMBER(hard_error); i++)
+    {
+      if (errno & hard_error[i].errno)
       {
-        struct aic7xxx_xferstats *sp;
-        long *ptr;
-        int x;
+        printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
+      }
+    }
+    printk("scsi%d: BRKADRINT, error 0x%x, seqaddr 0x%x.\n", p->host_no,
+           inb(p->base + ERROR),
+           (inb(p->base + SEQADDR1) << 8) | inb(p->base + SEQADDR0));
+    aic7xxx_reset_device(p, ALL_TARGETS, ALL_CHANNELS, ALL_LUNS, SCB_LIST_NULL);
+    aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+  }
+
+  if (intstat & SEQINT)
+  {
+    aic7xxx_handle_seqint(p, intstat);
+  }
+
+  if (intstat & SCSIINT)
+  {
+    aic7xxx_handle_scsiint(p, intstat);
+  }
+
+  if (p->waiting_scbs.head != NULL)
+  {
+    aic7xxx_run_waiting_queues(p);
+  }
+
+  p->flags &= ~IN_ISR;
+  restore_flags(flags);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_device_queue_depth
+ *
+ * Description:
+ *   Determines the queue depth for a given device.  There are two ways
+ *   a queue depth can be obtained for a tagged queueing device.  One
+ *   way is the default queue depth which is determined by whether
+ *   AIC7XXX_CMDS_PER_LUN is defined.  If it is defined, then it is used
+ *   as the default queue depth.  Otherwise, we use either 4 or 8 as the
+ *   default queue depth (dependent on the number of hardware SCBs).
+ *   The other way we determine queue depth is through the use of the
+ *   aic7xxx_tag_info array which is enabled by defining
+ *   AIC7XXX_TAGGED_QUEUEING_BY_DEVICE.  This array can be initialized
+ *   with queue depths for individual devices.  It also allows tagged
+ *   queueing to be [en|dis]abled for a specific adapter.
+ *-F*************************************************************************/
+static void
+aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
+{
+  int default_depth = 2;
+
+  device->queue_depth = default_depth;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+  if (device->tagged_supported)
+  {
+    unsigned short target_mask;
+    int tag_enabled = TRUE;
 
-        sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
-        sp->xfers++;
+    target_mask = (1 << (device->id | (device->channel << 3)));
+
+#ifdef AIC7XXX_CMDS_PER_LUN
+    default_depth = AIC7XXX_CMDS_PER_LUN;
+#else
+    if (p->scb_data->maxhscbs <= 4)
+    {
+      default_depth = 4;  /* Not many SCBs to work with. */
+    }
+    else
+    {
+      default_depth = 8;
+    }
+#endif
+ 
+    if (!(p->discenable & target_mask))
+    {
+      printk(KERN_INFO "(scsi%d:%d:%d) Disconnection disabled, unable to "
+             "enable tagged queueing.\n",
+             p->host_no, device->id, device->channel);
+    }
+    else
+    {
+#ifndef AIC7XXX_TAGGED_QUEUEING_BY_DEVICE
+      device->queue_depth = default_depth;
+#else
+      if (p->instance > NUMBER(aic7xxx_tag_info))
+      {
+        device->queue_depth = default_depth;
+      }
+      else
+      {
+        unsigned char  tindex;
 
-        if (cmd->request.cmd == WRITE)
+        tindex = device->id | (device->channel << 3);
+        if (aic7xxx_tag_info[p->instance].tag_commands[tindex] < 0)
         {
-          sp->w_total++;
-          sp->w_total512 += (actual >> 9);
-          ptr = sp->w_bins;
+          tag_enabled = FALSE;
+          device->queue_depth = 2;  /* Tagged queueing is disabled. */
         }
-        else
+        else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
         {
-          sp->r_total++;
-          sp->r_total512 += (actual >> 9);
-          ptr = sp->r_bins;
+          device->queue_depth = default_depth;
         }
-        for (x = 9; x <= 17; x++)
+        else
         {
-          if (actual < (1 << x))
-          {
-            ptr[x - 9]++;
-            break;
-          }
+          device->queue_depth =
+            aic7xxx_tag_info[p->instance].tag_commands[tindex];
         }
-        if (x > 17)
+      }
+#endif
+      if ((device->tagged_queue == 0) && tag_enabled)
+      {
+        if (aic7xxx_verbose)
         {
-          ptr[x - 9]++;
+    	  printk(KERN_INFO "(scsi%d:%d:%d) Enabled tagged queuing, "
+    	         "queue depth %d.\n", p->host_no,
+    	         device->id, device->channel, device->queue_depth);
         }
+        device->tagged_queue = 1;
+        device->current_tag = SCB_LIST_NULL;
       }
-#endif /* AIC7XXX_PROC_STATS */
-
-    } while (inb(QOUTCNT + base) & p->qcntmask);
+    }
   }
-  aic7xxx_done_cmds_complete(p);
-  p->flags &= ~IN_ISR;
-  aic7xxx_run_waiting_queues(p);
+#endif
 }
 
 /*+F*************************************************************************
@@ -3306,59 +4101,18 @@
  *   algorithm for determining the queue depth based on the maximum
  *   SCBs for the controller.
  *-F*************************************************************************/
-static void aic7xxx_select_queue_depth(struct Scsi_Host *host,
+static void
+aic7xxx_select_queue_depth(struct Scsi_Host *host,
     Scsi_Device *scsi_devs)
 {
-  Scsi_Device *device = scsi_devs;
-  int tq_depth = 2;
+  Scsi_Device *device;
   struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
 
-#ifdef AIC7XXX_CMDS_PER_LUN
-  tq_depth = AIC7XXX_CMDS_PER_LUN;
-#else
-  {
-    if (p->maxhscbs <= 4)
-    {
-      tq_depth = 4;  /* Not many SCBs to work with. */
-    }
-    else
-    {
-      tq_depth = 8;
-    }
-  }
-#endif
-
   for (device = scsi_devs; device != NULL; device = device->next)
   {
     if (device->host == host)
     {
-      device->queue_depth = 2;
-#ifdef AIC7XXX_TAGGED_QUEUEING
-      if (device->tagged_supported)
-      {
-        unsigned short target_mask = (1 << device->id) | device->channel;
-
-        if (!(p->discenable & target_mask))
-        {
-          printk(KERN_INFO "scsi%d: Disconnection disabled, unable to enable "
-                 "tagged queueing for target %d, channel %d, LUN %d.\n",
-                 host->host_no, device->id, device->channel, device->lun);
-        }
-        else
-        {
-          device->queue_depth = tq_depth;
-          if (device->tagged_queue == 0)
-          {
-            printk(KERN_INFO "scsi%d: Enabled tagged queuing for target %d, "
-	           "channel %d, LUN %d, queue depth %d.\n", host->host_no,
-                   device->id, device->channel, device->lun,
-                   device->queue_depth);
-            device->tagged_queue = 1;
-            device->current_tag = SCB_LIST_NULL;
-          }
-        }
-      }
-#endif
+      aic7xxx_device_queue_depth(p, device);
     }
   }
 }
@@ -3385,7 +4139,7 @@
  *   The fourth byte's lowest bit seems to be an enabled/disabled
  *   flag (rest of the bits are reserved?).
  *-F*************************************************************************/
-static aha_type
+static aha_chip_type
 aic7xxx_probe(int slot, int base, aha_status_type *bios)
 {
   int i;
@@ -3394,7 +4148,7 @@
   static struct {
     int n;
     unsigned char signature[sizeof(buf)];
-    aha_type type;
+    aha_chip_type type;
     int bios_disabled;
   } AIC7xxx[] = {
     { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771, FALSE }, /* host adapter 274x */
@@ -3433,7 +4187,8 @@
 	return (AIC7xxx[i].type);
       }
 
-      printk("aic7xxx: Disabled at slot %d, ignored.\n", slot);
+      printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
+             "disabled at slot %d, ignored.\n", slot);
     }
   }
 
@@ -3460,10 +4215,9 @@
  *   useful in that it gives us an 800 nsec timer.  After a read from the
  *   SEECTL_2840 register the timing flag is cleared and goes high 800 nsec
  *   later.
- *
  *-F*************************************************************************/
 static int
-read_2840_seeprom(int base, struct seeprom_config *sc)
+read_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
 {
   int i = 0, k = 0;
   unsigned char temp;
@@ -3476,11 +4230,11 @@
   struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
 
 #define CLOCK_PULSE(p) \
-  while ((inb(STATUS_2840 + base) & EEPROM_TF) == 0)	\
+  while ((inb(p->base + STATUS_2840) & EEPROM_TF) == 0)	\
   {						\
     ;  /* Do nothing */				\
   }						\
-  (void) inb(SEECTL_2840 + base);
+  (void) inb(p->base + SEECTL_2840);
 
   /*
    * Read the first 32 registers of the seeprom.  For the 2840,
@@ -3493,8 +4247,8 @@
     /*
      * Send chip select for one clock cycle.
      */
-    outb(CK_2840 | CS_2840, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
+    outb(CK_2840 | CS_2840, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
 
     /*
      * Now we're ready to send the read command followed by the
@@ -3503,11 +4257,11 @@
     for (i = 0; i < seeprom_read.len; i++)
     {
       temp = CS_2840 | seeprom_read.bits[i];
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
     }
     /*
      * Send the 6 bit address (MSB first, LSB last).
@@ -3517,11 +4271,11 @@
       temp = k;
       temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
       temp = CS_2840 | temp;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
     }
 
     /*
@@ -3533,12 +4287,12 @@
     for (i = 0; i <= 16; i++)
     {
       temp = CS_2840;
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
       temp = temp ^ CK_2840;
-      seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840 + base) & DI_2840);
-      outb(temp, SEECTL_2840 + base);
-      CLOCK_PULSE(base);
+      seeprom[k] = (seeprom[k] << 1) | (inb(p->base + STATUS_2840) & DI_2840);
+      outb(temp, p->base + SEECTL_2840);
+      CLOCK_PULSE(p);
     }
     /*
      * The serial EEPROM has a checksum in the last word.  Keep a
@@ -3554,12 +4308,12 @@
     /*
      * Reset the chip select for the next command cycle.
      */
-    outb(0, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
-    outb(CK_2840, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
-    outb(0, SEECTL_2840 + base);
-    CLOCK_PULSE(base);
+    outb(0, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
+    outb(CK_2840, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
+    outb(0, p->base + SEECTL_2840);
+    CLOCK_PULSE(p);
   }
 
 #if 0
@@ -3588,6 +4342,53 @@
 
 /*+F*************************************************************************
  * Function:
+ *   acquire_seeprom
+ *
+ * Description:
+ *   Acquires access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static inline int
+acquire_seeprom(struct aic7xxx_host *p)
+{
+  int wait;
+
+  /*
+   * Request access of the memory port.  When access is
+   * granted, SEERDY will go high.  We use a 1 second
+   * timeout which should be near 1 second more than
+   * is needed.  Reason: after the 7870 chip reset, there
+   * should be no contention.
+   */
+  outb(SEEMS, p->base + SEECTL);
+  wait = 1000;  /* 1000 msec = 1 second */
+  while ((wait > 0) && ((inb(p->base + SEECTL) & SEERDY) == 0))
+  {
+    wait--;
+    udelay(1000);  /* 1 msec */
+  }
+  if ((inb(p->base + SEECTL) & SEERDY) == 0)
+  {
+    outb(0, p->base + SEECTL);
+    return (0);
+  }
+  return (1);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   release_seeprom
+ *
+ * Description:
+ *   Releases access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static inline void
+release_seeprom(struct aic7xxx_host *p)
+{
+  outb(0, p->base + SEECTL);
+}
+
+/*+F*************************************************************************
+ * Function:
  *   read_seeprom
  *
  * Description:
@@ -3625,7 +4426,7 @@
  *   first).  The clock cycling from low to high initiates the next data
  *   bit to be sent from the chip.
  *
- *   The 7870 interface to the 93C46 serial EEPROM is through the SEECTL
+ *   The 78xx interface to the 93C46 serial EEPROM is through the SEECTL
  *   register.  After successful arbitration for the memory port, the
  *   SEECS bit of the SEECTL register is connected to the chip select.
  *   The SEECK, SEEDO, and SEEDI are connected to the clock, data out,
@@ -3635,17 +4436,14 @@
  *   to this is when we first request access to the memory port.  The
  *   SEERDY goes high to signify that access has been granted and, for
  *   this case, has no implied timing.
- *
  *-F*************************************************************************/
 static int
-read_seeprom(int base, int offset, struct seeprom_config *sc,
-    seeprom_chip_type chip)
+read_seeprom(struct aic7xxx_host *p, int offset, unsigned short *scarray,
+    unsigned int len, seeprom_chip_type chip)
 {
   int i = 0, k;
-  unsigned long timeout;
   unsigned char temp;
   unsigned short checksum = 0;
-  unsigned short *seeprom = (unsigned short *) sc;
   struct seeprom_cmd {
     unsigned char len;
     unsigned char bits[3];
@@ -3653,43 +4451,33 @@
   struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
 
 #define CLOCK_PULSE(p) \
-  while ((inb(SEECTL + base) & SEERDY) == 0)	\
+  while ((inb(p->base + SEECTL) & SEERDY) == 0)	\
   {						\
     ;  /* Do nothing */				\
   }
 
   /*
-   * Request access of the memory port.  When access is
-   * granted, SEERDY will go high.  We use a 1 second
-   * timeout which should be near 1 second more than
-   * is needed.  Reason: after the 7870 chip reset, there
-   * should be no contention.
+   * Request access of the memory port.
    */
-  outb(SEEMS, SEECTL + base);
-  timeout = jiffies + 100;  /* 1 second timeout */
-  while ((jiffies < timeout) && ((inb(SEECTL + base) & SEERDY) == 0))
+  if (acquire_seeprom(p) == 0)
   {
-    ; /* Do nothing!  Wait for access to be granted.  */
-  }
-  if ((inb(SEECTL + base) & SEERDY) == 0)
-  {
-    outb(0, SEECTL + base);
     return (0);
   }
 
   /*
-   * Read the first 32 registers of the seeprom.  For the 7870,
-   * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers
-   * but only the first 32 are used by Adaptec BIOS.  The loop
-   * will range from 0 to 31.
+   * Read 'len' registers of the seeprom.  For the 7870, the 93C46
+   * SEEPROM is a 1024-bit device with 64 16-bit registers but only
+   * the first 32 are used by Adaptec BIOS.  Some adapters use the
+   * 93C56 SEEPROM which is a 2048-bit device.  The loop will range
+   * from 0 to 'len' - 1.
    */
-  for (k = 0; k < (sizeof(*sc) / 2); k++)
+  for (k = 0; k < len; k++)
   {
     /*
      * Send chip select for one clock cycle.
      */
-    outb(SEEMS | SEECK | SEECS, SEECTL + base);
-    CLOCK_PULSE(base);
+    outb(SEEMS | SEECK | SEECS, p->base + SEECTL);
+    CLOCK_PULSE(p);
 
     /*
      * Now we're ready to send the read command followed by the
@@ -3698,25 +4486,25 @@
     for (i = 0; i < seeprom_read.len; i++)
     {
       temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
     }
     /*
-     * Send the 6 bit address (MSB first, LSB last).
+     * Send the 6 or 8 bit address (MSB first, LSB last).
      */
     for (i = ((int) chip - 1); i >= 0; i--)
     {
       temp = k + offset;
       temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
       temp = SEEMS | SEECS | (temp << 1);
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
     }
 
     /*
@@ -3728,56 +4516,57 @@
     for (i = 0; i <= 16; i++)
     {
       temp = SEEMS | SEECS;
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
       temp = temp ^ SEECK;
-      seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL + base) & SEEDI);
-      outb(temp, SEECTL + base);
-      CLOCK_PULSE(base);
+      scarray[k] = (scarray[k] << 1) | (inb(p->base + SEECTL) & SEEDI);
+      outb(temp, p->base + SEECTL);
+      CLOCK_PULSE(p);
     }
 
     /*
-     * The serial EEPROM has a checksum in the last word.  Keep a
-     * running checksum for all words read except for the last
-     * word.  We'll verify the checksum after all words have been
-     * read.
+     * The serial EEPROM should have a checksum in the last word.
+     * Keep a running checksum for all words read except for the
+     * last word.  We'll verify the checksum after all words have
+     * been read.
      */
-    if (k < (sizeof(*sc) / 2) - 1)
+    if (k < (len - 1))
     {
-      checksum = checksum + seeprom[k];
+      checksum = checksum + scarray[k];
     }
 
     /*
      * Reset the chip select for the next command cycle.
      */
-    outb(SEEMS, SEECTL + base);
-    CLOCK_PULSE(base);
-    outb(SEEMS | SEECK, SEECTL + base);
-    CLOCK_PULSE(base);
-    outb(SEEMS, SEECTL + base);
-    CLOCK_PULSE(base);
+    outb(SEEMS, p->base + SEECTL);
+    CLOCK_PULSE(p);
+    outb(SEEMS | SEECK, p->base + SEECTL);
+    CLOCK_PULSE(p);
+    outb(SEEMS, p->base + SEECTL);
+    CLOCK_PULSE(p);
   }
 
   /*
    * Release access to the memory port and the serial EEPROM.
    */
-  outb(0, SEECTL + base);
+  release_seeprom(p);
 
 #if 0
-  printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
+  printk("Computed checksum 0x%x, checksum read 0x%x\n",
+         checksum, scarray[len - 1]);
   printk("Serial EEPROM:");
-  for (k = 0; k < (sizeof(*sc) / 2); k++)
+  for (k = 0; k < len; k++)
   {
     if (((k % 8) == 0) && (k != 0))
     {
       printk("\n              ");
     }
-    printk(" 0x%x", seeprom[k]);
+    printk(" 0x%x", scarray[k]);
   }
   printk("\n");
 #endif
 
-  if (checksum != sc->checksum)
+  if (checksum != scarray[len - 1])
   {
     return (0);
   }
@@ -3788,563 +4577,452 @@
 
 /*+F*************************************************************************
  * Function:
- *   detect_maxscb
+ *   write_brdctl
  *
  * Description:
- *   Detects the maximum number of SCBs for the controller and returns
- *   the count and a mask in config (config->maxscbs, config->qcntmask).
+ *   Writes a value to the BRDCTL register.
  *-F*************************************************************************/
-static void
-detect_maxscb(struct aic7xxx_host_config *config)
+static inline void
+write_brdctl(struct aic7xxx_host *p, unsigned char value)
 {
-  unsigned char sblkctl_reg;
-  int base, i;
-
-#ifdef AIC7XXX_PAGE_ENABLE
-  config->flags |= PAGE_ENABLED;
-#endif
-  base = config->base;
-  switch (config->type)
-  {
-    case AIC_7770:
-    case AIC_7771:
-    case AIC_284x:
-      /*
-       * Check for Rev C or E boards. Rev E boards can supposedly have
-       * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
-       * It's still not clear extactly what is different about the Rev E
-       * boards, but we think it allows 8 bit entries in the QOUTFIFO to
-       * support "paging" SCBs (more than 4 commands can be active at once).
-       *
-       * The Rev E boards have a read/write autoflush bit in the
-       * SBLKCTL register, while in the Rev C boards it is read only.
-       */
-      sblkctl_reg = inb(SBLKCTL + base) ^ AUTOFLUSHDIS;
-      outb(sblkctl_reg, SBLKCTL + base);
-      if (inb(SBLKCTL + base) == sblkctl_reg)
-      {
-        /*
-         * We detected a Rev E board, we allow paging on this board.
-         */
-        printk(KERN_INFO "aic7xxx: %s Rev E and subsequent.\n",
-               board_names[config->type]);
-	outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL + base);
-      }
-      else
-      {
-        /* Do not allow paging. */
-        config->flags &= ~PAGE_ENABLED;
-        printk(KERN_INFO "aic7xxx: %s Rev C and previous.\n",
-               board_names[config->type]);
-      }
-      break;
-
-    default:
-      break;
-  }
-
-  /*
-   * Walk the SCBs to determine how many there are.
-   */
-  i = 1;
-  outb(0, SCBPTR + base);
-  outb(0, SCBARRAY + base);
-
-  while (i < AIC7XXX_MAXSCB)
-  {
-    outb(i, SCBPTR + base);
-    outb(i, SCBARRAY + base);
-    if (inb(SCBARRAY + base) != i)
-      break;
-    outb(0, SCBPTR + base);
-    if (inb(SCBARRAY + base) != 0)
-      break;
-
-    outb(i, SCBPTR + base);      /* Clear the control byte. */
-    outb(0, SCBARRAY + base);
-
-    config->qcntmask |= i;       /* Update the count mask. */
-    i++;
-  }
-  outb(i, SCBPTR + base);   /* Ensure we clear the control bytes. */
-  outb(0, SCBARRAY + base);
-  outb(0, SCBPTR + base); 
-  outb(0, SCBARRAY + base);
-
-  config->maxhscbs = i;
-  config->qcntmask |= i;
-  if ((config->flags & PAGE_ENABLED) && (config->maxhscbs < AIC7XXX_MAXSCB))
-  {
-    config->maxscbs = AIC7XXX_MAXSCB;
-  }
-  else
-  {
-    config->flags &= ~PAGE_ENABLED;  /* Disable paging if we have 255 SCBs!. */
-    config->maxscbs = config->maxhscbs;
-  }
-
-  printk(KERN_INFO "aic7xxx: Memory check yields %d SCBs", config->maxhscbs);
-  if (config->flags & PAGE_ENABLED)
-    printk(", %d page-enabled SCBs.\n", config->maxscbs);
-  else
-    printk(", paging not enabled.\n");
+  unsigned char brdctl;
 
+  brdctl = BRDCS | BRDSTB;
+  outb(brdctl, p->base + BRDCTL);
+  brdctl |= value;
+  outb(brdctl, p->base + BRDCTL);
+  brdctl &= ~BRDSTB;
+  outb(brdctl, p->base + BRDCTL);
+  brdctl &= ~BRDCS;
+  outb(brdctl, p->base + BRDCTL);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_register
+ *   read_brdctl
  *
  * Description:
- *   Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *   Reads the BRDCTL register.
  *-F*************************************************************************/
-static int
-aic7xxx_register(Scsi_Host_Template *template,
-    struct aic7xxx_host_config *config)
+static inline unsigned char
+read_brdctl(struct aic7xxx_host *p)
 {
-  int i;
-  unsigned char sblkctl, flags = 0;
-  int max_targets;
-  int found = 1;
-  unsigned int sram, base;
-  unsigned char target_settings;
-  unsigned char scsi_conf, host_conf;
-  unsigned short ultraenable = 0;
-  int have_seeprom = FALSE;
-  struct Scsi_Host *host;
-  struct aic7xxx_host *p;
-  struct seeprom_config sc;
-
-  base = config->base;
+  outb(BRDRW | BRDCS, p->base + BRDCTL);
+  return (inb(p->base + BRDCTL));
+}
 
-  /*
-   * Lock out other contenders for our i/o space.
-   */
-  request_region(base, MAXREG - MINREG, "aic7xxx");
+/*+F*************************************************************************
+ * Function:
+ *   configure_termination
+ *
+ * Description:
+ *   Configures the termination settings on PCI adapters that have
+ *   SEEPROMs available.
+ *-F*************************************************************************/
+static void
+configure_termination(struct aic7xxx_host *p, unsigned char *sxfrctl1,
+    unsigned short adapter_control, unsigned char max_targ)
+{
+  unsigned char brdctl_int, brdctl_ext;
+  int internal50_present;
+  int internal68_present = 0;
+  int external_present = 0;
+  int eprom_present;
+  int high_on;
+  int low_on;
+  int old_verbose;
+
+  if (acquire_seeprom(p))
+  {
+    if (adapter_control & CFAUTOTERM)
+    {
+      old_verbose = aic7xxx_verbose;
+      printk(KERN_INFO "aic7xxx: Warning - detected auto-termination.  Please "
+                       "verify driver");
+      printk(KERN_INFO "         detected settings and use manual termination "
+                       "if necessary."); 
 
-  switch (config->type)
-  {
-    case AIC_7770:
-    case AIC_7771:
-      /*
-       * Use the boot-time option for the interrupt trigger type.  If not
-       * supplied (-1), then we use BIOS settings to determine the interrupt
-       * trigger type (level or edge) and use this value for pausing and
-       * unpausing the sequencer.
-       */
-      switch (aic7xxx_irq_trigger)
-      {
-        case  0: config->unpause = INTEN;          /* Edge */
-                 break;
-        case  1: config->unpause = IRQMS | INTEN;  /* Level */
-                 break;
-        case -1:
-        default: config->unpause = (inb(HCNTRL + base) & IRQMS) | INTEN; 
-                 break;
-      }
-      config->pause = config->unpause | PAUSE;
+      /* Configure auto termination. */
+      outb(SEECS | SEEMS, p->base + SEECTL);
 
       /*
-       * For some 274x boards, we must clear the CHIPRST bit and pause
-       * the sequencer. For some reason, this makes the driver work.
-       * For 284x boards, we give it a CHIPRST just like the 294x boards.
+       * First read the status of our cables.  Set the rom bank to
+       * 0 since the bank setting serves as a multiplexor for the
+       * cable detection logic.  BRDDAT5 controls the bank switch.
        */
-      outb(config->pause | CHIPRST, HCNTRL + base);
-      aic7xxx_delay(1);
-      if (inb(HCNTRL + base) & CHIPRST)
-      {
-	printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
-      }
-      outb(config->pause, HCNTRL + base);
+      write_brdctl(p, 0);
 
       /*
-       * Just to be on the safe side with the 274x, we will re-read the irq
-       * since there was some issue about resetting the board.
+       * Now read the state of the internal connectors.  The
+       * bits BRDDAT6 and BRDDAT7 are 0 when cables are present
+       * set when cables are not present (BRDDAT6 is INT50 and
+       * BRDDAT7 is INT68).
        */
-      config->irq = inb(INTDEF + base) & 0x0F;
-      if ((config->type == AIC_7771) &&
-          (inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
-      {
-        config->bios = AIC_DISABLED;
-        config->flags |= USE_DEFAULTS;
-      }
-      else
+      brdctl_int = read_brdctl(p);
+      internal50_present = (brdctl_int & BRDDAT6) ? 0 : 1;
+      if (max_targ > 8)
       {
-        host_conf = inb(HOSTCONF + base);
-        config->bus_speed = host_conf & DFTHRSH;
-        config->busrtime = (host_conf << 2) & BOFF;
+        internal68_present = (brdctl_int & BRDDAT7) ? 0 : 1;
       }
 
       /*
-       * Setup the FIFO threshold and the bus off time
+       * Set the rom bank to 1 and determine
+       * the other signals.
        */
-      outb(config->bus_speed & DFTHRSH, BUSSPD + base);
-      outb(config->busrtime, BUSTIME + base);
+      write_brdctl(p, BRDDAT5);
 
       /*
-       * A reminder until this can be detected automatically.
+       * Now read the state of the external connectors.  BRDDAT6 is
+       * 0 when an external cable is present, and BRDDAT7 (EPROMPS) is
+       * set when the eprom is present.
        */
-      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-	     (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-      break;
-
-    case AIC_284x:
-      outb(CHIPRST, HCNTRL + base);
-      config->unpause = UNPAUSE_284X;
-      config->pause = REQ_PAUSE; /* DWG would like to be like the rest */
-      aic7xxx_delay(1);
-      outb(config->pause, HCNTRL + base);
-
-      config->parity = AIC_ENABLED;
-      config->irq = inb(INTDEF + base) & 0x0F;
-      host_conf = inb(HOSTCONF + base);
-
-      printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
-      have_seeprom = read_2840_seeprom(base, &sc);
-      if (!have_seeprom)
-      {
-	printk("aic7xxx: Unable to read SEEPROM.\n");
-      }
-      else
+      brdctl_ext = read_brdctl(p);
+      external_present = (brdctl_ext & BRDDAT6) ? 0 : 1;
+      eprom_present = brdctl_ext & BRDDAT7;
+      if (aic7xxx_verbose)
       {
-	printk("done.\n");
-        config->flags |= HAVE_SEEPROM;
-        if (sc.bios_control & CF284XEXTEND)
-          config->flags |= EXTENDED_TRANSLATION;
-        if (!(sc.bios_control & CFBIOSEN))
+        if (max_targ > 8)
         {
-          /*
-           * The BIOS is disabled; the values left over in scratch
-           * RAM are still valid.  Do not use defaults as in the
-           * AIC-7770 case.
-           */
-          config->bios = AIC_DISABLED;
+          printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Int-68 %s, "
+                 "Ext-68 %s)\n",
+                 internal50_present ? "YES" : "NO",
+                 internal68_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
         }
         else
         {
-	  config->parity = (sc.adapter_control & CFSPARITY) ?
-			   AIC_ENABLED : AIC_DISABLED;
-	  config->low_term = (sc.adapter_control & CF284XSTERM) ?
-			        AIC_ENABLED : AIC_DISABLED;
-	  /*
-	   * XXX - Adaptec *does* make 284x wide controllers, but the
-	   *       documents do not say where the high byte termination
-	   *       enable bit is located.
-           */
+          printk(KERN_INFO "aic7xxx: Cables present (Int-50 %s, Ext-50 %s)\n",
+                 internal50_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
         }
+        printk(KERN_INFO "aic7xxx: eprom %s present, brdctl_int=0x%x, "
+               "brdctl_ext=0x%x\n",
+               eprom_present ? "is" : "not", brdctl_int, brdctl_ext);
       }
 
-      host_conf = inb(HOSTCONF + base);
-      config->bus_speed = host_conf & DFTHRSH;
-      config->busrtime = (host_conf << 2) & BOFF;
-
-      /*
-       * Setup the FIFO threshold and the bus off time
-       */
-      outb(config->bus_speed & DFTHRSH, BUSSPD + base);
-      outb(config->busrtime, BUSTIME + base);
-
-      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-	     (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-      break;
-
-    case AIC_7860:
-    case AIC_7861:
-    case AIC_7880:
-    case AIC_7881:
-    case AIC_7882:
-    case AIC_7883:
-    case AIC_7884:
-      /*
-       * Remember if Ultra was enabled in case there is no SEEPROM.
-       * Fall through to the rest of the AIC_78xx code.
-       */
-      if ((inb(SXFRCTL0 + base) & ULTRAEN) || aic7xxx_enable_ultra)
-        config->flags |= ULTRA_ENABLED;
-
-    case AIC_7850:
-    case AIC_7855:
-    case AIC_7870:
-    case AIC_7871:
-    case AIC_7872:
-    case AIC_7873:
-    case AIC_7874:
       /*
-       * Grab the SCSI ID before chip reset in case there is no SEEPROM.
+       * Now set the termination based on what we found.  BRDDAT6
+       * controls wide termination enable.
        */
-      config->scsi_id = inb(SCSIID + base) & OID;
-      outb(CHIPRST, HCNTRL + base);
-      config->unpause = UNPAUSE_294X;
-      config->pause = config->unpause | PAUSE;
-      aic7xxx_delay(1);
-      outb(config->pause, HCNTRL + base);
-
-      config->parity = AIC_ENABLED;
+      high_on = FALSE;
+      low_on = FALSE;
+      if ((max_targ > 8) &&
+          ((external_present == 0) || (internal68_present == 0)))
+      {
+        high_on = TRUE;
+      }
 
-      printk(KERN_INFO "aic7xxx: Reading SEEPROM...");
-      if ((config->type == AIC_7873) || (config->type == AIC_7883))
+      if ((internal50_present + internal68_present + external_present) <= 1)
       {
-        have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
-                                    &sc, c56_66);
+        low_on = TRUE;
       }
-      else
+          
+      if (internal50_present && internal68_present && external_present)
       {
-        have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2),
-                                    &sc, c46);
+        printk(KERN_WARNING "aic7xxx: Illegal cable configuration!!\n"
+               "         Only two connectors on the adapter may be "
+               "used at a time!\n");
       }
-      if (!have_seeprom)
+
+      if (high_on == TRUE)
+        write_brdctl(p, BRDDAT6);
+      else
+        write_brdctl(p, 0);
+
+      if (low_on == TRUE)
+        *sxfrctl1 |= STPWEN;
+
+      if (aic7xxx_verbose)
       {
-        for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
-        {
-          if (inb(sram) != 0x00)
-            break;
-        }
-        if (sram == base + TARG_SCRATCH)
-        {
-          for (sram = base + TARG_SCRATCH; sram < base + 0x60; sram++)
-          {
-            if (inb(sram) != 0xFF)
-              break;
-          }
-        }
-        if ((sram != base + 0x60) && (config->scsi_id != 0))
+        if (max_targ > 8)
         {
-          config->flags &= ~USE_DEFAULTS;
-	  printk("\naic7xxx: Unable to read SEEPROM; "
-                 "using leftover BIOS values.\n");
+          printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+                 low_on ? "ON" : "OFF",
+                 high_on ? "ON" : "OFF");
         }
         else
         {
-          printk("\n");
-          printk(KERN_INFO "aic7xxx: Unable to read SEEPROM; using default "
-                 "settings.\n");
-          config->flags |= USE_DEFAULTS;
-          config->flags &= ~ULTRA_ENABLED;
-          config->scsi_id = 7;
+          printk(KERN_INFO "aic7xxx: Termination %s\n", low_on ? "ON" : "OFF");
         }
-        scsi_conf = ENSPCHK | RESET_SCSI;
+      }
+      aic7xxx_verbose = old_verbose;
+    }
+    else
+    {
+      if (adapter_control & CFSTERM)
+      {
+        *sxfrctl1 |= STPWEN;
+      }
+      outb(SEEMS | SEECS, p->base + SEECTL);
+      /*
+       * Configure high byte termination.
+       */
+      if (adapter_control & CFWSTERM)
+      {
+        write_brdctl(p, BRDDAT6);
       }
       else
       {
-	printk("done.\n");
-        config->flags |= HAVE_SEEPROM;
-        if (!(sc.bios_control & CFBIOSEN))
-        {
-          /*
-           * The BIOS is disabled; the values left over in scratch
-           * RAM are still valid.  Do not use defaults as in the
-           * AIC-7770 case.
-           */
-          config->bios = AIC_DISABLED;
-          scsi_conf = ENSPCHK | RESET_SCSI;
-        }
-        else
-        {
-          scsi_conf = 0;
-          if (sc.adapter_control & CFRESETB)
-            scsi_conf |= RESET_SCSI;
-          if (sc.adapter_control & CFSPARITY)
-            scsi_conf |= ENSPCHK;
-	  if (sc.bios_control & CFEXTEND)
-            config->flags |= EXTENDED_TRANSLATION;
-	  config->scsi_id = (sc.brtime_id & CFSCSIID);
-	  config->parity = (sc.adapter_control & CFSPARITY) ?
-			     AIC_ENABLED : AIC_DISABLED;
-	  config->low_term = (sc.adapter_control & CFSTERM) ?
-			       AIC_ENABLED : AIC_DISABLED;
-	  config->high_term = (sc.adapter_control & CFWSTERM) ?
-			        AIC_ENABLED : AIC_DISABLED;
-	  config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8);
-          if (((config->type == AIC_7880) || (config->type == AIC_7881) ||
-               (config->type == AIC_7882) || (config->type == AIC_7883) ||
-               (config->type == AIC_7884)) && (sc.adapter_control & CFULTRAEN))
-          {
-            printk(KERN_INFO "aic7xxx: Enabling support for Ultra SCSI "
-                   "speed.\n");
-            config->flags |= ULTRA_ENABLED;
-          }
-        }
+        write_brdctl(p, 0);
+      }
+      if (aic7xxx_verbose)
+      {
+        printk(KERN_INFO "aic7xxx: Termination (Low %s, High %s)\n",
+               (adapter_control & CFSTERM) ? "ON" : "OFF",
+               (adapter_control & CFWSTERM) ? "ON" : "OFF");
       }
+    }
+    release_seeprom(p);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   detect_maxscb
+ *
+ * Description:
+ *   Detects the maximum number of SCBs for the controller and returns
+ *   the count and a mask in p (p->maxscbs, p->qcntmask).
+ *-F*************************************************************************/
+static void
+detect_maxscb(struct aic7xxx_host *p)
+{
+  int i;
+  unsigned char max_scbid = 255;
 
-      outb(scsi_conf | (config->scsi_id & 0x07), SCSICONF + base);
-      config->bus_speed = DFTHRSH_100;
-      outb(config->bus_speed, DSPCISTATUS + base);
+  /*
+   * It's possible that we've already done this for multichannel
+   * adapters.
+   */
+  if (p->scb_data->maxhscbs == 0)
+  {
+    /*
+     * We haven't initialized the SCB settings yet.  Walk the SCBs to
+     * determince how many there are.
+     */
+    outb(0, p->base + FREE_SCBH);
 
-      /*
-       * In case we are a wide card...
-       */
-      outb(config->scsi_id, SCSICONF + base + 1);
+    for (i = 0; i < AIC7XXX_MAXSCB; i++)
+    {
+      outb(i, p->base + SCBPTR);
+      outb(i, p->base + SCB_CONTROL);
+      if (inb(p->base + SCB_CONTROL) != i)
+        break;
+      outb(0, p->base + SCBPTR);
+      if (inb(p->base + SCB_CONTROL) != 0)
+        break;
 
-      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-	     (config->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
-      break;
+      outb(i, p->base + SCBPTR);
+      outb(0, p->base + SCB_CONTROL);   /* Clear the control byte. */
+      outb(i + 1, p->base + SCB_NEXT);  /* Set the next pointer. */
+      outb(SCB_LIST_NULL, p->base + SCB_TAG);  /* Make the tag invalid. */
 
-    default:
-      panic(KERN_WARNING "aic7xxx: (aic7xxx_register) Internal error.\n");
+      /* Make the non-tagged targets not busy. */
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS);
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 1);
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 2);
+      outb(SCB_LIST_NULL, p->base + SCB_BUSYTARGETS + 3);
+    }
+
+    /* Make sure the last SCB terminates the free list. */
+    outb(i - 1, p->base + SCBPTR);
+    outb(SCB_LIST_NULL, p->base + SCB_NEXT);
+
+    /* Ensure we clear the first (0) SCBs control byte. */
+    outb(0, p->base + SCBPTR);
+    outb(0, p->base + SCB_CONTROL);
+
+    p->scb_data->maxhscbs = i;
   }
 
-  detect_maxscb(config);
+  if ((p->flags & PAGE_ENABLED) && (p->scb_data->maxhscbs < AIC7XXX_MAXSCB))
+  {
+    /* Determine the number of valid bits in the FIFOs. */
+    outb(max_scbid, p->base + QINFIFO);
+    max_scbid = inb(p->base + QINFIFO);
+    p->scb_data->maxscbs = MIN(AIC7XXX_MAXSCB, max_scbid + 1);
+  }
+  else
+  {
+    p->scb_data->maxscbs = p->scb_data->maxhscbs;
+  }
+  if (p->scb_data->maxscbs == p->scb_data->maxhscbs)
+  {
+    /*
+     * Disable paging if the QINFIFO doesn't allow more SCBs than
+     * we have in hardware.
+     */
+    p->flags &= ~PAGE_ENABLED;
+  }
 
-  if (config->chip_type == AIC_777x)
+  /*
+   * Set the Queue Full Count.  Some cards have more queue space than
+   * SCBs.
+   */
+  switch (p->chip_class)
   {
-    if (config->pause & IRQMS)
-    {
-      printk(KERN_INFO "aic7xxx: Using level sensitive interrupts.\n");
-    }
-    else
-    {
-      printk(KERN_INFO "aic7xxx: Using edge triggered interrupts.\n");
-    }
+    case AIC_777x:
+      p->qfullcount = 4;
+      p->qcntmask = 0x07;
+      break;
+    case AIC_785x:
+    case AIC_786x:
+      p->qfullcount = 8;
+      p->qcntmask = 0x0f;
+      break;
+    case AIC_787x:
+    case AIC_788x:
+      if (p->scb_data->maxhscbs == AIC7XXX_MAXSCB)
+      {
+        p->qfullcount = AIC7XXX_MAXSCB;
+        p->qcntmask = 0xFF;
+      }
+      else
+      {
+        p->qfullcount = 16;
+        p->qcntmask = 0x1F;
+      }
+      break;
   }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_register
+ *
+ * Description:
+ *   Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *-F*************************************************************************/
+static int
+aic7xxx_register(Scsi_Host_Template *template, struct aic7xxx_host *p)
+{
+  int i;
+  unsigned char sblkctl, flags = 0;
+  int max_targets;
+  int found = 1;
+  char channel_ids[] = {'A', 'B', 'C'};
+  unsigned char target_settings;
+  unsigned char scsi_conf, sxfrctl1;
+  unsigned short ultraenable = 0;
+  struct Scsi_Host *host;
+
+  /*
+   * Lock out other contenders for our i/o space.
+   */
+  request_region(p->base, MAXREG - MINREG, "aic7xxx");
 
   /*
    * Read the bus type from the SBLKCTL register. Set the FLAGS
    * register in the sequencer for twin and wide bus cards.
    */
-  sblkctl = inb(SBLKCTL + base);
-  if (config->flags & PAGE_ENABLED)
+  sblkctl = inb(p->base + SBLKCTL);
+  if (p->flags & PAGE_ENABLED)
     flags = PAGESCBS;
 
   switch (sblkctl & SELBUS_MASK)
   {
     case SELNARROW:     /* narrow/normal bus */
-      config->scsi_id = inb(SCSICONF + base) & 0x07;
-      config->bus_type = AIC_SINGLE;
-      outb(flags | SINGLE_BUS, FLAGS + base);
+      p->scsi_id = inb(p->base + SCSICONF) & 0x07;
+      p->bus_type = AIC_SINGLE;
+      p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
+      if (p->flags & MULTI_CHANNEL)
+      {
+        printk(KERN_INFO "aic7xxx: Channel %c, SCSI ID %d, ",
+               channel_ids[p->chan_num], p->scsi_id);
+      }
+      else
+      {
+        printk (KERN_INFO "aic7xxx: Single Channel, SCSI ID %d, ",
+                p->scsi_id);
+      }
+      outb(flags | SINGLE_BUS, p->base + SEQ_FLAGS);
       break;
 
     case SELWIDE:     /* Wide bus */
-      config->scsi_id = inb(SCSICONF + base + 1) & 0x0F;
-      config->bus_type = AIC_WIDE;
-      printk("aic7xxx: Enabling wide channel of %s-Wide.\n",
-	     board_names[config->type]);
-      outb(flags | WIDE_BUS, FLAGS + base);
-      break;
-
-    case SELBUSB:     /* Twin bus */
-      config->scsi_id = inb(SCSICONF + base) & 0x07;
-#ifdef AIC7XXX_TWIN_SUPPORT
-      config->scsi_id_b = inb(SCSICONF + base + 1) & 0x07;
-      config->bus_type = AIC_TWIN;
-      printk(KERN_INFO "aic7xxx: Enabled channel B of %s-Twin.\n",
-	     board_names[config->type]);
-      outb(flags | TWIN_BUS, FLAGS + base);
-#else
-      config->bus_type = AIC_SINGLE;
-      printk(KERN_INFO "aic7xxx: Channel B of %s-Twin will be ignored.\n",
-	     board_names[config->type]);
-      outb(flags, FLAGS + base);
-#endif
+      p->scsi_id = inb(p->base + SCSICONF + 1) & HWSCSIID;
+      p->bus_type = AIC_WIDE;
+      p->flags &= ~FLAGS_CHANNEL_B_PRIMARY;
+      if (p->flags & MULTI_CHANNEL)
+      {
+        printk(KERN_INFO "aic7xxx: Wide Channel %c, SCSI ID %d, ",
+               channel_ids[p->chan_num], p->scsi_id);
+      }
+      else
+      {
+        printk (KERN_INFO "aic7xxx: Wide Channel, SCSI ID %d, ",
+                p->scsi_id);
+      }
+      outb(flags | WIDE_BUS, p->base + SEQ_FLAGS);
       break;
 
-    default:
-      printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please "
-	     "mail deang@teleport.com\n", inb(SBLKCTL + base));
-      outb(0, FLAGS + base);
-      return (0);
-  }
-
-  /*
-   * For the 294x cards, clearing DIAGLEDEN and DIAGLEDON, will
-   * take the card out of diagnostic mode and make the host adapter
-   * LED follow bus activity (will not always be on).
-   */
-  outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL + base);
-
-  /*
-   * The IRQ level in i/o port 4 maps directly onto the real
-   * IRQ number. If it's ok, register it with the kernel.
-   *
-   * NB. the Adaptec documentation says the IRQ number is only
-   *     in the lower four bits; the ECU information shows the
-   *     high bit being used as well. Which is correct?
-   *
-   * The PCI cards get their interrupt from PCI BIOS.
-   */
-  if ((config->chip_type == AIC_777x) && ((config->irq < 9) || (config->irq > 15)))
-  {
-    printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ level, "
-          "ignoring.\n");
-    return (0);
+    case SELBUSB:     /* Twin bus */
+      p->scsi_id = inb(p->base + SCSICONF) & HSCSIID;
+      p->scsi_id_b = inb(p->base + SCSICONF + 1) & HSCSIID;
+      p->bus_type = AIC_TWIN;
+      printk(KERN_INFO "aic7xxx: Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
+             p->scsi_id, p->scsi_id_b);
+      outb(flags | TWIN_BUS, p->base + SEQ_FLAGS);
+      break;
+
+    default:
+      printk(KERN_WARNING "aic7xxx: Unsupported type 0x%x, please "
+	     "mail deang@teleport.com\n", inb(p->base + SBLKCTL));
+      outb(0, p->base + SEQ_FLAGS);
+      return (0);
   }
 
   /*
-   * Print out debugging information before re-enabling
-   * the card - a lot of registers on it can't be read
-   * when the sequencer is active.
-   */
-  debug_config(config);
-
-  /*
-   * Register each "host" and fill in the returned Scsi_Host
-   * structure as best we can. Some of the parameters aren't
-   * really relevant for bus types beyond ISA, and none of the
-   * high-level SCSI code looks at it anyway. Why are the fields
-   * there? Also save the pointer so that we can find the
-   * information when an IRQ is triggered.
+   * Detect SCB parameters and initialize the SCB array.
    */
-  host = scsi_register(template, sizeof(struct aic7xxx_host));
-  host->can_queue = config->maxscbs;
+  detect_maxscb(p);
+  printk("%d/%d SCBs, QFull %d, QMask 0x%x\n",
+         p->scb_data->maxhscbs, p->scb_data->maxscbs,
+         p->qfullcount, p->qcntmask);
+
+  host = p->host;
+
+  host->can_queue = p->scb_data->maxscbs;
   host->cmd_per_lun = 2;
+  host->sg_tablesize = AIC7XXX_MAX_SG;
   host->select_queue_depths = aic7xxx_select_queue_depth;
-  host->this_id = config->scsi_id;
-  host->io_port = config->base;
+  host->this_id = p->scsi_id;
+  host->io_port = p->base;
   host->n_io_port = 0xFF;
-  host->base = (unsigned char *)config->mbase;
-  host->irq = config->irq;
-  if (config->bus_type == AIC_WIDE)
+  host->base = (unsigned char *) p->mbase;
+  host->irq = p->irq;
+  if (p->bus_type == AIC_WIDE)
   {
     host->max_id = 16;
   }
-  if (config->bus_type == AIC_TWIN)
+  if (p->bus_type == AIC_TWIN)
   {
     host->max_channel = 1;
   }
 
-  p = (struct aic7xxx_host *) host->hostdata;
-
   p->host = host;
-  p->host_no = (int)host->host_no;
+  p->host_no = host->host_no;
   p->isr_count = 0;
-  p->base = base;
-  p->maxscbs = config->maxscbs;
-  p->maxhscbs = config->maxhscbs;
-  p->qcntmask = config->qcntmask;
-  p->mbase = (char *)config->mbase;
-  p->type = config->type;
-  p->chip_type = config->chip_type;
-  p->flags = config->flags;
-  p->chan_num = config->chan_num;
-  p->scb_link = &(p->scb_usage);
-#if defined(CONFIG_PCI) && defined(AIC7XXX_SHARE_SCBS)
-  if ((p->chan_num == 0) && ((p->type == AIC_7873) | (p->type == AIC_7883)))
-  {
-    shared_3985_scbs = &(p->scb_usage);
-    p->scb_link = &(p->scb_usage);
-  }
-#endif
-  p->scb_link->numscbs = 0;
-  p->bus_type = config->bus_type;
-  p->seeprom = sc;
   p->next = NULL;
   p->completeq.head = NULL;
   p->completeq.tail = NULL;
-  scbq_init(&p->scb_link->free_scbs);
-  scbq_init(&p->page_scbs);
+  scbq_init(&p->scb_data->free_scbs);
   scbq_init(&p->waiting_scbs);
-  scbq_init(&p->assigned_scbs);
 
-  p->unpause = config->unpause;
-  p->pause = config->pause;
-
-  for (i = 0; i <= 15; i++)
+  for (i = 0; i <= NUMBER(p->device_status); i++)
   {
     p->device_status[i].commands_sent = 0;
     p->device_status[i].flags = 0;
+    p->device_status[i].active_cmds = 0;
     p->device_status[i].last_reset = 0;
   }
-  if (aic7xxx_boards[config->irq] == NULL)
+  if (aic7xxx_boards[p->irq] == NULL)
   {
+    int result;
+    int irq_flags = 0;
+
+#ifdef AIC7XXX_OLD_ISR_TYPE
+    irg_flags = SA_INTERRUPT;
+#endif
     /*
      * Warning! This must be done before requesting the irq.  It is
      * possible for some boards to raise an interrupt as soon as
@@ -4352,17 +5030,26 @@
      * kernel, an interrupt is triggered immediately.  Therefore, we
      * must ensure the board data is correctly set before the request.
      */
-    aic7xxx_boards[config->irq] = host;
+    aic7xxx_boards[p->irq] = host;
 
     /*
-     * Register IRQ with the kernel.
+     * Register IRQ with the kernel.  Only allow sharing IRQs with
+     * PCI devices.
      */
-    if (request_irq(config->irq, aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
-       "aic7xxx", NULL))
+    if (p->chip_class == AIC_777x)
+    {
+      result = (request_irq(p->irq, aic7xxx_isr, irq_flags, "aic7xxx", NULL));
+    }
+    else
+    {
+      result = (request_irq(p->irq, aic7xxx_isr, irq_flags | SA_SHIRQ,
+                "aic7xxx", NULL));
+    }
+    if (result < 0)
     {
       printk(KERN_WARNING "aic7xxx: Couldn't register IRQ %d, ignoring.\n",
-             config->irq);
-      aic7xxx_boards[config->irq] = NULL;
+             p->irq);
+      aic7xxx_boards[p->irq] = NULL;
       return (0);
     }
   }
@@ -4373,79 +5060,74 @@
      * registered host adapter. Add this host adapter's Scsi_Host
      * to the beginning of the linked list of hosts at the same IRQ.
      */
-    p->next = aic7xxx_boards[config->irq];
-    aic7xxx_boards[config->irq] = host;
-  }
-
-  /*
-   * Load the sequencer program, then re-enable the board -
-   * resetting the AIC-7770 disables it, leaving the lights
-   * on with nobody home. On the PCI bus you *may* be home,
-   * but then your mailing address is dynamically assigned
-   * so no one can find you anyway :-)
-   */
-  printk(KERN_INFO "aic7xxx: Downloading sequencer code...");
-  aic7xxx_loadseq(base);
-
-  /*
-   * Set Fast Mode and Enable the board
-   */
-  outb(FASTMODE, SEQCTL + base);
-
-  if (p->chip_type == AIC_777x)
-  {
-    outb(ENABLE, BCTL + base);
+    p->next = aic7xxx_boards[p->irq];
+    aic7xxx_boards[p->irq] = host;
   }
 
-  printk("done.\n");
-
   /*
    * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
    */
   if (p->bus_type == AIC_TWIN)
   {
     /*
-     * Select Channel B.
+     * The controller is gated to channel B after a chip reset; set
+     * bus B values first.
      */
-    outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
-
-    outb(config->scsi_id_b, SCSIID + base);
-    scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL);
-    outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-#if EXPERIMENTAL_FLAGS
-    outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
-#else
-    outb(ENSELTIMO, SIMODE1 + base);
-#endif
+    outb(p->scsi_id_b, p->base + SCSIID);
+    scsi_conf = inb(p->base + SCSICONF + 1);
+    sxfrctl1 = inb(p->base + SXFRCTL1);
+    outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | 
+         ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
+    outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
     if (p->flags & ULTRA_ENABLED)
     {
-      outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+      outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
     }
     else
     {
-      outb(DFON | SPIOEN, SXFRCTL0 + base);
+      outb(DFON | SPIOEN, p->base + SXFRCTL0);
     }
 
-    /*
-     * Select Channel A
-     */
-    outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+    if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+    {
+      /* Reset SCSI bus B. */
+      if (aic7xxx_verbose)
+        printk(KERN_INFO "aic7xxx: Resetting channel B\n");
+
+      aic7xxx_reset_current_bus(p);
+    }
+
+    /* Select channel A */
+    outb(SELNARROW, p->base + SBLKCTL);
   }
-  outb(config->scsi_id, SCSIID + base);
-  scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL);
-  outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
-#if EXPERIMENTAL_FLAGS
-  outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1 + base);
-#else
-  outb(ENSELTIMO, SIMODE1 + base);
-#endif
+
+  outb(p->scsi_id, p->base + SCSIID);
+  scsi_conf = inb(p->base + SCSICONF);
+  sxfrctl1 = inb(p->base + SXFRCTL1);
+  outb((scsi_conf & (ENSPCHK | STIMESEL)) | (sxfrctl1 & STPWEN) | 
+       ENSTIMER | ACTNEGEN, p->base + SXFRCTL1);
+  outb(ENSELTIMO | ENSCSIRST | ENSCSIPERR, p->base + SIMODE1);
   if (p->flags & ULTRA_ENABLED)
   {
-    outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+    outb(DFON | SPIOEN | FAST20, p->base + SXFRCTL0);
   }
   else
   {
-    outb(DFON | SPIOEN, SXFRCTL0 + base);
+    outb(DFON | SPIOEN, p->base + SXFRCTL0);
+  }
+
+  if ((scsi_conf & RESET_SCSI) && (aic7xxx_no_reset == 0))
+  {
+    /* Reset SCSI bus A. */
+    if (aic7xxx_verbose)
+      printk(KERN_INFO "aic7xxx: Resetting channel A\n");
+
+    aic7xxx_reset_current_bus(p);
+
+    /*
+     * Delay for the reset delay.
+     */
+    aic7xxx_delay(AIC7XXX_RESET_DELAY);
   }
 
   /*
@@ -4472,67 +5154,47 @@
   /*
    * Grab the disconnection disable table and invert it for our needs
    */
-  if (have_seeprom)
+  if (p->flags & USE_DEFAULTS)
   {
-    p->discenable = 0x0;
+    printk(KERN_INFO "aic7xxx: Host adapter BIOS disabled. Using default SCSI "
+           "device parameters.\n");
+    p->discenable = 0xFFFF;
   }
   else
   {
-    if (config->bios == AIC_DISABLED)
-    {
-      printk(KERN_INFO "aic7xxx : Host adapter BIOS disabled. Using default SCSI "
-             "device parameters.\n");
-      p->discenable = 0xFFFF;
-    }
-    else
-    {
-      p->discenable = ~((inb(DISC_DSB + base + 1) << 8) |
-          inb(DISC_DSB + base));
-    }
+    p->discenable = ~((inb(p->base + DISC_DSB + 1) << 8) |
+        inb(p->base + DISC_DSB));
   }
 
   for (i = 0; i < max_targets; i++)
   {
-    if (config->flags & USE_DEFAULTS)
+    if (p->flags & USE_DEFAULTS)
     {
-      target_settings = 0;  /* 10 MHz */
+      target_settings = 0;  /* 10 or 20 MHz depending on Ultra enable */
       p->needsdtr_copy |= (0x01 << i);
       p->needwdtr_copy |= (0x01 << i);
+      if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+        ultraenable |= (0x01 << i);
     }
     else
     {
-      if (have_seeprom)
+      target_settings = inb(p->base + TARG_SCRATCH + i);
+      if (target_settings & 0x0F)
       {
-  	target_settings = ((sc.device_flags[i] & CFXFER) << 4);
-  	if (sc.device_flags[i] & CFSYNCH)
-  	{
-  	  p->needsdtr_copy |= (0x01 << i);
-  	}
-  	if (sc.device_flags[i] & CFWIDEB)
-  	{
-  	  p->needwdtr_copy |= (0x01 << i);
-  	}
-  	if (sc.device_flags[i] & CFDISC)
-        {
-          p->discenable |= (0x01 << i);
-        }
+        p->needsdtr_copy |= (0x01 << i);
+        /*
+         * Default to asynchronous transfers (0 offset)
+         */
+        target_settings &= 0xF0;
       }
-      else
+      if (target_settings & 0x80)
       {
-        target_settings = inb(TARG_SCRATCH + base + i);
-        if (target_settings & 0x0F)
-        {
-          p->needsdtr_copy |= (0x01 << i);
-          /*
-           * Default to asynchronous transfers (0 offset)
-           */
-          target_settings &= 0xF0;
-        }
-        if (target_settings & 0x80)
-        {
-          p->needwdtr_copy |= (0x01 << i);
-          target_settings &= 0x7F;
-        }
+        p->needwdtr_copy |= (0x01 << i);
+        /*
+         * Clear the wide flag. When wide negotiation is successful,
+         * we'll enable it.
+         */
+        target_settings &= 0x7F;
       }
       if (p->flags & ULTRA_ENABLED)
       {
@@ -4543,126 +5205,471 @@
           case 0x20:
             ultraenable |= (0x01 << i);
             break;
-          case 0x40:
+          case 0x40:  /* treat 10MHz as 10MHz without Ultra enabled */
             target_settings &= ~(0x70);
             break;
           default:
             break;
         }
       }
-    }
-    outb(target_settings, (TARG_SCRATCH + base + i));
+    }
+    outb(target_settings, p->base + TARG_SCRATCH + i);
+  }
+
+  /*
+   * If we are not wide, forget WDTR. This makes the driver
+   * work on some cards that don't leave these fields cleared
+   * when BIOS is not installed.
+   */
+  if (p->bus_type != AIC_WIDE)
+  {
+    p->needwdtr_copy = 0;
+  }
+  p->needsdtr = p->needsdtr_copy;
+  p->needwdtr = p->needwdtr_copy;
+  p->orderedtag = 0;
+  outb(ultraenable & 0xFF, p->base + ULTRA_ENB);
+  outb((ultraenable >> 8) & 0xFF, p->base + ULTRA_ENB + 1);
+
+  /*
+   * Set the number of available hardware SCBs.
+   */
+  outb(p->scb_data->maxhscbs, p->base + SCBCOUNT);
+
+  /*
+   * 2s compliment of maximum tag value.
+   */
+  i = p->scb_data->maxscbs;
+  outb(-i & 0xFF, p->base + COMP_SCBCOUNT);
+
+  /*
+   * Allocate enough hardware scbs to handle the maximum number of
+   * concurrent transactions we can have.  We have to make sure that
+   * the allocated memory is contiguous memory.  The Linux kmalloc
+   * routine should only allocate contiguous memory, but note that
+   * this could be a problem if kmalloc() is changed.
+   */
+  if (p->scb_data->hscbs == NULL)
+  {
+    size_t array_size;
+    unsigned int hscb_physaddr;
+
+    array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb);
+    p->scb_data->hscbs = kmalloc(array_size, GFP_ATOMIC);
+    if (p->scb_data->hscbs == NULL)
+    {
+      printk("aic7xxx: Unable to allocate hardware SCB array; "
+             "failing detection.\n");
+      release_region(p->base, MAXREG - MINREG);
+      /*
+       * Ensure that we only free the IRQ when there is _not_ another
+       * aic7xxx adapter sharing this IRQ.  The adapters are always
+       * added to the beginning of the list, so we can grab the next
+       * pointer and place it back in the board array.
+       */
+      if (p->next == NULL)
+      {
+        free_irq(p->irq, aic7xxx_isr);
+      }
+      aic7xxx_boards[p->irq] = p->next;
+      return(0);
+    }
+    /* At least the control byte of each SCB needs to be 0. */
+    memset(p->scb_data->hscbs, 0, array_size);
+
+    /* Tell the sequencer where it can find the hardware SCB array. */
+    hscb_physaddr = VIRT_TO_BUS(p->scb_data->hscbs);
+    outb(hscb_physaddr & 0xFF, p->base + HSCB_ADDR);
+    outb((hscb_physaddr >> 8) & 0xFF, p->base + HSCB_ADDR + 1);
+    outb((hscb_physaddr >> 16) & 0xFF, p->base + HSCB_ADDR + 2);
+    outb((hscb_physaddr >> 24) & 0xFF, p->base + HSCB_ADDR + 3);
+  }
+
+  /*
+   * QCount mask to deal with broken aic7850s that sporadically get
+   * garbage in the upper bits of their QCNT registers.
+    */
+  outb(p->qcntmask, p->base + QCNTMASK);
+
+  /*
+   * We don't have any waiting selections or disconnected SCBs.
+   */
+  outb(SCB_LIST_NULL, p->base + WAITING_SCBH);
+  outb(SCB_LIST_NULL, p->base + DISCONNECTED_SCBH);
+
+  /*
+   * Message out buffer starts empty
+   */
+  outb(0, p->base + MSG_LEN);
+
+  /*
+   * Load the sequencer program, then re-enable the board -
+   * resetting the AIC-7770 disables it, leaving the lights
+   * on with nobody home. On the PCI bus you *may* be home,
+   * but then your mailing address is dynamically assigned
+   * so no one can find you anyway :-)
+   */
+  aic7xxx_loadseq(p);
+
+  if (p->chip_class == AIC_777x)
+  {
+    outb(ENABLE, p->base + BCTL);  /* Enable the boards BUS drivers. */
+  }
+
+  /*
+   * Unpause the sequencer before returning and enable
+   * interrupts - we shouldn't get any until the first
+   * command is sent to us by the high-level SCSI code.
+   */
+  unpause_sequencer(p, /* unpause_always */ TRUE);
+
+  return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_chip_reset
+ *
+ * Description:
+ *   Perform a chip reset on the aic7xxx SCSI controller.  The controller
+ *   is paused upon return.
+ *-F*************************************************************************/
+static void
+aic7xxx_chip_reset(struct aic7xxx_host *p)
+{
+  unsigned char hcntrl;
+  int wait;
+
+  /* Retain the IRQ type across the chip reset. */
+  hcntrl = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+
+  /*
+   * For some 274x boards, we must clear the CHIPRST bit and pause
+   * the sequencer. For some reason, this makes the driver work.
+   */
+  outb(PAUSE | CHIPRST, p->base + HCNTRL);
+
+  /*
+   * In the future, we may call this function as a last resort for
+   * error handling.  Let's be nice and not do any unecessary delays.
+   */
+  wait = 1000;  /* 1 second (1000 * 1000 usec) */
+  while ((wait > 0) && ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0))
+  {
+    udelay(1000);  /* 1 msec = 1000 usec */
+    wait = wait - 1;
+  }
+
+  if ((inb(p->base + HCNTRL) & CHIPRSTACK) == 0)
+  {
+    printk(KERN_INFO "aic7xxx: Chip reset not cleared; clearing manually.\n");
+  }
+
+  outb(hcntrl | PAUSE, p->base + HCNTRL);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_alloc
+ *
+ * Description:
+ *   Allocate and initialize a host structure.  Returns NULL upon error
+ *   and a pointer to a aic7xxx_host struct upon success.
+ *-F*************************************************************************/
+static struct aic7xxx_host *
+aic7xxx_alloc(Scsi_Host_Template *sht, unsigned int base, unsigned int mbase,
+    aha_chip_type chip_type, int flags, scb_data_type *scb_data)
+{
+  struct aic7xxx_host *p = NULL;
+  struct Scsi_Host *host;
+
+  /*
+   * Allocate a storage area by registering us with the mid-level
+   * SCSI layer.
+   */
+  host = scsi_register(sht, sizeof(struct aic7xxx_host));
+
+  if (host != NULL)
+  {
+    p = (struct aic7xxx_host *) host->hostdata;
+    memset(p, 0, sizeof(struct aic7xxx_host));
+    p->host = host;
+
+    if (scb_data != NULL)
+    {
+      /*
+       * We are sharing SCB data areas; use the SCB data pointer
+       * provided.
+       */
+      p->scb_data = scb_data;
+      p->flags |= SHARED_SCBDATA;
+    }
+    else
+    {
+      /*
+       * We are not sharing SCB data; allocate one.
+       */
+      p->scb_data = kmalloc(sizeof(scb_data_type), GFP_ATOMIC);
+      if (p->scb_data != NULL)
+      {
+        memset(p->scb_data, 0, sizeof(scb_data_type));
+        scbq_init (&p->scb_data->free_scbs);
+      }
+      else
+      {
+        /*
+         * For some reason we don't have enough memory.  Free the
+         * allocated memory for the aic7xxx_host struct, and return NULL.
+         */
+        scsi_unregister(host);
+        p = NULL;
+      }
+    }
+    if (p != NULL)
+    {
+      p->host_no = host->host_no;
+      p->base = base;
+      p->mbase = mbase;
+      p->maddr = NULL;
+      p->flags = flags;
+      p->chip_type = chip_type;
+      p->unpause = (inb(p->base + HCNTRL) & IRQMS) | INTEN;
+      p->pause = p->unpause | PAUSE;
+    }
+  }
+  return (p);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_free
+ *
+ * Description:
+ *   Frees and releases all resources associated with an instance of
+ *   the driver (struct aic7xxx_host *).
+ *-F*************************************************************************/
+static void
+aic7xxx_free (struct aic7xxx_host *p)
+{
+  int i;
+
+  /*
+   * We should be careful in freeing the scb_data area.  For those
+   * adapters sharing external SCB RAM(398x), there will be only one
+   * scb_data area allocated.  The flag SHARED_SCBDATA indicates if
+   * one adapter is sharing anothers SCB RAM.
+   */
+  if (!(p->flags & SHARED_SCBDATA))
+  {
+    /*
+     * Free the allocated hardware SCB space.
+     */
+    if (p->scb_data->hscbs != NULL)
+    {
+      kfree(p->scb_data->hscbs);
+    }
+    /*
+     * Free the driver SCBs.  These were allocated on an as-need
+     * basis.
+     */
+    for (i = 0; i < p->scb_data->numscbs; i++)
+    {
+      kfree(p->scb_data->scb_array[i]);
+    }
+    /*
+     * Free the hardware SCBs.
+     */
+    if (p->scb_data->hscbs != NULL)
+    {
+      kfree(p->scb_data->hscbs);
+    }
+
+    /*
+     * Free the SCB data area.
+     */
+    kfree(p->scb_data);
+  }
+  /*
+   * Free the instance of the device structure.
+   */
+  scsi_unregister(p->host);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_load_seeprom
+ *
+ * Description:
+ *   Load the seeprom and configure adapter and target settings.
+ *   Returns 1 if the load was successful and 0 otherwise.
+ *-F*************************************************************************/
+static int
+load_seeprom (struct aic7xxx_host *p, unsigned char *sxfrctl1)
+{
+  int have_seeprom = 0;
+  int i, max_targets;
+  unsigned char target_settings, scsi_conf;
+  unsigned short scarray[128];
+  struct seeprom_config *sc = (struct seeprom_config *) scarray;
+
+  if (aic7xxx_verbose)
+  {
+    printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
+  }
+  switch (p->chip_type)
+  {
+    case AIC_7770:  /* None of these adapters have seeproms. */
+    case AIC_7771:
+    case AIC_7850:
+    case AIC_7855:
+      break;
+
+    case AIC_284x:
+      have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
+      break;
+
+    case AIC_7861:
+    case AIC_7870:
+    case AIC_7871:
+    case AIC_7872:
+    case AIC_7874:
+    case AIC_7881:
+    case AIC_7882:
+    case AIC_7884:
+      have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+                                  scarray, sizeof(*sc)/2, C46);
+      break;
+
+    case AIC_7860:  /* Motherboard Ultra controllers might have RAID port. */
+    case AIC_7880:
+      have_seeprom = read_seeprom(p, 0, scarray, sizeof(*sc)/2, C46);
+      if (!have_seeprom)
+      {
+        have_seeprom = read_seeprom(p, 0, scarray, sizeof(scarray)/2, C56_66);
+      }
+      break;
+
+    case AIC_7873:  /* The 3985 adapters use the 93c56 serial EEPROM. */
+    case AIC_7883:
+      have_seeprom = read_seeprom(p, p->chan_num * (sizeof(*sc)/2),
+                                  scarray, sizeof(scarray)/2, C56_66);
+      break;
+
+    default:
+      break;
   }
 
-  /*
-   * If we are not wide, forget WDTR. This makes the driver
-   * work on some cards that don't leave these fields cleared
-   * when BIOS is not installed.
-   */
-  if (p->bus_type != AIC_WIDE)
+  if (!have_seeprom)
   {
-    p->needwdtr_copy = 0;
+    if (aic7xxx_verbose)
+    {
+      printk("\naic7xxx: No SEEPROM available; using defaults.\n");
+    }
+    p->flags |= USE_DEFAULTS;
   }
-  p->needsdtr = p->needsdtr_copy;
-  p->needwdtr = p->needwdtr_copy;
-  p->orderedtag = 0;
-#if 0
-  printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr);
-  printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr);
-#endif
-  outb(ultraenable & 0xFF, ULTRA_ENB + base);
-  outb((ultraenable >> 8) & 0xFF, ULTRA_ENB + base + 1);
+  else
+  {
+    if (aic7xxx_verbose)
+    {
+      printk("done\n");
+    }
+    p->flags |= HAVE_SEEPROM;
 
-  /*
-   * Set the number of available SCBs.
-   */
-  outb(config->maxhscbs, SCBCOUNT + base);
+    /*
+     * Update the settings in sxfrctl1 to match the termination settings.
+     */
+    *sxfrctl1 = 0;
 
-  /*
-   * 2s compliment of maximum tag value.
-   */
-  i = p->maxscbs;
-  outb(-i & 0xFF, COMP_SCBCOUNT + base);
+    /*
+     * First process the settings that are different between the VLB
+     * and PCI adapter seeproms.
+     */
+    if (p->chip_class == AIC_777x)
+    {
+      /* VLB adapter seeproms */
+      if (sc->bios_control & CF284XEXTEND)
+        p->flags |= EXTENDED_TRANSLATION;
 
-  /*
-   * Set the QCNT (queue count) mask to deal with broken aic7850s that
-   * sporatically get garbage in the upper bits of their QCNT registers.
-   */
-  outb(config->qcntmask, QCNTMASK + base);
+      if (sc->adapter_control & CF284XSTERM)
+        *sxfrctl1 |= STPWEN;
+      /*
+       * The 284x SEEPROM doesn't have a max targets field.  We
+       * set it to 16 to make sure we take care of the 284x-wide
+       * adapters.  For narrow adapters, going through the extra
+       * 8 target entries will not cause any harm since they will
+       * will not be used.
+       *
+       * XXX - We should probably break out the bus detection
+       *       from the register function so we can use it here
+       *       to tell us how many targets there really are.
+       */
+      max_targets = 16;
+    }
+    else
+    {
+      /* PCI adapter seeproms */
+      if (sc->bios_control & CFEXTEND)
+        p->flags |= EXTENDED_TRANSLATION;
 
-  /*
-   * Clear the active flags - no targets are busy.
-   */
-  outb(0, ACTIVE_A + base);
-  outb(0, ACTIVE_B + base);
+      if (sc->adapter_control & CFSTERM)
+        *sxfrctl1 |= STPWEN;
 
-  /*
-   * We don't have any waiting selections or disconnected SCBs.
-   */
-  outb(SCB_LIST_NULL, WAITING_SCBH + base);
-  outb(SCB_LIST_NULL, DISCONNECTED_SCBH + base);
+      /* Limit to 16 targets just in case. */
+      max_targets = MIN(sc->max_targets & CFMAXTARG, 16);
+    }
 
-  /*
-   * Message out buffer starts empty
-   */
-  outb(0, MSG_LEN + base);
+    for (i = 0; i < max_targets; i++)
+    {
+      target_settings = (sc->device_flags[i] & CFXFER) << 4;
+      if (sc->device_flags[i] & CFSYNCH)
+        target_settings |= SOFS;
+      if (sc->device_flags[i] & CFWIDEB)
+        target_settings |= WIDEXFER;
+      if (sc->device_flags[i] & CFDISC)
+        p->discenable |= (0x01 << i);
+      outb(target_settings, p->base + TARG_SCRATCH + i);
+    }
+    outb(~(p->discenable & 0xFF), p->base + DISC_DSB);
+    outb(~((p->discenable >> 8) & 0xFF), p->base + DISC_DSB + 1);
 
-  /*
-   * Reset the SCSI bus. Is this necessary?
-   *   There may be problems for a warm boot without resetting
-   *   the SCSI bus. Either BIOS settings in scratch RAM
-   *   will not get reinitialized, or devices may stay at
-   *   previous negotiated settings (SDTR and WDTR) while
-   *   the driver will think that no negotiations have been
-   *   performed.
-   *
-   * Some devices need a long time to "settle" after a SCSI
-   * bus reset.
-   */
-  if (!aic7xxx_no_reset)
-  {
-    printk("aic7xxx: Resetting the SCSI bus...");
-    if (p->bus_type == AIC_TWIN)
+    p->scsi_id = sc->brtime_id & CFSCSIID;
+
+    scsi_conf = (p->scsi_id & 0x7);
+    if (sc->adapter_control & CFSPARITY)
+      scsi_conf |= ENSPCHK;
+    if (sc->adapter_control & CFRESETB)
+      scsi_conf |= RESET_SCSI;
+
+    if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
     {
       /*
-       * Select Channel B.
+       * We allow the operator to override ultra enable through
+       * the boot prompt.
        */
-      outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
+      if (!(sc->adapter_control & CFULTRAEN) && (aic7xxx_enable_ultra == 0))
+      {
+        /* Treat us as a non-ultra card */
+        p->flags &= ~ULTRA_ENABLED;
+      }
+    }
 
-      outb(SCSIRSTO, SCSISEQ + base);
-      udelay(1000);
-      outb(0, SCSISEQ + base);
-
-      /* Ensure we don't get a RSTI interrupt from this. */
-      outb(CLRSCSIRSTI, CLRSINT1 + base);
-      outb(CLRSCSIINT, CLRINT + base);
+    /* Set the host ID */
+    outb(scsi_conf, p->base + SCSICONF);
+    /* In case we are a wide card */
+    outb(p->scsi_id, p->base + SCSICONF + 1);
 
-     /*
-       * Select Channel A.
+    if (p->chip_class != AIC_777x)
+    {
+      /*
+       * Update the settings in sxfrctl1 to match the termination
+       * settings.
        */
-      outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+      *sxfrctl1 = 0;
+      configure_termination(p, sxfrctl1, sc->adapter_control,
+        (unsigned char) sc->max_targets & CFMAXTARG);
     }
-
-    outb(SCSIRSTO, SCSISEQ + base);
-    udelay(1000);
-    outb(0, SCSISEQ + base);
-
-    /* Ensure we don't get a RSTI interrupt from this. */
-    outb(CLRSCSIRSTI, CLRSINT1 + base);
-    outb(CLRSCSIINT, CLRINT + base);
-
-    aic7xxx_delay(AIC7XXX_RESET_DELAY);
-
-    printk("done.\n");
   }
-
-  /*
-   * Unpause the sequencer before returning and enable
-   * interrupts - we shouldn't get any until the first
-   * command is sent to us by the high-level SCSI code.
-   */
-  UNPAUSE_SEQUENCER(p);
-  return (found);
+  return (have_seeprom);
 }
 
 /*+F*************************************************************************
@@ -4671,17 +5678,24 @@
  *
  * Description:
  *   Try to detect and register an Adaptec 7770 or 7870 SCSI controller.
+ *
+ * XXX - This should really be called aic7xxx_probe().  A sequence of
+ *       probe(), attach()/detach(), and init() makes more sense than
+ *       one do-it-all function.  This may be useful when (and if) the
+ *       mid-level SCSI code is overhauled.
  *-F*************************************************************************/
 int
 aic7xxx_detect(Scsi_Host_Template *template)
 {
-  int found = 0, slot, base;
-  unsigned char irq = 0;
+  int found = 0;
+  aha_status_type adapter_bios;
+  aha_chip_class_type chip_class;
+  aha_chip_type chip_type;
+  int slot, base;
+  int chan_num = 0;
+  unsigned char hcntrl, sxfrctl1, sblkctl, hostconf, irq = 0;
   int i;
-  struct aic7xxx_host_config config;
-
-  template->proc_dir = &proc_scsi_aic7xxx;
-  config.chan_num = 0;
+  struct aic7xxx_host *p;
 
   /*
    * Since we may allow sharing of IRQs, it is imperative
@@ -4695,6 +5709,10 @@
     aic7xxx_boards[i] = NULL;
   }
 
+  template->proc_dir = &proc_scsi_aic7xxx;
+  template->name = aic7xxx_info(NULL);
+  template->sg_tablesize = AIC7XXX_MAX_SG;
+
   /*
    * Initialize the spurious count to 0.
    */
@@ -4716,33 +5734,174 @@
       continue;
     }
 
-    config.type = aic7xxx_probe(slot, HID0 + base, &(config.bios));
-    if (config.type != AIC_NONE)
+    chip_type = aic7xxx_probe(slot, base + HID0, &(adapter_bios));
+    if (chip_type != AIC_NONE)
     {
+
+      switch (chip_type)
+      {
+        case AIC_7770:
+        case AIC_7771:
+          printk("aic7xxx: <%s> at EISA %d\n",
+                 board_names[chip_type], slot);
+          break;
+        case AIC_284x:
+          printk("aic7xxx: <%s> at VLB %d\n",
+                 board_names[chip_type], slot);
+          break;
+        default:
+          break;
+      }
+
       /*
        * We found a card, allow 1 spurious interrupt.
        */
       aic7xxx_spurious_count = 1;
 
       /*
-       * We "find" a AIC-7770 if we locate the card
-       * signature and we can set it up and register
-       * it with the kernel without incident.
+       * Pause the card preserving the IRQ type.  Allow the operator
+       * to override the IRQ trigger.
        */
-      config.chip_type = AIC_777x;
-      config.base = base;
-      config.mbase = 0;
-      config.irq = irq;
-      config.parity = AIC_ENABLED;
-      config.low_term = AIC_UNKNOWN;
-      config.high_term = AIC_UNKNOWN;
-      config.flags = 0;
-      if (aic7xxx_extended)
-        config.flags |= EXTENDED_TRANSLATION;
-      config.bus_speed = DFTHRSH_100;
-      config.busrtime = BOFF;
-      found += aic7xxx_register(template, &config);
+      if (aic7xxx_irq_trigger == 1)
+        hcntrl = IRQMS;  /* Level */
+      else if (aic7xxx_irq_trigger == 0)
+        hcntrl = 0;  /* Edge */
+      else
+        hcntrl = inb(base + HCNTRL) & IRQMS;  /* Default */
+      outb(hcntrl | PAUSE, base + HCNTRL);
+
+      irq = inb(INTDEF + base) & 0x0F;
+      switch (irq)
+      {
+        case 9:
+        case 10:
+        case 11:
+        case 12:
+        case 14:
+        case 15:
+          break;
+
+        default:
+          printk(KERN_WARNING "aic7xxx: Host adapter uses unsupported IRQ "
+          "level, ignoring.\n");
+          irq = 0;
+          break;
+      }
+
+      if (irq != 0)
+      {
+        p = aic7xxx_alloc(template, base, 0, chip_type, 0, NULL);
+        if (p == NULL)
+        {
+          printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+          continue;
+        }
+        p->irq = irq & 0x0F;
+        p->chip_class = AIC_777x;
+#ifdef AIC7XXX_PAGE_ENABLE
+        p->flags |= PAGE_ENABLED;
+#endif
+        p->instance = found;
+        if (aic7xxx_extended)
+        {
+          p->flags |= EXTENDED_TRANSLATION;
+        }
+        aic7xxx_chip_reset(p);
+
+        switch (p->chip_type)
+        {
+          case AIC_7770:
+          case AIC_7771:
+          {
+            unsigned char biosctrl = inb(p->base + HA_274_BIOSCTRL);
+
+            /*
+             * Get the primary channel information.  Right now we don't
+             * do anything with this, but someday we will be able to inform
+             * the mid-level SCSI code which channel is primary.
+             */
+            if (biosctrl & CHANNEL_B_PRIMARY)
+            {
+              p->flags |= FLAGS_CHANNEL_B_PRIMARY;
+            }
+
+            if ((biosctrl & BIOSMODE) == BIOSDISABLED)
+            {
+              p->flags |= USE_DEFAULTS;
+            }
+            break;
+          }
 
+          case AIC_284x:
+            if (!load_seeprom(p, &sxfrctl1))
+            {
+              if (aic7xxx_verbose)
+                printk(KERN_INFO "aic7xxx: SEEPROM not available.\n");
+            }
+            break;
+
+          default:  /* Won't get here. */
+            break;
+        }
+        printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, IRQ %d (%s), ",
+               (p->flags & USE_DEFAULTS) ? "dis" : "en", p->base, p->irq,
+               (p->pause & IRQMS) ? "level sensitive" : "edge triggered");
+        /*
+         * Check for Rev C or E boards. Rev E boards can supposedly have
+         * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
+         * It's still not clear extactly what is different about the Rev E
+         * boards, but we think it allows 8 bit entries in the QOUTFIFO to
+         * support "paging" SCBs (more than 4 commands can be active at once).
+         *
+         * The Rev E boards have a read/write autoflush bit in the
+         * SBLKCTL register, while in the Rev C boards it is read only.
+         */
+        sblkctl = inb(p->base + SBLKCTL) ^ AUTOFLUSHDIS;
+        outb(sblkctl, p->base + SBLKCTL);
+        if (inb(p->base + SBLKCTL) == sblkctl)
+        {
+          /*
+           * We detected a Rev E board, we allow paging on this board.
+           */
+          printk("Revision >= E\n");
+          outb(sblkctl & ~AUTOFLUSHDIS, base + SBLKCTL);
+        }
+        else
+        {
+          /* Do not allow paging. */
+          p->flags &= ~PAGE_ENABLED;
+          printk("Revision <= C\n");
+        }
+
+        if (aic7xxx_verbose)
+          printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+                 (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+
+        /*
+         * Set the FIFO threshold and the bus off time.
+         */
+        hostconf = inb(p->base + HOSTCONF);
+        outb(hostconf & DFTHRSH, p->base + BUSSPD);
+        outb((hostconf << 2) & BOFF, p->base + BUSTIME);
+
+        /*
+         * Try to initialize the card and register it with the kernel.
+         */
+        if (aic7xxx_register(template, p))
+        {
+          /*
+           * We successfully found a board and registered it.
+           */
+          found = found + 1;
+        }
+        else
+        {
+          /*
+           * Something went wrong; release and free all resources.
+           */
+          aic7xxx_free(p);
+        }
+      }
       /*
        * Disallow spurious interrupts.
        */
@@ -4758,15 +5917,15 @@
   {
     struct
     {
-      unsigned short vendor_id;
-      unsigned short device_id;
-      aha_type       card_type;
-      aha_chip_type  chip_type;
+      unsigned short      vendor_id;
+      unsigned short      device_id;
+      aha_chip_type       chip_type;
+      aha_chip_class_type chip_class;
     } const aic7xxx_pci_devices[] = {
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AIC_7850, AIC_785x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AIC_7855, AIC_785x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_785x},
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_785x},
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AIC_7860, AIC_786x},
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AIC_7861, AIC_786x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AIC_7870, AIC_787x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AIC_7871, AIC_787x},
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AIC_7872, AIC_787x},
@@ -4779,14 +5938,14 @@
       {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x}
     };
 
-    int error;
+    int error, flags;
     int done = 0;
     unsigned int iobase, mbase;
     unsigned short index = 0;
     unsigned char pci_bus, pci_device_fn;
-    unsigned int  csize_lattime;
-    unsigned int  class_revid;
-    unsigned int  devconfig;
+    unsigned char ultra_enb = 0;
+    unsigned int  devconfig, class_revid;
+    scb_data_type *shared_scb_data = NULL;
     char rev_id[] = {'B', 'C', 'D'};
 
     for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
@@ -4803,36 +5962,33 @@
         }
         else  /* Found an Adaptec PCI device. */
         {
-          config.type = aic7xxx_pci_devices[i].card_type;
-          config.chip_type = aic7xxx_pci_devices[i].chip_type;
-          config.chan_num = 0;
-          config.bios = AIC_ENABLED;  /* Assume bios is enabled. */
-          config.flags = 0;
-          config.busrtime = 40;
-          switch (config.type)
+          chip_class = aic7xxx_pci_devices[i].chip_class;
+          chip_type = aic7xxx_pci_devices[i].chip_type;
+          chan_num = 0;
+          flags = 0;
+          switch (aic7xxx_pci_devices[i].chip_type)
           {
             case AIC_7850:
             case AIC_7855:
-            case AIC_7860:
-            case AIC_7861:
-              config.bios = AIC_DISABLED;
-              config.flags |= USE_DEFAULTS;
-              config.bus_speed = DFTHRSH_100;
+              flags |= USE_DEFAULTS;
               break;
 
             case AIC_7872:  /* 3940 */
             case AIC_7882:  /* 3940-Ultra */
-              config.chan_num = number_of_3940s & 0x1;  /* Has 2 controllers */
+              flags |= MULTI_CHANNEL;
+              chan_num = number_of_3940s & 0x1;  /* Has 2 controllers */
               number_of_3940s++;
               break;
 
             case AIC_7873:  /* 3985 */
             case AIC_7883:  /* 3985-Ultra */
-              config.chan_num = number_of_3985s;  /* Has 3 controllers */
+              chan_num = number_of_3985s;  /* Has 3 controllers */
+              flags |= MULTI_CHANNEL;
               number_of_3985s++;
               if (number_of_3985s == 3)
               {
                 number_of_3985s = 0;
+                shared_scb_data = NULL;
               }
               break;
 
@@ -4849,39 +6005,165 @@
                                             PCI_INTERRUPT_LINE, &irq);
           error += pcibios_read_config_dword(pci_bus, pci_device_fn,
                                             PCI_BASE_ADDRESS_1, &mbase);
+          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                             DEVCONFIG, &devconfig);
+          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+                                             CLASS_PROGIF_REVID, &class_revid);
+
+          printk("aic7xxx: <%s> at PCI %d\n",
+                 board_names[chip_type], PCI_SLOT(pci_device_fn));
 
           /*
-           * The first bit of PCI_BASE_ADDRESS_0 is always set, so
+           * The first bit (LSB) of PCI_BASE_ADDRESS_0 is always set, so
            * we mask it off.
            */
           iobase &= PCI_BASE_ADDRESS_IO_MASK;
 
+          p = aic7xxx_alloc(template, iobase, mbase, chip_type, flags,
+                            shared_scb_data);
+
+          if (p == NULL)
+          {
+            printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+            continue;
+          }
+
+          /* Remember to set the channel number, irq, and chip class. */
+          p->chan_num = chan_num;
+          p->irq = irq;
+          p->chip_class = chip_class;
+#ifdef AIC7XXX_PAGE_ENABLE
+          p->flags |= PAGE_ENABLED;
+#endif
+          p->instance = found;
+
           /*
-           * Read the PCI burst size and latency timer.
+           * Remember how the card was setup in case there is no seeprom.
            */
-          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                             CSIZE_LATTIME, &csize_lattime);
-          printk(KERN_INFO "aic7xxx: BurstLen = %d DWDs, Latency Timer = %d "
-                 "PCLKS\n", (int) (csize_lattime & CACHESIZE),
-                 (csize_lattime >> 8) & 0x000000ff);
+          p->scsi_id = inb(p->base + SCSIID) & OID;
+          if ((p->chip_class == AIC_786x) || (p->chip_class == AIC_788x))
+          {
+            p->flags |= ULTRA_ENABLED;
+            ultra_enb = inb(p->base + SXFRCTL1) & FAST20;
+          }
+	  sxfrctl1 = inb(p->base + SXFRCTL1) & STPWEN;
 
-          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                             CLASS_PROGIF_REVID, &class_revid);
-          if ((class_revid & DEVREVID) < 3)
+          aic7xxx_chip_reset(p);
+
+#ifdef AIC7XXX_USE_EXT_SCBRAM
+          if (devconfig & RAMPSM)
+          {
+            printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
+                   "access.\n");
+            /*
+             * XXX - Assume 9 bit SRAM and enable parity checking.
+             */
+            devconfig |= EXTSCBPEN;
+
+            /*
+             * XXX - Assume fast SRAM and only enable 2 cycle access if we
+             *       are sharing the SRAM across multiple adapters (398x).
+             */
+            if ((devconfig & MPORTMODE) == 0)
+            {
+              devconfig |= EXTSCBTIME;
+            }
+            devconfig &= ~SCBRAMSEL;
+            pcibios_write_config_dword(pci_bus, pci_device_fn,
+                                       DEVCONFIG, devconfig);
+          }
+#endif
+
+          if ((p->flags & USE_DEFAULTS) == 0)
           {
-            printk(KERN_INFO "aic7xxx: %s Rev %c.\n", board_names[config.type],
-                   rev_id[class_revid & DEVREVID]);
+            load_seeprom(p, &sxfrctl1);
           }
 
-          error += pcibios_read_config_dword(pci_bus, pci_device_fn,
-                                             DEVCONFIG, &devconfig);
-          if (error)
+          /*
+           * Take the LED out of diagnostic mode
+           */
+          sblkctl = inb(p->base + SBLKCTL);
+          outb((sblkctl & ~(DIAGLEDEN | DIAGLEDON)), p->base + SBLKCTL);
+
+          /*
+           * We don't know where this is set in the SEEPROM or by the
+           * BIOS, so we default to 100%.
+           */
+          outb(DFTHRSH_100, p->base + DSPCISTATUS);
+
+          if (p->flags & USE_DEFAULTS)
           {
-            panic("aic7xxx: (aic7xxx_detect) Error %d reading PCI registers.\n",
-                  error);
+            int j;
+            /*
+             * Default setup; should only be used if the adapter does
+             * not have a SEEPROM.
+             */
+            /*
+             * Check the target scratch area to see if someone set us
+             * up already.  We are previously set up if the scratch
+             * area contains something other than all zeroes and ones.
+             */
+            for (j = TARG_SCRATCH; j < 0x60; j++)
+            {
+              if (inb(p->base + j) != 0x00)      /* Check for all zeroes. */
+                break;
+            }
+            if (j == TARG_SCRATCH)
+            {
+              for (j = TARG_SCRATCH; j < 0x60; j++)
+              {
+                if (inb(p->base + 1) != 0xFF)    /* Check for all ones. */
+                  break;
+              }
+            }
+            if ((j != 0x60) && (p->scsi_id != 0))
+            {
+              p->flags &= ~USE_DEFAULTS;
+              if (aic7xxx_verbose)
+              {
+                printk(KERN_INFO "aic7xxx: Using leftover BIOS values.\n");
+              }
+            }
+            else
+            {
+              if (aic7xxx_verbose)
+              {
+                printk(KERN_INFO "aic7xxx: No BIOS found; using default "
+                       "settings.\n");
+              }
+              /*
+               * Assume only one connector and always turn on
+               * termination.
+               */
+              sxfrctl1 = STPWEN;
+              p->scsi_id = 7;
+            }
+            outb((p->scsi_id & HSCSIID) | ENSPCHK | RESET_SCSI,
+                 p->base + SCSICONF);
+            /* In case we are a wide card. */
+            outb(p->scsi_id, p->base + SCSICONF + 1);
+            if ((ultra_enb == 0) && ((p->flags & USE_DEFAULTS) == 0))
+            {
+              /*
+               * If there wasn't a BIOS or the board wasn't in this mode
+               * to begin with, turn off Ultra.
+               */
+              p->flags &= ~ULTRA_ENABLED;
+            }
           }
 
-          printk(KERN_INFO "aic7xxx: devconfig = 0x%x.\n", devconfig);
+          /*
+           * Print some additional information about the adapter.
+           */
+          printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%x, "
+                 "IO Mem 0x%x, IRQ %d",
+                 (p->flags & USE_DEFAULTS) ? "dis" : "en",
+                 p->base, p->mbase, p->irq);
+          if ((class_revid & DEVREVID) < 3)
+          {
+            printk(", Revision %c", rev_id[class_revid & DEVREVID]);
+          }
+          printk("\n");
 
           /*
            * I don't think we need to bother with allowing
@@ -4890,58 +6172,57 @@
            */
           aic7xxx_spurious_count = 1;
 
-          config.base = iobase;
-          config.mbase = mbase;
-          config.irq = irq;
-          config.parity = AIC_ENABLED;
-          config.low_term = AIC_UNKNOWN;
-          config.high_term = AIC_UNKNOWN;
           if (aic7xxx_extended)
-            config.flags |= EXTENDED_TRANSLATION;
-#ifdef AIC7XXX_SHARE_SCBs
-          if (devconfig & RAMPSM)
-#else
-          if ((devconfig & RAMPSM) && (config.type != AIC_7873) &&
-              (config.type != AIC_7883))
-#endif
+            p->flags |= EXTENDED_TRANSLATION;
+
+          if (aic7xxx_verbose)
+            printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+                   (p->flags & EXTENDED_TRANSLATION) ? "en" : "dis");
+
+          /*
+           * Put our termination setting into sxfrctl1 now that the
+           * generic initialization is complete.
+           */
+          sxfrctl1 |= inb(p->base + SXFRCTL1);
+          outb(sxfrctl1, p->base + SXFRCTL1);
+
+          if (aic7xxx_register(template, p) == 0)
+          {
+            aic7xxx_free(p);
+          }
+          else
           {
+            found = found + 1;
+
+#ifdef AIC7XXX_USE_EXT_SCBRAM
             /*
-             * External SRAM present.  The probe will walk the SCBs to see
-             * how much SRAM we have and set the number of SCBs accordingly.
-             * We have to turn off SCBRAMSEL to access the external SCB
-             * SRAM.
-             *
-             * It seems that early versions of the aic7870 didn't use these
-             * bits, hence the hack for the 3940 above.  I would guess that
-             * recent 3940s using later aic7870 or aic7880 chips do actually
-             * set RAMPSM.
+             * Set the shared SCB data once we've successfully probed a
+             * 398x adapter.
              *
-             * The documentation isn't clear, but it sounds like the value
-             * written to devconfig must not have RAMPSM set.  The second
-             * sixteen bits of the register are R/O anyway, so it shouldn't
-             * affect RAMPSM either way.
+             * Note that we can only do this if the use of external
+             * SCB RAM is enabled.
              */
-            printk(KERN_INFO "aic7xxx: External RAM detected; enabling RAM "
-                   "access.\n");
-            devconfig &= ~(RAMPSM | SCBRAMSEL);
-            pcibios_write_config_dword(pci_bus, pci_device_fn,
-                                       DEVCONFIG, devconfig);
+            if ((p->chip_type == AIC_7873) || (p->chip_type == AIC_7883))
+            {
+              if (shared_scb_data == NULL)
+              {
+                shared_scb_data = p->scb_data;
+              }
+            }
+#endif
           }
-          found += aic7xxx_register(template, &config);
 
+          index++;
           /*
            * Disable spurious interrupts.
            */
           aic7xxx_spurious_count = 0;
-
-          index++;
         }  /* Found an Adaptec PCI device. */
       }
     }
   }
 #endif CONFIG_PCI
 
-  template->name = aic7xxx_info(NULL);
   return (found);
 }
 
@@ -4957,45 +6238,45 @@
 aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
     struct aic7xxx_scb *scb)
 {
-  unsigned int addr; /* must be 32 bits */
   unsigned short mask;
+  struct aic7xxx_hwscb *hscb;
 
   mask = (0x01 << TARGET_INDEX(cmd));
+  hscb = scb->hscb;
+
   /*
    * Setup the control byte if we need negotiation and have not
    * already requested it.
    */
-#ifdef AIC7XXX_TAGGED_QUEUEING
-  if (cmd->device->tagged_queue)
+  if (p->discenable & mask)
   {
-    cmd->tag = scb->tag;
-    cmd->device->current_tag = scb->tag;
-    scb->control |= TAG_ENB;
-    p->device_status[TARGET_INDEX(cmd)].commands_sent++;
-    if (p->device_status[TARGET_INDEX(cmd)].commands_sent == 200)
-    {
-      scb->control |= 0x02;
-      p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
-    }
-#if 0
-    if (p->orderedtag & mask)
+    hscb->control |= DISCENB;
+#ifdef AIC7XXX_TAGGED_QUEUEING
+    if (cmd->device->tagged_queue)
     {
-      scb->control |= 0x02;
-      p->orderedtag = p->orderedtag & ~mask;
+      cmd->tag = hscb->tag;
+      p->device_status[TARGET_INDEX(cmd)].commands_sent++;
+      if (p->device_status[TARGET_INDEX(cmd)].commands_sent < 75)
+      {
+        hscb->control |= MSG_SIMPLE_Q_TAG;
+      }
+      else
+      {
+        hscb->control |= MSG_ORDERED_Q_TAG;
+        p->device_status[TARGET_INDEX(cmd)].commands_sent = 0;
+      }
     }
-#endif
-  }
-#endif
-  if (p->discenable & mask)
-  {
-    scb->control |= DISCENB;
+#endif  /* Tagged queueing */
   }
+
   if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
   {
     p->wdtr_pending |= mask;
-    scb->control |= NEEDWDTR;
+    hscb->control |= MK_MESSAGE;
+    scb->flags |= SCB_MSGOUT_WDTR;
 #if 0
-    printk("aic7xxx: Sending WDTR request to target %d.\n", cmd->target);
+    printk("scsi%d: Sending WDTR request to target %d.\n",
+           p->host_no, cmd->target);
 #endif
   }
   else
@@ -5003,19 +6284,20 @@
     if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
     {
       p->sdtr_pending |= mask;
-      scb->control |= NEEDSDTR;
+      hscb->control |= MK_MESSAGE;
+      scb->flags |= SCB_MSGOUT_SDTR;
 #if 0
-      printk("aic7xxx: Sending SDTR request to target %d.\n", cmd->target);
+      printk("scsi%d: Sending SDTR request to target %d.\n",
+             p->host_no, cmd->target);
 #endif
     }
   }
-
 #if 0
   printk("aic7xxx: (build_scb) Target %d, cmd(0x%x) size(%u) wdtr(0x%x) "
          "mask(0x%x).\n",
 	 cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask);
 #endif
-  scb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
+  hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
 	((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
 
   /*
@@ -5029,9 +6311,8 @@
    * XXX - this relies on the host data being stored in a
    *       little-endian format.
    */
-  addr = VIRT_TO_BUS(cmd->cmnd);
-  scb->SCSI_cmd_length = cmd->cmd_len;
-  memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
+  hscb->SCSI_cmd_length = cmd->cmd_len;
+  hscb->SCSI_cmd_pointer = VIRT_TO_BUS(cmd->cmnd);
 
   if (cmd->use_sg)
   {
@@ -5051,15 +6332,16 @@
       scb->sg_list[i].address = VIRT_TO_BUS(sg[i].address);
       scb->sg_list[i].length = (unsigned int) sg[i].length;
     }
-    scb->SG_segment_count = cmd->use_sg;
-    addr = VIRT_TO_BUS(scb->sg_list);
-    memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
-    memcpy(scb->data_pointer, &(scb->sg_list[0].address),
-           sizeof(scb->data_pointer));
-    scb->data_count = scb->sg_list[0].length;
+    hscb->SG_list_pointer = VIRT_TO_BUS(scb->sg_list);
+    hscb->SG_segment_count = cmd->use_sg;
+    scb->sg_count = hscb->SG_segment_count;
+
+    /* Copy the first SG into the data pointer area. */
+    hscb->data_pointer = scb->sg_list[0].address;
+    hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
 #if 0
     printk("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n",
-           cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count);
+           cmd->use_sg, aic7xxx_length(cmd, 0), hscb->data_count);
 #endif
   }
   else
@@ -5068,28 +6350,23 @@
   printk("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n",
 	(unsigned long) cmd->request_buffer, cmd->request_bufflen);
 #endif
-    if (cmd->request_bufflen == 0)
+    if (cmd->request_bufflen)
     {
-      /*
-       * In case the higher level SCSI code ever tries to send a zero
-       * length command, ensure the SCB indicates no data.  The driver
-       * will interpret a zero length command as a Bus Device Reset.
-       */
-      scb->SG_segment_count = 0;
-      memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
-      memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
-      scb->data_count = 0;
+      hscb->SG_segment_count = 1;
+      scb->sg_count = 1;
+      scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
+      scb->sg_list[0].length = cmd->request_bufflen;
+      hscb->SG_list_pointer = VIRT_TO_BUS(&scb->sg_list[0]);
+      hscb->data_count = scb->sg_list[0].length | (SCB_LIST_NULL << 24);
+      hscb->data_pointer = VIRT_TO_BUS(cmd->request_buffer);
     }
     else
     {
-      scb->SG_segment_count = 1;
-      scb->sg_list[0].address = VIRT_TO_BUS(cmd->request_buffer);
-      scb->sg_list[0].length = cmd->request_bufflen;
-      addr = VIRT_TO_BUS(&scb->sg_list[0]);
-      memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
-      scb->data_count = scb->sg_list[0].length;
-      addr = VIRT_TO_BUS(cmd->request_buffer);
-      memcpy(scb->data_pointer, &addr, sizeof(scb->data_pointer));
+      hscb->SG_segment_count = 0;
+      scb->sg_count = 0;
+      hscb->SG_list_pointer = 0;
+      hscb->data_pointer = 0;
+      hscb->data_count = SCB_LIST_NULL << 24;
     }
   }
 }
@@ -5107,7 +6384,6 @@
   long processor_flags;
   struct aic7xxx_host *p;
   struct aic7xxx_scb *scb;
-  u_char curscb, intstat;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
   if (p->host != cmd->host)
@@ -5139,34 +6415,21 @@
 	cmd->lun & 0x07);
 #endif
 
-  /*
-   * This is a critical section, since we don't want the interrupt
-   * routine mucking with the host data or the card.  For this reason
-   * it is nice to know that this function can only be called in one
-   * of two ways from scsi.c  First, as part of a routine queue command,
-   * in which case, the irq for our card is disabled before this
-   * function is called.  This doesn't help us if there is more than
-   * one card using more than one IRQ in our system, therefore, we
-   * should disable all interrupts on these grounds alone.  Second,
-   * this can be called as part of the scsi_done routine, in which case
-   * we are in the aic7xxx_isr routine already and interrupts are
-   * disabled, therefore we should saveflags first, then disable the
-   * interrupts, do our work, then restore the CPU flags. If it weren't
-   * for the possibility of more than one card using more than one IRQ
-   * in our system, we wouldn't have to touch the interrupt flags at all.
-   */
-  save_flags(processor_flags);
-  cli();
-
+  if (p->device_status[TARGET_INDEX(cmd)].active_cmds
+      > cmd->device->queue_depth)
+  {
+    printk(KERN_WARNING "(scsi%d:%d:%d) Commands queued exceeds queue depth\n",
+           p->host_no, cmd->target, cmd->channel);
+  }
   scb = aic7xxx_allocate_scb(p);
   if (scb == NULL)
   {
-    panic("aic7xxx: (aic7xxx_free) Couldn't find a free SCB.\n");
+    panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n");
   }
   else
   {
     scb->cmd = cmd;
-    aic7xxx_position(cmd) = scb->tag;
+    aic7xxx_position(cmd) = scb->hscb->tag;
 #if 0
     debug_scb(scb);
 #endif;
@@ -5178,14 +6441,14 @@
     aic7xxx_buildscb(p, cmd, scb);
 
 #if 0
-    if (scb != (p->scb_array[scb->position]))
+    if (scb != (p->scb_data->scb_array[scb->hscb->tag]))
     {
       printk("aic7xxx: (queue) Address of SCB by position does not match SCB "
              "address.\n");
     }
     printk("aic7xxx: (queue) SCB pos(%d) cmdptr(0x%x) state(%d) freescb(0x%x)\n",
-	   scb->position, (unsigned int) scb->cmd,
-	   scb->state, (unsigned int) p->free_scb);
+	   scb->hscb->tag, (unsigned int) scb->cmd,
+	   scb->flags, (unsigned int) p->free_scb);
 #endif
 
     /*
@@ -5200,70 +6463,28 @@
     cmd->host_scribble = NULL;
     memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
 
-    if (scb->position != SCB_LIST_NULL)
-    {
-      /* We've got a valid slot, yeah! */
-      if (p->flags & IN_ISR)
-      {
-        scbq_insert_tail(&p->assigned_scbs, scb);
-        scb->state |= SCB_ASSIGNEDQ;
-      }
-      else
-      {
-        /*
-         * Pause the sequencer so we can play with its registers -
-         * wait for it to acknowledge the pause.
-         *
-         * XXX - should the interrupts be left on while doing this?
-         */
-        PAUSE_SEQUENCER(p);
-        intstat = inb(INTSTAT + p->base);
-
-        /*
-         * Save the SCB pointer and put our own pointer in - this
-         * selects one of the four banks of SCB registers. Load
-         * the SCB, then write its pointer into the queue in FIFO
-         * and restore the saved SCB pointer.
-         */
-        curscb = inb(SCBPTR + p->base);
-        outb(scb->position, SCBPTR + p->base);
-        aic7xxx_putscb(p, scb);
-        outb(curscb, SCBPTR + p->base);
-        outb(scb->position, QINFIFO + p->base);
-        scb->state |= SCB_ACTIVE;
+    scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
 
-        /*
-         * Guard against unpausing the sequencer if there is an interrupt
-         * waiting to happen.
-         */
-        if (!(intstat & (BRKADRINT | SEQINT | SCSIINT)))
-        {
-          UNPAUSE_SEQUENCER(p);
-        }
-      }
-    }
-    else
+    save_flags(processor_flags);
+    cli();
+    scbq_insert_tail(&p->waiting_scbs, scb);
+    if ((p->flags & (IN_ISR | IN_TIMEOUT)) == 0)
     {
-      scb->state |= SCB_WAITINGQ;
-      scbq_insert_tail(&p->waiting_scbs, scb);
-      if (!(p->flags & IN_ISR))
-      {
-        aic7xxx_run_waiting_queues(p);
-      }
+      aic7xxx_run_waiting_queues(p);
     }
 
+    restore_flags(processor_flags);
 #if 0
     printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n",
-           (long) cmd, (long) scb->cmd, scb->position);
+           (long) cmd, (long) scb->cmd, scb->hscb->tag);
 #endif;
-    restore_flags(processor_flags);
   }
   return (0);
 }
 
 /*+F*************************************************************************
  * Function:
- *   aic7xxx_abort_reset
+ *   aic7xxx_bus_device_reset
  *
  * Description:
  *   Abort or reset the current SCSI command(s).  If the scb has not
@@ -5275,204 +6496,257 @@
 static int
 aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
 {
-  struct aic7xxx_scb  *scb;
+  struct aic7xxx_scb   *scb;
+  struct aic7xxx_hwscb *hscb;
   unsigned char bus_state;
-  int base, result = -1;
+  int result = -1;
   char channel;
 
-  scb = (p->scb_array[aic7xxx_position(cmd)]);
-  base = p->base;
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+  hscb = scb->hscb;
+
+  /*
+   * Ensure that the card doesn't do anything behind our back.
+   * Also make sure that we didn't just miss an interrupt that
+   * could affect this abort/reset.
+   */
+  pause_sequencer(p);
+  while (inb(p->base + INTSTAT) & INT_PEND);
+  {
+    aic7xxx_isr(p->irq, (void *) NULL, (void *) NULL);
+    pause_sequencer(p);
+  } 
+  if ((cmd != scb->cmd) || ((scb->flags & SCB_ACTIVE) == 0))
+  {
+    result = SCSI_RESET_NOT_RUNNING;
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+    return(result);
+  }
+
+
+  printk(KERN_WARNING "(scsi%d:%d:%d) Abort_reset, scb flags 0x%x, ",
+         p->host_no, TC_OF_SCB(scb), scb->flags);
+  bus_state = inb(p->base + LASTPHASE);
 
-  channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
-  if ((cmd == scb->cmd) && (scb->state & SCB_IN_PROGRESS))
+  switch (bus_state)
   {
+    case P_DATAOUT:
+      printk("Data-Out phase, ");
+      break;
+    case P_DATAIN:
+      printk("Data-In phase, ");
+      break;
+    case P_COMMAND:
+      printk("Command phase, ");
+      break;
+    case P_MESGOUT:
+      printk("Message-Out phase, ");
+      break;
+    case P_STATUS:
+      printk("Status phase, ");
+      break;
+    case P_MESGIN:
+      printk("Message-In phase, ");
+      break;
+    default:
+      /*
+       * We're not in a valid phase, so assume we're idle.
+       */
+      printk("while idle, LASTPHASE = 0x%x, ", bus_state);
+      break;
+  }
+  printk("SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 0x%x\n",
+         inb(p->base + SCSISIGI),
+         inb(p->base + SEQADDR0) | (inb(p->base + SEQADDR1) << 8),
+         inb(p->base + SSTAT0), inb(p->base + SSTAT1));
 
-    if (scb->state & SCB_IN_PROGRESS)
+  channel = hscb->target_channel_lun & SELBUSB ? 'B': 'A';
+  /*
+   * Determine our course of action.
+   */
+  if (scb->flags & SCB_ABORT)
+  {
+    /*
+     * Been down this road before; do a full bus reset.
+     */
+    scb->flags |= SCB_RECOVERY_SCB;
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+    result = -1;
+  }
+#if 0
+  else if (hscb->control & TAG_ENB)
     {
       /*
-       * Ensure that the card doesn't do anything
-       * behind our back.
+       * We could be starving this command; try sending and ordered tag
+       * command to the target we come from.
        */
-      PAUSE_SEQUENCER(p);
+      scb->flags |= SCB_SENTORDEREDTAG | SCB_RECOVERY_SCB;
+      p->orderedtag = p->orderedtag | 0xFF;
+      result = SCSI_RESET_PENDING;
+      unpause_sequencer(p, /* unpause_always */ TRUE);
+      printk(KERN_WARNING "scsi%d: Abort_reset, odered tag queued.\n",
+             p->host_no);
+    }
+#endif
+  else
+  {
+    unsigned char active_scb_index, saved_scbptr;
+    struct aic7xxx_scb *active_scb;
 
-      printk(KERN_WARNING "aic7xxx: (abort_reset) scb state 0x%x, ", scb->state);
-      bus_state = inb(LASTPHASE + p->base);
+    /*
+     * Send an Abort Message:
+     * The target that is holding up the bus may not be the same as
+     * the one that triggered this timeout (different commands have
+     * different timeout lengths).  Our strategy here is to queue an
+     * abort message to the timed out target if it is disconnected.
+     * Otherwise, if we have an active target we stuff the message buffer
+     * with an abort message and assert ATN in the hopes that the target
+     * will let go of the bus and go to the mesgout phase.  If this
+     * fails, we'll get another timeout a few seconds later which will
+     * attempt a bus reset.
+     */
+    saved_scbptr = inb(p->base + SCBPTR);
+    active_scb_index = inb(p->base + SCB_TAG);
+    active_scb = p->scb_data->scb_array[active_scb_index];
 
-      switch (bus_state)
+    if (bus_state != P_BUSFREE)
+    {
+      if (active_scb_index >= p->scb_data->numscbs)
       {
-	case P_DATAOUT:
-          printk("Data-Out phase, ");
-          break;
-	case P_DATAIN:
-          printk("Data-In phase, ");
-          break;
-	case P_COMMAND:
-          printk("Command phase, ");
-          break;
-	case P_MESGOUT:
-          printk("Message-Out phase, ");
-          break;
-	case P_STATUS:
-          printk("Status phase, ");
-          break;
-	case P_MESGIN:
-          printk("Message-In phase, ");
-          break;
-	default:
-          printk("while idle, LASTPHASE = 0x%x, ", bus_state);
-          /*
-           * We're not in a valid phase, so assume we're idle.
-           */
-          bus_state = 0;
-          break;
+        /*
+         * Perform a bus reset.
+         *
+         * XXX - We want to queue an abort for the timedout SCB
+         *       instead.
+         */
+        result = -1;
+        printk(KERN_WARNING "scsi%d: Invalid SCB ID %d is active, "
+               "SCB flags = 0x%x.\n", p->host_no, scb->hscb->tag, scb->flags);
       }
-      printk("SCSISIGI = 0x%x\n", inb(p->base + SCSISIGI));
-
-      /*
-       * First, determine if we want to do a bus reset or simply a bus device
-       * reset.  If this is the first time that a transaction has timed out
-       * and the SCB is not paged out, just schedule a bus device reset.
-       * Otherwise, we reset the bus and abort all pending I/Os on that bus.
-       */
-      if (!(scb->state & (SCB_ABORTED | SCB_PAGED_OUT)))
+      else
       {
-#if 0
-	if (scb->control & TAG_ENB)
-	{
+        /* Send the abort message to the active SCB. */
+        outb(1, p->base + MSG_LEN);
+        if (active_scb->hscb->control & TAG_ENB)
+        {
+          outb(MSG_ABORT_TAG, p->base + MSG_OUT);
+        }
+        else
+        {
+          outb(MSG_ABORT, p->base + MSG_OUT);
+        }
+        outb(bus_state | ATNO, p->base + SCSISIGO);
+        printk(KERN_WARNING "scsi%d: abort message in message buffer\n",
+               p->host_no);
+        active_scb->flags |= SCB_ABORT | SCB_RECOVERY_SCB;
+        if (active_scb != scb)
+        {
           /*
-           * We could be starving this command; try sending and ordered tag
-           * command to the target we come from.
+           * XXX - We would like to increment the timeout on scb, but
+           *       access to that routine is denied because it is hidden
+           *       in scsi.c.  If we were able to do this, it would give
+           *       scb a new lease on life.
            */
-          scb->state = scb->state | SCB_ABORTED | SCB_SENTORDEREDTAG;
-          p->orderedtag = p->orderedtag | 0xFF;
           result = SCSI_RESET_PENDING;
-          UNPAUSE_SEQUENCER(p);
-          printk(KERN_WARNING "aic7xxx: (abort_reset) Ordered tag queued.\n");
-	}
-#endif
-	unsigned char active_scb, control;
-	struct aic7xxx_scb *active_scbp;
+          aic7xxx_error(active_scb->cmd) = DID_RESET;
+        }
+        else
+        {
+          aic7xxx_error(scb->cmd) = DID_RESET;
+          result = SCSI_RESET_PENDING;
+        }
+        unpause_sequencer(p, /* unpause_always */ TRUE);
+      }
+    }
+    else
+    {
+      unsigned char hscb_index, linked_next;
+      int disconnected;
 
-	/*
-	 * Send a Bus Device Reset Message:
-	 * The target we select to send the message to may be entirely
-	 * different than the target pointed to by the scb that timed
-	 * out.  If the command is in the QINFIFO or the waiting for
-	 * selection list, its not tying up the bus and isn't responsible
-	 * for the delay so we pick off the active command which should
-	 * be the SCB selected by SCBPTR.  If its disconnected or active,
-	 * we device reset the target scbp points to.  Although it may
-	 * be that this target is not responsible for the delay, it may
-	 * may also be that we're timing out on a command that just takes
-	 * too much time, so we try the bus device reset there first.
-	 */
-	active_scb = inb(SCBPTR + base);
-	active_scbp = (p->scb_array[inb(SCB_TAG + base)]);
-	control = inb(SCB_CONTROL + base);
+      disconnected = FALSE;
+      hscb_index = aic7xxx_find_scb(p, scb);
+      if (hscb_index == SCB_LIST_NULL)
+      {
+        disconnected = TRUE;
+        linked_next = (scb->hscb->data_count >> 24) & 0xFF;
+      }
+      else
+      {
+        outb(hscb_index, p->base + SCBPTR);
+        if (inb(p->base + SCB_CONTROL) & DISCONNECTED)
+        {
+          disconnected = TRUE;
+        }
+        linked_next = inb(p->base + SCB_LINKED_NEXT);
+      }
+      if (disconnected)
+      {
+        /*
+         * Simply set the ABORT_SCB control bit and preserve the
+         * linked next pointer.
+         */
+        scb->hscb->control |= ABORT_SCB | MK_MESSAGE;
+        scb->hscb->data_count &= ~0xFF000000;
+        scb->hscb->data_count |= linked_next << 24;
+        if ((p->flags & PAGE_ENABLED) == 0)
+        {
+          scb->hscb->control &= ~DISCONNECTED;
+        }
+        scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+        if (hscb_index != SCB_LIST_NULL)
+        {
+          unsigned char scb_control;
 
-	/*
-	 * Test to see if scbp is disconnected
-	 */
-	outb(scb->position, SCBPTR + base);
-	if (inb(SCB_CONTROL + base) & DISCONNECTED)
-	{
-#ifdef AIC7XXX_DEBUG_ABORT
-          printk("aic7xxx: (abort_scb) scb %d is disconnected; "
-                 "bus device reset message queued.\n", scb->position);
-#endif
-          if (p->flags & PAGE_ENABLED)
-          {
-            /* Pull this SCB out of the disconnected list. */
-            u_char prev = inb(SCB_PREV + base);
-            u_char next = inb(SCB_NEXT + base);
-            if (prev == SCB_LIST_NULL)
-            {
-              /* Head of list */
-              outb(next, DISCONNECTED_SCBH + base);
-            }
-            else
-            {
-              outb(prev, SCBPTR + base);
-              outb(next, SCB_NEXT + base);
-              if (next != SCB_LIST_NULL)
-              {
-        	outb(next, SCBPTR + base);
-        	outb(prev, SCB_PREV + base);
-              }
-              outb(scb->position, SCBPTR + base);
-            }
-          }
-	  scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
-          scb->control = scb->control & DISCENB;
-          scb->SCSI_cmd_length = 0;
-	  scb->SG_segment_count = 0;
-	  memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
-	  memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
-	  scb->data_count = 0;
-	  aic7xxx_putscb(p, scb);
-	  aic7xxx_add_waiting_scb(base, scb);
-	  outb(active_scb, SCBPTR + base);
-          result = SCSI_RESET_PENDING;
-	  UNPAUSE_SEQUENCER(p);
-	}
-	else
-	{
-	  /*
-	   * Is the active SCB really active?
-	   */
-	  if ((active_scbp->state & SCB_ACTIVE) && bus_state)
-	  {
-            /*
-             * Load the message buffer and assert attention.
-             */
-            active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
-            outb(1, MSG_LEN + base);
-            outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
-            outb(bus_state | ATNO, SCSISIGO + base);
-#ifdef AIC7XXX_DEBUG_ABORT
-            printk("aic7xxx: (abort_scb) asserted ATN - "
-                   "bus device reset in message buffer.\n");
-#endif
-            if (active_scbp != scb)
-            {
-              /*
-               * XXX - We would like to increment the timeout on scb, but
-               *       access to that routine is denied because it is hidden
-               *       in scsi.c.  If we were able to do this, it would give
-               *       scb a new lease on life.
-               */
-              ;
-            }
-            aic7xxx_error(scb->cmd) = DID_RESET;
-            /*
-             * Restore the active SCB and unpause the sequencer.
-             */
-            outb(active_scb, SCBPTR + base);
-            if (active_scbp != scb)
-            {
-              /*
-               * The mid-level SCSI code requested us to reset a command
-               * different from the one that we actually reset.  Return
-               * a "not running" indication and hope that the SCSI code
-               * will Do the Right Thing (tm).
-               */
-              result = SCSI_RESET_NOT_RUNNING;
-            }
-            else
-            {
-              result = SCSI_RESET_PENDING;
-            }
-            UNPAUSE_SEQUENCER(p);
-	  }
-	}
+          scb_control = inb(p->base + SCB_CONTROL);
+          outb(scb_control | MK_MESSAGE| ABORT_SCB, p->base + SCB_CONTROL);
+        }
+        /*
+         * Actually requeue this SCB in case we can select the
+         * device before it reconnects.  If the transaction we
+         * want to abort is not tagged, unbusy it first so that
+         * we don't get held back from sending the command.
+         */
+        if ((scb->hscb->control & TAG_ENB) == 0)
+        {
+          unsigned char target;
+          int lun;
+
+          target = scb->cmd->target;
+          lun = scb->cmd->lun;
+          aic7xxx_search_qinfifo(p, target, channel, lun, SCB_LIST_NULL,
+              0, /* requeue */ TRUE);
+        }
+        printk(KERN_WARNING "(scsi%d:%d:%d) Queueing an Abort SCB.\n",
+               p->host_no, TC_OF_SCB(scb));
+        scbq_insert_head(&p->waiting_scbs, scb);
+        scb->flags |= SCB_WAITINGQ;
+        outb(saved_scbptr, p->base + SCBPTR);
+        if ((p->flags & IN_ISR) == 0)
+        {
+          /*
+           * Processing the waiting queue may unpause us.
+           */
+          aic7xxx_run_waiting_queues(p);
+          /*
+           * If we are using AAP, aic7xxx_run_waiting_queues() will not
+           * unpause us, so ensure we are unpaused.
+           */
+          unpause_sequencer(p, /*unpause_always*/ FALSE);
+        }
+        else
+        {
+          unpause_sequencer(p, /*unpause_always*/ TRUE);
+        }
+        result = SCSI_RESET_PENDING;
+      }
+      else
+      {
+        scb->flags |= SCB_RECOVERY_SCB;
+        unpause_sequencer(p, /* unpause_always */ TRUE);
+        result = -1;
       }
     }
   }
-  /* Make sure the sequencer is unpaused upon return. */
-  if (result == -1)
-  {
-    UNPAUSE_SEQUENCER(p);
-  }
   return (result);
 }
 
@@ -5490,16 +6764,48 @@
   struct aic7xxx_scb  *scb = NULL;
   struct aic7xxx_host *p;
   int    base, result;
+  unsigned long processor_flags;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = (p->scb_array[aic7xxx_position(cmd)]);
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
   base = p->base;
 
+  save_flags(processor_flags);
+  cli();
+
 #ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (abort) Aborting scb %d, TCL %d/%d/%d\n",
-         scb->position, TCL_OF_SCB(scb));
+  if (scb != NULL)
+  {
+    printk("(scsi%d:%d:%d) Aborting scb %d, flags 0x%x\n",
+           p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+  }
+  else
+  {
+    printk("aic7xxx: Abort called with no SCB for cmd.\n");
+  }
 #endif
 
+  if (p->flags & IN_TIMEOUT)
+  {
+    /*
+     * We've already started a recovery operation.
+     */
+    if ((scb->flags & SCB_RECOVERY_SCB) == 0)
+    {
+      restore_flags(processor_flags);
+      return (SCSI_ABORT_PENDING);
+    }
+    else
+    {
+      /*
+       * This is the second time we've tried to abort the recovery
+       * SCB.  We want the mid-level SCSI code to call the reset
+       * function to reset the SCSI bus.
+       */
+      restore_flags(processor_flags);
+      return (SCSI_ABORT_NOT_RUNNING);
+    }
+  }
   if (cmd->serial_number != cmd->serial_number_at_timeout)
   {
     result = SCSI_ABORT_NOT_RUNNING;
@@ -5508,14 +6814,34 @@
   {
     result = SCSI_ABORT_NOT_RUNNING;
   }
-  else if ((scb->cmd != cmd) || (!(scb->state & SCB_IN_PROGRESS)))
+  else if ((scb->cmd != cmd) || (!(scb->flags & SCB_ACTIVE)))
   {
     result = SCSI_ABORT_NOT_RUNNING;
   }
   else
   {
-    result = SCSI_ABORT_SNOOZE;
+    /*
+     * XXX - Check use of IN_TIMEOUT to see if we're Doing the
+     *       Right Thing with it.
+     */
+    p->flags |= IN_TIMEOUT;
+    result = aic7xxx_bus_device_reset(p, scb->cmd);
+    switch (result)
+    {
+      case SCSI_RESET_NOT_RUNNING:
+        p->flags &= ~IN_TIMEOUT;
+        result = SCSI_ABORT_NOT_RUNNING;
+        break;
+      case SCSI_RESET_PENDING:
+        result = SCSI_ABORT_PENDING;
+        break;
+      default:
+        p->flags &= ~IN_TIMEOUT;
+        result = SCSI_ABORT_SNOOZE;
+        break;
+     }
   }
+  restore_flags(processor_flags);
   return (result);
 }
 
@@ -5535,18 +6861,27 @@
 {
   struct aic7xxx_scb *scb = NULL;
   struct aic7xxx_host *p;
-  int    base, found, tindex, min_target, max_target, result = -1;
+  int    base, found, tindex, min_target, max_target;
+  int    result = -1;
   char   channel = 'A';
   unsigned long processor_flags;
 
   p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = (p->scb_array[aic7xxx_position(cmd)]);
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
   base = p->base;
   channel = cmd->channel ? 'B': 'A';
   tindex = (cmd->channel << 4) | cmd->target;
 
-#ifdef AIC7XXX_DEBUG_ABORT
-  printk("aic7xxx: (reset) target/channel %d/%d\n", cmd->target, cmd->channel);
+#ifdef 0   /* AIC7XXX_DEBUG_ABORT */
+  if (scb != NULL)
+  {
+    printk("(scsi%d:%d:%d) Reset called, scb %d, flags 0x%x\n",
+           p->host_no, TC_OF_SCB(scb), scb->hscb->tag, scb->flags);
+  }
+  else
+  {
+    printk("aic7xxx: Reset called with no SCB for cmd.\n");
+  }
 #endif
 
   /* 
@@ -5561,34 +6896,45 @@
   if (scb->cmd != cmd)
     scb = NULL;
 
-  if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET)) 
-      && (scb != NULL))
+  if (p->flags & IN_TIMEOUT)
   {
     /*
-     * Attempt a bus device reset if commands have completed successfully
-     * since the last bus device reset, or it has been less than 100ms
-     * since the last reset.
+     * We've already started a recovery operation.
      */
-    if ((p->flags & DEVICE_SUCCESS) ||
-        ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
+    if ((scb->flags & SCB_RECOVERY_SCB) == 0)
     {
-      if (cmd->serial_number != cmd->serial_number_at_timeout)
-      {
-        result = SCSI_RESET_NOT_RUNNING;
-      }
-      else
+      restore_flags(processor_flags);
+      return (SCSI_RESET_PENDING);
+    }
+  }
+  else
+  {
+    if (!(flags & (SCSI_RESET_SUGGEST_HOST_RESET | SCSI_RESET_SUGGEST_BUS_RESET))
+        && (scb != NULL))
+    {
+      /*
+       * Attempt a bus device reset if commands have completed successfully
+       * since the last bus device reset, or it has been less than 100ms
+       * since the last reset.
+       */
+      if ((p->flags & DEVICE_SUCCESS) ||
+          ((jiffies - p->device_status[tindex].last_reset) < HZ/10))
       {
-        if (scb == NULL)
+	if (cmd->serial_number != cmd->serial_number_at_timeout)
+	{
+          result = SCSI_RESET_NOT_RUNNING;
+	}
+	else if (scb == NULL)
         {
           result = SCSI_RESET_NOT_RUNNING;
         }
         else if (flags & SCSI_RESET_ASYNCHRONOUS)
         {
-          if (scb->state & SCB_ABORTED)
+          if (scb->flags & SCB_ABORTED)
           {
             result = SCSI_RESET_PENDING;
           }
-          else if (!(scb->state & SCB_IN_PROGRESS))
+          else if (!(scb->flags & SCB_ACTIVE))
           {
             result = SCSI_RESET_NOT_RUNNING;
           }
@@ -5599,20 +6945,23 @@
           if ((flags & SCSI_RESET_SYNCHRONOUS) &&
               (p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING))
           {
-            scb->state |= SCB_ABORTED;
+            scb->flags |= SCB_ABORTED;
             result = SCSI_RESET_PENDING;
           }
           else
           {
+            p->flags |= IN_TIMEOUT;
             result = aic7xxx_bus_device_reset(p, cmd);
             if (result == 0)
+            {
+              p->flags &= ~IN_TIMEOUT;
               result = SCSI_RESET_PENDING;
+            }
           }
-        }
+	}
       }
     }
   }
-
   if (result == -1)
   {
     /*
@@ -5625,11 +6974,11 @@
       {
 	result = SCSI_RESET_NOT_RUNNING;
       }
-      else if (!(scb->state & SCB_IN_PROGRESS))
+      else if (!(scb->flags & SCB_ACTIVE))
       {
 	result = SCSI_RESET_NOT_RUNNING;
       }
-      else if ((scb->state & SCB_ABORTED) &&
+      else if ((scb->flags & SCB_ABORTED) &&
                (!(p->device_status[tindex].flags & BUS_DEVICE_RESET_PENDING)))
       {
 	result = SCSI_RESET_PENDING;
@@ -5641,8 +6990,9 @@
       /*
        * The reset channel function assumes that the sequencer is paused.
        */
-      PAUSE_SEQUENCER(p);
+      pause_sequencer(p);
       found = aic7xxx_reset_channel(p, channel, TRUE);
+      p->flags = p->flags & ~IN_TIMEOUT;
 
       /*
        * If this is a synchronous reset and there is no SCB for this
@@ -5688,8 +7038,10 @@
       }
 
       result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
+      p->flags &= ~IN_TIMEOUT;
     }
   }
+  aic7xxx_run_waiting_queues(p);
   restore_flags(processor_flags);
   return (result);
 }
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx.h linux/drivers/scsi/aic7xxx.h
--- v2.0.30/linux/drivers/scsi/aic7xxx.h	Sat Aug 17 11:19:27 1996
+++ linux/drivers/scsi/aic7xxx.h	Thu Jul 31 12:37:17 1997
@@ -40,13 +40,13 @@
 	aic7xxx_info,						\
 	NULL,							\
 	aic7xxx_queue,						\
-	aic7xxx_abort,						\
+	NULL,							\
 	aic7xxx_reset,						\
 	NULL,							\
 	aic7xxx_biosparam,					\
 	-1,			/* max simultaneous cmds      */\
 	-1,			/* scsi id of host adapter    */\
-	SG_ALL,			/* max scatter-gather cmds    */\
+	0,			/* max scatter-gather cmds    */\
 	2,			/* cmds per lun (linked cmds) */\
 	0,			/* number of 7xxx's present   */\
 	0,			/* no memory DMA restrictions */\
@@ -57,7 +57,6 @@
 extern int aic7xxx_biosparam(Disk *, kdev_t, int[]);
 extern int aic7xxx_detect(Scsi_Host_Template *);
 extern int aic7xxx_command(Scsi_Cmnd *);
-extern int aic7xxx_abort(Scsi_Cmnd *);
 extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int);
 
 extern const char *aic7xxx_info(struct Scsi_Host *);
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx.seq linux/drivers/scsi/aic7xxx.seq
--- v2.0.30/linux/drivers/scsi/aic7xxx.seq	Sun Oct 13 03:42:28 1996
+++ linux/drivers/scsi/aic7xxx.seq	Wed Dec 31 16:00:00 1969
@@ -1,1127 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx device driver for Linux and FreeBSD.
- *
- * Copyright (c) 1994 John Aycock
- *   The University of Calgary Department of Computer Science.
- *
- *Modifications/enhancements:
- *  Copyright (c) 1994, 1995, 1996 Justin Gibbs. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * FreeBSD, Twin, Wide, 2 command per target support, tagged queuing and other 
- * optimizations provided by Justin T. Gibbs (gibbs@FreeBSD.org)
- *
- * This version corresponds to version 1.42 of FreeBSDs aic7xxx.seq.
- *
- *-M*************************************************************************/
-
-VERSION AIC7XXX_SEQ_VER "$Id: aic7xxx.seq,v 4.0 1996/10/13 08:23:42 deang Exp $"
-
-#ifdef linux
-#include "aic7xxx_reg.h"
-#else
-#if defined(__NetBSD__)
-#include "../../../../dev/ic/aic7xxxreg.h"
-#elif defined(__FreeBSD__)
-#include "../../dev/aic7xxx/aic7xxx_reg.h"
-#endif
-#endif
-
-/*
- * We can't just use ACCUM in the sequencer code because it
- * must be treated specially by the assembler, and it currently
- * looks for the symbol 'A'.  This is the only register defined in
- * the assembler's symbol space.
- */
-A = ACCUM
-
-/* After starting the selection hardware, we check for reconnecting targets
- * as well as for our selection to complete just in case the reselection wins
- * bus arbitration.  The problem with this is that we must keep track of the
- * SCB that we've already pulled from the QINFIFO and started the selection
- * on just in case the reselection wins so that we can retry the selection at
- * a later time.  This problem cannot be resolved by holding a single entry
- * in scratch ram since a reconnecting target can request sense and this will
- * create yet another SCB waiting for selection.  The solution used here is to 
- * use byte 27 of the SCB as a pseudo-next pointer and to thread a list
- * of SCBs that are awaiting selection.  Since 0-0xfe are valid SCB offsets, 
- * SCB_LIST_NULL is 0xff which is out of range.  The kernel driver must
- * add an entry to this list every time a request sense occurs.  The sequencer
- * will automatically consume the entries.
- */
-
-/*
- * We assume that the kernel driver may reset us at any time, even in the
- * middle of a DMA, so clear DFCNTRL too.
- */
-reset:
-	clr	DFCNTRL
-	clr	SCSISIGO		/* De-assert BSY */
-/*
- * We jump to start after every bus free.
- */
-start:
-	and	FLAGS,0x0f		/* clear target specific flags */
-	mvi	SCSISEQ,ENRSELI		/* Always allow reselection */
-	clr	SCSIRATE		/*
-					 * We don't know the target we will
-					 * connect to, so default to narrow
-					 * transfers to avoid parity problems.
-					 */
-poll_for_work:
-	/*
-	 * Are we a twin channel device?
-	 * For fairness, we check the other bus first,
-	 * since we just finished a transaction on the
-	 * current channel.
-	 */
-	test	FLAGS,TWIN_BUS	jz start2
-	xor	SBLKCTL,SELBUSB			/* Toggle to the other bus */
-	test	SSTAT0,SELDI	jnz reselect
-	xor	SBLKCTL,SELBUSB			/* Toggle to the original bus */
-start2:
-	test	SSTAT0,SELDI	jnz reselect
-	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting
-	mov	A, QCNTMASK
-	test	QINCNT,A	jz poll_for_work
-
-/*
- * We have at least one queued SCB now and we don't have any 
- * SCBs in the list of SCBs awaiting selection.  Set the SCB
- * pointer from the FIFO so we see the right bank of SCB 
- * registers.
- */
-	mov	SCBPTR,QINFIFO
-
-/*
- * See if there is not already an active SCB for this target.  This code
- * locks out on a per target basis instead of target/lun.  Although this
- * is not ideal for devices that have multiple luns active at the same
- * time, it is faster than looping through all SCB's looking for active
- * commands.  It may be beneficial to make findscb a more general procedure
- * to see if the added cost of the search is negligible.  This code also 
- * assumes that the kernel driver will clear the active flags on board 
- * initialization, board reset, and a target SELTO.  Tagged commands
- * don't set the active bits since you can queue more than one command
- * at a time.  We do, however, look to see if there are any non-tagged
- * I/Os in progress, and requeue the command if there are.  Tagged and
- * non-tagged commands cannot be mixed to a single target.
- */
-
-test_busy:
-	mov	FUNCTION1,SCB_TCL
-	mov	A,FUNCTION1
-	test	SCB_TCL,0x88	jz test_a	/* Id < 8 && A channel */
-
-	test	ACTIVE_B,A	jnz requeue
-	test	SCB_CONTROL,TAG_ENB	jnz start_scb
-	/* Mark the current target as busy */
-	or	ACTIVE_B,A
-	jmp	start_scb
-
-/* Place the currently active SCB back on the queue for later processing */
-requeue:
-	mov	QINFIFO, SCBPTR
-	jmp	poll_for_work
-
-/*
- * Pull the first entry off of the waiting for selection list
- * We don't have to "test_busy" because only transactions that
- * have passed that test can be in the waiting_scb list.
- */
-start_waiting:
-	mov	SCBPTR,WAITING_SCBH
-	jmp	start_scb2
-
-test_a:
-	test	ACTIVE_A,A jnz requeue
-	test	SCB_CONTROL,TAG_ENB jnz start_scb
-	/* Mark the current target as busy */
-	or	ACTIVE_A,A
-
-start_scb:
-	mov	SCB_NEXT,WAITING_SCBH
-	mov	WAITING_SCBH, SCBPTR
-start_scb2:
-	and	SINDEX,0xf7,SBLKCTL	/* Clear the channel select bit */
-	and	A,0x08,SCB_TCL		/* Get new channel bit */
-	or	SINDEX,A
-	mov	SBLKCTL,SINDEX		/* select channel */
-	mov	SCB_TCL	call initialize_scsiid
-
-/*
- * Enable selection phase as an initiator, and do automatic ATN
- * after the selection.  We do this now so that we can overlap the
- * rest of our work to set up this target with the arbitration and
- * selection bus phases.
- */
-start_selection:
-	mvi	SCSISEQ,0x58		/* ENSELO|ENAUTOATNO|ENRSELI */
-
-/*
- * As soon as we get a successful selection, the target should go
- * into the message out phase since we have ATN asserted.  Prepare
- * the message to send.
- *
- * Messages are stored in scratch RAM starting with a length byte
- * followed by the message itself.
- */
-	test	SCB_CMDLEN,0xff jnz mk_identify	/* 0 Length Command? */
-
-/*
- * The kernel has sent us an SCB with no command attached.  This implies
- * that the kernel wants to send a message of some sort to this target,
- * so we interrupt the driver, allow it to fill the message buffer, and
- * then go back into the arbitration loop
- */
-	mvi     INTSTAT,AWAITING_MSG
-	jmp     wait_for_selection
-
-mk_identify:
-	and	A,DISCENB,SCB_CONTROL	/* mask off disconnect privledge */
-
-	and	MSG0,0x7,SCB_TCL	/* lun */
-	or	MSG0,A			/* or in disconnect privledge */
-	or	MSG0,MSG_IDENTIFY
-	mvi	MSG_LEN, 1
-
-	test	SCB_CONTROL,0xb0 jz  !message	/* WDTR, SDTR or TAG?? */
-/*
- * Send a tag message if TAG_ENB is set in the SCB control block.
- * Use SCB_TAG (the position in the kernel's SCB array) as the tag value.
- */
-
-mk_tag:
-	mvi	DINDEX, MSG1
-	test	SCB_CONTROL,TAG_ENB jz mk_tag_done
-	and	DINDIR,0x23,SCB_CONTROL
-	mov	DINDIR,SCB_TAG
-
-	add	MSG_LEN,COMP_MSG0,DINDEX	/* update message length */
-
-mk_tag_done:
-
-	test	SCB_CONTROL,0x90 jz !message	/* NEEDWDTR|NEEDSDTR */
-	mov	DINDEX	call mk_dtr	/* build DTR message if needed */
-
-!message:
-wait_for_selection:
-	test	SSTAT0,SELDO	jnz select 
-	test	SSTAT0,SELDI	jz wait_for_selection
-
-/*
- * Reselection has been initiated by a target. Make a note that we've been
- * reselected, but haven't seen an IDENTIFY message from the target
- * yet.
- */
-reselect:
-	clr	MSG_LEN		/* Don't have anything in the mesg buffer */
-	mov	SELID		call initialize_scsiid
-	or	FLAGS,RESELECTED
-	jmp	select2
-
-/*
- * After the selection, remove this SCB from the "waiting for selection"
- * list.  This is achieved by simply moving our "next" pointer into
- * WAITING_SCBH.  Our next pointer will be set to null the next time this
- * SCB is used, so don't bother with it now.
- */
-select:
-	mov	WAITING_SCBH,SCB_NEXT
-	or	FLAGS,SELECTED
-select2:
-/*
- * Set CLRCHN here before the target has entered a data transfer mode -
- * with synchronous SCSI, if you do it later, you blow away some
- * data in the SCSI FIFO that the target has already sent to you.
- */
-	or	SXFRCTL0,CLRCHN
-/*
- * Initialize SCSIRATE with the appropriate value for this target.
- */
-	call	ndx_dtr
-	mov	SCSIRATE,SINDIR
-
-/*
- * Initialize Ultra mode setting.
- */
-	mov	FUNCTION1,SCSIID
-	mov	A,FUNCTION1
-	and	SINDEX,0xdf,SXFRCTL0		/* default to Ultra disabled */
-	test	SCSIID, 0x80	 jnz ultra_b	/* Target ID > 7 */
-	test	SBLKCTL, SELBUSB jnz ultra_b	/* Second channel device */
-	test	ULTRA_ENB,A	 jz  set_sxfrctl0
-	or	SINDEX, ULTRAEN  jmp set_sxfrctl0
-ultra_b:
-	test	ULTRA_ENB_B,A	 jz  set_sxfrctl0
-	or	SINDEX, ULTRAEN
-
-set_sxfrctl0:
-	mov	SXFRCTL0,SINDEX
-
-	mvi	SCSISEQ,ENAUTOATNP		/*
-						 * ATN on parity errors
-						 * for "in" phases
-						 */
-	mvi	CLRSINT1,CLRBUSFREE
-	mvi	CLRSINT0,0x60			/* CLRSELDI|CLRSELDO */
-/*
- * Main loop for information transfer phases.  If BSY is false, then
- * we have a bus free condition, expected or not.  Otherwise, wait
- * for the target to assert REQ before checking MSG, C/D and I/O
- * for the bus phase.
- *
- */
-ITloop:
-	test	SSTAT1,BUSFREE	jnz p_busfree
-	test	SSTAT1,REQINIT	jz ITloop
-
-	and	A,PHASE_MASK,SCSISIGI
-	mov	LASTPHASE,A
-	mov	SCSISIGO,A
-
-	cmp	ALLZEROS,A	je p_dataout
-	cmp	A,P_DATAIN	je p_datain
-	cmp	A,P_COMMAND	je p_command
-	cmp	A,P_MESGOUT	je p_mesgout
-	cmp	A,P_STATUS	je p_status
-	cmp	A,P_MESGIN	je p_mesgin
-
-	mvi	INTSTAT,BAD_PHASE	/* unknown phase - signal driver */
-	jmp	ITloop			/* Try reading the bus again. */
-
-p_dataout:
-	mvi	DMAPARAMS,0x7d			/*
-						 * WIDEODD|SCSIEN|SDMAEN|HDMAEN|
-						 * DIRECTION|FIFORESET
-						 */
-	jmp	data_phase_init
-
-/*
- * If we re-enter the data phase after going through another phase, the
- * STCNT may have been cleared, so restore it from the residual field.
- */
-data_phase_reinit:
-	mov	STCNT0,SCB_RESID_DCNT0
-	mov	STCNT1,SCB_RESID_DCNT1
-	mov	STCNT2,SCB_RESID_DCNT2
-	jmp	data_phase_loop
-
-p_datain:
-	mvi	DMAPARAMS,0x79		/*
-					 * WIDEODD|SCSIEN|SDMAEN|HDMAEN|
-					 * !DIRECTION|FIFORESET
-					 */
-data_phase_init:
-	call	assert
-
-	test	FLAGS, DPHASE	jnz data_phase_reinit
-	call	sg_scb2ram
-	or	FLAGS, DPHASE		/* We have seen a data phase */
-
-data_phase_loop:
-/* Guard against overruns */
-	test	SG_COUNT, 0xff jnz data_phase_inbounds
-/*
- * Turn on 'Bit Bucket' mode, set the transfer count to
- * 16meg and let the target run until it changes phase.
- * When the transfer completes, notify the host that we
- * had an overrun.
- */
-	or	SXFRCTL1,BITBUCKET
-	mvi	STCNT0,0xff
-	mvi	STCNT1,0xff
-	mvi	STCNT2,0xff
-
-data_phase_inbounds:
-/* If we are the last SG block, don't set wideodd. */
-	cmp	SG_COUNT,0x01 jne data_phase_wideodd
-	and	DMAPARAMS, 0xbf		/* Turn off WIDEODD */
-data_phase_wideodd:
-	mov	DMAPARAMS  call dma
-
-/* Go tell the host about any overruns */
-	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun
-
-/* Exit if we had an underrun */
-	test	SSTAT0,SDONE	jz data_phase_finish /* underrun STCNT != 0 */
-
-/*
- * Advance the scatter-gather pointers if needed 
- */
-sg_advance:
-	dec	SG_COUNT	/* one less segment to go */
-
-	test	SG_COUNT, 0xff	jz data_phase_finish /* Are we done? */
-
-	clr	A			/* add sizeof(struct scatter) */
-	add	SG_NEXT0,SG_SIZEOF,SG_NEXT0
-	adc	SG_NEXT1,A,SG_NEXT1
-
-/*
- * Load a struct scatter and set up the data address and length.
- * If the working value of the SG count is nonzero, then
- * we need to load a new set of values.
- *
- * This, like all DMA's, assumes a little-endian host data storage.
- */
-sg_load:
-	clr	HCNT2
-	clr	HCNT1
-	mvi	HCNT0,SG_SIZEOF
-
-	mov	HADDR0,SG_NEXT0
-	mov	HADDR1,SG_NEXT1
-	mov	HADDR2,SG_NEXT2
-	mov	HADDR3,SG_NEXT3
-
-	or	DFCNTRL,0xd			/* HDMAEN|DIRECTION|FIFORESET */
-
-/*
- * Wait for DMA from host memory to data FIFO to complete, then disable
- * DMA and wait for it to acknowledge that it's off.
- */
-dma_finish:
-	test	DFSTATUS,HDONE	jz dma_finish
-	/* Turn off DMA preserving WIDEODD */
-	and	DFCNTRL,WIDEODD
-dma_finish2:
-	test	DFCNTRL,HDMAENACK jnz dma_finish2
-
-/*
- * Copy data from FIFO into SCB data pointer and data count.  In
- * both FreeBSD and Linux, the scatter list entry is 8 bytes.
- * 
- * struct ahc_dma_seg {
- *       physaddr addr;                  four bytes, little-endian order
- *       long    len;                    four bytes, little endian order
- * };
- */
-
-	mov	HADDR0,DFDAT
-	mov	HADDR1,DFDAT
-	mov	HADDR2,DFDAT
-	mov	HADDR3,DFDAT
-	mov	HCNT0,DFDAT
-	mov	HCNT1,DFDAT
-	mov	HCNT2,DFDAT
-
-/* Load STCNT as well.  It is a mirror of HCNT */
-	mov	STCNT0,HCNT0
-	mov	STCNT1,HCNT1
-	mov	STCNT2,HCNT2
-        test    SSTAT1,PHASEMIS  jz data_phase_loop
-
-data_phase_finish:
-/*
- * After a DMA finishes, save the SG and STCNT residuals back into the SCB
- * We use STCNT instead of HCNT, since it's a reflection of how many bytes 
- * were transferred on the SCSI (as opposed to the host) bus.
- */
-	mov	SCB_RESID_DCNT0,STCNT0
-	mov	SCB_RESID_DCNT1,STCNT1
-	mov	SCB_RESID_DCNT2,STCNT2
-	mov	SCB_RESID_SGCNT, SG_COUNT
-	jmp	ITloop
-
-data_phase_overrun:
-/*
- * Turn off BITBUCKET mode and notify the host
- */
-	and	SXFRCTL1,0x7f		/* ~BITBUCKET */
-	mvi	INTSTAT,DATA_OVERRUN
-	jmp	ITloop
-
-/*
- * Command phase.  Set up the DMA registers and let 'er rip.
- */
-p_command:
-	call	assert
-
-/*
- * Load HADDR and HCNT.
- */
-	mov	HADDR0, SCB_CMDPTR0
-	mov	HADDR1, SCB_CMDPTR1
-	mov	HADDR2, SCB_CMDPTR2
-	mov	HADDR3, SCB_CMDPTR3
-	mov	HCNT0, SCB_CMDLEN
-	clr	HCNT1
-	clr	HCNT2
-
-	mov	STCNT0, HCNT0
-	mov	STCNT1, HCNT1
-	mov	STCNT2, HCNT2
-
-	mvi	0x3d		call dma	# SCSIEN|SDMAEN|HDMAEN|
-						#   DIRECTION|FIFORESET
-	jmp	ITloop
-
-/*
- * Status phase.  Wait for the data byte to appear, then read it
- * and store it into the SCB.
- */
-p_status:
-	mvi	SCB_TARGET_STATUS	call inb_first
-	jmp	mesgin_done
-
-/*
- * Message out phase.  If there is not an active message, but the target
- * took us into this phase anyway, build a no-op message and send it.
- */
-p_mesgout:
-	test	MSG_LEN, 0xff	jnz  p_mesgout_start
-	mvi	MSG_NOP		call mk_mesg	/* build NOP message */
-
-p_mesgout_start:
-/*
- * Set up automatic PIO transfer from MSG0.  Bit 3 in
- * SXFRCTL0 (SPIOEN) is already on.
- */
-	mvi	SINDEX,MSG0
-	mov	DINDEX,MSG_LEN
-
-/*
- * When target asks for a byte, drop ATN if it's the last one in
- * the message.  Otherwise, keep going until the message is exhausted.
- *
- * Keep an eye out for a phase change, in case the target issues
- * a MESSAGE REJECT.
- */
-p_mesgout_loop:
-	test	SSTAT1,PHASEMIS	jnz p_mesgout_phasemis
-	test	SSTAT0,SPIORDY	jz p_mesgout_loop
-	test	SSTAT1,PHASEMIS	jnz p_mesgout_phasemis
-	cmp	DINDEX,1	jne p_mesgout_outb	/* last byte? */
-	mvi	CLRSINT1,CLRATNO			/* drop ATN */
-p_mesgout_outb:
-	dec	DINDEX
-	or	CLRSINT0, CLRSPIORDY
-	mov	SCSIDATL,SINDIR
-	
-p_mesgout4:
-	test	DINDEX,0xff	jnz p_mesgout_loop
-
-/*
- * If the next bus phase after ATN drops is a message out, it means
- * that the target is requesting that the last message(s) be resent.
- */
-p_mesgout_snoop:
-	test	SSTAT1,BUSFREE	jnz p_mesgout_done
-	test	SSTAT1,REQINIT	jz p_mesgout_snoop
-
-	test	SSTAT1,PHASEMIS	jnz p_mesgout_done
-
-	or	SCSISIGO,ATNO			/* turn on ATNO */
-
-	jmp	ITloop
-
-p_mesgout_phasemis:
-	mvi	CLRSINT1,CLRATNO	/* Be sure to turn ATNO off */
-p_mesgout_done:
-	clr	MSG_LEN			/* no active msg */
-	jmp	ITloop
-
-/*
- * Message in phase.  Bytes are read using Automatic PIO mode.
- */
-p_mesgin:
-	mvi	A		call inb_first	/* read the 1st message byte */
-	mov	REJBYTE,A			/* save it for the driver */
-
-	test	A,MSG_IDENTIFY		jnz mesgin_identify
-	cmp	A,MSG_DISCONNECT	je mesgin_disconnect
-	cmp	A,MSG_SDPTRS		je mesgin_sdptrs
-	cmp	ALLZEROS,A		je mesgin_complete
-	cmp	A,MSG_RDPTRS		je mesgin_rdptrs
-	cmp	A,MSG_EXTENDED		je mesgin_extended
-	cmp	A,MSG_REJECT		je mesgin_reject
-
-rej_mesgin:
-/*
- * We have no idea what this message in is, and there's no way
- * to pass it up to the kernel, so we issue a message reject and
- * hope for the best.  Since we're now using manual PIO mode to
- * read in the message, there should no longer be a race condition
- * present when we assert ATN.  In any case, rejection should be a
- * rare occurrence - signal the driver when it happens.
- */
-	or	SCSISIGO,ATNO			/* turn on ATNO */
-	mvi	INTSTAT,SEND_REJECT		/* let driver know */
-
-	mvi	MSG_REJECT	call mk_mesg
-
-mesgin_done:
-	call	inb_last			/*ack & turn auto PIO back on*/
-	jmp	ITloop
-
-
-mesgin_complete:
-/*
- * We got a "command complete" message, so put the SCB_TAG into QUEUEOUT,
- * and trigger a completion interrupt.  Check status for non zero return
- * and interrupt driver if needed.  This allows the driver to interpret
- * errors only when they occur instead of always uploading the scb.  If
- * the status is SCSI_CHECK, the driver will download a new scb requesting
- * sense to replace the old one, modify the "waiting for selection" SCB list
- * and set RETURN_1 to SEND_SENSE.  If RETURN_1 is set to SEND_SENSE the
- * sequencer imediately jumps to main loop where it will run down the waiting
- * SCB list and process the sense request.  If the kernel driver does not
- * wish to request sense, it need only clear RETURN_1, and the command is
- * allowed to complete.  We don't bother to post to the QOUTFIFO in the
- * error case since it would require extra work in the kernel driver to
- * ensure that the entry was removed before the command complete code tried
- * processing it.
- *
- * First check for residuals
- */
-	test	SCB_RESID_SGCNT,0xff	jz check_status
-/*
- * If we have a residual count, interrupt and tell the host.  Other
- * alternatives are to pause the sequencer on all command completes (yuck),
- * dma the resid directly to the host (slick, we may have space to do it now)
- * or have the sequencer pause itself when it encounters a non-zero resid 
- * (unnecessary pause just to flag the command -yuck-, but takes one instruction
- * and since it shouldn't happen that often is good enough for our purposes).  
- */
-resid:
-	mvi	INTSTAT,RESIDUAL
-
-check_status:
-	test	SCB_TARGET_STATUS,0xff	jz status_ok	/* Good Status? */
-	mvi	INTSTAT,BAD_STATUS			/* let driver know */
-	cmp	RETURN_1, SEND_SENSE	jne status_ok
-	jmp	mesgin_done
-
-status_ok:
-/* First, mark this target as free. */
-	test	SCB_CONTROL,TAG_ENB jnz test_immediate	/*
-							 * Tagged commands
-							 * don't busy the
-							 * target.
-							 */
-	mov	FUNCTION1,SCB_TCL
-	mov	A,FUNCTION1
-	test	SCB_TCL,0x88 jz clear_a
-	xor	ACTIVE_B,A
-	jmp	test_immediate
-
-clear_a:
-	xor	ACTIVE_A,A
-
-test_immediate:
-	test    SCB_CMDLEN,0xff jnz complete  /* Immediate message complete */
-/*
- * Pause the sequencer until the driver gets around to handling the command
- * complete.  This is so that any action that might require careful timing
- * with the completion of this command can occur.
- */
-	mvi	INTSTAT,IMMEDDONE
-	jmp	start
-complete:
-	mov	QOUTFIFO,SCB_TAG
-	mvi	INTSTAT,CMDCMPLT
-	jmp	mesgin_done
-
-
-/*
- * Is it an extended message?  We only support the synchronous and wide data
- * transfer request messages, which will probably be in response to
- * WDTR or SDTR message outs from us.  If it's not SDTR or WDTR, reject it -
- * apparently this can be done after any message in byte, according
- * to the SCSI-2 spec.
- */
-mesgin_extended:
-	mvi	ARG_1		call inb_next	/* extended message length */
-	mvi	REJBYTE_EXT	call inb_next	/* extended message code */
-
-	cmp	REJBYTE_EXT,MSG_SDTR	je p_mesginSDTR
-	cmp	REJBYTE_EXT,MSG_WDTR	je p_mesginWDTR
-	jmp	rej_mesgin
-
-p_mesginWDTR:
-	cmp	ARG_1,2		jne rej_mesgin	/* extended mesg length=2 */
-	mvi	ARG_1		call inb_next	/* Width of bus */
-	mvi	INTSTAT,WDTR_MSG		/* let driver know */
-	test	RETURN_1,0xff jz mesgin_done	/* Do we need to send WDTR? */
-	cmp	RETURN_1,SEND_REJ je rej_mesgin /*
-						 * Bus width was too large 
-						 * Reject it.
-						 */
-
-/* We didn't initiate the wide negotiation, so we must respond to the request */
-	and	RETURN_1,0x7f			/* Clear the SEND_WDTR Flag */
-	mvi	DINDEX,MSG0
-	mvi	MSG0	call mk_wdtr		/* build WDTR message */
-	or	SCSISIGO,ATNO			/* turn on ATNO */
-	jmp	mesgin_done
-
-p_mesginSDTR:
-	cmp	ARG_1,3		jne rej_mesgin	/* extended mesg length=3 */
-	mvi	ARG_1		call inb_next	/* xfer period */
-	mvi	A		call inb_next	/* REQ/ACK offset */
-	mvi	INTSTAT,SDTR_MSG		/* call driver to convert */
-
-	test	RETURN_1,0xff	jz mesgin_done  /* Do we need to mk_sdtr/rej */
-	cmp	RETURN_1,SEND_REJ je rej_mesgin /*
-						 * Requested SDTR too small
-						 * Reject it.
-						 */
-	clr	ARG_1				/* Use the scratch ram rate */
-	mvi	DINDEX, MSG0
-	mvi     MSG0     call mk_sdtr
-	or	SCSISIGO,ATNO			/* turn on ATNO */
-	jmp	mesgin_done
-
-/*
- * Is it a disconnect message?  Set a flag in the SCB to remind us
- * and await the bus going free.
- */
-mesgin_disconnect:
-	or	SCB_CONTROL,DISCONNECTED
-	test	FLAGS, PAGESCBS jz mesgin_done
-/*
- * Link this SCB into the DISCONNECTED list.  This list holds the
- * candidates for paging out an SCB if one is needed for a new command.
- * Modifying the disconnected list is a critical(pause dissabled) section.
- */
-	mvi	SCB_PREV, SCB_LIST_NULL
-	mvi	SEQCTL,0x50			/* PAUSEDIS|FASTMODE */
-	mov	SCB_NEXT, DISCONNECTED_SCBH
-	mov	DISCONNECTED_SCBH, SCBPTR
-	cmp	SCB_NEXT,SCB_LIST_NULL je linkdone
-	mov	SCBPTR,SCB_NEXT
-	mov	SCB_PREV,DISCONNECTED_SCBH
-	mov	SCBPTR,DISCONNECTED_SCBH
-linkdone:
-	mvi	SEQCTL,0x10			/* !PAUSEDIS|FASTMODE */
-	jmp	mesgin_done
-
-/*
- * Save data pointers message?  Copy working values into the SCB,
- * usually in preparation for a disconnect.
- */
-mesgin_sdptrs:
-	call	sg_ram2scb
-	jmp	mesgin_done
-
-/*
- * Restore pointers message?  Data pointers are recopied from the
- * SCB anytime we enter a data phase for the first time, so all
- * we need to do is clear the DPHASE flag and let the data phase
- * code do the rest.
- */
-mesgin_rdptrs:
-	and	FLAGS,0xef			/*
-						 * !DPHASE we'll reload them
-						 * the next time through
-						 */
-	jmp	mesgin_done
-
-/*
- * Identify message?  For a reconnecting target, this tells us the lun
- * that the reconnection is for - find the correct SCB and switch to it,
- * clearing the "disconnected" bit so we don't "find" it by accident later.
- */
-mesgin_identify:
-	test	A,0x78	jnz rej_mesgin	/*!DiscPriv|!LUNTAR|!Reserved*/
-
-	and	A,0x07			/* lun in lower three bits */
-	or      SAVED_TCL,A,SELID          
-	and     SAVED_TCL,0xf7
-	and     A,SELBUSB,SBLKCTL	/* B Channel?? */
-	or      SAVED_TCL,A
-	call	inb_last		/* ACK */
-
-/*
- * Here we "snoop" the bus looking for a SIMPLE QUEUE TAG message.
- * If we get one, we use the tag returned to switch to find the proper
- * SCB.  With SCB paging, this requires using findSCB for both tagged
- * and non-tagged transactions since the SCB may exist in any slot.
- * If we're not using SCB paging, we can use the tag as the direct
- * index to the SCB.
- */
-	mvi	ARG_1,SCB_LIST_NULL	/* Default to no-tag */
-snoop_tag_loop:
-	test	SSTAT1,BUSFREE	jnz use_findSCB
-	test	SSTAT1,REQINIT	jz snoop_tag_loop
-	test	SSTAT1,PHASEMIS	jnz use_findSCB
-	mvi	A		call inb_first
-	cmp	A,MSG_SIMPLE_TAG jne use_findSCB
-get_tag:
-	mvi	ARG_1	call inb_next	/* tag value */
-/*
- * See if the tag is in range.  The tag is < SCBCOUNT if we add
- * the complement of SCBCOUNT to the incoming tag and there is
- * no carry.
- */
-	mov	A,COMP_SCBCOUNT	
-	add	SINDEX,A,ARG_1
-	jc	abort_tag
-
-/*
- * Ensure that the SCB the tag points to is for a SCB transaction
- * to the reconnecting target.
- */
-	test	FLAGS, PAGESCBS	jz index_by_tag
-	call	inb_last			/* Ack Tag */
-use_findSCB:
-	mov	ALLZEROS	call findSCB	  /* Have to search */
-setup_SCB:
-	and	SCB_CONTROL,0xfb	  /* clear disconnect bit in SCB */
-	or	FLAGS,IDENTIFY_SEEN	  /* make note of IDENTIFY */
-	jmp	ITloop
-index_by_tag:
-	mov	SCBPTR,ARG_1
-	mov	A,SAVED_TCL
-	cmp	SCB_TCL,A		jne abort_tag
-	test	SCB_CONTROL,TAG_ENB	jz  abort_tag
-	call	inb_last			/* Ack Successful tag */
-	jmp	setup_SCB
-
-abort_tag:
-	or	SCSISIGO,ATNO			/* turn on ATNO */
-	mvi	INTSTAT,ABORT_TAG 		/* let driver know */
-	mvi	MSG_ABORT_TAG	call mk_mesg	/* ABORT TAG message */
-	jmp	mesgin_done
-
-/*
- * Message reject?  Let the kernel driver handle this.  If we have an 
- * outstanding WDTR or SDTR negotiation, assume that it's a response from 
- * the target selecting 8bit or asynchronous transfer, otherwise just ignore 
- * it since we have no clue what it pertains to.
- */
-mesgin_reject:
-	mvi	INTSTAT, REJECT_MSG
-	jmp	mesgin_done
-
-/*
- * [ ADD MORE MESSAGE HANDLING HERE ]
- */
-
-/*
- * Bus free phase.  It might be useful to interrupt the device
- * driver if we aren't expecting this.  For now, make sure that
- * ATN isn't being asserted and look for a new command.
- */
-p_busfree:
-	mvi	CLRSINT1,CLRATNO
-	clr	LASTPHASE
-
-/*
- * if this is an immediate command, perform a pseudo command complete to
- * notify the driver.
- */
-	test	SCB_CMDLEN,0xff	jz status_ok
-	jmp	start
-
-/*
- * Locking the driver out, build a one-byte message passed in SINDEX
- * if there is no active message already.  SINDEX is returned intact.
- */
-mk_mesg:
-	mvi	SEQCTL,0x50			/* PAUSEDIS|FASTMODE */
-	test	MSG_LEN,0xff	jz mk_mesg1	/* Should always succeed */
-	
-	/*
-	 * Hmmm.  For some reason the mesg buffer is in use.
-	 * Tell the driver.  It should look at SINDEX to find
-	 * out what we wanted to use the buffer for and resolve
-	 * the conflict.
-	 */
-	mvi	SEQCTL,0x10			/* !PAUSEDIS|FASTMODE */
-	mvi	INTSTAT,MSG_BUFFER_BUSY
-
-mk_mesg1:
-	mvi	MSG_LEN,1		/* length = 1 */
-	mov	MSG0,SINDEX		/* 1-byte message */
-	mvi	SEQCTL,0x10	ret	/* !PAUSEDIS|FASTMODE */
-
-/*
- * Functions to read data in Automatic PIO mode.
- *
- * According to Adaptec's documentation, an ACK is not sent on input from
- * the target until SCSIDATL is read from.  So we wait until SCSIDATL is
- * latched (the usual way), then read the data byte directly off the bus
- * using SCSIBUSL.  When we have pulled the ATN line, or we just want to
- * acknowledge the byte, then we do a dummy read from SCISDATL.  The SCSI
- * spec guarantees that the target will hold the data byte on the bus until
- * we send our ACK.
- *
- * The assumption here is that these are called in a particular sequence,
- * and that REQ is already set when inb_first is called.  inb_{first,next}
- * use the same calling convention as inb.
- */
-
-inb_next:
-	or	CLRSINT0, CLRSPIORDY
-	mov	NONE,SCSIDATL			/*dummy read from latch to ACK*/
-inb_next_wait:
-	test	SSTAT1,PHASEMIS	jnz mesgin_phasemis
-	test	SSTAT0,SPIORDY	jz inb_next_wait /* wait for next byte */
-inb_first:
-	mov	DINDEX,SINDEX
-	test	SSTAT1,PHASEMIS	jnz mesgin_phasemis
-	mov	DINDIR,SCSIBUSL	ret		/*read byte directly from bus*/
-inb_last:
-	mov	NONE,SCSIDATL ret		/*dummy read from latch to ACK*/
-
-mesgin_phasemis:
-/*
- * We expected to receive another byte, but the target changed phase
- */
-	mvi	INTSTAT, MSGIN_PHASEMIS
-	jmp	ITloop
-
-/*
- * DMA data transfer.  HADDR and HCNT must be loaded first, and
- * SINDEX should contain the value to load DFCNTRL with - 0x3d for
- * host->scsi, or 0x39 for scsi->host.  The SCSI channel is cleared
- * during initialization.
- */
-dma:
-	mov	DFCNTRL,SINDEX
-dma1:
-	test	SSTAT0,DMADONE	jnz dma3
-	test	SSTAT1,PHASEMIS	jz dma1		/* ie. underrun */
-
-/*
- * We will be "done" DMAing when the transfer count goes to zero, or
- * the target changes the phase (in light of this, it makes sense that
- * the DMA circuitry doesn't ACK when PHASEMIS is active).  If we are
- * doing a SCSI->Host transfer, the data FIFO should be flushed auto-
- * magically on STCNT=0 or a phase change, so just wait for FIFO empty
- * status.
- */
-dma3:
-	test	SINDEX,DIRECTION	jnz dma5
-dma4:
-	test	DFSTATUS,FIFOEMP	jz dma4
-
-/*
- * Now shut the DMA enables off and make sure that the DMA enables are 
- * actually off first lest we get an ILLSADDR.
- */
-dma5:
-	/* disable DMA, but maintain WIDEODD */
-	and	DFCNTRL,WIDEODD
-dma6:
-	test	DFCNTRL,0x38	jnz dma6  /* SCSIENACK|SDMAENACK|HDMAENACK */
-
-	ret
-
-/*
- * Common SCSI initialization for selection and reselection.  Expects
- * the target SCSI ID to be in the upper four bits of SINDEX, and A's
- * contents are stomped on return.
- */
-initialize_scsiid:
-	and	SINDEX,0xf0		/* Get target ID */
-	and	A,0x0f,SCSIID
-	or	SINDEX,A
-	mov	SCSIID,SINDEX ret
-
-/*
- * Assert that if we've been reselected, then we've seen an IDENTIFY
- * message.
- */
-assert:
-	test	FLAGS,RESELECTED	jz return	/* reselected? */
-	test	FLAGS,IDENTIFY_SEEN	jnz return	/* seen IDENTIFY? */
-
-	mvi	INTSTAT,NO_IDENT 	ret	/* no - cause a kernel panic */
-
-/*
- * Locate the SCB matching the target ID/channel/lun in SAVED_TCL, and the tag
- * value in ARG_1.  If ARG_1 == SCB_LIST_NULL, we're looking for a non-tagged
- * SCB.  Have the kernel print a warning message if it can't be found, and
- * generate an ABORT/ABORT_TAG message to the target.  SINDEX should be
- * cleared on call.
- */
-findSCB:
-	mov	A,SAVED_TCL
-	mov	SCBPTR,SINDEX			/* switch to next SCB */
-	mvi	SEQCTL,0x50			/* PAUSEDIS|FASTMODE */
-	cmp	SCB_TCL,A	jne findSCB1 /* target ID/channel/lun match? */
-	test	SCB_CONTROL,DISCONNECTED jz findSCB1 /*should be disconnected*/
-	test	SCB_CONTROL,TAG_ENB jnz findTaggedSCB
-	cmp	ARG_1,SCB_LIST_NULL je foundSCB
-	jmp	findSCB1
-findTaggedSCB:
-	mov	A, ARG_1			/* Tag passed in ARG_1 */
-	cmp	SCB_TAG,A	jne findSCB1	/* Found it? */
-foundSCB:
-	test	FLAGS,PAGESCBS	jz foundSCB_ret
-/* Remove this SCB from the disconnection list */
-	cmp	SCB_NEXT,SCB_LIST_NULL je unlink_prev
-	mov	SAVED_LINKPTR, SCB_PREV
-	mov	SCBPTR, SCB_NEXT
-	mov	SCB_PREV, SAVED_LINKPTR
-	mov	SCBPTR, SINDEX
-unlink_prev:
-	cmp	SCB_PREV,SCB_LIST_NULL	je rHead/* At the head of the list */
-	mov	SAVED_LINKPTR, SCB_NEXT
-	mov	SCBPTR, SCB_PREV
-	mov	SCB_NEXT, SAVED_LINKPTR
-	mov	SCBPTR, SINDEX
-	mvi	SEQCTL,0x10	ret		/* !PAUSEDIS|FASTMODE */
-rHead:
-	mov	DISCONNECTED_SCBH,SCB_NEXT
-foundSCB_ret:
-	mvi	SEQCTL,0x10	ret		/* !PAUSEDIS|FASTMODE */
-
-findSCB1:
-	mvi	SEQCTL,0x10			/* !PAUSEDIS|FASTMODE */
-	inc	SINDEX
-	mov	A,SCBCOUNT
-	cmp	SINDEX,A	jne findSCB
-
-	mvi	INTSTAT,NO_MATCH		/* not found - signal kernel */
-	cmp	RETURN_1,SCB_PAGEDIN je return
-	or	SCSISIGO,ATNO			/* assert ATNO */
-	cmp	ARG_1,SCB_LIST_NULL jne find_abort_tag
-	mvi	MSG_ABORT	call mk_mesg
-	jmp	ITloop
-find_abort_tag:
-	mvi	MSG_ABORT_TAG	call mk_mesg
-	jmp	ITloop
-
-/*
- * Make a working copy of the scatter-gather parameters from the SCB.
- */
-sg_scb2ram:
-	mov	HADDR0, SCB_DATAPTR0
-	mov	HADDR1, SCB_DATAPTR1
-	mov	HADDR2, SCB_DATAPTR2
-	mov	HADDR3, SCB_DATAPTR3
-	mov	HCNT0, SCB_DATACNT0
-	mov	HCNT1, SCB_DATACNT1
-	mov	HCNT2, SCB_DATACNT2
-
-	mov	STCNT0, HCNT0
-	mov	STCNT1, HCNT1
-	mov	STCNT2, HCNT2
-
-	mov	SG_COUNT,SCB_SGCOUNT
-
-	mov	SG_NEXT0, SCB_SGPTR0
-	mov	SG_NEXT1, SCB_SGPTR1
-	mov	SG_NEXT2, SCB_SGPTR2
-	mov	SG_NEXT3, SCB_SGPTR3 ret
-
-/*
- * Copying RAM values back to SCB, for Save Data Pointers message, but
- * only if we've actually been into a data phase to change them.  This
- * protects against bogus data in scratch ram and the residual counts
- * since they are only initialized when we go into data_in or data_out.
- */
-sg_ram2scb:
-	test	FLAGS, DPHASE	jz return
-	mov	SCB_SGCOUNT,SG_COUNT
-
-	mov	SCB_SGPTR0,SG_NEXT0
-	mov	SCB_SGPTR1,SG_NEXT1
-	mov	SCB_SGPTR2,SG_NEXT2
-	mov	SCB_SGPTR3,SG_NEXT3
-	
-	mov	SCB_DATAPTR0,SHADDR0
-	mov	SCB_DATAPTR1,SHADDR1
-	mov	SCB_DATAPTR2,SHADDR2
-	mov	SCB_DATAPTR3,SHADDR3
-
-/*
- * Use the residual number since STCNT is corrupted by any message transfer
- */
-	mov	SCB_DATACNT0,SCB_RESID_DCNT0
-	mov	SCB_DATACNT1,SCB_RESID_DCNT1
-	mov	SCB_DATACNT2,SCB_RESID_DCNT2 ret
-
-/*
- * Add the array base TARG_SCRATCH to the target offset (the target address
- * is in SCSIID), and return the result in SINDEX.  The accumulator
- * contains the 3->8 decoding of the target ID on return.
- */
-ndx_dtr:
-	shr	A,SCSIID,4
-	test	SBLKCTL,SELBUSB	jz ndx_dtr_2
-	or	A,0x08		/* Channel B entries add 8 */
-ndx_dtr_2:
-	add	SINDEX,TARG_SCRATCH,A ret
-
-/*
- * If we need to negotiate transfer parameters, build the WDTR or SDTR message
- * starting at the address passed in SINDEX.  DINDEX is modified on return.
- * The SCSI-II spec requires that Wide negotiation occur first and you can
- * only negotiate one or the other at a time otherwise in the event of a message
- * reject, you wouldn't be able to tell which message was the culprit.
- */
-mk_dtr:
-	test	SCB_CONTROL,NEEDWDTR jnz  mk_wdtr_16bit
-	mvi	ARG_1, MAXOFFSET	/* Force an offset of 15 or 8 if WIDE */
-
-mk_sdtr:
-	mvi	DINDIR,1		/* extended message */
-	mvi	DINDIR,3		/* extended message length = 3 */
-	mvi	DINDIR,1		/* SDTR code */
-	call	sdtr_to_rate
-	mov	DINDIR,RETURN_1		/* REQ/ACK transfer period */
-	cmp	ARG_1, MAXOFFSET je mk_sdtr_max_offset
-	and	DINDIR,0x0f,SINDIR	/* Sync Offset */
-
-mk_sdtr_done:
-	add	MSG_LEN,COMP_MSG0,DINDEX ret	/* update message length */
-
-mk_sdtr_max_offset:
-/*
- * We're initiating sync negotiation, so request the max offset we can (15 or 8)
- */
-	/* Talking to a WIDE device? */
-	test	SCSIRATE, WIDEXFER	jnz wmax_offset	
-	mvi	DINDIR, MAX_OFFSET_8BIT
-	jmp	mk_sdtr_done
-
-wmax_offset:
-	mvi	DINDIR, MAX_OFFSET_16BIT
-	jmp	mk_sdtr_done
-
-mk_wdtr_16bit:
-	mvi	ARG_1,BUS_16_BIT
-mk_wdtr:
-	mvi	DINDIR,1		/* extended message */
-	mvi	DINDIR,2		/* extended message length = 2 */
-	mvi	DINDIR,3		/* WDTR code */
-	mov	DINDIR,ARG_1		/* bus width */
-
-	add	MSG_LEN,COMP_MSG0,DINDEX ret	/* update message length */
-	
-sdtr_to_rate:
-	call	ndx_dtr			/* index scratch space for target */
-	shr	A,SINDIR,0x4
-	dec	SINDEX			/* Preserve SINDEX */
-	and	A,0x7
-	clr	RETURN_1
-sdtr_to_rate_loop:
-	test	A,0x0f	jz sdtr_to_rate_done
-	add	RETURN_1,0x19
-	dec	A	
-	jmp	sdtr_to_rate_loop
-sdtr_to_rate_done:
-	shr	RETURN_1,0x2
-	add	RETURN_1,0x19
-	test	SXFRCTL0,ULTRAEN jz return
-	shr	RETURN_1,0x1
-return:
-	ret
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx_asm.c linux/drivers/scsi/aic7xxx_asm.c
--- v2.0.30/linux/drivers/scsi/aic7xxx_asm.c	Sat Apr 20 10:59:10 1996
+++ linux/drivers/scsi/aic7xxx_asm.c	Wed Dec 31 16:00:00 1969
@@ -1,734 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx sequencer code assembler.
- *
- * Copyright (c) 1994 John Aycock
- *   The University of Calgary Department of Computer Science.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * Comments are started by `#' and continue to the end of the line; lines
- * may be of the form:
- *      <label>*
- *      <label>*  <undef-sym> = <value>
- *      <label>*  <opcode> <operand>*
- *
- * A <label> is an <undef-sym> ending in a colon.  Spaces, tabs, and commas
- * are token separators.
- *-M*************************************************************************/
-static const char id[] = "$Id: aic7xxx_asm.c,v 3.0 1996/04/16 08:52:23 deang Exp $";
-#include <ctype.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-
-#define MEMORY		448
-#define MAXLINE		1024
-#define MAXTOKEN	32
-#define ADOTOUT		"a.out"
-#define NOVALUE		-1
-
-#ifndef TRUE
-#  define TRUE 1
-#endif
-#ifndef FALSE
-#  define FALSE 0
-#endif
-#define MAX_ARGS	16
-static const char *cpp[] = {
-  "/lib/cpp -P - -",
-  "/usr/lib/cpp -P - -",
-  "/usr/bin/cpp -P - -",
-  "/usr/bin/gcc -E -P -",
-  "/usr/bin/cc -E -P -"
-};
-
-#define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
-
-/*
- * AIC-7770/AIC-7870 register definitions
- */
-#define R_SINDEX	0x65
-#define R_ALLONES	0x69
-#define R_ALLZEROS	0x6a
-#define R_NONE		0x6a
-
-int debug;
-int lineno, LC;
-char *filename;
-unsigned char M[MEMORY][4];
-
-void 
-error(const char *s)
-{
-	fprintf(stderr, "%s: %s at line %d\n", filename, s, lineno);
-	exit(EXIT_FAILURE);
-}
-
-void *
-Malloc(size_t size)
-{
-	void *p = malloc(size);
-	if (!p)
-		error("out of memory");
-	return(p);
-}
-
-void *
-Realloc(void *ptr, size_t size)
-{
-	void *p = realloc(ptr, size);
-	if (!p)
-		error("out of memory");
-	return(p);
-}
-
-char *
-Strdup(char *s)
-{
-	char *p = (char *)Malloc(strlen(s) + 1);
-	strcpy(p, s);
-	return(p);
-}
-
-typedef struct sym_t {
-	struct sym_t	*next;		/* MUST BE FIRST */
-	char		*name;
-	int		value;
-	int		npatch; 
-	int		*patch;
-} sym_t;
-
-sym_t *head;
-
-void
-define(char *name, int value)
-{
-	sym_t *p, *q;
-
-	for (p = head, q = (sym_t *)&head; p; p = p->next) {
-		if (!strcmp(p->name, name))
-			error("redefined symbol");
-		q = p;
-	}
-
-	p = q->next = (sym_t *)Malloc(sizeof(sym_t));
-	p->next = NULL;
-	p->name = Strdup(name);
-	p->value = value;
-	p->npatch = 0;
-	p->patch = NULL;
-
-	if (debug) {
-		fprintf(stderr, "\"%s\" ", p->name);
-		if (p->value != NOVALUE)
-			fprintf(stderr, "defined as 0x%x\n", p->value);
-		else
-			fprintf(stderr, "undefined\n");
-	}
-}
-
-sym_t *
-lookup(char *name)
-{
-	sym_t *p;
-
-	for (p = head; p; p = p->next)
-		if (!strcmp(p->name, name))
-			return(p);
-	return(NULL);
-}
-
-void 
-patch(sym_t *p, int location)
-{
-	p->npatch += 1;
-	p->patch = (int *)Realloc(p->patch, p->npatch * sizeof(int *));
-
-	p->patch[p->npatch - 1] = location;
-}
-
-void backpatch(void)
-{
-	int i;
-	sym_t *p;
-
-	for (p = head; p; p = p->next) {
-
-		if (p->value == NOVALUE) {
-			fprintf(stderr,
-				"%s: undefined symbol \"%s\"\n",
-				filename, p->name);
-			exit(EXIT_FAILURE);
-		}
-
-		if (p->npatch) {
-			if (debug)
-				fprintf(stderr,
-					"\"%s\" (0x%x) patched at",
-					p->name, p->value);
-
-			for (i = 0; i < p->npatch; i++) {
-				M[p->patch[i]][0] &= ~1;
-				M[p->patch[i]][0] |= ((p->value >> 8) & 1);
-				M[p->patch[i]][1] = p->value & 0xff;
-
-				if (debug)
-					fprintf(stderr, " 0x%x", p->patch[i]);
-			}
-
-			if (debug)
-				fputc('\n', stderr);
-		}
-	}
-}
-
-/*
- *  Output words in byte-reversed order (least significant first)
- *  since the sequencer RAM is loaded that way.
- */
-void
-output(FILE *fp)
-{
-	int i;
-
-	for (i = 0; i < LC; i++)
-		fprintf(fp, "\t0x%02x, 0x%02x, 0x%02x, 0x%02x,\n",
-			M[i][3],
-			M[i][2],
-			M[i][1],
-			M[i][0]);
-	printf("%d out of %d instructions used.\n", LC, MEMORY);
-}
-
-char **
-getl(int *n)
-{
-	int i;
-	char *p, *quote;
-	static char buf[MAXLINE];
-	static char *a[MAXTOKEN];
-
-	i = 0;
-
-	while (fgets(buf, sizeof(buf), stdin)) {
-
-		lineno += 1;
-
-		if (buf[strlen(buf)-1] != '\n')
-			error("line too long");
-
-		p = strchr(buf, '#');
-		if (p)
-			*p = '\0';
-		p = buf;
-rescan:
-		quote = strchr(p, '\"');
-		if (quote)
-			*quote = '\0';
-		for (p = strtok(p, ", \t\n"); p; p = strtok(NULL, ", \t\n"))
-			if (i < MAXTOKEN-1)
-				a[i++] = p;
-			else
-				error("too many tokens");
-		if (quote) {
-			quote++; 
-			p = strchr(quote, '\"');
-			if (!p)
-				error("unterminated string constant");
-			else if (i < MAXTOKEN-1) {
-				a[i++] = quote;
-				*p = '\0';
-				p++;
-			}
-			else
-				error("too many tokens");
-			goto rescan;
-		}		
-		if (i) {
-			*n = i;
-			return(a);
-		}
-	}
-	return(NULL);
-}
-
-#define A	0x8000		/* `A'ccumulator ok */
-#define I	0x4000		/* use as immediate value */
-#define SL	0x2000		/* shift left */
-#define SR	0x1000		/* shift right */
-#define RL	0x0800		/* rotate left */
-#define RR	0x0400		/* rotate right */
-#define LO	0x8000		/* lookup: ori-{jmp,jc,jnc,call} */
-#define LA	0x4000		/* lookup: and-{jz,jnz} */
-#define LX	0x2000		/* lookup: xor-{je,jne} */
-#define NA	-1		/* not applicable */
-
-struct {
-	const char *name;
-	int n;			/* number of operands, including opcode */
-	unsigned int op;	/* immediate or L?|pos_from_0 */
-	unsigned int dest;	/* NA, pos_from_0, or I|immediate */
-	unsigned int src;	/* NA, pos_from_0, or I|immediate */
-	unsigned int imm;	/* pos_from_0, A|pos_from_0, or I|immediate */
-	unsigned int addr;	/* NA or pos_from_0 */
-	int fmt;		/* instruction format - 1, 2, or 3 */
-} instr[] = {
-/*
- *		  N  OP    DEST		SRC		IMM	ADDR	FMT
- */
-	{ "mov",  3, 1,	   1,		2,		I|0xff,	NA,	1 },
-	{ "mov",  4, LO|2, NA,		1,		I|0,	3,	3 },
-	{ "mvi",  3, 0,	   1,		I|R_ALLZEROS,	A|2,	NA,	1 },
-	{ "mvi",  4, LO|2, NA,		I|R_ALLZEROS,	1,	3,	3 },
-	{ "not",  2, 2,	   1,		1,		I|0xff,	NA,	1 },
-	{ "and",  3, 1,	   1,		1,		A|2,	NA,	1 },
-	{ "and",  4, 1,	   1,		3,		A|2,	NA,	1 },
-	{ "or",	  3, 0,	   1,		1,		A|2,	NA,	1 },
-	{ "or",	  4, 0,	   1,		3,		A|2,	NA,	1 },
-	{ "or",   5, LO|3, NA,		1,		2,	4,	3 },
-	{ "xor",  3, 2,	   1,		1,		A|2,	NA,	1 },
-	{ "xor",  4, 2,	   1,		3,		A|2,	NA,	1 },
-	{ "nop",  1, 1,	   I|R_NONE,	I|R_ALLZEROS,	I|0xff,	NA,	1 },
-	{ "inc",  2, 3,	   1,		1,		I|1,	NA,	1 },
-	{ "inc",  3, 3,	   1,		2,		I|1,	NA,	1 },
-	{ "dec",  2, 3,	   1,		1,		I|0xff,	NA,	1 },
-	{ "dec",  3, 3,	   1,		2,		I|0xff,	NA,	1 },
-	{ "jmp",  2, LO|0,   NA,	I|R_SINDEX,	I|0,	1,	3 },
-	{ "jc",   2, LO|0,   NA,	I|R_SINDEX,	I|0,	1,	3 },
-	{ "jnc",  2, LO|0,   NA,	I|R_SINDEX,	I|0,	1,	3 },
-	{ "call", 2, LO|0,   NA,	I|R_SINDEX,	I|0,	1,	3 },
-	{ "test", 5, LA|3,   NA,	1,		A|2,	4,	3 },
-	{ "cmp",  5, LX|3,   NA,	1,		A|2,	4,	3 },
-	{ "ret",  1, 1,	 I|R_NONE,	I|R_ALLZEROS,	I|0xff,	NA,	1 },
-	{ "ret",  1, 1,	 I|R_NONE,	I|R_ALLZEROS,	I|0xff,	NA,	1 },
-	{ "clc",  1, 3,	 I|R_NONE,	I|R_ALLZEROS,	I|1,	NA,	1 },
-	{ "clc",  4, 3,	 2,		I|R_ALLZEROS,	A|3,	NA,	1 },
-	{ "stc",  2, 3,	 1,		I|R_ALLONES,	I|1,	NA,	1 },
-	{ "add",  3, 3,	 1,		1,		A|2,	NA,	1 },
-	{ "add",  4, 3,	 1,		3,		A|2,	NA,	1 },
-	{ "adc",  3, 4,	 1,		1,		A|2,	NA,	1 },
-	{ "adc",  4, 4,	 1,		3,		A|2,	NA,	1 },
-	{ "shl",  3, 5,	 1,		1,		SL|2,	NA,	2 },
-	{ "shl",  4, 5,	 1,		2,		SL|3,	NA,	2 },
-	{ "shr",  3, 5,	 1,		1,		SR|2,	NA,	2 },
-	{ "shr",  4, 5,	 1,		2,		SR|3,	NA,	2 },
-	{ "rol",  3, 5,	 1,		1,		RL|2,	NA,	2 },
-	{ "rol",  4, 5,	 1,		2,		RL|3,	NA,	2 },
-	{ "ror",  3, 5,	 1,		1,		RR|2,	NA,	2 },
-	{ "ror",  4, 5,	 1,		2,		RR|3,	NA,	2 },
-	/*
-	 *  Extensions (note also that mvi allows A)
-	 */
- 	{ "clr",  2, 1,	 1,	I|R_ALLZEROS,		I|0xff,	NA,	1 },
-	{ 0,      0, 0,  0, 	0,			0,	0,	0 }
-};
-
-int 
-eval_operand(char **a, int spec)
-{
-	int i;
-	unsigned int want = spec & (LO|LA|LX);
-
-	static struct {
-		unsigned int what;
-		const char *name;
-		int value;
-	} jmptab[] = {
-		{ LO,	"jmp",		8  },
-		{ LO,	"jc",		9  },
-		{ LO,	"jnc",		10 },
-		{ LO,	"call",		11 },
-		{ LA,	"jz",		15 },
-		{ LA,	"jnz",		13 },
-		{ LX,	"je",		14 },
-		{ LX,	"jne",		12 },
-	};
-
-	spec &= ~(LO|LA|LX);
-
-	for (i = 0; i < sizeof(jmptab)/sizeof(jmptab[0]); i++)
-		if (jmptab[i].what == want &&
-		    !strcmp(jmptab[i].name, a[spec]))
-		{
-			return(jmptab[i].value);
-		}
-
-	if (want)
-		error("invalid jump");
-
-	return(spec);		/* "case 0" - no flags set */
-}
-
-int
-eval_sdi(char **a, int spec)
-{
-	sym_t *p;
-	unsigned val;
-
-	if (spec == NA)
-		return(NA);
-
-	switch (spec & (A|I|SL|SR|RL|RR)) {
-	    case SL:
-	    case SR:
-	    case RL:
-	    case RR:
-		if (isdigit(*a[spec &~ (SL|SR|RL|RR)]))
-			val = strtol(a[spec &~ (SL|SR|RL|RR)], NULL, 0);
-		else {
-			p = lookup(a[spec &~ (SL|SR|RL|RR)]);
-			if (!p)
-				error("undefined symbol used");
-			val = p->value;
-		}
-
-		switch (spec & (SL|SR|RL|RR)) {		/* blech */
-		    case SL:
-			if (val > 7)
-				return(0xf0);
-			return(((val % 8) << 4) |
-			       (val % 8));
-		    case SR:
-			if (val > 7)
-				return(0xf0);
-			return(((val % 8) << 4) |
-			       (1 << 3) |
-			       ((8 - (val % 8)) % 8));
-		    case RL:
-			return(val % 8);
-		    case RR:
-			return((8 - (val % 8)) % 8);
-		}
-	    case I:
-		return(spec &~ I);
-	    case A:
-		/*
-		 *  An immediate field of zero selects
-		 *  the accumulator.  Vigorously object
-		 *  if zero is given otherwise - it's
-		 *  most likely an error.
-		 */
-		spec &= ~A;
-		if (!strcmp("A", a[spec]))
-			return(0);
-		if (isdigit(*a[spec]) &&
-		    strtol(a[spec], NULL, 0) == 0)
-		{
-			error("immediate value of zero selects accumulator");
-		}
-		/* falls through */
-	    case 0:
-		if (isdigit(*a[spec]))
-			return(strtol(a[spec], NULL, 0));
-		p = lookup(a[spec]);
-		if (p)
-			return(p->value);
-		error("undefined symbol used");
-	}
-
-	return(NA);		/* shut the compiler up */
-}
-
-int
-eval_addr(char **a, int spec)
-{
-	sym_t *p;
-
-	if (spec == NA)
-		return(NA);
-	if (isdigit(*a[spec]))
-		return(strtol(a[spec], NULL, 0));
-
-	p = lookup(a[spec]);
-
-	if (p) {
-		if (p->value != NOVALUE)
-			return(p->value);
-		patch(p, LC);
-	} else {
-		define(a[spec], NOVALUE);
-		p = lookup(a[spec]);
-		patch(p, LC);
-	}
-
-	return(NA);		/* will be patched in later */
-}
-
-int
-crack(char **a, int n)
-{
-	int i;
-	int I_imm, I_addr;
-	int I_op, I_dest, I_src, I_ret;
-
-	/*
-	 *  Check for "ret" at the end of the line; remove
-	 *  it unless it's "ret" alone - we still want to
-	 *  look it up in the table.
-	 */
-	I_ret = (strcmp(a[n-1], "ret") ? 0 : !0);
-	if (I_ret && n > 1)
-		n -= 1;
-
-	for (i = 0; instr[i].name; i++) {
-		/*
-		 *  Look for match in table given constraints,
-		 *  currently just the name and the number of
-		 *  operands.
-		 */
-		if (!strcmp(instr[i].name, *a) && instr[i].n == n)
-			break;
-	}
-	if (!instr[i].name)
-		error("unknown opcode or wrong number of operands");
-
-	I_op	= eval_operand(a, instr[i].op);
-	I_src	= eval_sdi(a, instr[i].src);
-	I_imm	= eval_sdi(a, instr[i].imm);
-	I_dest	= eval_sdi(a, instr[i].dest);
-	I_addr	= eval_addr(a, instr[i].addr);
-
-	if( LC >= MEMORY )
-		error("Memory exhausted!\n");
-
-	switch (instr[i].fmt) {
-	    case 1:
-	    case 2:
-		M[LC][0] = (I_op << 1) | I_ret;
-		M[LC][1] = I_dest;
-		M[LC][2] = I_src;
-		M[LC][3] = I_imm;
-		break;
-	    case 3:
-		if (I_ret)
-			error("illegal use of \"ret\"");
-		M[LC][0] = (I_op << 1) | ((I_addr >> 8) & 1);
-		M[LC][1] = I_addr & 0xff;
-		M[LC][2] = I_src;
-		M[LC][3] = I_imm;
-		break;
-	}
-
-	return (1);		/* no two-byte instructions yet */
-}
-
-#undef SL
-#undef SR
-#undef RL
-#undef RR
-#undef LX
-#undef LA
-#undef LO
-#undef I
-#undef A
-
-void
-assemble(FILE *ofile)
-{
-	int n;
-	char **a;
-	sym_t *p;
-
-	while ((a = getl(&n))) {
-
-		while (a[0][strlen(*a)-1] == ':') {
-			a[0][strlen(*a)-1] = '\0';
-			p = lookup(*a);
-			if (p)
-				p->value = LC;
-			else
-				define(*a, LC);
-			a += 1;
-			n -= 1;
-		}
-
-		if (!n)			/* line was all labels */
-			continue;
-
-		if (n == 3 && !strcmp("VERSION", *a))
-			fprintf(ofile, "#define %s \"%s\"\n", a[1], a[2]);
-		else {
-			if (n == 3 && !strcmp("=", a[1]))
-				define(*a, strtol(a[2], NULL, 0));
-			else
-				LC += crack(a, n);
-		}
-	}
-
-	backpatch();
-	output(ofile);
-
-	if (debug)
-		output(stderr);
-}
-
-int
-main(int argc, char **argv)
-{
-	int c;
-	int pid;
-	int ifile;
-	int status;
-	FILE *ofile;
-	char *ofilename;
-	int fd[2];
-
-	ofile = NULL;
-	ofilename = NULL;
-	while ((c = getopt(argc, argv, "dho:vD:")) != EOF) {
-		switch (c) {
-		    case 'd':
-			debug = !0;
-			break;
-		    case 'D':
-		    {
-			char *p;
-			if ((p = strchr(optarg, '=')) != NULL) {
-				*p = '\0';
-				define(optarg, strtol(p + 1, NULL, 0));
-			}
-			else
-				define(optarg, 1);
-			break;
-		    }
-		    case 'o':
-		        ofilename = optarg;
-			if ((ofile = fopen(ofilename, "w")) == NULL) {
-				perror(optarg);
-				exit(EXIT_FAILURE);
-			}
-			break;
-		    case 'h':
-			printf("usage: %s [-d] [-Dname] [-ooutput] input\n", 
-				*argv);
-			exit(EXIT_SUCCESS);
-			break;
-		    case 'v':
-			printf("%s\n", id);
-			exit(EXIT_SUCCESS);
-			break;
-		    default:
-			exit(EXIT_FAILURE);
-			break;
-		}
-	}
-
-	if (argc - optind != 1) {
-		fprintf(stderr, "%s: must have one input file\n", *argv);
-		exit(EXIT_FAILURE);
-	}
-	filename = argv[optind];
-
-	
-	if ((ifile = open(filename, O_RDONLY)) < 0) {
-		perror(filename);
-		exit(EXIT_FAILURE);
-	}
-
-	if (!ofilename) {
-		ofilename = ADOTOUT;
-		if ((ofile = fopen(ofilename, "w")) < 0) {
-			perror(ofilename);
-			exit(EXIT_FAILURE);
-		}
-	}
-
-	if (pipe(fd) < 0) {
-		perror("pipe failed");
-		exit(1);
-	}
-
-	if ((pid = fork()) < 0 ) {
-		perror("fork failed");
-		exit(1);
-	}
-	else if (pid > 0) {		/* Parent */
-		close(fd[1]);		/* Close write end */
-		if (fd[0] != STDIN_FILENO) {
-			if (dup2(fd[0], STDIN_FILENO) != STDIN_FILENO) {
-				perror("dup2 error on stdin");
-				exit(EXIT_FAILURE);
-			}
-			close(fd[0]);
-		}
-		assemble(ofile);
-		if (wait(&status) < 0) {
-			perror("wait error");
-		}
-
-		if (status != 0) {
-			unlink(ofilename);
-		}
-		exit(status);
-	} else {				/* Child */
-                int i, arg_cnt, found;
-		char *args[MAX_ARGS];
-		char *buf;
-
-                arg_cnt = 0;
-                found = FALSE;
-                for (i = 0; (!found && (i < NUMBER(cpp))); i++) {
-			char *bp;
-
-			buf = strdup(cpp[i]);
-
-			for (bp = strtok(buf, " \t\n"), arg_cnt = 0;
-                             bp != NULL;
-                             bp = strtok(NULL, " \t\n"), arg_cnt++) {
-				if (arg_cnt == 0) {
-					if (access(bp, X_OK) == 0) {
-						found = TRUE;
-					}
-				}
-
-				args[arg_cnt] = bp;
-			}
-
-                        if (!found) {
-				free(buf);
-                        }
-                }
-		args[arg_cnt] = NULL;
-
-                if (found) {
-			close(fd[0]);		/* Close Read end */
-			if (fd[1] != STDOUT_FILENO) {
-				if (dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO) {
-					perror("dup2 error on stdout");
-					exit(EXIT_FAILURE);
-				}
-				close(fd[1]);
-			}
-			if (ifile != STDIN_FILENO) {
-				if (dup2(ifile, STDIN_FILENO) != STDIN_FILENO) {
-					perror("dup2 error on stdin");
-					exit(EXIT_FAILURE);
-				}
-				close(ifile);
-			}
-
-			if (execvp(args[0], args) < 0) {
-				perror("execvp() error");
-				exit(EXIT_FAILURE);
-			}
-        	} else {
-			fprintf(stderr, "%s: Cannot find CPP command.\n", argv[0]);
-			exit(EXIT_FAILURE);
-		}
-	}
-	return(EXIT_SUCCESS);
-}
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c
--- v2.0.30/linux/drivers/scsi/aic7xxx_proc.c	Sun Oct 13 03:42:28 1996
+++ linux/drivers/scsi/aic7xxx_proc.c	Thu Jul 31 12:37:17 1997
@@ -24,7 +24,7 @@
  *
  *  Dean W. Gehnert, deang@teleport.com, 05/01/96
  *
- *  $Id: aic7xxx_proc.c,v 4.0 1996/10/13 08:23:42 deang Exp $
+ *  $Id: aic7xxx_proc.c,v 4.1 1997/06/97 08:23:42 deang Exp $
  *-M*************************************************************************/
 
 #define BLS buffer + len + size
@@ -77,16 +77,18 @@
   struct Scsi_Host *HBAptr;
   struct aic7xxx_host *p;
   static u8 buff[512];
-  int   i; 
+  int   i;
+  int   found = FALSE;
   int   size = 0;
   int   len = 0;
   off_t begin = 0;
   off_t pos = 0;
   static char *bus_names[] = { "Single", "Twin", "Wide" };
-  static char *chip_names[] = { "AIC-777x", "AIC-785x", "AIC-787x", "AIC-788x" };
+  static char *chip_names[] = { "AIC-777x", "AIC-785x", "AIC-786x",
+      "AIC-787x", "AIC-788x" };
 
   HBAptr = NULL;
-  for (i = 0; i < NUMBER(aic7xxx_boards); i++)
+  for (i=0; i < NUMBER(aic7xxx_boards); i++)
   {
     if ((HBAptr = aic7xxx_boards[i]) != NULL)
     {
@@ -95,16 +97,23 @@
         break;
       }
 
-      while ((HBAptr->hostdata != NULL) &&
+      while ((HBAptr->hostdata != NULL) && !found &&
           ((HBAptr = ((struct aic7xxx_host *) HBAptr->hostdata)->next) != NULL))
       {
         if (HBAptr->host_no == hostno)
         {
-          break; break;
+          found = TRUE;
         }
       }
 
-      HBAptr = NULL;
+      if (!found)
+      {
+        HBAptr = NULL;
+      }
+      else
+      {
+        break;
+      }
     }
   }
 
@@ -129,8 +138,10 @@
 
   size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
   size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_C_VERSION));
-  size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_H_VERSION));
+  size += sprintf(BLS, "%s", rcs_version(AIC7XXX_H_VERSION));
+#if 0
   size += sprintf(BLS, "%s\n", rcs_version(AIC7XXX_SEQ_VER));
+#endif
   len += size; pos = begin + len; size = 0;
 
   size += sprintf(BLS, "\n");
@@ -141,11 +152,6 @@
 #ifdef AIC7XXX_CMDS_PER_LUN
   size += sprintf(BLS, "  AIC7XXX_CMDS_PER_LUN   : %d\n", AIC7XXX_CMDS_PER_LUN);
 #endif
-#ifdef AIC7XXX_TWIN_SUPPORT
-  size += sprintf(BLS, "  AIC7XXX_TWIN_SUPPORT   : Enabled\n");
-#else
-  size += sprintf(BLS, "  AIC7XXX_TWIN_SUPPORT   : Disabled\n");
-#endif
 #ifdef AIC7XXX_TAGGED_QUEUEING
   size += sprintf(BLS, "  AIC7XXX_TAGGED_QUEUEING: Enabled\n");
 #else
@@ -165,16 +171,18 @@
 
   size += sprintf(BLS, "\n");
   size += sprintf(BLS, "Adapter Configuration:\n");
-  size += sprintf(BLS, "          SCSI Adapter: %s\n", board_names[p->type]);
+  size += sprintf(BLS, "          SCSI Adapter: %s\n",
+      board_names[p->chip_type]);
   size += sprintf(BLS, "                        (%s chipset)\n",
-      chip_names[p->chip_type]);
+      chip_names[p->chip_class]);
   size += sprintf(BLS, "              Host Bus: %s\n", bus_names[p->bus_type]);
   size += sprintf(BLS, "               Base IO: %#.4x\n", p->base);
+  size += sprintf(BLS, "        Base IO Memory: 0x%x\n", p->mbase);
   size += sprintf(BLS, "                   IRQ: %d\n", HBAptr->irq);
   size += sprintf(BLS, "                  SCBs: Used %d, HW %d, Page %d\n",
-      p->scb_link->numscbs, p->maxhscbs, p->maxscbs);
+      p->scb_data->numscbs, p->scb_data->maxhscbs, p->scb_data->maxscbs);
   size += sprintf(BLS, "            Interrupts: %d", p->isr_count);
-  if (p->chip_type == AIC_777x)
+  if (p->chip_class == AIC_777x)
   {
     size += sprintf(BLS, " %s\n",
         (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx_reg.h linux/drivers/scsi/aic7xxx_reg.h
--- v2.0.30/linux/drivers/scsi/aic7xxx_reg.h	Sun Oct 13 03:42:28 1996
+++ linux/drivers/scsi/aic7xxx_reg.h	Thu Jul 31 12:37:17 1997
@@ -1,771 +1,469 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx register and scratch ram definitions.
- *
- * Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
- * All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2, or (at your option)
- * any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; see the file COPYING.  If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This version corresponds to version 1.12 of FreeBSDs aic7xxx_reg.h
- *
- * $Id: aic7xxx_reg.h,v 4.0 1996/10/13 08:23:42 deang Exp $
- *-M*************************************************************************/
-
-/*
- * This header is shared by the sequencer code and the kernel level driver.
- *
- * All page numbers refer to the Adaptec AIC-7770 Data Book available from
- * Adaptec's Technical Documents Department 1-800-934-2766
- */
-
-/*
- * SCSI Sequence Control (p. 3-11).
- * Each bit, when set starts a specific SCSI sequence on the bus
- */
-#define SCSISEQ			0x000
-#define		TEMODEO		0x80
-#define		ENSELO		0x40
-#define		ENSELI		0x20
-#define		ENRSELI		0x10
-#define		ENAUTOATNO	0x08
-#define		ENAUTOATNI	0x04
-#define		ENAUTOATNP	0x02
-#define		SCSIRSTO	0x01
-
-/*
- * SCSI Transfer Control 0 Register (pp. 3-13).
- * Controls the SCSI module data path.
- */
-#define	SXFRCTL0		0x001
-#define		DFON		0x80
-#define		DFPEXP		0x40
-#define		ULTRAEN		0x20
-#define		CLRSTCNT	0x10
-#define		SPIOEN		0x08
-#define		SCAMEN		0x04
-#define		CLRCHN		0x02
-/*  UNUSED			0x01 */
-
-/*
- * SCSI Transfer Control 1 Register (pp. 3-14,15).
- * Controls the SCSI module data path.
- */
-#define	SXFRCTL1		0x002
-#define		BITBUCKET	0x80
-#define		SWRAPEN		0x40
-#define		ENSPCHK		0x20
-#define		STIMESEL	0x18
-#define		ENSTIMER	0x04
-#define		ACTNEGEN	0x02
-#define		STPWEN		0x01	/* Powered Termination */
-
-/*
- * SCSI Control Signal Read Register (p. 3-15).
- * Reads the actual state of the SCSI bus pins
- */
-#define SCSISIGI		0x003
-#define		CDI		0x80
-#define		IOI		0x40
-#define		MSGI		0x20
-#define		ATNI		0x10
-#define		SELI		0x08
-#define		BSYI		0x04
-#define		REQI		0x02
-#define		ACKI		0x01
-
-/*
- * Possible phases in SCSISIGI
- */
-#define		PHASE_MASK	0xe0
-#define		P_DATAOUT	0x00
-#define		P_DATAIN	0x40
-#define		P_COMMAND	0x80
-#define		P_MESGOUT	0xa0
-#define		P_STATUS	0xc0
-#define		P_MESGIN	0xe0
-/*
- * SCSI Control Signal Write Register (p. 3-16).
- * Writing to this register modifies the control signals on the bus.  Only
- * those signals that are allowed in the current mode (Initiator/Target) are
- * asserted.
- */
-#define SCSISIGO		0x003
-#define		CDO		0x80
-#define		IOO		0x40
-#define		MSGO		0x20
-#define		ATNO		0x10
-#define		SELO		0x08
-#define		BSYO		0x04
-#define		REQO		0x02
-#define		ACKO		0x01
-
-/* 
- * SCSI Rate Control (p. 3-17).
- * Contents of this register determine the Synchronous SCSI data transfer
- * rate and the maximum synchronous Req/Ack offset.  An offset of 0 in the
- * SOFS (3:0) bits disables synchronous data transfers.  Any offset value
- * greater than 0 enables synchronous transfers.
- */
-#define SCSIRATE		0x004
-#define		WIDEXFER	0x80		/* Wide transfer control */
-#define		SXFR		0x70		/* Sync transfer rate */
-#define		SOFS		0x0f		/* Sync offset */
-
-/*
- * SCSI ID (p. 3-18).
- * Contains the ID of the board and the current target on the
- * selected channel.
- */
-#define SCSIID			0x005
-#define		TID		0xf0		/* Target ID mask */
-#define		OID		0x0f		/* Our ID mask */
-
-/*
- * SCSI Latched Data (p. 3-19).
- * Read/Write latches used to transfer data on the SCSI bus during
- * Automatic or Manual PIO mode.  SCSIDATH can be used for the
- * upper byte of a 16bit wide asynchronous data phase transfer.
- */
-#define SCSIDATL		0x006
-#define SCSIDATH		0x007
-
-/*
- * SCSI Transfer Count (pp. 3-19,20)
- * These registers count down the number of bytes transfered
- * across the SCSI bus.  The counter is decremented only once
- * the data has been safely transfered.  SDONE in SSTAT0 is
- * set when STCNT goes to 0
- */ 
-#define STCNT			0x008
-#define STCNT0			0x008
-#define STCNT1			0x009
-#define STCNT2			0x00a
-
-/*
- * Clear SCSI Interrupt 0 (p. 3-20)
- * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
- */
-#define	CLRSINT0		0x00b
-#define		CLRSELDO	0x40
-#define		CLRSELDI	0x20
-#define		CLRSELINGO	0x10
-#define		CLRSWRAP	0x08
-/*  UNUSED			0x04 */
-#define		CLRSPIORDY	0x02
-/*  UNUSED			0x01 */
-
-/*
- * SCSI Status 0 (p. 3-21)
- * Contains one set of SCSI Interrupt codes
- * These are most likely of interest to the sequencer
- */
-#define SSTAT0			0x00b
-#define		TARGET		0x80		/* Board acting as target */
-#define		SELDO		0x40		/* Selection Done */
-#define		SELDI		0x20		/* Board has been selected */
-#define		SELINGO		0x10		/* Selection In Progress */
-#define		SWRAP		0x08		/* 24bit counter wrap */
-#define		SDONE		0x04		/* STCNT = 0x000000 */
-#define		SPIORDY		0x02		/* SCSI PIO Ready */
-#define		DMADONE		0x01		/* DMA transfer completed */
-
-/*
- * Clear SCSI Interrupt 1 (p. 3-23)
- * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
- */
-#define CLRSINT1		0x00c
-#define		CLRSELTIMEO	0x80
-#define		CLRATNO		0x40
-#define		CLRSCSIRSTI	0x20
-/*  UNUSED			0x10 */
-#define		CLRBUSFREE	0x08
-#define		CLRSCSIPERR	0x04
-#define		CLRPHASECHG	0x02
-#define		CLRREQINIT	0x01
-
-/*
- * SCSI Status 1 (p. 3-24)
- */
-#define SSTAT1			0x00c
-#define		SELTO		0x80
-#define		ATNTARG 	0x40
-#define		SCSIRSTI	0x20
-#define		PHASEMIS	0x10
-#define		BUSFREE		0x08
-#define		SCSIPERR	0x04
-#define		PHASECHG	0x02
-#define		REQINIT		0x01
-
-/*
- * SCSI Interrupt Mode 1 (pp. 3-28,29)
- * Setting any bit will enable the corresponding function
- * in SIMODE1 to interrupt via the IRQ pin.
- */
-#define	SIMODE1			0x011
-#define		ENSELTIMO	0x80
-#define		ENATNTARG	0x40
-#define		ENSCSIRST	0x20
-#define		ENPHASEMIS	0x10
-#define		ENBUSFREE	0x08
-#define		ENSCSIPERR	0x04
-#define		ENPHASECHG	0x02
-#define		ENREQINIT	0x01
-
-/*
- * SCSI Data Bus (High) (p. 3-29)
- * This register reads data on the SCSI Data bus directly.
- */
-#define	SCSIBUSL		0x012
-#define	SCSIBUSH		0x013
-
-/*
- * SCSI/Host Address (p. 3-30)
- * These registers hold the host address for the byte about to be
- * transfered on the SCSI bus.  They are counted up in the same
- * manner as STCNT is counted down.  SHADDR should always be used
- * to determine the address of the last byte transfered since HADDR
- * can be skewed by write ahead.
- */
-#define	SHADDR			0x014
-#define	SHADDR0			0x014
-#define	SHADDR1			0x015
-#define	SHADDR2			0x016
-#define	SHADDR3			0x017
-
-/*
- * Selection/Reselection ID (p. 3-31)
- * Upper four bits are the device id.  The ONEBIT is set when the re/selecting
- * device did not set its own ID.
- */
-#define SELID			0x019
-#define		SELID_MASK	0xf0
-#define		ONEBIT		0x08
-/*  UNUSED			0x07 */
-
-/*
- * SCSI Block Control (p. 3-32)
- * Controls Bus type and channel selection.  In a twin channel configuration
- * addresses 0x00-0x1e are gated to the appropriate channel based on this
- * register.  SELWIDE allows for the coexistence of 8bit and 16bit devices
- * on a wide bus.
- */
-#define SBLKCTL			0x01f
-#define		DIAGLEDEN	0x80	/* Aic78X0 only */
-#define		DIAGLEDON	0x40	/* Aic78X0 only */
-#define		AUTOFLUSHDIS	0x20
-/*  UNUSED			0x10 */
-#define		SELBUS_MASK	0x0a
-#define		SELBUSB		0x08
-/*  UNUSED			0x04 */
-#define		SELWIDE		0x02
-/*  UNUSED			0x01 */
-#define		SELNARROW	0x00
-
-/*
- * Sequencer Control (p. 3-33)
- * Error detection mode and speed configuration
- */
-#define SEQCTL			0x060
-#define		PERRORDIS	0x80
-#define		PAUSEDIS	0x40
-#define		FAILDIS		0x20
-#define 	FASTMODE	0x10
-#define		BRKADRINTEN	0x08
-#define		STEP		0x04
-#define		SEQRESET	0x02
-#define		LOADRAM		0x01
-
-/*
- * Sequencer RAM Data (p. 3-34)
- * Single byte window into the Scratch Ram area starting at the address
- * specified by SEQADDR0 and SEQADDR1.  To write a full word, simply write
- * four bytes in succession.  The SEQADDRs will increment after the most
- * significant byte is written
- */
-#define SEQRAM			0x061
-
-/*
- * Sequencer Address Registers (p. 3-35)
- * Only the first bit of SEQADDR1 holds addressing information
- */
-#define SEQADDR0		0x062
-#define SEQADDR1		0x063
-#define 	SEQADDR1_MASK	0x01
-
-/*
- * Accumulator
- * We cheat by passing arguments in the Accumulator up to the kernel driver
- */
-#define ACCUM			0x064
-
-#define SINDEX			0x065
-#define DINDEX			0x066
-#define ALLZEROS		0x06a
-#define NONE			0x06a
-#define SINDIR			0x06c
-#define DINDIR			0x06d
-#define FUNCTION1		0x06e
-
-/*
- * Host Address (p. 3-48)
- * This register contains the address of the byte about
- * to be transfered across the host bus.
- */
-#define HADDR			0x088
-#define HADDR0			0x088
-#define HADDR1			0x089
-#define HADDR2			0x08a
-#define HADDR3			0x08b
-
-#define HCNT			0x08c
-#define HCNT0			0x08c
-#define HCNT1			0x08d
-#define HCNT2			0x08e
-/*
- * SCB Pointer (p. 3-49)
- * Gate one of the four SCBs into the SCBARRAY window.
- */
-#define SCBPTR			0x090
-
-/*
- * Board Control (p. 3-43)
- */
-#define BCTL			0x084
-/*   RSVD			0xf0 */
-#define		ACE		0x08	/* Support for external processors */
-/*   RSVD			0x06 */
-#define		ENABLE		0x01
-
-/*
- * On the aic78X0 chips, Board Control is replaced by the DSCommand
- * register (p. 4-64)
- */
-#define	DSCOMMAND		0x084
-#define		CACHETHEN	0x80	/* Cache Threshold enable */
-#define		DPARCKEN	0x40	/* Data Parity Check Enable */
-#define		MPARCKEN	0x20	/* Memory Parity Check Enable */
-#define		EXTREQLCK	0x10	/* External Request Lock */
-
-/*
- * Bus On/Off Time (p. 3-44)
- */
-#define BUSTIME			0x085
-#define		BOFF		0xf0
-#define		BON		0x0f
-
-/*
- * Bus Speed (p. 3-45)
- */
-#define	BUSSPD			0x086
-#define		DFTHRSH		0xc0
-#define		STBOFF		0x38
-#define		STBON		0x07
-#define		DFTHRSH_100	0xc0
-
-/*
- * Host Control (p. 3-47) R/W
- * Overall host control of the device.
- */
-#define HCNTRL			0x087
-/*    UNUSED			0x80 */
-#define		POWRDN		0x40
-/*    UNUSED			0x20 */
-#define		SWINT		0x10
-#define		IRQMS		0x08
-#define		PAUSE		0x04
-#define		INTEN		0x02
-#define		CHIPRST		0x01
-#define		CHIPRSTACK	0x01
-
-/*
- * Interrupt Status (p. 3-50)
- * Status for system interrupts
- */
-#define INTSTAT			0x091
-#define		SEQINT_MASK	0xf1		/* SEQINT Status Codes */
-#define			BAD_PHASE	0x01	/* unknown scsi bus phase */
-#define			SEND_REJECT	0x11	/* sending a message reject */
-#define			NO_IDENT	0x21	/* no IDENTIFY after reconnect*/
-#define			NO_MATCH	0x31	/* no cmd match for reconnect */
-#define			SDTR_MSG	0x41	/* SDTR message received */
-#define			WDTR_MSG	0x51	/* WDTR message received */
-#define			REJECT_MSG	0x61	/* Reject message received */
-#define			BAD_STATUS	0x71	/* Bad status from target */
-#define			RESIDUAL	0x81	/* Residual byte count != 0 */
-#define			ABORT_TAG	0x91	/* Sent an ABORT_TAG message */
-#define			AWAITING_MSG	0xa1	/*
-						 * Kernel requested to specify
-                                                 * a message to this target
-                                                 * (command was null), so tell
-                                                 * it that it can fill the
-                                                 * message buffer.
-                                                 */
-#define			IMMEDDONE	0xb1	/*
-						 * An immediate command has
-						 * completed
-						 */
-#define			MSG_BUFFER_BUSY	0xc1	/*
-						 * Sequencer wants to use the
-						 * message buffer, but it
-						 * already contains a message
-						 */
-#define			MSGIN_PHASEMIS	0xd1	/*
-						 * Target changed phase on us
-						 * when we were expecting
-						 * another msgin byte.
-						 */
-#define			DATA_OVERRUN	0xe1	/*
-						 * Target attempted to write
-						 * beyond the bounds of its
-						 * command.
-						 */
-#define 	BRKADRINT 0x08
-#define		SCSIINT	  0x04
-#define		CMDCMPLT  0x02
-#define		SEQINT    0x01
-#define		INT_PEND  (BRKADRINT | SEQINT | SCSIINT | CMDCMPLT)
-
-/*
- * Hard Error (p. 3-53)
- * Reporting of catastrophic errors.  You usually cannot recover from
- * these without a full board reset.
- */
-#define ERROR			0x092
-/*    UNUSED			0xf0 */
-#define		PARERR		0x08
-#define		ILLOPCODE	0x04
-#define		ILLSADDR	0x02
-#define		ILLHADDR	0x01
-
-/*
- * Clear Interrupt Status (p. 3-52)
- */
-#define CLRINT			0x092
-#define		CLRBRKADRINT	0x08
-#define		CLRSCSIINT      0x04
-#define		CLRCMDINT 	0x02
-#define		CLRSEQINT 	0x01
-
-#define	DFCNTRL			0x093
-#define		WIDEODD		0x40
-#define		SCSIEN		0x20
-#define		SDMAEN		0x10
-#define		SDMAENACK	0x10
-#define		HDMAEN		0x08
-#define		HDMAENACK	0x08
-#define		DIRECTION	0x04
-#define		FIFOFLUSH	0x02
-#define		FIFORESET	0x01
-
-#define	DFSTATUS		0x094
-#define		HDONE		0x08
-#define		FIFOEMP		0x01
-
-#define	DFDAT			0x099
-
-/*
- * SCB Auto Increment (p. 3-59)
- * Byte offset into the SCB Array and an optional bit to allow auto
- * incrementing of the address during download and upload operations
- */
-#define SCBCNT			0x09a
-#define		SCBAUTO		0x80
-#define		SCBCNT_MASK	0x1f
-
-/*
- * Queue In FIFO (p. 3-60)
- * Input queue for queued SCBs (commands that the sequencer has yet to start)
- */
-#define QINFIFO			0x09b
-
-/*
- * Queue In Count (p. 3-60)
- * Number of queued SCBs
- */
-#define QINCNT			0x09c
-
-/*
- * Queue Out FIFO (p. 3-61)
- * Queue of SCBs that have completed and await the host
- */
-#define QOUTFIFO		0x09d
-
-/*
- * Queue Out Count (p. 3-61)
- * Number of queued SCBs in the Out FIFO
- */
-#define QOUTCNT			0x09e
-
-/*
- * SCB Definition (p. 5-4)
- * The two reserved bytes at SCBARRAY+1[23] are expected to be set to
- * zero. Bit 3 in SCBARRAY+0 is used as an internal flag to indicate
- * whether or not to DMA an SCB from host ram. This flag prevents the
- * "re-fetching" of transactions that are requeued because the target is
- * busy with another command. We also use bits 6 & 7 to indicate whether
- * or not to initiate SDTR or WDTR respectively when starting this command.
- */
-#define SCBARRAY		0x0a0
-#define	SCB_CONTROL		0x0a0
-#define		NEEDWDTR        0x80
-#define		DISCENB         0x40
-#define		TAG_ENB		0x20
-#define		NEEDSDTR	0x10
-#define		DISCONNECTED	0x04
-#define		SCB_TAG_TYPE	0x03
-#define	SCB_TCL			0x0a1
-#define	SCB_TARGET_STATUS	0x0a2
-#define	SCB_SGCOUNT		0x0a3
-#define	SCB_SGPTR		0x0a4
-#define		SCB_SGPTR0	0x0a4
-#define		SCB_SGPTR1	0x0a5
-#define		SCB_SGPTR2	0x0a6
-#define		SCB_SGPTR3	0x0a7
-#define	SCB_RESID_SGCNT		0x0a8
-#define SCB_RESID_DCNT		0x0a9
-#define		SCB_RESID_DCNT0	0x0a9
-#define		SCB_RESID_DCNT1	0x0aa
-#define		SCB_RESID_DCNT2	0x0ab
-#define SCB_DATAPTR		0x0ac
-#define		SCB_DATAPTR0	0x0ac
-#define		SCB_DATAPTR1	0x0ad
-#define		SCB_DATAPTR2	0x0ae
-#define		SCB_DATAPTR3	0x0af
-#define	SCB_DATACNT		0x0b0
-#define		SCB_DATACNT0	0x0b0
-#define		SCB_DATACNT1	0x0b1
-#define		SCB_DATACNT2	0x0b2
-/* UNUSED - QUAD PADDING	0x0b3 */
-#define SCB_CMDPTR		0x0b4
-#define		SCB_CMDPTR0	0x0b4
-#define		SCB_CMDPTR1	0x0b5
-#define		SCB_CMDPTR2	0x0b6
-#define		SCB_CMDPTR3	0x0b7
-#define	SCB_CMDLEN		0x0b8
-#define SCB_TAG			0x0b9
-#define	SCB_NEXT		0x0ba
-#define	SCB_PREV		0x0bb
-
-#define	SG_SIZEOF		0x08		/* sizeof(struct ahc_dma) */
-
-/* --------------------- AHA-2840-only definitions -------------------- */
-
-#define	SEECTL_2840		0x0c0
-/*	UNUSED			0xf8 */
-#define		CS_2840		0x04
-#define		CK_2840		0x02
-#define		DO_2840		0x01
-
-#define	STATUS_2840		0x0c1
-#define		EEPROM_TF	0x80
-#define		BIOS_SEL	0x60
-#define		ADSEL		0x1e
-#define		DI_2840		0x01
-
-/* --------------------- AIC-7870-only definitions -------------------- */
-
-#define DSPCISTATUS		0x086
-
-/*
- * Serial EEPROM Control (p. 4-92 in 7870 Databook)
- * Controls the reading and writing of an external serial 1-bit
- * EEPROM Device.  In order to access the serial EEPROM, you must
- * first set the SEEMS bit that generates a request to the memory
- * port for access to the serial EEPROM device.  When the memory
- * port is not busy servicing another request, it reconfigures
- * to allow access to the serial EEPROM.  When this happens, SEERDY
- * gets set high to verify that the memory port access has been
- * granted.  
- *
- * After successful arbitration for the memory port, the SEECS bit of 
- * the SEECTL register is connected to the chip select.  The SEECK, 
- * SEEDO, and SEEDI are connected to the clock, data out, and data in 
- * lines respectively.  The SEERDY bit of SEECTL is useful in that it 
- * gives us an 800 nsec timer.  After a write to the SEECTL register, 
- * the SEERDY goes high 800 nsec later.  The one exception to this is 
- * when we first request access to the memory port.  The SEERDY goes 
- * high to signify that access has been granted and, for this case, has 
- * no implied timing.
- *
- * See 93cx6.c for detailed information on the protocol necessary to 
- * read the serial EEPROM.
- */
-#define SEECTL			0x01e
-#define		EXTARBACK	0x80
-#define		EXTARBREQ	0x40
-#define		SEEMS		0x20
-#define		SEERDY		0x10
-#define		SEECS		0x08
-#define		SEECK		0x04
-#define		SEEDO		0x02
-#define		SEEDI		0x01
-
-/* ---------------------- Scratch RAM Offsets ------------------------- */
-/* These offsets are either to values that are initialized by the board's
- * BIOS or are specified by the sequencer code.
- *
- * The host adapter card (at least the BIOS) uses 20-2f for SCSI
- * device information, 32-33 and 5a-5f as well. As it turns out, the
- * BIOS trashes 20-2f, writing the synchronous negotiation results
- * on top of the BIOS values, so we re-use those for our per-target
- * scratchspace (actually a value that can be copied directly into
- * SCSIRATE).  The kernel driver will enable synchronous negotiation
- * for all targets that have a value other than 0 in the lower four
- * bits of the target scratch space.  This should work regardless of
- * whether the bios has been installed.
- */
-
-/*
- * 1 byte per target starting at this address for configuration values
- */
-#define TARG_SCRATCH		0x020
-
-/*
- * The sequencer will stick the frist byte of any rejected message here so
- * we can see what is getting thrown away.  Extended messages put the
- * extended message type in REJBYTE_EXT.
- */
-#define REJBYTE			0x030
-#define REJBYTE_EXT		0x031
-
-/*
- * Bit vector of targets that have disconnection disabled.
- */
-#define	DISC_DSB		0x032
-#define		DISC_DSB_A	0x032
-#define		DISC_DSB_B	0x033
-
-/*
- * Length of pending message
- */
-#define MSG_LEN			0x034
-
-/* We reserve 8bytes to store outgoing messages */
-#define MSG0			0x035
-#define		COMP_MSG0	0xcb      /* 2's complement of MSG0 */
-#define MSG1			0x036
-#define MSG2			0x037
-#define MSG3			0x038
-#define MSG4			0x039
-#define MSG5			0x03a
-#define MSG6			0x03b
-#define MSG7			0x03c
-
 /*
- * These are offsets into the card's scratch ram.  Some of the values are
- * specified in the AHA2742 technical reference manual and are initialized
- * by the BIOS at boot time.
- */
-#define LASTPHASE		0x03d
-#define ARG_1			0x03e
-#define		MAXOFFSET	0x01
-#define RETURN_1		0x03f
-#define		SEND_WDTR	0x80
-#define		SEND_SDTR	0x60
-#define		SEND_SENSE	0x40
-#define		SEND_REJ	0x20
-#define		SCB_PAGEDIN	0x10
-
-#define SIGSTATE		0x040
-
-#define DMAPARAMS		0x041	/* Parameters for DMA Logic */
-
-#define	SG_COUNT		0x042
-#define	SG_NEXT			0x043	/* working value of SG pointer */
-#define		SG_NEXT0	0x043
-#define		SG_NEXT1	0x044
-#define		SG_NEXT2	0x045
-#define		SG_NEXT3	0x046
-
-#define	SCBCOUNT		0x047	/*
-					 * Number of SCBs supported by
-					 * this card.
-					 */
-#define	COMP_SCBCOUNT		0x048	/*
-					 * Two's compliment of SCBCOUNT
-					 */
-#define QCNTMASK		0x049	/*
-					 * Mask of bits to test against
-					 * when looking at the Queue Count
-					 * registers.  Works around a bug
-					 * on aic7850 chips. 
-					 */
-#define FLAGS			0x04a
-#define		SINGLE_BUS	0x00
-#define		TWIN_BUS	0x01
-#define		WIDE_BUS	0x02
-#define		PAGESCBS	0x04
-#define		DPHASE		0x10
-#define		SELECTED	0x20
-#define		IDENTIFY_SEEN	0x40
-#define		RESELECTED	0x80
-
-#define	SAVED_TCL		0x04b	/*
-					 * Temporary storage for the
-					 * target/channel/lun of a
-					 * reconnecting target
-					 */
-#define	ACTIVE_A		0x04c
-#define	ACTIVE_B		0x04d
-#define WAITING_SCBH		0x04e	/*
-					 * head of list of SCBs awaiting
-					 * selection
-					 */
-#define DISCONNECTED_SCBH	0x04f	/*
-					 * head of list of SCBs that are
-					 * disconnected.  Used for SCB
-					 * paging.
-					 */
-#define		SCB_LIST_NULL	0xff
-
-#define SAVED_LINKPTR		0x050
-#define SAVED_SCBPTR		0x051
-#define ULTRA_ENB		0x052
-#define ULTRA_ENB_B		0x053
-
-#define SCSICONF		0x05a
-#define		RESET_SCSI	0x40
-
-#define HOSTCONF		0x05d
-
-#define HA_274_BIOSCTRL		0x05f
-#define BIOSMODE		0x30
-#define BIOSDISABLED		0x30
-#define CHANNEL_B_PRIMARY	0x08
-
-/* Message codes */
-#define MSG_EXTENDED		0x01
-#define		MSG_SDTR	0x01
-#define		MSG_WDTR	0x03
-#define MSG_SDPTRS		0x02
-#define MSG_RDPTRS		0x03
-#define MSG_DISCONNECT		0x04
-#define MSG_INITIATOR_DET_ERROR	0x05
-#define MSG_ABORT		0x06
-#define	MSG_REJECT		0x07
-#define MSG_NOP			0x08
-#define MSG_MSG_PARITY_ERROR	0x09
-#define MSG_BUS_DEVICE_RESET	0x0c
-#define MSG_ABORT_TAG		0x0d
-#define MSG_SIMPLE_TAG		0x20
-#define MSG_IDENTIFY		0x80
-
-/* WDTR Message values */
-#define	BUS_8_BIT		0x00
-#define BUS_16_BIT		0x01
-#define BUS_32_BIT		0x02
+  * DO NOT EDIT - This file is automatically generated.
+  */
 
-#define MAX_OFFSET_8BIT		0x0f
-#define MAX_OFFSET_16BIT	0x08
+#define	SCSISEQ         		0x00
+#define		TEMODE          	0x80
+#define		ENSELO          	0x40
+#define		ENSELI          	0x20
+#define		ENRSELI         	0x10
+#define		ENAUTOATNO      	0x08
+#define		ENAUTOATNI      	0x04
+#define		ENAUTOATNP      	0x02
+#define		SCSIRSTO        	0x01
+
+#define	SXFRCTL0        		0x01
+#define		DFON            	0x80
+#define		DFPEXP          	0x40
+#define		FAST20          	0x20
+#define		CLRSTCNT        	0x10
+#define		SPIOEN          	0x08
+#define		SCAMEN          	0x04
+#define		CLRCHN          	0x02
+
+#define	SXFRCTL1        		0x02
+#define		BITBUCKET       	0x80
+#define		SWRAPEN         	0x40
+#define		ENSPCHK         	0x20
+#define		STIMESEL        	0x18
+#define		ENSTIMER        	0x04
+#define		ACTNEGEN        	0x02
+#define		STPWEN          	0x01
+
+#define	SCSISIGO        		0x03
+#define		CDO             	0x80
+#define		IOO             	0x40
+#define		MSGO            	0x20
+#define		ATNO            	0x10
+#define		SELO            	0x08
+#define		BSYO            	0x04
+#define		REQO            	0x02
+#define		ACKO            	0x01
+
+#define	SCSISIGI        		0x03
+#define		ATNI            	0x10
+#define		SELI            	0x08
+#define		BSYI            	0x04
+#define		REQI            	0x02
+#define		ACKI            	0x01
+
+#define	SCSIRATE        		0x04
+#define		WIDEXFER        	0x80
+#define		SXFR            	0x70
+#define		SOFS            	0x0f
+
+#define	SCSIID          		0x05
+#define		OID             	0x0f
+
+#define	SCSIDATL        		0x06
+
+#define	SCSIDATH        		0x07
+
+#define	STCNT           		0x08
+
+#define	CLRSINT0        		0x0b
+#define		CLRSELDO        	0x40
+#define		CLRSELDI        	0x20
+#define		CLRSELINGO      	0x10
+#define		CLRSWRAP        	0x08
+#define		CLRSPIORDY      	0x02
+
+#define	SSTAT0          		0x0b
+#define		TARGET          	0x80
+#define		SELDO           	0x40
+#define		SELDI           	0x20
+#define		SELINGO         	0x10
+#define		SWRAP           	0x08
+#define		SDONE           	0x04
+#define		SPIORDY         	0x02
+#define		DMADONE         	0x01
+
+#define	CLRSINT1        		0x0c
+#define		CLRSELTIMEO     	0x80
+#define		CLRATNO         	0x40
+#define		CLRSCSIRSTI     	0x20
+#define		CLRBUSFREE      	0x08
+#define		CLRSCSIPERR     	0x04
+#define		CLRPHASECHG     	0x02
+#define		CLRREQINIT      	0x01
+
+#define	SSTAT1          		0x0c
+#define		SELTO           	0x80
+#define		ATNTARG         	0x40
+#define		SCSIRSTI        	0x20
+#define		PHASEMIS        	0x10
+#define		BUSFREE         	0x08
+#define		SCSIPERR        	0x04
+#define		PHASECHG        	0x02
+#define		REQINIT         	0x01
+
+#define	SSTAT2          		0x0d
+#define		OVERRUN         	0x80
+#define		SFCNT           	0x1f
+
+#define	SSTAT3          		0x0e
+#define		SCSICNT         	0xf0
+#define		OFFCNT          	0x0f
+
+#define	SCSITEST        		0x0f
+#define		RQAKCNT         	0x04
+#define		CNTRTEST        	0x02
+#define		CMODE           	0x01
+
+#define	SIMODE0         		0x10
+#define		ENSELDO         	0x40
+#define		ENSELDI         	0x20
+#define		ENSELINGO       	0x10
+#define		ENSWRAP         	0x08
+#define		ENSDONE         	0x04
+#define		ENSPIORDY       	0x02
+#define		ENDMADONE       	0x01
+
+#define	SIMODE1         		0x11
+#define		ENSELTIMO       	0x80
+#define		ENATNTARG       	0x40
+#define		ENSCSIRST       	0x20
+#define		ENPHASEMIS      	0x10
+#define		ENBUSFREE       	0x08
+#define		ENSCSIPERR      	0x04
+#define		ENPHASECHG      	0x02
+#define		ENREQINIT       	0x01
+
+#define	SCSIBUSL        		0x12
+
+#define	SCSIBUSH        		0x13
+
+#define	SHADDR          		0x14
+
+#define	SELTIMER        		0x18
+#define		STAGE6          	0x20
+#define		STAGE5          	0x10
+#define		STAGE4          	0x08
+#define		STAGE3          	0x04
+#define		STAGE2          	0x02
+#define		STAGE1          	0x01
+
+#define	SELID           		0x19
+#define		SELID_MASK      	0xf0
+#define		ONEBIT          	0x08
+
+#define	BRDCTL          		0x1d
+#define		BRDDAT7         	0x80
+#define		BRDDAT6         	0x40
+#define		BRDDAT5         	0x20
+#define		BRDSTB          	0x10
+#define		BRDCS           	0x08
+#define		BRDRW           	0x04
+#define		BRDCTL1         	0x02
+#define		BRDCTL0         	0x01
+
+#define	SEECTL          		0x1e
+#define		EXTARBACK       	0x80
+#define		EXTARBREQ       	0x40
+#define		SEEMS           	0x20
+#define		SEERDY          	0x10
+#define		SEECS           	0x08
+#define		SEECK           	0x04
+#define		SEEDO           	0x02
+#define		SEEDI           	0x01
+
+#define	SBLKCTL         		0x1f
+#define		DIAGLEDEN       	0x80
+#define		DIAGLEDON       	0x40
+#define		AUTOFLUSHDIS    	0x20
+#define		SELWIDE         	0x02
+
+#define	SRAM_BASE       		0x20
+
+#define	TARG_SCRATCH    		0x20
+
+#define	ULTRA_ENB       		0x30
+
+#define	DISC_DSB        		0x32
+
+#define	MSG_LEN         		0x34
+
+#define	MSG_OUT         		0x35
+
+#define	DMAPARAMS       		0x3d
+#define		WIDEODD         	0x40
+#define		SCSIEN          	0x20
+#define		SDMAENACK       	0x10
+#define		SDMAEN          	0x10
+#define		HDMAEN          	0x08
+#define		HDMAENACK       	0x08
+#define		DIRECTION       	0x04
+#define		FIFOFLUSH       	0x02
+#define		FIFORESET       	0x01
+
+#define	SCBCOUNT        		0x3e
+
+#define	COMP_SCBCOUNT   		0x3f
+
+#define	QCNTMASK        		0x40
+
+#define	SEQ_FLAGS       		0x41
+#define		RESELECTED      	0x80
+#define		IDENTIFY_SEEN   	0x40
+#define		TAGGED_SCB      	0x20
+#define		DPHASE          	0x10
+#define		PAGESCBS        	0x04
+#define		WIDE_BUS        	0x02
+#define		TWIN_BUS        	0x01
+
+#define	SAVED_TCL       		0x42
+
+#define	SG_COUNT        		0x43
+
+#define	SG_NEXT         		0x44
+
+#define	WAITING_SCBH    		0x48
+
+#define	SAVED_LINKPTR   		0x49
+
+#define	SAVED_SCBPTR    		0x4a
+
+#define	REJBYTE         		0x4b
+
+#define	LASTPHASE       		0x4c
+#define		P_MESGIN        	0xe0
+#define		PHASE_MASK      	0xe0
+#define		P_STATUS        	0xc0
+#define		P_MESGOUT       	0xa0
+#define		P_COMMAND       	0x80
+#define		CDI             	0x80
+#define		IOI             	0x40
+#define		P_DATAIN        	0x40
+#define		MSGI            	0x20
+#define		P_BUSFREE       	0x01
+#define		P_DATAOUT       	0x00
+
+#define	MSGIN_EXT_LEN   		0x4d
+
+#define	MSGIN_EXT_OPCODE		0x4e
+
+#define	MSGIN_EXT_BYTES 		0x4f
+
+#define	DISCONNECTED_SCBH		0x52
+
+#define	FREE_SCBH       		0x53
+
+#define	HSCB_ADDR       		0x54
+
+#define	CUR_SCBID       		0x58
+
+#define	ARG_1           		0x59
+#define	RETURN_1        		0x59
+#define		SEND_MSG        	0x80
+#define		SEND_SENSE      	0x40
+#define		SEND_REJ        	0x20
+
+#define	SCSICONF        		0x5a
+#define		RESET_SCSI      	0x40
+
+#define	HOSTCONF        		0x5d
+
+#define	HA_274_BIOSCTRL 		0x5f
+#define		BIOSMODE        	0x30
+#define		BIOSDISABLED    	0x30
+#define		CHANNEL_B_PRIMARY	0x08
+
+#define	SEQCTL          		0x60
+#define		PERRORDIS       	0x80
+#define		PAUSEDIS        	0x40
+#define		FAILDIS         	0x20
+#define		FASTMODE        	0x10
+#define		BRKADRINTEN     	0x08
+#define		STEP            	0x04
+#define		SEQRESET        	0x02
+#define		LOADRAM         	0x01
+
+#define	SEQRAM          		0x61
+
+#define	SEQADDR0        		0x62
+
+#define	SEQADDR1        		0x63
+#define		SEQADDR1_MASK   	0x01
+
+#define	ACCUM           		0x64
+
+#define	SINDEX          		0x65
+
+#define	DINDEX          		0x66
+
+#define	ALLONES         		0x69
+
+#define	ALLZEROS        		0x6a
+
+#define	NONE            		0x6a
+
+#define	FLAGS           		0x6b
+#define		ZERO            	0x02
+#define		CARRY           	0x01
+
+#define	SINDIR          		0x6c
+
+#define	DINDIR          		0x6d
+
+#define	FUNCTION1       		0x6e
+
+#define	STACK           		0x6f
+
+#define	BCTL            		0x84
+#define		ACE             	0x08
+#define		ENABLE          	0x01
+
+#define	DSCOMMAND       		0x84
+#define		CACHETHEN       	0x80
+#define		DPARCKEN        	0x40
+#define		MPARCKEN        	0x20
+#define		EXTREQLCK       	0x10
+
+#define	BUSTIME         		0x85
+#define		BOFF            	0xf0
+#define		BON             	0x0f
+
+#define	BUSSPD          		0x86
+#define		DFTHRSH_100     	0xc0
+#define		DFTHRSH         	0xc0
+#define		STBOFF          	0x38
+#define		STBON           	0x07
+
+#define	DSPCISTATUS     		0x86
+
+#define	HCNTRL          		0x87
+#define		POWRDN          	0x40
+#define		SWINT           	0x10
+#define		IRQMS           	0x08
+#define		PAUSE           	0x04
+#define		INTEN           	0x02
+#define		CHIPRST         	0x01
+#define		CHIPRSTACK      	0x01
+
+#define	HADDR           		0x88
+
+#define	HCNT            		0x8c
+
+#define	SCBPTR          		0x90
+
+#define	INTSTAT         		0x91
+#define		SEQINT_MASK     	0xf1
+#define		DATA_OVERRUN    	0xe1
+#define		MSGIN_PHASEMIS  	0xd1
+#define		MSG_BUFFER_BUSY 	0xc1
+#define		AWAITING_MSG    	0xa1
+#define		ABORT_CMDCMPLT  	0x91
+#define		RESIDUAL        	0x81
+#define		BAD_STATUS      	0x71
+#define		REJECT_MSG      	0x61
+#define		NO_MATCH_BUSY   	0x51
+#define		EXTENDED_MSG    	0x41
+#define		NO_MATCH        	0x31
+#define		NO_IDENT        	0x21
+#define		SEND_REJECT     	0x11
+#define		INT_PEND        	0x0f
+#define		BRKADRINT       	0x08
+#define		SCSIINT         	0x04
+#define		CMDCMPLT        	0x02
+#define		BAD_PHASE       	0x01
+#define		SEQINT          	0x01
+
+#define	CLRINT          		0x92
+#define		CLRBRKADRINT    	0x08
+#define		CLRSCSIINT      	0x04
+#define		CLRCMDINT       	0x02
+#define		CLRSEQINT       	0x01
+
+#define	ERROR           		0x92
+#define		PARERR          	0x08
+#define		ILLOPCODE       	0x04
+#define		ILLSADDR        	0x02
+#define		ILLHADDR        	0x01
+
+#define	DFCNTRL         		0x93
+
+#define	DFSTATUS        		0x94
+#define		DWORDEMP        	0x20
+#define		MREQPEND        	0x10
+#define		HDONE           	0x08
+#define		DFTHRESH        	0x04
+#define		FIFOFULL        	0x02
+#define		FIFOEMP         	0x01
+
+#define	DFDAT           		0x99
+
+#define	SCBCNT          		0x9a
+#define		SCBAUTO         	0x80
+#define		SCBCNT_MASK     	0x1f
+
+#define	QINFIFO         		0x9b
+
+#define	QINCNT          		0x9c
+
+#define	QOUTFIFO        		0x9d
+
+#define	QOUTCNT         		0x9e
+
+#define	SCB_CONTROL     		0xa0
+#define		MK_MESSAGE      	0x80
+#define		DISCENB         	0x40
+#define		TAG_ENB         	0x20
+#define		MUST_DMAUP_SCB  	0x10
+#define		ABORT_SCB       	0x08
+#define		DISCONNECTED    	0x04
+#define		SCB_TAG_TYPE    	0x03
+
+#define	SCB_BASE        		0xa0
+
+#define	SCB_TCL         		0xa1
+#define		TID             	0xf0
+#define		SELBUSB         	0x08
+#define		LID             	0x07
+
+#define	SCB_TARGET_STATUS		0xa2
+
+#define	SCB_SGCOUNT     		0xa3
+
+#define	SCB_SGPTR       		0xa4
+
+#define	SCB_RESID_SGCNT 		0xa8
+
+#define	SCB_RESID_DCNT  		0xa9
+
+#define	SCB_DATAPTR     		0xac
+
+#define	SCB_DATACNT     		0xb0
+
+#define	SCB_LINKED_NEXT 		0xb3
+
+#define	SCB_CMDPTR      		0xb4
+
+#define	SCB_CMDLEN      		0xb8
+
+#define	SCB_TAG         		0xb9
+
+#define	SCB_NEXT        		0xba
+
+#define	SCB_PREV        		0xbb
+
+#define	SCB_BUSYTARGETS 		0xbc
+
+#define	SEECTL_2840     		0xc0
+#define		CS_2840         	0x04
+#define		CK_2840         	0x02
+#define		DO_2840         	0x01
+
+#define	STATUS_2840     		0xc1
+#define		EEPROM_TF       	0x80
+#define		BIOS_SEL        	0x60
+#define		ADSEL           	0x1e
+#define		DI_2840         	0x01
+
+
+#define	BUS_8_BIT	0x00
+#define	MAX_OFFSET_8BIT	0x0f
+#define	BUS_16_BIT	0x01
+#define	MAX_OFFSET_16BIT	0x08
+#define	SCB_LIST_NULL	0xff
+#define	SG_SIZEOF	0x08
+#define	BUS_32_BIT	0x02
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/aic7xxx_seq.h linux/drivers/scsi/aic7xxx_seq.h
--- v2.0.30/linux/drivers/scsi/aic7xxx_seq.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_seq.h	Mon Aug  4 15:23:47 1997
@@ -0,0 +1,479 @@
+/*
+  * DO NOT EDIT - This file is automatically generated.
+  */
+static u_int8_t seqprog[] = {
+	0xff, 0x6a, 0x03, 0x02,
+	0x12, 0x6a, 0x00, 0x00,
+	0x00, 0x65, 0x6f, 0x16,
+	0x40, 0x0b, 0x3c, 0x1a,
+	0x20, 0x0b, 0x37, 0x1a,
+	0x40, 0x00, 0x03, 0x1a,
+	0x08, 0x1f, 0x1f, 0x04,
+	0x40, 0x0b, 0x3c, 0x1a,
+	0x20, 0x0b, 0x37, 0x1a,
+	0x40, 0x00, 0x03, 0x1a,
+	0x08, 0x1f, 0x1f, 0x04,
+	0xff, 0x48, 0x2c, 0x18,
+	0xff, 0x40, 0x64, 0x02,
+	0x00, 0x9c, 0x03, 0x1e,
+	0x00, 0x6a, 0xaa, 0x17,
+	0xff, 0x65, 0x03, 0x1c,
+	0xff, 0x9b, 0x58, 0x02,
+	0xff, 0x58, 0x90, 0x02,
+	0x0d, 0x6a, 0x3d, 0x00,
+	0x00, 0x58, 0x74, 0x17,
+	0x28, 0xa0, 0x2a, 0x1a,
+	0x50, 0x6a, 0x60, 0x00,
+	0xff, 0x90, 0x4a, 0x02,
+	0x00, 0xa1, 0x9e, 0x17,
+	0xff, 0x6c, 0x59, 0x02,
+	0xff, 0x59, 0x27, 0x1c,
+	0xff, 0x4a, 0x90, 0x02,
+	0x00, 0x65, 0xa7, 0x17,
+	0x00, 0x6a, 0x4f, 0x17,
+	0xff, 0x65, 0x1f, 0x18,
+	0x51, 0x6a, 0x91, 0x00,
+	0xff, 0x58, 0xb3, 0x02,
+	0x00, 0x65, 0xb8, 0x17,
+	0x10, 0x6a, 0x60, 0x00,
+	0x00, 0x65, 0x03, 0x10,
+	0xff, 0x59, 0x90, 0x02,
+	0xff, 0x58, 0xb3, 0x02,
+	0x10, 0x6a, 0x60, 0x00,
+	0x00, 0x65, 0x03, 0x10,
+	0xff, 0x58, 0x6d, 0x02,
+	0xff, 0x4a, 0x90, 0x02,
+	0x10, 0x6a, 0x60, 0x00,
+	0xff, 0x48, 0xba, 0x02,
+	0xff, 0x90, 0x48, 0x02,
+	0xff, 0x48, 0x90, 0x02,
+	0x00, 0x65, 0x2f, 0x16,
+	0x00, 0x65, 0x03, 0x10,
+	0xf7, 0x1f, 0x65, 0x02,
+	0x08, 0xa1, 0x64, 0x02,
+	0x00, 0x65, 0x65, 0x00,
+	0xff, 0x65, 0x1f, 0x02,
+	0xf0, 0xa1, 0x64, 0x02,
+	0x0f, 0x05, 0x05, 0x02,
+	0x00, 0x05, 0x05, 0x00,
+	0x5a, 0x6a, 0x00, 0x01,
+	0xff, 0x6a, 0x34, 0x02,
+	0x20, 0x6a, 0x0b, 0x00,
+	0xf0, 0x19, 0x42, 0x02,
+	0x80, 0x41, 0x41, 0x00,
+	0x00, 0x65, 0x4c, 0x10,
+	0x12, 0x6a, 0x00, 0x00,
+	0x40, 0x6a, 0x0b, 0x00,
+	0xff, 0x48, 0x90, 0x02,
+	0xff, 0xba, 0x48, 0x02,
+	0xff, 0xa1, 0x42, 0x02,
+	0x07, 0xa1, 0x35, 0x02,
+	0x40, 0xa0, 0x64, 0x02,
+	0x00, 0x35, 0x35, 0x00,
+	0x80, 0x35, 0x35, 0x00,
+	0x01, 0x6a, 0x34, 0x00,
+	0x20, 0xa0, 0x4a, 0x1e,
+	0x23, 0xa0, 0x36, 0x02,
+	0xff, 0xb9, 0x37, 0x02,
+	0x02, 0x34, 0x34, 0x06,
+	0x80, 0xa0, 0x4c, 0x1e,
+	0xa1, 0x6a, 0x91, 0x00,
+	0x08, 0x6a, 0x0c, 0x00,
+	0x08, 0x11, 0x11, 0x00,
+	0x1a, 0x01, 0x01, 0x00,
+	0x31, 0x6a, 0x65, 0x00,
+	0x80, 0x42, 0x52, 0x1a,
+	0xff, 0x65, 0x65, 0x06,
+	0xff, 0x42, 0x6e, 0x02,
+	0xff, 0x6e, 0x64, 0x02,
+	0x00, 0x6c, 0x56, 0x1e,
+	0x20, 0x01, 0x01, 0x00,
+	0x4c, 0x42, 0x64, 0x0a,
+	0x08, 0x1f, 0x5a, 0x1e,
+	0x08, 0x42, 0x42, 0x00,
+	0x08, 0x64, 0x64, 0x00,
+	0x20, 0x64, 0x65, 0x06,
+	0xff, 0x6c, 0x04, 0x02,
+	0x01, 0x0c, 0x5c, 0x1e,
+	0x04, 0x0c, 0x5c, 0x1a,
+	0xe0, 0x03, 0x64, 0x02,
+	0xff, 0x64, 0x4c, 0x02,
+	0xff, 0x64, 0x03, 0x02,
+	0x00, 0x6a, 0x74, 0x1c,
+	0x40, 0x64, 0x79, 0x1c,
+	0x80, 0x64, 0xa5, 0x1c,
+	0xa0, 0x64, 0xb0, 0x1c,
+	0xc0, 0x64, 0xad, 0x1c,
+	0xe0, 0x64, 0xc3, 0x1c,
+	0x01, 0x6a, 0x91, 0x00,
+	0x00, 0x65, 0x5c, 0x10,
+	0xf7, 0x11, 0x11, 0x02,
+	0x00, 0x65, 0x6f, 0x16,
+	0xff, 0x06, 0x6a, 0x02,
+	0x09, 0x0c, 0x6c, 0x1e,
+	0x08, 0x0c, 0x03, 0x1a,
+	0x01, 0x6a, 0x91, 0x00,
+	0xff, 0x6a, 0x93, 0x02,
+	0xff, 0x6a, 0x04, 0x02,
+	0xdf, 0x01, 0x01, 0x02,
+	0x01, 0x6a, 0x4c, 0x00,
+	0x0f, 0x41, 0x41, 0x03,
+	0x7d, 0x6a, 0x3d, 0x00,
+	0x00, 0x65, 0x7a, 0x10,
+	0x08, 0x6a, 0x66, 0x00,
+	0xa9, 0x6a, 0x71, 0x17,
+	0x00, 0x65, 0x82, 0x10,
+	0x79, 0x6a, 0x3d, 0x00,
+	0x00, 0x65, 0x4c, 0x17,
+	0x10, 0x41, 0x76, 0x1a,
+	0x88, 0x6a, 0x66, 0x00,
+	0xac, 0x6a, 0x6d, 0x17,
+	0x00, 0x65, 0x6a, 0x17,
+	0xff, 0xa3, 0x43, 0x02,
+	0x44, 0x6a, 0x66, 0x00,
+	0xa4, 0x6a, 0x70, 0x17,
+	0xff, 0x43, 0x88, 0x1a,
+	0x80, 0x02, 0x02, 0x00,
+	0xff, 0x6a, 0x8c, 0x00,
+	0xff, 0x6a, 0x8d, 0x00,
+	0xff, 0x6a, 0x8e, 0x00,
+	0x00, 0x65, 0x6a, 0x17,
+	0x01, 0x43, 0x8a, 0x18,
+	0xbf, 0x3d, 0x3d, 0x02,
+	0x00, 0x3d, 0x41, 0x17,
+	0x80, 0x02, 0xa2, 0x1a,
+	0xff, 0x65, 0x9c, 0x1e,
+	0xff, 0x43, 0x43, 0x06,
+	0xff, 0x43, 0x9c, 0x1e,
+	0xff, 0x6a, 0x64, 0x02,
+	0x08, 0x44, 0x44, 0x06,
+	0x00, 0x45, 0x45, 0x08,
+	0x88, 0x6a, 0x66, 0x00,
+	0x44, 0x6a, 0x70, 0x17,
+	0x08, 0x6a, 0x8c, 0x00,
+	0xff, 0x6a, 0x8d, 0x02,
+	0xff, 0x6a, 0x8e, 0x02,
+	0x0d, 0x93, 0x93, 0x00,
+	0x00, 0x65, 0x9a, 0x17,
+	0x88, 0x6a, 0x92, 0x17,
+	0x00, 0x65, 0x6a, 0x17,
+	0x10, 0x0c, 0x82, 0x1e,
+	0xff, 0x08, 0xa9, 0x02,
+	0xff, 0x09, 0xaa, 0x02,
+	0xff, 0x0a, 0xab, 0x02,
+	0xff, 0x43, 0xa8, 0x02,
+	0x10, 0x41, 0x41, 0x00,
+	0x00, 0x65, 0x5c, 0x10,
+	0x7f, 0x02, 0x02, 0x02,
+	0xe1, 0x6a, 0x91, 0x00,
+	0x00, 0x65, 0x5c, 0x10,
+	0x00, 0x65, 0x4c, 0x17,
+	0x88, 0x6a, 0x66, 0x00,
+	0xb4, 0x6a, 0x6f, 0x17,
+	0xff, 0x6a, 0x8d, 0x02,
+	0xff, 0x6a, 0x8e, 0x02,
+	0x00, 0x65, 0x6a, 0x17,
+	0x3d, 0x6a, 0x41, 0x17,
+	0x00, 0x65, 0x5c, 0x10,
+	0x00, 0x65, 0x4c, 0x17,
+	0xff, 0x06, 0xa2, 0x02,
+	0x00, 0x65, 0x5c, 0x10,
+	0xff, 0x34, 0xb2, 0x1a,
+	0x08, 0x6a, 0x2f, 0x17,
+	0x35, 0x6a, 0x65, 0x00,
+	0xff, 0x34, 0x66, 0x02,
+	0x01, 0x0c, 0xb4, 0x1e,
+	0x04, 0x0c, 0xb4, 0x1a,
+	0xe0, 0x03, 0x4c, 0x02,
+	0xa0, 0x4c, 0xc0, 0x18,
+	0xff, 0x66, 0xbb, 0x1a,
+	0x10, 0x4c, 0x03, 0x00,
+	0x00, 0x65, 0xb2, 0x10,
+	0x01, 0x66, 0xbd, 0x18,
+	0x40, 0x6a, 0x0c, 0x00,
+	0xff, 0x66, 0x66, 0x06,
+	0xff, 0x6c, 0x06, 0x02,
+	0x00, 0x65, 0xb4, 0x10,
+	0x40, 0x6a, 0x0c, 0x00,
+	0xff, 0x6a, 0x34, 0x02,
+	0x00, 0x65, 0x5c, 0x10,
+	0x64, 0x6a, 0x3c, 0x17,
+	0xff, 0x64, 0x4b, 0x02,
+	0x80, 0x64, 0x0b, 0x1b,
+	0x04, 0x64, 0xfe, 0x1c,
+	0x02, 0x64, 0x01, 0x1d,
+	0x00, 0x6a, 0xd1, 0x1c,
+	0x03, 0x64, 0x09, 0x1d,
+	0x01, 0x64, 0xf2, 0x1c,
+	0x07, 0x64, 0x2d, 0x1d,
+	0x08, 0x64, 0xcf, 0x1c,
+	0x11, 0x6a, 0x91, 0x00,
+	0x07, 0x6a, 0x2f, 0x17,
+	0xff, 0x06, 0x6a, 0x02,
+	0x00, 0x65, 0x5c, 0x10,
+	0xff, 0xa8, 0xd3, 0x1a,
+	0xff, 0xa2, 0xda, 0x1e,
+	0x01, 0x6a, 0x3d, 0x00,
+	0x00, 0xb9, 0x74, 0x17,
+	0xff, 0xa2, 0xda, 0x1e,
+	0x71, 0x6a, 0x91, 0x00,
+	0x40, 0x59, 0xda, 0x18,
+	0xff, 0xb9, 0xb3, 0x02,
+	0x00, 0x65, 0xe4, 0x10,
+	0x20, 0xa0, 0xe0, 0x1a,
+	0xff, 0x90, 0x4a, 0x02,
+	0xff, 0xb3, 0x49, 0x02,
+	0x00, 0xa1, 0x9e, 0x17,
+	0xff, 0x49, 0x6d, 0x02,
+	0xff, 0x4a, 0x90, 0x02,
+	0xff, 0xb9, 0x9d, 0x02,
+	0x02, 0x6a, 0x91, 0x00,
+	0x08, 0xa0, 0xe4, 0x1e,
+	0x91, 0x6a, 0x91, 0x00,
+	0xff, 0xb3, 0xf0, 0x1c,
+	0xff, 0xb3, 0x64, 0x02,
+	0x00, 0xb9, 0xea, 0x1c,
+	0x00, 0x65, 0xa7, 0x17,
+	0xff, 0x64, 0x90, 0x02,
+	0x00, 0x65, 0xec, 0x10,
+	0x0d, 0x6a, 0x3d, 0x00,
+	0x00, 0xb3, 0x74, 0x17,
+	0xff, 0x48, 0xba, 0x02,
+	0xff, 0x90, 0x48, 0x02,
+	0x00, 0x65, 0x2f, 0x16,
+	0x00, 0x65, 0x69, 0x10,
+	0x00, 0x65, 0xa7, 0x17,
+	0x00, 0x65, 0x69, 0x10,
+	0x4d, 0x6a, 0x37, 0x17,
+	0xff, 0x4d, 0x64, 0x02,
+	0x00, 0x66, 0x37, 0x17,
+	0xff, 0x64, 0x64, 0x06,
+	0x52, 0x66, 0xf8, 0x18,
+	0xff, 0x66, 0x66, 0x06,
+	0xff, 0x64, 0xf4, 0x1a,
+	0x41, 0x6a, 0x91, 0x00,
+	0x20, 0x59, 0xcd, 0x1c,
+	0x80, 0x59, 0xcf, 0x18,
+	0x10, 0x4c, 0x03, 0x00,
+	0x00, 0x65, 0xcf, 0x10,
+	0x04, 0xa0, 0xa0, 0x00,
+	0x00, 0x65, 0xb8, 0x17,
+	0x00, 0x65, 0x69, 0x10,
+	0x10, 0x41, 0xcf, 0x1e,
+	0xff, 0x43, 0xa3, 0x02,
+	0xa4, 0x6a, 0x66, 0x00,
+	0x44, 0x6a, 0x70, 0x17,
+	0xac, 0x6a, 0x66, 0x00,
+	0x14, 0x6a, 0x70, 0x17,
+	0xa9, 0x6a, 0x71, 0x17,
+	0x00, 0x65, 0xcf, 0x10,
+	0xef, 0x41, 0x41, 0x02,
+	0x00, 0x65, 0xcf, 0x10,
+	0x78, 0x64, 0xcd, 0x1a,
+	0x07, 0x64, 0x64, 0x02,
+	0x00, 0x42, 0x42, 0x00,
+	0x00, 0x42, 0x9e, 0x17,
+	0xff, 0x6c, 0x59, 0x02,
+	0xff, 0x59, 0x25, 0x19,
+	0xff, 0x59, 0x15, 0x1d,
+	0xff, 0x59, 0x90, 0x02,
+	0x04, 0xa0, 0x2a, 0x1f,
+	0x00, 0x65, 0x27, 0x11,
+	0xff, 0x06, 0x6a, 0x02,
+	0x01, 0x0c, 0x16, 0x1f,
+	0x04, 0x0c, 0x16, 0x1b,
+	0xe0, 0x03, 0x4c, 0x02,
+	0xe0, 0x4c, 0x2a, 0x19,
+	0x20, 0x12, 0x2a, 0x19,
+	0x20, 0x41, 0x41, 0x00,
+	0x59, 0x6a, 0x37, 0x17,
+	0xff, 0x3f, 0x64, 0x02,
+	0x00, 0x59, 0x65, 0x06,
+	0x00, 0x65, 0x2a, 0x13,
+	0xff, 0x59, 0x90, 0x02,
+	0xff, 0x42, 0x64, 0x02,
+	0x00, 0xa1, 0x2a, 0x19,
+	0x20, 0xa0, 0x2a, 0x1f,
+	0x04, 0xa0, 0x2a, 0x1f,
+	0x00, 0x6a, 0x4f, 0x17,
+	0xff, 0x65, 0x2a, 0x1d,
+	0xfb, 0xa0, 0xa0, 0x02,
+	0x40, 0x41, 0x41, 0x00,
+	0x00, 0x65, 0xcf, 0x10,
+	0x31, 0x6a, 0x91, 0x00,
+	0x0c, 0x6a, 0x2f, 0x17,
+	0x00, 0x65, 0xcf, 0x10,
+	0x61, 0x6a, 0x91, 0x00,
+	0x00, 0x65, 0xcf, 0x10,
+	0x50, 0x6a, 0x60, 0x00,
+	0xff, 0x34, 0x33, 0x1f,
+	0x10, 0x6a, 0x60, 0x00,
+	0xc1, 0x6a, 0x91, 0x00,
+	0x10, 0x4c, 0x03, 0x00,
+	0x01, 0x6a, 0x34, 0x00,
+	0xff, 0x65, 0x35, 0x02,
+	0x10, 0x6a, 0x60, 0x01,
+	0xff, 0x06, 0x6a, 0x02,
+	0x01, 0x0c, 0x38, 0x1f,
+	0x04, 0x0c, 0x38, 0x1b,
+	0xe0, 0x03, 0x4c, 0x02,
+	0xe0, 0x4c, 0x3f, 0x19,
+	0xff, 0x65, 0x66, 0x02,
+	0xff, 0x12, 0x6d, 0x03,
+	0xff, 0x06, 0x6a, 0x03,
+	0xd1, 0x6a, 0x91, 0x00,
+	0x00, 0x65, 0x5c, 0x10,
+	0xff, 0x65, 0x93, 0x02,
+	0x01, 0x0b, 0x49, 0x1b,
+	0x10, 0x0c, 0x42, 0x1f,
+	0x04, 0x0b, 0x46, 0x1b,
+	0xff, 0x6a, 0x65, 0x02,
+	0x04, 0x93, 0x48, 0x1b,
+	0x01, 0x94, 0x47, 0x1f,
+	0x10, 0x94, 0x48, 0x1b,
+	0xc7, 0x93, 0x93, 0x02,
+	0x38, 0x93, 0x4a, 0x1b,
+	0xff, 0x6a, 0x6a, 0x03,
+	0x80, 0x41, 0x4b, 0x1f,
+	0x40, 0x41, 0x4b, 0x1b,
+	0x21, 0x6a, 0x91, 0x01,
+	0xff, 0x65, 0x90, 0x02,
+	0xff, 0x59, 0x64, 0x02,
+	0x00, 0xb9, 0x53, 0x19,
+	0x04, 0xa0, 0x5d, 0x1b,
+	0x01, 0x65, 0x65, 0x06,
+	0xff, 0x3e, 0x64, 0x02,
+	0x00, 0x65, 0x4f, 0x19,
+	0x00, 0x6a, 0xaa, 0x17,
+	0x0d, 0x6a, 0x3d, 0x00,
+	0x00, 0x59, 0x74, 0x17,
+	0xff, 0xa8, 0x5b, 0x1f,
+	0x10, 0xa0, 0xa0, 0x00,
+	0x08, 0xa0, 0x4b, 0x1f,
+	0xff, 0x6a, 0x65, 0x01,
+	0x08, 0xa0, 0x5c, 0x1b,
+	0xff, 0xba, 0x63, 0x1d,
+	0xff, 0xbb, 0x49, 0x02,
+	0xff, 0xba, 0x90, 0x02,
+	0xff, 0x49, 0xbb, 0x02,
+	0xff, 0x65, 0x90, 0x02,
+	0xff, 0xbb, 0x68, 0x1d,
+	0xff, 0xba, 0x49, 0x02,
+	0xff, 0xbb, 0x90, 0x02,
+	0xff, 0x49, 0xba, 0x02,
+	0xff, 0x65, 0x90, 0x03,
+	0xff, 0xba, 0x52, 0x03,
+	0xff, 0x6a, 0x6a, 0x03,
+	0xff, 0x8c, 0x08, 0x02,
+	0xff, 0x8d, 0x09, 0x02,
+	0xff, 0x8e, 0x0a, 0x03,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x6c, 0x6d, 0x02,
+	0xff, 0x6c, 0x6d, 0x03,
+	0x3d, 0x65, 0x66, 0x0a,
+	0x55, 0x65, 0x64, 0x0a,
+	0x00, 0x54, 0x88, 0x06,
+	0xff, 0x66, 0x64, 0x02,
+	0x00, 0x55, 0x89, 0x08,
+	0xff, 0x6a, 0x64, 0x02,
+	0x00, 0x56, 0x8a, 0x08,
+	0x00, 0x57, 0x8b, 0x08,
+	0x1c, 0x6a, 0x8c, 0x00,
+	0xff, 0x6a, 0x8d, 0x02,
+	0xff, 0x6a, 0x8e, 0x02,
+	0xff, 0x3d, 0x93, 0x02,
+	0x04, 0x3d, 0x8c, 0x1b,
+	0xa0, 0x6a, 0x65, 0x00,
+	0x1c, 0x65, 0x64, 0x06,
+	0xff, 0x6c, 0x99, 0x02,
+	0xff, 0x6c, 0x99, 0x02,
+	0xff, 0x6c, 0x99, 0x02,
+	0xff, 0x6c, 0x99, 0x02,
+	0xff, 0x6c, 0x99, 0x02,
+	0xff, 0x6c, 0x99, 0x02,
+	0xff, 0x6c, 0x99, 0x02,
+	0x00, 0x65, 0x83, 0x19,
+	0x0a, 0x93, 0x93, 0x00,
+	0x00, 0x65, 0x9a, 0x17,
+	0x04, 0x3d, 0x4b, 0x1f,
+	0xa0, 0x6a, 0x92, 0x17,
+	0x00, 0x65, 0x93, 0x17,
+	0x00, 0x65, 0x93, 0x17,
+	0x00, 0x65, 0x93, 0x11,
+	0xff, 0x65, 0x66, 0x02,
+	0xff, 0x99, 0x6d, 0x02,
+	0xff, 0x99, 0x6d, 0x02,
+	0xff, 0x99, 0x6d, 0x02,
+	0xff, 0x99, 0x6d, 0x02,
+	0xff, 0x99, 0x6d, 0x02,
+	0xff, 0x99, 0x6d, 0x02,
+	0xff, 0x99, 0x6d, 0x03,
+	0x08, 0x94, 0x9a, 0x1f,
+	0xf7, 0x93, 0x93, 0x02,
+	0x08, 0x93, 0x9c, 0x1b,
+	0xff, 0x6a, 0x6a, 0x03,
+	0xff, 0x65, 0x66, 0x02,
+	0x4c, 0x66, 0x66, 0x0a,
+	0x03, 0x66, 0x66, 0x02,
+	0xbc, 0x66, 0x66, 0x06,
+	0x6a, 0x65, 0x64, 0x0a,
+	0x08, 0x65, 0xa5, 0x1f,
+	0x02, 0x64, 0x64, 0x06,
+	0xff, 0x64, 0x90, 0x02,
+	0xff, 0x66, 0x65, 0x03,
+	0xff, 0x53, 0xba, 0x02,
+	0xff, 0x6a, 0xb9, 0x00,
+	0xff, 0x90, 0x53, 0x03,
+	0xff, 0x53, 0xb6, 0x19,
+	0xff, 0x52, 0xad, 0x19,
+	0xff, 0x6a, 0x65, 0x01,
+	0xff, 0x52, 0x90, 0x02,
+	0x10, 0xa0, 0xb1, 0x1f,
+	0xef, 0xa0, 0xa0, 0x02,
+	0x00, 0x65, 0xb3, 0x11,
+	0xff, 0xa8, 0xb3, 0x1b,
+	0xff, 0xb3, 0xb5, 0x1d,
+	0x01, 0x6a, 0x3d, 0x00,
+	0x00, 0xb9, 0x74, 0x17,
+	0x00, 0x90, 0x5e, 0x11,
+	0xff, 0x53, 0x90, 0x02,
+	0xff, 0xba, 0x53, 0x03,
+	0xff, 0x6a, 0xbb, 0x00,
+	0xff, 0x52, 0xba, 0x02,
+	0xff, 0x90, 0x52, 0x02,
+	0xff, 0xba, 0x4b, 0x1d,
+	0xff, 0xba, 0x90, 0x02,
+	0xff, 0x52, 0xbb, 0x02,
+	0xff, 0x52, 0x90, 0x03,
+};
+#define	ULTRA           	0x8
+#define	SCB_PAGING      	0x4
+#define	TWIN_CHANNEL    	0x2
+struct patch {
+	int	options;
+	int	negative;
+	int	begin;
+	int	end;
+} patches[] = {
+	{ 0x00000002, 0, 0x006, 0x00b },
+	{ 0x00000004, 0, 0x00e, 0x010 },
+	{ 0x00000004, 1, 0x011, 0x012 },
+	{ 0x00000004, 0, 0x01a, 0x023 },
+	{ 0x00000004, 1, 0x023, 0x027 },
+	{ 0x00000002, 0, 0x02f, 0x033 },
+	{ 0x00000008, 0, 0x04f, 0x056 },
+	{ 0x00000004, 1, 0x0e5, 0x0ea },
+	{ 0x00000004, 0, 0x0ff, 0x100 },
+	{ 0x00000004, 0, 0x110, 0x111 },
+	{ 0x00000004, 1, 0x111, 0x115 },
+	{ 0x00000004, 1, 0x120, 0x125 },
+	{ 0x00000004, 0, 0x125, 0x127 },
+	{ 0x00000004, 0, 0x14f, 0x169 },
+	{ 0x00000004, 1, 0x169, 0x16a },
+	{ 0x00000004, 0, 0x1aa, 0x1bf },
+	{ 0x00000000, 0, 0x000, 0x000 }
+};
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/hosts.c linux/drivers/scsi/hosts.c
--- v2.0.30/linux/drivers/scsi/hosts.c	Wed Oct 30 02:45:53 1996
+++ linux/drivers/scsi/hosts.c	Mon Aug  4 11:45:55 1997
@@ -165,6 +165,10 @@
 #include "esp.h"
 #endif
 
+#ifdef CONFIG_BLK_DEV_IDESCSI
+#include "ide-scsi.h"
+#endif
+
 #ifdef CONFIG_SCSI_DEBUG
 #include "scsi_debug.h"
 #endif
@@ -302,6 +306,9 @@
 #endif
 #ifdef CONFIG_SCSI_SUNESP
     SCSI_SPARC_ESP,
+#endif
+#ifdef CONFIG_BLK_DEV_IDESCSI
+    IDESCSI,
 #endif
 #ifdef CONFIG_SCSI_DEBUG
     SCSI_DEBUG,
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/ide-scsi.c linux/drivers/scsi/ide-scsi.c
--- v2.0.30/linux/drivers/scsi/ide-scsi.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/ide-scsi.c	Mon Aug  4 11:45:55 1997
@@ -0,0 +1,601 @@
+/*
+ * linux/drivers/scsi/ide-scsi.c	Version 0.3 - ALPHA	Jul  24, 1997
+ *
+ * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+/*
+ * Emulation of a SCSI host adapter for IDE ATAPI devices.
+ *
+ * With this driver, one can use the Linux SCSI drivers instead of the
+ * native IDE ATAPI drivers.
+ *
+ * Ver 0.1   Dec  3 96   Initial version.
+ * Ver 0.2   Jan 26 97   Fixed bug in cleanup_module() and added emulation
+ *                        of MODE_SENSE_6/MODE_SELECT_6 for cdroms. Thanks
+ *                        to Janos Farkas for pointing this out.
+ *                       Avoid using bitfields in structures for m68k.
+ *                       Added Scather/Gather and DMA support.
+ * Ver 0.3   Jul 24 97   Add support for ATAPI PD/CD drives.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/malloc.h>
+
+#include <asm/io.h>
+#include <asm/bitops.h>
+
+#include "../block/ide.h"
+
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "ide-scsi.h"
+
+#define IDESCSI_DEBUG_LOG		0
+
+typedef struct idescsi_pc_s {
+	u8 c[12];				/* Actual packet bytes */
+	int request_transfer;			/* Bytes to transfer */
+	int actually_transferred;		/* Bytes actually transferred */
+	int buffer_size;			/* Size of our data buffer */
+	struct request *rq;			/* The corresponding request */
+	byte *buffer;				/* Data buffer */
+	byte *current_position;			/* Pointer into the above buffer */
+	struct scatterlist *sg;			/* Scather gather table */
+	int b_count;				/* Bytes transferred from current entry */
+	Scsi_Cmnd *scsi_cmd;			/* SCSI command */
+	void (*done)(Scsi_Cmnd *);		/* Scsi completion routine */
+	unsigned int flags;			/* Status/Action flags */
+} idescsi_pc_t;
+
+/*
+ *	Packet command status bits.
+ */
+#define PC_DMA_IN_PROGRESS		0	/* 1 while DMA in progress */
+#define PC_WRITING			1	/* Data direction */
+
+typedef struct {
+	ide_drive_t *drive;
+	idescsi_pc_t *pc;			/* Current packet command */
+	unsigned int flags;			/* Status/Action flags */
+	int media;
+} idescsi_scsi_t;
+
+/*
+ *	Per ATAPI device status bits.
+ */
+#define IDESCSI_DRQ_INTERRUPT		0	/* DRQ interrupt device */
+
+/*
+ *	ide-scsi requests.
+ */
+#define IDESCSI_PC_RQ			90
+
+/*
+ *	Bits of the interrupt reason register.
+ */
+#define IDESCSI_IREASON_COD	0x1		/* Information transferred is command */
+#define IDESCSI_IREASON_IO	0x2		/* The device requests us to read */
+
+#define IDE_MIN(a,b)      ((a)<(b) ? (a):(b))
+#define IDE_MAX(a,b)      ((a)>(b) ? (a):(b))
+
+static void idescsi_discard_data (ide_drive_t *drive, unsigned int bcount)
+{
+	while (bcount--)
+		IN_BYTE (IDE_DATA_REG);
+}
+
+static void idescsi_output_zeros (ide_drive_t *drive, unsigned int bcount)
+{
+	while (bcount--)
+		OUT_BYTE (0, IDE_DATA_REG);
+}
+
+/*
+ *	PIO data transfer routines using the scather gather table.
+ */
+static void idescsi_input_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount)
+{
+	int count;
+
+	while (bcount) {
+		if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) {
+			printk (KERN_ERR "ide-scsi: scather gather table too small, discarding data\n");
+			idescsi_discard_data (drive, bcount);
+			return;
+		}
+		count = IDE_MIN (pc->sg->length - pc->b_count, bcount);
+		atapi_input_bytes (drive, pc->sg->address + pc->b_count, count);
+		bcount -= count; pc->b_count += count;
+		if (pc->b_count == pc->sg->length) {
+			pc->sg++;
+			pc->b_count = 0;
+		}
+	}
+}
+
+static void idescsi_output_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount)
+{
+	int count;
+
+	while (bcount) {
+		if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) {
+			printk (KERN_ERR "ide-scsi: scather gather table too small, padding with zeros\n");
+			idescsi_output_zeros (drive, bcount);
+			return;
+		}
+		count = IDE_MIN (pc->sg->length - pc->b_count, bcount);
+		atapi_output_bytes (drive, pc->sg->address + pc->b_count, count);
+		bcount -= count; pc->b_count += count;
+		if (pc->b_count == pc->sg->length) {
+			pc->sg++;
+			pc->b_count = 0;
+		}
+	}
+}
+
+/*
+ *	Most of the SCSI commands are supported directly by ATAPI devices.
+ *	idescsi_transform_pc handles the few exceptions.
+ */
+static inline void idescsi_transform_pc1 (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+	idescsi_scsi_t *scsi = drive->scsi;
+	u8 *c = pc->c, *buf = pc->buffer, *sc = pc->scsi_cmd->cmnd;
+	int i;
+
+	if (scsi->media == TYPE_ROM) {
+		if (c[0] == READ_6 || c[0] == WRITE_6) {
+			c[8] = c[4];		c[5] = c[3];		c[4] = c[2];
+			c[3] = c[1] & 0x1f;	c[2] = 0;		c[1] &= 0xe0;
+			c[0] += (READ_10 - READ_6);
+		}
+		if (c[0] == MODE_SENSE || (c[0] == MODE_SELECT && buf[3] == 8)) {
+			pc->request_transfer -= 4;
+			memset (c, 0, 12);
+			c[0] = sc[0] | 0x40;	c[2] = sc[2];		c[8] = sc[4] - 4;
+			if (c[0] == MODE_SENSE_10) return;
+			for (i = 0; i <= 7; i++) buf[i] = 0;
+			for (i = 8; i < pc->buffer_size - 4; i++) buf[i] = buf[i + 4];
+		}
+	}
+}
+
+static inline void idescsi_transform_pc2 (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+	idescsi_scsi_t *scsi = drive->scsi;
+	u8 *buf = pc->buffer;
+	int i;
+
+	if (scsi->media == TYPE_ROM) {
+		if (pc->c[0] == MODE_SENSE_10 && pc->scsi_cmd->cmnd[0] == MODE_SENSE) {
+			buf[0] = buf[1];	buf[1] = buf[2];
+			buf[2] = 0;		buf[3] = 8;
+			for (i = pc->buffer_size - 1; i >= 12; i--) buf[i] = buf[i - 4];
+			for (i = 11; i >= 4; i--) buf[i] = 0;
+		}
+		if (pc->c[0] == INQUIRY)
+			buf[2] |= 2;
+	}
+}
+
+static inline void idescsi_free_bh (struct buffer_head *bh)
+{
+	struct buffer_head *bhp;
+
+	while (bh) {
+		bhp = bh;
+		bh = bh->b_reqnext;
+		kfree (bhp);
+	}
+}
+
+void idescsi_end_request (byte uptodate, ide_hwgroup_t *hwgroup)
+{
+	ide_drive_t *drive = hwgroup->drive;
+	idescsi_scsi_t *scsi = drive->scsi;
+	struct request *rq = hwgroup->rq;
+	idescsi_pc_t *pc = (idescsi_pc_t *) rq->buffer;
+
+	if (rq->cmd != IDESCSI_PC_RQ) {
+		ide_end_request (uptodate, hwgroup);
+		return;
+	}
+	ide_end_drive_cmd (drive, 0, 0);
+	if (rq->errors >= ERROR_MAX) {
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: I/O error for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+#endif /* IDESCSI_DEBUG_LOG */
+		pc->scsi_cmd->result = DID_ERROR << 16;
+	} else if (rq->errors) {
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: check condition for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+#endif /* IDESCSI_DEBUG_LOG */
+		pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16);
+	} else {
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: success for %lu\n", drive->name, pc->scsi_cmd->serial_number);
+#endif /* IDESCSI_DEBUG_LOG */
+		pc->scsi_cmd->result = DID_OK << 16;
+		idescsi_transform_pc2 (drive, pc);
+	}
+	pc->done(pc->scsi_cmd);
+	idescsi_free_bh (rq->bh);
+	kfree(pc); kfree(rq);
+	scsi->pc = NULL;
+}
+
+/*
+ *	Our interrupt handler.
+ */
+static void idescsi_pc_intr (ide_drive_t *drive)
+{
+	idescsi_scsi_t *scsi = drive->scsi;
+	byte status, ireason;
+	int bcount;
+	idescsi_pc_t *pc=scsi->pc;
+	struct request *rq = pc->rq;
+	unsigned int temp;
+
+#if IDESCSI_DEBUG_LOG
+	printk (KERN_INFO "ide-scsi: Reached idescsi_pc_intr interrupt handler\n");
+#endif /* IDESCSI_DEBUG_LOG */
+
+	if (clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) {
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: DMA complete\n", drive->name);
+#endif /* IDESCSI_DEBUG_LOG */
+		pc->actually_transferred=pc->request_transfer;
+		(void) (HWIF(drive)->dmaproc(ide_dma_abort, drive));
+	}
+
+	status = GET_STAT();						/* Clear the interrupt */
+
+	if ((status & DRQ_STAT) == 0) {					/* No more interrupts */
+#if IDESCSI_DEBUG_LOG
+		printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred);
+#endif /* IDESCSI_DEBUG_LOG */
+		sti();
+		if (status & ERR_STAT)
+			rq->errors++;
+		idescsi_end_request (1, HWGROUP(drive));
+		return;
+	}
+	bcount = IN_BYTE (IDE_BCOUNTH_REG) << 8 | IN_BYTE (IDE_BCOUNTL_REG);
+	ireason = IN_BYTE (IDE_IREASON_REG);
+
+	if (ireason & IDESCSI_IREASON_COD) {
+		printk (KERN_ERR "ide-scsi: CoD != 0 in idescsi_pc_intr\n");
+		ide_do_reset (drive);
+		return;
+	}
+	if (ireason & IDESCSI_IREASON_IO) {
+		temp = pc->actually_transferred + bcount;
+		if ( temp > pc->request_transfer) {
+			if (temp > pc->buffer_size) {
+				printk (KERN_ERR "ide-scsi: The scsi wants to send us more data than expected - discarding data\n");
+				idescsi_discard_data (drive,bcount);
+				ide_set_handler (drive,&idescsi_pc_intr,WAIT_CMD);
+				return;
+			}
+#if IDESCSI_DEBUG_LOG
+			printk (KERN_NOTICE "ide-scsi: The scsi wants to send us more data than expected - allowing transfer\n");
+#endif /* IDESCSI_DEBUG_LOG */
+		}
+	}
+	if (ireason & IDESCSI_IREASON_IO) {
+		if (pc->sg)
+			idescsi_input_buffers (drive, pc, bcount);
+		else
+			atapi_input_bytes (drive,pc->current_position,bcount);
+	} else {
+		if (pc->sg)
+			idescsi_output_buffers (drive, pc, bcount);
+		else
+			atapi_output_bytes (drive,pc->current_position,bcount);
+	}
+	pc->actually_transferred+=bcount;				/* Update the current position */
+	pc->current_position+=bcount;
+
+	ide_set_handler (drive,&idescsi_pc_intr,WAIT_CMD);		/* And set the interrupt handler again */
+}
+
+static void idescsi_transfer_pc (ide_drive_t *drive)
+{
+	idescsi_scsi_t *scsi = drive->scsi;
+	byte ireason;
+
+	if (ide_wait_stat (drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) {
+		printk (KERN_ERR "ide-scsi: Strange, packet command initiated yet DRQ isn't asserted\n");
+		return;
+	}
+	ireason = IN_BYTE (IDE_IREASON_REG);
+	if ((ireason & (IDESCSI_IREASON_IO | IDESCSI_IREASON_COD)) != IDESCSI_IREASON_COD) {
+		printk (KERN_ERR "ide-scsi: (IO,CoD) != (0,1) while issuing a packet command\n");
+		ide_do_reset (drive);
+		return;
+	}
+	ide_set_handler (drive, &idescsi_pc_intr, WAIT_CMD);	/* Set the interrupt routine */
+	atapi_output_bytes (drive, scsi->pc->c, 12);		/* Send the actual packet */
+}
+
+/*
+ *	Issue a packet command
+ */
+static void idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+	idescsi_scsi_t *scsi = drive->scsi;
+	int bcount;
+	struct request *rq = pc->rq;
+	int dma_ok = 0;
+
+	scsi->pc=pc;							/* Set the current packet command */
+	pc->actually_transferred=0;					/* We haven't transferred any data yet */
+	pc->current_position=pc->buffer;
+	bcount = IDE_MIN (pc->request_transfer, 63 * 1024);		/* Request to transfer the entire buffer at once */
+
+	if (drive->using_dma && rq->bh)
+		dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive);
+
+	OUT_BYTE (drive->ctl,IDE_CONTROL_REG);
+	OUT_BYTE (dma_ok,IDE_FEATURE_REG);
+	OUT_BYTE (bcount >> 8,IDE_BCOUNTH_REG);
+	OUT_BYTE (bcount & 0xff,IDE_BCOUNTL_REG);
+	OUT_BYTE (drive->select.all,IDE_SELECT_REG);
+
+	if (dma_ok) {
+		set_bit (PC_DMA_IN_PROGRESS, &pc->flags);
+		(void) (HWIF(drive)->dmaproc(ide_dma_begin, drive));
+	}
+	if (test_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags)) {
+		ide_set_handler (drive, &idescsi_transfer_pc, WAIT_CMD);
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);		/* Issue the packet command */
+	} else {
+		OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG);
+		idescsi_transfer_pc (drive);
+	}
+}
+
+/*
+ *	idescsi_do_request is our request handling function.
+ */
+void idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+#if IDESCSI_DEBUG_LOG
+	printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors);
+	printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
+#endif /* IDESCSI_DEBUG_LOG */
+
+	if (rq->cmd == IDESCSI_PC_RQ) {
+		idescsi_issue_pc (drive, (idescsi_pc_t *) rq->buffer);
+		return;
+	}
+	printk (KERN_ERR "ide-scsi: %s: unsupported command in request queue (%x)\n", drive->name, rq->cmd);
+	idescsi_end_request (0,HWGROUP (drive));
+}
+
+int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+void idescsi_ide_release (struct inode *inode, struct file *filp, ide_drive_t *drive)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+static ide_drive_t *idescsi_drives[MAX_HWIFS * MAX_DRIVES];
+static int idescsi_initialized = 0;
+
+int idescsi_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+	return -EINVAL;
+}
+
+static struct proc_dir_entry idescsi_proc_dir = {PROC_SCSI_IDESCSI, 8, "ide-scsi", S_IFDIR | S_IRUGO | S_IXUGO, 2};
+
+/*
+ *	idescsi_setup will register the driver for each scsi.
+ */
+void idescsi_setup (ide_drive_t *drive)
+{
+	idescsi_scsi_t *scsi;
+	int i, id;
+
+	if (!idescsi_initialized) {
+		idescsi_initialized = 1;
+		for (i = 0; i < MAX_HWIFS * MAX_DRIVES; i++)
+			idescsi_drives[i] = NULL;
+	}
+	if ((scsi = (idescsi_scsi_t *) kmalloc (sizeof (idescsi_scsi_t), GFP_KERNEL)) == NULL) {
+		printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name);
+		return;
+	}
+	for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++);
+	idescsi_drives[id] = drive;
+	drive->scsi = scsi;
+	drive->ready_stat = 0;
+	memset (scsi, 0, sizeof (idescsi_scsi_t));
+	scsi->drive = drive;
+	scsi->media = (drive->id->config >> 8) & 0x1f;
+	if (drive->id && (drive->id->config & 0x0060) == 0x20)
+		set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags);
+}
+
+int idescsi_detect (Scsi_Host_Template *host_template)
+{
+	struct Scsi_Host *host;
+	int id;
+
+	host_template->proc_dir = &idescsi_proc_dir;
+	host = scsi_register(host_template, 0);
+	for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++);
+	host->max_id = id;
+	host->can_queue = host->cmd_per_lun * id;
+	return 1;
+}
+
+int idescsi_release (struct Scsi_Host *host)
+{
+	return 0;
+}
+
+const char *idescsi_info (struct Scsi_Host *host)
+{
+	return "SCSI host adapter emulation for IDE ATAPI devices";
+}
+
+static inline struct buffer_head *idescsi_kmalloc_bh (int count)
+{
+	struct buffer_head *bh, *bhp, *first_bh;
+
+	if ((first_bh = bhp = bh = kmalloc (sizeof(struct buffer_head), GFP_ATOMIC)) == NULL)
+		goto abort;
+	memset (bh, 0, sizeof (struct buffer_head));
+	bh->b_reqnext = NULL;
+	while (--count) {
+		if ((bh = kmalloc (sizeof(struct buffer_head), GFP_ATOMIC)) == NULL)
+			goto abort;
+		memset (bh, 0, sizeof (struct buffer_head));
+		bhp->b_reqnext = bh;
+		bhp = bh;
+		bh->b_reqnext = NULL;
+	}
+	return first_bh;
+abort:
+	idescsi_free_bh (first_bh);
+	return NULL;
+}
+
+static inline int idescsi_set_direction (idescsi_pc_t *pc)
+{
+	switch (pc->c[0]) {
+		case READ_6: case READ_10: case READ_12:
+			clear_bit (PC_WRITING, &pc->flags);
+			return 0;
+		case WRITE_6: case WRITE_10: case WRITE_12:
+			set_bit (PC_WRITING, &pc->flags);
+			return 0;
+		default:
+			return 1;
+	}
+}
+
+static inline struct buffer_head *idescsi_dma_bh (ide_drive_t *drive, idescsi_pc_t *pc)
+{
+	struct buffer_head *bh = NULL, *first_bh = NULL;
+	int segments = pc->scsi_cmd->use_sg;
+	struct scatterlist *sg = pc->scsi_cmd->request_buffer;
+
+	if (!drive->using_dma || pc->request_transfer % 1024)
+		return NULL;
+	if (idescsi_set_direction(pc))
+		return NULL;
+	if (segments) {
+		if ((first_bh = bh = idescsi_kmalloc_bh (segments)) == NULL)
+			return NULL;
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: building DMA table, %d segments, %dkB total\n", drive->name, segments, pc->request_transfer >> 10);
+#endif /* IDESCSI_DEBUG_LOG */
+		while (segments--) {
+			bh->b_data = sg->address;
+			bh->b_size = sg->length;
+			bh = bh->b_reqnext;
+			sg++;
+		}
+	} else {
+		if ((first_bh = bh = idescsi_kmalloc_bh (1)) == NULL)
+			return NULL;
+#if IDESCSI_DEBUG_LOG
+		printk ("ide-scsi: %s: building DMA table for a single buffer (%dkB)\n", drive->name, pc->request_transfer >> 10);
+#endif /* IDESCSI_DEBUG_LOG */
+		bh->b_data = pc->scsi_cmd->request_buffer;
+		bh->b_size = pc->request_transfer;
+	}
+	return first_bh;
+}
+
+int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+	ide_drive_t *drive = idescsi_drives[cmd->target];
+	struct request *rq = NULL;
+	idescsi_pc_t *pc = NULL;
+
+#if IDESCSI_DEBUG_LOG
+	printk ("idescsi_queue called, serial = %lu, cmd[0] = %x, id = %d\n", cmd->serial_number, cmd->cmnd[0], cmd->target);
+#endif	/* IDESCSI_DEBUG_LOG */
+
+	if (!drive) {
+		printk (KERN_ERR "ide-scsi: drive id %d not present\n", cmd->target);
+		goto abort;
+	}
+	pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC);
+	rq = kmalloc (sizeof (struct request), GFP_ATOMIC);
+	if (rq == NULL || pc == NULL) {
+		printk (KERN_ERR "ide-scsi: %s: out of memory\n", drive->name);
+		goto abort;
+	}
+
+	memset (pc->c, 0, 12);
+	pc->flags = 0;
+	pc->rq = rq;
+	memcpy (pc->c, cmd->cmnd, cmd->cmd_len);
+	if (cmd->use_sg) {
+		pc->buffer = NULL;
+		pc->sg = cmd->request_buffer;
+	} else {
+		pc->buffer = cmd->request_buffer;
+		pc->sg = NULL;
+	}
+	pc->b_count = 0;
+	pc->request_transfer = pc->buffer_size = cmd->request_bufflen;
+	pc->scsi_cmd = cmd;
+	pc->done = done;
+	idescsi_transform_pc1 (drive, pc);
+
+	ide_init_drive_cmd (rq);
+	rq->buffer = (char *) pc;
+	rq->bh = idescsi_dma_bh (drive, pc);
+	rq->cmd = IDESCSI_PC_RQ;
+	(void) ide_do_drive_cmd (drive, rq, ide_end);
+	return 0;
+abort:
+	if (pc) kfree (pc);
+	if (rq) kfree (rq);
+	cmd->result = DID_ERROR << 16;
+	done(cmd);
+	return 0;
+}
+
+int idescsi_abort (Scsi_Cmnd *cmd)
+{
+	return SCSI_ABORT_SNOOZE;
+}
+
+int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags)
+{
+	return SCSI_RESET_PUNT;
+}
+
+int idescsi_bios (Disk *disk, kdev_t dev, int *parm)
+{
+	ide_drive_t *drive = idescsi_drives[disk->device->id];
+
+	if (drive->cyl && drive->head && drive->sect) {
+		parm[0] = drive->head;
+		parm[1] = drive->sect;
+		parm[2] = drive->cyl;
+	}
+	return 0;
+}
diff -u --recursive --new-file v2.0.30/linux/drivers/scsi/ide-scsi.h linux/drivers/scsi/ide-scsi.h
--- v2.0.30/linux/drivers/scsi/ide-scsi.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/ide-scsi.h	Mon Aug  4 11:45:55 1997
@@ -0,0 +1,42 @@
+/*
+ * linux/drivers/scsi/ide-scsi.h
+ *
+ * Copyright (C) 1996, 1997 Gadi Oxman <gadio@netvision.net.il>
+ */
+
+#ifndef IDESCSI_H
+#define IDESCSI_H
+
+extern int idescsi_detect (Scsi_Host_Template *host_template);
+extern int idescsi_release (struct Scsi_Host *host);
+extern const char *idescsi_info (struct Scsi_Host *host);
+extern int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+extern int idescsi_abort (Scsi_Cmnd *cmd);
+extern int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags);
+extern int idescsi_bios (Disk *disk, kdev_t dev, int *parm);
+
+#define IDESCSI								\
+{	NULL,			/* next		*/			\
+	NULL,			/* module	*/			\
+	NULL,			/* proc_dir	*/			\
+	NULL,			/* proc_info	*/			\
+	"idescsi",		/* name		*/			\
+	idescsi_detect,		/* detect	*/			\
+	idescsi_release,	/* release	*/			\
+	idescsi_info,		/* info		*/			\
+	NULL,			/* command	*/			\
+	idescsi_queue,		/* queuecommand */			\
+	idescsi_abort,		/* abort	*/			\
+	idescsi_reset,		/* reset	*/			\
+	NULL,			/* slave_attach	*/			\
+	idescsi_bios,		/* bios_param	*/			\
+	10,			/* can_queue	*/			\
+	-1,			/* this_id	*/			\
+	256,			/* sg_tablesize	*/			\
+	5,			/* cmd_per_lun	*/			\
+	0,			/* present	*/			\
+	0,			/* isa_dma	*/			\
+	DISABLE_CLUSTERING	/* clustering	*/			\
+}
+
+#endif /* IDESCSI_H */
diff -u --recursive --new-file v2.0.30/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.0.30/linux/drivers/sound/sb_common.c	Wed Sep 11 07:57:14 1996
+++ linux/drivers/sound/sb_common.c	Sun Aug  3 15:25:08 1997
@@ -583,6 +583,20 @@
  * Detect the device
  */
 
+  cli();			/* Some ESS1688 cards need this */
+  inb (devc->base + 0x9);
+  inb (devc->base + 0x9);
+  inb (devc->base + 0x9);
+  inb (devc->base + 0xb);
+  inb (devc->base + 0x9);
+  inb (devc->base + 0xb);
+  inb (devc->base + 0x9);
+  inb (devc->base + 0x9);
+  inb (devc->base + 0xb);
+  inb (devc->base + 0x9);
+  inb (devc->base);
+  sti();
+
   if (sb_dsp_reset (devc))
     dsp_get_vers (devc);
   else
diff -u --recursive --new-file v2.0.30/linux/fs/Config.in linux/fs/Config.in
--- v2.0.30/linux/fs/Config.in	Sun Dec  1 05:58:05 1996
+++ linux/fs/Config.in	Sun Aug  3 13:59:07 1997
@@ -5,7 +5,6 @@
 comment 'Filesystems'
 
 bool	 'Quota support' CONFIG_QUOTA
-bool	 'Mandatory lock support' CONFIG_LOCK_MANDATORY
 tristate 'Minix fs support' CONFIG_MINIX_FS
 tristate 'Extended fs support' CONFIG_EXT_FS
 tristate 'Second extended fs support' CONFIG_EXT2_FS
diff -u --recursive --new-file v2.0.30/linux/fs/buffer.c linux/fs/buffer.c
--- v2.0.30/linux/fs/buffer.c	Tue Apr  8 08:47:46 1997
+++ linux/fs/buffer.c	Mon Aug  4 15:25:11 1997
@@ -524,7 +524,9 @@
 			if (bh->b_size == size)
 				 continue;
 			
+			bhnext->b_count++;
 			wait_on_buffer(bh);
+			bhnext->b_count--;
 			if (bh->b_dev == dev && bh->b_size != size) {
 				clear_bit(BH_Dirty, &bh->b_state);
 				clear_bit(BH_Uptodate, &bh->b_state);
@@ -546,7 +548,7 @@
 			 
 	if (mem_map[MAP_NR((unsigned long) bh->b_data)].count != 1 ||
 	    buffer_dirty(bh)) {
-		refile_buffer(bh);
+		/* WSH: don't attempt to refile here! */
 		return 0;
 	}
 
@@ -569,6 +571,8 @@
 			   of other sizes, this is necessary now that we
 			   no longer have the lav code. */
 			try_to_free_buffer(bh,&bh,1);
+			if (!bh)
+				break;
 			continue;
 		}
 
@@ -612,14 +616,15 @@
 	}
 
 repeat:
+	if(needed <= 0)
+		return;
+
 	/* OK, we cannot grow the buffer cache, now try to get some
 	   from the lru list */
 
 	/* First set the candidate pointers to usable buffers.  This
 	   should be quick nearly all of the time. */
 
-	if(needed <= 0) return;
-
 	for(i=0; i<BUF_DIRTY; i++){
 		buffers[i] = nr_buffers_type[i];
 		candidate[i] = find_candidate(lru_list[i], &buffers[i], size);
@@ -652,12 +657,9 @@
 			if (candidate[i] && !can_reclaim(candidate[i],size))
 				candidate[i] = find_candidate(candidate[i],&buffers[i], size);
 		}
-		if (needed >= 0)
-			goto repeat;
+		goto repeat;
 	}
 	
-	if(needed <= 0) return;
-	
 	/* Too bad, that was not enough. Try a little harder to grow some. */
 	
 	if (nr_free_pages > min_free_pages + 5) {
@@ -670,6 +672,8 @@
 	/* and repeat until we find something good */
 	if (!grow_buffers(GFP_ATOMIC, size))
 		wakeup_bdflush(1);
+
+	/* decrease needed even if there is no success */
 	needed -= PAGE_SIZE;
 	goto repeat;
 }
@@ -919,6 +923,34 @@
 	wake_up(&buffer_wait);
 }
 
+/* 
+ * We can't put completed temporary IO buffer_heads directly onto the
+ * unused_list when they become unlocked, since the device driver
+ * end_request routines still expect access to the buffer_head's
+ * fields after the final unlock.  So, the device driver puts them on
+ * the reuse_list instead once IO completes, and we recover these to
+ * the unused_list here.
+ *
+ * The reuse_list receives buffers from interrupt routines, so we need
+ * to be IRQ-safe here (but note that interrupts only _add_ to the
+ * reuse_list, never take away. So we don't need to worry about the
+ * reuse_list magically emptying).
+ */
+static inline void recover_reusable_buffer_heads(void)
+{
+	if (reuse_list) {
+		struct buffer_head *head;
+
+		head = xchg(&reuse_list, NULL);
+	
+		do {
+			struct buffer_head *bh = head;
+			head = head->b_next_free;
+			put_unused_buffer_head(bh);
+		} while (head);
+	}
+}
+
 static void get_more_buffer_heads(void)
 {
 	struct buffer_head * bh;
@@ -946,38 +978,14 @@
 		 */
 		run_task_queue(&tq_disk);
 		sleep_on(&buffer_wait);
+		/*
+		 * After we wake up, check for released async buffer heads.
+		 */
+		recover_reusable_buffer_heads();
 	}
 
 }
 
-/* 
- * We can't put completed temporary IO buffer_heads directly onto the
- * unused_list when they become unlocked, since the device driver
- * end_request routines still expect access to the buffer_head's
- * fields after the final unlock.  So, the device driver puts them on
- * the reuse_list instead once IO completes, and we recover these to
- * the unused_list here.
- *
- * The reuse_list receives buffers from interrupt routines, so we need
- * to be IRQ-safe here (but note that interrupts only _add_ to the
- * reuse_list, never take away. So we don't need to worry about the
- * reuse_list magically emptying).
- */
-static inline void recover_reusable_buffer_heads(void)
-{
-	if (reuse_list) {
-		struct buffer_head *head;
-
-		head = xchg(&reuse_list, NULL);
-	
-		do {
-			struct buffer_head *bh = head;
-			head = head->b_next_free;
-			put_unused_buffer_head(bh);
-		} while (head);
-	}
-}
-
 static struct buffer_head * get_unused_buffer_head(void)
 {
 	struct buffer_head * bh;
@@ -1158,6 +1166,8 @@
 		free_async_buffers(bh);
 		restore_flags(flags);
 		after_unlock_page(page);
+		if (waitqueue_active(&buffer_wait))
+			wake_up(&buffer_wait);
 	}
 	++current->maj_flt;
 	return 0;
@@ -1520,6 +1530,7 @@
 				 ndirty++;
 				 if(bh->b_flushtime > jiffies) continue;
 				 nwritten++;
+				 next->b_count++;
 				 bh->b_count++;
 				 bh->b_flushtime = 0;
 #ifdef DEBUG
@@ -1527,8 +1538,10 @@
 #endif
 				 ll_rw_block(WRITE, 1, &bh);
 				 bh->b_count--;
+				 next->b_count--;
 			 }
 	}
+	run_task_queue(&tq_disk);
 #ifdef DEBUG
 	if (ncount) printk("sync_old_buffers: %d dirty buffers not on dirty list\n", ncount);
 	printk("Wrote %d/%d buffers\n", nwritten, ndirty);
@@ -1660,6 +1673,7 @@
 					     currently dirty buffers are not shared, so it does not matter */
 					  if (refilled && major == LOOP_MAJOR)
 						   continue;
+					  next->b_count++;
 					  bh->b_count++;
 					  ndirty++;
 					  bh->b_flushtime = 0;
@@ -1675,6 +1689,7 @@
 					  if(nlist != BUF_DIRTY) ncount++;
 #endif
 					  bh->b_count--;
+					  next->b_count--;
 				  }
 		 }
 #ifdef DEBUG
diff -u --recursive --new-file v2.0.30/linux/fs/dquot.c linux/fs/dquot.c
--- v2.0.30/linux/fs/dquot.c	Sun May 12 21:36:19 1996
+++ linux/fs/dquot.c	Sun Aug  3 15:31:34 1997
@@ -19,6 +19,8 @@
  * 
  * Fixes:   Dmitry Gorodchanin <begemot@bgm.rosprint.net>, 11 Feb 96
  *	    removed race conditions in dqput(), dqget() and iput(). 
+ *          Nick Kralevich <nickkral@cal.alumni.berkeley.edu>, 21 Jul 97
+ *          Fixed a condition where user and group quotas could get mixed up.
  *
  * (C) Copyright 1994, 1995 Marco van Wieringen 
  *
@@ -549,12 +551,14 @@
 repeat:
 	dquot = *(hash(dev, id, type));
 	while (dquot) {
-		if (dquot->dq_dev != dev || dquot->dq_id != id) {
+		if (dquot->dq_dev != dev || dquot->dq_id != id ||
+		    dquot->dq_type != type) {
 			dquot = dquot->dq_hash_next;
 			continue;
 		}
 		wait_on_dquot(dquot);
-		if (dquot->dq_dev != dev || dquot->dq_id != id)
+		if (dquot->dq_dev != dev || dquot->dq_id != id ||
+		    dquot->dq_type != type)
 			goto repeat;
 		if (!dquot->dq_count)
 			nr_free_dquots--;
diff -u --recursive --new-file v2.0.30/linux/fs/ext2/namei.c linux/fs/ext2/namei.c
--- v2.0.30/linux/fs/ext2/namei.c	Sat Nov 30 02:21:20 1996
+++ linux/fs/ext2/namei.c	Mon Aug  4 12:30:53 1997
@@ -1015,10 +1015,15 @@
 			goto end_rename;
 	}
 	retval = -EPERM;
-	if (new_inode && (new_dir->i_mode & S_ISVTX) &&
-	    current->fsuid != new_inode->i_uid &&
-	    current->fsuid != new_dir->i_uid && !fsuser())
-		goto end_rename;
+	if (new_inode) {
+		if ((new_dir->i_mode & S_ISVTX) &&
+		    current->fsuid != new_inode->i_uid &&
+		    current->fsuid != new_dir->i_uid && !fsuser())
+			goto end_rename;
+		if (IS_APPEND(new_inode) || IS_IMMUTABLE(new_inode))
+			goto end_rename;
+	}
+	
 	if (S_ISDIR(old_inode->i_mode)) {
 		retval = -ENOTDIR;
 		if (new_inode && !S_ISDIR(new_inode->i_mode))
diff -u --recursive --new-file v2.0.30/linux/fs/locks.c linux/fs/locks.c
--- v2.0.30/linux/fs/locks.c	Fri Sep 20 07:00:35 1996
+++ linux/fs/locks.c	Sun Aug  3 13:59:07 1997
@@ -28,22 +28,22 @@
  *  dynamically with kmalloc()/kfree().
  *  Andy Walker (andy@lysaker.kvaerner.no), February 21, 1995
  *
- *  Implemented two lock personalities - F_FLOCK and F_POSIX.
+ *  Implemented two lock personalities - FL_FLOCK and FL_POSIX.
  *
- *  F_POSIX locks are created with calls to fcntl() and lockf() through the
+ *  FL_POSIX locks are created with calls to fcntl() and lockf() through the
  *  fcntl() system call. They have the semantics described above.
  *
- *  F_FLOCK locks are created with calls to flock(), through the flock()
+ *  FL_FLOCK locks are created with calls to flock(), through the flock()
  *  system call, which is new. Old C libraries implement flock() via fcntl()
  *  and will continue to use the old, broken implementation.
  *
- *  F_FLOCK locks follow the 4.4 BSD flock() semantics. They are associated
+ *  FL_FLOCK locks follow the 4.4 BSD flock() semantics. They are associated
  *  with a file pointer (filp). As a result they can be shared by a parent
  *  process and its children after a fork(). They are removed when the last
  *  file descriptor referring to the file pointer is closed (unless explicitly
  *  unlocked). 
  *
- *  F_FLOCK locks never deadlock, an existing lock is always removed before
+ *  FL_FLOCK locks never deadlock, an existing lock is always removed before
  *  upgrading from shared to exclusive (or vice versa). When this happens
  *  any processes blocked by the current lock are woken up and allowed to
  *  run before the new lock is applied.
@@ -76,22 +76,32 @@
  *  flock() and fcntl().
  *  Andy Walker (andy@lysaker.kvaerner.no), April 29, 1996.
  *
- *  Allow only one type of locking scheme (F_POSIX or F_FLOCK) to be in use
- *  for a given file at a time. Changed the CONFIG_MANDATORY_OPTION scheme to
+ *  Allow only one type of locking scheme (FL_POSIX or FL_FLOCK) to be in use
+ *  for a given file at a time. Changed the CONFIG_LOCK_MANDATORY scheme to
  *  guarantee sensible behaviour in the case where file system modules might
  *  be compiled with different options than the kernel itself.
  *  Andy Walker (andy@lysaker.kvaerner.no), May 15, 1996.
  *
- *  Added a couple of missing wake_up() calls.
+ *  Added a couple of missing wake_up() calls. Thanks to Thomas Meckel
+ *  (Thomas.Meckel@mni.fh-giessen.de) for spotting this.
  *  Andy Walker (andy@lysaker.kvaerner.no), May 15, 1996.
  *
- *  TODO: Do not honour mandatory locks on remote file systems. This matches
- *        the SVR4 semantics and neatly sidesteps a pile of awkward issues that
- *        would otherwise have to be addressed.
+ *  Changed FL_POSIX locks to use the block list in the same way as FL_FLOCK
+ *  locks. Changed process synchronisation to avoid dereferencing locks that
+ *  have already been freed.
+ *  Andy Walker (andy@lysaker.kvaerner.no), Sep 21, 1996.
+ *
+ *  Made the block list a circular list to minimise searching in the list.
+ *  Andy Walker (andy@lysaker.kvaerner.no), Sep 25, 1996.
+ *
+ *  Made mandatory locking a mount option. Default is not to allow mandatory
+ *  locking.
+ *  Andy Walker (andy@lysaker.kvaerner.no), Oct 04, 1996.
+ *
+ *  Fixed /proc/locks interface so that we can't overrun the buffer we are handed.
+ *  Andy Walker (andy@lysaker.kvaerner.no), May 12, 1997.
  */
 
-#include <linux/config.h>
-
 #include <linux/malloc.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -118,76 +128,115 @@
 			   unsigned int wait);
 static int posix_locks_deadlock(struct task_struct *my_task,
 				struct task_struct *blocked_task);
-static int locks_overlap(struct file_lock *fl1, struct file_lock *fl2);
-static void posix_remove_locks(struct file_lock **before, struct task_struct *task);
-static void flock_remove_locks(struct file_lock **before, struct file *filp);
 
 static struct file_lock *locks_alloc_lock(struct file_lock *fl);
 static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl);
-static void locks_delete_lock(struct file_lock **fl, unsigned int wait);
-static char *lock_get_status(struct file_lock *fl, char *p, int id, char *pfx);
+static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait);
+static char *lock_get_status(struct file_lock *fl, int id, char *pfx);
+
+static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter);
+static void locks_delete_block(struct file_lock *blocker, struct file_lock *waiter);
+static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait);
 
 static struct file_lock *file_lock_table = NULL;
-static struct file_lock *unused_file_locks = NULL;
 
-/*
- * Free lock not inserted in any queue
- *
- * Careful! We can't just "kfree()" it: there may be other processes
- * that have yet to remove themselves from the wait queues. Thus the
- * internal memory management.
+/* Free lock not inserted in any queue.
  */
 static inline void locks_free_lock(struct file_lock *fl)
 {
-	struct file_lock *next = unused_file_locks;
-	unused_file_locks = fl;
-	fl->fl_next = next;
-}
-
-/* Add lock fl to the blocked list pointed to by block.
- * We search to the end of the existing list and insert the the new
- * struct. This ensures processes will be woken up in the order they
- * blocked.
- * NOTE: nowhere does the documentation insist that processes be woken
- * up in this order, but it seems like the reasonable thing to do.
- * If the blocked list gets long then this search could get expensive,
- * in which case we could consider waking the processes up in reverse
- * order, or making the blocked list a doubly linked circular list.
- * 
- * This functions are called only from one place (flock_lock_file)
- * so they are inlined now. -- Dmitry Gorodchanin 02/09/96.
- */
-
-static inline void locks_insert_block(struct file_lock *bfl, 
-				      struct file_lock *fl)
+	if (waitqueue_active(&fl->fl_wait))
+		panic("Aarggh: attempting to free lock with active wait queue - shoot Andy");
+
+	if (fl->fl_nextblock != NULL || fl->fl_prevblock != NULL)
+		panic("Aarggh: attempting to free lock with active block list - shoot Andy");
+		
+	kfree(fl);
+	return;
+}
+
+/* Check if two locks overlap each other.
+ */
+static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
 {
-	while (bfl->fl_block != NULL) {
-		bfl = bfl->fl_block;
-	}
+	return ((fl1->fl_end >= fl2->fl_start) &&
+		(fl2->fl_end >= fl1->fl_start));
+}
 
-	bfl->fl_block = fl;
-	fl->fl_block = NULL;
+/* Insert waiter into blocker's block list.
+ * We use a circular list so that processes can be easily woken up in
+ * the order they blocked. The documentation doesn't require this but
+ * it seems seems like the reasonable thing to do.
+ */
+static void locks_insert_block(struct file_lock *blocker, 
+			       struct file_lock *waiter)
+{
+	struct file_lock *prevblock;
+
+	if (blocker->fl_prevblock == NULL)
+		/* No previous waiters - list is empty */
+		prevblock = blocker;
+	else
+		/* Previous waiters exist - add to end of list */
+		prevblock = blocker->fl_prevblock;
+
+	prevblock->fl_nextblock = waiter;
+	blocker->fl_prevblock = waiter;
+	waiter->fl_nextblock = blocker;
+	waiter->fl_prevblock = prevblock;
 	
 	return;
 }
 
-static inline void locks_delete_block(struct file_lock *bfl,
-				      struct file_lock *fl)
+/* Remove waiter from blocker's block list.
+ * When blocker ends up pointing to itself then the list is empty.
+ */
+static void locks_delete_block(struct file_lock *blocker,
+			       struct file_lock *waiter)
 {
-	struct file_lock *tfl;
+	struct file_lock *nextblock;
+	struct file_lock *prevblock;
 	
-	while ((tfl = bfl->fl_block) != NULL) {
-		if (tfl == fl) {
-			bfl->fl_block = fl->fl_block;
-			fl->fl_block = NULL;
-			return;
-		}
-		bfl = tfl;
+	nextblock = waiter->fl_nextblock;
+	prevblock = waiter->fl_prevblock;
+
+	if (nextblock == NULL)
+		return;
+	
+	nextblock->fl_prevblock = prevblock;
+	prevblock->fl_nextblock = nextblock;
+
+	waiter->fl_prevblock = waiter->fl_nextblock = NULL;
+	if (blocker->fl_nextblock == blocker)
+		/* No more locks on blocker's blocked list */
+		blocker->fl_prevblock = blocker->fl_nextblock = NULL;
+	return;
+}
+
+/* Wake up processes blocked waiting for blocker.
+ * If told to wait then schedule the processes until the block list
+ * is empty, otherwise empty the block list ourselves.
+ */
+static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait)
+{
+	struct file_lock *waiter;
+
+	while ((waiter = blocker->fl_nextblock) != NULL) {
+		wake_up(&waiter->fl_wait);
+		if (wait)
+			/* Let the blocked process remove waiter from the
+			 * block list when it gets scheduled.
+			 */
+			schedule();
+		else
+			/* Remove waiter from the block list, because by the
+			 * time it wakes up blocker won't exist any more.
+			 */
+			locks_delete_block(blocker, waiter);
 	}
 	return;
 }
 
-/* flock() system call entry point. Apply a FLOCK style lock to
+/* flock() system call entry point. Apply a FL_FLOCK style lock to
  * an open file descriptor.
  */
 asmlinkage int sys_flock(unsigned int fd, unsigned int cmd)
@@ -203,8 +252,8 @@
 	
 	if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3))
 		return (-EBADF);
-	
-	return (flock_lock_file(filp, &file_lock, cmd & LOCK_UN ? 0 : cmd & LOCK_NB ? 0 : 1));
+
+	return (flock_lock_file(filp, &file_lock, (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1));
 }
 
 /* Report the first existing lock that would conflict with l.
@@ -224,26 +273,24 @@
 		return (error);
 
 	memcpy_fromfs(&flock, l, sizeof(flock));
-	if ((flock.l_type == F_UNLCK) || (flock.l_type == F_EXLCK) ||
-	    (flock.l_type == F_SHLCK))
+	if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
 		return (-EINVAL);
 
 	if (!filp->f_inode || !posix_make_lock(filp, &file_lock, &flock))
 		return (-EINVAL);
 
-	if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & F_POSIX)) { 
-		while (fl != NULL) {
-			if (posix_locks_conflict(&file_lock, fl)) {
-				flock.l_pid = fl->fl_owner->pid;
-				flock.l_start = fl->fl_start;
-				flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
-					fl->fl_end - fl->fl_start + 1;
-				flock.l_whence = 0;
-				flock.l_type = fl->fl_type;
-				memcpy_tofs(l, &flock, sizeof(flock));
-				return (0);
-			}
-			fl = fl->fl_next;
+	for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
+		if (!(fl->fl_flags & FL_POSIX))
+			continue;
+		if (posix_locks_conflict(&file_lock, fl)) {
+			flock.l_pid = fl->fl_owner->pid;
+			flock.l_start = fl->fl_start;
+			flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
+				fl->fl_end - fl->fl_start + 1;
+			flock.l_whence = 0;
+			flock.l_type = fl->fl_type;
+			memcpy_tofs(l, &flock, sizeof(flock));
+			return (0);
 		}
 	}
 
@@ -254,8 +301,6 @@
 
 /* Apply the lock described by l to an open file descriptor.
  * This implements both the F_SETLK and F_SETLKW commands of fcntl().
- * It also emulates flock() in a pretty broken way for older C
- * libraries.
  */
 int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
 {
@@ -265,8 +310,7 @@
 	struct flock flock;
 	struct inode *inode;
 
-	/*
-	 * Get arguments and validate them ...
+	/* Get arguments and validate them ...
 	 */
 
 	if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd]))
@@ -279,11 +323,12 @@
 	if (!(inode = filp->f_inode))
 		return (-EINVAL);
 	
-#ifdef CONFIG_LOCK_MANDATORY
 	/* Don't allow mandatory locks on files that may be memory mapped
 	 * and shared.
 	 */
-	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID && inode->i_mmap) {
+	if (IS_MANDLOCK(inode) &&
+	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
+	    inode->i_mmap) {
 		struct vm_area_struct *vma = inode->i_mmap;
 		do {
 			if (vma->vm_flags & VM_MAYSHARE)
@@ -291,42 +336,29 @@
 			vma = vma->vm_next_share;
 		} while (vma != inode->i_mmap);
 	}
-#endif
 
 	memcpy_fromfs(&flock, l, sizeof(flock));
 	if (!posix_make_lock(filp, &file_lock, &flock))
 		return (-EINVAL);
 	
 	switch (flock.l_type) {
-	case F_RDLCK :
+	case F_RDLCK:
 		if (!(filp->f_mode & 1))
 			return (-EBADF);
 		break;
-	case F_WRLCK :
+	case F_WRLCK:
 		if (!(filp->f_mode & 2))
 			return (-EBADF);
 		break;
-	case F_SHLCK :
-	case F_EXLCK :
-#if 1
-/* warn a bit for now, but don't overdo it */
-{
-	static int count = 0;
-	if (!count) {
-		count=1;
+	case F_UNLCK:
+		break;
+	case F_SHLCK:
+	case F_EXLCK:
 		printk(KERN_WARNING
-		       "fcntl_setlk() called by process %d (%s) with broken flock() emulation\n",
+		       "fcntl_setlk(): process %d (%s) requested broken flock() emulation\n",
 		       current->pid, current->comm);
-	}
-}
-#endif
-		if (!(filp->f_mode & 3))
-			return (-EBADF);
-		break;
-	case F_UNLCK :
-		break;
 	default:
-		return -EINVAL;
+		return (-EINVAL);
 	}
 	
 	return (posix_lock_file(filp, &file_lock, cmd == F_SETLKW));
@@ -337,41 +369,18 @@
 void locks_remove_locks(struct task_struct *task, struct file *filp)
 {
 	struct file_lock *fl;
+	struct file_lock **before;
 
 	/* For POSIX locks we free all locks on this file for the given task.
 	 * For FLOCK we only free locks on this *open* file if it is the last
 	 * close on that file.
 	 */
-	if ((fl = filp->f_inode->i_flock) != NULL) {
-		if (fl->fl_flags & F_POSIX)
-			posix_remove_locks(&filp->f_inode->i_flock, task);
-		else
-			flock_remove_locks(&filp->f_inode->i_flock, filp);
-	}
-
-	return;
-}
-
-static void posix_remove_locks(struct file_lock **before, struct task_struct *task)
-{
-	struct file_lock *fl;
-
-	while ((fl = *before) != NULL) {
-		if (fl->fl_owner == task)
-			locks_delete_lock(before, 0);
-		else
-			before = &fl->fl_next;
-	}
-
-	return;
-}
-
-static void flock_remove_locks(struct file_lock **before, struct file *filp)
-{
-	struct file_lock *fl;
+	before = &filp->f_inode->i_flock;
 
 	while ((fl = *before) != NULL) {
-		if ((fl->fl_file == filp) && (filp->f_count == 1))
+		if (((fl->fl_flags & FL_POSIX) && (fl->fl_owner == task)) ||
+		    ((fl->fl_flags & FL_FLOCK) && (fl->fl_file == filp) &&
+		     (filp->f_count == 1)))
 			locks_delete_lock(before, 0);
 		else
 			before = &fl->fl_next;
@@ -382,46 +391,40 @@
 
 int locks_verify_locked(struct inode *inode)
 {
-#ifdef CONFIG_LOCK_MANDATORY
 	/* Candidates for mandatory locking have the setgid bit set
 	 * but no group execute bit -  an otherwise meaningless combination.
 	 */
-	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+	if (IS_MANDLOCK(inode) &&
+	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
 		return (locks_mandatory_locked(inode));
-#endif
 	return (0);
 }
 
 int locks_verify_area(int read_write, struct inode *inode, struct file *filp,
 		      unsigned int offset, unsigned int count)
 {
-#ifdef CONFIG_LOCK_MANDATORY	 
 	/* Candidates for mandatory locking have the setgid bit set
 	 * but no group execute bit -  an otherwise meaningless combination.
 	 */
-	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+	if (IS_MANDLOCK(inode) &&
+	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
 		return (locks_mandatory_area(read_write, inode, filp, offset,
 					     count));
-#endif					     
 	return (0);
 }
 
 int locks_mandatory_locked(struct inode *inode)
 {
-#ifdef CONFIG_LOCK_MANDATORY	
 	struct file_lock *fl;
 
 	/* Search the lock list for this inode for any POSIX locks.
 	 */
-	if ((fl = inode->i_flock) && (fl->fl_flags & F_FLOCK))
-		return (0);
-	
-	while (fl != NULL) {
+	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+		if (!(fl->fl_flags & FL_POSIX))
+			continue;
 		if (fl->fl_owner != current)
 			return (-EAGAIN);
-		fl = fl->fl_next;
 	}
-#endif
 	return (0);
 }
 
@@ -429,51 +432,50 @@
 			 struct file *filp, unsigned int offset,
 			 unsigned int count)
 {
-#ifdef CONFIG_LOCK_MANDATORY	
 	struct file_lock *fl;
+	struct file_lock tfl;
+
+	memset(&tfl, 0, sizeof(tfl));
+
+	tfl.fl_file = filp;
+	tfl.fl_flags = FL_POSIX | FL_ACCESS;
+	tfl.fl_owner = current;
+	tfl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
+	tfl.fl_start = offset;
+	tfl.fl_end = offset + count - 1;
 
 repeat:
-	/* Check that there are locks, and that they're not F_FLOCK locks.
-	 */
-	if ((fl = inode->i_flock) && (fl->fl_flags & F_FLOCK))
-		return (0);
-	
-	/*
-	 * Search the lock list for this inode for locks that conflict with
+	/* Search the lock list for this inode for locks that conflict with
 	 * the proposed read/write.
 	 */
-	while (fl != NULL) {
-		if (fl->fl_owner == current ||
-		    fl->fl_end < offset || fl->fl_start >= offset + count)
-			goto next_lock;
-
-		/*
-		 * Block for writes against a "read" lock,
+	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
+		if (!(fl->fl_flags & FL_POSIX))
+			continue;
+		/* Block for writes against a "read" lock,
 		 * and both reads and writes against a "write" lock.
 		 */
-		if ((read_write == FLOCK_VERIFY_WRITE) ||
-		    (fl->fl_type == F_WRLCK)) {
+		if (posix_locks_conflict(fl, &tfl)) {
 			if (filp && (filp->f_flags & O_NONBLOCK))
 				return (-EAGAIN);
 			if (current->signal & ~current->blocked)
 				return (-ERESTARTSYS);
 			if (posix_locks_deadlock(current, fl->fl_owner))
 				return (-EDEADLK);
-			interruptible_sleep_on(&fl->fl_wait);
+
+			locks_insert_block(fl, &tfl);
+			interruptible_sleep_on(&tfl.fl_wait);
+			locks_delete_block(fl, &tfl);
+
 			if (current->signal & ~current->blocked)
 				return (-ERESTARTSYS);
-			/*
-			 * If we've been sleeping someone might have
+			/* If we've been sleeping someone might have
 			 * changed the permissions behind our back.
 			 */
 			if ((inode->i_mode & (S_ISGID | S_IXGRP)) != S_ISGID)
 				break;
 			goto repeat;
 		}
-	next_lock:
-		fl = fl->fl_next;
 	}
-#endif
 	return (0);
 }
 
@@ -485,37 +487,31 @@
 {
 	off_t start;
 
-	fl->fl_flags = F_POSIX;
+	memset(fl, 0, sizeof(*fl));
+	
+	fl->fl_flags = FL_POSIX;
 
 	switch (l->l_type) {
-	case F_RDLCK :
-	case F_WRLCK :
-	case F_UNLCK :
+	case F_RDLCK:
+	case F_WRLCK:
+	case F_UNLCK:
 		fl->fl_type = l->l_type;
 		break;
-	case F_SHLCK :
-		fl->fl_type = F_RDLCK;
-		fl->fl_flags |= F_BROKEN;
-		break;
-	case F_EXLCK :
-		fl->fl_type = F_WRLCK;
-		fl->fl_flags |= F_BROKEN;
-		break;
-	default :
+	default:
 		return (0);
 	}
 
 	switch (l->l_whence) {
-	case 0 : /*SEEK_SET*/
+	case 0: /*SEEK_SET*/
 		start = 0;
 		break;
-	case 1 : /*SEEK_CUR*/
+	case 1: /*SEEK_CUR*/
 		start = filp->f_pos;
 		break;
-	case 2 : /*SEEK_END*/
+	case 2: /*SEEK_END*/
 		start = filp->f_inode->i_size;
 		break;
-	default :
+	default:
 		return (0);
 	}
 
@@ -527,8 +523,7 @@
 	
 	fl->fl_file = filp;
 	fl->fl_owner = current;
-	fl->fl_wait = NULL;		/* just for cleanliness */
-	
+
 	return (1);
 }
 
@@ -538,29 +533,30 @@
 static int flock_make_lock(struct file *filp, struct file_lock *fl,
 			   unsigned int cmd)
 {
+	memset(fl, 0, sizeof(*fl));
+
 	if (!filp->f_inode)	/* just in case */
 		return (0);
 
 	switch (cmd & ~LOCK_NB) {
-	case LOCK_SH :
+	case LOCK_SH:
 		fl->fl_type = F_RDLCK;
 		break;
-	case LOCK_EX :
+	case LOCK_EX:
 		fl->fl_type = F_WRLCK;
 		break;
-	case LOCK_UN :
+	case LOCK_UN:
 		fl->fl_type = F_UNLCK;
 		break;
-	default :
+	default:
 		return (0);
 	}
 
-	fl->fl_flags = F_FLOCK;
+	fl->fl_flags = FL_FLOCK;
 	fl->fl_start = 0;
 	fl->fl_end = OFFSET_MAX;
 	fl->fl_file = filp;
 	fl->fl_owner = NULL;
-	fl->fl_wait = NULL;		/* just for cleanliness */
 	
 	return (1);
 }
@@ -573,7 +569,8 @@
 	/* POSIX locks owned by the same process do not conflict with
 	 * each other.
 	 */
-	if (caller_fl->fl_owner == sys_fl->fl_owner)
+	if (!(sys_fl->fl_flags & FL_POSIX) ||
+	    (caller_fl->fl_owner == sys_fl->fl_owner))
 		return (0);
 
 	return (locks_conflict(caller_fl, sys_fl));
@@ -587,7 +584,8 @@
 	/* FLOCK locks referring to the same filp do not conflict with
 	 * each other.
 	 */
-	if (caller_fl->fl_file == sys_fl->fl_file)
+	if (!(sys_fl->fl_flags & FL_FLOCK) ||
+	    (caller_fl->fl_file == sys_fl->fl_file))
 		return (0);
 
 	return (locks_conflict(caller_fl, sys_fl));
@@ -602,10 +600,10 @@
 		return (0);
 
 	switch (caller_fl->fl_type) {
-	case F_RDLCK :
+	case F_RDLCK:
 		return (sys_fl->fl_type == F_WRLCK);
 		
-	case F_WRLCK :
+	case F_WRLCK:
 		return (1);
 
 	default:
@@ -616,14 +614,6 @@
 	return (0);	/* This should never happen */
 }
 
-/* Check if two locks overlap each other.
- */
-static int locks_overlap(struct file_lock *fl1, struct file_lock *fl2)
-{
-	return ((fl1->fl_end >= fl2->fl_start) &&
-		(fl2->fl_end >= fl1->fl_start));
-}
-
 /* This function tests for deadlock condition before putting a process to
  * sleep. The detection scheme is no longer recursive. Recursive was neat,
  * but dangerous - we risked stack corruption if the lock data was bad, or
@@ -637,34 +627,31 @@
 static int posix_locks_deadlock(struct task_struct *my_task,
 				struct task_struct *blocked_task)
 {
-	struct wait_queue *dlock_wait;
 	struct file_lock *fl;
+	struct file_lock *bfl;
 
 next_task:
 	if (my_task == blocked_task)
 		return (1);
 	for (fl = file_lock_table; fl != NULL; fl = fl->fl_nextlink) {
-		struct wait_queue *head;
-		if (fl->fl_owner == NULL || fl->fl_wait == NULL)
+		if (fl->fl_owner == NULL || fl->fl_nextblock == NULL)
 			continue;
-		head = WAIT_QUEUE_HEAD(&fl->fl_wait);
-		dlock_wait = fl->fl_wait;
-		while (dlock_wait != head) {
-			if (dlock_wait->task == blocked_task) {
+		for (bfl = fl->fl_nextblock; bfl != fl; bfl = bfl->fl_nextblock) {
+			if (bfl->fl_owner == blocked_task) {
 				if (fl->fl_owner == my_task) {
 					return (1);
 				}
 				blocked_task = fl->fl_owner;
 				goto next_task;
 			}
-			dlock_wait = dlock_wait->next;
 		}
 	}
 	return (0);
 }
 
-/* Try to create a FLOCK lock on filp. We always insert new locks at
- * the head of the list.
+/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks at
+ * the head of the list, but that's secret knowledge known only to the next
+ * two functions.
  */
 static int flock_lock_file(struct file *filp, struct file_lock *caller,
 			   unsigned int wait)
@@ -675,11 +662,7 @@
 	int change = 0;
 
 	before = &filp->f_inode->i_flock;
-
-	if ((fl = *before) && (fl->fl_flags & F_POSIX))
-		return (-EBUSY);
-
-	while ((fl = *before) != NULL) {
+	while (((fl = *before) != NULL) && (fl->fl_flags & FL_FLOCK)) {
 		if (caller->fl_file == fl->fl_file) {
 			if (caller->fl_type == fl->fl_type)
 				return (0);
@@ -698,51 +681,42 @@
 	if ((new_fl = locks_alloc_lock(caller)) == NULL)
 		return (-ENOLCK);
 repeat:
-	if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & F_POSIX)) {
-		locks_free_lock(new_fl);
-		return (-EBUSY);
-	}
-
-	while (fl != NULL) {
-		if (flock_locks_conflict(new_fl, fl)) {
-			if (!wait) {
-				locks_free_lock(new_fl);
-				return (-EAGAIN);
-			}
-			if (current->signal & ~current->blocked) {
-				/* Note: new_fl is not in any queue at this
-				 * point, so we must use locks_free_lock()
-				 * instead of locks_delete_lock()
-				 * 	Dmitry Gorodchanin 09/02/96.
-				 */
-				locks_free_lock(new_fl);
-				return (-ERESTARTSYS);
-			}
-			locks_insert_block(fl, new_fl);
-			interruptible_sleep_on(&new_fl->fl_wait);
-			wake_up(&new_fl->fl_wait);
-			if (current->signal & ~current->blocked) {
-				/* If we are here, than we were awakened
-				 * by a signal, so new_fl is still in the
-				 * block queue of fl. We need to remove 
-				 * new_fl and then free it.
-				 * 	Dmitry Gorodchanin 09/02/96.
-				 */
-				locks_delete_block(fl, new_fl);
-				locks_free_lock(new_fl);
-				return (-ERESTARTSYS);
-			}
-			goto repeat;
+	for (fl = filp->f_inode->i_flock; (fl != NULL) && (fl->fl_flags & FL_FLOCK);
+	     fl = fl->fl_next) {
+		if (!flock_locks_conflict(new_fl, fl))
+			continue;
+		if (!wait) {
+			locks_free_lock(new_fl);
+			return (-EAGAIN);
 		}
-		fl = fl->fl_next;
+		if (current->signal & ~current->blocked) {
+			/* Note: new_fl is not in any queue at this
+			 * point, so we must use locks_free_lock()
+			 * instead of locks_delete_lock()
+			 * 	Dmitry Gorodchanin 09/02/96.
+			 */
+			locks_free_lock(new_fl);
+			return (-ERESTARTSYS);
+		}
+		locks_insert_block(fl, new_fl);
+		interruptible_sleep_on(&new_fl->fl_wait);
+		locks_delete_block(fl, new_fl);
+		if (current->signal & ~current->blocked) {
+			/* Awakened by a signal. Free the new
+			 * lock and return an error.
+			 */
+			locks_free_lock(new_fl);
+			return (-ERESTARTSYS);
+		}
+		goto repeat;
 	}
 	locks_insert_lock(&filp->f_inode->i_flock, new_fl);
 	return (0);
 }
 
 /* Add a POSIX style lock to a file.
- * We merge adjacent locks whenever possible. POSIX locks come after FLOCK
- * locks in the list and are sorted by owner task, then by starting address
+ * We merge adjacent locks whenever possible. POSIX locks are sorted by owner
+ * task, then by starting address
  *
  * Kai Petzke writes:
  * To make freeing a lock much faster, we keep a pointer to the lock before the
@@ -762,39 +736,39 @@
 	struct file_lock **before;
 	int added = 0;
 
-repeat:
-	if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & F_FLOCK))
-		return (-EBUSY);
-
 	if (caller->fl_type != F_UNLCK) {
-		while (fl != NULL) {
-			if (posix_locks_conflict(caller, fl)) {
-				if (!wait)
-					return (-EAGAIN);
-				if (current->signal & ~current->blocked)
-					return (-ERESTARTSYS);
-				if (posix_locks_deadlock(caller->fl_owner, fl->fl_owner))
-					return (-EDEADLK);
-				interruptible_sleep_on(&fl->fl_wait);
-				if (current->signal & ~current->blocked)
-					return (-ERESTARTSYS);
-				goto repeat;
-			}
-			fl = fl->fl_next;
+  repeat:
+		for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
+			if (!(fl->fl_flags & FL_POSIX))
+				continue;
+			if (!posix_locks_conflict(caller, fl))
+				continue;
+			if (!wait)
+				return (-EAGAIN);
+			if (current->signal & ~current->blocked)
+				return (-ERESTARTSYS);
+			if (posix_locks_deadlock(caller->fl_owner, fl->fl_owner))
+				return (-EDEADLK);
+			locks_insert_block(fl, caller);
+			interruptible_sleep_on(&caller->fl_wait);
+			locks_delete_block(fl, caller);
+			if (current->signal & ~current->blocked)
+				return (-ERESTARTSYS);
+			goto repeat;
   		}
   	}
-	/*
-	 * Find the first old lock with the same owner as the new lock.
+
+	/* Find the first old lock with the same owner as the new lock.
 	 */
 	
 	before = &filp->f_inode->i_flock;
 
-	/* First skip FLOCK locks and locks owned by other processes.
+	/* First skip locks owned by other processes.
 	 */
-	while ((fl = *before) && (caller->fl_owner != fl->fl_owner)) {
+	while ((fl = *before) && (!(fl->fl_flags & FL_POSIX) ||
+				  (caller->fl_owner != fl->fl_owner))) {
 		before = &fl->fl_next;
 	}
-	
 
 	/* Process locks with this owner.
 	 */
@@ -862,7 +836,7 @@
 				 * as the change in lock type might satisfy
 				 * their needs.
 				 */
-				wake_up(&fl->fl_wait);
+				locks_wake_up_blocks(fl, 0);
 				fl->fl_start = caller->fl_start;
 				fl->fl_end = caller->fl_end;
 				fl->fl_type = caller->fl_type;
@@ -897,52 +871,43 @@
 			locks_insert_lock(before, left);
 		}
 		right->fl_start = caller->fl_end + 1;
-		wake_up(&right->fl_wait);
+		locks_wake_up_blocks(right, 0);
 	}
 	if (left) {
 		left->fl_end = caller->fl_start - 1;
-		wake_up(&left->fl_wait);
+		locks_wake_up_blocks(left, 0);
 	}
 	return (0);
 }
 
-/* Allocate memory for a new lock and initialize its fields from
- * fl. The lock is not inserted into any lists until locks_insert_lock()
- * or locks_insert_block() are called.
+/* Allocate new lock.
+ * Initialize its fields from fl. The lock is not inserted into any
+ * lists until locks_insert_lock() or locks_insert_block() are called.
  */
-
 static struct file_lock *locks_alloc_lock(struct file_lock *fl)
 {
-	struct file_lock *retval;
+	struct file_lock *tmp;
 
-	retval = unused_file_locks;
-	if (retval) {
-		unused_file_locks = retval->fl_next;
-		goto init_file_lock;
-	}
-	retval = (struct file_lock *)
-		kmalloc(sizeof(struct file_lock), GFP_ATOMIC);
-	if (retval) {
-		retval->fl_wait = NULL;
-init_file_lock:
-		retval->fl_next = NULL;
-		retval->fl_nextlink = NULL;
-		retval->fl_prevlink = NULL;
-		retval->fl_block = NULL;
-		retval->fl_owner = fl->fl_owner;
-		retval->fl_file = fl->fl_file;
-		retval->fl_flags = fl->fl_flags;
-		retval->fl_type = fl->fl_type;
-		retval->fl_start = fl->fl_start;
-		retval->fl_end = fl->fl_end;		
-	}
-	return retval;
+	/* Okay, let's make a new file_lock structure... */
+	if ((tmp = (struct file_lock *)kmalloc(sizeof(struct file_lock),
+					       GFP_ATOMIC)) == NULL)
+		return (tmp);
+
+	memset(tmp, 0, sizeof(*tmp));
+
+	tmp->fl_flags = fl->fl_flags;
+	tmp->fl_owner = fl->fl_owner;
+	tmp->fl_file = fl->fl_file;
+	tmp->fl_type = fl->fl_type;
+	tmp->fl_start = fl->fl_start;
+	tmp->fl_end = fl->fl_end;
+
+	return (tmp);
 }
 
 /* Insert file lock fl into an inode's lock list at the position indicated
  * by pos. At the same time add the lock to the global file lock list.
  */
-
 static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
 {
 	fl->fl_nextlink = file_lock_table;
@@ -957,63 +922,52 @@
 }
 
 /* Delete a lock and free it.
- * First remove our lock from the lock lists. Then remove all the blocked
- * locks from our blocked list, waking up the processes that own them. If
- * told to wait, then sleep on each of these lock's wait queues. Each
- * blocked process will wake up and immediately wake up its own wait queue
- * allowing us to be scheduled again. Lastly, wake up our own wait queue
- * before freeing the file_lock structure.
+ * First remove our lock from the active lock lists. Then call
+ * locks_wake_up_blocks() to wake up processes that are blocked
+ * waiting for this lock. Finally free the lock structure.
  */
-
-static void locks_delete_lock(struct file_lock **fl_p, unsigned int wait)
+static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait)
 {
-	struct file_lock *fl;
-	struct file_lock *pfl;
-	struct file_lock *nfl;
+	struct file_lock *thisfl;
+	struct file_lock *prevfl;
+	struct file_lock *nextfl;
 	
-	fl = *fl_p;
-	*fl_p = fl->fl_next;
-	pfl = fl->fl_prevlink;
-	nfl = fl->fl_nextlink;
+	thisfl = *thisfl_p;
+	*thisfl_p = thisfl->fl_next;
+
+	prevfl = thisfl->fl_prevlink;
+	nextfl = thisfl->fl_nextlink;
 
-	if (nfl != NULL)
-		nfl->fl_prevlink = pfl;
+	if (nextfl != NULL)
+		nextfl->fl_prevlink = prevfl;
 
-	if (pfl != NULL)
-		pfl->fl_nextlink = nfl;
+	if (prevfl != NULL)
+		prevfl->fl_nextlink = nextfl;
 	else
-		file_lock_table = nfl;
+		file_lock_table = nextfl;
 	
-	while ((nfl = fl->fl_block) != NULL) {
-		fl->fl_block = nfl->fl_block;
-		nfl->fl_block = NULL;
-		wake_up(&nfl->fl_wait);
-		if (wait)
-			sleep_on(&nfl->fl_wait);
-	}
-
-	wake_up(&fl->fl_wait);
-	locks_free_lock(fl);
+	locks_wake_up_blocks(thisfl, wait);
+	locks_free_lock(thisfl);
 
 	return;
 }
 
 
-static char *lock_get_status(struct file_lock *fl, char *p, int id, char *pfx)
+static char *lock_get_status(struct file_lock *fl, int id, char *pfx)
 {
-	struct wait_queue *wt;
+	static char temp[129];
+	char *p = temp;
+	struct inode *inode;
+
+	inode = fl->fl_file->f_inode;
 
 	p += sprintf(p, "%d:%s ", id, pfx);
-	if (fl->fl_flags & F_POSIX) {
-#ifdef CONFIG_LOCK_MANDATORY	 
-		p += sprintf(p, "%s %s ",
-			     (fl->fl_flags & F_BROKEN) ? "BROKEN" : "POSIX ",
-			     ((fl->fl_file->f_inode->i_mode & (S_IXGRP | S_ISGID))
-			      == S_ISGID) ? "MANDATORY" : "ADVISORY ");
-#else
-		p += sprintf(p, "%s ADVISORY ",
-			     (fl->fl_flags & F_BROKEN) ? "BROKEN" : "POSIX ");
-#endif
+	if (fl->fl_flags & FL_POSIX) {
+		p += sprintf(p, "%6s %s ",
+			     (fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
+			     (IS_MANDLOCK(inode) &&
+			      (inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ?
+			     "MANDATORY" : "ADVISORY ");
 	}
 	else {
 		p += sprintf(p, "FLOCK  ADVISORY  ");
@@ -1021,36 +975,70 @@
 	p += sprintf(p, "%s ", (fl->fl_type == F_RDLCK) ? "READ " : "WRITE");
 	p += sprintf(p, "%d %s:%ld %ld %ld ",
 		     fl->fl_owner ? fl->fl_owner->pid : 0,
-		     kdevname(fl->fl_file->f_inode->i_dev),
-		     fl->fl_file->f_inode->i_ino, fl->fl_start,
+		     kdevname(inode->i_dev), inode->i_ino, fl->fl_start,
 		     fl->fl_end);
-	p += sprintf(p, "%08lx %08lx %08lx %08lx %08lx\n%d:%s",
-		     (long)fl, (long)fl->fl_prevlink, (long)fl->fl_nextlink,
-		     (long)fl->fl_next, (long)fl->fl_block, id, pfx);
-	if ((wt = fl->fl_wait) != NULL) {
-		struct wait_queue *head = WAIT_QUEUE_HEAD(&fl->fl_wait);
-		while (wt != head) {
-			p += sprintf(p, " %d", wt->task->pid);
-			wt = wt->next;
+	sprintf(p, "%08lx %08lx %08lx %08lx %08lx\n",
+		(long)fl, (long)fl->fl_prevlink, (long)fl->fl_nextlink,
+		(long)fl->fl_next, (long)fl->fl_nextblock);
+	return (temp);
+}
+
+static inline int copy_lock_status(char *p, char **q, off_t pos, int len,
+				   off_t offset, int length)
+{
+	int i;
+
+	i = pos - offset;
+	if (i > 0) {
+		if (i >= length) {
+			i = len + length - i;
+			memcpy(*q, p, i);
+			*q += i;
+			return (0);
+		}
+		if (i < len) {
+			p += len - i;
 		}
+		else
+			i = len;
+		memcpy(*q, p, i);
+		*q += i;
 	}
-	p += sprintf(p, "\n");
-	return (p);
+	
+	return (1);
 }
 
-int get_locks_status(char *buf)
+int get_locks_status(char *buffer, char **start, off_t offset, int length)
 {
 	struct file_lock *fl;
 	struct file_lock *bfl;
 	char *p;
+	char *q = buffer;
 	int i;
+	int len;
+	off_t pos = 0;
 
-	p = buf;
 	for (fl = file_lock_table, i = 1; fl != NULL; fl = fl->fl_nextlink, i++) {
-		p = lock_get_status(fl, p, i, "");
-		for (bfl = fl; bfl->fl_block != NULL; bfl = bfl->fl_block)
-			p = lock_get_status(bfl->fl_block, p, i, " ->");
-	}
-	return  (p - buf);
+		p = lock_get_status(fl, i, "");
+		len = strlen(p);
+		pos += len;
+		if (!copy_lock_status(p, &q, pos, len, offset, length))
+			goto done;
+		if ((bfl = fl->fl_nextblock) == NULL)
+			continue;
+		do {
+			p = lock_get_status(bfl, i, " ->");
+			len = strlen(p);
+			pos += len;
+			if (!copy_lock_status(p, &q, pos, len, offset, length))
+				goto done;
+		} while ((bfl = bfl->fl_nextblock) != fl);
+	}
+done:
+	if (q != buffer)
+		*start = buffer;
+	return (q - buffer);
 }
+
+
 
diff -u --recursive --new-file v2.0.30/linux/fs/proc/array.c linux/fs/proc/array.c
--- v2.0.30/linux/fs/proc/array.c	Tue Oct 29 17:42:41 1996
+++ linux/fs/proc/array.c	Sun Aug  3 13:59:07 1997
@@ -975,7 +975,7 @@
 extern int get_pci_list(char*);
 extern int get_md_status (char *);
 extern int get_rtc_status (char *);
-extern int get_locks_status (char *);
+extern int get_locks_status (char *, char **, off_t, int);
 #ifdef __SMP_PROF__
 extern int get_smp_prof_list(char *);
 #endif
@@ -1051,7 +1051,7 @@
 			return get_rtc_status(page);
 #endif
 		case PROC_LOCKS:
-			return get_locks_status(page);
+			return get_locks_status(page, start, offset, length);
 	}
 	return -EBADF;
 }
diff -u --recursive --new-file v2.0.30/linux/fs/smbfs/sock.c linux/fs/smbfs/sock.c
--- v2.0.30/linux/fs/smbfs/sock.c	Mon Dec 16 03:07:42 1996
+++ linux/fs/smbfs/sock.c	Mon Aug  4 08:41:23 1997
@@ -373,7 +373,6 @@
 	int total_data = 0;
 	int total_param = 0;
 	int result;
-	unsigned char *inbuf = server->packet;
 	unsigned char *rcv_buf;
 	int buf_len;
 	int data_len = 0;
@@ -389,8 +388,8 @@
 		*ldata = *lparam = 0;
 		return 0;
 	}
-	total_data = WVAL(inbuf, smb_tdrcnt);
-	total_param = WVAL(inbuf, smb_tprcnt);
+	total_data = WVAL(server->packet, smb_tdrcnt);
+	total_param = WVAL(server->packet, smb_tprcnt);
 
 	DDPRINTK("smb_receive_trans2: td=%d,tp=%d\n", total_data, total_param);
 
@@ -415,6 +414,8 @@
 
 	while (1)
 	{
+		unsigned char *inbuf = server->packet;
+
 		if (WVAL(inbuf, smb_prdisp) + WVAL(inbuf, smb_prcnt)
 		    > total_param)
 		{
diff -u --recursive --new-file v2.0.30/linux/fs/super.c linux/fs/super.c
--- v2.0.30/linux/fs/super.c	Tue Apr  8 08:47:46 1997
+++ linux/fs/super.c	Sun Aug  3 13:59:07 1997
@@ -277,6 +277,7 @@
 	{ MS_NOSUID, ",nosuid" },
 	{ MS_NODEV, ",nodev" },
 	{ MS_SYNCHRONOUS, ",sync" },
+	{ MS_MANDLOCK, ",mand" },
 #ifdef MS_NOSUB			/* Can't find this except in mount.c */
 	{ MS_NOSUB, ",nosub" },
 #endif
diff -u --recursive --new-file v2.0.30/linux/include/asm-alpha/fcntl.h linux/include/asm-alpha/fcntl.h
--- v2.0.30/linux/include/asm-alpha/fcntl.h	Mon Aug  5 00:13:54 1996
+++ linux/include/asm-alpha/fcntl.h	Sun Aug  3 13:59:07 1997
@@ -49,12 +49,6 @@
 				   blocking */
 #define LOCK_UN		8	/* remove lock */
 
-#ifdef __KERNEL__
-#define F_POSIX		1
-#define F_FLOCK		2
-#define F_BROKEN	4	/* broken flock() emulation */
-#endif /* __KERNEL__ */
-
 struct flock {
 	short l_type;
 	short l_whence;
diff -u --recursive --new-file v2.0.30/linux/include/asm-alpha/fpu.h linux/include/asm-alpha/fpu.h
--- v2.0.30/linux/include/asm-alpha/fpu.h	Thu Dec 21 22:22:06 1995
+++ linux/include/asm-alpha/fpu.h	Sun Aug  3 10:58:38 1997
@@ -57,8 +57,29 @@
 				 IEEE_STATUS_OVF | IEEE_STATUS_UNF |	\
 				 IEEE_STATUS_INE)
 
+#define IEEE_SW_MASK		(IEEE_TRAP_ENABLE_MASK | IEEE_STATUS_MASK)
+
 #define IEEE_STATUS_TO_EXCSUM_SHIFT	16
 
 #define IEEE_INHERIT    (1UL<<63)	/* inherit on thread create? */
+
+/*
+ * Convert the software IEEE trap enables and status bits into the 
+ * hardware fpcr format.
+ */
+
+static inline unsigned long
+ieee_sw_to_fpcr(unsigned long sw)
+{
+	unsigned long fpcw;
+	fpcw = (sw & IEEE_STATUS_MASK) << 35;
+	fpcw |= sw & IEEE_STATUS_MASK ? FPCR_SUM : 0;
+	fpcw |= (~sw & (IEEE_TRAP_ENABLE_INV
+			| IEEE_TRAP_ENABLE_DZE
+			| IEEE_TRAP_ENABLE_OVF)) << 48;
+	fpcw |= (~sw & (IEEE_TRAP_ENABLE_UNF | IEEE_TRAP_ENABLE_INE)) << 57;
+	return fpcw;
+}
+
 
 #endif /* __ASM_ALPHA_FPU_H */
diff -u --recursive --new-file v2.0.30/linux/include/asm-alpha/processor.h linux/include/asm-alpha/processor.h
--- v2.0.30/linux/include/asm-alpha/processor.h	Sun Sep 15 00:34:18 1996
+++ linux/include/asm-alpha/processor.h	Sun Aug  3 10:58:38 1997
@@ -43,8 +43,9 @@
 
 	/* the fields below are Linux-specific: */
 	/*
-	 * bit 0:    perform syscall argument validation (get/set_fs)
-	 * bit 1..5: IEEE_TRAP_ENABLE bits (see fpu.h)
+	 * bit 0:	perform syscall argument validation (get/set_fs)
+	 * bit 1..5:	IEEE_TRAP_ENABLE bits (see fpu.h)
+	 * bit 17..21:	IEEE_STATUS bits (see fpu.h)
 	 */
 	unsigned long flags;
 };
diff -u --recursive --new-file v2.0.30/linux/include/asm-i386/fcntl.h linux/include/asm-i386/fcntl.h
--- v2.0.30/linux/include/asm-i386/fcntl.h	Wed Jul 17 05:10:03 1996
+++ linux/include/asm-i386/fcntl.h	Sun Aug  3 13:59:08 1997
@@ -48,12 +48,6 @@
 				   blocking */
 #define LOCK_UN		8	/* remove lock */
 
-#ifdef __KERNEL__
-#define F_POSIX		1
-#define F_FLOCK		2
-#define F_BROKEN	4	/* broken flock() emulation */
-#endif /* __KERNEL__ */
-
 struct flock {
 	short l_type;
 	short l_whence;
diff -u --recursive --new-file v2.0.30/linux/include/asm-m68k/fcntl.h linux/include/asm-m68k/fcntl.h
--- v2.0.30/linux/include/asm-m68k/fcntl.h	Tue Apr 30 03:09:45 1996
+++ linux/include/asm-m68k/fcntl.h	Sun Aug  3 13:59:08 1997
@@ -48,12 +48,6 @@
 				   blocking */
 #define LOCK_UN		8	/* remove lock */
 
-#ifdef __KERNEL__
-#define F_POSIX		1
-#define F_FLOCK		2
-#define F_BROKEN	4	/* broken flock() emulation */
-#endif /* __KERNEL__ */
-
 struct flock {
 	short l_type;
 	short l_whence;
diff -u --recursive --new-file v2.0.30/linux/include/asm-mips/fcntl.h linux/include/asm-mips/fcntl.h
--- v2.0.30/linux/include/asm-mips/fcntl.h	Tue Apr 30 03:09:45 1996
+++ linux/include/asm-mips/fcntl.h	Sun Aug  3 13:59:08 1997
@@ -49,12 +49,6 @@
 				   blocking */
 #define LOCK_UN		8	/* remove lock */
 
-#ifdef __KERNEL__
-#define F_POSIX		1
-#define F_FLOCK		2
-#define F_BROKEN	4	/* broken flock() emulation */
-#endif /* __KERNEL__ */
-
 typedef struct flock {
 	short l_type;
 	short l_whence;
diff -u --recursive --new-file v2.0.30/linux/include/asm-ppc/fcntl.h linux/include/asm-ppc/fcntl.h
--- v2.0.30/linux/include/asm-ppc/fcntl.h	Tue Apr 30 03:09:45 1996
+++ linux/include/asm-ppc/fcntl.h	Sun Aug  3 13:59:08 1997
@@ -48,12 +48,6 @@
 				   blocking */
 #define LOCK_UN		8	/* remove lock */
 
-#ifdef __KERNEL__
-#define F_POSIX		1
-#define F_FLOCK		2
-#define F_BROKEN	4	/* broken flock() emulation */
-#endif /* __KERNEL__ */
-
 struct flock {
 	short l_type;
 	short l_whence;
diff -u --recursive --new-file v2.0.30/linux/include/asm-sparc/fcntl.h linux/include/asm-sparc/fcntl.h
--- v2.0.30/linux/include/asm-sparc/fcntl.h	Tue Apr 30 03:09:45 1996
+++ linux/include/asm-sparc/fcntl.h	Sun Aug  3 13:59:08 1997
@@ -48,12 +48,6 @@
 				   blocking */
 #define LOCK_UN		8	/* remove lock */
 
-#ifdef __KERNEL__
-#define F_POSIX		1
-#define F_FLOCK		2
-#define F_BROKEN	4	/* broken flock() emulation */
-#endif /* __KERNEL__ */
-
 struct flock {
 	short l_type;
 	short l_whence;
diff -u --recursive --new-file v2.0.30/linux/include/linux/fs.h linux/include/linux/fs.h
--- v2.0.30/linux/include/linux/fs.h	Fri Mar 28 16:08:17 1997
+++ linux/include/linux/fs.h	Mon Aug  4 15:01:06 1997
@@ -69,6 +69,7 @@
 #define MS_NOEXEC	 8	/* Disallow program execution */
 #define MS_SYNCHRONOUS	16	/* Writes are synced at once */
 #define MS_REMOUNT	32	/* Alter flags of a mounted FS */
+#define MS_MANDLOCK	64	/* Allow mandatory locks on an FS */
 #define S_WRITE		128	/* Write on file/directory/symlink */
 #define S_APPEND	256	/* Append-only file */
 #define S_IMMUTABLE	512	/* Immutable file */
@@ -76,7 +77,7 @@
 /*
  * Flags that can be altered by MS_REMOUNT
  */
-#define MS_RMT_MASK (MS_RDONLY)
+#define MS_RMT_MASK (MS_RDONLY|MS_MANDLOCK)
 
 /*
  * Magic mount flag number. Has to be or-ed to the flag values.
@@ -97,6 +98,7 @@
 #define IS_NODEV(inode) ((inode)->i_flags & MS_NODEV)
 #define IS_NOEXEC(inode) ((inode)->i_flags & MS_NOEXEC)
 #define IS_SYNC(inode) ((inode)->i_flags & MS_SYNCHRONOUS)
+#define IS_MANDLOCK(inode) ((inode)->i_flags & MS_MANDLOCK)
 
 #define IS_WRITABLE(inode) ((inode)->i_flags & S_WRITE)
 #define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
@@ -335,16 +337,22 @@
 	void *private_data;	/* needed for tty driver, and maybe others */
 };
 
+#define FL_POSIX	1
+#define FL_FLOCK	2
+#define FL_BROKEN	4	/* broken flock() emulation */
+#define FL_ACCESS	8	/* for processes suspended by mandatory locking */
+
 struct file_lock {
 	struct file_lock *fl_next;	/* singly linked list for this inode  */
 	struct file_lock *fl_nextlink;	/* doubly linked list of all locks */
 	struct file_lock *fl_prevlink;	/* used to simplify lock removal */
-	struct file_lock *fl_block;
+	struct file_lock *fl_nextblock; /* circular list of blocked processes */
+	struct file_lock *fl_prevblock;
 	struct task_struct *fl_owner;
 	struct wait_queue *fl_wait;
 	struct file *fl_file;
-	char fl_flags;
-	char fl_type;
+	unsigned char fl_flags;
+	unsigned char fl_type;
 	off_t fl_start;
 	off_t fl_end;
 };
@@ -367,27 +375,25 @@
 
 extern inline int locks_verify_locked(struct inode *inode)
 {
-#ifdef CONFIG_LOCK_MANDATORY	 
 	/* Candidates for mandatory locking have the setgid bit set
 	 * but no group execute bit -  an otherwise meaningless combination.
 	 */
-	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+	if (IS_MANDLOCK(inode) &&
+	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
 		return (locks_mandatory_locked(inode));
-#endif
 	return (0);
 }
 extern inline int locks_verify_area(int read_write, struct inode *inode,
 				    struct file *filp, unsigned int offset,
 				    unsigned int count)
 {
-#ifdef CONFIG_LOCK_MANDATORY	 
 	/* Candidates for mandatory locking have the setgid bit set
 	 * but no group execute bit -  an otherwise meaningless combination.
 	 */
-	if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
+	if (IS_MANDLOCK(inode) &&
+	    (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
 		return (locks_mandatory_area(read_write, inode, filp, offset,
 					     count));
-#endif
 	return (0);
 }
 
diff -u --recursive --new-file v2.0.30/linux/include/linux/hdreg.h linux/include/linux/hdreg.h
--- v2.0.30/linux/include/linux/hdreg.h	Fri Mar 28 16:08:17 1997
+++ linux/include/linux/hdreg.h	Mon Aug  4 15:01:06 1997
@@ -53,6 +53,7 @@
 
 #define WIN_DOORLOCK		0xde	/* lock door on removable drives */
 #define WIN_DOORUNLOCK		0xdf	/* unlock door on removable drives */
+#define WIN_ACKMC		0xdb	/* acknowledge media change */
 
 #define WIN_MULTREAD		0xC4	/* read sectors using multiple mode */
 #define WIN_MULTWRITE		0xC5	/* write sectors using multiple mode */
@@ -67,11 +68,15 @@
 #define WIN_SRST		0x08	/* ATAPI soft reset command */
 #define WIN_PACKETCMD		0xa0	/* Send a packet command. */
 
+/* Non-standard commands */
+#define EXABYTE_ENABLE_NEST	0xf0
+
 /* Bits for HD_ERROR */
 #define MARK_ERR	0x01	/* Bad address mark */
 #define TRK0_ERR	0x02	/* couldn't find track 0 */
 #define ABRT_ERR	0x04	/* Command aborted */
 #define ID_ERR		0x10	/* ID field not found */
+#define MC_ERR		0x20	/* media changed */
 #define ECC_ERR		0x40	/* Uncorrectable ECC error */
 #define	BBD_ERR		0x80	/* block marked bad */
 
diff -u --recursive --new-file v2.0.30/linux/include/linux/ipx.h linux/include/linux/ipx.h
--- v2.0.30/linux/include/linux/ipx.h	Mon May 13 13:39:28 1996
+++ linux/include/linux/ipx.h	Mon Aug  4 08:42:09 1997
@@ -75,5 +75,6 @@
 #define SIOCAIPXITFCRT		(SIOCPROTOPRIVATE)
 #define SIOCAIPXPRISLT		(SIOCPROTOPRIVATE+1)
 #define SIOCIPXCFGDATA		(SIOCPROTOPRIVATE+2)
+#define SIOCIPXNCPCONN		(SIOCPROTOPRIVATE+3)
 #endif
 
diff -u --recursive --new-file v2.0.30/linux/include/linux/pagemap.h linux/include/linux/pagemap.h
--- v2.0.30/linux/include/linux/pagemap.h	Fri Mar 28 16:08:17 1997
+++ linux/include/linux/pagemap.h	Mon Aug  4 15:25:11 1997
@@ -11,6 +11,7 @@
 
 #include <linux/mm.h>
 #include <linux/fs.h>
+#include <linux/swapctl.h>
 
 static inline unsigned long page_address(struct page * page)
 {
@@ -20,7 +21,7 @@
 #define PAGE_HASH_BITS 11
 #define PAGE_HASH_SIZE (1 << PAGE_HASH_BITS)
 
-#define PAGE_AGE_VALUE 16
+#define PAGE_AGE_VALUE ((PAGE_INITIAL_AGE)+(PAGE_ADVANCE))
 
 extern unsigned long page_cache_size; /* # of pages currently in the hash table */
 extern struct page * page_hash_table[PAGE_HASH_SIZE];
diff -u --recursive --new-file v2.0.30/linux/include/linux/pci.h linux/include/linux/pci.h
--- v2.0.30/linux/include/linux/pci.h	Mon Mar 31 13:23:23 1997
+++ linux/include/linux/pci.h	Mon Aug  4 11:45:56 1997
@@ -564,6 +564,7 @@
 #define PCI_DEVICE_ID_INTEL_82371SB_1	0x7010
 #define PCI_DEVICE_ID_INTEL_82371SB_2	0x7020
 #define PCI_DEVICE_ID_INTEL_82437VX	0x7030
+#define PCI_DEVICE_ID_INTEL_82371AB	0x7111
 #define PCI_DEVICE_ID_INTEL_P6		0x84c4
 
 #define PCI_VENDOR_ID_ADAPTEC		0x9004
diff -u --recursive --new-file v2.0.30/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.0.30/linux/include/linux/proc_fs.h	Tue Apr  8 08:47:46 1997
+++ linux/include/linux/proc_fs.h	Mon Aug  4 15:01:09 1997
@@ -141,6 +141,7 @@
 	PROC_SCSI_A2091,
 	PROC_SCSI_GVP11,
 	PROC_SCSI_ATARI,
+	PROC_SCSI_IDESCSI,
 	PROC_SCSI_SCSI_DEBUG,	
 	PROC_SCSI_NOT_PRESENT,
 	PROC_SCSI_FILE,                        /* I'm assuming here that we */
diff -u --recursive --new-file v2.0.30/linux/include/linux/sched.h linux/include/linux/sched.h
--- v2.0.30/linux/include/linux/sched.h	Fri Mar 28 16:08:17 1997
+++ linux/include/linux/sched.h	Mon Aug  4 15:01:06 1997
@@ -342,7 +342,7 @@
 extern void wake_up_interruptible(struct wait_queue ** p);
 extern void wake_up_process(struct task_struct * tsk);
 
-extern void notify_parent(struct task_struct * tsk);
+extern void notify_parent(struct task_struct * tsk, int signal);
 extern void force_sig(unsigned long sig,struct task_struct * p);
 extern int send_sig(unsigned long sig,struct task_struct * p,int priv);
 extern int in_group_p(gid_t grp);
diff -u --recursive --new-file v2.0.30/linux/include/linux/wireless.h linux/include/linux/wireless.h
--- v2.0.30/linux/include/linux/wireless.h	Thu Mar  6 10:03:51 1997
+++ linux/include/linux/wireless.h	Mon Aug  4 15:08:21 1997
@@ -63,20 +63,25 @@
  * (there is some stuff that will be added in the future...)
  * I just plan to increment with each new version.
  */
-#define WIRELESS_EXT	4
+#define WIRELESS_EXT	5
 
 /*
  * Changes :
  *
  * V2 to V3
  * --------
- *	Alan Cox start some imcompatibles changes. I've integrated a bit more.
+ *	Alan Cox start some incompatibles changes. I've integrated a bit more.
  *	- Encryption renamed to Encode to avoid US regulation problems
  *	- Frequency changed from float to struct to avoid problems on old 386
  *
  * V3 to V4
  * --------
  *	- Add sensitivity
+ *
+ * V4 to V5
+ * --------
+ *	- Missing encoding definitions in range
+ *	- Access points stuff
  */
 
 /* -------------------------- IOCTL LIST -------------------------- */
@@ -103,11 +108,16 @@
 #define SIOCSIWSPY	0x8B10		/* set spy addresses */
 #define SIOCGIWSPY	0x8B11		/* get spy info (quality of link) */
 
+/* Access Point manipulation */
+#define SIOCSIWAP	0x8B14		/* set access point hardware addresses */
+#define SIOCGIWAP	0x8B15		/* get access point hardware addresses */
+#define SIOCGIWAPLIST	0x8B17		/* get list of access point in range */
+
 /* ------------------------- IOCTL STUFF ------------------------- */
 
 /* The first and the last (range) */
 #define SIOCIWFIRST	0x8B00
-#define SIOCIWLAST	0x8B13
+#define SIOCIWLAST	0x8B17
 
 /* Even : get (world access), odd : set (root access) */
 #define IW_IS_SET(cmd)	(!((cmd) & 0x1))
@@ -185,6 +195,17 @@
 	__u32		misc;		/* Others cases */
 };
 
+/*
+ *	Encoding information (setting and so on)
+ *	Encoding might be hardware encryption, scrambing or others
+ */
+struct	iw_encoding
+{
+  __u8	method;			/* Algorithm number / key used */
+  __u64	code;			/* Data/key used for algorithm */
+};
+
+
 /* ------------------------ WIRELESS STATS ------------------------ */
 /*
  * Wireless statistics (used for /proc/net/wireless)
@@ -234,13 +255,11 @@
 					 * 0-1000 = channel
 					 * > 1000 = frequency in Hz */
 
-		struct		/* Encoding stuff */
-		{
-			__u8	method;		/* Algorithm number / off */
-			__u64	code;		/* Data used for algorithm */
-		}	encoding;
+		struct iw_encoding	encoding;	/* Encoding stuff */
 
-		__u32	sensitivity;	/* signal level threshold */
+		__u32	sensitivity;		/* signal level threshold */
+
+		struct sockaddr	ap_addr;	/* Access point address */
 
 		struct		/* For all data bigger than 16 octets */
 		{
@@ -275,16 +294,16 @@
 	__u16		num_channels;	/* Number of channels [0; num - 1] */
 	__u8		num_frequency;	/* Number of entry in the list */
 	struct iw_freq	freq[IW_MAX_FREQUENCIES];	/* list */
-
 	/* Note : this frequency list doesn't need to fit channel numbers */
 
-	/* Encoder stuff */
-
 	/* signal level threshold range */
 	__u32	sensitivity;
 
 	/* Quality of link & SNR stuff */
 	struct iw_quality	max_qual;	/* Quality of the link */
+
+	/* Encoder stuff */
+	struct iw_encoding	max_encoding;	/* Encoding max range */
 };
 
 /*
diff -u --recursive --new-file v2.0.30/linux/include/net/sock.h linux/include/net/sock.h
--- v2.0.30/linux/include/net/sock.h	Tue Apr  8 08:47:47 1997
+++ linux/include/net/sock.h	Mon Aug  4 15:03:38 1997
@@ -109,7 +109,11 @@
  * know the connection this socket belongs to. 
  */
 	struct ncp_server       *ncp_server;
-	
+/* 
+ * To handle special ncp connection-handling sockets for mars_nwe,
+ * the connection number must be stored in the socket.
+ */
+	unsigned short		ipx_ncp_conn;
 };
 #endif
 
diff -u --recursive --new-file v2.0.30/linux/init/main.c linux/init/main.c
--- v2.0.30/linux/init/main.c	Sat Nov 23 05:28:23 1996
+++ linux/init/main.c	Sun Aug  3 21:41:20 1997
@@ -59,6 +59,7 @@
 static int init(void *);
 extern int bdflush(void *);
 extern int kswapd(void *);
+extern void kswapd_setup(void);
 
 extern void init_IRQ(void);
 extern void init_modules(void);
@@ -920,6 +921,7 @@
 	/* Launch bdflush from here, instead of the old syscall way. */
 	kernel_thread(bdflush, NULL, 0);
 	/* Start the background pageout daemon. */
+	kswapd_setup();
 	kernel_thread(kswapd, NULL, 0);
 
 #ifdef CONFIG_BLK_DEV_INITRD
diff -u --recursive --new-file v2.0.30/linux/ipc/shm.c linux/ipc/shm.c
--- v2.0.30/linux/ipc/shm.c	Fri Nov 22 06:25:18 1996
+++ linux/ipc/shm.c	Mon Aug  4 15:25:11 1997
@@ -13,6 +13,7 @@
 #include <linux/stat.h>
 #include <linux/malloc.h>
 #include <linux/swap.h>
+#include <linux/swapctl.h>
 
 #include <asm/segment.h>
 #include <asm/pgtable.h>
@@ -672,6 +673,11 @@
 			shm_swp--;
 		}
 		shm_rss++;
+
+		/* Give the physical reallocated page a bigger start */
+		if (shm_rss < (MAP_NR(high_memory) >> 3))
+			mem_map[MAP_NR(page)].age = (PAGE_INITIAL_AGE + PAGE_ADVANCE);
+
 		pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
 		shp->shm_pages[idx] = pte_val(pte);
 	} else
diff -u --recursive --new-file v2.0.30/linux/kernel/exit.c linux/kernel/exit.c
--- v2.0.30/linux/kernel/exit.c	Tue Oct 29 17:42:42 1996
+++ linux/kernel/exit.c	Mon Aug  4 15:01:57 1997
@@ -100,11 +100,9 @@
 	return 0;
 }
 
-void notify_parent(struct task_struct * tsk)
+void notify_parent(struct task_struct * tsk, int signal)
 {
-	if (tsk->p_pptr == task[smp_num_cpus])		/* Init */
-		tsk->exit_signal = SIGCHLD;
-	send_sig(tsk->exit_signal, tsk->p_pptr, 1);
+	send_sig(signal, tsk->p_pptr, 1);
 	wake_up_interruptible(&tsk->p_pptr->wait_chldexit);
 }
 
@@ -385,11 +383,10 @@
 	struct task_struct * p;
 
 	for_each_task(p) {
-		if (p->p_opptr == father)
-			if (task[smp_num_cpus])	/* init */
-				p->p_opptr = task[smp_num_cpus];
-			else
-				p->p_opptr = task[0];
+		if (p->p_opptr == father) {
+			p->exit_signal = SIGCHLD;
+			p->p_opptr = task[smp_num_cpus] ? : task[0];	/* init */
+		}
 	}
 }
 
@@ -519,7 +516,7 @@
 		kill_pg(current->pgrp,SIGCONT,1);
 	}
 	/* Let father know we died */
-	notify_parent(current);
+	notify_parent(current, current->exit_signal);
 	
 	/*
 	 * This loop does two things:
@@ -533,15 +530,14 @@
 		current->p_cptr = p->p_osptr;
 		p->p_ysptr = NULL;
 		p->flags &= ~(PF_PTRACED|PF_TRACESYS);
-		if (task[smp_num_cpus] && task[smp_num_cpus] != current) /* init */
-			p->p_pptr = task[smp_num_cpus];
-		else
-			p->p_pptr = task[0];
+
+		p->p_pptr = p->p_opptr;
 		p->p_osptr = p->p_pptr->p_cptr;
 		p->p_osptr->p_ysptr = p;
 		p->p_pptr->p_cptr = p;
 		if (p->state == TASK_ZOMBIE)
-			notify_parent(p);
+			notify_parent(p, p->exit_signal);
+
 		/*
 		 * process group orphan check
 		 * Case ii: Our child is in a different pgrp 
@@ -672,7 +668,7 @@
 					REMOVE_LINKS(p);
 					p->p_pptr = p->p_opptr;
 					SET_LINKS(p);
-					notify_parent(p);
+					notify_parent(p, p->exit_signal);
 				} else
 					release(p);
 #ifdef DEBUG_PROC_TREE
diff -u --recursive --new-file v2.0.30/linux/kernel/sched.c linux/kernel/sched.c
--- v2.0.30/linux/kernel/sched.c	Tue Apr  8 08:47:47 1997
+++ linux/kernel/sched.c	Sun Aug  3 15:17:16 1997
@@ -1460,8 +1460,9 @@
 {
 	cli();
 	move_last_runqueue(current);
-	sti();
+	current->counter = 0;
 	need_resched = 1;
+	sti();
 	return 0;
 }
 
diff -u --recursive --new-file v2.0.30/linux/kernel/sysctl.c linux/kernel/sysctl.c
--- v2.0.30/linux/kernel/sysctl.c	Mon Mar 31 13:22:37 1997
+++ linux/kernel/sysctl.c	Mon Aug  4 15:02:53 1997
@@ -201,7 +201,7 @@
 	do {
 		context = 0;
 		error = parse_table(name, nlen, oldval, oldlenp, 
-				    newval, newlen, root_table, &context);
+				    newval, newlen, tmp->ctl_table, &context);
 		if (context)
 			kfree(context);
 		if (error != -ENOTDIR)
diff -u --recursive --new-file v2.0.30/linux/mm/filemap.c linux/mm/filemap.c
--- v2.0.30/linux/mm/filemap.c	Wed Sep 11 07:57:19 1996
+++ linux/mm/filemap.c	Mon Aug  4 15:25:11 1997
@@ -41,18 +41,7 @@
  * Simple routines for both non-shared and shared mappings.
  */
 
-/*
- * This is a special fast page-free routine that _only_ works
- * on page-cache pages that we are currently using. We can
- * just decrement the page count, because we know that the page
- * has a count > 1 (the page cache itself counts as one, and
- * we're currently using it counts as one). So we don't need
- * the full free_page() stuff..
- */
-static inline void release_page(struct page * page)
-{
-	atomic_dec(&page->count);
-}
+#define release_page(page) __free_page((page))
 
 /*
  * Invalidate the pages of an inode, removing all pages that aren't
@@ -100,7 +89,7 @@
 		/* page wholly truncated - free it */
 		if (offset >= start) {
 			if (PageLocked(page)) {
-				wait_on_page(page);
+				__wait_on_page(page);
 				goto repeat;
 			}
 			inode->i_nrpages--;
@@ -169,8 +158,12 @@
 		switch (page->count) {
 			case 1:
 				/* If it has been referenced recently, don't free it */
-				if (clear_bit(PG_referenced, &page->flags))
+				if (clear_bit(PG_referenced, &page->flags)) {
+					/* age this page potential used */
+					if (priority < 4)
+						age_page(page);
 					break;
+				}
 
 				/* is it a page cache page? */
 				if (page->inode) {
@@ -448,7 +441,7 @@
 
 #define PageAlignSize(size) (((size) + PAGE_SIZE -1) & PAGE_MASK)
 
-#if 0  /* small readahead */
+#ifdef CONFIG_READA_SMALL  /* small readahead */
 #define MAX_READAHEAD PageAlignSize(4096*7)
 #define MIN_READAHEAD PageAlignSize(4096*2)
 #else /* large readahead */
@@ -784,8 +777,15 @@
 found_page:
 	/*
 	 * Ok, found a page in the page cache, now we need to check
-	 * that it's up-to-date
+	 * that it's up-to-date.  First check whether we'll need an
+	 * extra page -- better to overlap the allocation with the I/O.
 	 */
+	if (no_share && !new_page) {
+		new_page = __get_free_page(GFP_KERNEL);
+		if (!new_page)
+			goto failure;
+	}
+
 	if (PageLocked(page))
 		goto page_locked_wait;
 	if (!PageUptodate(page))
@@ -810,13 +810,8 @@
 	}
 
 	/*
-	 * Check that we have another page to copy it over to..
+	 * No sharing ... copy to the new page.
 	 */
-	if (!new_page) {
-		new_page = __get_free_page(GFP_KERNEL);
-		if (!new_page)
-			goto failure;
-	}
 	memcpy((void *) new_page, (void *) old_page, PAGE_SIZE);
 	flush_page_to_ram(new_page);
 	release_page(page);
@@ -880,6 +875,8 @@
 	 */
 failure:
 	release_page(page);
+	if (new_page)
+		free_page(new_page);
 no_page:
 	return 0;
 }
@@ -1003,6 +1000,8 @@
 	unsigned long page;
 	int error;
 
+	if (pte_none(pte))
+		return 0;
 	if (!(flags & MS_INVALIDATE)) {
 		if (!pte_present(pte))
 			return 0;
@@ -1015,8 +1014,6 @@
 		page = pte_page(pte);
 		mem_map[MAP_NR(page)].count++;
 	} else {
-		if (pte_none(pte))
-			return 0;
 		flush_cache_page(vma, address);
 		pte_clear(ptep);
 		flush_tlb_page(vma, address);
diff -u --recursive --new-file v2.0.30/linux/mm/mlock.c linux/mm/mlock.c
--- v2.0.30/linux/mm/mlock.c	Wed Sep 11 07:57:19 1996
+++ linux/mm/mlock.c	Mon Aug  4 15:25:11 1997
@@ -202,7 +202,7 @@
 
 	/* we may lock at most half of physical memory... */
 	/* (this check is pretty bogus, but doesn't hurt) */
-	if (locked > MAP_NR(high_memory)/2)
+	if (locked > (MAP_NR(high_memory) >> 1))
 		return -ENOMEM;
 
 	return do_mlock(start, len, 1);
@@ -259,7 +259,7 @@
 
 	/* we may lock at most half of physical memory... */
 	/* (this check is pretty bogus, but doesn't hurt) */
-	if (current->mm->total_vm > MAP_NR(high_memory)/2)
+	if (current->mm->total_vm > (MAP_NR(high_memory) >> 1))
 		return -ENOMEM;
 
 	return do_mlockall(flags);
diff -u --recursive --new-file v2.0.30/linux/mm/page_alloc.c linux/mm/page_alloc.c
--- v2.0.30/linux/mm/page_alloc.c	Sat Aug 17 11:19:29 1996
+++ linux/mm/page_alloc.c	Mon Aug  4 15:25:11 1997
@@ -264,11 +264,11 @@
 
 	/*
 	 * select nr of pages we try to keep free for important stuff
-	 * with a minimum of 16 pages. This is totally arbitrary
+	 * with a minimum of 24 pages. This is totally arbitrary
 	 */
 	i = (end_mem - PAGE_OFFSET) >> (PAGE_SHIFT+7);
-	if (i < 16)
-		i = 16;
+	if (i < 24)
+		i = 24;
 	min_free_pages = i;
 	free_pages_low = i + (i>>1);
 	free_pages_high = i + i;
@@ -311,7 +311,8 @@
 	unsigned long page = __get_free_page(GFP_KERNEL);
 
 	if (pte_val(*page_table) != entry) {
-		free_page(page);
+		if (page)
+			free_page(page);
 		return;
 	}
 	if (!page) {
@@ -327,6 +328,11 @@
 	}
 	vma->vm_mm->rss++;
 	tsk->maj_flt++;
+
+	/* Give the physical reallocated page a bigger start */
+	if (vma->vm_mm->rss < (MAP_NR(high_memory) >> 2))
+		mem_map[MAP_NR(page)].age = (PAGE_INITIAL_AGE + PAGE_ADVANCE);
+
 	if (!write_access && add_to_swap_cache(MAP_NR(page), entry)) {
 		/* keep swap page allocated for the moment (swap cache) */
 		set_pte(page_table, mk_pte(page, vma->vm_page_prot));
diff -u --recursive --new-file v2.0.30/linux/mm/vmscan.c linux/mm/vmscan.c
--- v2.0.30/linux/mm/vmscan.c	Sat Dec 14 04:24:31 1996
+++ linux/mm/vmscan.c	Mon Aug  4 15:25:11 1997
@@ -19,6 +19,7 @@
 #include <linux/swap.h>
 #include <linux/fs.h>
 #include <linux/swapctl.h>
+#include <linux/pagemap.h>
 #include <linux/smp_lock.h>
 
 #include <asm/dma.h>
@@ -27,11 +28,22 @@
 #include <asm/bitops.h>
 #include <asm/pgtable.h>
 
+/*
+ * To check memory consuming code elsewhere set this to 1
+ */
+#define MM_DEBUG 0
+
 /* 
  * When are we next due for a page scan? 
  */
 static int next_swap_jiffies = 0;
 
+/*
+ * Was the last kswapd wakeup caused by
+ *     nr_free_pages < free_pages_low
+ */
+static int last_wakeup_low = 0;
+
 /* 
  * How often do we do a pageout scan during normal conditions?
  * Default is four times a second.
@@ -275,10 +287,42 @@
 static int swap_out(unsigned int priority, int dma, int wait)
 {
 	static int swap_task;
-	int loop, counter;
+	int loop, counter, shfrv;
 	struct task_struct *p;
 
-	counter = ((PAGEOUT_WEIGHT * nr_tasks) >> 10) >> priority;
+#ifdef MM_DEBUG
+	shfrv = 10;
+#else
+	/*
+	 * Trouble due ageing pages: In some situations it is possible that we cross only tasks
+	 * which are swapped out or which have only physical pages with age >= 3.
+	 * High values of swap_cnt for memory consuming tasks do aggravate such situations.
+	 *
+	 * If PAGEOUT_WEIGHT has a value of 8192 a right shift value of 10 leads to
+	 *     (8 * nr_tasks) >> priority
+	 * Together with a high number of tasks, say 100, we have counters (due priority)
+	 *     12(6) + 25(5) + 50(4) + 100(3) + 200(2) + 400(1) + 800(0)
+	 * and as total result 1587 scans of swap_out() to swap out a task page.
+	 *
+	 * Just assume 80 tasks are swapped out and the remaining tasks have a swap_cnt value >= 40
+	 * together with pages with age >= 3.  Then we need approx 20*40*2 = 1600 scans to get a
+	 * free page.
+	 * And now assume that the amount of cached pages, buffers, and ipc pages are really low.
+	 */
+	switch (priority) {
+		case 6: case 5: case 4:  /* be friendly */
+			shfrv = 10;
+			break;
+		case 3: case 2: case 1:  /* more intensive */
+			shfrv =  9;
+			break;
+		case 0: default:         /* sorry we need a page */
+			shfrv =  8;
+			break;
+	}
+#endif
+
+	counter = ((PAGEOUT_WEIGHT * nr_tasks) >> shfrv) >> priority;
 	for(; counter >= 0; counter--) {
 		/*
 		 * Check that swap_task is suitable for swapping.  If not, look for
@@ -305,14 +349,19 @@
 		 * Determine the number of pages to swap from this process.
 		 */
 		if (!p->swap_cnt) {
- 			/* Normalise the number of pages swapped by
-			   multiplying by (RSS / 1MB) */
+ 			/*
+			 * Normalise the number of pages swapped by
+			 * multiplying by (RSS / 1MB)
+			 */
 			p->swap_cnt = AGE_CLUSTER_SIZE(p->mm->rss);
 		}
 		if (!--p->swap_cnt)
 			swap_task++;
 		switch (swap_out_process(p, dma, wait)) {
 			case 0:
+				if (p->state == TASK_STOPPED)
+					/* Stopped task occupy nonused ram */
+					break;
 				if (p->swap_cnt)
 					swap_task++;
 				break;
@@ -322,6 +371,14 @@
 				break;
 		}
 	}
+#ifdef MM_DEBUG
+	if (!priority) {
+		printk("swap_out: physical ram %6dkB, min pages   %6dkB\n",
+			(int)(high_memory>>10), min_free_pages<<(PAGE_SHIFT-10));
+		printk("swap_out:   free pages %6dkB, async pages %6dkB\n",
+			nr_free_pages<<(PAGE_SHIFT-10), nr_async_pages<<(PAGE_SHIFT-10));
+	}
+#endif
 	return 0;
 }
 
@@ -360,6 +417,24 @@
 	return 0;
 }
 
+/*
+ * Before we start the kernel thread, print out the 
+ * kswapd initialization message (otherwise the init message 
+ * may be printed in the middle of another driver's init 
+ * message).  It looks very bad when that happens.
+ */
+void kswapd_setup(void)
+{
+       int i;
+       char *revision="$Revision: 1.4.2.2 $", *s, *e;
+
+       if ((s = strchr(revision, ':')) &&
+           (e = strchr(s, '$')))
+               s++, i = e - s;
+       else
+               s = revision, i = -1;
+       printk ("Starting kswapd v%.*s\n", i, s);
+}
 
 /*
  * The background pageout daemon.
@@ -368,7 +443,6 @@
 int kswapd(void *unused)
 {
 	int i;
-	char *revision="$Revision: 1.4.2.2 $", *s, *e;
 	
 	current->session = 1;
 	current->pgrp = 1;
@@ -394,14 +468,10 @@
 
 	init_swap_timer();
 	
-	if ((s = strchr(revision, ':')) &&
-	    (e = strchr(s, '$')))
-		s++, i = e - s;
-	else
-		s = revision, i = -1;
-	printk ("Started kswapd v%.*s\n", i, s);
-
 	while (1) {
+		/* low on memory, we need to start swapping soon */
+		next_swap_jiffies = jiffies +
+			(last_wakeup_low ? swapout_interval >> 1 : swapout_interval);
 		kswapd_awake = 0;
 		current->signal = 0;
 		run_task_queue(&tq_disk);
@@ -410,7 +480,8 @@
 		swapstats.wakeups++;
 		/* Do the background pageout: */
 		for (i=0; i < kswapd_ctl.maxpages; i++)
-			try_to_free_page(GFP_KERNEL, 0, 0);
+			try_to_free_page(GFP_KERNEL, 0,
+					 (nr_free_pages < min_free_pages));
 	}
 }
 
@@ -421,16 +492,15 @@
 void swap_tick(void)
 {
 	int	want_wakeup = 0;
-	static int	last_wakeup_low = 0;
 
 	if ((nr_free_pages + nr_async_pages) < free_pages_low) {
 		if (last_wakeup_low)
-			want_wakeup = jiffies >= next_swap_jiffies;
+			want_wakeup = (jiffies >= next_swap_jiffies);
 		else
 			last_wakeup_low = want_wakeup = 1;
 	}
 	else if (((nr_free_pages + nr_async_pages) < free_pages_high) && 
-	         jiffies >= next_swap_jiffies) {
+	         (jiffies >= next_swap_jiffies)) {
 		last_wakeup_low = 0;
 		want_wakeup = 1;
 	}
@@ -440,7 +510,6 @@
 			wake_up(&kswapd_wait);
 			need_resched = 1;
 		}
-		next_swap_jiffies = jiffies + swapout_interval;
 	}
 	timer_active |= (1<<SWAP_TIMER);
 }
diff -u --recursive --new-file v2.0.30/linux/net/appletalk/ddp.c linux/net/appletalk/ddp.c
--- v2.0.30/linux/net/appletalk/ddp.c	Thu Nov 14 05:20:10 1996
+++ linux/net/appletalk/ddp.c	Sun Aug  3 15:30:32 1997
@@ -828,7 +828,7 @@
 			else 
 			{
 				limit=ntohs(nr->nr_lastnet);
-				if(limit-ntohs(nr->nr_firstnet) > 256)
+				if(limit-ntohs(nr->nr_firstnet) > 4096)
 				{
 					printk(KERN_WARNING "Too many routes/iface.\n");
 					return -EINVAL;
@@ -1144,6 +1144,7 @@
 	MOD_INC_USE_COUNT;
 
 	sk->no_check=0;		/* Checksums on by default */
+	sk->no_check=1;		/* Checksums off by default */
 	sk->allocation=GFP_KERNEL;
 	sk->rcvbuf=SK_RMEM_MAX;
 	sk->sndbuf=SK_WMEM_MAX;
@@ -1623,6 +1624,67 @@
 	return atalk_rcv(skb,dev,pt);
 }
 
+
+/*
+ *	This is slower, and copies the whole data area 
+ */
+ 
+static struct sk_buff *ddp_skb_copy(struct sk_buff *skb, int priority)
+{
+	struct sk_buff *n;
+	unsigned long offset;
+
+	/*
+	 *	Allocate the copy buffer
+	 */
+	 
+	IS_SKB(skb);
+	
+	n=alloc_skb(skb->truesize-sizeof(struct sk_buff),priority);
+	if(n==NULL)
+		return NULL;
+
+	/*
+	 *	Shift between the two data areas in bytes
+	 */
+	 
+	offset=n->head-skb->head;
+
+	/* Set the data pointer */
+	skb_reserve(n,skb->data-skb->head);
+	/* Set the tail pointer and length */
+	skb_put(n,skb->len);
+	/* Copy the bytes */
+	memcpy(n->head,skb->head,skb->end-skb->head);
+	n->link3=NULL;
+	n->list=NULL;
+	n->sk=NULL;
+	n->when=skb->when;
+	n->dev=skb->dev;
+	n->h.raw=skb->h.raw+offset;
+	n->mac.raw=skb->mac.raw+offset;
+	n->ip_hdr=(struct iphdr *)(((char *)skb->ip_hdr)+offset);
+	n->saddr=skb->saddr;
+	n->daddr=skb->daddr;
+	n->raddr=skb->raddr;
+	n->seq=skb->seq;
+	n->end_seq=skb->end_seq;
+	n->ack_seq=skb->ack_seq;
+	n->acked=skb->acked;
+	memcpy(n->proto_priv, skb->proto_priv, sizeof(skb->proto_priv));
+	n->used=skb->used;
+	n->free=1;
+	n->arp=skb->arp;
+	n->tries=0;
+	n->lock=0;
+	n->users=0;
+	n->pkt_type=skb->pkt_type;
+	n->stamp=skb->stamp;
+	
+	IS_SKB(n);
+	return n;
+}
+
 static int atalk_sendmsg(struct socket *sock, struct msghdr *msg, int len, int nonblock, int flags)
 {
 	atalk_socket *sk=(atalk_socket *)sock->data;
@@ -1763,16 +1825,27 @@
 	{
 		if((!(rt->flags&RTF_GATEWAY))&&(!(dev->flags&IFF_LOOPBACK)))
 		{
-			struct sk_buff *skb2=skb_clone(skb, GFP_KERNEL);
-			if(skb2)
-			{
-				loopback=1;
-				if(sk->debug)
-					printk("SK %p: send out(copy).\n", sk);
-				if(aarp_send_ddp(dev,skb2,&usat->sat_addr, NULL)==-1)
-					kfree_skb(skb2, FREE_WRITE);
-				/* else queued/sent above in the aarp queue */
-			}
+		  struct sk_buff *skb2;
+
+		  /* Make a copy of the skbuf so that the loopback does not trash it
+		   * in the next block.
+		   * Added by Peter Skarpetis, Serendipity Software 24 June 1997
+		   */
+		  skb2 = ddp_skb_copy(skb, GFP_ATOMIC);
+		  if (skb2 == NULL) {
+			printk("ddp.c: cannot allocate skb copy buffer\n");
+			return -1;
+		  }
+		  
+		  if(skb2)
+		    {
+		      loopback=1;
+		      if(sk->debug)
+			printk("SK %p: send out(copy).\n", sk);
+		      if(aarp_send_ddp(dev, skb2, &usat->sat_addr, NULL)==-1)
+			kfree_skb(skb2, FREE_WRITE);
+		      /* else queued/sent above in the aarp queue */
+		    }
 		}
 	}
 
diff -u --recursive --new-file v2.0.30/linux/net/core/dev.c linux/net/core/dev.c
--- v2.0.30/linux/net/core/dev.c	Tue Apr  8 08:47:47 1997
+++ linux/net/core/dev.c	Mon Aug  4 12:27:03 1997
@@ -1427,7 +1427,6 @@
 		case SIOCGIFMTU:
 		case SIOCGIFMEM:
 		case SIOCGIFHWADDR:
-		case SIOCSIFHWADDR:
 		case SIOCGIFSLAVE:
 		case SIOCGIFMAP:
 			return dev_ifsioc(arg, cmd);
@@ -1444,6 +1443,7 @@
 		case SIOCSIFMETRIC:
 		case SIOCSIFMTU:
 		case SIOCSIFMEM:
+		case SIOCSIFHWADDR:
 		case SIOCSIFMAP:
 		case SIOCSIFSLAVE:
 		case SIOCADDMULTI:
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/arp.c linux/net/ipv4/arp.c
--- v2.0.30/linux/net/ipv4/arp.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/arp.c	Mon Aug  4 12:08:23 1997
@@ -1704,7 +1704,7 @@
 	skb->arp = 1;
 	skb->dev = dev;
 	skb->free = 1;
-	skb->protocol = htons (ETH_P_IP);
+	skb->protocol = htons (ETH_P_ARP);
 
 	/*
 	 *	Fill the device header for the ARP frame
@@ -1922,14 +1922,12 @@
 		/*
 		 *	net_alias_dev_rcv_sel32 returns main dev if it fails to found other.
 		 */
-		if (ip_chk_addr(tip) == IS_MYADDR) {
-			dev = net_alias_dev_rcv_sel32(dev, AF_INET, sip, tip);
+		dev = net_alias_dev_rcv_sel32(dev, AF_INET, sip, tip);
 
-			if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
-			{
-				kfree_skb(skb, FREE_READ);
-				return 0;
-			}
+		if (dev->type != ntohs(arp->ar_hrd) || dev->flags & IFF_NOARP)
+		{
+			kfree_skb(skb, FREE_READ);
+			return 0;
 		}
 	}
 #endif
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.0.30/linux/net/ipv4/icmp.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/icmp.c	Sun Aug  3 15:36:43 1997
@@ -831,8 +831,9 @@
 	NETDEBUG(printk(KERN_INFO "icmp: ICMP redirect ignored. dest = %lX, "
 	       "orig gw = %lX, \"new\" gw = %lX, device = %s.\n", ntohl(ip),
 		ntohl(source), ntohl(icmph->un.gateway), dev->name));
+		goto flush_it;
 	}
-#else	
+#endif
 	switch(icmph->code & 7) 
 	{
 		case ICMP_REDIR_NET:
@@ -870,7 +871,7 @@
 		default:
 			break;
   	}
-#endif  	
+
   	/*
   	 *	Discard the original packet
   	 */
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_fragment.c linux/net/ipv4/ip_fragment.c
--- v2.0.30/linux/net/ipv4/ip_fragment.c	Fri Nov 22 01:58:34 1996
+++ linux/net/ipv4/ip_fragment.c	Sun Aug  3 15:22:52 1997
@@ -388,6 +388,8 @@
 		fp = fp->next;
 	}
 
+	skb->pkt_type = qp->fragments->skb->pkt_type;
+	skb->protocol = qp->fragments->skb->protocol;
 	/* We glued together all fragments, so remove the queue entry. */
 	ip_free(qp);
 
@@ -567,7 +569,7 @@
 			else
 				qp->fragments = tmp->next;
 
-			if (tfp->next != NULL)
+			if (tmp->next != NULL)
 				tmp->next->prev = tmp->prev;
 			
 			next=tfp;	/* We have killed the original next frame */
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_fw.c linux/net/ipv4/ip_fw.c
--- v2.0.30/linux/net/ipv4/ip_fw.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/ip_fw.c	Sun Aug  3 15:34:35 1997
@@ -1183,7 +1183,7 @@
 			ntohl(i->fw_dst.s_addr),ntohl(i->fw_dmsk.s_addr),
 			(i->fw_vianame)[0] ? i->fw_vianame : "-",
 			ntohl(i->fw_via.s_addr),i->fw_flg);
-		len+=sprintf(buffer+len,"%u %u %-9lu %-9lu",
+		len+=sprintf(buffer+len,"%u %u %-10lu %-10lu",
 			i->fw_nsp,i->fw_ndp, i->fw_pcnt,i->fw_bcnt);
 		for (p = 0; p < IP_FW_MAX_PORTS; p++)
 			len+=sprintf(buffer+len, " %u", i->fw_pts[p]);
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.0.30/linux/net/ipv4/ip_input.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/ip_input.c	Sun Aug  3 15:27:19 1997
@@ -303,8 +303,7 @@
 
 #ifdef CONFIG_NET_ALIAS
 	if (iph->daddr != skb->dev->pa_addr && net_alias_has(skb->dev)) {
-		if (ip_chk_addr(iph->daddr) == IS_MYADDR)
-			skb->dev = dev = net_alias_dev_rcv_sel32(skb->dev, AF_INET, iph->saddr, iph->daddr);
+		skb->dev = dev = net_alias_dev_rcv_sel32(skb->dev, AF_INET, iph->saddr, iph->daddr);
 	}
 #endif
 
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/ipip.c linux/net/ipv4/ipip.c
--- v2.0.30/linux/net/ipv4/ipip.c	Mon May 13 02:15:24 1996
+++ linux/net/ipv4/ipip.c	Sun Aug  3 14:02:55 1997
@@ -60,6 +60,7 @@
 	 *	Discard the original IP header
 	 */
 	 
+	skb->mac.raw = skb->data;
 	skb_pull(skb, ((struct iphdr *)skb->data)->ihl<<2);
 	
 	/*
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/route.c linux/net/ipv4/route.c
--- v2.0.30/linux/net/ipv4/route.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/route.c	Sun Aug  3 15:22:52 1997
@@ -203,6 +203,7 @@
 			fi->fib_prev->fib_next = fi->fib_next;
 		if (fi == fib_info_list)
 			fib_info_list = fi->fib_next;
+		kfree_s(fi, sizeof(struct fib_info));
 	}
 	kfree_s(f, sizeof(struct fib_node));
 }
@@ -1436,6 +1437,8 @@
 		ip_rt_unlock();
 		kfree_s(rth, sizeof(struct rtable));
 #ifdef CONFIG_KERNELD		
+		if (MULTICAST(daddr)) 
+			return NULL; 
 		daddr=ntohl(daddr);
 		sprintf(wanted_route, "%d.%d.%d.%d",
 			(int)(daddr >> 24) & 0xff, (int)(daddr >> 16) & 0xff,
diff -u --recursive --new-file v2.0.30/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.0.30/linux/net/ipv4/tcp_input.c	Tue Apr  8 08:47:47 1997
+++ linux/net/ipv4/tcp_input.c	Mon Aug  4 12:28:05 1997
@@ -266,6 +266,7 @@
 		if(sk->daddr		== saddr		&& /* remote address */
 		   sk->dummy_th.dest	== sport		&& /* remote port    */
 		   sk->num		== hnum			&& /* local port     */
+		   sk->state 		!= TCP_CLOSE		&& /* comment above is false */
 		   sk->rcv_saddr	== daddr)		   /* local address  */
 			goto hit; /* You sunk my battleship! */
 	sk = tcp_v4_lookup_longway(daddr, hnum);
diff -u --recursive --new-file v2.0.30/linux/net/ipx/af_ipx.c linux/net/ipx/af_ipx.c
--- v2.0.30/linux/net/ipx/af_ipx.c	Tue Nov 26 23:44:21 1996
+++ linux/net/ipx/af_ipx.c	Mon Aug  4 08:42:09 1997
@@ -468,7 +468,60 @@
 	ipx_socket	*sock1 = NULL, *sock2 = NULL;
 	struct sk_buff	*skb1 = NULL, *skb2 = NULL;
 
-	sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);
+	if (intrfc == ipx_primary_net
+	  && ntohs(ipx->ipx_dest.sock) == 0x451) 
+	{
+	  /* 
+	   * The packet's target is a NCP connection handler. We want to
+	   * hand it to the correct socket directly within the kernel,
+	   * so that the mars_nwe packet distribution process
+	   * does not have to do it. Here we only care about NCP and
+	   * BURST packets.
+	   * You might call this a hack, but believe me, you do not
+	   * want a complete NCP layer in the kernel, and this is
+	   * VERY fast as well.
+	   */
+	  int connection = 0;
+
+	  if (    *((char*)(ipx+1))   == 0x22
+	      &&  *((char*)(ipx+1)+1) == 0x22) 
+	  {
+	  	/*
+		 * The packet is a NCP request
+		 */
+		 connection = ( ((int) *((char*)(ipx+1)+5)) << 8 )
+		 	       | (int) *((char*)(ipx+1)+3);
+	  } 
+	  else if (    *((char*)(ipx+1))   == 0x77
+		   &&  *((char*)(ipx+1)+1) == 0x77) 
+	  {
+		/*
+		 * The packet is a BURST packet
+		 */
+		 connection = ( ((int) *((char*)(ipx+1)+9)) << 8 )
+		 	       | (int) *((char*)(ipx+1)+8);
+	  }
+
+          if (connection) 
+	  {
+	    /*
+	     * Now we have to look for a special NCP connection handling
+	     * socket. Only these sockets have ipx_ncp_conn != 0, set
+	     * by SIOCIPXNCPCONN.
+	     */
+            for (sock1=intrfc->if_sklist;
+		(sock1 != NULL) &&
+		(sock1->protinfo.af_ipx.ipx_ncp_conn != connection);
+		sock1=sock1->next);;
+          }
+        }
+        if (sock1 == NULL) 
+	{
+		/* No special socket found, forward the packet the
+		 * normal way.
+		 */
+		sock1 = ipxitf_find_socket(intrfc, ipx->ipx_dest.sock);
+	}
 
 	/*
 	 *	We need to check if there is a primary net and if
@@ -1723,6 +1776,7 @@
 	}
 	sk->rcvbuf=SK_RMEM_MAX;
 	sk->sndbuf=SK_WMEM_MAX;
+	sk->allocation=GFP_KERNEL;
 	sk->prot=NULL;	/* So we use default free mechanisms */
 	skb_queue_head_init(&sk->receive_queue);
 	skb_queue_head_init(&sk->write_queue);
@@ -2242,6 +2296,21 @@
 			if(err) return err;
 			return(ipxcfg_get_config_data((void *)arg));
 		}
+
+		case SIOCIPXNCPCONN:
+                {
+		  /*
+		   * This socket wants to take care of the NCP connection
+		   * handed to us in arg.
+		   */
+                  if (!suser()) return(-EPERM);
+                  err = verify_area(VERIFY_READ, (void *)arg,
+                                sizeof(unsigned short));
+                  if (err) return err;
+                  sk->protinfo.af_ipx.ipx_ncp_conn = get_fs_word(arg);
+                  return 0;
+                }
+
 		case SIOCGSTAMP:
 			if (sk)
 			{
diff -u --recursive --new-file v2.0.30/linux/scripts/Configure linux/scripts/Configure
--- v2.0.30/linux/scripts/Configure	Tue Apr  8 08:47:48 1997
+++ linux/scripts/Configure	Sun Aug  3 15:38:39 1997
@@ -14,7 +14,7 @@
 # Menuconfig or xconfig, please notify the respective authors so that 
 # those utilities can be updated in parallel.
 #
-# Menuconfig:  <roadcapw@cfw.com>
+# Menuconfig:  <roadcapw@titus.org>
 # xconfig:     <apenwarr@foxnet.net>  <eric@aib.com>
 #              ****************************************
 #
@@ -46,6 +46,9 @@
 # 090397 Axel Boldt (boldt@math.ucsb.edu) - avoid ? and + in regular 
 # expressions for GNU expr since version 1.15 and up use \? and \+.
 
+# 040697 Larry Augustin (lma@varesearch.com) - integer expr test
+# fails with GNU expr 1.12. Re-write to work with new and old expr.
+
 #
 # Make sure we're really running bash.
 #
@@ -285,13 +288,20 @@
 # int processes an integer argument
 #
 #	int question define default
-#
+# GNU expr changed handling of ?.  In older versions you need ?,
+# in newer you need \?
+OLD_EXPR=`expr "0" : '0\?'`
+if [ $OLD_EXPR -eq 1 ]; then
+    INT_EXPR='0$\|-\?[1-9][0-9]*$'
+else
+    INT_EXPR='0$\|-?[1-9][0-9]*$'
+fi
 function int () {
 	old=$(eval echo "\${$2}")
 	def=${old:-$3}
 	while :; do
 	  readln "$1 ($2) [$def] " "$def" "$old"
-	  if expr "$ans" : '0$\|\(-[1-9]\|[1-9]\)[0-9]*$' > /dev/null; then
+	  if expr "$ans" : $INT_EXPR > /dev/null; then
 	    define_int "$2" "$ans"
 	    break
 	  else
diff -u --recursive --new-file v2.0.30/linux/scripts/Menuconfig linux/scripts/Menuconfig
--- v2.0.30/linux/scripts/Menuconfig	Tue Oct 29 07:38:17 1996
+++ linux/scripts/Menuconfig	Sun Aug  3 15:38:39 1997
@@ -19,17 +19,10 @@
 # Portions of this script were borrowed from the original Configure
 # script.
 #
-# Please send comments / questions / bug fixes to roadcapw@cfw.com
+# Please send comments / questions / bug fixes to roadcapw@titus.org
 #
 #----------------------------------------------------------------------------
 
-
-#
-# Change this to TRUE if you prefer all kernel options listed
-# in a single menu rather than the standard menu hierarchy.
-#
-single_menu_mode=
-
 #
 # Make sure we're really running bash.
 #
@@ -41,7 +34,12 @@
 set -h +o posix
 
 
-
+#
+# If you prefer all kernel options listed in a single menu rather than
+# the standard menu hierarchy, set SINGLE_MENU_MODE to "TRUE" in your
+# environment.
+#
+single_menu_mode="${SINGLE_MENU_MODE:-FALSE}"
 
 
 #
@@ -400,7 +398,8 @@
 			answer="`cat MCdialog.out`"
 			answer="${answer:-$3}"
 
-			if expr $answer : '0$\|-?[1-9][0-9]*$' >/dev/null
+			# Avoid problems with GNU vs POSIX expr semantics.
+			if expr "$answer" : '0$\|-[1-9][0-9]*$\|[1-9][0-9]*$' >/dev/null
 			then
 				eval $2="$answer"
 			else
@@ -433,7 +432,8 @@
 			answer="${answer:-$3}"
 			answer="${answer##*[x,X]}"
 
-			if expr $answer : '[0-9a-fA-F]+$' >/dev/null
+			# Avoid problems with GNU vs POSIX expr semantics.
+			if expr "$answer" : '[0-9a-fA-F][0-9a-fA-F]*$' >/dev/null
 			then
 				eval $2="$answer"
 			else
@@ -452,7 +452,7 @@
 }
 
 #
-# Handle a on-of-many choice list.
+# Handle a one-of-many choice list.
 #
 function l_choice () {
 	#
@@ -493,7 +493,7 @@
 	done
 
 	#
-	# Now set the boolean value of each option base on
+	# Now set the boolean value of each option based on
 	# the selection made from the radiolist.
 	#
 	set -- $choices
@@ -579,7 +579,7 @@
 		if ($1 ~ /mainmenu_option|endmenu/) {
 			printf("") >>menu
 		} 
-		else if ($0 ~ /^#|$MAKE|mainmenu_name/) {
+		else if ($0 ~ /^#|\$MAKE|mainmenu_name/) {
 			printf("") >>menu
 		}
 		else if ($1 == "source") {
@@ -655,7 +655,7 @@
 Menuconfig has encountered a possible error in one of the kernel's
 configuration files and is unable to continue.
 
-Please report this to the author <roadcapw@cfw.com>.  You may also
+Please report this to the author <roadcapw@titus.org>.  You may also
 send a problem report to linux-kernel@vger.rutgers.edu or post a
 message to the linux.dev.kernel news group.
 
@@ -718,7 +718,7 @@
 "make clean all" command.
 
 If you have verified that your ncurses install is correct, you may email
-the author <roadcapw@cfw.com> or post a message on the linux.dev.kernel
+the author <roadcapw@titus.org> or post a message on the linux.dev.kernel
 news group for additional assistance. 
 
 EOM
@@ -746,7 +746,7 @@
 
 	while true
 	do
-		ALT_CONFIG="${ALT_CONFIG:-$DEFAULTS}"
+		ALT_CONFIG="${ALT_CONFIG:-$_CONFIG}"
 
 		$DIALOG --backtitle "$backtitle" \
 			--inputbox "\
@@ -812,6 +812,7 @@
 			
 	while true
 	do
+
 		$DIALOG --backtitle "$backtitle" \
 			--inputbox "\
 Enter a filename to which this configuration should be saved \
@@ -858,20 +859,19 @@
 	rm -f help.out MCdialog.out
 }
 
+
+#
+# Load config file into the environment converting all
+# "# OPTION is not set" lines to "OPTION=n".
 #
-# Load config options from a file.
-# Converts all "# OPTION is not set" lines to "OPTION=n" lines
+# The $ARCH defaults are loaded first so "new"/previously 
+# unconfigured parameters are assigned the proper defaults.
 #
 function load_config_file () {
-	awk '
-	  /# .* is not set.*/ { printf("%s=n\n", $2) }
-	! /# .* is not set.*/ { print }
-	' $1 >.tmpconfig
-
-	source ./.tmpconfig
-	rm -f .tmpconfig
+	eval "`sed -e 's/# \(.*\) is not set.*/\1=n/' arch/$ARCH/defconfig $1`"
 }
 
+
 #
 # Just what it says.
 #
@@ -1007,8 +1007,8 @@
 		fi
 	}
 
-	DEF_CONFIG="${1:-.config}"
-	DEF_CONFIG_H="include/linux/autoconf.h"
+	DEF_CONFIG="${1:-$_CONFIG}"
+	DEF_CONFIG_H="$AUTOCONF_H"
 
 	CONFIG=.tmpconfig
 	CONFIG_H=.tmpconfig.h
@@ -1025,7 +1025,11 @@
 	
 	if . $CONFIG_IN >>.menuconfig.log 2>&1
 	then
-		if [ "$DEF_CONFIG" = ".config" ]
+		#
+		# Skip these steps if we are saving to an
+		# alternate configuration file.
+		#
+		if [ "$DEF_CONFIG" = "$_CONFIG" ]
 		then
 			#
 			# Create the sound driver's config files for cards
@@ -1055,12 +1059,14 @@
 	fi
 }
 
+
 #
 # Remove temporary files
 #
 cleanup () {
 	cleanup1
 	cleanup2
+	stty $S_TERMIO
 }
 
 cleanup1 () {
@@ -1093,6 +1099,7 @@
 	ROWS=$((ROWS-4))  COLS=$((COLS-5))
 }
 
+S_TERMIO=`stty -g`
 
 set_geometry `stty size 2>/dev/null`
 
@@ -1111,7 +1118,7 @@
 Press <?> for additional information about this option."
 
 inputbox_instructions_int="\
-Please enter a decimal value between 1 and 9999. \
+Please enter a decimal value. \
 Fractions will not be accepted.  \
 Use the <TAB> key to move from the input field to the buttons below it."
 
@@ -1119,7 +1126,7 @@
 Please enter a hexadecimal value. \
 Use the <TAB> key to move from the input field to the buttons below it."
 
-DIALOG="./scripts/lxdialog/lxdialog"
+DIALOG="${DIALOG:-./scripts/lxdialog/lxdialog}"
 
 kernel_version="${VERSION}.${PATCHLEVEL}.${SUBLEVEL}"
 
@@ -1131,29 +1138,28 @@
 #
 # Locate default files.
 #
-CONFIG_IN=./config.in
-if [ "$1" != "" ] ; then
-	CONFIG_IN=$1
-fi
+DEFAULTS="arch/$ARCH/defconfig"
+
+CONFIG_IN="${1:-./config.in}"
 
-DEFAULTS=arch/$ARCH/defconfig
-if [ -f .config ]; then
-  DEFAULTS=.config
+_CONFIG="${2:-.config}"
+
+if [ ! -f "$_CONFIG" ]; then
+  _CONFIG=$DEFAULTS
 fi
 
-if [ -f $DEFAULTS ]
+AUTOCONF_H="${3:-include/linux/autoconf.h}"
+
+if [ -f $_CONFIG ]
 then
-  echo "#"
-  echo "# Using defaults found in" $DEFAULTS
-  echo "#"
-  load_config_file $DEFAULTS
+  echo
+  echo "Using defaults found in" $_CONFIG
+  load_config_file $_CONFIG
 else
-  echo "#"
-  echo "# No defaults found"
-  echo "#"
+  echo
+  echo "No defaults found"
 fi
 
-
 # Fresh new log.
 >.menuconfig.log
 
@@ -1211,8 +1217,9 @@
 	   
 then
 	save_configuration
-
+	stty $S_TERMIO
 	clear
+
 	cat <<EOM
 
 
@@ -1224,6 +1231,7 @@
 EOM
 else
 	clear
+	stty $S_TERMIO
 	echo -e "Your kernel configuration changes were NOT saved.\n"
 fi
 
diff -u --recursive --new-file v2.0.30/linux/scripts/README.Menuconfig linux/scripts/README.Menuconfig
--- v2.0.30/linux/scripts/README.Menuconfig	Thu Apr 18 04:38:54 1996
+++ linux/scripts/README.Menuconfig	Sun Aug  3 15:38:39 1997
@@ -171,10 +171,11 @@
 
 ******** IMPORTANT, OPTIONAL ALTERNATE PERSONALITY AVAILABLE ********
 ********                                                     ********
-If you prefer to have all of the kernel options listed in a single
-menu, rather than the default multimenu hierarchy, you may edit the
-Menuconfig script and change the line "single_menu_mode="  to 
-"single_menu_mode=TRUE".
+If you prefer having all of the kernel options listed in a single
+menu rather than the default multimenu hierarchy, place the following
+line in your login scripts. (ie. .profile, .bashrc, etc...)
+
+export SINGLE_MENU_MODE=TRUE
 
 This mode is not recommended unless you have a fairly fast machine.
 *********************************************************************
@@ -189,6 +190,6 @@
 He will not be able to assist.
 
 Please feel free to send any questions, comments or suggestions to
-William Roadcap <roadcapw@cfw.com>.
+William Roadcap <roadcapw@titus.org>.
 
 <END OF FILE>
diff -u --recursive --new-file v2.0.30/linux/scripts/lxdialog/lxdialog.c linux/scripts/lxdialog/lxdialog.c
--- v2.0.30/linux/scripts/lxdialog/lxdialog.c	Sun Feb 25 01:17:58 1996
+++ linux/scripts/lxdialog/lxdialog.c	Sun Aug  3 15:38:39 1997
@@ -119,7 +119,8 @@
     if (modePtr->argmax && argc - offset > modePtr->argmax)
 	Usage (argv[0]);
 
-
+    if (title && strlen(title) > atoi(argv[offset+4])-4)
+	title[atoi(argv[offset+4])-4] = 0;
 
     init_dialog ();
     retval = (*(modePtr->jumper)) (title, argc - offset, argv + offset);
