## Automatically generated incremental diff
## From:   linux-2.4.3-pre1
## To:     linux-2.4.3-pre2
## Robot:  $Id: make-incremental-diff,v 1.8 2001/12/04 06:05:04 hpa Exp $

diff -urN linux-2.4.3-pre1/CREDITS linux/CREDITS
--- linux-2.4.3-pre1/CREDITS	Mon Dec  3 22:29:33 2001
+++ linux/CREDITS	Mon Dec  3 22:29:47 2001
@@ -679,12 +679,8 @@
 
 N: Cort Dougan
 E: cort@fsmlabs.com
-W: http://www.ppc.kernel.org/~cort/
+W: http://www.fsmlabs.com/linuxppcbk.html
 D: PowerPC
-S: Finite State Machine Labs
-S: P.O. 1829
-S: Socorro, New Mexico 87801
-S: USA
 
 N: Oleg Drokin
 E: green@ccssu.crimea.ua
@@ -1026,11 +1022,11 @@
 S: Canada
 
 N: Richard Günther
-E: richard.guenther@student.uni-tuebingen.de
+E: rguenth@tat.physik.uni-tuebingen.de
+W: http://www.tat.physik.uni-tuebingen.de/~rguenth
 P: 2048/2E829319 2F 83 FC 93 E9 E4 19 E2  93 7A 32 42 45 37 23 57
 D: binfmt_misc
-S: Fichtenweg 3/511
-S: 72076 Tübingen
+S: 72074 Tübingen
 S: Germany
 
 N: Justin Guyett
@@ -1347,9 +1343,7 @@
 
 N: Dave Jones
 E: davej@suse.de
-E: dave@powertweak.com
-E: djones2@glam.ac.uk
-W: http://powertweak.sourceforge.net
+W: http://www.suse.de/~davej
 D: Moved PCI bridge tuning to userspace (Powertweak).
 D: Centaur/IDT Winchip/Winchip 2 tweaks.
 D: AFFS fixes for 2.3.x
@@ -2191,15 +2185,15 @@
 S: USA
 
 N: Kai Petzke
-E: wpp@marie.physik.tu-berlin.de
-W: http://physik.tu-berlin.de/~wpp
-P: 1024/B42868C1 D9 59 B9 98 BB 93 05 38  2E 3E 31 79 C3 65 5D E1 
+E: petzke@teltarif.de
+W: http://www.teltarif.de/
+P: 1024/B42868C1 D9 59 B9 98 BB 93 05 38  2E 3E 31 79 C3 65 5D E1
 D: Driver for Laser Magnetic Storage CD-ROM
 D: Some kernel bug fixes
 D: Port of the database Postgres
-D: "Unix fuer Jedermann" a German introduction to linux (see my web page)
-S: M"ullerstr. 69
-S: 13349 Berlin
+D: Book: "Linux verstehen und anwenden" (Hanser-Verlag)
+S: Triftstra=DFe 55
+S: 13353 Berlin
 S: Germany
 
 N: Emanuel Pirker
diff -urN linux-2.4.3-pre1/Documentation/Configure.help linux/Documentation/Configure.help
--- linux-2.4.3-pre1/Documentation/Configure.help	Mon Feb 19 10:18:18 2001
+++ linux/Documentation/Configure.help	Mon Dec  3 22:29:49 2001
@@ -5469,6 +5469,45 @@
 
 Adaptec AIC7xxx chipset SCSI controller support
 CONFIG_SCSI_AIC7XXX
+  This driver supports all of Adaptec's PCI based SCSI controllers (not
+  the hardware RAID controllers though) as well as the aic7770 based
+  EISA and VLB SCSI controllers (the 274x and 284x series).  This is
+  an Adaptec sponsored driver written by Justin Gibbs.  It is intended
+  to replace the previous aic7xxx driver maintained by Doug Ledford since
+  Doug is no longer maintaining that driver.
+
+Default number of TCQ commands per device
+CONFIG_AIC7XXX_CMDS_PER_DEVICE
+  Specify the number of commands you would like to allocate per SCSI
+  device when Tagged Command Queueing (TCQ) is enabled on that device.
+
+  This is an upper bound value for the number of tagged transactions
+  to be used for any device.  The aic7xxx driver will automatically
+  vary this number based on device behavior.  For devices with a
+  fixed maximum, the driver will eventually lock to this maximum
+  and display a console message inidicating this value.
+
+  Note: Unless you experience some type of device failure, the default
+	value, no enforced limit, should work for you.
+
+  Default: 253
+
+Initial Bus Reset Settle Delay
+CONFIG_AIC7XXX_RESET_DELAY
+  The number of milliseconds to delay after an initial bus reset.
+  The bus settle delay following all error recovery actions is
+  dictated by the SCSI layer and is not affected by this value.
+
+  Default: 5000 (5 seconds)
+
+Old Adaptec AIC7xxx chipset SCSI controller support
+CONFIG_SCSI_AIC7XXX_OLD
+  WARNING This driver is an older aic7xxx driver and is no longer under
+  active development.  Adaptec, Inc. is writing a new driver to take the
+  place of this one, and it is recommended that whenever possible, people
+  should use the new Adaptec written driver instead of this one.  This
+  driver will eventually be phased out entirely.
+
   This is support for the various aic7xxx based Adaptec SCSI
   controllers. These include the 274x EISA cards; 284x VLB cards;
   2902, 2910, 293x, 294x, 394x, 3985 and several other PCI and
@@ -5490,7 +5529,7 @@
 
   Information on the configuration options for this controller can be
   found by checking the help file for each of the available
-  configuration options. You should read drivers/scsi/README.aic7xxx
+  configuration options. You should read drivers/scsi/aic7xxx_old/README.aic7xxx
   at a minimum before contacting the maintainer with any questions.
   The SCSI-HOWTO, available from
   http://www.linuxdoc.org/docs.html#howto , can also be of great
@@ -5499,10 +5538,10 @@
   If you want to compile this driver as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
   say M here and read Documentation/modules.txt. The module will be
-  called aic7xxx.o.
+  called aic7xxx_old.o.
 
 Enable or Disable Tagged Command Queueing by default
-CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
+CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT
   This option causes the aic7xxx driver to attempt to use Tagged
   Command Queueing (TCQ) on all devices that claim to support it. 
 
@@ -5536,7 +5575,7 @@
   reduce performance.
 
 Default number of TCQ commands per device
-CONFIG_AIC7XXX_CMDS_PER_DEVICE
+CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE
   Specify the number of commands you would like to allocate per SCSI
   device when Tagged Command Queueing (TCQ) is enabled on that device.
 
@@ -5557,7 +5596,7 @@
   Default: 8
 
 Collect statistics to report in /proc
-CONFIG_AIC7XXX_PROC_STATS
+CONFIG_AIC7XXX_OLD_PROC_STATS
   This option tells the driver to keep track of how many commands have
   been sent to each particular device and report that information to
   the user via the /proc/scsi/aic7xxx/n file, where n is the number of
@@ -5569,21 +5608,6 @@
 
   If unsure, say N.
 
-Delay in seconds after SCSI bus reset
-CONFIG_AIC7XXX_RESET_DELAY
-  This sets how long the driver will wait after resetting the SCSI bus
-  before attempting to communicate with the devices on the SCSI bus
-  again. This delay will be used during the reset phase at bootup time
-  as well as after any reset that might occur during normal operation.
-  Reasonable numbers range anywhere from 5 to 15 seconds depending on
-  your devices. DAT tape drives are notorious for needing more time
-  after a bus reset to be ready for the next command, but most hard
-  drives and CD-ROM devices are ready in only a few seconds. This
-  option has a maximum upper limit of 20 seconds to avoid bad
-  interactions between the aic7xxx driver and the rest of the Linux
-  kernel. The default value has been reduced to 5 seconds. If this
-  doesn't work with your hardware, try increasing this value.
-
 IBM ServeRAID Support
 CONFIG_SCSI_IPS
   This is support for the IBM ServeRAID hardware RAID controllers.
@@ -8390,22 +8414,6 @@
   module, say M here and read Documentation/modules.txt as well as
   Documentation/networking/net-modules.txt.
 
-RealTek 8129 (not 8019/8029/8139!) support (EXPERIMENTAL)
-CONFIG_RTL8129
-  This is NOT for RTL-8139 cards.  Instead, select the 8139too driver
-  (CONFIG_8139TOO).
-  This is a driver for the Fast Ethernet PCI network cards based on
-  the RTL8129 chip. If you have one of those, say Y and
-  read the Ethernet-HOWTO, available from
-  http://www.linuxdoc.org/docs.html#howto .
-
-  Note: the 8029 is a NE2000 PCI clone, you can use the NE2K-PCI driver.
-
-  If you want to compile this driver as a module ( = code which can be
-  inserted in and removed from the running kernel whenever you want),
-  say M here and read Documentation/modules.txt. This is recommended.
-  The module will be called rtl8129.o.
-
 RealTek RTL-8139 PCI Fast Ethernet Adapter support
 CONFIG_8139TOO
   This is a driver for the Fast Ethernet PCI network cards based on
@@ -8481,7 +8489,7 @@
   If you want to compile this driver as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want),
   say M here and read Documentation/modules.txt. This is recommended.
-  The module will be called starfile.o.
+  The module will be called starfire.o.
   
 Alteon AceNIC/3Com 3C985/NetGear GA620 Gigabit support
 CONFIG_ACENIC
@@ -9399,7 +9407,7 @@
 
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
-  The module will will be called olympic.o. If you want to compile it
+  The module will be called olympic.o. If you want to compile it
   as a module, say M here and read Documentation/modules.txt.
 
   Also read the file Documentation/networking/olympic.txt or check the
@@ -9439,7 +9447,7 @@
 
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
-  The module will will be called tms380tr.o. If you want to compile it
+  The module will be called tms380tr.o. If you want to compile it
   as a module, say M here and read Documentation/modules.txt.
 
 Generic TMS380 PCI support
@@ -9454,7 +9462,7 @@
 
   This driver is available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
-  The module will will be called tmspci.o. If you want to compile it
+  The module will be called tmspci.o. If you want to compile it
   as a module, say M here and read Documentation/modules.txt.
 
 Madge Smart 16/4 PCI Mk2 support
@@ -9464,7 +9472,7 @@
 
   This driver is available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
-  The module will will be called abyss.o. If you want to compile it
+  The module will be called abyss.o. If you want to compile it
   as a module, say M here and read Documentation/modules.txt.
 
 Madge Smart 16/4 Ringode MicroChannel 
@@ -9474,7 +9482,7 @@
 
   This driver is available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
-  The module will will be called madgemc.o. If you want to compile it
+  The module will be called madgemc.o. If you want to compile it
   as a module, say M here and read Documentation/modules.txt.
 
 SMC ISA TokenRing adapter support
@@ -9490,7 +9498,7 @@
 
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
-  The module will will be called smctr.o. If you want to compile it
+  The module will be called smctr.o. If you want to compile it
   as a module, say M here and read Documentation/modules.txt.
 
 Sun Happy Meal 10/100baseT support
diff -urN linux-2.4.3-pre1/Documentation/DMA-mapping.txt linux/Documentation/DMA-mapping.txt
--- linux-2.4.3-pre1/Documentation/DMA-mapping.txt	Mon Dec 11 13:45:42 2000
+++ linux/Documentation/DMA-mapping.txt	Mon Dec  3 22:29:49 2001
@@ -59,7 +59,7 @@
 1) Use some non-DMA mode for data transfer, if possible.
 2) Ignore this device and do not initialize it.
 
-It is recommended that your driver print a kernel KERN_WARN message
+It is recommended that your driver print a kernel KERN_WARNING message
 when you do one of these two things.  In this manner, if a user of
 your driver reports that performance is bad or that the device is not
 even detected, you can ask him for the kernel messages to find out
diff -urN linux-2.4.3-pre1/Documentation/DocBook/kernel-api.tmpl linux/Documentation/DocBook/kernel-api.tmpl
--- linux-2.4.3-pre1/Documentation/DocBook/kernel-api.tmpl	Sat Dec 30 18:16:13 2000
+++ linux/Documentation/DocBook/kernel-api.tmpl	Mon Dec  3 22:29:49 2001
@@ -34,6 +34,18 @@
  </bookinfo>
 
 <toc></toc>
+
+  <chapter id="Basics">
+     <title>Driver Basic</title>
+     <sect1><title>Driver Entry and Exit points</title>
+!Iinclude/linux/init.h
+     </sect1>
+
+     <sect1><title>Atomics</title>
+!Iinclude/asm-i386/atomic.h
+     </sect1>
+  </chapter>
+
   <chapter id="adt">
      <title>Data Types</title>
      <sect1><title>Doubly Linked Lists</title>
diff -urN linux-2.4.3-pre1/Documentation/binfmt_misc.txt linux/Documentation/binfmt_misc.txt
--- linux-2.4.3-pre1/Documentation/binfmt_misc.txt	Tue Apr 28 14:22:03 1998
+++ linux/Documentation/binfmt_misc.txt	Mon Dec  3 22:29:49 2001
@@ -81,6 +81,6 @@
 
 
 There is a web page about binfmt_misc at
-http://www.anatom.uni-tuebingen.de/~richi/linux/binfmt_misc.html
+http://www.tat.physik.uni-tuebingen.de/~rguenth/linux/binfmt_misc.html
 
-Richard Günther, richard.guenther@student.uni-tuebingen.de
+Richard Günther <rguenth@tat.physik.uni-tuebingen.de>
diff -urN linux-2.4.3-pre1/Documentation/dnotify.txt linux/Documentation/dnotify.txt
--- linux-2.4.3-pre1/Documentation/dnotify.txt	Tue Nov 14 10:54:07 2000
+++ linux/Documentation/dnotify.txt	Mon Dec  3 22:29:49 2001
@@ -28,7 +28,7 @@
 information.  However, if the F_SETSIG fcntl(2) call is used to let the
 kernel know which signal to deliver, a siginfo structure will be passed to
 the signal handler and the si_fd member of that structure will contain the
-file descriptor associated with the direcory in which the event occured.
+file descriptor associated with the directory in which the event occurred.
 
 Preferably the application will choose one of the real time signals
 (SIGRTMIN + <n>) so that the notifications may be queued.  This is
diff -urN linux-2.4.3-pre1/Documentation/ioctl-number.txt linux/Documentation/ioctl-number.txt
--- linux-2.4.3-pre1/Documentation/ioctl-number.txt	Tue Feb 13 14:13:42 2001
+++ linux/Documentation/ioctl-number.txt	Mon Dec  3 22:29:49 2001
@@ -92,6 +92,7 @@
 					<http://linux01.gwdg.de/~alatham/ppdd.html>
 'M'	all	linux/soundcard.h	conflict!
 'M'	00-1F	linux/isicom.h		conflict!
+'N'	00-1F	drivers/usb/scanner.h
 'P'	all	linux/soundcard.h
 'Q'	all	linux/soundcard.h
 'R'	00-1F	linux/random.h
diff -urN linux-2.4.3-pre1/Documentation/isdn/INTERFACE linux/Documentation/isdn/INTERFACE
--- linux-2.4.3-pre1/Documentation/isdn/INTERFACE	Mon Dec  3 22:29:33 2001
+++ linux/Documentation/isdn/INTERFACE	Mon Dec  3 22:29:49 2001
@@ -481,7 +481,7 @@
       driver      = driver-Id
       command     = ISDN_CMD_PROT_IO
       arg         = The lower 8 Bits define the addressed protocol as defined
-                    in ISDN_PTYPE..., the upper bits are used to differenciate
+                    in ISDN_PTYPE..., the upper bits are used to differentiate
                     the protocol specific CMD.  
       
       para        = protocol and function specific. See isdnif.h for detail.
diff -urN linux-2.4.3-pre1/Documentation/kernel-doc-nano-HOWTO.txt linux/Documentation/kernel-doc-nano-HOWTO.txt
--- linux-2.4.3-pre1/Documentation/kernel-doc-nano-HOWTO.txt	Mon Jun 19 12:56:07 2000
+++ linux/Documentation/kernel-doc-nano-HOWTO.txt	Mon Dec  3 22:29:49 2001
@@ -57,7 +57,8 @@
 If you want to see man pages instead, you can do this:
 
 $ cd linux
-$ scripts/kernel-doc -man $(find -name '*.c' '*.h') | split-man.pl /tmp/man
+$ scripts/kernel-doc -man $(find -name '*.c') | split-man.pl /tmp/man
+$ scripts/kernel-doc -man $(find -name '*.h') | split-man.pl /tmp/man
 
 Here is split-man.pl:
 
@@ -68,7 +69,7 @@
    die "where do I put the results?\n";
 }
 
-mkdir $ARGV[0],0777 or die "Can't create $ARGV[0]: $!\n";
+mkdir $ARGV[0],0777;
 $state = 0;
 while (<STDIN>) {
     if (/^\.TH \"[^\"]*\" 4 \"([^\"]*)\"/) {
diff -urN linux-2.4.3-pre1/Documentation/powerpc/ppc_htab.txt linux/Documentation/powerpc/ppc_htab.txt
--- linux-2.4.3-pre1/Documentation/powerpc/ppc_htab.txt	Wed Mar 10 21:49:10 1999
+++ linux/Documentation/powerpc/ppc_htab.txt	Mon Dec  3 22:29:49 2001
@@ -2,7 +2,7 @@
 =====================================================================
 
 This document and the related code was written by me (Cort Dougan), please
-email me (cort@cs.nmt.edu) if you have questions, comments or corrections.
+email me (cort@fsmlabs.com) if you have questions, comments or corrections.
 
 Last Change: 2.16.98
 
diff -urN linux-2.4.3-pre1/Documentation/powerpc/smp.txt linux/Documentation/powerpc/smp.txt
--- linux-2.4.3-pre1/Documentation/powerpc/smp.txt	Thu Apr 29 12:39:07 1999
+++ linux/Documentation/powerpc/smp.txt	Mon Dec  3 22:29:49 2001
@@ -2,7 +2,7 @@
 =====================================================================
 
 This document and the related code was written by me
-(Cort Dougan, cort@cs.nmt.edu) please email me if you have questions,
+(Cort Dougan, cort@fsmlabs.com) please email me if you have questions,
 comments or corrections.
 
 Last Change: 3.31.99
diff -urN linux-2.4.3-pre1/Documentation/powerpc/sound.txt linux/Documentation/powerpc/sound.txt
--- linux-2.4.3-pre1/Documentation/powerpc/sound.txt	Mon Jun 28 13:40:39 1999
+++ linux/Documentation/powerpc/sound.txt	Mon Dec  3 22:29:49 2001
@@ -1,7 +1,7 @@
             Information about PowerPC Sound support
 =====================================================================
 
-Please mail me (Cort Dougan, cort@cs.nmt.edu) if you have questions,
+Please mail me (Cort Dougan, cort@fsmlabs.com) if you have questions,
 comments or corrections.
 
 Last Change: 6.16.99
diff -urN linux-2.4.3-pre1/Documentation/powerpc/zImage_layout.txt linux/Documentation/powerpc/zImage_layout.txt
--- linux-2.4.3-pre1/Documentation/powerpc/zImage_layout.txt	Sat Oct 10 09:53:24 1998
+++ linux/Documentation/powerpc/zImage_layout.txt	Mon Dec  3 22:29:49 2001
@@ -1,7 +1,7 @@
           Information about the Linux/PPC kernel images
 =====================================================================
 
-Please mail me me (Cort Dougan, cort@cs.nmt.edu) if you have questions,
+Please mail me (Cort Dougan, cort@fsmlabs.com) if you have questions,
 comments or corrections.
 
 This document is meant to answer several questions I've had about how
diff -urN linux-2.4.3-pre1/MAINTAINERS linux/MAINTAINERS
--- linux-2.4.3-pre1/MAINTAINERS	Mon Feb 19 10:18:18 2001
+++ linux/MAINTAINERS	Mon Dec  3 22:29:49 2001
@@ -745,7 +745,7 @@
 LINUX FOR POWERPC
 P:	Cort Dougan
 M:	cort@fsmlabs.com
-W:	http://www.ppc.kernel.org/
+W:	http://www.fsmlabs.com/linuxppcbk.html
 S:	Maintained
 
 LINUX FOR POWER MACINTOSH
diff -urN linux-2.4.3-pre1/Makefile linux/Makefile
--- linux-2.4.3-pre1/Makefile	Mon Dec  3 22:29:33 2001
+++ linux/Makefile	Mon Dec  3 22:29:49 2001
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 3
-EXTRAVERSION =-pre1
+EXTRAVERSION =-pre2
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
@@ -144,6 +144,7 @@
 DRIVERS-$(CONFIG_ATM) += drivers/atm/atm.o
 DRIVERS-$(CONFIG_IDE) += drivers/ide/idedriver.o
 DRIVERS-$(CONFIG_SCSI) += drivers/scsi/scsidrv.o
+DRIVERS-$(CONFIG_SCSI_AIC7XXX) += drivers/scsi/aic7xxx/aic7xxx_drv.o
 DRIVERS-$(CONFIG_IEEE1394) += drivers/ieee1394/ieee1394drv.o
 
 ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR)$(CONFIG_PARIDE_PCD),)
@@ -440,8 +441,8 @@
 	find . -type f -print | sort | xargs sum > .SUMS
 
 dep-files: scripts/mkdep archdep include/linux/version.h
-	scripts/mkdep init/*.c > .depend
-	scripts/mkdep `find $(FINDHPATH) -name SCCS -prune -o -follow -name \*.h ! -name modversions.h -print` > .hdepend
+	scripts/mkdep -- init/*.c > .depend
+	scripts/mkdep -- `find $(FINDHPATH) -name SCCS -prune -o -follow -name \*.h ! -name modversions.h -print` > .hdepend
 	$(MAKE) $(patsubst %,_sfdep_%,$(SUBDIRS)) _FASTDEP_ALL_SUB_DIRS="$(SUBDIRS)"
 ifdef CONFIG_MODVERSIONS
 	$(MAKE) update-modverfile
diff -urN linux-2.4.3-pre1/Rules.make linux/Rules.make
--- linux-2.4.3-pre1/Rules.make	Fri Dec 29 14:07:19 2000
+++ linux/Rules.make	Mon Dec  3 22:29:49 2001
@@ -123,7 +123,7 @@
 # This make dependencies quickly
 #
 fastdep: dummy
-	$(TOPDIR)/scripts/mkdep $(wildcard *.[chS] local.h.master) > .depend
+	$(TOPDIR)/scripts/mkdep $(CFLAGS) $(EXTRA_CFLAGS) -- $(wildcard *.[chS]) > .depend
 ifdef ALL_SUB_DIRS
 	$(MAKE) $(patsubst %,_sfdep_%,$(ALL_SUB_DIRS)) _FASTDEP_ALL_SUB_DIRS="$(ALL_SUB_DIRS)"
 endif
@@ -150,7 +150,7 @@
 #
 ALL_MOBJS = $(filter-out $(obj-y), $(obj-m))
 ifneq "$(strip $(ALL_MOBJS))" ""
-PDWN=$(shell $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh)
+MOD_DESTDIR ?= $(shell $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh)
 endif
 
 unexport MOD_DIRS
@@ -172,8 +172,8 @@
 .PHONY: _modinst__
 _modinst__: dummy
 ifneq "$(strip $(ALL_MOBJS))" ""
-	mkdir -p $(MODLIB)/kernel/$(PDWN)
-	cp $(ALL_MOBJS) $(MODLIB)/kernel/$(PDWN)
+	mkdir -p $(MODLIB)/kernel/$(MOD_DESTDIR)
+	cp $(ALL_MOBJS) $(MODLIB)/kernel/$(MOD_DESTDIR)$(MOD_TARGET)
 endif
 
 .PHONY: modules_install
@@ -222,9 +222,9 @@
 
 $(MODINCL)/%.ver: %.c
 	@if [ ! -r $(MODINCL)/$*.stamp -o $(MODINCL)/$*.stamp -ot $< ]; then \
-		echo '$(CC) $(CFLAGS) -E -D__GENKSYMS__ $<'; \
+		echo '$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -E -D__GENKSYMS__ $<'; \
 		echo '| $(GENKSYMS) $(genksyms_smp_prefix) -k $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) > $@.tmp'; \
-		$(CC) $(CFLAGS) -E -D__GENKSYMS__ $< \
+		$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -E -D__GENKSYMS__ $< \
 		| $(GENKSYMS) $(genksyms_smp_prefix) -k $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) > $@.tmp; \
 		if [ -r $@ ] && cmp -s $@ $@.tmp; then echo $@ is unchanged; rm -f $@.tmp; \
 		else echo mv $@.tmp $@; mv -f $@.tmp $@; fi; \
diff -urN linux-2.4.3-pre1/arch/alpha/defconfig linux/arch/alpha/defconfig
--- linux-2.4.3-pre1/arch/alpha/defconfig	Mon Oct 16 15:38:41 2000
+++ linux/arch/alpha/defconfig	Mon Dec  3 22:29:49 2001
@@ -285,10 +285,8 @@
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
 CONFIG_SCSI_AIC7XXX=y
-CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT=y
-CONFIG_AIC7XXX_CMDS_PER_DEVICE=8
-CONFIG_AIC7XXX_PROC_STATS=y
-CONFIG_AIC7XXX_RESET_DELAY=5
+CONFIG_AIC7XXX_CMDS_PER_DEVICE=253
+CONFIG_AIC7XXX_RESET_DELAY=5000
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
diff -urN linux-2.4.3-pre1/arch/arm/mach-integrator/Makefile linux/arch/arm/mach-integrator/Makefile
--- linux-2.4.3-pre1/arch/arm/mach-integrator/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-integrator/Makefile	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,24 @@
+#
+# Makefile for the linux kernel.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+
+USE_STANDARD_AS_RULE := true
+
+O_TARGET		:= integrator.o
+
+# Object file lists.
+
+obj-y			:= arch.o irq.o mm.o time.o
+obj-m			:=
+obj-n			:=
+obj-			:=
+
+export-objs		:= leds.o
+
+obj-$(CONFIG_LEDS)	+= leds.o
+obj-$(CONFIG_PCI)	+= pci_v3.o pci.o
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.4.3-pre1/arch/arm/mach-integrator/arch.c linux/arch/arm/mach-integrator/arch.c
--- linux-2.4.3-pre1/arch/arm/mach-integrator/arch.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-integrator/arch.c	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,70 @@
+/*
+ *  linux/arch/arm/mach-integrator/arch.c
+ *
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/amba_kmi.h>
+
+extern void integrator_map_io(void);
+extern void integrator_init_irq(void);
+
+#ifdef CONFIG_KMI_KEYB
+static struct kmi_info integrator_keyboard __initdata = {
+	base:	IO_ADDRESS(KMI0_BASE),
+	irq:	IRQ_KMIINT0,
+	divisor: 24 / 8 - 1,
+	type:	KMI_KEYBOARD,
+};
+
+static struct kmi_info integrator_mouse __initdata = {
+	base:	IO_ADDRESS(KMI1_BASE),
+	irq:	IRQ_KMIINT1,
+	divisor: 24 / 8 - 1,
+	type:	KMI_MOUSE,
+};
+#endif
+
+static void __init
+integrator_fixup(struct machine_desc *desc, struct param_struct *params,
+		 char **cmdline, struct meminfo *mi)
+{
+#ifdef CONFIG_KMI_KEYB
+	register_kmi(&integrator_keyboard);
+	register_kmi(&integrator_mouse);
+#endif
+}
+
+MACHINE_START(INTEGRATOR, "ARM-Integrator")
+	MAINTAINER("ARM Ltd/Deep Blue Solutions Ltd")
+	BOOT_MEM(0x00000000, 0x16000000, 0xf1600000)
+	BOOT_PARAMS(0x00000100)
+	FIXUP(integrator_fixup)
+	MAPIO(integrator_map_io)
+	INITIRQ(integrator_init_irq)
+MACHINE_END
diff -urN linux-2.4.3-pre1/arch/arm/mach-integrator/dma.c linux/arch/arm/mach-integrator/dma.c
--- linux-2.4.3-pre1/arch/arm/mach-integrator/dma.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-integrator/dma.c	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,36 @@
+/*
+ *  linux/arch/arm/mach-integrator/dma.c
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/mman.h>
+#include <linux/init.h>
+
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+#include <asm/mach/dma.h>
+
+void __init arch_dma_init(dma_t *dma)
+{
+}
diff -urN linux-2.4.3-pre1/arch/arm/mach-integrator/irq.c linux/arch/arm/mach-integrator/irq.c
--- linux-2.4.3-pre1/arch/arm/mach-integrator/irq.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-integrator/irq.c	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,71 @@
+/*
+ *  linux/arch/arm/mach-integrator/irq.c
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+
+#include <asm/mach/irq.h>
+
+/* 
+ * All IO addresses are mapped onto VA 0xFFFx.xxxx, where x.xxxx
+ * is the (PA >> 12).
+ *
+ * Setup a VA for the Integrator interrupt controller (for header #0,
+ * just for now).
+ */
+#define VA_IC_BASE              IO_ADDRESS(INTEGRATOR_IC_BASE) 
+#define VA_CMIC_BASE            IO_ADDRESS(INTEGRATOR_HDR_BASE) + INTEGRATOR_HDR_IC_OFFSET
+
+#define ALLPCI ( (1 << IRQ_PCIINT0) | (1 << IRQ_PCIINT1) | (1 << IRQ_PCIINT2) | (1 << IRQ_PCIINT3) ) 
+
+static void sc_mask_irq(unsigned int irq)
+{
+        __raw_writel(1 << irq, VA_IC_BASE + IRQ_ENABLE_CLEAR);
+}
+
+static void sc_unmask_irq(unsigned int irq)
+{
+        __raw_writel(1 << irq, VA_IC_BASE + IRQ_ENABLE_SET);
+}
+ 
+void __init integrator_init_irq(void)
+{
+	unsigned int i;
+
+	for (i = 0; i < NR_IRQS; i++) {
+	        if (((1 << i) && INTEGRATOR_SC_VALID_INT) != 0) {
+		        irq_desc[i].valid	= 1;
+			irq_desc[i].probe_ok	= 1;
+			irq_desc[i].mask_ack	= sc_mask_irq;
+			irq_desc[i].mask	= sc_mask_irq;
+			irq_desc[i].unmask	= sc_unmask_irq;
+		}
+	}
+
+	/* Disable all interrupts initially. */
+	/* Do the core module ones */
+	__raw_writel(-1, VA_CMIC_BASE + IRQ_ENABLE_CLEAR);
+
+	/* do the header card stuff next */
+	__raw_writel(-1, VA_IC_BASE + IRQ_ENABLE_CLEAR);
+	__raw_writel(-1, VA_IC_BASE + FIQ_ENABLE_CLEAR);
+}
diff -urN linux-2.4.3-pre1/arch/arm/mach-integrator/leds.c linux/arch/arm/mach-integrator/leds.c
--- linux-2.4.3-pre1/arch/arm/mach-integrator/leds.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-integrator/leds.c	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,94 @@
+/*
+ *  linux/arch/arm/mach-integrator/leds.c
+ *
+ *  Integrator LED control routines
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/leds.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+
+static int saved_leds;
+
+static void integrator_leds_event(led_event_t ledevt)
+{
+	unsigned long flags;
+	const unsigned int dbg_base = IO_ADDRESS(INTEGRATOR_DBG_BASE);
+	const unsigned int hdr_ctrl = IO_ADDRESS(INTEGRATOR_HDR_BASE) +
+					INTEGRATOR_HDR_CTRL_OFFSET;
+	unsigned int ctrl;
+	unsigned int update_alpha_leds;
+	
+	// yup, change the LEDs
+	local_irq_save(flags);
+	update_alpha_leds = 0;
+
+	switch(ledevt) {
+	case led_idle_start:
+		ctrl = __raw_readl(hdr_ctrl);
+		ctrl &= ~INTEGRATOR_HDR_CTRL_LED;
+		__raw_writel(ctrl, hdr_ctrl);
+		break;
+
+	case led_idle_end:
+		ctrl = __raw_readl(hdr_ctrl);
+		ctrl |= INTEGRATOR_HDR_CTRL_LED;
+		__raw_writel(ctrl, hdr_ctrl);
+		break;
+
+	case led_timer:
+		saved_leds ^= GREEN_LED;
+		update_alpha_leds = 1;
+		break;
+
+	case led_red_on:
+		saved_leds |= RED_LED;
+		update_alpha_leds = 1;
+		break;
+
+	case led_red_off:
+		saved_leds &= ~RED_LED;
+		update_alpha_leds = 1;
+		break;
+
+	default:
+		break;
+	}
+
+	if (update_alpha_leds) {
+		while (__raw_readl(dbg_base + INTEGRATOR_DBG_ALPHA_OFFSET) & 1);
+		__raw_writel(saved_leds, dbg_base + INTEGRATOR_DBG_LEDS_OFFSET);
+	}
+	local_irq_restore(flags);
+}
+
+static int __init leds_init(void)
+{
+	if (machine_is_integrator())
+		leds_event = integrator_leds_event;
+
+	return 0;
+}
+
+__initcall(leds_init);
diff -urN linux-2.4.3-pre1/arch/arm/mach-integrator/mm.c linux/arch/arm/mach-integrator/mm.c
--- linux-2.4.3-pre1/arch/arm/mach-integrator/mm.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-integrator/mm.c	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,78 @@
+/*
+ *  linux/arch/arm/mach-integrator/mm.c
+ *
+ *  Extra MM routines for the ARM Integrator board
+ *
+ *  Copyright (C) 1999,2000 Arm Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+ 
+#include <asm/mach/map.h>
+
+/*
+ * Logical      Physical
+ * e8000000	40000000	PCI memory
+ * ec000000	62000000	PCI config space
+ * ed000000	61000000	PCI V3 regs
+ * ee000000	60000000	PCI IO
+ * ef000000			Cache flush
+ * f1000000	10000000	Core module registers
+ * f1100000	11000000	System controller registers
+ * f1200000	12000000	EBI registers
+ * f1300000	13000000	Counter/Timer
+ * f1400000	14000000	Interrupt controller
+ * f1500000	15000000	RTC
+ * f1600000	16000000	UART 0
+ * f1700000	17000000	UART 1
+ * f1800000	18000000	Keyboard
+ * f1900000	19000000	Mouse
+ * f1a00000	1a000000	Debug LEDs
+ * f1b00000	1b000000	GPIO
+ */
+ 
+static struct map_desc integrator_io_desc[] __initdata = {
+ { IO_ADDRESS(INTEGRATOR_HDR_BASE),   INTEGRATOR_HDR_BASE,   SZ_4K     , DOMAIN_IO, 0, 1},
+ { IO_ADDRESS(INTEGRATOR_SC_BASE),    INTEGRATOR_SC_BASE,    SZ_4K     , DOMAIN_IO, 0, 1},
+ { IO_ADDRESS(INTEGRATOR_EBI_BASE),   INTEGRATOR_EBI_BASE,   SZ_4K     , DOMAIN_IO, 0, 1},
+ { IO_ADDRESS(INTEGRATOR_CT_BASE),    INTEGRATOR_CT_BASE,    SZ_4K     , DOMAIN_IO, 0, 1},
+ { IO_ADDRESS(INTEGRATOR_IC_BASE),    INTEGRATOR_IC_BASE,    SZ_4K     , DOMAIN_IO, 0, 1},
+ { IO_ADDRESS(INTEGRATOR_RTC_BASE),   INTEGRATOR_RTC_BASE,   SZ_4K     , DOMAIN_IO, 0, 1},
+ { IO_ADDRESS(INTEGRATOR_UART0_BASE), INTEGRATOR_UART0_BASE, SZ_4K     , DOMAIN_IO, 0, 1},
+ { IO_ADDRESS(INTEGRATOR_UART1_BASE), INTEGRATOR_UART1_BASE, SZ_4K     , DOMAIN_IO, 0, 1},
+ { IO_ADDRESS(INTEGRATOR_KBD_BASE),   INTEGRATOR_KBD_BASE,   SZ_4K     , DOMAIN_IO, 0, 1},
+ { IO_ADDRESS(INTEGRATOR_MOUSE_BASE), INTEGRATOR_MOUSE_BASE, SZ_4K     , DOMAIN_IO, 0, 1},
+ { IO_ADDRESS(INTEGRATOR_DBG_BASE),   INTEGRATOR_DBG_BASE,   SZ_4K     , DOMAIN_IO, 0, 1},
+ { IO_ADDRESS(INTEGRATOR_GPIO_BASE),  INTEGRATOR_GPIO_BASE,  SZ_4K     , DOMAIN_IO, 0, 1},
+ { PCI_MEMORY_VADDR,                  PHYS_PCI_MEM_BASE,     SZ_16M    , DOMAIN_IO, 0, 1},
+ { PCI_CONFIG_VADDR,                  PHYS_PCI_CONFIG_BASE,  SZ_16M    , DOMAIN_IO, 0, 1},
+ { PCI_V3_VADDR,                      PHYS_PCI_V3_BASE,      SZ_512K   , DOMAIN_IO, 0, 1},
+ { PCI_IO_VADDR,                      PHYS_PCI_IO_BASE,      SZ_64K    , DOMAIN_IO, 0, 1},
+ LAST_DESC
+};
+
+void __init integrator_map_io(void)
+{
+	iotable_init(integrator_io_desc);
+}
diff -urN linux-2.4.3-pre1/arch/arm/mach-integrator/pci.c linux/arch/arm/mach-integrator/pci.c
--- linux-2.4.3-pre1/arch/arm/mach-integrator/pci.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-integrator/pci.c	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,119 @@
+/*
+ *  linux/arch/arm/mach-integrator/pci-integrator.c
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ *  PCI functions for Integrator
+ */
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/ptrace.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/mach/pci.h>
+
+/* 
+ * A small note about bridges and interrupts.  The DECchip 21050 (and
+ * later) adheres to the PCI-PCI bridge specification.  This says that
+ * the interrupts on the other side of a bridge are swizzled in the
+ * following manner:
+ *
+ * Dev    Interrupt   Interrupt 
+ *        Pin on      Pin on 
+ *        Device      Connector
+ *
+ *   4    A           A
+ *        B           B
+ *        C           C
+ *        D           D
+ * 
+ *   5    A           B
+ *        B           C
+ *        C           D
+ *        D           A
+ *
+ *   6    A           C
+ *        B           D
+ *        C           A
+ *        D           B
+ *
+ *   7    A           D
+ *        B           A
+ *        C           B
+ *        D           C
+ *
+ * Where A = pin 1, B = pin 2 and so on and pin=0 = default = A.
+ * Thus, each swizzle is ((pin-1) + (device#-4)) % 4
+ *
+ * The following code swizzles for exactly one bridge.  
+ */
+static inline int bridge_swizzle(int pin, unsigned int slot) 
+{
+	return (pin + slot) & 3;
+}
+
+/*
+ * This routine handles multiple bridges.
+ */
+static u8 __init integrator_swizzle(struct pci_dev *dev, u8 *pinp)
+{
+	int pin = *pinp;
+
+	if (pin == 0)
+		pin = 1;
+
+	pin -= 1;
+	while (dev->bus->self) {
+		pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn));
+		/*
+		 * move up the chain of bridges, swizzling as we go.
+		 */
+		dev = dev->bus->self;
+	}
+	*pinp = pin + 1;
+
+	return PCI_SLOT(dev->devfn);
+}
+
+static int irq_tab[4] __initdata = {
+	IRQ_PCIINT0,	IRQ_PCIINT1,	IRQ_PCIINT2,	IRQ_PCIINT3
+};
+
+/*
+ * map the specified device/slot/pin to an IRQ.  This works out such
+ * that slot 9 pin 1 is INT0, pin 2 is INT1, and slot 10 pin 1 is INT1.
+ */
+static int __init integrator_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+	int intnr = ((slot - 9) + (pin - 1)) & 3;
+
+	return irq_tab[intnr];
+}
+
+extern void pci_v3_init(struct arm_pci_sysdata *);
+
+struct hw_pci integrator_pci __initdata = {
+	init:		pci_v3_init,
+	swizzle:	integrator_swizzle,
+	map_irq:	integrator_map_irq,
+};
diff -urN linux-2.4.3-pre1/arch/arm/mach-integrator/pci_v3.c linux/arch/arm/mach-integrator/pci_v3.c
--- linux-2.4.3-pre1/arch/arm/mach-integrator/pci_v3.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-integrator/pci_v3.c	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,460 @@
+/*
+ *  linux/arch/arm/mach-integrator/pci_v3.c
+ *
+ *  PCI functions for V3 host PCI bridge
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/ptrace.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/mach/pci.h>
+
+#include <asm/hardware/pci_v3.h>
+
+/*
+ * The V3 PCI interface chip in Integrator provides several windows from
+ * local bus memory into the PCI memory areas.   Unfortunately, there
+ * are not really enough windows for our usage, therefore we reuse 
+ * one of the windows for access to PCI configuration space.  The
+ * memory map is as follows:
+ * 
+ * Local Bus Memory         Usage
+ * 
+ * 40000000 - 4FFFFFFF      PCI memory.  256M non-prefetchable
+ * 50000000 - 5FFFFFFF      PCI memory.  256M prefetchable
+ * 60000000 - 60FFFFFF      PCI IO.  16M
+ * 68000000 - 68FFFFFF      PCI Configuration. 16M
+ * 
+ * There are three V3 windows, each described by a pair of V3 registers.
+ * These are LB_BASE0/LB_MAP0, LB_BASE1/LB_MAP1 and LB_BASE2/LB_MAP2.
+ * Base0 and Base1 can be used for any type of PCI memory access.   Base2
+ * can be used either for PCI I/O or for I20 accesses.  By default, uHAL
+ * uses this only for PCI IO space.
+ * 
+ * PCI Memory is mapped so that assigned addresses in PCI Memory match
+ * local bus memory addresses.  In other words, if a PCI device is assigned
+ * address 80200000 then that address is a valid local bus address as well
+ * as a valid PCI Memory address.  PCI IO addresses are mapped to start
+ * at zero.  This means that local bus address 60000000 maps to PCI IO address
+ * 00000000 and so on.   Device driver writers need to be aware of this 
+ * distinction.
+ * 
+ * Normally these spaces are mapped using the following base registers:
+ * 
+ * Usage Local Bus Memory         Base/Map registers used
+ * 
+ * Mem   40000000 - 4FFFFFFF      LB_BASE0/LB_MAP0
+ * Mem   50000000 - 5FFFFFFF      LB_BASE1/LB_MAP1
+ * IO    60000000 - 60FFFFFF      LB_BASE2/LB_MAP2
+ * Cfg   68000000 - 68FFFFFF      
+ * 
+ * This means that I20 and PCI configuration space accesses will fail.
+ * When PCI configuration accesses are needed (via the uHAL PCI 
+ * configuration space primitives) we must remap the spaces as follows:
+ * 
+ * Usage Local Bus Memory         Base/Map registers used
+ * 
+ * Mem   40000000 - 4FFFFFFF      LB_BASE0/LB_MAP0
+ * Mem   50000000 - 5FFFFFFF      LB_BASE0/LB_MAP0
+ * IO    60000000 - 60FFFFFF      LB_BASE2/LB_MAP2
+ * Cfg   68000000 - 68FFFFFF      LB_BASE1/LB_MAP1
+ * 
+ * To make this work, the code depends on overlapping windows working.
+ * The V3 chip translates an address by checking its range within 
+ * each of the BASE/MAP pairs in turn (in ascending register number
+ * order).  It will use the first matching pair.   So, for example,
+ * if the same address is mapped by both LB_BASE0/LB_MAP0 and
+ * LB_BASE1/LB_MAP1, the V3 will use the translation from 
+ * LB_BASE0/LB_MAP0.
+ * 
+ * To allow PCI Configuration space access, the code enlarges the
+ * window mapped by LB_BASE0/LB_MAP0 from 256M to 512M.  This occludes
+ * the windows currently mapped by LB_BASE1/LB_MAP1 so that it can
+ * be remapped for use by configuration cycles.
+ * 
+ * At the end of the PCI Configuration space accesses, 
+ * LB_BASE1/LB_MAP1 is reset to map PCI Memory.  Finally the window
+ * mapped by LB_BASE0/LB_MAP0 is reduced in size from 512M to 256M to
+ * reveal the now restored LB_BASE1/LB_MAP1 window.
+ * 
+ * NOTE: We do not set up I2O mapping.  I suspect that this is only
+ * for an intelligent (target) device.  Using I2O disables most of
+ * the mappings into PCI memory.
+ */
+
+// V3 access routines
+#define _V3Write16(o,v) __raw_writew(v, PCI_V3_VADDR + (unsigned int)(o))
+#define _V3Read16(o)    (__raw_readw(PCI_V3_VADDR + (unsigned int)(o)))
+
+#define _V3Write32(o,v) __raw_writel(v, PCI_V3_VADDR + (unsigned int)(o))
+#define _V3Read32(o)    (__raw_readl(PCI_V3_VADDR + (unsigned int)(o)))
+
+/*============================================================================
+ *
+ * routine:	uHALir_PCIMakeConfigAddress()
+ *
+ * parameters:	bus = which bus
+ *              device = which device
+ *              function = which function
+ *		offset = configuration space register we are interested in
+ *
+ * description:	this routine will generate a platform dependant config
+ *		address.
+ *
+ * calls:	none
+ *
+ * returns:	configuration address to play on the PCI bus
+ *
+ * To generate the appropriate PCI configuration cycles in the PCI 
+ * configuration address space, you present the V3 with the following pattern 
+ * (which is very nearly a type 1 (except that the lower two bits are 00 and
+ * not 01).   In order for this mapping to work you need to set up one of
+ * the local to PCI aperatures to 16Mbytes in length translating to
+ * PCI configuration space starting at 0x0000.0000.
+ *
+ * PCI configuration cycles look like this:
+ *
+ * Type 0:
+ *
+ *  3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 
+ *  3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | |D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|0|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *	31:11	Device select bit.
+ * 	10:8	Function number
+ * 	 7:2	Register number
+ *
+ * Type 1:
+ *
+ *  3 3|3 3 2 2|2 2 2 2|2 2 2 2|1 1 1 1|1 1 1 1|1 1 
+ *  3 2|1 0 9 8|7 6 5 4|3 2 1 0|9 8 7 6|5 4 3 2|1 0 9 8|7 6 5 4|3 2 1 0
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | | | | | | | | | |B|B|B|B|B|B|B|B|D|D|D|D|D|F|F|F|R|R|R|R|R|R|0|1|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *	31:24	reserved
+ *	23:16	bus number (8 bits = 128 possible buses)
+ *	15:11	Device number (5 bits)
+ *	10:8	function number
+ *	 7:2	register number
+ *  
+ */
+static spinlock_t v3_lock = SPIN_LOCK_UNLOCKED;
+
+#define PCI_BUS_NONMEM_START	0x00000000
+#define PCI_BUS_NONMEM_SIZE	0x10000000
+
+#define PCI_BUS_PREMEM_START	0x10000000
+#define PCI_BUS_PREMEM_SIZE	0x10000000
+
+#if PCI_BUS_NONMEM_START & 0x000fffff
+#error PCI_BUS_NONMEM_START must be megabyte aligned
+#endif
+#if PCI_BUS_PREMEM_START & 0x000fffff
+#error PCI_BUS_PREMEM_START must be megabyte aligned
+#endif
+
+static unsigned long v3_open_config_window(struct pci_dev *dev, int offset)
+{
+	unsigned int address, mapaddress, busnr;
+
+	busnr = dev->bus->number;
+
+	/*
+	 * Trap out illegal values
+	 */
+	if (offset > 255)
+		BUG();
+	if (busnr > 255)
+		BUG();
+	if (dev->devfn > 255)
+		BUG();
+
+	if (busnr == 0) {
+		int slot = PCI_SLOT(dev->devfn);
+
+		/*
+		 * local bus segment so need a type 0 config cycle
+		 *
+		 * build the PCI configuration "address" with one-hot in
+		 * A31-A11
+		 *
+		 * mapaddress:
+		 *  3:1 = config cycle (101)
+		 *  0   = PCI A1 & A0 are 0 (0)
+		 */
+		address = PCI_FUNC(dev->devfn) << 8;
+		mapaddress = 0x0a;
+
+		if (slot > 12)
+			/*
+			 * high order bits are handled by the MAP register
+			 */
+			mapaddress |= 1 << (slot - 4);
+		else
+			/*
+			 * low order bits handled directly in the address
+			 */
+			address |= 1 << (slot + 11);
+	} else {
+        	/*
+		 * not the local bus segment so need a type 1 config cycle
+		 *
+		 * address:
+		 *  23:16 = bus number
+		 *  15:11 = slot number (7:3 of devfn)
+		 *  10:8  = func number (2:0 of devfn)
+		 *
+		 * mapaddress:
+		 *  3:1 = config cycle (101)
+		 *  0   = PCI A1 & A0 from host bus (1)
+		 */
+		mapaddress = 0x0b;
+		address = (busnr << 16) | (dev->devfn << 8);
+	}
+
+	/*
+	 * Set up base0 to see all 512Mbytes of memory space (not prefetchable), this
+	 * frees up base1 for re-use by configuration memory
+	 */
+	_V3Write32(V3_LB_BASE0, (PHYS_PCI_MEM_BASE & 0xFFF00000) | 0x90 | V3_LB_BASE_M_ENABLE);
+
+	/*
+	 * Set up base1/map1 to point into configuration space.
+	 */
+	_V3Write32(V3_LB_BASE1, (PHYS_PCI_CONFIG_BASE & 0xFFF00000) | 0x40 | V3_LB_BASE_M_ENABLE);
+        _V3Write16(V3_LB_MAP1, mapaddress);
+
+	return PCI_CONFIG_VADDR + address + offset;
+}
+
+static void v3_close_config_window(void)
+{
+	/*
+	 * Reassign base1 for use by prefetchable PCI memory
+	 */
+	_V3Write32(V3_LB_BASE1, ((PHYS_PCI_MEM_BASE + SZ_256M) & 0xFFF00000) | 0x84 | V3_LB_BASE_M_ENABLE);
+	_V3Write16(V3_LB_MAP1, ((PCI_BUS_PREMEM_START & 0xFFF00000) >> 16) | 0x0006);
+
+	/*
+	 * And shrink base0 back to a 256M window (NOTE: MAP0 already correct)
+	 */
+	_V3Write32(V3_LB_BASE0, (PHYS_PCI_MEM_BASE & 0xFFF00000) | 0x80 | V3_LB_BASE_M_ENABLE);
+}
+
+static int v3_read_config_byte(struct pci_dev *dev, int where, u8 *val)
+{
+	unsigned long addr;
+	unsigned long flags;
+	u8 v;
+
+	spin_lock_irqsave(&v3_lock, flags);
+	addr = v3_open_config_window(dev, where);
+
+	v = __raw_readb(addr);
+
+	v3_close_config_window();
+	spin_unlock_irqrestore(&v3_lock, flags);
+
+	*val = v;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int v3_read_config_word(struct pci_dev *dev, int where, u16 *val)
+{
+	unsigned long addr;
+	unsigned long flags;
+	u16 v;
+
+	spin_lock_irqsave(&v3_lock, flags);
+	addr = v3_open_config_window(dev, where);
+
+	v = __raw_readw(addr);
+
+	v3_close_config_window();
+	spin_unlock_irqrestore(&v3_lock, flags);
+
+	*val = v;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int v3_read_config_dword(struct pci_dev *dev, int where, u32 *val)
+{
+	unsigned long addr;
+	unsigned long flags;
+	u32 v;
+
+	spin_lock_irqsave(&v3_lock, flags);
+	addr = v3_open_config_window(dev, where);
+
+	v = __raw_readl(addr);
+
+	v3_close_config_window();
+	spin_unlock_irqrestore(&v3_lock, flags);
+
+	*val = v;
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int v3_write_config_byte(struct pci_dev *dev, int where, u8 val)
+{
+	unsigned long addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&v3_lock, flags);
+	addr = v3_open_config_window(dev, where);
+
+	__raw_writeb(val, addr);
+	__raw_readb(addr);
+	
+	v3_close_config_window();
+	spin_unlock_irqrestore(&v3_lock, flags);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int v3_write_config_word(struct pci_dev *dev, int where, u16 val)
+{
+	unsigned long addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&v3_lock, flags);
+	addr = v3_open_config_window(dev, where);
+
+	__raw_writew(val, addr);
+	__raw_readw(addr);
+
+	v3_close_config_window();
+	spin_unlock_irqrestore(&v3_lock, flags);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int v3_write_config_dword(struct pci_dev *dev, int where, u32 val)
+{
+	unsigned long addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&v3_lock, flags);
+	addr = v3_open_config_window(dev, where);
+
+	__raw_writel(val, addr);
+	__raw_readl(addr);
+
+	v3_close_config_window();
+	spin_unlock_irqrestore(&v3_lock, flags);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops pci_v3_ops = {
+	read_byte:	v3_read_config_byte,
+	read_word:	v3_read_config_word,
+	read_dword:	v3_read_config_dword,
+	write_byte:	v3_write_config_byte,
+	write_word:	v3_write_config_word,
+	write_dword:	v3_write_config_dword,
+};
+
+static struct resource non_mem = {
+	name:	"PCI non-prefetchable",
+	start:	PCI_BUS_NONMEM_START,
+	end:	PCI_BUS_NONMEM_START + PCI_BUS_NONMEM_SIZE - 1,
+	flags:	IORESOURCE_MEM,
+};
+
+static struct resource pre_mem = {
+	name:	"PCI prefetchable",
+	start:	PCI_BUS_PREMEM_START,
+	end:	PCI_BUS_PREMEM_START + PCI_BUS_PREMEM_SIZE - 1,
+	flags:	IORESOURCE_MEM | IORESOURCE_PREFETCH,
+};
+
+/*
+ * V3_LB_BASE? - local bus address
+ * V3_LB_MAP?  - pci bus address
+ */
+void __init pci_v3_init(struct arm_pci_sysdata *sysdata)
+{
+	struct pci_bus *bus;
+	unsigned int pci_cmd;
+	unsigned long flags;
+
+	spin_lock_irqsave(&v3_lock, flags);
+
+	/*
+	 * Setup window 0 - PCI non-prefetchable memory
+	 *  Local: 0x40000000 Bus: 0x00000000 Size: 256MB
+	 */
+	_V3Write32(V3_LB_BASE0, (PHYS_PCI_MEM_BASE & 0xfff00000) | 0x80 | V3_LB_BASE_M_ENABLE);
+	_V3Write16(V3_LB_MAP0, (PCI_BUS_NONMEM_START >> 16) | 0x0006);
+
+	/*
+	 * Setup window 1 - PCI prefetchable memory
+	 *  Local: 0x50000000 Bus: 0x10000000 Size: 256MB
+	 */
+	_V3Write32(V3_LB_BASE1, ((PHYS_PCI_MEM_BASE + SZ_256M) & 0xFFF00000) | 0x84 | V3_LB_BASE_M_ENABLE);
+	_V3Write16(V3_LB_MAP1, (PCI_BUS_PREMEM_START >> 16) | 0x0006);
+
+	/*
+	 * Setup window 2 - PCI IO
+	 */
+//	_V3Write32(V3_LB_BASE2, (PHYS_PCI_IO_BASE & 0xff000000) | V3_LB_BASE_M_ENABLE);
+//	_V3Write16(V3_LB_MAP2, 0);
+
+	spin_unlock_irqrestore(&v3_lock, flags);
+
+	bus = pci_scan_bus(0, &pci_v3_ops, sysdata);
+
+	if (request_resource(&iomem_resource, &non_mem))
+		printk("PCI: unable to allocate non-prefetchable memory region");
+	if (request_resource(&iomem_resource, &pre_mem))
+		printk("PCI: unable to allocate prefetchable memory region");
+
+	/*
+	 * bus->resource[0] is the IO resource for this bus
+	 * bus->resource[1] is the mem resource for this bus
+	 * bus->resource[2] is the prefetch mem resource for this bus
+	 */
+	bus->resource[1] = &non_mem;
+	bus->resource[2] = &pre_mem;
+
+	pci_cmd = PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+		  PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE;
+
+	pci_cmd |= sysdata->bus[0].features;
+
+	_V3Write16(V3_PCI_CMD, pci_cmd);
+
+	printk("PCI: Fast back to back transfers %sabled\n",
+	       (sysdata->bus[0].features & PCI_COMMAND_FAST_BACK) ?
+		"en" : "dis");
+}
diff -urN linux-2.4.3-pre1/arch/arm/mach-integrator/time.c linux/arch/arm/mach-integrator/time.c
--- linux-2.4.3-pre1/arch/arm/mach-integrator/time.c	Wed Dec 31 16:00:00 1969
+++ linux/arch/arm/mach-integrator/time.c	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,45 @@
+/*
+ *  linux/arch/arm/mach-integrator/time.c
+ *
+ *  Copyright (C) 2000 Deep Blue Solutions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+
+#include <asm/hardware.h>
+
+#define RTC_DR		(*(unsigned long *)(IO_ADDRESS(INTEGRATOR_RTC_BASE) + 0))
+#define RTC_MR		(*(unsigned long *)(IO_ADDRESS(INTEGRATOR_RTC_BASE) + 4))
+#define RTC_STAT	(*(unsigned long *)(IO_ADDRESS(INTEGRATOR_RTC_BASE) + 8))
+#define RTC_EOI		(*(unsigned long *)(IO_ADDRESS(INTEGRATOR_RTC_BASE) + 8))
+#define RTC_LR		(*(unsigned long *)(IO_ADDRESS(INTEGRATOR_RTC_BASE) + 12))
+#define RTC_CR		(*(unsigned long *)(IO_ADDRESS(INTEGRATOR_RTC_BASE) + 16))
+
+#define RTC_CR_MIE	0x00000001
+
+extern int (*set_rtc)(void);
+
+static int integrator_set_rtc(void)
+{
+	RTC_LR = xtime.tv_sec;
+	return 1;
+}
+
+static int integrator_rtc_init(void)
+{
+	RTC_CR = 0;
+	RTC_EOI = 0;
+
+	xtime.tv_sec = RTC_DR;
+
+	set_rtc = integrator_set_rtc;
+
+	return 0;
+}
+
+__initcall(integrator_rtc_init);
diff -urN linux-2.4.3-pre1/arch/cris/drivers/serial.c linux/arch/cris/drivers/serial.c
--- linux-2.4.3-pre1/arch/cris/drivers/serial.c	Tue Feb 13 14:13:43 2001
+++ linux/arch/cris/drivers/serial.c	Mon Dec  3 22:29:49 2001
@@ -146,7 +146,7 @@
  *
  * Revision 1.24  2000/02/09 18:02:28  bjornw
  * * Clear serial errors (overrun, framing, parity) correctly. Before, the
- *   receiver would get stuck if an error occured and we did not restart
+ *   receiver would get stuck if an error occurred and we did not restart
  *   the input DMA.
  * * Cosmetics (indentation, some code made into inlines)
  * * Some more debug options
@@ -1371,7 +1371,7 @@
 			} 
 			else { /* it was a valid byte, now let the dma do the rest */
 #ifdef SERIAL_DEBUG_INTR
-				printk("** OK, disabling ser_interupts\n");
+				printk("** OK, disabling ser_interrupts\n");
 #endif
 				e100_disable_serial_data_irq(info);
 			}
diff -urN linux-2.4.3-pre1/arch/cris/kernel/entry.S linux/arch/cris/kernel/entry.S
--- linux-2.4.3-pre1/arch/cris/kernel/entry.S	Thu Feb  8 16:32:44 2001
+++ linux/arch/cris/kernel/entry.S	Mon Dec  3 22:29:49 2001
@@ -23,7 +23,7 @@
  *  Revision 1.8  2000/11/17 16:53:35  bjornw
  *  Added detection of frame-type in Rexit, so that mmu_bus_fault can
  *  use ret_from_intr in the return-path to check for signals (like SEGV)
- *  and other foul things that might have occured during the fault.
+ *  and other foul things that might have occurred during the fault.
  *
  *  Revision 1.7  2000/10/06 15:04:28  bjornw
  *  Include mof in register savings
diff -urN linux-2.4.3-pre1/arch/cris/mm/fault.c linux/arch/cris/mm/fault.c
--- linux-2.4.3-pre1/arch/cris/mm/fault.c	Thu Feb  8 16:32:44 2001
+++ linux/arch/cris/mm/fault.c	Mon Dec  3 22:29:49 2001
@@ -146,7 +146,7 @@
  * routines.
  *
  * Notice that the address we're given is aligned to the page the fault
- * occured in, since we only get the PFN in R_MMU_CAUSE not the complete
+ * occurred in, since we only get the PFN in R_MMU_CAUSE not the complete
  * address.
  *
  * error_code:
@@ -381,7 +381,7 @@
                 pmd = pmd_offset(pgd, address);
                 pmd_k = pmd_offset(pgd_k, address);
 
-                if (pmd_present(*pmd) || !pmd_present(*pmd_k))
+                if (!pmd_present(*pmd_k))
                         goto bad_area_nosemaphore;
                 set_pmd(pmd, *pmd_k);
                 return;
diff -urN linux-2.4.3-pre1/arch/i386/defconfig linux/arch/i386/defconfig
--- linux-2.4.3-pre1/arch/i386/defconfig	Mon Dec  3 22:29:33 2001
+++ linux/arch/i386/defconfig	Mon Dec  3 22:29:49 2001
@@ -280,6 +280,7 @@
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
 # CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
diff -urN linux-2.4.3-pre1/arch/mips/defconfig linux/arch/mips/defconfig
--- linux-2.4.3-pre1/arch/mips/defconfig	Thu Jul 27 18:36:54 2000
+++ linux/arch/mips/defconfig	Mon Dec  3 22:29:49 2001
@@ -175,6 +175,7 @@
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
 # CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
diff -urN linux-2.4.3-pre1/arch/mips64/defconfig linux/arch/mips64/defconfig
--- linux-2.4.3-pre1/arch/mips64/defconfig	Sun Feb  4 21:48:46 2001
+++ linux/arch/mips64/defconfig	Mon Dec  3 22:29:49 2001
@@ -165,6 +165,7 @@
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
 # CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
diff -urN linux-2.4.3-pre1/arch/ppc/8xx_io/Config.in linux/arch/ppc/8xx_io/Config.in
--- linux-2.4.3-pre1/arch/ppc/8xx_io/Config.in	Thu Jul 13 09:42:50 2000
+++ linux/arch/ppc/8xx_io/Config.in	Mon Dec  3 22:29:49 2001
@@ -7,24 +7,24 @@
 if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
   bool 'CPM SCC Ethernet' CONFIG_SCC_ENET
   if [ "$CONFIG_SCC_ENET" = "y" ]; then
-  bool 'Ethernet on SCC1' CONFIG_SCC1_ENET
-    if [ "$CONFIG_SCC1_ENET" != "y" ]; then
-      bool 'Ethernet on SCC2' CONFIG_SCC2_ENET
-    fi
+    choice 'SCC used for Ethernet'	\
+  	"SCC1	CONFIG_SCC1_ENET	\
+	 SCC2	CONFIG_SCC2_ENET	\
+	 SCC3	CONFIG_SCC3_ENET"	SCC1
   fi
   bool '860T FEC Ethernet' CONFIG_FEC_ENET
+  if [ "$CONFIG_FEC_ENET" = "y" ]; then
+    bool 'Use MDIO for PHY configuration' CONFIG_USE_MDIO
+  fi
   bool 'Use Big CPM Ethernet Buffers' CONFIG_ENET_BIG_BUFFERS
 fi
 bool 'Use SMC2 for UART' CONFIG_8xxSMC2
 if [ "$CONFIG_8xxSMC2" = "y" ]; then
   bool 'Use Alternate SMC2 I/O (823/850)' CONFIG_8xx_ALTSMC2
+  bool 'Use SMC2 for Console' CONFIG_8xx_CONS_SMC2
 fi
 bool 'Enable SCC2 and SCC3 for UART' CONFIG_8xxSCC
 
-if [ "$CONFIG_TQM860" = "y" -o "$CONFIG_TQM860L" = "y" -o "$CONFIG_TQM8xxL" = "y" ]; then
-  bool 'Use SMC2 for Console' TQM_SMC2_CONSOLE
-fi
-
 # This doesn't really belong here, but it is convenient to ask
 # 8xx specific questions.
 
@@ -32,4 +32,7 @@
 bool 'Copy-Back Data Cache (else Writethrough)' CONFIG_8xx_COPYBACK
 bool 'CPU6 Silicon Errata (860 Pre Rev. C)' CONFIG_8xx_CPU6
 
+if [ "$CONFIG_IDE" = "y" ]; then
+	bool 'MPC8xx direct IDE support on PCMCIA port' CONFIG_BLK_DEV_MPC8xx_IDE
+fi
 endmenu
diff -urN linux-2.4.3-pre1/arch/ppc/8xx_io/commproc.c linux/arch/ppc/8xx_io/commproc.c
--- linux-2.4.3-pre1/arch/ppc/8xx_io/commproc.c	Sat Nov 11 18:14:38 2000
+++ linux/arch/ppc/8xx_io/commproc.c	Mon Dec  3 22:29:49 2001
@@ -61,22 +61,6 @@
 	imp = (immap_t *)IMAP_ADDR;
 	commproc = (cpm8xx_t *)&imp->im_cpm;
 
-#ifdef notdef
-	/* We can't do this.  It seems to blow away the microcode
-	 * patch that EPPC-Bug loaded for us.  EPPC-Bug uses SCC1 for
-	 * Ethernet, SMC1 for the console, and I2C for serial EEPROM.
-	 * Our own drivers quickly reset all of these.
-	 */
-
-	/* Perform a reset.
-	*/
-	commproc->cp_cpcr = (CPM_CR_RST | CPM_CR_FLG);
-
-	/* Wait for it.
-	*/
-	while (commproc->cp_cpcr & CPM_CR_FLG);
-#endif
-
 	/* Set SDMA Bus Request priority 5.
 	 * On 860T, this also enables FEC priority 6.  I am not sure
 	 * this is what we realy want for some applications, but the
@@ -168,6 +152,14 @@
 void
 cpm_install_handler(int vec, void (*handler)(void *), void *dev_id)
 {
+
+	/* If null handler, assume we are trying to free the IRQ.
+	*/
+	if (!handler) {
+		cpm_free_handler(vec);
+		return;
+	}
+
 	if (cpm_vecs[vec].handler != 0)
 		printk("CPM interrupt %x replacing %x\n",
 			(uint)handler, (uint)cpm_vecs[vec].handler);
@@ -226,8 +218,9 @@
  * The internal baud rate clock is the system clock divided by 16.
  * This assumes the baudrate is 16x oversampled by the uart.
  */
-#define BRG_INT_CLK	(((bd_t *)__res)->bi_intfreq * 1000000)
-#define BRG_UART_CLK	(BRG_INT_CLK/16)
+#define BRG_INT_CLK		(((bd_t *)__res)->bi_intfreq * 1000000)
+#define BRG_UART_CLK		(BRG_INT_CLK/16)
+#define BRG_UART_CLK_DIV16	(BRG_UART_CLK/16)
 
 void
 m8xx_cpm_setbrg(uint brg, uint rate)
@@ -238,6 +231,12 @@
 	*/
 	bp = (uint *)&cpmp->cp_brgc1;
 	bp += brg;
-	*bp = ((BRG_UART_CLK / rate) << 1) | CPM_BRG_EN;
+	/* The BRG has a 12-bit counter.  For really slow baud rates (or
+	 * really fast processors), we may have to further divide by 16.
+	 */
+	if (((BRG_UART_CLK / rate) - 1) < 4096)
+		*bp = (((BRG_UART_CLK / rate) - 1) << 1) | CPM_BRG_EN;
+	else
+		*bp = (((BRG_UART_CLK_DIV16 / rate) - 1) << 1) |
+						CPM_BRG_EN | CPM_BRG_DIV16;
 }
-
diff -urN linux-2.4.3-pre1/arch/ppc/8xx_io/commproc.h linux/arch/ppc/8xx_io/commproc.h
--- linux-2.4.3-pre1/arch/ppc/8xx_io/commproc.h	Fri Feb 16 16:02:34 2001
+++ linux/arch/ppc/8xx_io/commproc.h	Mon Dec  3 22:29:49 2001
@@ -324,7 +324,7 @@
 */
 #define SCC_EB	((u_char)0x10)	/* Set big endian byte order */
 
-/* CPM Ethernet through SCC1.
+/* CPM Ethernet through SCCx.
  */
 typedef struct scc_enet {
 	sccp_t	sen_genscc;
@@ -379,6 +379,8 @@
 	ushort	sen_taddrl;	/* temp address (LSB) */
 } scc_enet_t;
 
+/***  MBX  ************************************************************/
+
 #ifdef CONFIG_MBX
 /* Bits in parallel I/O port registers that have to be set/cleared
  * to configure the pins for SCC1 use.  The TCLK and RCLK seem unique
@@ -399,7 +401,9 @@
  */
 #define SICR_ENET_MASK	((uint)0x000000ff)
 #define SICR_ENET_CLKRT	((uint)0x0000003d)
-#endif
+#endif	/* CONFIG_MBX */
+
+/***  RPXLITE  ********************************************************/
 
 #ifdef CONFIG_RPXLITE
 /* This ENET stuff is for the MPC850 with ethernet on SCC2.  Some of
@@ -416,7 +420,9 @@
 
 #define SICR_ENET_MASK	((uint)0x0000ff00)
 #define SICR_ENET_CLKRT	((uint)0x00003d00)
-#endif
+#endif	/* CONFIG_RPXLITE */
+
+/***  BSEIP  **********************************************************/
 
 #ifdef CONFIG_BSEIP
 /* This ENET stuff is for the MPC823 with ethernet on SCC2.
@@ -438,7 +444,9 @@
 
 #define SICR_ENET_MASK	((uint)0x0000ff00)
 #define SICR_ENET_CLKRT	((uint)0x00002c00)
-#endif
+#endif	/* CONFIG_BSEIP */
+
+/***  RPXCLASSIC  *****************************************************/
 
 #ifdef CONFIG_RPXCLASSIC
 /* Bits in parallel I/O port registers that have to be set/cleared
@@ -457,27 +465,63 @@
  */
 #define SICR_ENET_MASK	((uint)0x000000ff)
 #define SICR_ENET_CLKRT	((uint)0x0000003d)
-#endif
+#endif	/* CONFIG_RPXCLASSIC */
+
+/***  TQM823L, TQM850L  ***********************************************/
+
+#if defined(CONFIG_TQM823L) || defined(CONFIG_TQM850L)
+/* Bits in parallel I/O port registers that have to be set/cleared
+ * to configure the pins for SCC1 use.
+ */
+#define PA_ENET_RXD	((ushort)0x0004)	/* PA 13 */
+#define PA_ENET_TXD	((ushort)0x0008)	/* PA 12 */
+#define PA_ENET_RCLK	((ushort)0x0100)	/* PA  7 */
+#define PA_ENET_TCLK	((ushort)0x0400)	/* PA  5 */
+
+#define PB_ENET_TENA	((uint)0x00002000)	/* PB 18 */
+
+#define PC_ENET_CLSN	((ushort)0x0040)	/* PC  9 */
+#define PC_ENET_RENA	((ushort)0x0080)	/* PC  8 */
+
+/* Control bits in the SICR to route TCLK (CLK3) and RCLK (CLK1) to
+ * SCC2.  Also, make sure GR2 (bit 16) and SC2 (bit 17) are zero.
+ */
+#define SICR_ENET_MASK	((uint)0x0000ff00)
+#define SICR_ENET_CLKRT	((uint)0x00002600)
+#endif	/* CONFIG_TQM823L, CONFIG_TQM850L */
+
+/***  FPS850L  *********************************************************/
+
+#ifdef CONFIG_FPS850L
+/* Bits in parallel I/O port registers that have to be set/cleared
+ * to configure the pins for SCC1 use.
+ */
+#define PA_ENET_RXD	((ushort)0x0004)	/* PA 13 */
+#define PA_ENET_TXD	((ushort)0x0008)	/* PA 12 */
+#define PA_ENET_RCLK	((ushort)0x0100)	/* PA  7 */
+#define PA_ENET_TCLK	((ushort)0x0400)	/* PA  5 */
+
+#define PC_ENET_TENA	((ushort)0x0002)	/* PC 14 */
+#define PC_ENET_CLSN	((ushort)0x0040)	/* PC  9 */
+#define PC_ENET_RENA	((ushort)0x0080)	/* PC  8 */
 
-#if (defined(CONFIG_TQM860) || defined(CONFIG_TQM860L))
-/*
- * TQM860 and TQM860L Configuration:
- *
- * Signal       PAR     DIR     ODR     DAT     Function
- * Port A,  5    1       0       -       -       TCLK (CLK3) for Ethernet
- * Port A,  7    1       0       -       -       RCLK (CLK1) for Ethernet
- * Port A, 14    1       0       -       -       TXD for Ethernet (SCC1)
- * Port A, 15    1       0       -       -       RXD for Ethernet (SCC1)
- * Port C,  7    0       0       0       -       -> ETH-LOOP
- * Port C, 10    0       0       1       -       CD  for Ethernet (SCC1)
- * Port C, 11    0       0       1       -       CTS for Ethernet (SCC1)
- * Port C, 15    *       *       0       -       TENA/RTS for Ethernet
+/* Control bits in the SICR to route TCLK (CLK2) and RCLK (CLK4) to
+ * SCC2.  Also, make sure GR2 (bit 16) and SC2 (bit 17) are zero.
  */
+#define SICR_ENET_MASK	((uint)0x0000ff00)
+#define SICR_ENET_CLKRT	((uint)0x00002600)
+#endif	/* CONFIG_FPS850L */
+
+/***  TQM860L  ********************************************************/
 
+#ifdef CONFIG_TQM860L
+/* Bits in parallel I/O port registers that have to be set/cleared
+ * to configure the pins for SCC1 use.
+ */
 #define PA_ENET_RXD	((ushort)0x0001)	/* PA 15 */
 #define PA_ENET_TXD	((ushort)0x0002)	/* PA 14 */
-#define PA_ENET_TCLK	((ushort)0x0400)	/* PA  5 */
 #define PA_ENET_RCLK	((ushort)0x0100)	/* PA  7 */
+#define PA_ENET_TCLK	((ushort)0x0400)	/* PA  5 */
 
 #define PC_ENET_TENA	((ushort)0x0001)	/* PC 15 */
 #define PC_ENET_CLSN	((ushort)0x0010)	/* PC 11 */
@@ -488,51 +532,59 @@
  */
 #define SICR_ENET_MASK	((uint)0x000000ff)
 #define SICR_ENET_CLKRT	((uint)0x00000026)
+#endif	/* CONFIG_TQM860L */
 
-#endif	/* CONFIG_TQM860, TQM860L */
+/***  SPD823TS  *******************************************************/
 
-#ifdef CONFIG_TQM8xxL
-/*
- * TQM8xxL Configuration (except TQM860L):
- *
- * Signal       PAR     DIR     ODR     DAT     Function
- * Port A,  5    1       0       -       -       TCLK (CLK3) for Ethernet
- * Port A,  7    1       0       -       -       RCLK (CLK1) for Ethernet
- * Port A, 12    1       0       -       -       TXD for Ethernet (SCC2)
- * Port A, 13    1       0       -       -       RXD for Ethernet (SCC2)
- * Port B, 18    1       1       -       -       TENA/RTS for Ethernet on STK8xx
- * Port C,  7    0       0       0       -       -> ETH-LOOP
- * Port C,  8    0       0       1       -       CD  for Ethernet (SCC2)
- * Port C,  9    0       0       1       -       CTS for Ethernet (SCC2)
- * Port C, 14    *       *       0       -       TENA/RTS for Ethernet on FPS850
- *
- * Note: Using PC14 as RTS2 (TENA) does not work on the TQM850L when
- * used with the starter-kit mainboard; we *must* use PB18 instead.
- * For the FPS850 system, we *must* use PC14 :-(
+#ifdef CONFIG_SPD823TS
+/* Bits in parallel I/O port registers that have to be set/cleared
+ * to configure the pins for SCC2 use.
  */
-
+#define PA_ENET_MDC	((ushort)0x0001)	/* PA 15 !!! */
+#define PA_ENET_MDIO	((ushort)0x0002)	/* PA 14 !!! */
 #define PA_ENET_RXD	((ushort)0x0004)	/* PA 13 */
 #define PA_ENET_TXD	((ushort)0x0008)	/* PA 12 */
-#define PA_ENET_RCLK	((ushort)0x0100)	/* PA  7 */
+#define PA_ENET_RCLK	((ushort)0x0200)	/* PA  6 */
 #define PA_ENET_TCLK	((ushort)0x0400)	/* PA  5 */
 
-#ifndef	CONFIG_FPS850	/* not valid on FPS board */
-#define	PB_ENET_TENA	((uint)0x00002000)
-#endif	/* !CONFIG_FPS850 */
+#define PB_ENET_TENA	((uint)0x00002000)	/* PB 18 */
 
-#ifdef	CONFIG_FPS850	/* FPS uses default configuration */
-#define PC_ENET_TENA	((ushort)0x0002)	/* PC 14 */
-#endif	/* CONFIG_FPS850 */
 #define PC_ENET_CLSN	((ushort)0x0040)	/* PC  9 */
 #define PC_ENET_RENA	((ushort)0x0080)	/* PC  8 */
+#define	PC_ENET_RESET	((ushort)0x0100)	/* PC  7 !!! */
 
-/* Control bits in the SICR to route TCLK (CLK3) and RCLK (CLK1) to
+/* Control bits in the SICR to route TCLK (CLK3) and RCLK (CLK2) to
  * SCC2.  Also, make sure GR2 (bit 16) and SC2 (bit 17) are zero.
  */
 #define SICR_ENET_MASK	((uint)0x0000ff00)
-#define SICR_ENET_CLKRT	((uint)0x00002600)
+#define SICR_ENET_CLKRT	((uint)0x00002E00)
+#endif	/* CONFIG_SPD823TS */
+
+
+/***  SM850  *********************************************************/
 
-#endif	/* CONFIG_TQM8xxL */
+/* The SM850 Service Module uses SCC2 for IrDA and SCC3 for Ethernet */
+
+#ifdef CONFIG_SM850
+#define PB_ENET_RXD	((uint)0x00000004)	/* PB 29 */
+#define PB_ENET_TXD	((uint)0x00000002)	/* PB 30 */
+#define PA_ENET_RCLK	((ushort)0x0100)	/* PA  7 */
+#define PA_ENET_TCLK	((ushort)0x0400)	/* PA  5 */
+
+#define PC_ENET_LBK	((ushort)0x0008)	/* PC 12 */
+#define PC_ENET_TENA	((ushort)0x0004)	/* PC 13 */
+
+#define PC_ENET_RENA	((ushort)0x0800)	/* PC  4 */
+#define PC_ENET_CLSN	((ushort)0x0400)	/* PC  5 */
+
+/* Control bits in the SICR to route TCLK (CLK3) and RCLK (CLK1) to
+ * SCC3.  Also, make sure GR3 (bit 8) and SC3 (bit 9) are zero.
+ */
+#define SICR_ENET_MASK	((uint)0x00FF0000)
+#define SICR_ENET_CLKRT	((uint)0x00260000)
+#endif	/* CONFIG_SM850 */
+
+/*********************************************************************/
 
 /* SCC Event register as used by Ethernet.
 */
@@ -723,8 +775,6 @@
 #define	CPMVEC_PIO_PC4		((ushort)0x01)
 #define	CPMVEC_ERROR		((ushort)0x00)
 
-extern void cpm_install_handler(int vec, void (*handler)(void *), void *dev_id);
-
 /* CPM interrupt configuration vector.
 */
 #define	CICR_SCD_SCC4		((uint)0x00c00000)	/* SCC4 @ SCCd */
@@ -735,4 +785,8 @@
 #define CICR_HP_MASK		((uint)0x00001f00)	/* Hi-pri int. */
 #define CICR_IEN		((uint)0x00000080)	/* Int. enable */
 #define CICR_SPS		((uint)0x00000001)	/* SCC Spread */
+
+extern void cpm_install_handler(int vec, void (*handler)(void *), void *dev_id);
+extern void cpm_free_handler(int vec);
+
 #endif /* __CPM_8XX__ */
diff -urN linux-2.4.3-pre1/arch/ppc/8xx_io/enet.c linux/arch/ppc/8xx_io/enet.c
--- linux-2.4.3-pre1/arch/ppc/8xx_io/enet.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/ppc/8xx_io/enet.c	Mon Dec  3 22:29:49 2001
@@ -154,20 +154,26 @@
 /*static	ushort	my_enet_addr[] = { 0x0800, 0x3e26, 0x1559 };*/
 
 /* Typically, 860(T) boards use SCC1 for Ethernet, and other 8xx boards
- * use SCC2.  This is easily extended if necessary.
+ * use SCC2. Some even may use SCC3.
+ * This is easily extended if necessary.
  */
-#ifdef CONFIG_SCC2_ENET
+#if defined(CONFIG_SCC3_ENET)
+#define CPM_CR_ENET	CPM_CR_CH_SCC3
+#define PROFF_ENET	PROFF_SCC3
+#define SCC_ENET	2		/* Index, not number! */
+#define CPMVEC_ENET	CPMVEC_SCC3
+#elif defined(CONFIG_SCC2_ENET)
 #define CPM_CR_ENET	CPM_CR_CH_SCC2
 #define PROFF_ENET	PROFF_SCC2
 #define SCC_ENET	1		/* Index, not number! */
 #define CPMVEC_ENET	CPMVEC_SCC2
-#endif
-
-#ifdef CONFIG_SCC1_ENET
-#define CPM_CR_ENET CPM_CR_CH_SCC1
+#elif defined(CONFIG_SCC1_ENET)
+#define CPM_CR_ENET	CPM_CR_CH_SCC1
 #define PROFF_ENET	PROFF_SCC1
-#define SCC_ENET	0
+#define SCC_ENET	0		/* Index, not number! */
 #define CPMVEC_ENET	CPMVEC_SCC1
+#else
+#error CONFIG_SCCx_ENET not defined
 #endif
 
 static int
@@ -642,10 +648,11 @@
 	volatile	scc_t		*sccp;
 	volatile	scc_enet_t	*ep;
 	volatile	immap_t		*immap;
+	extern unsigned long _get_IMMR(void);
 
 	cp = cpmp;	/* Get pointer to Communication Processor */
 
-	immap = (immap_t *)IMAP_ADDR;	/* and to internal registers */
+	immap = (immap_t *)(_get_IMMR() & 0xFFFF0000);	/* and to internal registers */
 
 	bd = (bd_t *)__res;
 
@@ -683,28 +690,47 @@
 	 * It can't last though......
 	 */
 
+#if (defined(PA_ENET_RXD) && defined(PA_ENET_TXD))
 	/* Configure port A pins for Txd and Rxd.
 	*/
-	immap->im_ioport.iop_papar |= (PA_ENET_RXD | PA_ENET_TXD);
+	immap->im_ioport.iop_papar |=  (PA_ENET_RXD | PA_ENET_TXD);
 	immap->im_ioport.iop_padir &= ~(PA_ENET_RXD | PA_ENET_TXD);
-	immap->im_ioport.iop_paodr &= ~PA_ENET_TXD;
+	immap->im_ioport.iop_paodr &=                ~PA_ENET_TXD;
+#elif (defined(PB_ENET_RXD) && defined(PB_ENET_TXD))
+	/* Configure port B pins for Txd and Rxd.
+	*/
+	immap->im_cpm.cp_pbpar |=  (PB_ENET_RXD | PB_ENET_TXD);
+	immap->im_cpm.cp_pbdir &= ~(PB_ENET_RXD | PB_ENET_TXD);
+	immap->im_cpm.cp_pbodr &=		 ~PB_ENET_TXD;
+#else
+#error Exactly ONE pair of PA_ENET_[RT]XD, PB_ENET_[RT]XD must be defined
+#endif
+
+#if defined(PC_ENET_LBK)
+	/* Configure port C pins to disable External Loopback
+	 */
+	immap->im_ioport.iop_pcpar &= ~PC_ENET_LBK;
+	immap->im_ioport.iop_pcdir |=  PC_ENET_LBK;
+	immap->im_ioport.iop_pcso  &= ~PC_ENET_LBK;
+	immap->im_ioport.iop_pcdat &= ~PC_ENET_LBK;	/* Disable Loopback */
+#endif	/* PC_ENET_LBK */
 
 	/* Configure port C pins to enable CLSN and RENA.
 	*/
 	immap->im_ioport.iop_pcpar &= ~(PC_ENET_CLSN | PC_ENET_RENA);
 	immap->im_ioport.iop_pcdir &= ~(PC_ENET_CLSN | PC_ENET_RENA);
-	immap->im_ioport.iop_pcso |= (PC_ENET_CLSN | PC_ENET_RENA);
+	immap->im_ioport.iop_pcso  |=  (PC_ENET_CLSN | PC_ENET_RENA);
 
 	/* Configure port A for TCLK and RCLK.
 	*/
-	immap->im_ioport.iop_papar |= (PA_ENET_TCLK | PA_ENET_RCLK);
+	immap->im_ioport.iop_papar |=  (PA_ENET_TCLK | PA_ENET_RCLK);
 	immap->im_ioport.iop_padir &= ~(PA_ENET_TCLK | PA_ENET_RCLK);
 
 	/* Configure Serial Interface clock routing.
 	 * First, clear all SCC bits to zero, then set the ones we want.
 	 */
 	cp->cp_sicr &= ~SICR_ENET_MASK;
-	cp->cp_sicr |= SICR_ENET_CLKRT;
+	cp->cp_sicr |=  SICR_ENET_CLKRT;
 
 	/* Manual says set SDDR, but I can't find anything with that
 	 * name.  I think it is a misprint, and should be SDCR.  This
@@ -884,20 +910,17 @@
 	/* It is now OK to enable the Ethernet transmitter.
 	 * Unfortunately, there are board implementation differences here.
 	 */
-#if (defined(CONFIG_MBX) || defined(CONFIG_TQM860) || defined(CONFIG_TQM860L) || defined(CONFIG_FPS850))
-	immap->im_ioport.iop_pcpar |= PC_ENET_TENA;
+#if   (!defined (PB_ENET_TENA) &&  defined (PC_ENET_TENA))
+	immap->im_ioport.iop_pcpar |=  PC_ENET_TENA;
 	immap->im_ioport.iop_pcdir &= ~PC_ENET_TENA;
-#endif
-
-#if (defined(CONFIG_TQM8xxL) && !defined(CONFIG_FPS850))
+#elif ( defined (PB_ENET_TENA) && !defined (PC_ENET_TENA))
 	cp->cp_pbpar |= PB_ENET_TENA;
 	cp->cp_pbdir |= PB_ENET_TENA;
+#else
+#error Configuration Error: define exactly ONE of PB_ENET_TENA, PC_ENET_TENA
 #endif
 
 #if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC)
-	cp->cp_pbpar |= PB_ENET_TENA;
-	cp->cp_pbdir |= PB_ENET_TENA;
-
 	/* And while we are here, set the configuration to enable ethernet.
 	*/
 	*((volatile uint *)RPX_CSR_ADDR) &= ~BCSR0_ETHLPBK;
@@ -906,9 +929,6 @@
 #endif
 
 #ifdef CONFIG_BSEIP
-	cp->cp_pbpar |= PB_ENET_TENA;
-	cp->cp_pbdir |= PB_ENET_TENA;
-
 	/* BSE uses port B and C for PHY control.
 	*/
 	cp->cp_pbpar &= ~(PB_BSE_POWERUP | PB_BSE_FDXDIS);
@@ -941,11 +961,12 @@
 	*/
 	sccp->scc_gsmrl |= (SCC_GSMRL_ENR | SCC_GSMRL_ENT);
 
-	printk("%s: CPM ENET Version 0.2, ", dev->name);
+	printk("%s: CPM ENET Version 0.2 on SCC%d, ", dev->name, SCC_ENET+1);
 	for (i=0; i<5; i++)
 		printk("%02x:", dev->dev_addr[i]);
 	printk("%02x\n", dev->dev_addr[5]);
 
 	return 0;
 }
+
 
diff -urN linux-2.4.3-pre1/arch/ppc/8xx_io/fec.c linux/arch/ppc/8xx_io/fec.c
--- linux-2.4.3-pre1/arch/ppc/8xx_io/fec.c	Fri Feb 16 16:02:34 2001
+++ linux/arch/ppc/8xx_io/fec.c	Mon Dec  3 22:29:49 2001
@@ -8,7 +8,9 @@
  * describes connections using the internal parallel port I/O, which
  * is basically all of Port D.
  *
- * Right now, I am very watseful with the buffers.  I allocate memory
+ * Includes support for the following PHYs: QS6612, LXT970, LXT971/2.
+ *
+ * Right now, I am very wasteful with the buffers.  I allocate memory
  * pages and then divide them into 2K frame buffers.  This way I know I
  * have buffers large enough to hold one frame within one buffer descriptor.
  * Once I get this working, I will use 64 or 128 byte CPM buffers, which
@@ -18,13 +20,16 @@
  * Much better multiple PHY support by Magnus Damm.
  * Copyright (c) 2000 Ericsson Radio Systems AB.
  *
+ * Make use of MII for PHY control configurable.
+ * Some fixes.
+ * Copyright (c) 2000 Wolfgang Denk, DENX Software Engineering.
  */
 
 /* List of PHYs we wish to support.
 */
-#define CONFIG_FEC_LXT970
-#define CONFIG_FEC_LXT971
-#define CONFIG_FEC_QS6612
+#undef	CONFIG_FEC_LXT970
+#define	CONFIG_FEC_LXT971
+#undef	CONFIG_FEC_QS6612
 
 #include <linux/config.h>
 #include <linux/kernel.h>
@@ -54,6 +59,7 @@
 #include <asm/uaccess.h>
 #include "commproc.h"
 
+#ifdef	CONFIG_USE_MDIO
 /* Forward declarations of some structures to support different PHYs
 */
 
@@ -71,6 +77,7 @@
 	const phy_cmd_t *ack_int;
 	const phy_cmd_t *shutdown;
 } phy_info_t;
+#endif	/* CONFIG_USE_MDIO */
 
 /* The number of Tx and Rx buffers.  These are allocated from the page
  * pool.  The code may assume these are power of two, so it is best
@@ -78,20 +85,20 @@
  * We don't need to allocate pages for the transmitter.  We just use
  * the skbuffer directly.
  */
-#if 1
-#define FEC_ENET_RX_PAGES	4
+#ifdef CONFIG_ENET_BIG_BUFFERS
+#define FEC_ENET_RX_PAGES	16
 #define FEC_ENET_RX_FRSIZE	2048
 #define FEC_ENET_RX_FRPPG	(PAGE_SIZE / FEC_ENET_RX_FRSIZE)
 #define RX_RING_SIZE		(FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)
-#define TX_RING_SIZE		8	/* Must be power of two */
-#define TX_RING_MOD_MASK	7	/*   for this to work */
+#define TX_RING_SIZE		16	/* Must be power of two */
+#define TX_RING_MOD_MASK	15	/*   for this to work */
 #else
-#define FEC_ENET_RX_PAGES	16
+#define FEC_ENET_RX_PAGES	4
 #define FEC_ENET_RX_FRSIZE	2048
 #define FEC_ENET_RX_FRPPG	(PAGE_SIZE / FEC_ENET_RX_FRSIZE)
 #define RX_RING_SIZE		(FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES)
-#define TX_RING_SIZE		16	/* Must be power of two */
-#define TX_RING_MOD_MASK	15	/*   for this to work */
+#define TX_RING_SIZE		8	/* Must be power of two */
+#define TX_RING_MOD_MASK	7	/*   for this to work */
 #endif
 
 /* Interrupt events/masks.
@@ -107,6 +114,26 @@
 #define FEC_ENET_MII	((uint)0x00800000)	/* MII interrupt */
 #define FEC_ENET_EBERR	((uint)0x00400000)	/* SDMA bus error */
 
+/*
+*/
+#define FEC_ECNTRL_PINMUX	0x00000004
+#define FEC_ECNTRL_ETHER_EN	0x00000002
+#define FEC_ECNTRL_RESET	0x00000001
+
+#define FEC_RCNTRL_BC_REJ	0x00000010
+#define FEC_RCNTRL_PROM		0x00000008
+#define FEC_RCNTRL_MII_MODE	0x00000004
+#define FEC_RCNTRL_DRT		0x00000002
+#define FEC_RCNTRL_LOOP		0x00000001
+
+#define FEC_TCNTRL_FDEN		0x00000004
+#define FEC_TCNTRL_HBC		0x00000002
+#define FEC_TCNTRL_GTS		0x00000001
+
+/* Delay to wait for FEC reset command to complete (in us)
+*/
+#define FEC_RESET_DELAY		50
+
 /* The FEC stores dest/src/type, data, and checksum for receive packets.
  */
 #define PKT_MAXBUF_SIZE		1518
@@ -138,6 +165,7 @@
 	uint	tx_full;
 	spinlock_t lock;
 
+#ifdef	CONFIG_USE_MDIO
 	uint	phy_id;
 	uint	phy_id_done;
 	uint	phy_status;
@@ -148,6 +176,7 @@
 	uint	sequence_done;
 
 	uint	phy_addr;
+#endif	/* CONFIG_USE_MDIO */
 
 	int	link;
 	int	old_link;
@@ -165,7 +194,9 @@
 
 static int fec_enet_open(struct net_device *dev);
 static int fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev);
+#ifdef	CONFIG_USE_MDIO
 static void fec_enet_mii(struct net_device *dev);
+#endif	/* CONFIG_USE_MDIO */
 static void fec_enet_interrupt(int irq, void * dev_id, struct pt_regs * regs);
 #ifdef CONFIG_FEC_PACKETHOOK
 static void  fec_enet_tx(struct net_device *dev, __u32 regval);
@@ -181,6 +212,7 @@
 static void fec_stop(struct net_device *dev);
 static	ushort	my_enet_addr[3];
 
+#ifdef	CONFIG_USE_MDIO
 /* MII processing.  We keep this as simple as possible.  Requests are
  * placed on the list (if there is room).  When the request is finished
  * by the MII, an optional function may be called.
@@ -197,7 +229,7 @@
 mii_list_t	*mii_head;
 mii_list_t	*mii_tail;
 
-static int	mii_queue(struct net_device *dev, int request, 
+static int	mii_queue(struct net_device *dev, int request,
 				void (*func)(uint, struct net_device *));
 
 /* Make MII read/write commands for the FEC.
@@ -206,11 +238,13 @@
 #define mk_mii_write(REG, VAL)	(0x50020000 | ((REG & 0x1f) << 18) | \
 						(VAL & 0xffff))
 #define mk_mii_end	0
+#endif	/* CONFIG_USE_MDIO */
 
 /* Transmitter timeout.
 */
 #define TX_TIMEOUT (2*HZ)
 
+#ifdef	CONFIG_USE_MDIO
 /* Register definitions for the PHY.
 */
 
@@ -218,7 +252,7 @@
 #define MII_REG_SR          1  /* Status Register                          */
 #define MII_REG_PHYIR1      2  /* PHY Identification Register 1            */
 #define MII_REG_PHYIR2      3  /* PHY Identification Register 2            */
-#define MII_REG_ANAR        4  /* A-N Advertisement Register               */ 
+#define MII_REG_ANAR        4  /* A-N Advertisement Register               */
 #define MII_REG_ANLPAR      5  /* A-N Link Partner Ability Register        */
 #define MII_REG_ANER        6  /* A-N Expansion Register                   */
 #define MII_REG_ANNPTR      7  /* A-N Next Page Transmit Register          */
@@ -230,18 +264,19 @@
 #define PHY_CONF_LOOP	0x0002  /* 1 loopback mode enabled */
 #define PHY_CONF_SPMASK	0x00f0  /* mask for speed */
 #define PHY_CONF_10HDX	0x0010  /* 10 Mbit half duplex supported */
-#define PHY_CONF_10FDX	0x0020  /* 10 Mbit full duplex supported */ 
+#define PHY_CONF_10FDX	0x0020  /* 10 Mbit full duplex supported */
 #define PHY_CONF_100HDX	0x0040  /* 100 Mbit half duplex supported */
-#define PHY_CONF_100FDX	0x0080  /* 100 Mbit full duplex supported */ 
+#define PHY_CONF_100FDX	0x0080  /* 100 Mbit full duplex supported */
 
 #define PHY_STAT_LINK	0x0100  /* 1 up - 0 down */
 #define PHY_STAT_FAULT	0x0200  /* 1 remote fault */
 #define PHY_STAT_ANC	0x0400  /* 1 auto-negotiation complete	*/
 #define PHY_STAT_SPMASK	0xf000  /* mask for speed */
 #define PHY_STAT_10HDX	0x1000  /* 10 Mbit half duplex selected	*/
-#define PHY_STAT_10FDX	0x2000  /* 10 Mbit full duplex selected	*/ 
+#define PHY_STAT_10FDX	0x2000  /* 10 Mbit full duplex selected	*/
 #define PHY_STAT_100HDX	0x4000  /* 100 Mbit half duplex selected */
-#define PHY_STAT_100FDX	0x8000  /* 100 Mbit full duplex selected */ 
+#define PHY_STAT_100FDX	0x8000  /* 100 Mbit full duplex selected */
+#endif	/* CONFIG_USE_MDIO */
 
 #ifdef CONFIG_FEC_PACKETHOOK
 int
@@ -291,7 +326,7 @@
 	fep->ph_proto = 0;
 	fep->ph_regaddr = NULL;
 	fep->ph_priv = NULL;
-	
+
 	fep->ph_lock = 0;
 
 	return retval;
@@ -345,7 +380,7 @@
 
 	fep->stats.tx_bytes += skb->len;
 	fep->skb_cur = (fep->skb_cur+1) & TX_RING_MOD_MASK;
-	
+
 	/* Push the data cache so the CPM does not get stale memory
 	 * data.
 	 */
@@ -404,7 +439,7 @@
 	bdp = fep->tx_bd_base;
 	printk(" tx: %u buffers\n",  TX_RING_SIZE);
 	for (i = 0 ; i < TX_RING_SIZE; i++) {
-		printk("  %08x: %04x %04x %08x\n", 
+		printk("  %08x: %04x %04x %08x\n",
 		       (uint) bdp,
 		       bdp->cbd_sc,
 		       bdp->cbd_datlen,
@@ -443,7 +478,6 @@
 
 	if (fep->ph_regaddr) regval = *fep->ph_regaddr;
 #endif
-
 	fecp = (volatile fec_t*)dev->base_addr;
 
 	/* Get the interrupt events that caused us to be here.
@@ -478,9 +512,13 @@
 		}
 
 		if (int_events & FEC_ENET_MII) {
+#ifdef	CONFIG_USE_MDIO
 			fec_enet_mii(dev);
+#else
+printk("%s[%d] %s: unexpected FEC_ENET_MII event\n", __FILE__,__LINE__,__FUNCTION__);
+#endif	/* CONFIG_USE_MDIO */
 		}
-	
+
 	}
 }
 
@@ -541,23 +579,23 @@
 		 */
 		if (bdp->cbd_sc & BD_ENET_TX_DEF)
 			fep->stats.collisions++;
-	    
+
 		/* Free the sk buffer associated with this last transmit.
 		 */
 #if 0
 printk("TXI: %x %x %x\n", bdp, skb, fep->skb_dirty);
 #endif
-		dev_kfree_skb(skb/*, FREE_WRITE*/);
+		dev_kfree_skb_irq (skb/*, FREE_WRITE*/);
 		fep->tx_skbuff[fep->skb_dirty] = NULL;
 		fep->skb_dirty = (fep->skb_dirty + 1) & TX_RING_MOD_MASK;
-	    
+
 		/* Update pointer to next buffer descriptor to be transmitted.
 		 */
 		if (bdp->cbd_sc & BD_ENET_TX_WRAP)
 			bdp = fep->tx_bd_base;
 		else
 			bdp++;
-	    
+
 		/* Since we have freed up a buffer, the ring is no longer
 		 * full.
 		 */
@@ -617,7 +655,7 @@
 	/* Check for errors. */
 	if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
 			   BD_ENET_RX_CR | BD_ENET_RX_OV)) {
-		fep->stats.rx_errors++;       
+		fep->stats.rx_errors++;
 		if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
 		/* Frame too long or too short. */
 			fep->stats.rx_length_errors++;
@@ -703,7 +741,7 @@
 		bdp = fep->rx_bd_base;
 	else
 		bdp++;
-	
+
 #if 1
 	/* Doing this here will keep the FEC running while we process
 	 * incoming frames.  On a heavily loaded network, we should be
@@ -732,6 +770,7 @@
 }
 
 
+#ifdef	CONFIG_USE_MDIO
 static void
 fec_enet_mii(struct net_device *dev)
 {
@@ -743,7 +782,7 @@
 	fep = (struct fec_enet_private *)dev->priv;
 	ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec);
 	mii_reg = ep->fec_mii_data;
-	
+
 	if ((mip = mii_head) == NULL) {
 		printk("MII and no head!\n");
 		return;
@@ -756,8 +795,9 @@
 	mip->mii_next = mii_free;
 	mii_free = mip;
 
-	if ((mip = mii_head) != NULL)
+	if ((mip = mii_head) != NULL) {
 		ep->fec_mii_data = mip->mii_regval;
+	}
 }
 
 static int
@@ -786,13 +826,11 @@
 		if (mii_head) {
 			mii_tail->mii_next = mip;
 			mii_tail = mip;
-		}
-		else {
+		} else {
 			mii_head = mii_tail = mip;
 			(&(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec))->fec_mii_data = regval;
 		}
-	}
-	else {
+	} else {
 		retval = 1;
 	}
 
@@ -808,7 +846,7 @@
 	if(!c)
 		return;
 
-	for(k = 0; (c+k)->mii_data != mk_mii_end; k++) 
+	for(k = 0; (c+k)->mii_data != mk_mii_end; k++)
 		mii_queue(dev, (c+k)->mii_data, (c+k)->funct);
 }
 
@@ -896,7 +934,7 @@
 }
 
 static phy_info_t phy_info_lxt970 = {
-	0x07810000, 
+	0x07810000,
 	"LXT970",
 
 	(const phy_cmd_t []) {  /* config */
@@ -919,12 +957,12 @@
 	},
 	(const phy_cmd_t []) { /* ack_int */
 		/* read SR and ISR to acknowledge */
-		
+
 		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
 		{ mk_mii_read(MII_LXT970_ISR), NULL },
 
 		/* find out the current status */
-		
+
 		{ mk_mii_read(MII_LXT970_CSR), mii_parse_lxt970_csr },
 		{ mk_mii_end, }
 	},
@@ -933,7 +971,7 @@
 		{ mk_mii_end, }
 	},
 };
-	
+
 #endif /* CONFIG_FEC_LXT970 */
 
 /* ------------------------------------------------------------------------- */
@@ -950,7 +988,7 @@
 #define MII_LXT971_LCR       20  /* LED Control Register      */
 #define MII_LXT971_TCR       30  /* Transmit Control Register */
 
-/* 
+/*
  * I had some nice ideas of running the MDIO faster...
  * The 971 should support 8MHz and I tried it, but things acted really
  * weird, so 2.5 MHz ought to be enough for anyone...
@@ -980,14 +1018,11 @@
 }
 
 static phy_info_t phy_info_lxt971 = {
-	0x0001378e, 
+	0x0001378e,
 	"LXT971",
-	
-	(const phy_cmd_t []) {  /* config */  
-		/* limit to 10MBit because my protorype board 
-		 * doesn't work with 100. */
 
-		{ mk_mii_write(MII_REG_ANAR, 0x061), NULL }, /* 10 MBit */
+	(const phy_cmd_t []) {  /* config */
+//		{ mk_mii_write(MII_REG_ANAR, 0x021), NULL }, /* 10  Mbps, HD */
 		{ mk_mii_read(MII_REG_CR), mii_parse_cr },
 		{ mk_mii_read(MII_REG_ANAR), mii_parse_anar },
 		{ mk_mii_end, }
@@ -995,12 +1030,12 @@
 	(const phy_cmd_t []) {  /* startup - enable interrupts */
 		{ mk_mii_write(MII_LXT971_IER, 0x00f2), NULL },
 		{ mk_mii_write(MII_REG_CR, 0x1200), NULL }, /* autonegotiate */
-	
+
 		/* Somehow does the 971 tell me that the link is down
 		 * the first read after power-up.
 		 * read here to get a valid value in ack_int */
 
-		{ mk_mii_read(MII_REG_SR), mii_parse_sr }, 
+		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
 		{ mk_mii_end, }
 	},
 	(const phy_cmd_t []) { /* ack_int */
@@ -1008,9 +1043,9 @@
 
 		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
 		{ mk_mii_read(MII_LXT971_SR2), mii_parse_lxt971_sr2 },
-		
+
 		/* we only need to read ISR to acknowledge */
-		
+
 		{ mk_mii_read(MII_LXT971_ISR), NULL },
 		{ mk_mii_end, }
 	},
@@ -1053,13 +1088,13 @@
 }
 
 static phy_info_t phy_info_qs6612 = {
-	0x00181440, 
+	0x00181440,
 	"QS6612",
-	
-	(const phy_cmd_t []) {  /* config */  
-//	{ mk_mii_write(MII_REG_ANAR, 0x061), NULL }, /* 10 MBit */
 
-		/* The PHY powers up isolated on the RPX, 
+	(const phy_cmd_t []) {  /* config */
+//	{ mk_mii_write(MII_REG_ANAR, 0x061), NULL }, /* 10  Mbps */
+
+		/* The PHY powers up isolated on the RPX,
 		 * so send a command to allow operation.
 		 */
 
@@ -1077,9 +1112,9 @@
 		{ mk_mii_end, }
 	},
 	(const phy_cmd_t []) { /* ack_int */
-		
+
 		/* we need to read ISR, SR and ANER to acknowledge */
-		
+
 		{ mk_mii_read(MII_QS6612_ISR), NULL },
 		{ mk_mii_read(MII_REG_SR), mii_parse_sr },
 		{ mk_mii_read(MII_REG_ANER), NULL },
@@ -1134,10 +1169,10 @@
 		printk("link up");
 
 		switch(*s & PHY_STAT_SPMASK) {
-		case PHY_STAT_100FDX: printk(", 100MBit Full Duplex"); break;
-		case PHY_STAT_100HDX: printk(", 100MBit Half Duplex"); break;
-		case PHY_STAT_10FDX: printk(", 10MBit Full Duplex"); break;
-		case PHY_STAT_10HDX: printk(", 10MBit Half Duplex"); break;
+		case PHY_STAT_100FDX: printk(", 100 Mbps Full Duplex"); break;
+		case PHY_STAT_100HDX: printk(", 100 Mbps Half Duplex"); break;
+		case PHY_STAT_10FDX: printk(", 10 Mbps Full Duplex"); break;
+		case PHY_STAT_10HDX: printk(", 10 Mbps Half Duplex"); break;
 		default:
 			printk(", Unknown speed/duplex");
 		}
@@ -1177,7 +1212,7 @@
 
 	if (*s & PHY_CONF_LOOP)
 		printk(", loopback enabled");
-	
+
 	printk(".\n");
 
 	fep->sequence_done = 1;
@@ -1194,7 +1229,7 @@
 
 	if (fep->link) {
 		duplex = 0;
-		if (fep->phy_status 
+		if (fep->phy_status
 		    & (PHY_STAT_100FDX | PHY_STAT_10FDX))
 			duplex = 1;
 		fec_restart(dev, duplex);
@@ -1245,18 +1280,20 @@
 
 	fep = dev->priv;
 	fep->phy_id |= (mii_reg & 0xffff);
-	printk("fec: Phy @ 0x%x, type 0x%08x\n", fep->phy_addr, fep->phy_id);
 
 	for(i = 0; phy_info[i]; i++)
 		if(phy_info[i]->id == (fep->phy_id >> 4))
 			break;
 
 	if(!phy_info[i])
-		panic("%s: PHY id 0x%08x is not supported!\n", 
+		panic("%s: PHY id 0x%08x is not supported!\n",
 		      dev->name, fep->phy_id);
-      
+
 	fep->phy = phy_info[i];
 	fep->phy_id_done = 1;
+
+	printk("%s: Phy @ 0x%x, type %s (0x%08x)\n",
+		dev->name, fep->phy_addr, fep->phy->name, fep->phy_id);
 }
 
 /* Scan all of the MII PHY addresses looking for someone to respond
@@ -1270,25 +1307,23 @@
 
 	fep = dev->priv;
 
-	if (fep->phy_addr < 32) {
-		if ((phytype = (mii_reg & 0xffff)) != 0xffff) {
-			
-			/* Got first part of ID, now get remainder.
-			*/
-			fep->phy_id = phytype << 16;
-			mii_queue(dev, mk_mii_read(MII_REG_PHYIR2),
-							mii_discover_phy3);
-		}
-		else {
-			fep->phy_addr++;
+	if ((phytype = (mii_reg & 0xffff)) != 0xffff) {
+
+		/* Got first part of ID, now get remainder.
+		*/
+		fep->phy_id = phytype << 16;
+		mii_queue(dev, mk_mii_read(MII_REG_PHYIR2), mii_discover_phy3);
+	} else {
+		fep->phy_addr++;
+		if (fep->phy_addr < 32) {
 			mii_queue(dev, mk_mii_read(MII_REG_PHYIR1),
 							mii_discover_phy);
+		} else {
+			printk("fec: No PHY device found.\n");
 		}
 	}
-	else {
-		printk("FEC: No PHY device found.\n");
-	}
 }
+#endif	/* CONFIG_USE_MDIO */
 
 /* This interrupt occurs when the PHY detects a link change.
 */
@@ -1299,16 +1334,36 @@
 mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs)
 #endif
 {
+#ifdef	CONFIG_USE_MDIO
 	struct	net_device *dev = dev_id;
 	struct fec_enet_private *fep = dev->priv;
+	volatile immap_t *immap = (immap_t *)IMAP_ADDR;
+	volatile fec_t *fecp = &(immap->im_cpm.cp_fec);
+	unsigned int ecntrl = fecp->fec_ecntrl;
+
+	/* We need the FEC enabled to access the MII
+	*/
+	if ((ecntrl & FEC_ECNTRL_ETHER_EN) == 0) {
+		fecp->fec_ecntrl |= FEC_ECNTRL_ETHER_EN;
+	}
+#endif	/* CONFIG_USE_MDIO */
 
 #if 0
 	disable_irq(fep->mii_irq);  /* disable now, enable later */
 #endif
 
+
+#ifdef	CONFIG_USE_MDIO
 	mii_do_cmd(dev, fep->phy->ack_int);
 	mii_do_cmd(dev, phy_cmd_relink);  /* restart and display status */
 
+	if ((ecntrl & FEC_ECNTRL_ETHER_EN) == 0) {
+		fecp->fec_ecntrl = ecntrl;	/* restore old settings */
+	}
+#else
+printk("%s[%d] %s: unexpected Link interrupt\n", __FILE__,__LINE__,__FUNCTION__);
+#endif	/* CONFIG_USE_MDIO */
+
 }
 
 static int
@@ -1320,6 +1375,7 @@
 	 * a simple way to do that.
 	 */
 
+#ifdef	CONFIG_USE_MDIO
 	fep->sequence_done = 0;
 	fep->link = 0;
 
@@ -1327,7 +1383,6 @@
 		mii_do_cmd(dev, fep->phy->ack_int);
 		mii_do_cmd(dev, fep->phy->config);
 		mii_do_cmd(dev, phy_cmd_config);  /* display configuration */
-
 		while(!fep->sequence_done)
 			schedule();
 
@@ -1335,8 +1390,12 @@
 		netif_start_queue(dev);
 		return 0;		/* Success */
 	}
-
 	return -ENODEV;		/* No PHY we understand */
+#else
+	fep->link = 1;
+	netif_start_queue(dev);
+	return 0;	/* Success */
+#endif	/* CONFIG_USE_MDIO */
 
 }
 
@@ -1377,13 +1436,13 @@
 	ep = &(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec);
 
 	if (dev->flags&IFF_PROMISC) {
-	  
+
 		/* Log any net taps. */
 		printk("%s: Promiscuous mode enabled.\n", dev->name);
-		ep->fec_r_cntrl |= 0x0008;
+		ep->fec_r_cntrl |= FEC_RCNTRL_PROM;
 	} else {
 
-		ep->fec_r_cntrl &= ~0x0008;
+		ep->fec_r_cntrl &= ~FEC_RCNTRL_PROM;
 
 		if (dev->flags & IFF_ALLMULTI) {
 			/* Catch all multicast addresses, so set the
@@ -1404,7 +1463,7 @@
 			dmi = dev->mc_list;
 
 			for (i=0; i<dev->mc_count; i++) {
-				
+
 				/* Only support group multicast for now.
 				*/
 				if (!(dmi->dmi_addr[0] & 1))
@@ -1448,7 +1507,7 @@
 	volatile	fec_t	*fecp;
 	bd_t		*bd;
 	extern		uint	_get_IMMR(void);
-#ifdef CONFIG_RPXCLASSIC
+#ifdef CONFIG_SCC_ENET
 	unsigned char	tmpaddr[6];
 #endif
 
@@ -1472,8 +1531,15 @@
 
 	/* Whack a reset.  We should wait for this.
 	*/
-	fecp->fec_ecntrl = 1;
-	udelay(10);
+	fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET;
+	for (i = 0;
+	     (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY);
+	     ++i) {
+		udelay(1);
+	}
+	if (i == FEC_RESET_DELAY) {
+		printk ("FEC Reset timeout!\n");
+	}
 
 	/* Set the Ethernet address.  If using multiple Enets on the 8xx,
 	 * this needs some work to get unique addresses.
@@ -1481,12 +1547,13 @@
 	eap = (unsigned char *)my_enet_addr;
 	iap = bd->bi_enetaddr;
 
-#ifdef CONFIG_RPXCLASSIC
-	/* The Embedded Planet boards have only one MAC address in
-	 * the EEPROM, but can have two Ethernet ports.  For the
-	 * FEC port, we create another address by setting one of
-	 * the address bits above something that would have (up to
-	 * now) been allocated.
+#ifdef CONFIG_SCC_ENET
+	/*
+         * If a board has Ethernet configured both on a SCC and the
+         * FEC, it needs (at least) 2 MAC addresses (we know that Sun
+         * disagrees, but anyway). For the FEC port, we create
+         * another address by setting one of the address bits above
+         * something that would have (up to now) been allocated.
 	 */
 	for (i=0; i<6; i++)
 		tmpaddr[i] = *iap++;
@@ -1494,8 +1561,9 @@
 	iap = tmpaddr;
 #endif
 
-	for (i=0; i<6; i++)
+	for (i=0; i<6; i++) {
 		dev->dev_addr[i] = *eap++ = *iap++;
+	}
 
 	/* Allocate memory for buffer descriptors.
 	*/
@@ -1518,9 +1586,6 @@
 	fep->rx_bd_base = cbd_base;
 	fep->tx_bd_base = cbd_base + RX_RING_SIZE;
 
-	fep->dirty_tx = fep->cur_tx = fep->tx_bd_base;
-	fep->cur_rx = fep->rx_bd_base;
-
 	fep->skb_cur = fep->skb_dirty = 0;
 
 	/* Initialize the receive buffer descriptors.
@@ -1565,22 +1630,27 @@
 	*/
 	if (request_8xxirq(FEC_INTERRUPT, fec_enet_interrupt, 0, "fec", dev) != 0)
 		panic("Could not allocate FEC IRQ!");
+
 #ifdef CONFIG_RPXCLASSIC
 	/* Make Port C, bit 15 an input that causes interrupts.
 	*/
 	immap->im_ioport.iop_pcpar &= ~0x0001;
 	immap->im_ioport.iop_pcdir &= ~0x0001;
-	immap->im_ioport.iop_pcso &= ~0x0001;
-	immap->im_ioport.iop_pcint |= 0x0001;
+	immap->im_ioport.iop_pcso  &= ~0x0001;
+	immap->im_ioport.iop_pcint |=  0x0001;
 	cpm_install_handler(CPMVEC_PIO_PC15, mii_link_interrupt, dev);
 
 	/* Make LEDS reflect Link status.
 	*/
 	*((uint *) RPX_CSR_ADDR) &= ~BCSR2_FETHLEDMODE;
 #endif
-#ifdef CONFIG_FADS
-	if (request_8xxirq(SIU_IRQ2, mii_link_interrupt, 0, "mii", dev) != 0)
+
+#ifdef PHY_INTERRUPT
+	if (request_8xxirq(PHY_INTERRUPT, mii_link_interrupt, 0, "mii", dev) != 0)
 		panic("Could not allocate MII IRQ!");
+
+	((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel |=
+		(0x80000000 >> PHY_INTERRUPT);
 #endif
 
 	dev->base_addr = (unsigned long)fecp;
@@ -1595,9 +1665,11 @@
 	dev->get_stats = fec_enet_get_stats;
 	dev->set_multicast_list = set_multicast_list;
 
+#ifdef	CONFIG_USE_MDIO
 	for (i=0; i<NMII-1; i++)
 		mii_cmds[i].mii_next = &mii_cmds[i+1];
 	mii_free = mii_cmds;
+#endif	/* CONFIG_USE_MDIO */
 
 	/* Configure all of port D for MII.
 	*/
@@ -1609,23 +1681,46 @@
 		immap->im_ioport.iop_pddir = 0x1c58;	/* Pre rev. D */
 	else
 		immap->im_ioport.iop_pddir = 0x1fff;	/* Rev. D and later */
-	
+
+#ifdef	CONFIG_USE_MDIO
 	/* Set MII speed to 2.5 MHz
 	*/
-	fecp->fec_mii_speed = fep->phy_speed = 
-		((bd->bi_busfreq * 1000000) / 2500000) & 0x7e;
+	fecp->fec_mii_speed = fep->phy_speed =
+		(
+		  ( ((bd->bi_intfreq * 1000000) + 500000) / 2500000 / 2 )
+		  & 0x3F
+		) << 1;
+#else
+	fecp->fec_mii_speed = 0;	/* turn off MDIO */
+#endif	/* CONFIG_USE_MDIO */
 
-	printk("%s: FEC ENET Version 0.2, ", dev->name);
-	for (i=0; i<5; i++)
-		printk("%02x:", dev->dev_addr[i]);
-	printk("%02x\n", dev->dev_addr[5]);
+	printk ("%s: FEC ENET Version 0.2, FEC irq %d"
+#ifdef PHY_INTERRUPT
+		", MII irq %d"
+#endif
+		", addr ",
+		dev->name, FEC_INTERRUPT
+#ifdef PHY_INTERRUPT
+		, PHY_INTERRUPT
+#endif
+	);
+	for (i=0; i<6; i++)
+		printk("%02x%c", dev->dev_addr[i], (i==5) ? '\n' : ':');
 
+#ifdef	CONFIG_USE_MDIO	/* start in full duplex mode, and negotiate speed */
+	fec_restart (dev, 1);
+#else			/* always use half duplex mode only */
+	fec_restart (dev, 0);
+#endif
+
+#ifdef	CONFIG_USE_MDIO
 	/* Queue up command to detect the PHY and initialize the
 	 * remainder of the interface.
 	 */
 	fep->phy_id_done = 0;
 	fep->phy_addr = 0;
 	mii_queue(dev, mk_mii_read(MII_REG_PHYIR1), mii_discover_phy);
+#endif	/* CONFIG_USE_MDIO */
 
 	return 0;
 }
@@ -1639,7 +1734,6 @@
 {
 	struct fec_enet_private *fep;
 	int i;
-	unsigned char	*eap;
 	volatile	cbd_t	*bdp;
 	volatile	immap_t	*immap;
 	volatile	fec_t	*fecp;
@@ -1652,33 +1746,25 @@
 
 	/* Whack a reset.  We should wait for this.
 	*/
-	fecp->fec_ecntrl = 1;
-	udelay(10);
-
-	/* Enable interrupts we wish to service.
-	*/
-	fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB |
-				FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII);
-
-	/* Clear any outstanding interrupt.
-	*/
-	fecp->fec_ievent = 0xffc0;
-
-	fecp->fec_ivec = (FEC_INTERRUPT/2) << 29;
+	fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_RESET;
+	for (i = 0;
+	     (fecp->fec_ecntrl & FEC_ECNTRL_RESET) && (i < FEC_RESET_DELAY);
+	     ++i) {
+		udelay(1);
+	}
+	if (i == FEC_RESET_DELAY) {
+		printk ("FEC Reset timeout!\n");
+	}
 
 	/* Set station address.
 	*/
-	fecp->fec_addr_low = (my_enet_addr[0] << 16) | my_enet_addr[1];
-	fecp->fec_addr_high = my_enet_addr[2];
-
-	eap = (unsigned char *)&my_enet_addr[0];
-	for (i=0; i<6; i++)
-		dev->dev_addr[i] = *eap++;
+	fecp->fec_addr_low  = (my_enet_addr[0] << 16) | my_enet_addr[1];
+	fecp->fec_addr_high =  my_enet_addr[2];
 
 	/* Reset all multicast.
 	*/
 	fecp->fec_hash_table_high = 0;
-	fecp->fec_hash_table_low = 0;
+	fecp->fec_hash_table_low  = 0;
 
 	/* Set maximum receive buffer size.
 	*/
@@ -1739,12 +1825,12 @@
 	/* Enable MII mode.
 	*/
 	if (duplex) {
-		fecp->fec_r_cntrl = 0x04;	/* MII enable */
-		fecp->fec_x_cntrl = 0x04;	/* FD enable */
+		fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE;	/* MII enable */
+		fecp->fec_x_cntrl = FEC_TCNTRL_FDEN;		/* FD enable */
 	}
 	else {
-		fecp->fec_r_cntrl = 0x06;	/* MII enable|No Rcv on Xmit */
-		fecp->fec_x_cntrl = 0x00;
+		fecp->fec_r_cntrl = FEC_RCNTRL_MII_MODE | FEC_RCNTRL_DRT;
+		fecp->fec_x_cntrl = 0;
 	}
 	fep->full_duplex = duplex;
 
@@ -1752,13 +1838,26 @@
 	*/
 	fecp->fec_fun_code = 0x78000000;
 
+#ifdef	CONFIG_USE_MDIO
 	/* Set MII speed.
 	*/
 	fecp->fec_mii_speed = fep->phy_speed;
+#endif	/* CONFIG_USE_MDIO */
+
+	/* Clear any outstanding interrupt.
+	*/
+	fecp->fec_ievent = 0xffc0;
+
+	fecp->fec_ivec = (FEC_INTERRUPT/2) << 29;
+
+	/* Enable interrupts we wish to service.
+	*/
+	fecp->fec_imask = ( FEC_ENET_TXF | FEC_ENET_TXB |
+			    FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII );
 
 	/* And last, enable the transmit and receive processing.
 	*/
-	fecp->fec_ecntrl = 6;
+	fecp->fec_ecntrl = FEC_ECNTRL_PINMUX | FEC_ECNTRL_ETHER_EN;
 	fecp->fec_r_des_active = 0x01000000;
 }
 
@@ -1768,33 +1867,45 @@
 	volatile	immap_t	*immap;
 	volatile	fec_t	*fecp;
 	struct fec_enet_private *fep;
+	int i;
 
 	immap = (immap_t *)IMAP_ADDR;	/* pointer to internal registers */
-	
+
 	fecp = &(immap->im_cpm.cp_fec);
-	
+
+	if ((fecp->fec_ecntrl & FEC_ECNTRL_ETHER_EN) == 0)
+		return;	/* already down */
+
 	fep = dev->priv;
 
 
 	fecp->fec_x_cntrl = 0x01;	/* Graceful transmit stop */
 
-	while(!(fecp->fec_ievent & 0x10000000));
-
-	/* Whack a reset.  We should wait for this.
-	*/
-	fecp->fec_ecntrl = 1;
-	udelay(10);
+	for (i = 0;
+	     ((fecp->fec_ievent & 0x10000000) == 0) && (i < FEC_RESET_DELAY);
+	     ++i) {
+		udelay(1);
+	}
+	if (i == FEC_RESET_DELAY) {
+		printk ("FEC timeout on graceful transmit stop\n");
+	}
 
 	/* Clear outstanding MII command interrupts.
 	*/
 	fecp->fec_ievent = FEC_ENET_MII;
 
-	/* Enable MII command finihed interrupt 
+	/* Enable MII command finished interrupt
 	*/
 	fecp->fec_ivec = (FEC_INTERRUPT/2) << 29;
 	fecp->fec_imask = FEC_ENET_MII;
 
+#ifdef	CONFIG_USE_MDIO
 	/* Set MII speed.
 	*/
 	fecp->fec_mii_speed = fep->phy_speed;
+#endif	/* CONFIG_USE_MDIO */
+
+	/* Disable FEC
+	*/
+	fecp->fec_ecntrl &= ~(FEC_ECNTRL_ETHER_EN);
 }
diff -urN linux-2.4.3-pre1/arch/ppc/8xx_io/uart.c linux/arch/ppc/8xx_io/uart.c
--- linux-2.4.3-pre1/arch/ppc/8xx_io/uart.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/ppc/8xx_io/uart.c	Mon Dec  3 22:29:49 2001
@@ -53,10 +53,22 @@
 
 /* this defines the index into rs_table for the port to use
 */
-#ifndef CONFIG_SERIAL_CONSOLE_PORT
-#define CONFIG_SERIAL_CONSOLE_PORT	0
-#endif
-#endif
+# ifndef CONFIG_SERIAL_CONSOLE_PORT
+#  ifdef CONFIG_SCC3_ENET
+#   ifdef CONFIG_8xx_CONS_SMC2
+#    define CONFIG_SERIAL_CONSOLE_PORT	0	/* Console on SMC2 is 1st port */
+#   else
+#    error "Can't use SMC1 for console with Ethernet on SCC3"
+#   endif
+#  else	/* ! CONFIG_SCC3_ENET */
+#   ifdef CONFIG_8xx_CONS_SMC2			/* Console on SMC2 */
+#    define CONFIG_SERIAL_CONSOLE_PORT	1
+#   else					/* Console on SMC1 */
+#    define CONFIG_SERIAL_CONSOLE_PORT	0
+#   endif /* CONFIG_8xx_CONS_SMC2 */
+#  endif  /* CONFIG_SCC3_ENET */
+# endif	  /* CONFIG_SERIAL_CONSOLE_PORT */
+#endif	  /* CONFIG_SERIAL_CONSOLE */
 
 #if 0
 /* SCC2 for console
@@ -118,14 +130,22 @@
  */
 static struct serial_state rs_table[] = {
 	/* UART CLK   PORT          IRQ      FLAGS  NUM   */
-	{ 0,     0, PROFF_SMC1, CPMVEC_SMC1,   0,    0 },    /* SMC1 ttyS0 */
-#ifdef CONFIG_8xxSMC2
-	{ 0,     0, PROFF_SMC2, CPMVEC_SMC2,   0,    1 },    /* SMC2 ttyS1 */
-#endif
-#ifdef CONFIG_8xxSCC
-	{ 0,     0, PROFF_SCC2, CPMVEC_SCC2,   0,    (NUM_IS_SCC | 1) },    /* SCC2 ttyS2 */
-	{ 0,     0, PROFF_SCC3, CPMVEC_SCC3,   0,    (NUM_IS_SCC | 2) },    /* SCC3 ttyS3 */
+#ifndef CONFIG_SCC3_ENET	/* SMC1 not usable with Ethernet on SCC3 */
+  	{ 0,     0, PROFF_SMC1, CPMVEC_SMC1,   0,    0 },    /* SMC1 ttyS0 */
 #endif
+#if !defined(CONFIG_USB_MPC8xx) && !defined(CONFIG_USB_CLIENT_MPC8xx)
+# ifdef CONFIG_8xxSMC2
+  	{ 0,     0, PROFF_SMC2, CPMVEC_SMC2,   0,    1 },    /* SMC2 ttyS1 */
+# endif
+# ifdef CONFIG_8xxSCC
+  	{ 0,     0, PROFF_SCC2, CPMVEC_SCC2,   0,    (NUM_IS_SCC | 1) },    /* SCC2 ttyS2 */
+  	{ 0,     0, PROFF_SCC3, CPMVEC_SCC3,   0,    (NUM_IS_SCC | 2) },    /* SCC3 ttyS3 */
+# endif
+  #else /* CONFIG_USB_xxx */
+# ifdef CONFIG_8xxSCC
+  	{ 0,     0, PROFF_SCC3, CPMVEC_SCC3,   0,    (NUM_IS_SCC | 2) },    /* SCC3 ttyS3 */
+# endif
+#endif	/* CONFIG_USB_xxx */
 };
 
 #define NR_PORTS	(sizeof(rs_table)/sizeof(struct serial_state))
@@ -2733,10 +2753,10 @@
 				cp->cp_pbdir &= ~iobits;
 				cp->cp_pbodr &= ~iobits;
 #else
+				iobits = 0xc0;
 				if (idx == 0) {
 					/* SMC1 on Port B, like all 8xx.
 					*/
-					iobits = 0xc0;
 					cp->cp_pbpar |= iobits;
 					cp->cp_pbdir &= ~iobits;
 					cp->cp_pbodr &= ~iobits;
@@ -2744,7 +2764,6 @@
 				else {
 					/* SMC2 is on Port A.
 					*/
-					iobits = 0x300;
 					immap->im_ioport.iop_papar |= iobits;
 					immap->im_ioport.iop_padir &= ~iobits;
 					immap->im_ioport.iop_paodr &= ~iobits;
@@ -2836,6 +2855,9 @@
 	for (bidx = 0; bidx < (sizeof(baud_table) / sizeof(int)); bidx++)
 		if (bd->bi_baudrate == baud_table[bidx])
 			break;
+	/* make sure we have a useful value */
+	if (bidx == (sizeof(baud_table) / sizeof(int)))
+		bidx = 13;	/* B9600 */
 
 	co->cflag = CREAD|CLOCAL|bidx|CS8;
 	baud_idx = bidx;
@@ -2958,7 +2980,7 @@
 		*/
 		chan = smc_chan_map[idx];
 		cp->cp_cpcr = mk_cr_cmd(chan, CPM_CR_INIT_TRX) | CPM_CR_FLG;
-		printk("");
+		printk("%s", "");
 		while (cp->cp_cpcr & CPM_CR_FLG);
 
 		/* Set UART mode, 8 bit, no parity, one stop.
diff -urN linux-2.4.3-pre1/arch/ppc/config.in linux/arch/ppc/config.in
--- linux-2.4.3-pre1/arch/ppc/config.in	Mon Jan 22 15:41:14 2001
+++ linux/arch/ppc/config.in	Mon Dec  3 22:29:49 2001
@@ -57,14 +57,25 @@
 	"RPX-Lite	CONFIG_RPXLITE		\
 	 RPX-Classic	CONFIG_RPXCLASSIC	\
 	 BSE-IP		CONFIG_BSEIP		\
-	 TQM8xxL	CONFIG_TQM8xxL		\
-	 TQM860L	CONFIG_TQM860L		\
-	 TQM860		CONFIG_TQM860		\
+ 	 TQM823L	CONFIG_TQM823L		\
+ 	 TQM850L	CONFIG_TQM850L		\
+ 	 TQM855L	CONFIG_TQM855L		\
+  	 TQM860L	CONFIG_TQM860L		\
+ 	 FPS850L	CONFIG_FPS850L		\
+  	 TQM860		CONFIG_TQM860		\
+ 	 SPD823TS	CONFIG_SPD823TS		\
+ 	 IVMS8		CONFIG_IVMS8		\
+ 	 SM850		CONFIG_SM850		\
 	 MBX		CONFIG_MBX		\
 	 WinCept	CONFIG_WINCEPT"		RPX-Lite
-	 
-  if [ "$CONFIG_TQM8xxL" = "y" ]; then
-    bool 'FPS850 Mainboard' CONFIG_FPS850
+
+  if [ "$CONFIG_TQM823L" = "y" -o \
+       "$CONFIG_TQM850L" = "y" -o \
+       "$CONFIG_FPS850L" = "y" -o \
+       "$CONFIG_TQM855L" = "y" -o \
+       "$CONFIG_TQM860L" = "y" -o \
+       "$CONFIG_SM850"   = "y" ]; then
+ 	define_bool	CONFIG_TQM8xxL	y
   fi
 fi
 
@@ -298,6 +309,11 @@
     else
       bool '   Support for ADB keyboard (old driver)' CONFIG_ADB_KEYBOARD
     fi
+  fi
+  # This is for drivers/macintosh/mac_hid.o, which is needed if the input
+  # layer is used.
+  if [ "$CONFIG_INPUT" != "n" ]; then
+    define_bool CONFIG_MAC_HID y
   fi
 fi
 endmenu
diff -urN linux-2.4.3-pre1/arch/ppc/configs/IVMS8_defconfig linux/arch/ppc/configs/IVMS8_defconfig
--- linux-2.4.3-pre1/arch/ppc/configs/IVMS8_defconfig	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/configs/IVMS8_defconfig	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,452 @@
+#
+# Automatically generated make config: don't edit
+#
+# CONFIG_UID16 is not set
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+# CONFIG_6xx is not set
+# CONFIG_4xx is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8260 is not set
+CONFIG_8xx=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_RPXLITE is not set
+# CONFIG_RPXCLASSIC is not set
+# CONFIG_BSEIP is not set
+# CONFIG_TQM823L is not set
+# CONFIG_TQM850L is not set
+# CONFIG_TQM855L is not set
+# CONFIG_TQM860L is not set
+# CONFIG_FPS850L is not set
+# CONFIG_TQM860 is not set
+# CONFIG_SPD823TS is not set
+CONFIG_IVMS8=y
+# CONFIG_SM850 is not set
+# CONFIG_MBX is not set
+# CONFIG_WINCEPT is not set
+# CONFIG_ALL_PPC is not set
+# CONFIG_SMP is not set
+CONFIG_MACH_SPECIFIC=y
+CONFIG_MATH_EMULATION=y
+CONFIG_SASH=y
+CONFIG_SASH_PATH="/bin/sash"
+
+#
+# General setup
+#
+# CONFIG_ISA is not set
+# CONFIG_SBUS is not set
+# CONFIG_PCI is not set
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_KCORE_ELF=y
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_HOTPLUG is not set
+# CONFIG_PCMCIA is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_LVM is not set
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID5 is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_INITRD is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK=y
+# CONFIG_RTNETLINK is not set
+# CONFIG_NETLINK_DEV is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_INET_ECN is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+# CONFIG_ATM is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_IDEDISK_MULTI_MODE=y
+# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
+# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
+# CONFIG_BLK_DEV_IDEDISK_IBM is not set
+# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
+# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
+# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
+# CONFIG_BLK_DEV_IDEDISK_WD is not set
+# CONFIG_BLK_DEV_COMMERIAL is not set
+# CONFIG_BLK_DEV_TIVO is not set
+# CONFIG_BLK_DEV_IDECS 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
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+CONFIG_BLK_DEV_MPC8xx_IDE=y
+# CONFIG_IDE_CHIPSETS is not set
+# CONFIG_IDEDMA_AUTO is not set
+# CONFIG_DMA_NONPCI is not set
+CONFIG_BLK_DEV_IDE_MODES=y
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MACE is not set
+# CONFIG_BMAC is not set
+# CONFIG_GMAC is not set
+# CONFIG_NCR885E is not set
+# CONFIG_OAKNET is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+# CONFIG_IRDA is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Console drivers
+#
+
+#
+# Frame-buffer support
+#
+# CONFIG_FB is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=32
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_INTEL_RNG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_FLASH is not set
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_DRM is not set
+# CONFIG_AGP is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_RAMFS is not set
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_SYSV_FS_WRITE is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V3 is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_MOUNT_SUBDIR is not set
+# CONFIG_NCPFS_NDS_DOMAINS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+CONFIG_MAC_PARTITION=y
+# CONFIG_MSDOS_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_NLS is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# MPC8xx CPM Options
+#
+# CONFIG_SCC_ENET is not set
+CONFIG_FEC_ENET=y
+CONFIG_USE_MDIO=y
+CONFIG_ENET_BIG_BUFFERS=y
+# CONFIG_8xxSMC2 is not set
+# CONFIG_8xxSCC is not set
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+# CONFIG_8xx_CPU6 is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_KGDB is not set
+# CONFIG_XMON is not set
diff -urN linux-2.4.3-pre1/arch/ppc/configs/SM850_defconfig linux/arch/ppc/configs/SM850_defconfig
--- linux-2.4.3-pre1/arch/ppc/configs/SM850_defconfig	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/configs/SM850_defconfig	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,420 @@
+#
+# Automatically generated make config: don't edit
+#
+# CONFIG_UID16 is not set
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+# CONFIG_6xx is not set
+# CONFIG_4xx is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8260 is not set
+CONFIG_8xx=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_RPXLITE is not set
+# CONFIG_RPXCLASSIC is not set
+# CONFIG_BSEIP is not set
+# CONFIG_TQM823L is not set
+# CONFIG_TQM850L is not set
+# CONFIG_TQM855L is not set
+# CONFIG_TQM860L is not set
+# CONFIG_FPS850L is not set
+# CONFIG_TQM860 is not set
+# CONFIG_SPD823TS is not set
+CONFIG_SM850=y
+# CONFIG_MBX is not set
+# CONFIG_WINCEPT is not set
+CONFIG_TQM8xxL=y
+# CONFIG_ALL_PPC is not set
+# CONFIG_SMP is not set
+CONFIG_MACH_SPECIFIC=y
+CONFIG_MATH_EMULATION=y
+CONFIG_SASH=y
+CONFIG_SASH_PATH="/bin/sash"
+
+#
+# General setup
+#
+# CONFIG_ISA is not set
+# CONFIG_SBUS is not set
+# CONFIG_PCI is not set
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_KCORE_ELF=y
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_HOTPLUG is not set
+# CONFIG_PCMCIA is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_LVM is not set
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID5 is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_INITRD is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK=y
+# CONFIG_RTNETLINK is not set
+# CONFIG_NETLINK_DEV is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_INET_ECN is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+# CONFIG_ATM is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MACE is not set
+# CONFIG_BMAC is not set
+# CONFIG_GMAC is not set
+# CONFIG_NCR885E is not set
+# CONFIG_OAKNET is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+# CONFIG_IRDA is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Console drivers
+#
+
+#
+# Frame-buffer support
+#
+# CONFIG_FB is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=32
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_INTEL_RNG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+CONFIG_FLASH=y
+CONFIG_AMD_FLASH=y
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_DRM is not set
+# CONFIG_AGP is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_RAMFS is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_EXT2_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_SYSV_FS_WRITE is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V3 is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_MOUNT_SUBDIR is not set
+# CONFIG_NCPFS_NDS_DOMAINS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_MSDOS_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_NLS is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# MPC8xx CPM Options
+#
+CONFIG_SCC_ENET=y
+# CONFIG_SCC1_ENET is not set
+# CONFIG_SCC2_ENET is not set
+CONFIG_SCC3_ENET=y
+# CONFIG_FEC_ENET is not set
+CONFIG_ENET_BIG_BUFFERS=y
+CONFIG_8xxSMC2=y
+CONFIG_8xx_ALTSMC2=y
+CONFIG_8xx_CONS_SMC2=y
+# CONFIG_8xxSCC is not set
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+CONFIG_8xx_CPU6=y
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_KGDB is not set
+# CONFIG_XMON is not set
diff -urN linux-2.4.3-pre1/arch/ppc/configs/SPD823TS_defconfig linux/arch/ppc/configs/SPD823TS_defconfig
--- linux-2.4.3-pre1/arch/ppc/configs/SPD823TS_defconfig	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/configs/SPD823TS_defconfig	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,416 @@
+#
+# Automatically generated make config: don't edit
+#
+# CONFIG_UID16 is not set
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+# CONFIG_6xx is not set
+# CONFIG_4xx is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8260 is not set
+CONFIG_8xx=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_RPXLITE is not set
+# CONFIG_RPXCLASSIC is not set
+# CONFIG_BSEIP is not set
+# CONFIG_TQM823L is not set
+# CONFIG_TQM850L is not set
+# CONFIG_TQM855L is not set
+# CONFIG_TQM860L is not set
+# CONFIG_FPS850L is not set
+# CONFIG_TQM860 is not set
+CONFIG_SPD823TS=y
+# CONFIG_MBX is not set
+# CONFIG_WINCEPT is not set
+# CONFIG_ALL_PPC is not set
+# CONFIG_SMP is not set
+CONFIG_MACH_SPECIFIC=y
+CONFIG_MATH_EMULATION=y
+CONFIG_SASH=y
+CONFIG_SASH_PATH="/bin/sash"
+
+#
+# General setup
+#
+# CONFIG_ISA is not set
+# CONFIG_SBUS is not set
+# CONFIG_PCI is not set
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_KCORE_ELF=y
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_HOTPLUG is not set
+# CONFIG_PCMCIA is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_LVM is not set
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID5 is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_INITRD is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK=y
+# CONFIG_RTNETLINK is not set
+# CONFIG_NETLINK_DEV is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_INET_ECN is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+# CONFIG_ATM is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MACE is not set
+# CONFIG_BMAC is not set
+# CONFIG_GMAC is not set
+# CONFIG_NCR885E is not set
+# CONFIG_OAKNET is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+# CONFIG_IRDA is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Console drivers
+#
+
+#
+# Frame-buffer support
+#
+# CONFIG_FB is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=32
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_INTEL_RNG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_FLASH is not set
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_DRM is not set
+# CONFIG_AGP is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_RAMFS is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_EXT2_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_SYSV_FS_WRITE is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V3 is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_MOUNT_SUBDIR is not set
+# CONFIG_NCPFS_NDS_DOMAINS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_MSDOS_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_NLS is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# MPC8xx CPM Options
+#
+CONFIG_SCC_ENET=y
+# CONFIG_SCC1_ENET is not set
+CONFIG_SCC2_ENET=y
+# CONFIG_FEC_ENET is not set
+CONFIG_ENET_BIG_BUFFERS=y
+CONFIG_8xxSMC2=y
+CONFIG_8xx_ALTSMC2=y
+# CONFIG_8xx_CONS_SMC2 is not set
+# CONFIG_8xxSCC is not set
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+# CONFIG_8xx_CPU6 is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_KGDB is not set
+# CONFIG_XMON is not set
diff -urN linux-2.4.3-pre1/arch/ppc/configs/TQM823L_defconfig linux/arch/ppc/configs/TQM823L_defconfig
--- linux-2.4.3-pre1/arch/ppc/configs/TQM823L_defconfig	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/configs/TQM823L_defconfig	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,419 @@
+#
+# Automatically generated make config: don't edit
+#
+# CONFIG_UID16 is not set
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+# CONFIG_6xx is not set
+# CONFIG_4xx is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8260 is not set
+CONFIG_8xx=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_RPXLITE is not set
+# CONFIG_RPXCLASSIC is not set
+# CONFIG_BSEIP is not set
+CONFIG_TQM823L=y
+# CONFIG_TQM850L is not set
+# CONFIG_TQM855L is not set
+# CONFIG_TQM860L is not set
+# CONFIG_FPS850L is not set
+# CONFIG_TQM860 is not set
+# CONFIG_SPD823TS is not set
+# CONFIG_MBX is not set
+# CONFIG_WINCEPT is not set
+CONFIG_TQM8xxL=y
+# CONFIG_ALL_PPC is not set
+# CONFIG_SMP is not set
+CONFIG_MACH_SPECIFIC=y
+CONFIG_MATH_EMULATION=y
+CONFIG_SASH=y
+CONFIG_SASH_PATH="/bin/sash"
+
+#
+# General setup
+#
+# CONFIG_ISA is not set
+# CONFIG_SBUS is not set
+# CONFIG_PCI is not set
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_KCORE_ELF=y
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_HOTPLUG is not set
+# CONFIG_PCMCIA is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_LVM is not set
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID5 is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_INITRD is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK=y
+# CONFIG_RTNETLINK is not set
+# CONFIG_NETLINK_DEV is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_INET_ECN is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+# CONFIG_ATM is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MACE is not set
+# CONFIG_BMAC is not set
+# CONFIG_GMAC is not set
+# CONFIG_NCR885E is not set
+# CONFIG_OAKNET is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+# CONFIG_IRDA is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Console drivers
+#
+
+#
+# Frame-buffer support
+#
+# CONFIG_FB is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=32
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_INTEL_RNG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+CONFIG_FLASH=y
+CONFIG_AMD_FLASH=y
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_DRM is not set
+# CONFIG_AGP is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_RAMFS is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_EXT2_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_SYSV_FS_WRITE is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V3 is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_MOUNT_SUBDIR is not set
+# CONFIG_NCPFS_NDS_DOMAINS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_MSDOS_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_NLS is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# MPC8xx CPM Options
+#
+CONFIG_SCC_ENET=y
+# CONFIG_SCC1_ENET is not set
+CONFIG_SCC2_ENET=y
+# CONFIG_SCC3_ENET is not set
+# CONFIG_FEC_ENET is not set
+CONFIG_ENET_BIG_BUFFERS=y
+CONFIG_8xxSMC2=y
+CONFIG_8xx_ALTSMC2=y
+# CONFIG_8xx_CONS_SMC2 is not set
+# CONFIG_8xxSCC is not set
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+# CONFIG_8xx_CPU6 is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_KGDB is not set
+# CONFIG_XMON is not set
diff -urN linux-2.4.3-pre1/arch/ppc/configs/TQM850L_defconfig linux/arch/ppc/configs/TQM850L_defconfig
--- linux-2.4.3-pre1/arch/ppc/configs/TQM850L_defconfig	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/configs/TQM850L_defconfig	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,419 @@
+#
+# Automatically generated make config: don't edit
+#
+# CONFIG_UID16 is not set
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+# CONFIG_6xx is not set
+# CONFIG_4xx is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8260 is not set
+CONFIG_8xx=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_RPXLITE is not set
+# CONFIG_RPXCLASSIC is not set
+# CONFIG_BSEIP is not set
+# CONFIG_TQM823L is not set
+CONFIG_TQM850L=y
+# CONFIG_TQM855L is not set
+# CONFIG_TQM860L is not set
+# CONFIG_FPS850L is not set
+# CONFIG_TQM860 is not set
+# CONFIG_SPD823TS is not set
+# CONFIG_MBX is not set
+# CONFIG_WINCEPT is not set
+CONFIG_TQM8xxL=y
+# CONFIG_ALL_PPC is not set
+# CONFIG_SMP is not set
+CONFIG_MACH_SPECIFIC=y
+CONFIG_MATH_EMULATION=y
+CONFIG_SASH=y
+CONFIG_SASH_PATH="/bin/sash"
+
+#
+# General setup
+#
+# CONFIG_ISA is not set
+# CONFIG_SBUS is not set
+# CONFIG_PCI is not set
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_KCORE_ELF=y
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_HOTPLUG is not set
+# CONFIG_PCMCIA is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_LVM is not set
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID5 is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_INITRD is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK=y
+# CONFIG_RTNETLINK is not set
+# CONFIG_NETLINK_DEV is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_INET_ECN is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+# CONFIG_ATM is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MACE is not set
+# CONFIG_BMAC is not set
+# CONFIG_GMAC is not set
+# CONFIG_NCR885E is not set
+# CONFIG_OAKNET is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+# CONFIG_IRDA is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Console drivers
+#
+
+#
+# Frame-buffer support
+#
+# CONFIG_FB is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=32
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_INTEL_RNG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+CONFIG_FLASH=y
+CONFIG_AMD_FLASH=y
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_DRM is not set
+# CONFIG_AGP is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_RAMFS is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_EXT2_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_SYSV_FS_WRITE is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V3 is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_MOUNT_SUBDIR is not set
+# CONFIG_NCPFS_NDS_DOMAINS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_MSDOS_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_NLS is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# MPC8xx CPM Options
+#
+CONFIG_SCC_ENET=y
+# CONFIG_SCC1_ENET is not set
+CONFIG_SCC2_ENET=y
+# CONFIG_SCC3_ENET is not set
+# CONFIG_FEC_ENET is not set
+CONFIG_ENET_BIG_BUFFERS=y
+CONFIG_8xxSMC2=y
+CONFIG_8xx_ALTSMC2=y
+# CONFIG_8xx_CONS_SMC2 is not set
+# CONFIG_8xxSCC is not set
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+CONFIG_8xx_CPU6=y
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_KGDB is not set
+# CONFIG_XMON is not set
diff -urN linux-2.4.3-pre1/arch/ppc/configs/TQM860L_defconfig linux/arch/ppc/configs/TQM860L_defconfig
--- linux-2.4.3-pre1/arch/ppc/configs/TQM860L_defconfig	Wed Dec 31 16:00:00 1969
+++ linux/arch/ppc/configs/TQM860L_defconfig	Mon Dec  3 22:29:49 2001
@@ -0,0 +1,419 @@
+#
+# Automatically generated make config: don't edit
+#
+# CONFIG_UID16 is not set
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# Platform support
+#
+CONFIG_PPC=y
+# CONFIG_6xx is not set
+# CONFIG_4xx is not set
+# CONFIG_POWER3 is not set
+# CONFIG_POWER4 is not set
+# CONFIG_8260 is not set
+CONFIG_8xx=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_RPXLITE is not set
+# CONFIG_RPXCLASSIC is not set
+# CONFIG_BSEIP is not set
+# CONFIG_TQM823L is not set
+# CONFIG_TQM850L is not set
+# CONFIG_TQM855L is not set
+CONFIG_TQM860L=y
+# CONFIG_FPS850L is not set
+# CONFIG_TQM860 is not set
+# CONFIG_SPD823TS is not set
+# CONFIG_MBX is not set
+# CONFIG_WINCEPT is not set
+CONFIG_TQM8xxL=y
+# CONFIG_ALL_PPC is not set
+# CONFIG_SMP is not set
+CONFIG_MACH_SPECIFIC=y
+CONFIG_MATH_EMULATION=y
+CONFIG_SASH=y
+CONFIG_SASH_PATH="/bin/sash"
+
+#
+# General setup
+#
+# CONFIG_ISA is not set
+# CONFIG_SBUS is not set
+# CONFIG_PCI is not set
+CONFIG_NET=y
+CONFIG_SYSCTL=y
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_KCORE_ELF=y
+CONFIG_BINFMT_ELF=y
+CONFIG_KERNEL_ELF=y
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_HOTPLUG is not set
+# CONFIG_PCMCIA is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_LVM is not set
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID5 is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_INITRD is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+CONFIG_NETLINK=y
+# CONFIG_RTNETLINK is not set
+# CONFIG_NETLINK_DEV is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_FILTER is not set
+CONFIG_UNIX=y
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_IP_ROUTER is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_ALIAS is not set
+# CONFIG_INET_ECN is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+# CONFIG_ATM is not set
+
+#
+#  
+#
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+# CONFIG_IDE is not set
+# CONFIG_BLK_DEV_IDE_MODES is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_ETHERTAP is not set
+# CONFIG_NET_SB1000 is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MACE is not set
+# CONFIG_BMAC is not set
+# CONFIG_GMAC is not set
+# CONFIG_NCR885E is not set
+# CONFIG_OAKNET is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_YELLOWFIN is not set
+# CONFIG_ACENIC is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+# CONFIG_IRDA is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Old CD-ROM drivers (not SCSI, not IDE)
+#
+# CONFIG_CD_NO_IDESCSI is not set
+
+#
+# Console drivers
+#
+
+#
+# Frame-buffer support
+#
+# CONFIG_FB is not set
+
+#
+# Character devices
+#
+# CONFIG_VT is not set
+# CONFIG_SERIAL is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=32
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_JOYSTICK is not set
+# CONFIG_QIC02_TAPE is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_INTEL_RNG is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+CONFIG_FLASH=y
+CONFIG_AMD_FLASH=y
+
+#
+# Video For Linux
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_DRM is not set
+# CONFIG_AGP is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_FAT_FS is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_RAMFS is not set
+# CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_EXT2_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_SYSV_FS_WRITE is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+CONFIG_NFS_FS=y
+# CONFIG_NFS_V3 is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V3 is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_MOUNT_SUBDIR is not set
+# CONFIG_NCPFS_NDS_DOMAINS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+# CONFIG_MSDOS_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_NLS is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+
+#
+# MPC8xx CPM Options
+#
+CONFIG_SCC_ENET=y
+CONFIG_SCC1_ENET=y
+# CONFIG_SCC2_ENET is not set
+# CONFIG_SCC3_ENET is not set
+# CONFIG_FEC_ENET is not set
+CONFIG_ENET_BIG_BUFFERS=y
+CONFIG_8xxSMC2=y
+# CONFIG_8xx_ALTSMC2 is not set
+# CONFIG_8xx_CONS_SMC2 is not set
+# CONFIG_8xxSCC is not set
+
+#
+# Generic MPC8xx Options
+#
+CONFIG_8xx_COPYBACK=y
+# CONFIG_8xx_CPU6 is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_KGDB is not set
+# CONFIG_XMON is not set
diff -urN linux-2.4.3-pre1/arch/ppc/configs/common_defconfig linux/arch/ppc/configs/common_defconfig
--- linux-2.4.3-pre1/arch/ppc/configs/common_defconfig	Mon Jan 22 15:41:14 2001
+++ linux/arch/ppc/configs/common_defconfig	Mon Dec  3 22:29:49 2001
@@ -295,11 +295,11 @@
 # CONFIG_SCSI_AHA152X is not set
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
-CONFIG_SCSI_AIC7XXX=y
-# CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT is not set
-CONFIG_AIC7XXX_CMDS_PER_DEVICE=8
-CONFIG_AIC7XXX_PROC_STATS=y
-CONFIG_AIC7XXX_RESET_DELAY=15
+CONFIG_SCSI_AIC7XXX=m
+CONFIG_SCSI_AIC7XXX_OLD=m
+# CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT is not set
+CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE=8
+CONFIG_AIC7XXX_OLD_PROC_STATS=y
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
diff -urN linux-2.4.3-pre1/arch/ppc/defconfig linux/arch/ppc/defconfig
--- linux-2.4.3-pre1/arch/ppc/defconfig	Mon Jan 22 15:41:15 2001
+++ linux/arch/ppc/defconfig	Mon Dec  3 22:29:49 2001
@@ -296,10 +296,9 @@
 # CONFIG_SCSI_AHA1542 is not set
 # CONFIG_SCSI_AHA1740 is not set
 CONFIG_SCSI_AIC7XXX=y
-# CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT is not set
-CONFIG_AIC7XXX_CMDS_PER_DEVICE=8
-CONFIG_AIC7XXX_PROC_STATS=y
-CONFIG_AIC7XXX_RESET_DELAY=15
+CONFIG_AIC7XXX_CMDS_PER_DEVICE=253
+CONFIG_AIC7XXX_RESET_DELAY=15000
+# CONFIG_SCSI_AIC7XXX_OLD is not set
 # CONFIG_SCSI_ADVANSYS is not set
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/Makefile linux/arch/ppc/kernel/Makefile
--- linux-2.4.3-pre1/arch/ppc/kernel/Makefile	Mon Jan 22 15:41:15 2001
+++ linux/arch/ppc/kernel/Makefile	Mon Dec  3 22:29:49 2001
@@ -70,6 +70,7 @@
 
 include $(TOPDIR)/Rules.make
 
+entry.o: entry.S ppc_defs.h
 head.o: head.S ppc_defs.h
 head_4xx.o: head_4xx.S ppc_defs.h
 head_8xx.o: head_8xx.S ppc_defs.h
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/chrp_pci.c linux/arch/ppc/kernel/chrp_pci.c
--- linux-2.4.3-pre1/arch/ppc/kernel/chrp_pci.c	Mon Jan 22 15:41:15 2001
+++ linux/arch/ppc/kernel/chrp_pci.c	Mon Dec  3 22:29:49 2001
@@ -298,8 +298,9 @@
     	bus->resource[1] = &gg2_resources.pci_mem;
 }
 
-static void process_bridge_ranges(struct pci_controller *hose,
-				  struct device_node *dev, int index)
+/* this is used by the pmac_pci code too... - paulus */
+void process_bridge_ranges(struct pci_controller *hose,
+			   struct device_node *dev, int primary)
 {
 	unsigned int *ranges;
 	int rlen = 0;
@@ -316,31 +317,34 @@
 				break;
 			hose->io_base_phys = ranges[3];
 			hose->io_base_virt = ioremap(ranges[3], ranges[5]);
-			if (index == 0) {
+			if (primary)
 				isa_io_base = (unsigned long) hose->io_base_virt;
-				printk("isa_io_base=%lx\n", isa_io_base);
-			}
 			res = &hose->io_resource;
 			res->flags = IORESOURCE_IO;
+			res->start = ranges[2];
 			break;
 		case 2:		/* memory space */
-			if (index == 0 && ranges[1] == 0 && ranges[2] == 0){
-				isa_mem_base = ranges[3];
-				printk("isa_mem_base=%lx\n", isa_mem_base);
+			memno = 0;
+			if (ranges[1] == 0 && ranges[2] == 0
+			    && ranges[5] <= (16 << 20)) {
+				/* 1st 16MB, i.e. ISA memory area */
+				if (primary)
+					isa_mem_base = ranges[3];
+				memno = 1;
 			}
-			if (memno == 0) {
+			while (memno < 3 && hose->mem_resources[memno].flags)
+				++memno;
+			if (memno == 0)
 				hose->pci_mem_offset = ranges[3] - ranges[2];
-				printk("pci_mem_offset=%lx for this bridge\n",
-				       hose->pci_mem_offset);
+			if (memno < 3) {
+				res = &hose->mem_resources[memno];
+				res->flags = IORESOURCE_MEM;
+				res->start = ranges[3];
 			}
-			res = &hose->mem_resources[memno];
-			res->flags = IORESOURCE_MEM;
-			++memno;
 			break;
 		}
 		if (res != NULL) {
 			res->name = dev->full_name;
-			res->start = ranges[3];
 			res->end = res->start + ranges[5] - 1;
 			res->parent = NULL;
 			res->sibling = NULL;
@@ -401,7 +405,7 @@
        		hose->cfg_addr = (volatile unsigned int *) cfg;
        		hose->cfg_data = cfg + 0x10;
 
-		process_bridge_ranges(hose, dev, index);
+		process_bridge_ranges(hose, dev, index == 0);
 
 #ifdef CONFIG_POWER3
                 openpic_setup_ISU(index, opprop[index+1]);
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/chrp_setup.c linux/arch/ppc/kernel/chrp_setup.c
--- linux-2.4.3-pre1/arch/ppc/kernel/chrp_setup.c	Tue Feb 13 13:15:04 2001
+++ linux/arch/ppc/kernel/chrp_setup.c	Mon Dec  3 22:29:50 2001
@@ -237,7 +237,6 @@
 void __init
 chrp_setup_arch(void)
 {
-	extern char cmd_line[];
 	struct device_node *device;
 
 	/* init to some ~sane value until calibrate_delay() runs */
@@ -252,7 +251,6 @@
 	else
 #endif
 		ROOT_DEV = to_kdev_t(0x0802); /* sda2 (sda1 is for the kernel) */
-	printk("Boot arguments: %s\n", cmd_line);
 
 	/* Lookup PCI host bridges */
 	chrp_find_bridges();
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/head.S linux/arch/ppc/kernel/head.S
--- linux-2.4.3-pre1/arch/ppc/kernel/head.S	Mon Jan 22 15:41:15 2001
+++ linux/arch/ppc/kernel/head.S	Mon Dec  3 22:29:50 2001
@@ -1135,7 +1135,7 @@
 #ifdef CONFIG_APUS
 /*
  * On APUS the physical base address of the kernel is not known at compile
- * time, which means the __pa/__va constants used are incorect. In the
+ * time, which means the __pa/__va constants used are incorrect. In the
  * __init section is recorded the virtual addresses of instructions using
  * these constants, so all that has to be done is fix these before
  * continuing the kernel boot.
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/head_8xx.S linux/arch/ppc/kernel/head_8xx.S
--- linux-2.4.3-pre1/arch/ppc/kernel/head_8xx.S	Sun Sep 17 09:48:06 2000
+++ linux/arch/ppc/kernel/head_8xx.S	Mon Dec  3 22:29:50 2001
@@ -968,26 +968,6 @@
 	SYNC
 	blr
 
-/* Jump into the system reset for the rom.
- * We first disable the MMU, and then jump to the ROM reset address.
- *
- * r3 is the board info structure, r4 is the location for starting.
- * I use this for building a small kernel that can load other kernels,
- * rather than trying to write or rely on a rom monitor that can tftp load.
- */
-       .globl  m8xx_gorom
-m8xx_gorom:
-       li      r5,MSR_KERNEL & ~(MSR_IR|MSR_DR)
-       lis     r6,2f@h
-       addis   r6,r6,-KERNELBASE@h
-       ori     r6,r6,2f@l
-       mtspr   SRR0,r6
-       mtspr   SRR1,r5
-       rfi
-2:
-       mtlr    r4
-       blr
-
 #ifdef CONFIG_8xx_CPU6
 /* It's here because it is unique to the 8xx.
  * It is important we get called with interrupts disabled.  I used to
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/irq.c linux/arch/ppc/kernel/irq.c
--- linux-2.4.3-pre1/arch/ppc/kernel/irq.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/ppc/kernel/irq.c	Mon Dec  3 22:29:50 2001
@@ -7,8 +7,8 @@
  *    Copyright (C) 1992 Linus Torvalds
  *  Adapted from arch/i386 by Gary Thomas
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
- *  Updated and modified by Cort Dougan (cort@cs.nmt.edu)
- *    Copyright (C) 1996 Cort Dougan
+ *  Updated and modified by Cort Dougan <cort@fsmlabs.com>
+ *    Copyright (C) 1996-2001 Cort Dougan
  *  Adapted for Power Macintosh by Paul Mackerras
  *    Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
  *  Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk).
@@ -258,7 +258,10 @@
 	
 	retval = setup_irq(irq, action);
 	if (retval)
+	{
 		kfree(action);
+		return retval;
+	}
 		
 	return 0;
 }
@@ -464,13 +467,11 @@
 			ppc_spurious_interrupts++;
 			printk(KERN_DEBUG "Unhandled interrupt %x, disabled\n", irq);
 			/* We can't call disable_irq here, it would deadlock */
-			if (!desc->depth)
-				desc->depth = 1;
+			++desc->depth;
 			desc->status |= IRQ_DISABLED;
-			/* This is not a real spurrious interrupt, we
-			 * have to eoi it, so we jump to out
-			 */
 			mask_irq(irq);
+			/* This is a real interrupt, we have to eoi it,
+			   so we jump to out */
 			goto out;
 		}
 		status &= ~IRQ_PENDING; /* we commit to handling */
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/m8260_setup.c linux/arch/ppc/kernel/m8260_setup.c
--- linux-2.4.3-pre1/arch/ppc/kernel/m8260_setup.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/ppc/kernel/m8260_setup.c	Mon Dec  3 22:29:50 2001
@@ -85,10 +85,6 @@
 void __init
 m8260_setup_arch(void)
 {
-	extern char cmd_line[];
-	
-	printk("Boot arguments: %s\n", cmd_line);
-
 	/* Reset the Communication Processor Module.
 	*/
 	m8260_cpm_reset();
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/m8xx_setup.c linux/arch/ppc/kernel/m8xx_setup.c
--- linux-2.4.3-pre1/arch/ppc/kernel/m8xx_setup.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/ppc/kernel/m8xx_setup.c	Mon Dec  3 22:29:50 2001
@@ -32,6 +32,7 @@
 #include <linux/init.h>
 #include <linux/blk.h>
 #include <linux/ioport.h>
+#include <asm/mpc8xx.h>  /* Before ide.h to avoid warning: `MAX_HWIFS' redefined */
 #include <linux/ide.h>
 #include <linux/bootmem.h>
 
@@ -41,7 +42,6 @@
 #include <asm/io.h>
 #include <asm/pgtable.h>
 #include <asm/ide.h>
-#include <asm/mpc8xx.h>
 #include <asm/8xx_immap.h>
 #include <asm/machdep.h>
 
@@ -52,19 +52,65 @@
 unsigned long m8xx_get_rtc_time(void);
 void m8xx_calibrate_decr(void);
 
-#if 0
-extern int mackbd_setkeycode(unsigned int scancode, unsigned int keycode);
-extern int mackbd_getkeycode(unsigned int scancode);
-extern int mackbd_pretranslate(unsigned char scancode, char raw_mode);
-extern int mackbd_translate(unsigned char scancode, unsigned char *keycode,
-			   char raw_mode);
-extern char mackbd_unexpected_up(unsigned char keycode);
-extern void mackbd_leds(unsigned char leds);
-extern void mackbd_init_hw(void);
-#endif
-
 unsigned char __res[sizeof(bd_t)];
 unsigned long empty_zero_page[1024];
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+
+#ifdef	CONFIG_BLK_DEV_MPC8xx_IDE
+#include "../../../drivers/ide/ide_modes.h"
+
+static void m8xx_ide_tuneproc(ide_drive_t *drive, byte pio);
+
+typedef	struct ide_ioport_desc {
+	unsigned long	base_off;		/* Offset to PCMCIA memory */
+	ide_ioreg_t	reg_off[IDE_NR_PORTS];	/* controller reg. offsets */
+	int		irq;			/* IRQ	*/
+} ide_ioport_desc_t;
+
+ide_ioport_desc_t ioport_dsc[MAX_HWIFS] = {
+#ifdef IDE0_BASE_OFFSET
+	{ IDE0_BASE_OFFSET,
+	    {
+		IDE0_DATA_REG_OFFSET,
+		IDE0_ERROR_REG_OFFSET,
+		IDE0_NSECTOR_REG_OFFSET,
+		IDE0_SECTOR_REG_OFFSET,
+		IDE0_LCYL_REG_OFFSET,
+		IDE0_HCYL_REG_OFFSET,
+		IDE0_SELECT_REG_OFFSET,
+		IDE0_STATUS_REG_OFFSET,
+		IDE0_CONTROL_REG_OFFSET,
+		IDE0_IRQ_REG_OFFSET,
+	    },
+	    IDE0_INTERRUPT,
+	},
+# ifdef IDE1_BASE_OFFSET
+	{ IDE1_BASE_OFFSET,
+	    {
+		IDE1_DATA_REG_OFFSET,
+		IDE1_ERROR_REG_OFFSET,
+		IDE1_NSECTOR_REG_OFFSET,
+		IDE1_SECTOR_REG_OFFSET,
+		IDE1_LCYL_REG_OFFSET,
+		IDE1_HCYL_REG_OFFSET,
+		IDE1_SELECT_REG_OFFSET,
+		IDE1_STATUS_REG_OFFSET,
+		IDE1_CONTROL_REG_OFFSET,
+		IDE1_IRQ_REG_OFFSET,
+	    },
+	    IDE1_INTERRUPT,
+	},
+# endif /* IDE1_BASE_OFFSET */
+#endif	/* IDE0_BASE_OFFSET */
+};
+
+ide_pio_timings_t ide_pio_clocks[6];
+
+/* Make clock cycles and always round up */
+#define PCMCIA_MK_CLKS( t, T ) (( (t) * (T) + 999U ) / 1000U )
+
+#endif	/* CONFIG_BLK_DEV_MPC8xx_IDE */
+#endif	/* CONFIG_BLK_DEV_IDE || CONFIG_BLK_DEV_IDE_MODULE */
 
 #ifdef CONFIG_BLK_DEV_RAM
 extern int rd_doload;		/* 1 = load ramdisk, 0 = don't load */
@@ -77,6 +123,8 @@
 extern unsigned long find_available_memory(void);
 extern void m8xx_cpm_reset(uint);
 
+static void ide_interrupt_handler(void* dev_id);
+
 void __init adbdev_init(void)
 {
 }
@@ -85,12 +133,9 @@
 m8xx_setup_arch(void)
 {
 	int	cpm_page;
-	extern char cmd_line[];
 	
 	cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE);
 	
-	printk("Boot arguments: %s\n", cmd_line);
-
 	/* Reset the Communication Processor Module.
 	*/
 	m8xx_cpm_reset(cpm_page);
@@ -137,7 +182,7 @@
 */
 void timebase_interrupt(int irq, void * dev, struct pt_regs * regs)
 {
-	printk("timebase_interrupt()\n");
+	printk ("timebase_interrupt()\n");
 }
 
 /* The decrementer counts at the system (internal) clock frequency divided by
@@ -164,7 +209,7 @@
 	fp = (binfo->bi_intfreq * 1000000) / 16;
 	freq = fp*60;	/* try to make freq/1e6 an integer */
         divisor = 60;
-        printk("time_init: decrementer frequency = %d/%d\n", freq, divisor);
+        printk("Decrementer Frequency = %d/%d\n", freq, divisor);
         tb_ticks_per_jiffy = freq / HZ / divisor;
 	tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000);
 
@@ -232,9 +277,21 @@
 void
 m8xx_restart(char *cmd)
 {
-	extern void m8xx_gorom(void);
+	__volatile__ unsigned char dummy;
+	uint	msr;
 
-	m8xx_gorom();
+	cli();
+	((immap_t *)IMAP_ADDR)->im_clkrst.car_plprcr |= 0x00000080;
+
+	/* Clear the ME bit in MSR to cause checkstop on machine check
+	*/
+	__asm__("mfmsr %0" : "=r" (msr) );
+	msr &= ~0x1000;
+	__asm__("mtmsr %0" : : "r" (msr) );
+
+	dummy = ((immap_t *)IMAP_ADDR)->im_clkrst.res[0];
+	printk("Restart failed\n"); 
+	while(1);
 }
 
 void
@@ -257,8 +314,8 @@
 
 	bp = (bd_t *)__res;
 			
-	len += sprintf(len+buffer,"clock\t\t: %dMHz\n"
-		       "bus clock\t: %dMHz\n",
+	len += sprintf(len+buffer,"clock\t\t: %ldMHz\n"
+		       "bus clock\t: %ldMHz\n",
 		       bp->bi_intfreq /*/ 1000000*/,
 		       bp->bi_busfreq /*/ 1000000*/);
 
@@ -298,17 +355,13 @@
 
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
 
-/* Define this to make a PCMCIA ATA Flash card work.
-*/
-#define ATA_FLASH 1
-
 /*
  * IDE stuff.
  */
 void
 m8xx_ide_insw(ide_ioreg_t port, void *buf, int ns)
 {
-#ifdef ATA_FLASH
+#ifdef CONFIG_BLK_DEV_MPC8xx_IDE
 	ide_insw(port, buf, ns);
 #else
 	ide_insw(port+_IO_BASE, buf, ns);
@@ -318,7 +371,7 @@
 void
 m8xx_ide_outsw(ide_ioreg_t port, void *buf, int ns)
 {
-#ifdef ATA_FLASH
+#ifdef CONFIG_BLK_DEV_MPC8xx_IDE
 	ide_outsw(port, buf, ns);
 #else
 	ide_outsw(port+_IO_BASE, buf, ns);
@@ -328,8 +381,11 @@
 int
 m8xx_ide_default_irq(ide_ioreg_t base)
 {
-#ifdef ATA_FLASH
-	return PCMCIA_INTERRUPT;
+#ifdef CONFIG_BLK_DEV_MPC8xx_IDE
+	if (base >= MAX_HWIFS)
+		return 0;
+	
+	return (ioport_dsc[base].irq);
 #else
         return 14;
 #endif
@@ -348,60 +404,105 @@
 		       const char *device,
 		       void *dev_id)
 {
-#ifdef ATA_FLASH
+#ifdef CONFIG_BLK_DEV_MPC8xx_IDE
 	return request_8xxirq(irq, handler, flags, device, dev_id);
 #else
 	return request_irq(irq, handler, flags, device, dev_id);
 #endif
 }
 
-/* We can use an external IDE controller or wire the IDE interface to
- * the internal PCMCIA controller.
+/* We can use an external IDE controller
+ * or wire the IDE interface to the internal PCMCIA controller.
+ *
+ * See include/linux/ide.h for definition of hw_regs_t (p, base)
  */
-void __init m8xx_ide_init_hwif_ports(ide_ioreg_t *p, ide_ioreg_t base, int *irq)
+void m8xx_ide_init_hwif_ports(hw_regs_t *hw,
+	ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq)
 {
-	ide_ioreg_t port = base;
+	ide_ioreg_t *p = hw->io_ports;
 	int i;
-#ifdef ATA_FLASH
+#ifdef CONFIG_BLK_DEV_MPC8xx_IDE
 	volatile pcmconf8xx_t	*pcmp;
+
+	static unsigned long pcmcia_base = 0;
+#else
+	ide_ioreg_t port = data_port;	/* ??? XXX ??? XXX */
 #endif
+	unsigned long base;
 
-#ifdef ATA_FLASH
+#ifdef CONFIG_BLK_DEV_MPC8xx_IDE
 	*p = 0;
-	*irq = 0;
+	if (irq)
+		*irq = 0;
+
+	pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia));
 
-	if (base != 0)		/* Only map the first ATA flash drive */
+	if (!pcmcia_base) {
+		/* relies PCMCIA registers being set up by firmware */
+		pcmcia_base = (unsigned long) ioremap(PCMCIA_MEM_ADDR,
+						      PCMCIA_MEM_SIZE);
+
+		/* Compute clock cycles for PIO timings */
+		for (i=0; i<6; ++i) {
+			bd_t	*binfo = (bd_t *)__res;
+
+			ide_pio_clocks[i].hold_time   =
+				PCMCIA_MK_CLKS (ide_pio_timings[i].hold_time,
+						binfo->bi_busfreq);
+			ide_pio_clocks[i].setup_time  =
+				PCMCIA_MK_CLKS (ide_pio_timings[i].setup_time,
+						binfo->bi_busfreq);
+			ide_pio_clocks[i].active_time =
+				PCMCIA_MK_CLKS (ide_pio_timings[i].active_time,
+						binfo->bi_busfreq);
+			ide_pio_clocks[i].cycle_time  =
+				PCMCIA_MK_CLKS (ide_pio_timings[i].cycle_time,
+						binfo->bi_busfreq);
+#if 0
+			printk ("PIO mode %d timings: %d/%d/%d => %d/%d/%d\n",
+				i,
+				ide_pio_clocks[i].setup_time,
+				ide_pio_clocks[i].active_time,
+				ide_pio_clocks[i].hold_time,
+				ide_pio_clocks[i].cycle_time,
+				ide_pio_timings[i].setup_time,
+				ide_pio_timings[i].active_time,
+				ide_pio_timings[i].hold_time,
+				ide_pio_timings[i].cycle_time);
+#endif
+		}
+	}
+
+	if (data_port >= MAX_HWIFS)
 		return;
 
-	pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia));
+	base = pcmcia_base + ioport_dsc[data_port].base_off;
+
+# if (!defined(CONFIG_SPD823TS) && !defined(CONFIG_IVMS8))
+	/* SPD823TS and IVMS8 have a direct connection */
 	if (pcmp->pcmc_pipr & 0x18000000)
 		return;		/* No card in slot */
+# endif	/* CONFIG_SPD823TS, CONFIG_IVMS8 */
 
-	base = (unsigned long) ioremap(PCMCIA_MEM_ADDR, 0x200);
+	for (i = 0; i < IDE_NR_PORTS; ++i) {
+	 	*p++ = base + ioport_dsc[data_port].reg_off[i];
+	}
 
-	/* For the M-Systems ATA card, the first 8 registers map 1:1.
-	 * The following register, control/Altstatus, is located at 0x0e.
-	 * Following that, the irq offset, is not used, so we place it in
-	 * an unused location, 0x0a.
-	 */
-	*p++ = base + 8;
-	for (i = 1; i < 8; ++i)
-		*p++ = base + i;
-	*p++ = base + 0x0e;		/* control/altstatus */
-	*p = base + 0x0a;		/* IRQ, not used */
-	if (irq)
-		*irq = PCMCIA_INTERRUPT;
+	if (irq) {
+	    *irq = ioport_dsc[data_port].irq;
+	}
 
-	/* Configure the interface for this interrupt.
-	*/
-	pcmp->pcmc_pgcra = (mk_int_int_mask(PCMCIA_INTERRUPT) << 24) |
-		(mk_int_int_mask(PCMCIA_INTERRUPT) << 16);
+	/* register routine to tune PIO mode */
+	ide_hwifs[data_port].tuneproc = m8xx_ide_tuneproc;
 
-	/* Enable status change interrupt from slot A.
-	*/
-	pcmp->pcmc_per = 0xff100000;
-	pcmp->pcmc_pscr = ~0;
-#else
+	/* Enable Harddisk Interrupt,
+	 * and make it edge sensitive
+	 */
+	hw->ack_intr = (ide_ack_intr_t *)ide_interrupt_handler;
+	((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel |=
+					(0x80000000 >> ioport_dsc[data_port].irq);
+
+#else	/* ! CONFIG_BLK_DEV_MPC8xx_IDE */
 	
 	/* Just a regular IDE drive on some I/O port.
 	*/
@@ -411,10 +512,84 @@
 	*p++ = base + 0x206;
 	if (irq != NULL)
 		*irq = 0;
-#endif
+#endif	/* CONFIG_BLK_DEV_MPC8xx_IDE */
 }
+#endif	/* CONFIG_BLK_DEV_IDE || CONFIG_BLK_DEV_IDE_MODULE */
+
+
+/* -------------------------------------------------------------------- */
+
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+#ifdef CONFIG_BLK_DEV_MPC8xx_IDE
+
+/* PCMCIA Timing */
+#ifndef	PCMCIA_SHT
+#define PCMCIA_SHT(t)	((t & 0x0F)<<16)	/* Strobe Hold  Time 	*/
+#define PCMCIA_SST(t)	((t & 0x0F)<<12)	/* Strobe Setup Time	*/
+#define PCMCIA_SL(t) ((t==32) ? 0 : ((t & 0x1F)<<7)) /* Strobe Length	*/
 #endif
 
+
+/* Calculate PIO timings */
+static void
+m8xx_ide_tuneproc(ide_drive_t *drive, byte pio)
+{
+	volatile pcmconf8xx_t	*pcmp;
+	ide_pio_data_t d;
+	ulong timing, mask, reg;
+
+	pio = ide_get_best_pio_mode(drive, pio, 4, &d);
+
+#if 1
+	printk("%s[%d] %s: best PIO mode: %d\n",
+		__FILE__,__LINE__,__FUNCTION__, pio);
+#endif
+	pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia));
+
+	mask = ~(PCMCIA_SHT(0xFF) | PCMCIA_SST(0xFF) | PCMCIA_SL(0xFF));
+
+	timing  = PCMCIA_SHT(ide_pio_clocks[pio].hold_time  )
+		| PCMCIA_SST(ide_pio_clocks[pio].setup_time )
+		| PCMCIA_SL (ide_pio_clocks[pio].active_time)
+		;
+
+#if 1
+	printk ("Setting timing bits 0x%08lx in PCMCIA controller\n", timing);
+#endif
+	if ((reg = pcmp->pcmc_por0 & mask) != 0)
+		pcmp->pcmc_por0 = reg | timing;
+
+	if ((reg = pcmp->pcmc_por1 & mask) != 0)
+		pcmp->pcmc_por1 = reg | timing;
+
+	if ((reg = pcmp->pcmc_por2 & mask) != 0)
+		pcmp->pcmc_por2 = reg | timing;
+
+	if ((reg = pcmp->pcmc_por3 & mask) != 0)
+		pcmp->pcmc_por3 = reg | timing;
+
+	if ((reg = pcmp->pcmc_por4 & mask) != 0)
+		pcmp->pcmc_por4 = reg | timing;
+
+	if ((reg = pcmp->pcmc_por5 & mask) != 0)
+		pcmp->pcmc_por5 = reg | timing;
+
+	if ((reg = pcmp->pcmc_por6 & mask) != 0)
+		pcmp->pcmc_por6 = reg | timing;
+
+	if ((reg = pcmp->pcmc_por7 & mask) != 0)
+		pcmp->pcmc_por7 = reg | timing;
+}
+
+void ide_interrupt_handler (void *dev)
+{
+}
+
+#endif	/* CONFIG_BLK_DEV_MPC8xx_IDE */
+#endif	/* CONFIG_BLK_DEV_IDE || CONFIG_BLK_DEV_IDE_MODULE */
+
+/* -------------------------------------------------------------------- */
+
 void __init
 m8xx_init(unsigned long r3, unsigned long r4, unsigned long r5,
 	 unsigned long r6, unsigned long r7)
@@ -490,7 +665,6 @@
         ppc_ide_md.default_io_base = m8xx_ide_default_io_base;
         ppc_ide_md.fix_driveid = ppc_generic_ide_fix_driveid;
         ppc_ide_md.ide_init_hwif = m8xx_ide_init_hwif_ports;
-        ppc_ide_md.ide_request_irq = m8xx_ide_request_irq;
 
         ppc_ide_md.io_base = _IO_BASE;
 #endif		
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/open_pic.c linux/arch/ppc/kernel/open_pic.c
--- linux-2.4.3-pre1/arch/ppc/kernel/open_pic.c	Mon Jan 22 15:41:15 2001
+++ linux/arch/ppc/kernel/open_pic.c	Mon Dec  3 22:29:50 2001
@@ -42,6 +42,45 @@
 
 OpenPIC_SourcePtr ISU[OPENPIC_MAX_ISU];
 
+/* Global Operations */
+static void openpic_disable_8259_pass_through(void);
+static u_int openpic_irq(void);
+static void openpic_eoi(void);
+static void openpic_set_priority(u_int pri);
+static void openpic_set_spurious(u_int vector);
+
+#ifdef CONFIG_SMP
+/* Interprocessor Interrupts */
+static void openpic_initipi(u_int ipi, u_int pri, u_int vector);
+static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs);
+#endif
+
+/* Timer Interrupts */
+static void openpic_inittimer(u_int timer, u_int pri, u_int vector);
+static void openpic_maptimer(u_int timer, u_int cpumask);
+
+/* Interrupt Sources */
+static void openpic_enable_irq(u_int irq);
+static void openpic_disable_irq(u_int irq);
+static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity,
+			    int is_level);
+static void openpic_mapirq(u_int irq, u_int cpumask);
+
+/*
+ * These functions are not used but the code is kept here
+ * for completeness and future reference.
+ */
+#ifdef notused
+static void openpic_reset(void);
+static void openpic_enable_8259_pass_through(void);
+static u_int openpic_get_priority(void);
+static u_int openpic_get_spurious(void);
+static void openpic_set_sense(u_int irq, int sense);
+#endif /* notused */
+
+/*
+ * Description of the openpic for the higher-level irq code
+ */
 static void openpic_end_irq(unsigned int irq_nr);
 static void openpic_ack_irq(unsigned int irq_nr);
 static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask);
@@ -370,17 +409,19 @@
 #endif
 }
 
-static inline void openpic_reset(void)
+#ifdef notused
+static void openpic_reset(void)
 {
 	openpic_setfield(&OpenPIC->Global.Global_Configuration0,
 			 OPENPIC_CONFIG_RESET);
 }
 
-static inline void openpic_enable_8259_pass_through(void)
+static void openpic_enable_8259_pass_through(void)
 {
 	openpic_clearfield(&OpenPIC->Global.Global_Configuration0,
 			   OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
 }
+#endif /* notused */
 
 static void openpic_disable_8259_pass_through(void)
 {
@@ -412,8 +453,8 @@
 	(void)openpic_read(&OpenPIC->THIS_CPU.EOI);
 }
 
-
-static inline u_int openpic_get_priority(void)
+#ifdef notused
+static u_int openpic_get_priority(void)
 {
 	DECL_THIS_CPU;
 
@@ -421,6 +462,7 @@
 	return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority,
 				 OPENPIC_CURRENT_TASK_PRIORITY_MASK);
 }
+#endif /* notused */
 
 static void openpic_set_priority(u_int pri)
 {
@@ -435,11 +477,13 @@
 /*
  *  Get/set the spurious vector
  */
-static inline u_int openpic_get_spurious(void)
+#ifdef notused
+static u_int openpic_get_spurious(void)
 {
 	return openpic_readfield(&OpenPIC->Global.Spurious_Vector,
 				 OPENPIC_VECTOR_MASK);
 }
+#endif /* notused */
 
 static void openpic_set_spurious(u_int vec)
 {
@@ -604,21 +648,6 @@
 static void openpic_enable_irq(u_int irq)
 {
 	check_arg_irq(irq);
-
-        /*
-         * Never want to disable a timer or ipi irq
-         * (only want to disable irqs within an ISU).
-         */
-        if (((irq >= OPENPIC_VEC_IPI+open_pic_irq_offset)   &&
-           (irq <  OPENPIC_VEC_IPI+open_pic_irq_offset+OPENPIC_NUM_IPI)) ||
-           ((irq >= OPENPIC_VEC_TIMER+open_pic_irq_offset) &&
-            (irq <  OPENPIC_VEC_TIMER+open_pic_irq_offset+OPENPIC_NUM_TIMERS)))
-        {
-        /* silently ignore the enable of the timer or ipi irq. */
-        return;
-        }
-
-
 	openpic_clearfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK);
 	/* make sure mask gets to controller before we return to user */
 	do {
@@ -632,19 +661,6 @@
 	u32 vp;
 	
 	check_arg_irq(irq);
-        /*
-         * Never want to disable a timer or ipi irq
-         * (only want to disable irqs within an ISU).
-         */
-        if (((irq >= OPENPIC_VEC_IPI+open_pic_irq_offset)   &&
-             (irq <  OPENPIC_VEC_IPI+open_pic_irq_offset+OPENPIC_NUM_IPI)) ||
-             ((irq >= OPENPIC_VEC_TIMER+open_pic_irq_offset) &&
-             (irq <  OPENPIC_VEC_TIMER+open_pic_irq_offset+OPENPIC_NUM_TIMERS)))
-        {
-                panic("openpic_disable_irq - disabling non-ISU irq");
-        }
-
-
 	openpic_setfield(&GET_ISU(irq - open_pic_irq_offset).Vector_Priority, OPENPIC_MASK);
 	/* make sure mask gets to controller before we return to user */
 	do {
@@ -667,11 +683,13 @@
 	openpic_clearfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK);
 
 }
+
 void openpic_disable_ipi(u_int irq)
 {
-   /* NEVER disable an IPI... that's just plain wrong! */
+	irq -= (OPENPIC_VEC_IPI+open_pic_irq_offset);
+	check_arg_ipi(irq);
+	openpic_setfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK);
 }
-
 #endif
 
 /*
@@ -702,39 +720,33 @@
 	openpic_write(&GET_ISU(irq).Destination, physmask);
 }
 
+#ifdef notused
 /*
  *  Set the sense for an interrupt source (and disable it!)
  *
  *  sense: 1 for level, 0 for edge
  */
-static inline void openpic_set_sense(u_int irq, int sense)
+static void openpic_set_sense(u_int irq, int sense)
 {
 	openpic_safe_writefield(&GET_ISU(irq).Vector_Priority,
 				OPENPIC_SENSE_LEVEL,
 				(sense ? OPENPIC_SENSE_LEVEL : 0));
 }
+#endif /* notused */
 
 /* No spinlocks, should not be necessary with the OpenPIC
  * (1 register = 1 interrupt and we have the desc lock).
  */
 static void openpic_ack_irq(unsigned int irq_nr)
 {
-#if 1 /* masking should be unnecessary, but I still get spurrious */
 	openpic_disable_irq(irq_nr);
-#endif		
-	if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0)
-		openpic_eoi();
+	openpic_eoi();
 }
 
 static void openpic_end_irq(unsigned int irq_nr)
 {
-	if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0)
-		openpic_eoi();
-	
-#if 1 /* masking should be unnecessary, but I still get spurrious */
 	if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
 		openpic_enable_irq(irq_nr);
-#endif
 }
 
 static void openpic_set_affinity(unsigned int irq_nr, unsigned long cpumask)
@@ -745,23 +757,11 @@
 #ifdef CONFIG_SMP
 static void openpic_ack_ipi(unsigned int irq_nr)
 {
+	openpic_eoi();
 }
 
 static void openpic_end_ipi(unsigned int irq_nr)
 {
-	/* IPIs are marked IRQ_PER_CPU. This has the side effect of
-	 * preventing the IRQ_PENDING/IRQ_INPROGRESS logic from
-	 * applying to them. We EOI them late to avoid re-entering.
-	 * however, I'm wondering if we could simply let them have the
-	 * SA_INTERRUPT flag and let them execute with all interrupts OFF.
-	 * This would have the side effect of either running cross-CPU
-	 * functions with interrupts off, or we can re-enable them explicitely
-	 * with a __sti() in smp_call_function_interrupt(), since
-	 * smp_call_function() is protected by a spinlock.
-	 * Or maybe we shouldn't set the IRQ_PER_CPU flag on cross-CPU
-	 * function calls IPI at all but that would make a special case.
-	 */
-	openpic_eoi();
 }
 
 static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs)
@@ -791,8 +791,11 @@
 			irq = i8259_irq( smp_processor_id() );
 		openpic_eoi();
         }
-	if (irq == OPENPIC_VEC_SPURIOUS + open_pic_irq_offset)
+	if (irq == OPENPIC_VEC_SPURIOUS + open_pic_irq_offset) {
 		irq = -1;
+		/* That's not SMP safe ... but who cares ? */
+		ppc_spurious_interrupts++;
+	}
 	return irq;
 }
 
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/open_pic_defs.h linux/arch/ppc/kernel/open_pic_defs.h
--- linux-2.4.3-pre1/arch/ppc/kernel/open_pic_defs.h	Mon Jan 22 15:41:15 2001
+++ linux/arch/ppc/kernel/open_pic_defs.h	Mon Dec  3 22:29:50 2001
@@ -28,8 +28,6 @@
 
 #ifdef __KERNEL__
 
-#include <linux/config.h>
-
     /*
      *  OpenPIC supports up to 2048 interrupt sources and up to 32 processors
      */
@@ -288,40 +286,6 @@
 /* Interrupt Source Registers */
 #define Vector_Priority			_Vector_Priority.Reg
 #define Destination			_Destination.Reg
-
-    /*
-     *  Local (static) OpenPIC Operations
-     */
-
-
-/* Global Operations */
-static void openpic_reset(void);
-static void openpic_enable_8259_pass_through(void);
-static void openpic_disable_8259_pass_through(void);
-static u_int openpic_irq(void);
-static void openpic_eoi(void);
-static u_int openpic_get_priority(void);
-static void openpic_set_priority(u_int pri);
-static u_int openpic_get_spurious(void);
-static void openpic_set_spurious(u_int vector);
-
-#ifdef CONFIG_SMP
-/* Interprocessor Interrupts */
-static void openpic_initipi(u_int ipi, u_int pri, u_int vector);
-static void openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs);
-#endif
-
-/* Timer Interrupts */
-static void openpic_inittimer(u_int timer, u_int pri, u_int vector);
-static void openpic_maptimer(u_int timer, u_int cpumask);
-
-/* Interrupt Sources */
-static void openpic_enable_irq(u_int irq);
-static void openpic_disable_irq(u_int irq);
-static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity,
-			    int is_level);
-static void openpic_mapirq(u_int irq, u_int cpumask);
-static void openpic_set_sense(u_int irq, int sense);
 
 #endif /* __KERNEL__ */
 
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/pci.c linux/arch/ppc/kernel/pci.c
--- linux-2.4.3-pre1/arch/ppc/kernel/pci.c	Wed Jan 24 15:28:36 2001
+++ linux/arch/ppc/kernel/pci.c	Mon Dec  3 22:29:50 2001
@@ -109,7 +109,7 @@
 		struct resource *res = dev->resource + i;
 		if (!res->start)
 			continue;
-		if (res->flags & IORESOURCE_MEM) {
+		if ((res->flags & IORESOURCE_MEM) && hose->pci_mem_offset) {
 			res->start += hose->pci_mem_offset;
 			res->end += hose->pci_mem_offset;
 #ifdef DEBUG
@@ -121,7 +121,9 @@
 
 		if ((res->flags & IORESOURCE_IO)
 		    && (unsigned long) hose->io_base_virt != isa_io_base) {
-			unsigned long offs = (unsigned long) hose->io_base_virt - isa_io_base;
+			unsigned long offs;
+
+			offs = (unsigned long)hose->io_base_virt - isa_io_base;
 			res->start += offs;
 			res->end += offs;
 			printk("Fixup IO res, dev: %x.%x, res_start: %lx->%lx\n",
@@ -245,13 +247,30 @@
 	}
 }
 
+static inline void alloc_resource(struct pci_dev *dev, int idx)
+{
+	struct resource *pr, *r = &dev->resource[idx];
+
+	DBG("PCI:%x:%x:%x: Resource %08lx-%08lx (f=%lx)\n",
+	    dev->bus->number, dev->devfn >> 3, dev->devfn & 7,
+	    r->start, r->end, r->flags);
+	pr = pci_find_parent_resource(dev, r);
+	if (!pr || request_resource(pr, r) < 0) {
+		printk(KERN_ERR "PCI: Cannot allocate resource region %d"
+		       " of device %s\n", idx, dev->slot_name);
+		/* We'll assign a new address later */
+		r->end -= r->start;
+		r->start = 0;
+	}
+}
+
 static void __init
 pcibios_allocate_resources(int pass)
 {
 	struct pci_dev *dev;
 	int idx, disabled;
 	u16 command;
-	struct resource *r, *pr;
+	struct resource *r;
 
 	pci_for_each_dev(dev) {
 		pci_read_config_word(dev, PCI_COMMAND, &command);
@@ -259,7 +278,7 @@
 			r = &dev->resource[idx];
 			if (r->parent)		/* Already allocated */
 				continue;
-			if (!r->start)		/* Address not assigned at all */
+			if (!r->start)		/* Not assigned at all */
 				continue;
 			if (r->end == 0xffffffff) {
 				/* LongTrail OF quirk: unassigned */
@@ -273,28 +292,19 @@
 				disabled = !(command & PCI_COMMAND_IO);
 			else
 				disabled = !(command & PCI_COMMAND_MEMORY);
-			if (pass == disabled) {
-				DBG("PCI: Resource %08lx-%08lx (f=%lx, d=%d, p=%d)\n",
-				    r->start, r->end, r->flags, disabled, pass);
-				pr = pci_find_parent_resource(dev, r);
-				if (!pr || request_resource(pr, r) < 0) {
-					printk(KERN_ERR "PCI: Cannot allocate resource region %d of device %s\n", idx, dev->slot_name);
-					/* We'll assign a new address later */
-					r->end -= r->start;
-					r->start = 0;
-				}
-			}
+			if (pass == disabled)
+				alloc_resource(dev, idx);
 		}
-		if (!pass) {
-			r = &dev->resource[PCI_ROM_RESOURCE];
-			if (r->flags & PCI_ROM_ADDRESS_ENABLE) {
-				/* Turn the ROM off, leave the resource region, but keep it unregistered. */
-				u32 reg;
-				DBG("PCI: Switching off ROM of %s\n", dev->slot_name);
-				r->flags &= ~PCI_ROM_ADDRESS_ENABLE;
-				pci_read_config_dword(dev, dev->rom_base_reg, &reg);
-				pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE);
-			}
+		if (pass)
+			continue;
+		r = &dev->resource[PCI_ROM_RESOURCE];
+		if (r->flags & PCI_ROM_ADDRESS_ENABLE) {
+			/* Turn the ROM off, leave the resource region, but keep it unregistered. */
+			u32 reg;
+			DBG("PCI: Switching off ROM of %s\n", dev->slot_name);
+			r->flags &= ~PCI_ROM_ADDRESS_ENABLE;
+			pci_read_config_dword(dev, dev->rom_base_reg, &reg);
+			pci_write_config_dword(dev, dev->rom_base_reg, reg & ~PCI_ROM_ADDRESS_ENABLE);
 		}
 	}
 }
@@ -315,17 +325,18 @@
 
 		for(idx=0; idx<6; idx++) {
 			r = &dev->resource[idx];
-
+#if 0	/* we don't need this PC-ism */
 			/*
 			 *  Don't touch IDE controllers and I/O ports of video cards!
 			 */
 			if ((class == PCI_CLASS_STORAGE_IDE && idx < 4) ||
 			    (class == PCI_CLASS_DISPLAY_VGA && (r->flags & IORESOURCE_IO)))
 				continue;
+#endif
 
 			/*
 			 *  We shall assign a new address to this resource, either because
-			 *  the BIOS forgot to do so or because we have decided the old
+			 *  the BIOS (sic) forgot to do so or because we have decided the old
 			 *  address was unusable for some reason.
 			 */
 			if (!r->start && r->end &&
@@ -572,7 +583,7 @@
 {
 	struct pci_controller *hose;
 	struct pci_bus *bus;
-	int next_busno;
+	int next_busno, i;
 
 	printk("PCI: Probing PCI hardware\n");
 
@@ -582,6 +593,17 @@
 			hose->first_busno = next_busno;
 		hose->last_busno = 0xff;
 		bus = pci_scan_bus(hose->first_busno, hose->ops, hose);
+		if (hose->io_resource.flags) {
+			unsigned long offs;
+
+			offs = (unsigned long)hose->io_base_virt - isa_io_base;
+			hose->io_resource.start += offs;
+			hose->io_resource.end += offs;
+			bus->resource[0] = &hose->io_resource;
+		}
+		for (i = 0; i < 3; ++i)
+			if (hose->mem_resources[i].flags)
+				bus->resource[i+1] = &hose->mem_resources[i];
 		hose->bus = bus;
 		hose->last_busno = bus->subordinate;
 		if (pci_assign_all_busses || next_busno <= hose->last_busno)
@@ -654,8 +676,20 @@
 
 void __init pcibios_fixup_bus(struct pci_bus *bus)
 {
+	struct pci_controller *hose;
+
 	pci_read_bridge_bases(bus);
-	
+
+	hose = pci_bus_to_hose(bus->number);
+
+	/* Apply pci_mem_offset to bridge mem resource */
+	if (hose->first_busno != bus->number)
+		if (bus->resource[1]->start && (bus->resource[1]->end != -1))
+		{
+			bus->resource[1]->start += hose->pci_mem_offset;
+			bus->resource[1]->end += hose->pci_mem_offset;
+		}
+
 	if ( ppc_md.pcibios_fixup_bus )
 		ppc_md.pcibios_fixup_bus(bus);
 }
@@ -773,6 +807,32 @@
 /* Obsolete functions. Should be removed once the symbios driver
  * is fixed
  */
+unsigned long
+phys_to_bus(unsigned long pa)
+{
+	struct pci_controller *hose;
+	int i;
+
+	for (hose = hose_head; hose; hose = hose->next) {
+		for (i = 0; i < 3; ++i) {
+			if (pa >= hose->mem_resources[i].start
+			    && pa <= hose->mem_resources[i].end) {
+				/*
+				 * XXX the hose->pci_mem_offset really
+				 * only applies to mem_resources[0].
+				 * We need a way to store an offset for
+				 * the others.  -- paulus
+				 */
+				if (i == 0)
+					pa -= hose->pci_mem_offset;
+				return pa;
+			}
+		}
+	}
+	/* hmmm, didn't find it */
+	return 0;
+}
+
 unsigned long
 pci_phys_to_bus(unsigned long pa, int busnr)
 {
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/pmac_pci.c linux/arch/ppc/kernel/pmac_pci.c
--- linux-2.4.3-pre1/arch/ppc/kernel/pmac_pci.c	Mon Jan 22 15:41:15 2001
+++ linux/arch/ppc/kernel/pmac_pci.c	Mon Dec  3 22:29:50 2001
@@ -29,6 +29,8 @@
 
 #undef DEBUG
 
+extern void process_bridge_ranges(struct pci_controller *hose,
+				  struct device_node *dev, int primary);
 static void add_bridges(struct device_node *dev);
 
 /* XXX Could be per-controller, but I don't think we risk anything by
@@ -372,7 +374,7 @@
 	(void)in_le32((volatile unsigned int *)bp->cfg_data);
 }
 
-static void __init
+static int __init
 setup_uninorth(struct pci_controller* hose, struct reg_property* addr)
 {
 	pci_assign_all_busses = 1;
@@ -380,6 +382,7 @@
 	hose->ops = &macrisc_pci_ops;
 	hose->cfg_addr = ioremap(addr->address + 0x800000, 0x1000);
 	hose->cfg_data = ioremap(addr->address + 0xc00000, 0x1000);
+#if 0 /* done in process_bridge_ranges now - paulus */
 	hose->io_base_phys = addr->address;
 	/* is 0x10000 enough for io space ? */
 	hose->io_base_virt = (void *)ioremap(addr->address, 0x10000);
@@ -389,6 +392,9 @@
 	 */
 	if (addr->address == 0xf2000000)
 		isa_io_base = (unsigned long)hose->io_base_virt;
+#endif
+	/* We "know" that the bridge at f2000000 has the PCI slots. */
+	return addr->address == 0xf2000000;
 }
 
 static void __init
@@ -399,8 +405,10 @@
 		ioremap(addr->address + 0x800000, 0x1000);
 	hose->cfg_data = (volatile unsigned char *)
 		ioremap(addr->address + 0xc00000, 0x1000);
+#if 0 /* done in process_bridge_ranges now - paulus */
 	hose->io_base_phys = addr->address;
 	hose->io_base_virt = (void *) ioremap(addr->address, 0x10000);
+#endif
 	init_bandit(hose);
 }
 
@@ -413,19 +421,23 @@
 		ioremap(addr->address + 0x800000, 0x1000);
 	hose->cfg_data = (volatile unsigned char *)
 		ioremap(addr->address + 0xc00000, 0x1000);
+#if 0 /* done in process_bridge_ranges now - paulus */
 	hose->io_base_phys = addr->address;
 	hose->io_base_virt = (void *) ioremap(addr->address, 0x10000);
+#endif
 }
 
 void __init
 setup_grackle(struct pci_controller *hose, unsigned io_space_size)
 {
 	setup_indirect_pci(hose, 0xfec00000, 0xfee00000);
+#if 0 /* done in process_bridge_ranges now - paulus */
 	hose->io_base_phys = 0xfe000000;
 	hose->io_base_virt = (void *) ioremap(0xfe000000, io_space_size);
 	pci_dram_offset = 0;
 	isa_mem_base = 0xfd000000;
 	isa_io_base = (unsigned long) hose->io_base_virt;
+#endif
 	if (machine_is_compatible("AAPL,PowerBook1998"))
 		grackle_set_loop_snoop(hose, 1);
 #if 0	/* Disabled for now, HW problems ??? */
@@ -445,6 +457,7 @@
 	struct reg_property *addr;
 	char* disp_name;
 	int *bus_range;
+	int first = 1, primary;
 	
 	for (; dev != NULL; dev = dev->next) {
 		addr = (struct reg_property *) get_property(dev, "reg", &len);
@@ -467,8 +480,9 @@
 		hose->last_busno = bus_range ? bus_range[1] : 0xff;
 
 		disp_name = NULL;
+		primary = first;
 		if (device_is_compatible(dev, "uni-north")) {
-			setup_uninorth(hose, addr);
+			primary = setup_uninorth(hose, addr);
 			disp_name = "UniNorth";
 		} else if (strcmp(dev->name, "pci") == 0) {
 			/* XXX assume this is a mpc106 (grackle) */
@@ -480,6 +494,7 @@
 		} else if (strcmp(dev->name, "chaos") == 0) {
 			setup_chaos(hose, addr);
 			disp_name = "Chaos";
+			primary = 0;
 		}
 		printk(KERN_INFO "Found %s PCI host bridge at 0x%08x. Firmware bus number: %d->%d\n",
 			disp_name, addr->address, hose->first_busno, hose->last_busno);
@@ -488,12 +503,14 @@
 			hose, hose->cfg_addr, hose->cfg_data);
 #endif		
 		
-		/* Setup a default isa_io_base */
-		if (isa_io_base == 0)
-			isa_io_base = (unsigned long)hose->io_base_virt;
+		/* Interpret the "ranges" property */
+		/* This also maps the I/O region and sets isa_io/mem_base */
+		process_bridge_ranges(hose, dev, primary);
 
 		/* Fixup "bus-range" OF property */
 		fixup_bus_range(dev);
+
+		first &= !primary;
 	}
 }
 
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/pmac_setup.c linux/arch/ppc/kernel/pmac_setup.c
--- linux-2.4.3-pre1/arch/ppc/kernel/pmac_setup.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/ppc/kernel/pmac_setup.c	Mon Dec  3 22:29:50 2001
@@ -81,7 +81,7 @@
 extern int mackbd_getkeycode(unsigned int scancode);
 extern int mackbd_translate(unsigned char keycode, unsigned char *keycodep,
 		     char raw_mode);
-extern int mackbd_unexpected_up(unsigned char keycode);
+extern char mackbd_unexpected_up(unsigned char keycode);
 extern void mackbd_leds(unsigned char leds);
 extern void __init mackbd_init_hw(void);
 extern int mac_hid_kbd_translate(unsigned char scancode, unsigned char *keycode,
@@ -253,6 +253,7 @@
 
 #endif
 
+#ifdef CONFIG_VT
 /*
  * Dummy mksound function that does nothing.
  * The real one is in the dmasound driver.
@@ -262,6 +263,7 @@
 pmac_mksound(unsigned int hz, unsigned int ticks)
 {
 }
+#endif /* CONFIG_VT */
 
 static volatile u32 *sysctrl_regs;
 
@@ -345,9 +347,9 @@
 #ifdef CONFIG_DUMMY_CONSOLE
 	conswitchp = &dummy_con;
 #endif
-
+#ifdef CONFIG_VT
 	kd_mksound = pmac_mksound;
-
+#endif
 #ifdef CONFIG_BLK_DEV_INITRD
 	if (initrd_start)
 		ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/ppc8xx_pic.c linux/arch/ppc/kernel/ppc8xx_pic.c
--- linux-2.4.3-pre1/arch/ppc/kernel/ppc8xx_pic.c	Tue May  2 13:05:40 2000
+++ linux/arch/ppc/kernel/ppc8xx_pic.c	Mon Dec  3 22:29:50 2001
@@ -153,6 +153,20 @@
 	irq += i8259_pic.irq_offset;
 	return (request_8xxirq(irq, handler, irqflags, devname, dev_id));
 #else
-	panic("request_irq");
+	/*
+	 * Handle other "well-known" interrupts, but panic on unknown ones.
+	 */
+	switch (irq) {
+#ifdef	IDE0_INTERRUPT
+	case IDE0_INTERRUPT:		/* fall through */
+#endif
+#ifdef	IDE1_INTERRUPT
+	case IDE1_INTERRUPT:		/* fall through */
+#endif
+		return (request_8xxirq(irq, handler, irqflags, devname, dev_id));
+	
+	default:			/* unknown IRQ -> panic */
+		panic("request_irq");
+	}
 #endif
 }
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/ppc_ksyms.c linux/arch/ppc/kernel/ppc_ksyms.c
--- linux-2.4.3-pre1/arch/ppc/kernel/ppc_ksyms.c	Tue Feb 13 13:15:04 2001
+++ linux/arch/ppc/kernel/ppc_ksyms.c	Mon Dec  3 22:29:50 2001
@@ -46,6 +46,10 @@
 #endif /* CONFIG_SMP */
 #include <asm/time.h>
 
+#ifdef  CONFIG_8xx
+#include "../8xx_io/commproc.h"
+#endif
+
 /* Tell string.h we don't want memcpy etc. as cpp defines */
 #define EXPORT_SYMTAB_STROPS
 
@@ -94,7 +98,7 @@
 EXPORT_SYMBOL_NOVERS(pci_dram_offset);
 #endif
 EXPORT_SYMBOL(ISA_DMA_THRESHOLD);
-EXPORT_SYMBOL(DMA_MODE_READ);
+EXPORT_SYMBOL_NOVERS(DMA_MODE_READ);
 EXPORT_SYMBOL(DMA_MODE_WRITE);
 #ifndef CONFIG_8xx
 #if defined(CONFIG_ALL_PPC)
@@ -192,6 +196,9 @@
 EXPORT_SYMBOL(giveup_altivec);
 #endif /* CONFIG_ALTIVEC */
 #ifdef CONFIG_SMP
+EXPORT_SYMBOL(global_irq_lock);
+EXPORT_SYMBOL(global_irq_count);
+EXPORT_SYMBOL(global_irq_holder);
 EXPORT_SYMBOL(__global_cli);
 EXPORT_SYMBOL(__global_sti);
 EXPORT_SYMBOL(__global_save_flags);
@@ -242,11 +249,11 @@
 EXPORT_SYMBOL(set_backlight_enable);
 EXPORT_SYMBOL(register_backlight_controller);
 #endif /* CONFIG_PMAC_BACKLIGHT */
-EXPORT_SYMBOL_NOVERS(sys_ctrler);
 #ifndef CONFIG_MACH_SPECIFIC
 EXPORT_SYMBOL_NOVERS(have_of);
 #endif /* CONFIG_MACH_SPECIFIC */
 #if defined(CONFIG_ALL_PPC)
+EXPORT_SYMBOL_NOVERS(sys_ctrler);
 EXPORT_SYMBOL(find_devices);
 EXPORT_SYMBOL(find_type_devices);
 EXPORT_SYMBOL(find_compatible_devices);
@@ -280,7 +287,9 @@
 #if defined(CONFIG_SCSI) && defined(CONFIG_ALL_PPC)
 EXPORT_SYMBOL(note_scsi_host);
 #endif
+#ifdef CONFIG_VT
 EXPORT_SYMBOL(kd_mksound);
+#endif
 #ifdef CONFIG_NVRAM
 EXPORT_SYMBOL(nvram_read_byte);
 EXPORT_SYMBOL(nvram_write_byte);
@@ -342,11 +351,18 @@
 EXPORT_SYMBOL(debugger_fault_handler);
 #endif
 
+#ifdef  CONFIG_8xx
+EXPORT_SYMBOL(request_8xxirq);
+EXPORT_SYMBOL(cpm_install_handler);
+EXPORT_SYMBOL(cpm_free_handler);
+#endif /* CONFIG_8xx */
+
 EXPORT_SYMBOL(ret_to_user_hook);
 EXPORT_SYMBOL(do_softirq);
 EXPORT_SYMBOL(next_mmu_context);
 EXPORT_SYMBOL(set_context);
 EXPORT_SYMBOL(mmu_context_overflow);
+EXPORT_SYMBOL_NOVERS(disarm_decr);
 #if !defined(CONFIG_8xx) && !defined(CONFIG_4xx)
 extern long *intercept_table;
 EXPORT_SYMBOL(intercept_table);
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/prep_pci.c linux/arch/ppc/kernel/prep_pci.c
--- linux-2.4.3-pre1/arch/ppc/kernel/prep_pci.c	Wed Jan 24 15:28:36 2001
+++ linux/arch/ppc/kernel/prep_pci.c	Mon Dec  3 22:29:50 2001
@@ -37,6 +37,10 @@
 /* How is the 82378 PIRQ mapping setup? */
 unsigned char *Motherboard_routes;
 
+void (*Motherboard_non0)(struct pci_dev *);
+
+void Powerplus_Map_Non0(struct pci_dev *);
+
 /* Used for Motorola to store system config register */
 static unsigned long	*ProcInfo;
 
@@ -505,6 +509,52 @@
         13      /* Line 4 */
 };
 
+/* Motorola PowerPlus architecture PCI IRQ tables */
+/* Interrupt line values for INTA-D on primary/secondary MPIC inputs */
+
+struct powerplus_irq_list
+{
+	unsigned char primary[4];       /* INT A-D */
+	unsigned char secondary[4];     /* INT A-D */
+};
+
+/*
+ * For standard PowerPlus boards, bus 0 PCI INTs A-D are routed to
+ * OpenPIC inputs 9-12.  PCI INTs A-D from the on board P2P bridge
+ * are routed to OpenPIC inputs 5-8.  These values are offset by
+ * 16 in the table to reflect the Linux kernel interrupt value.
+ */
+struct powerplus_irq_list Powerplus_pci_IRQ_list =
+{
+	{25, 26, 27, 28},
+	{21, 22, 23, 24}
+};
+
+/*
+ * For the MCP750 (system slot board), cPCI INTs A-D are routed to
+ * OpenPIC inputs 8-11 and the PMC INTs A-D are routed to OpenPIC
+ * input 3.  On a hot swap MCP750, the companion card PCI INTs A-D
+ * are routed to OpenPIC inputs 12-15. These values are offset by
+ * 16 in the table to reflect the Linux kernel interrupt value.
+ */
+struct powerplus_irq_list Mesquite_pci_IRQ_list =
+{
+	{24, 25, 26, 27},
+	{28, 29, 30, 31}
+};
+
+/*
+ * This table represents the standard PCI swizzle defined in the
+ * PCI bus specification.
+ */
+static unsigned char prep_pci_intpins[4][4] =
+{
+	{ 1, 2, 3, 4},  /* Buses 0, 4, 8, ... */
+	{ 2, 3, 4, 1},  /* Buses 1, 5, 9, ... */
+	{ 3, 4, 1, 2},  /* Buses 2, 6, 10 ... */
+	{ 4, 1, 2, 3},  /* Buses 3, 7, 11 ... */
+};
+
 /* We have to turn on LEVEL mode for changed IRQ's */
 /* All PCI IRQ's need to be level mode, so this should be something
  * other than hard-coded as well... IRQ's are individually mappable
@@ -599,6 +649,7 @@
 #define MOT_RAVEN_PRESENT	0x1
 #define MOT_HAWK_PRESENT	0x2
 
+int mot_entry = -1;
 int prep_keybd_present = 1;
 int MotMPIC;
 int mot_multi;
@@ -682,33 +733,36 @@
 	const char	*name;
 	unsigned char	*map;
 	unsigned char	*routes;
+	void            (*map_non0_bus)(struct pci_dev *);      /* For boards with more than bus 0 devices. */
+	struct powerplus_irq_list *pci_irq_list; /* List of PCI MPIC inputs */
+	unsigned char   secondary_bridge_devfn; /* devfn of secondary bus transparent bridge */
 } mot_info[] = {
-	{0x300, 0x00, 0x00, "MVME 2400",			Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x010, 0x00, 0x00, "Genesis",				Genesis_pci_IRQ_map,	Genesis_pci_IRQ_routes},
-	{0x020, 0x00, 0x00, "Powerstack (Series E)",		Comet_pci_IRQ_map,	Comet_pci_IRQ_routes},
-	{0x040, 0x00, 0x00, "Blackhawk (Powerstack)",		Blackhawk_pci_IRQ_map,	Blackhawk_pci_IRQ_routes},
-	{0x050, 0x00, 0x00, "Omaha (PowerStack II Pro3000)",	Omaha_pci_IRQ_map,	Omaha_pci_IRQ_routes},
-	{0x060, 0x00, 0x00, "Utah (Powerstack II Pro4000)",	Utah_pci_IRQ_map,	Utah_pci_IRQ_routes},
-	{0x0A0, 0x00, 0x00, "Powerstack (Series EX)",		Comet2_pci_IRQ_map,	Comet2_pci_IRQ_routes},
-	{0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)",		Mesquite_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)",		Sitka_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC",	Mesquite_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xF6, 0x80, "MTX Plus",				MTXplus_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xF6, 0x81, "Dual MTX Plus",			MTXplus_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port",		MTX_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port",	MTX_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port",		MTX_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port",	MTX_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xF9, 0x00, "MVME 2300",			Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xFA, 0x00, "MVME 2300SC/2600",			Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M",		Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761",	Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M",		Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M",		Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761",		Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761",		Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x1E0, 0xFF, 0x00, "MVME 1600-001 or 1600-011",	Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes},
-	{0x000, 0x00, 0x00, "",					NULL,			NULL}
+	{0x300, 0x00, 0x00, "MVME 2400",			Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF},
+	{0x010, 0x00, 0x00, "Genesis",				Genesis_pci_IRQ_map,	Genesis_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00},
+	{0x020, 0x00, 0x00, "Powerstack (Series E)",		Comet_pci_IRQ_map,	Comet_pci_IRQ_routes, NULL, NULL, 0x00},
+	{0x040, 0x00, 0x00, "Blackhawk (Powerstack)",		Blackhawk_pci_IRQ_map,	Blackhawk_pci_IRQ_routes, NULL, NULL, 0x00},
+	{0x050, 0x00, 0x00, "Omaha (PowerStack II Pro3000)",	Omaha_pci_IRQ_map,	Omaha_pci_IRQ_routes, NULL, NULL, 0x00},
+	{0x060, 0x00, 0x00, "Utah (Powerstack II Pro4000)",	Utah_pci_IRQ_map,	Utah_pci_IRQ_routes, NULL, NULL, 0x00},
+	{0x0A0, 0x00, 0x00, "Powerstack (Series EX)",		Comet2_pci_IRQ_map,	Comet2_pci_IRQ_routes, NULL, NULL, 0x00},
+	{0x1E0, 0xE0, 0x00, "Mesquite cPCI (MCP750)",		Mesquite_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Mesquite_pci_IRQ_list, 0xFF},
+	{0x1E0, 0xE1, 0x00, "Sitka cPCI (MCPN750)",		Sitka_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF},
+	{0x1E0, 0xE2, 0x00, "Mesquite cPCI (MCP750) w/ HAC",	Mesquite_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Mesquite_pci_IRQ_list, 0xC0},
+	{0x1E0, 0xF6, 0x80, "MTX Plus",				MTXplus_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xA0},
+	{0x1E0, 0xF6, 0x81, "Dual MTX Plus",			MTXplus_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xA0},
+	{0x1E0, 0xF7, 0x80, "MTX wo/ Parallel Port",		MTX_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00},
+	{0x1E0, 0xF7, 0x81, "Dual MTX wo/ Parallel Port",	MTX_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00},
+	{0x1E0, 0xF8, 0x80, "MTX w/ Parallel Port",		MTX_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00},
+	{0x1E0, 0xF8, 0x81, "Dual MTX w/ Parallel Port",	MTX_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00},
+	{0x1E0, 0xF9, 0x00, "MVME 2300",			Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF},
+	{0x1E0, 0xFA, 0x00, "MVME 2300SC/2600",			Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF},
+	{0x1E0, 0xFB, 0x00, "MVME 2600 with MVME712M",		Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF},
+	{0x1E0, 0xFC, 0x00, "MVME 2600/2700 with MVME761",	Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF},
+	{0x1E0, 0xFD, 0x80, "MVME 3600 with MVME712M",		Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0x00},
+	{0x1E0, 0xFD, 0x81, "MVME 4600 with MVME712M",		Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF},
+	{0x1E0, 0xFE, 0x80, "MVME 3600 with MVME761",		Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF},
+	{0x1E0, 0xFE, 0x81, "MVME 4600 with MVME761",		Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF},
+	{0x1E0, 0xFF, 0x00, "MVME 1600-001 or 1600-011",	Genesis2_pci_IRQ_map,	Raven_pci_IRQ_routes, Powerplus_Map_Non0, &Powerplus_pci_IRQ_list, 0xFF},
+	{0x000, 0x00, 0x00, "",					NULL,			NULL, NULL, NULL, 0x00}
 };
 
 unsigned long __init prep_route_pci_interrupts(void)
@@ -723,7 +777,6 @@
 		unsigned char  cpu_type;
 		unsigned char  base_mod;
 		int	       entry;
-		int	       mot_entry = -1;
 
 		cpu_type = inb(MOTOROLA_CPUTYPE_REG) & 0xF0;
 		base_mod = inb(MOTOROLA_BASETYPE_REG);
@@ -769,6 +822,7 @@
 		Motherboard_map_name = (unsigned char *)mot_info[mot_entry].name;
 		Motherboard_map = mot_info[mot_entry].map;
 		Motherboard_routes = mot_info[mot_entry].routes;
+		Motherboard_non0 = mot_info[mot_entry].map_non0_bus;
 
 		if (!(mot_info[entry].cpu_type & 0x100)) {
 			/* AJF adjust level/edge control according to routes */
@@ -836,6 +890,157 @@
 }
 
 void __init
+prep_pib_init(void)
+{
+unsigned char   reg;
+unsigned short  short_reg;
+
+struct pci_dev *dev = NULL;
+
+	if (( _prep_type == _PREP_Motorola) && (OpenPIC_Addr)) {
+		/*
+		 * Perform specific configuration for the Via Tech or
+		 * or Winbond PCI-ISA-Bridge part.
+		 */
+		if ((dev = pci_find_device(PCI_VENDOR_ID_VIA, 
+					PCI_DEVICE_ID_VIA_82C586_1, dev))) {
+			/*
+			 * PPCBUG does not set the enable bits
+			 * for the IDE device. Force them on here.
+			 */
+			pcibios_read_config_byte(dev->bus->number, 
+					dev->devfn, 0x40, &reg);
+
+			reg |= 0x03; /* IDE: Chip Enable Bits */
+			pcibios_write_config_byte(dev->bus->number, 
+					dev->devfn, 0x40, reg);
+
+			/* Force correct IDE function interrupt */
+			dev->irq = 14;
+			pcibios_write_config_byte(dev->bus->number,
+					dev->devfn,
+					PCI_INTERRUPT_LINE,
+					dev->irq);
+
+		} else if ((dev = pci_find_device(PCI_VENDOR_ID_WINBOND, 
+					PCI_DEVICE_ID_WINBOND_83C553, dev))) {
+			/*
+			 * Clear the PCI Interrupt Routing Control Register.
+			 */
+			short_reg = 0x0000;
+			pci_write_config_word(dev, 0x44, short_reg);
+			if (OpenPIC_Addr){
+				/*
+				 * Route both IDE interrupts to IRQ 14
+				 */
+				reg = 0xEE;
+				pci_write_config_byte(dev, 0x44, reg);
+			}
+		}
+		if ((dev = pci_find_device(PCI_VENDOR_ID_VIA,
+						PCI_DEVICE_ID_VIA_82C586_2,
+						dev)))
+		{
+			/* Force correct USB function interrupt */
+			dev->irq = 11;
+			pcibios_write_config_byte(dev->bus->number,
+					dev->devfn,
+					PCI_INTERRUPT_LINE,
+					dev->irq);
+		}
+	}
+	if ((dev = pci_find_device(PCI_VENDOR_ID_WINBOND,
+				   PCI_DEVICE_ID_WINBOND_82C105, dev))){
+		if (OpenPIC_Addr){
+			/* Disable LEGIRQ mode so PCI INTs are routed to
+			   the 8259 */
+			printk("Set winbond IDE to native mode\n");
+			pci_write_config_dword(dev, 0x40, 0x10ff00a1);
+		}else{
+			/* Enable LEGIRQ for PCI INT -> 8259 IRQ routing */
+			pci_write_config_dword(dev, 0x40, 0x10ff08a1);
+		}
+	}
+}
+
+void
+Powerplus_Map_Non0(struct pci_dev *dev)
+{
+	struct pci_bus  *pbus;          /* Parent bus structure pointer */
+	struct pci_dev  *tdev = dev;    /* Temporary device structure */
+	unsigned int    devnum;         /* Accumulated device number */
+	unsigned char   intline;        /* Linux interrupt value */
+	unsigned char   intpin;         /* PCI interrupt pin */
+
+	/* Check for valid PCI dev pointer */
+	if (dev == NULL) return;
+
+	/* Initialize bridge IDSEL variable */
+	devnum = PCI_SLOT(tdev->devfn);
+
+	/* Read the interrupt pin of the device and adjust for indexing */
+	pcibios_read_config_byte(dev->bus->number, dev->devfn,
+			PCI_INTERRUPT_PIN, &intpin);
+
+	/* If device doesn't request an interrupt, return */
+	if ( (intpin < 1) || (intpin > 4) )
+		return;
+
+	intpin--;
+
+	/*
+	 * Walk up to bus 0, adjusting the interrupt pin for the standard
+	 * PCI bus swizzle.
+	 */
+	do {
+		intpin = (prep_pci_intpins[devnum % 4][intpin]) - 1;
+		pbus = tdev->bus;        /* up one level */
+		tdev = pbus->self;
+		devnum = PCI_SLOT(tdev->devfn);
+	} while(tdev->bus->number);
+
+	/* Use the primary interrupt inputs by default */
+	intline = mot_info[mot_entry].pci_irq_list->primary[intpin];
+
+	/*
+	 * If the board has secondary interrupt inputs, walk the bus and
+	 * note the devfn of the bridge from bus 0.  If it is the same as
+	 * the devfn of the bus bridge with secondary inputs, use those.
+	 * Otherwise, assume it's a PMC site and get the interrupt line
+	 * value from the interrupt routing table.
+	 */ 
+	if (mot_info[mot_entry].secondary_bridge_devfn)
+	{
+		pbus = dev->bus;
+
+		while (pbus->primary != 0)
+			pbus = pbus->parent;
+
+		if ((pbus->self)->devfn != 0xA0)
+		{
+			if ((pbus->self)->devfn == mot_info[mot_entry].secondary_bridge_devfn)
+				intline = mot_info[mot_entry].pci_irq_list->secondary[intpin];
+			else
+			{
+				if ((char *)(mot_info[mot_entry].map) == (char *)Mesquite_pci_IRQ_map)
+					intline = mot_info[mot_entry].map[((pbus->self)->devfn)/8] + 16;
+				else
+				{
+					int i;
+					for (i=0;i<3;i++)
+						intpin = (prep_pci_intpins[devnum % 4][intpin]) - 1;
+					intline = mot_info[mot_entry].pci_irq_list->primary[intpin];
+				}
+			}
+		}
+	}
+
+	/* Write calculated interrupt value to header and device list */
+	dev->irq = intline;
+	pci_write_config_byte(dev, PCI_INTERRUPT_LINE, (u8)dev->irq);
+}
+
+void __init
 prep_pcibios_fixup(void)
 {
         struct pci_dev *dev;
@@ -849,11 +1054,21 @@
 	if (OpenPIC_Addr) {
 		/* PCI interrupts are controlled by the OpenPIC */
 		pci_for_each_dev(dev) {
-			if (dev->bus->number == 0) {
+			if (dev->bus->number == 0)
+			{
                        		dev->irq = openpic_to_irq(Motherboard_map[PCI_SLOT(dev->devfn)]);
-				pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_PIN, dev->irq);
+				pcibios_write_config_byte(dev->bus->number, dev->devfn, PCI_INTERRUPT_LINE, dev->irq);
+			}
+			else
+			{
+				if (Motherboard_non0 != NULL)
+					Motherboard_non0(dev);
 			}
 		}
+
+		/* Setup the Winbond or Via PIB */
+		prep_pib_init();
+
 		return;
 	}
 
@@ -912,6 +1127,7 @@
 	hose->first_busno = 0;
 	hose->last_busno = 0xff;
 	hose->pci_mem_offset = PREP_ISA_MEM_BASE;
+	hose->io_base_virt = (void *)PREP_ISA_IO_BASE;
 	
 	printk("PReP architecture\n");
 	{
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/prep_setup.c linux/arch/ppc/kernel/prep_setup.c
--- linux-2.4.3-pre1/arch/ppc/kernel/prep_setup.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/ppc/kernel/prep_setup.c	Mon Dec  3 22:29:50 2001
@@ -215,7 +215,6 @@
 void __init
 prep_setup_arch(void)
 {
-	extern char cmd_line[];
 	unsigned char reg;
 #if 0 /* unused?? */
 	unsigned char ucMothMemType;
@@ -272,8 +271,6 @@
               }
       }
 
-	printk("Boot arguments: %s\n", cmd_line);
-	
 #ifdef CONFIG_SOUND_CS4232
 	/*
 	 * setup proper values for the cs4232 driver so we don't have
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c
--- linux-2.4.3-pre1/arch/ppc/kernel/prom.c	Mon Jan 22 15:41:15 2001
+++ linux/arch/ppc/kernel/prom.c	Mon Dec  3 22:29:50 2001
@@ -757,8 +757,11 @@
 		setup_disp_fake_bi(RELOC(prom_disp_node));
 #endif
 
-	/* If OpenFirmware version >= 3, then use quiesce call */
-	if (prom_version >= 3) {
+	/* If pmac, then use quiesce call. We can't rely on prom_version
+	 * since some old iMacs appear to have an incorrect /openprom/model
+	 * entry in the device tree
+	 */
+	if (!chrp) {
 		prom_print(RELOC("Calling quiesce ...\n"));
 		call_prom(RELOC("quiesce"), 0, 0);
 	}
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/setup.c linux/arch/ppc/kernel/setup.c
--- linux-2.4.3-pre1/arch/ppc/kernel/setup.c	Tue Feb 13 13:15:04 2001
+++ linux/arch/ppc/kernel/setup.c	Mon Dec  3 22:29:50 2001
@@ -386,8 +386,8 @@
 	if ( i )
 		len += sprintf(buffer+len, "\n");
 	len += sprintf(buffer+len,"total bogomips\t: %lu.%02lu\n",
-		       (bogosum+2500)/500000,
-		       (bogosum+2500)/5000 % 100);
+		       (bogosum+2500)/(500000/HZ),
+		       (bogosum+2500)/(5000/HZ) % 100);
 #endif /* CONFIG_SMP */
 
 	/*
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/smp.c linux/arch/ppc/kernel/smp.c
--- linux-2.4.3-pre1/arch/ppc/kernel/smp.c	Mon Jan 22 15:41:15 2001
+++ linux/arch/ppc/kernel/smp.c	Mon Dec  3 22:29:50 2001
@@ -100,14 +100,14 @@
 #define PSURGE_QUAD_CKSTOP_RDBK	8
 #define PSURGE_QUAD_RESET_CTL	11
 
-#define PSURGE_QUAD_OUT(r, v)	(out_8((u8 *)(quad_base+((r)<<2)+1), (v)))
-#define PSURGE_QUAD_IN(r)	(in_8((u8 *)(quad_base+((r)<<2)+1)) & 0x0f)
+#define PSURGE_QUAD_OUT(r, v)	(out_8(quad_base + ((r) << 4) + 4, (v)))
+#define PSURGE_QUAD_IN(r)	(in_8(quad_base + ((r) << 4) + 4) & 0x0f)
 #define PSURGE_QUAD_BIS(r, v)	(PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) | (v)))
 #define PSURGE_QUAD_BIC(r, v)	(PSURGE_QUAD_OUT((r), PSURGE_QUAD_IN(r) & ~(v)))
 
 /* virtual addresses for the above */
 static volatile u8 *hhead_base;
-static volatile u32 *quad_base;
+static volatile u8 *quad_base;
 static volatile u32 *psurge_pri_intr;
 static volatile u8 *psurge_sec_intr;
 static volatile u32 *psurge_start;
@@ -216,7 +216,7 @@
 
 /*
  * Determine a quad card presence. We read the board ID register, we
- * for the data bus to change to something else, and we read it again.
+ * force the data bus to change to something else, and we read it again.
  * It it's stable, then the register probably exist (ugh !)
  */
 static int __init psurge_quad_probe(void)
@@ -303,11 +303,7 @@
 	psurge_type = psurge_quad_probe();
 	if (psurge_type != PSURGE_DUAL) {
 		psurge_quad_init();
-		/* I believe we could "count" CPUs by counting 1 bits
-		 * in procbits on a quad board. For now, we assume 4,
-		 * non-present CPUs will just be seen as "stuck".
-		 * (hope they are the higher-numbered ones -- paulus)
-		 */
+		/* All released cards using this HW design have 4 CPUs */
 		ncpus = 4;
 	} else {
 		iounmap((void *) quad_base);
@@ -424,11 +420,10 @@
 		break;
 	case MSG_ALL_BUT_SELF:
 		openpic_cause_IPI(msg,
-       			0xffffffff & ~(1 << smp_hw_index[smp_processor_id()])); 
-
+				  0xffffffff & ~(1 << smp_processor_id()));
 		break;
 	default:
-		openpic_cause_IPI(msg, smp_hw_index[1<<target]);
+		openpic_cause_IPI(msg, 1<<target);
 		break;
 	}
 }
@@ -437,27 +432,14 @@
 smp_core99_probe(void)
 {
 	struct device_node *cpus;
-	int *pp;
 	int i, ncpus = 1;
 
 	if (ppc_md.progress) ppc_md.progress("smp_core99_probe", 0x345);
-#if 0	/* Paulus method.. doesn't seem to work on earlier dual G4's??*/
-	cpus = find_devices("cpus");
-	if (cpus != 0) {
-		pp = (int *) get_property(cpus, "#cpus", NULL);
-		if (pp != NULL)
-			ncpus = *pp;
-	}
-#else	/* My original method -- Troy <hozer@drgw.net> */
-	
 	cpus = find_type_devices("cpu");
-	if (cpus){
-		for ( ncpus = 1; cpus->next; cpus = cpus->next ){
-					ncpus++;
-		}
-	}
-#endif
-	printk("smp_core99_probe: OF reports %d cpus\n", ncpus);
+	if (cpus)
+		while ((cpus = cpus->next) != NULL)
+			++ncpus;
+	printk("smp_core99_probe: found %d cpus\n", ncpus);
 	if (ncpus > 1) {
 		openpic_request_IPIs();
 		for (i = 1; i < ncpus; ++i)
@@ -1010,6 +992,9 @@
 
 	/* Setup CPU 0 last (important) */
 	smp_ops->setup_cpu(0);
+	
+	if (smp_num_cpus < 2)
+		smp_tb_synchronized = 1;
 }
 
 void __init smp_software_tb_sync(int cpu)
@@ -1027,17 +1012,6 @@
 	register unsigned long start = 0;
 	register unsigned long stop = 0;
 	register unsigned long temp = 0;
-	
-	if (smp_num_cpus < 2) {
-		smp_tb_synchronized = 1;
-		return;
-	}
-
-	/* This code need fixing on >2 CPUs --BenH/paulus */
-	if (smp_num_cpus > 2) {
-		smp_tb_synchronized = 0;
-		return;
-	}
 
 	set_tb(0, 0);
 
@@ -1107,6 +1081,7 @@
 	if (ppc_md.progress) ppc_md.progress("smp_commence", 0x370);
 	wmb();
 	smp_commenced = 1;
+
 	/* if the smp_ops->setup_cpu function has not already synched the
 	 * timebases with a nicer hardware-based method, do so now
 	 *
@@ -1117,9 +1092,9 @@
 	 * since if this code runs pretty early and needs all cpus that
 	 * reported in in smp_callin_map to be working
 	 *
-	 * NOTE2: this code doesn't seem to work on > 2 cpus. -- paulus
+	 * NOTE2: this code doesn't seem to work on > 2 cpus. -- paulus/BenH
 	 */
-	if (!smp_tb_synchronized) {
+	if (!smp_tb_synchronized && smp_num_cpus == 2) {
 		unsigned long flags;
 		__save_and_cli(flags);	
 		smp_software_tb_sync(0);
@@ -1142,7 +1117,7 @@
 	while(!smp_commenced)
 		barrier();
 	/* see smp_commence for more info */
-	if (!smp_tb_synchronized){
+	if (!smp_tb_synchronized && smp_num_cpus == 2) {
 		smp_software_tb_sync(cpu);
 	}
 	__sti();
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/softemu8xx.c linux/arch/ppc/kernel/softemu8xx.c
--- linux-2.4.3-pre1/arch/ppc/kernel/softemu8xx.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/ppc/kernel/softemu8xx.c	Mon Dec  3 22:29:50 2001
@@ -75,12 +75,12 @@
 		sdisp = (instword & 0xffff);
 		ea = (uint *)(regs->gpr[idxreg] + sdisp);
 		if (copy_from_user(ip, ea, sizeof(double)))
-			retval = EFAULT;
+			retval = -EFAULT;
 		break;
 		
 	case LFDU:
 		if (copy_from_user(ip, ea, sizeof(double)))
-			retval = EFAULT;
+			retval = -EFAULT;
 		else
 			regs->gpr[idxreg] = (uint)ea;
 		break;
@@ -88,7 +88,7 @@
 		sdisp = (instword & 0xffff);
 		ea = (uint *)(regs->gpr[idxreg] + sdisp);
 		if (copy_from_user(ip, ea, sizeof(float)))
-			retval = EFAULT;
+			retval = -EFAULT;
 		break;
 	case STFD:
 		/* this is a 16 bit quantity that is sign extended
@@ -97,12 +97,12 @@
 		sdisp = (instword & 0xffff);
 		ea = (uint *)(regs->gpr[idxreg] + sdisp);
 		if (copy_to_user(ea, ip, sizeof(double)))
-			retval = EFAULT;
+			retval = -EFAULT;
 		break;
 
 	case STFDU:
 		if (copy_to_user(ea, ip, sizeof(double)))
-			retval = EFAULT;
+			retval = -EFAULT;
 		else
 			regs->gpr[idxreg] = (uint)ea;
 		break;
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/time.c linux/arch/ppc/kernel/time.c
--- linux-2.4.3-pre1/arch/ppc/kernel/time.c	Mon Jan 22 15:41:15 2001
+++ linux/arch/ppc/kernel/time.c	Mon Dec  3 22:29:50 2001
@@ -67,6 +67,8 @@
 
 #include <asm/time.h>
 
+unsigned long disarm_decr[NR_CPUS];
+
 #ifdef CONFIG_SMP
 extern void smp_local_timer_interrupt(struct pt_regs *);
 extern int smp_tb_synchronized;
@@ -147,7 +149,6 @@
 	
 	if (!user_mode(regs))
 		ppc_do_profile(instruction_pointer(regs));
-
 	do { 
 		jiffy_stamp += tb_ticks_per_jiffy;
 	  	if (smp_processor_id()) continue;
@@ -184,7 +185,8 @@
 		}
 		write_unlock(&xtime_lock);
 	} while((next_dec = tb_ticks_per_jiffy - tb_delta(&jiffy_stamp)) < 0);
-	set_dec(next_dec);
+	if ( !disarm_decr[smp_processor_id()] )
+		set_dec(next_dec);
 	last_jiffy_stamp(cpu) = jiffy_stamp;
 
 #ifdef CONFIG_SMP
diff -urN linux-2.4.3-pre1/arch/ppc/kernel/traps.c linux/arch/ppc/kernel/traps.c
--- linux-2.4.3-pre1/arch/ppc/kernel/traps.c	Fri Feb  9 11:29:44 2001
+++ linux/arch/ppc/kernel/traps.c	Mon Dec  3 22:29:50 2001
@@ -237,7 +237,7 @@
 		return retval;
 
 	if (get_user(instword, (uint *)(regs->nip)))
-		return EFAULT;
+		return -EFAULT;
 
 	/* Emulate the mfspr rD, PVR.
 	 */
@@ -281,7 +281,7 @@
 		/* Try to emulate it if we should. */
 		int errcode;
 		if ((errcode = emulate_instruction(regs))) {
-			if (errcode == EFAULT)
+			if (errcode == -EFAULT)
 				_exception(SIGBUS, regs);
 			else
 				_exception(SIGILL, regs);
diff -urN linux-2.4.3-pre1/arch/ppc/mm/fault.c linux/arch/ppc/mm/fault.c
--- linux-2.4.3-pre1/arch/ppc/mm/fault.c	Mon Jan 22 15:41:15 2001
+++ linux/arch/ppc/mm/fault.c	Mon Dec  3 22:29:50 2001
@@ -276,7 +276,7 @@
 	
 	pte = va_to_pte(address);
 	if (pte)
-		return(((unsigned long)(pte_val(*pte)) & PAGE_MASK) | (address & ~(PAGE_MASK-1)));
+		return(((unsigned long)(pte_val(*pte)) & PAGE_MASK) | (address & ~(PAGE_MASK)));
 	return (0);
 }
 
diff -urN linux-2.4.3-pre1/arch/ppc/mm/init.c linux/arch/ppc/mm/init.c
--- linux-2.4.3-pre1/arch/ppc/mm/init.c	Mon Jan 22 15:41:15 2001
+++ linux/arch/ppc/mm/init.c	Mon Dec  3 22:29:50 2001
@@ -889,13 +889,14 @@
 #ifdef CONFIG_BLK_DEV_INITRD
 void free_initrd_mem(unsigned long start, unsigned long end)
 {
+	printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
+
 	for (; start < end; start += PAGE_SIZE) {
 		ClearPageReserved(virt_to_page(start));
 		set_page_count(virt_to_page(start), 1);
 		free_page(start);
 		totalram_pages++;
 	}
-	printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10);
 }
 #endif
 
diff -urN linux-2.4.3-pre1/arch/ppc/treeboot/mkevimg linux/arch/ppc/treeboot/mkevimg
--- linux-2.4.3-pre1/arch/ppc/treeboot/mkevimg	Mon May 15 14:53:30 2000
+++ linux/arch/ppc/treeboot/mkevimg	Mon Dec  3 22:29:50 2001
@@ -431,7 +431,7 @@
 
   close(BOOT);
 
-  print("\nBoot image file \"$ofile\" built successfuly.\n\n");
+  print("\nBoot image file \"$ofile\" built successfully.\n\n");
 
   exit(0);
 }
diff -urN linux-2.4.3-pre1/arch/sparc64/config.in linux/arch/sparc64/config.in
--- linux-2.4.3-pre1/arch/sparc64/config.in	Fri Feb 16 16:02:35 2001
+++ linux/arch/sparc64/config.in	Mon Dec  3 22:29:50 2001
@@ -165,12 +165,14 @@
    dep_tristate 'PTI Qlogic, ISP Driver' CONFIG_SCSI_QLOGICPTI $CONFIG_SCSI
 
    if [ "$CONFIG_PCI" != "n" ]; then
-      dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
-      if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
-	 bool '  Enable tagged command queueing (TCQ) by default' CONFIG_AIC7XXX_TAGGED_QUEUEING
-	 int  '  Maximum number of TCQ commands per device' CONFIG_AIC7XXX_CMDS_PER_DEVICE 8
-  	 bool '  Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS
-	 int  '  Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5
+      source drivers/scsi/aic7xxx/Config.in
+      if [ "$CONFIG_SCSI_AIC7XXX" != "y" ]; then
+         dep_tristate 'Old Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX_OLD $CONFIG_SCSI
+         if [ "$CONFIG_SCSI_AIC7XXX_OLD" != "n" ]; then
+            bool '  Enable Tagged Command Queueing (TCQ) by default' CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT
+            int  '  Maximum number of TCQ commands per device' CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE 8
+            bool '  Collect statistics to report in /proc' CONFIG_AIC7XXX_OLD_PROC_STATS
+         fi
       fi
       dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI
       dep_tristate 'SYM53C8XX SCSI support' CONFIG_SCSI_SYM53C8XX $CONFIG_SCSI
diff -urN linux-2.4.3-pre1/arch/sparc64/defconfig linux/arch/sparc64/defconfig
--- linux-2.4.3-pre1/arch/sparc64/defconfig	Mon Jan 29 08:08:46 2001
+++ linux/arch/sparc64/defconfig	Mon Dec  3 22:29:50 2001
@@ -305,10 +305,12 @@
 CONFIG_SCSI_SUNESP=y
 CONFIG_SCSI_QLOGICPTI=m
 CONFIG_SCSI_AIC7XXX=m
-CONFIG_AIC7XXX_TAGGED_QUEUEING=y
-CONFIG_AIC7XXX_CMDS_PER_DEVICE=8
-CONFIG_AIC7XXX_PROC_STATS=y
-CONFIG_AIC7XXX_RESET_DELAY=5
+CONFIG_AIC7XXX_CMDS_PER_DEVICE=253
+CONFIG_AIC7XXX_RESET_DEALY=5000
+CONFIG_SCSI_AIC7XXX_OLD=m
+CONFIG_AIC7XXX_OLD_TAGGED_QUEUEING=y
+CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE=8
+CONFIG_AIC7XXX_OLD_PROC_STATS=y
 CONFIG_SCSI_NCR53C8XX=m
 CONFIG_SCSI_SYM53C8XX=y
 CONFIG_SCSI_NCR53C8XX_DEFAULT_TAGS=4
diff -urN linux-2.4.3-pre1/drivers/acorn/char/i2c.c linux/drivers/acorn/char/i2c.c
--- linux-2.4.3-pre1/drivers/acorn/char/i2c.c	Mon Sep 18 15:15:21 2000
+++ linux/drivers/acorn/char/i2c.c	Mon Dec  3 22:29:50 2001
@@ -122,43 +122,62 @@
 #define SCL		0x02
 #define SDA		0x01
 
-static int ioc_control;
+/*
+ * We must preserve all non-i2c output bits in IOC_CONTROL.
+ * Note also that we need to preserve the value of SCL and
+ * SDA outputs as well (which may be different from the
+ * values read back from IOC_CONTROL).
+ */
+static u_int force_ones;
 
 static void ioc_setscl(void *data, int state)
 {
+	u_int ioc_control = ioc_readb(IOC_CONTROL) & ~(SCL | SDA);
+	u_int ones = force_ones;
+
 	if (state)
-		ioc_control |= SCL;
+		ones |= SCL;
 	else
-		ioc_control &= ~SCL;
- 	outb(ioc_control, IOC_CONTROL);
+		ones &= ~SCL;
+
+	force_ones = ones;
+
+ 	ioc_writeb(ioc_control | ones, IOC_CONTROL);
 }
 
 static void ioc_setsda(void *data, int state)
 {
+	u_int ioc_control = ioc_readb(IOC_CONTROL) & ~(SCL | SDA);
+	u_int ones = force_ones;
+
 	if (state)
-		ioc_control |= SDA;
+		ones |= SDA;
 	else
-		ioc_control &= ~SDA;
- 	outb(ioc_control, IOC_CONTROL);
+		ones &= ~SDA;
+
+	force_ones = ones;
+
+ 	ioc_writeb(ioc_control | ones, IOC_CONTROL);
 }
 
 static int ioc_getscl(void *data)
 {
-	return (inb(IOC_CONTROL) & SCL) != 0;
+	return (ioc_readb(IOC_CONTROL) & SCL) != 0;
 }
 
 static int ioc_getsda(void *data)
 {
-	return (inb(IOC_CONTROL) & SDA) != 0;
+	return (ioc_readb(IOC_CONTROL) & SDA) != 0;
 }
 
 static struct i2c_algo_bit_data ioc_data = {
-	NULL,
-	ioc_setsda,
-	ioc_setscl,
-	ioc_getsda,
-	ioc_getscl,
-	80, 80, 100
+	setsda:		ioc_setsda,
+	setscl:		ioc_setscl,
+	getsda:		ioc_getsda,
+	getscl:		ioc_getscl,
+	udelay:		 80,
+	mdelay:		 80,
+	timeout:	100
 };
 
 static int ioc_client_reg(struct i2c_client *client)
@@ -184,22 +203,16 @@
 }
 
 static struct i2c_adapter ioc_ops = {
-	"IOC/IOMD",
-	I2C_HW_B_IOC,
-	NULL,
-	&ioc_data,
-	NULL,
-	NULL,
-	ioc_client_reg,
-	ioc_client_unreg
+	name:			"IOC/IOMD",
+	id:			I2C_HW_B_IOC,
+	algo_data:		&ioc_data,
+	client_register:	ioc_client_reg,
+	client_unregister:	ioc_client_unreg
 };
 
 static int __init i2c_ioc_init(void)
 {
-	ioc_control = inb(IOC_CONTROL) | FORCE_ONES;
-
-	ioc_setscl(NULL, 1);
-	ioc_setsda(NULL, 1);
+	force_ones = FORCE_ONES | SCL | SDA;
 
 	return i2c_bit_add_bus(&ioc_ops);
 }
diff -urN linux-2.4.3-pre1/drivers/acorn/char/pcf8583.c linux/drivers/acorn/char/pcf8583.c
--- linux-2.4.3-pre1/drivers/acorn/char/pcf8583.c	Fri Feb  9 11:30:22 2001
+++ linux/drivers/acorn/char/pcf8583.c	Mon Dec  3 22:29:50 2001
@@ -20,16 +20,16 @@
 static struct i2c_driver pcf8583_driver;
 
 static unsigned short ignore[] = { I2C_CLIENT_END };
-static unsigned short normal_addr[] = { 0x50, 0x51, I2C_CLIENT_END };
+static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END };
 
 static struct i2c_client_address_data addr_data = {
-	force:			ignore,
+	normal_i2c:		normal_addr,
+	normal_i2c_range:	ignore,
+	probe:			ignore,
+	probe_range:		ignore,
 	ignore:			ignore,
 	ignore_range:		ignore,
-	normal_i2c:		ignore,
-	normal_i2c_range:	normal_addr,
-	probe:			ignore,
-	probe_range:		ignore
+	force:			ignore,
 };
 
 #define DAT(x) ((unsigned int)(x->data))
@@ -86,7 +86,10 @@
 	};
 	int ret = -EIO;
 
-	if (i2c_transfer(client->adapter, msgs, 2) == 2) {
+	memset(buf, 0, sizeof(buf));
+
+	ret = i2c_transfer(client->adapter, msgs, 2);
+	if (ret == 2) {
 		dt->year_off = buf[4] >> 6;
 		dt->wday     = buf[5] >> 5;
 
@@ -126,6 +129,8 @@
 	}
 
 	ret = i2c_master_send(client, (char *)buf, len);
+	if (ret == len)
+		ret = 0;
 
 	buf[1] = DAT(client);
 	i2c_master_send(client, (char *)buf, 2);
@@ -219,12 +224,12 @@
 }
 
 static struct i2c_driver pcf8583_driver = {
-	"PCF8583",
-	I2C_DRIVERID_PCF8583,
-	I2C_DF_NOTIFY,
-	pcf8583_probe,
-	pcf8583_detach,
-	pcf8583_command
+	name:		"PCF8583",
+	id:		I2C_DRIVERID_PCF8583,
+	flags:		I2C_DF_NOTIFY,
+	attach_adapter:	pcf8583_probe,
+	detach_client:	pcf8583_detach,
+	command:	pcf8583_command
 };
 
 static __init int pcf8583_init(void)
diff -urN linux-2.4.3-pre1/drivers/acorn/scsi/acornscsi.c linux/drivers/acorn/scsi/acornscsi.c
--- linux-2.4.3-pre1/drivers/acorn/scsi/acornscsi.c	Mon Sep 18 15:15:22 2000
+++ linux/drivers/acorn/scsi/acornscsi.c	Mon Dec  3 22:29:50 2001
@@ -1248,7 +1248,7 @@
 
 /*
  * Function: void acornscsi_dma_adjust(AS_Host *host)
- * Purpose : adjust DMA pointers & count for bytes transfered to
+ * Purpose : adjust DMA pointers & count for bytes transferred to
  *	     SBIC but not SCSI bus.
  * Params  : host - host to adjust DMA count for
  */
diff -urN linux-2.4.3-pre1/drivers/acorn/scsi/cumana_2.c linux/drivers/acorn/scsi/cumana_2.c
--- linux-2.4.3-pre1/drivers/acorn/scsi/cumana_2.c	Thu Feb  8 16:32:44 2001
+++ linux/drivers/acorn/scsi/cumana_2.c	Mon Dec  3 22:29:50 2001
@@ -90,7 +90,7 @@
 /*
  * Use term=0,1,0,0,0 to turn terminators on/off
  */
-int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
 
 #define NR_SG	256
 
diff -urN linux-2.4.3-pre1/drivers/acorn/scsi/eesox.c linux/drivers/acorn/scsi/eesox.c
--- linux-2.4.3-pre1/drivers/acorn/scsi/eesox.c	Thu Feb  8 16:32:44 2001
+++ linux/drivers/acorn/scsi/eesox.c	Mon Dec  3 22:29:50 2001
@@ -88,7 +88,7 @@
 /*
  * Use term=0,1,0,0,0 to turn terminators on/off
  */
-int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
 
 #define NR_SG	256
 
diff -urN linux-2.4.3-pre1/drivers/acorn/scsi/powertec.c linux/drivers/acorn/scsi/powertec.c
--- linux-2.4.3-pre1/drivers/acorn/scsi/powertec.c	Thu Feb  8 16:32:44 2001
+++ linux/drivers/acorn/scsi/powertec.c	Mon Dec  3 22:29:50 2001
@@ -87,7 +87,7 @@
 /*
  * Use term=0,1,0,0,0 to turn terminators on/off
  */
-int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
+static int term[MAX_ECARDS] = { 1, 1, 1, 1, 1, 1, 1, 1 };
 
 #define NR_SG	256
 
diff -urN linux-2.4.3-pre1/drivers/acpi/Makefile linux/drivers/acpi/Makefile
--- linux-2.4.3-pre1/drivers/acpi/Makefile	Mon Jan 22 13:23:42 2001
+++ linux/drivers/acpi/Makefile	Mon Dec  3 22:29:50 2001
@@ -20,14 +20,6 @@
 
 EXTRA_CFLAGS += $(ACPI_CFLAGS)
 
-# genksyms only reads $(CFLAGS), it should really read $(EXTRA_CFLAGS) as well.
-# Without EXTRA_CFLAGS the gcc pass for genksyms fails, resulting in an empty
-# include/linux/modules/acpi_ksyms.ver.  Changing genkyms to use EXTRA_CFLAGS
-# will hit everything, too risky in 2.4.0-prerelease.  Bandaid by tweaking
-# CFLAGS only for .ver targets.  Review after 2.4.0 release.  KAO
-
-$(MODINCL)/%.ver: CFLAGS := -I./include $(CFLAGS)
-
 acpi-subdirs := common dispatcher events hardware \
 		interpreter namespace parser resources tables
 
diff -urN linux-2.4.3-pre1/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- linux-2.4.3-pre1/drivers/block/ll_rw_blk.c	Thu Feb 15 16:58:34 2001
+++ linux/drivers/block/ll_rw_blk.c	Mon Dec  3 22:29:50 2001
@@ -1239,9 +1239,6 @@
 #ifdef CONFIG_BLK_DEV_RAM
 	rd_init();
 #endif
-#ifdef CONFIG_BLK_DEV_LOOP
-	loop_init();
-#endif
 #ifdef CONFIG_ISP16_CDI
 	isp16_init();
 #endif
diff -urN linux-2.4.3-pre1/drivers/block/loop.c linux/drivers/block/loop.c
--- linux-2.4.3-pre1/drivers/block/loop.c	Fri Feb  9 11:30:22 2001
+++ linux/drivers/block/loop.c	Mon Dec  3 22:29:50 2001
@@ -31,11 +31,14 @@
  * max_loop=<1-255> to the kernel on boot.
  * Erik I. Bolsĝ, <eriki@himolde.no>, Oct 31, 1999
  *
+ * Completely rewrite request handling to be make_request_fn style and
+ * non blocking, pushing work to a helper thread. Lots of fixes from
+ * Al Viro too.
+ * Jens Axboe <axboe@suse.de>, Nov 2000
+ *
  * Still To Fix:
  * - Advisory locking is ignored here. 
  * - Should use an own CAP_* category instead of CAP_SYS_ADMIN 
- * - Should use the underlying filesystems/devices read function if possible
- *   to support read ahead (and for write)
  *
  * WARNING/FIXME:
  * - The block number as IV passing to low level transfer functions is broken:
@@ -48,6 +51,7 @@
  *   number.
  */ 
 
+#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/sched.h>
@@ -56,9 +60,13 @@
 #include <linux/stat.h>
 #include <linux/errno.h>
 #include <linux/major.h>
-
+#include <linux/wait.h>
+#include <linux/blk.h>
 #include <linux/init.h>
 #include <linux/devfs_fs_kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/swap.h>
+#include <linux/slab.h>
 
 #include <asm/uaccess.h>
 
@@ -66,40 +74,28 @@
 
 #define MAJOR_NR LOOP_MAJOR
 
-#define DEVICE_NAME "loop"
-#define DEVICE_REQUEST do_lo_request
-#define DEVICE_NR(device) (MINOR(device))
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-#define DEVICE_NO_RANDOM
-#define TIMEOUT_VALUE (6 * HZ)
-#include <linux/blk.h>
-
-#include <linux/slab.h>
 static int max_loop = 8;
 static struct loop_device *loop_dev;
 static int *loop_sizes;
 static int *loop_blksizes;
 static devfs_handle_t devfs_handle;      /*  For the directory */
 
-#define FALSE 0
-#define TRUE (!FALSE)
-
 /*
  * Transfer functions
  */
 static int transfer_none(struct loop_device *lo, int cmd, char *raw_buf,
-		  char *loop_buf, int size, int real_block)
+			 char *loop_buf, int size, int real_block)
 {
 	if (cmd == READ)
 		memcpy(loop_buf, raw_buf, size);
 	else
 		memcpy(raw_buf, loop_buf, size);
+
 	return 0;
 }
 
 static int transfer_xor(struct loop_device *lo, int cmd, char *raw_buf,
-		 char *loop_buf, int size, int real_block)
+			char *loop_buf, int size, int real_block)
 {
 	char	*in, *out, *key;
 	int	i, keysize;
@@ -111,17 +107,18 @@
 		in = loop_buf;
 		out = raw_buf;
 	}
+
 	key = lo->lo_encrypt_key;
 	keysize = lo->lo_encrypt_key_size;
-	for (i=0; i < size; i++)
+	for (i = 0; i < size; i++)
 		*out++ = *in++ ^ key[(i & 511) % keysize];
 	return 0;
 }
 
 static int none_status(struct loop_device *lo, struct loop_info *info)
 {
-	return 0; 
-} 
+	return 0;
+}
 
 static int xor_status(struct loop_device *lo, struct loop_info *info)
 {
@@ -133,7 +130,7 @@
 struct loop_func_table none_funcs = { 
 	number: LO_CRYPT_NONE,
 	transfer: transfer_none,
-	init: none_status
+	init: none_status,
 }; 	
 
 struct loop_func_table xor_funcs = { 
@@ -150,29 +147,27 @@
 
 #define MAX_DISK_SIZE 1024*1024*1024
 
-static void figure_loop_size(struct loop_device *lo)
+static int compute_loop_size(struct loop_device *lo, struct dentry * lo_dentry, kdev_t lodev)
 {
-	int	size;
-
-	if (S_ISREG(lo->lo_dentry->d_inode->i_mode))
-		size = (lo->lo_dentry->d_inode->i_size - lo->lo_offset) >> BLOCK_SIZE_BITS;
-	else {
-		kdev_t lodev = lo->lo_device;
-		if (blk_size[MAJOR(lodev)])
-			size = blk_size[MAJOR(lodev)][MINOR(lodev)] -
+	if (S_ISREG(lo_dentry->d_inode->i_mode))
+		return (lo_dentry->d_inode->i_size - lo->lo_offset) >> BLOCK_SIZE_BITS;
+	if (blk_size[MAJOR(lodev)])
+		return blk_size[MAJOR(lodev)][MINOR(lodev)] -
                                 (lo->lo_offset >> BLOCK_SIZE_BITS);
-		else
-			size = MAX_DISK_SIZE;
-	}
+	return MAX_DISK_SIZE;
+}
 
-	loop_sizes[lo->lo_number] = size;
+static void figure_loop_size(struct loop_device *lo)
+{
+	loop_sizes[lo->lo_number] = compute_loop_size(lo,
+					lo->lo_backing_file->f_dentry,
+					lo->lo_device);
 }
 
-static int lo_send(struct loop_device *lo, char *data, int len, loff_t pos,
-	int blksize)
+static int lo_send(struct loop_device *lo, char *data, int len, loff_t pos)
 {
 	struct file *file = lo->lo_backing_file; /* kudos to NFsckingS */
-	struct address_space *mapping = lo->lo_dentry->d_inode->i_mapping;
+	struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
 	struct address_space_operations *aops = mapping->a_ops;
 	struct page *page;
 	char *kaddr;
@@ -182,7 +177,8 @@
 	index = pos >> PAGE_CACHE_SHIFT;
 	offset = pos & (PAGE_CACHE_SIZE - 1);
 	while (len > 0) {
-		int IV = index * (PAGE_CACHE_SIZE/blksize) + offset/blksize;
+		int IV = index * (PAGE_CACHE_SIZE/lo->lo_blksize) + offset/lo->lo_blksize;
+		IV >>= 2;
 		size = PAGE_CACHE_SIZE - offset;
 		if (size > len)
 			size = len;
@@ -193,7 +189,8 @@
 		if (aops->prepare_write(file, page, offset, offset+size))
 			goto unlock;
 		kaddr = page_address(page);
-		if ((lo->transfer)(lo, WRITE, kaddr+offset, data, size, IV))
+		flush_dcache_page(page);
+		if (lo_do_transfer(lo, WRITE, kaddr + offset, data, size, IV))
 			goto write_fail;
 		if (aops->commit_write(file, page, offset, offset+size))
 			goto unlock;
@@ -203,6 +200,7 @@
 		index++;
 		pos += size;
 		UnlockPage(page);
+		deactivate_page(page);
 		page_cache_release(page);
 	}
 	return 0;
@@ -213,6 +211,7 @@
 	kunmap(page);
 unlock:
 	UnlockPage(page);
+	deactivate_page(page);
 	page_cache_release(page);
 fail:
 	return -1;
@@ -221,7 +220,6 @@
 struct lo_read_data {
 	struct loop_device *lo;
 	char *data;
-	int blksize;
 };
 
 static int lo_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size)
@@ -230,16 +228,17 @@
 	unsigned long count = desc->count;
 	struct lo_read_data *p = (struct lo_read_data*)desc->buf;
 	struct loop_device *lo = p->lo;
-	int IV = page->index * (PAGE_CACHE_SIZE/p->blksize) + offset/p->blksize;
+	int IV = page->index * (PAGE_CACHE_SIZE/lo->lo_blksize) + offset/lo->lo_blksize;
+
+	IV >>= 2;
 
 	if (size > count)
 		size = count;
 
 	kaddr = kmap(page);
-	if ((lo->transfer)(lo,READ,kaddr+offset,p->data,size,IV)) {
+	if (lo_do_transfer(lo, READ, kaddr + offset, p->data, size, IV)) {
 		size = 0;
-		printk(KERN_ERR "loop: transfer error block %ld\n",
-		       page->index);
+		printk(KERN_ERR "loop: transfer error block %ld\n",page->index);
 		desc->error = -EINVAL;
 	}
 	kunmap(page);
@@ -250,160 +249,314 @@
 	return size;
 }
 
-static int lo_receive(struct loop_device *lo, char *data, int len, loff_t pos,
-	int blksize)
+static int lo_receive(struct loop_device *lo, char *data, int len, loff_t pos)
 {
-	struct file *file = lo->lo_backing_file;
 	struct lo_read_data cookie;
 	read_descriptor_t desc;
+	struct file *file;
 
 	cookie.lo = lo;
 	cookie.data = data;
-	cookie.blksize = blksize;
 	desc.written = 0;
 	desc.count = len;
 	desc.buf = (char*)&cookie;
 	desc.error = 0;
+	spin_lock_irq(&lo->lo_lock);
+	file = lo->lo_backing_file;
+	spin_unlock_irq(&lo->lo_lock);
 	do_generic_file_read(file, &pos, &desc, lo_read_actor);
 	return desc.error;
 }
 
-static void do_lo_request(request_queue_t * q)
+static int do_bh_filebacked(struct loop_device *lo, struct buffer_head *bh, int rw)
 {
-	int	block, offset, len, blksize, size;
-	char	*dest_addr;
-	struct loop_device *lo;
-	struct buffer_head *bh;
-	struct request *current_request;
 	loff_t pos;
+	int ret;
+
+	pos = ((loff_t) bh->b_rsector << 9) + lo->lo_offset;
+
+	if (rw == WRITE)
+		ret = lo_send(lo, bh->b_data, bh->b_size, pos);
+	else
+		ret = lo_receive(lo, bh->b_data, bh->b_size, pos);
+
+	return ret;
+}
+
+static void loop_put_buffer(struct buffer_head *bh)
+{
+	if (bh) {
+		kunmap(bh->b_page);
+		__free_page(bh->b_page);
+		kmem_cache_free(bh_cachep, bh);
+	}
+}
+
+/*
+ * Add buffer_head to back of pending list
+ */
+static void loop_add_bh(struct loop_device *lo, struct buffer_head *bh)
+{
+	unsigned long flags;
 
-repeat:
-	INIT_REQUEST;
-	current_request=CURRENT;
-	blkdev_dequeue_request(current_request);
-	if (MINOR(current_request->rq_dev) >= max_loop)
-		goto error_out;
-	lo = &loop_dev[MINOR(current_request->rq_dev)];
-	if (!lo->lo_dentry || !lo->transfer)
-		goto error_out;
-	if (current_request->cmd == WRITE) {
+	spin_lock_irqsave(&lo->lo_lock, flags);
+	if (lo->lo_bhtail) {
+		lo->lo_bhtail->b_reqnext = bh;
+		lo->lo_bhtail = bh;
+	} else
+		lo->lo_bh = lo->lo_bhtail = bh;
+	spin_unlock_irqrestore(&lo->lo_lock, flags);
+
+	atomic_inc(&lo->lo_pending);
+	up(&lo->lo_bh_mutex);
+}
+
+/*
+ * Grab first pending buffer
+ */
+static struct buffer_head *loop_get_bh(struct loop_device *lo)
+{
+	struct buffer_head *bh;
+
+	spin_lock_irq(&lo->lo_lock);
+	if ((bh = lo->lo_bh)) {
+		if (bh == lo->lo_bhtail)
+			lo->lo_bhtail = NULL;
+		lo->lo_bh = bh->b_reqnext;
+		bh->b_reqnext = NULL;
+	}
+	spin_unlock_irq(&lo->lo_lock);
+
+	return bh;
+}
+
+/*
+ * when buffer i/o has completed. if BH_Dirty is set, this was a WRITE
+ * and lo->transfer stuff has already been done. if not, it was a READ
+ * so queue it for the loop thread and let it do the transfer out of
+ * b_end_io context (we don't want to do decrypt of a page with irqs
+ * disabled)
+ */
+static void loop_end_io_transfer(struct buffer_head *bh, int uptodate)
+{
+	struct loop_device *lo = &loop_dev[MINOR(bh->b_dev)];
+
+	if (!uptodate || test_bit(BH_Dirty, &bh->b_state)) {
+		struct buffer_head *rbh = bh->b_private;
+
+		rbh->b_end_io(rbh, uptodate);
+		if (atomic_dec_and_test(&lo->lo_pending))
+			up(&lo->lo_bh_mutex);
+		loop_put_buffer(bh);
+	} else
+		loop_add_bh(lo, bh);
+}
+
+static struct buffer_head *loop_get_buffer(struct loop_device *lo,
+					   struct buffer_head *rbh)
+{
+	struct buffer_head *bh;
+
+	do {
+		bh = kmem_cache_alloc(bh_cachep, SLAB_BUFFER);
+		if (bh)
+			break;
+
+		run_task_queue(&tq_disk);
+		schedule_timeout(HZ);
+	} while (1);
+	memset(bh, 0, sizeof(*bh));
+
+	bh->b_size = rbh->b_size;
+	bh->b_dev = rbh->b_dev;
+	spin_lock_irq(&lo->lo_lock);
+	bh->b_rdev = lo->lo_device;
+	spin_unlock_irq(&lo->lo_lock);
+	bh->b_state = (1 << BH_Req) | (1 << BH_Mapped) | (1 << BH_Lock);
+
+	/*
+	 * easy way out, although it does waste some memory for < PAGE_SIZE
+	 * blocks... if highmem bounce buffering can get away with it,
+	 * so can we :-)
+	 */
+	bh->b_page = alloc_page(GFP_BUFFER);
+	bh->b_data = kmap(bh->b_page);
+
+	bh->b_end_io = loop_end_io_transfer;
+	bh->b_rsector = rbh->b_rsector + (lo->lo_offset >> 9);
+	init_waitqueue_head(&bh->b_wait);
+
+	return bh;
+}
+
+static int loop_make_request(request_queue_t *q, int rw, struct buffer_head *rbh)
+{
+	struct buffer_head *bh = NULL;
+	struct loop_device *lo;
+	unsigned long IV;
+
+	if (!buffer_locked(rbh))
+		BUG();
+
+	if (MINOR(rbh->b_rdev) >= max_loop)
+		goto out;
+
+	lo = &loop_dev[MINOR(rbh->b_rdev)];
+	spin_lock_irq(&lo->lo_lock);
+	if (lo->lo_state != Lo_bound)
+		goto inactive;
+	spin_unlock_irq(&lo->lo_lock);
+
+	if (rw == WRITE) {
 		if (lo->lo_flags & LO_FLAGS_READ_ONLY)
-			goto error_out;
-	} else if (current_request->cmd != READ) {
-		printk(KERN_ERR "unknown loop device command (%d)?!?",
-		       current_request->cmd);
-		goto error_out;
+			goto err;
+	} else if (rw == READA) {
+		rw = READ;
+	} else if (rw != READ) {
+		printk(KERN_ERR "loop: unknown command (%d)\n", rw);
+		goto err;
 	}
 
-	dest_addr = current_request->buffer;
-	len = current_request->current_nr_sectors << 9;
+#if CONFIG_HIGHMEM
+	rbh = create_bounce(rw, rbh);
+#endif
+
+	if (lo->lo_blksize != rbh->b_size)
+		lo->lo_blksize = rbh->b_size;
+
+	/*
+	 * file backed, queue for loop_thread to handle
+	 */
+	if (lo->lo_flags & LO_FLAGS_DO_BMAP) {
+		if (rw == WRITE)
+			set_bit(BH_Dirty, &rbh->b_state);
+		loop_add_bh(lo, rbh);
+		return 0;
+	}
 
-	blksize = BLOCK_SIZE;
-	if (blksize_size[MAJOR(lo->lo_device)]) {
-	    blksize = blksize_size[MAJOR(lo->lo_device)][MINOR(lo->lo_device)];
-	    if (!blksize)
-	      blksize = BLOCK_SIZE;
+	/*
+	 * piggy old buffer on original, and submit for I/O
+	 */
+	bh = loop_get_buffer(lo, rbh);
+	bh->b_private = rbh;
+	IV = (bh->b_rsector / (lo->lo_blksize >> 9));
+	IV += lo->lo_offset / lo->lo_blksize;
+	IV >>= 2;
+	if (rw == WRITE) {
+		set_bit(BH_Dirty, &bh->b_state);
+		if (lo_do_transfer(lo, WRITE, bh->b_data, rbh->b_data, bh->b_size, IV))
+			goto err;
 	}
 
-	if (lo->lo_flags & LO_FLAGS_DO_BMAP)
-		goto file_backed;
+	generic_make_request(rw, bh);
+	return 0;
 
-	if (blksize < 512) {
-		block = current_request->sector * (512/blksize);
-		offset = 0;
+err:
+	loop_put_buffer(bh);
+out:
+	buffer_IO_error(rbh);
+	return 0;
+inactive:
+	spin_unlock_irq(&lo->lo_lock);
+	goto out;
+}
+
+static inline void loop_handle_bh(struct loop_device *lo,struct buffer_head *bh)
+{
+	int ret;
+
+	/*
+	 * For block backed loop, we know this is a READ
+	 */
+	if (lo->lo_flags & LO_FLAGS_DO_BMAP) {
+		int rw = !!test_and_clear_bit(BH_Dirty, &bh->b_state);
+
+		ret = do_bh_filebacked(lo, bh, rw);
+		bh->b_end_io(bh, !ret);
 	} else {
-		block = current_request->sector / (blksize >> 9);
-		offset = (current_request->sector % (blksize >> 9)) << 9;
-	}
-	block += lo->lo_offset / blksize;
-	offset += lo->lo_offset % blksize;
-	if (offset >= blksize) {
-		block++;
-		offset -= blksize;
+		struct buffer_head *rbh = bh->b_private;
+		unsigned long IV;
+
+		IV = (bh->b_rsector / (bh->b_size >> 9));
+		IV += lo->lo_offset / bh->b_size;
+		IV >>= 2;
+
+		ret = lo_do_transfer(lo, READ, bh->b_data, rbh->b_data,
+				     bh->b_size, IV);
+
+		rbh->b_end_io(rbh, !ret);
+		loop_put_buffer(bh);
 	}
-	spin_unlock_irq(&io_request_lock);
+}
 
-	while (len > 0) {
+/*
+ * worker thread that handles reads/writes to file backed loop devices,
+ * to avoid blocking in our make_request_fn. it also does loop decrypting
+ * on reads for block backed loop, as that is too heavy to do from
+ * b_end_io context where irqs may be disabled.
+ */
+static int loop_thread(void *data)
+{
+	struct loop_device *lo = data;
+	struct buffer_head *bh;
 
-		size = blksize - offset;
-		if (size > len)
-			size = len;
+	daemonize();
+	exit_files(current);
 
-		bh = getblk(lo->lo_device, block, blksize);
-		if (!bh) {
-			printk(KERN_ERR "loop: device %s: getblk(-, %d, %d) returned NULL",
-				kdevname(lo->lo_device),
-				block, blksize);
-			goto error_out_lock;
-		}
-		if (!buffer_uptodate(bh) && ((current_request->cmd == READ) ||
-					(offset || (len < blksize)))) {
-			ll_rw_block(READ, 1, &bh);
-			wait_on_buffer(bh);
-			if (!buffer_uptodate(bh)) {
-				brelse(bh);
-				goto error_out_lock;
-			}
-		}
+	sprintf(current->comm, "loop%d", lo->lo_number);
 
-		if ((lo->transfer)(lo, current_request->cmd,
-				   bh->b_data + offset,
-				   dest_addr, size, block)) {
-			printk(KERN_ERR "loop: transfer error block %d\n",
-			       block);
-			brelse(bh);
-			goto error_out_lock;
-		}
+	spin_lock_irq(&current->sigmask_lock);
+	sigfillset(&current->blocked);
+	flush_signals(current);
+	spin_unlock_irq(&current->sigmask_lock);
+
+	current->policy = SCHED_OTHER;
+	current->nice = -20;
+
+	spin_lock_irq(&lo->lo_lock);
+	lo->lo_state = Lo_bound;
+	atomic_inc(&lo->lo_pending);
+	spin_unlock_irq(&lo->lo_lock);
+
+	/*
+	 * up sem, we are running
+	 */
+	up(&lo->lo_sem);
+
+	for (;;) {
+		down_interruptible(&lo->lo_bh_mutex);
+		if (!atomic_read(&lo->lo_pending))
+			break;
 
-		if (current_request->cmd == WRITE) {
-			mark_buffer_uptodate(bh, 1);
-			mark_buffer_dirty(bh);
+		bh = loop_get_bh(lo);
+		atomic_dec(&lo->lo_pending);
+		if (!bh) {
+			printk("missing bh\n");
+			continue;
 		}
-		brelse(bh);
-		dest_addr += size;
-		len -= size;
-		offset = 0;
-		block++;
+		loop_handle_bh(lo, bh);
 	}
-	goto done;
 
-file_backed:
-	pos = ((loff_t)current_request->sector << 9) + lo->lo_offset;
-	spin_unlock_irq(&io_request_lock);
-	if (current_request->cmd == WRITE) {
-		if (lo_send(lo, dest_addr, len, pos, blksize))
-			goto error_out_lock;
-	} else {
-		if (lo_receive(lo, dest_addr, len, pos, blksize))
-			goto error_out_lock;
-	}
-done:
-	spin_lock_irq(&io_request_lock);
-	current_request->sector += current_request->current_nr_sectors;
-	current_request->nr_sectors -= current_request->current_nr_sectors;
-	list_add(&current_request->queue, &q->queue_head);
-	end_request(1);
-	goto repeat;
-error_out_lock:
-	spin_lock_irq(&io_request_lock);
-error_out:
-	list_add(&current_request->queue, &q->queue_head);
-	end_request(0);
-	goto repeat;
+	up(&lo->lo_sem);
+	return 0;
 }
 
-static int loop_set_fd(struct loop_device *lo, kdev_t dev, unsigned int arg)
+static int loop_set_fd(struct loop_device *lo, struct file *lo_file, kdev_t dev,
+		       unsigned int arg)
 {
 	struct file	*file;
 	struct inode	*inode;
-	int error;
+	kdev_t		lo_device;
+	int		lo_flags = 0;
+	int		error;
+	int		bs;
 
 	MOD_INC_USE_COUNT;
 
 	error = -EBUSY;
-	if (lo->lo_dentry)
+	if (lo->lo_state != Lo_unbound)
 		goto out;
-
+	 
 	error = -EBADF;
 	file = fget(arg);
 	if (!file)
@@ -412,24 +565,13 @@
 	error = -EINVAL;
 	inode = file->f_dentry->d_inode;
 
-	if (S_ISBLK(inode->i_mode)) {
-		/* dentry will be wired, so... */
-		error = blkdev_get(inode->i_bdev, file->f_mode,
-				   file->f_flags, BDEV_FILE);
-
-		lo->lo_device = inode->i_rdev;
-		lo->lo_flags = 0;
-
-		/* Backed by a block device - don't need to hold onto
-		   a file structure */
-		lo->lo_backing_file = NULL;
+	if (!(file->f_mode & FMODE_WRITE))
+		lo_flags |= LO_FLAGS_READ_ONLY;
 
-		if (error)
-			goto out_putf;
+	if (S_ISBLK(inode->i_mode)) {
+		lo_device = inode->i_rdev;
 	} else if (S_ISREG(inode->i_mode)) {
-		struct address_space_operations *aops;
-
-		aops = inode->i_mapping->a_ops;
+		struct address_space_operations *aops = inode->i_mapping->a_ops;
 		/*
 		 * If we can't read - sorry. If we only can't write - well,
 		 * it's going to be read-only.
@@ -439,57 +581,51 @@
 			goto out_putf;
 
 		if (!aops->prepare_write || !aops->commit_write)
-			lo->lo_flags |= LO_FLAGS_READ_ONLY;
-
-		error = get_write_access(inode);
-		if (error)
-			goto out_putf;
-
-		/* Backed by a regular file - we need to hold onto a file
-		   structure for this file.  Friggin' NFS can't live without
-		   it on write and for reading we use do_generic_file_read(),
-		   so...  We create a new file structure based on the one
-		   passed to us via 'arg'.  This is to avoid changing the file
-		   structure that the caller is using */
-
-		lo->lo_device = inode->i_dev;
-		lo->lo_flags |= LO_FLAGS_DO_BMAP;
-
-		error = -ENFILE;
-		lo->lo_backing_file = get_empty_filp();
-		if (lo->lo_backing_file == NULL) {
-			put_write_access(inode);
-			goto out_putf;
-		}
-
-		lo->lo_backing_file->f_mode = file->f_mode;
-		lo->lo_backing_file->f_pos = file->f_pos;
-		lo->lo_backing_file->f_flags = file->f_flags;
-		lo->lo_backing_file->f_owner = file->f_owner;
-		lo->lo_backing_file->f_dentry = file->f_dentry;
-		lo->lo_backing_file->f_vfsmnt = mntget(file->f_vfsmnt);
-		lo->lo_backing_file->f_op = fops_get(file->f_op);
-		lo->lo_backing_file->private_data = file->private_data;
-		file_moveto(lo->lo_backing_file, file);
+			lo_flags |= LO_FLAGS_READ_ONLY;
 
+		lo_device = inode->i_dev;
+		lo_flags |= LO_FLAGS_DO_BMAP;
 		error = 0;
-	}
+	} else
+		goto out_putf;
 
-	if (IS_RDONLY (inode) || is_read_only(lo->lo_device))
-		lo->lo_flags |= LO_FLAGS_READ_ONLY;
+	get_file(file);
 
-	set_device_ro(dev, (lo->lo_flags & LO_FLAGS_READ_ONLY)!=0);
+	if (IS_RDONLY (inode) || is_read_only(lo_device)
+	    || !(lo_file->f_mode & FMODE_WRITE))
+		lo_flags |= LO_FLAGS_READ_ONLY;
 
-	lo->lo_dentry = dget(file->f_dentry);
+	set_device_ro(dev, (lo_flags & LO_FLAGS_READ_ONLY)!=0);
+
+	lo->lo_device = lo_device;
+	lo->lo_flags = lo_flags;
+	lo->lo_backing_file = file;
 	lo->transfer = NULL;
 	lo->ioctl = NULL;
 	figure_loop_size(lo);
+	lo->old_gfp_mask = inode->i_mapping->gfp_mask;
+	inode->i_mapping->gfp_mask = GFP_BUFFER;
+
+	bs = 0;
+	if (blksize_size[MAJOR(inode->i_rdev)])
+		bs = blksize_size[MAJOR(inode->i_rdev)][MINOR(inode->i_rdev)];
+	if (!bs)
+		bs = BLOCK_SIZE;
+
+	lo->lo_blksize = bs;
+	set_blocksize(dev, bs);
+
+	lo->lo_bh = lo->lo_bhtail = NULL;
+	kernel_thread(loop_thread, lo, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	down(&lo->lo_sem);
+
+	fput(file);
+	return 0;
 
  out_putf:
 	fput(file);
  out:
-	if (error)
-		MOD_DEC_USE_COUNT;
+	MOD_DEC_USE_COUNT;
 	return error;
 }
 
@@ -525,27 +661,25 @@
 
 static int loop_clr_fd(struct loop_device *lo, kdev_t dev)
 {
-	struct dentry *dentry = lo->lo_dentry;
+	struct file *filp = lo->lo_backing_file;
+	int gfp = lo->old_gfp_mask;
 
-	if (!dentry)
+	if (lo->lo_state != Lo_bound)
 		return -ENXIO;
 	if (lo->lo_refcnt > 1)	/* we needed one fd for the ioctl */
 		return -EBUSY;
+	if (filp==NULL)
+		return -EINVAL;
 
-	if (S_ISBLK(dentry->d_inode->i_mode))
-		blkdev_put(dentry->d_inode->i_bdev, BDEV_FILE);
+	spin_lock_irq(&lo->lo_lock);
+	lo->lo_state = Lo_rundown;
+	if (atomic_dec_and_test(&lo->lo_pending))
+		up(&lo->lo_bh_mutex);
+	spin_unlock_irq(&lo->lo_lock);
 
-	lo->lo_dentry = NULL;
+	down(&lo->lo_sem);
 
-	if (lo->lo_backing_file != NULL) {
-		struct file *filp = lo->lo_backing_file;
-		if ((filp->f_mode & FMODE_WRITE) == 0)
-			put_write_access(filp->f_dentry->d_inode);
-		fput(filp);
-		lo->lo_backing_file = NULL;
-	} else {
-		dput(dentry);
-	}
+	lo->lo_backing_file = NULL;
 
 	loop_release_xfer(lo);
 	lo->transfer = NULL;
@@ -554,10 +688,14 @@
 	lo->lo_encrypt_type = 0;
 	lo->lo_offset = 0;
 	lo->lo_encrypt_key_size = 0;
+	lo->lo_flags = 0;
 	memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
 	memset(lo->lo_name, 0, LO_NAME_SIZE);
 	loop_sizes[lo->lo_number] = 0;
 	invalidate_buffers(dev);
+	filp->f_dentry->d_inode->i_mapping->gfp_mask = gfp;
+	lo->lo_state = Lo_unbound;
+	fput(filp);
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -571,7 +709,7 @@
 	if (lo->lo_encrypt_key_size && lo->lo_key_owner != current->uid && 
 	    !capable(CAP_SYS_ADMIN))
 		return -EPERM;
-	if (!lo->lo_dentry)
+	if (lo->lo_state != Lo_bound)
 		return -ENXIO;
 	if (copy_from_user(&info, arg, sizeof (struct loop_info)))
 		return -EFAULT; 
@@ -608,15 +746,16 @@
 static int loop_get_status(struct loop_device *lo, struct loop_info *arg)
 {
 	struct loop_info	info;
+	struct file *file = lo->lo_backing_file;
 
-	if (!lo->lo_dentry)
+	if (lo->lo_state != Lo_bound)
 		return -ENXIO;
 	if (!arg)
 		return -EINVAL;
 	memset(&info, 0, sizeof(info));
 	info.lo_number = lo->lo_number;
-	info.lo_device = kdev_t_to_nr(lo->lo_dentry->d_inode->i_dev);
-	info.lo_inode = lo->lo_dentry->d_inode->i_ino;
+	info.lo_device = kdev_t_to_nr(file->f_dentry->d_inode->i_dev);
+	info.lo_inode = file->f_dentry->d_inode->i_ino;
 	info.lo_rdevice = kdev_t_to_nr(lo->lo_device);
 	info.lo_offset = lo->lo_offset;
 	info.lo_flags = lo->lo_flags;
@@ -634,7 +773,7 @@
 	unsigned int cmd, unsigned long arg)
 {
 	struct loop_device *lo;
-	int dev;
+	int dev, err;
 
 	if (!inode)
 		return -EINVAL;
@@ -647,25 +786,36 @@
 	if (dev >= max_loop)
 		return -ENODEV;
 	lo = &loop_dev[dev];
+	down(&lo->lo_ctl_mutex);
 	switch (cmd) {
 	case LOOP_SET_FD:
-		return loop_set_fd(lo, inode->i_rdev, arg);
+		err = loop_set_fd(lo, file, inode->i_rdev, arg);
+		break;
 	case LOOP_CLR_FD:
-		return loop_clr_fd(lo, inode->i_rdev);
+		err = loop_clr_fd(lo, inode->i_rdev);
+		break;
 	case LOOP_SET_STATUS:
-		return loop_set_status(lo, (struct loop_info *) arg);
+		err = loop_set_status(lo, (struct loop_info *) arg);
+		break;
 	case LOOP_GET_STATUS:
-		return loop_get_status(lo, (struct loop_info *) arg);
-	case BLKGETSIZE:   /* Return device size */
-		if (!lo->lo_dentry)
-			return -ENXIO;
-		if (!arg)
-			return -EINVAL;
-		return put_user(loop_sizes[lo->lo_number] << 1, (long *) arg);
+		err = loop_get_status(lo, (struct loop_info *) arg);
+		break;
+	case BLKGETSIZE:
+		if (lo->lo_state != Lo_bound) {
+			err = -ENXIO;
+			break;
+		}
+		if (!arg) {
+			err = -EINVAL;
+			break;
+		}
+		err = put_user(loop_sizes[lo->lo_number] << 1, (long *) arg);
+		break;
 	default:
-		return lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
+		err = lo->ioctl ? lo->ioctl(lo, cmd, arg) : -EINVAL;
 	}
-	return 0;
+	up(&lo->lo_ctl_mutex);
+	return err;
 }
 
 static int lo_open(struct inode *inode, struct file *file)
@@ -673,7 +823,6 @@
 	struct loop_device *lo;
 	int	dev, type;
 
-
 	if (!inode)
 		return -EINVAL;
 	if (MAJOR(inode->i_rdev) != MAJOR_NR) {
@@ -681,23 +830,25 @@
 		return -ENODEV;
 	}
 	dev = MINOR(inode->i_rdev);
-	if (dev >= max_loop) {
+	if (dev >= max_loop)
 		return -ENODEV;
-	}
+
 	lo = &loop_dev[dev];
+	MOD_INC_USE_COUNT;
+	down(&lo->lo_ctl_mutex);
 
 	type = lo->lo_encrypt_type; 
 	if (type && xfer_funcs[type] && xfer_funcs[type]->lock)
 		xfer_funcs[type]->lock(lo);
 	lo->lo_refcnt++;
-	MOD_INC_USE_COUNT;
+	up(&lo->lo_ctl_mutex);
 	return 0;
 }
 
 static int lo_release(struct inode *inode, struct file *file)
 {
 	struct loop_device *lo;
-	int	dev;
+	int	dev, type;
 
 	if (!inode)
 		return 0;
@@ -709,17 +860,16 @@
 	dev = MINOR(inode->i_rdev);
 	if (dev >= max_loop)
 		return 0;
+
 	lo = &loop_dev[dev];
-	if (lo->lo_refcnt <= 0)
-		printk(KERN_ERR "lo_release: refcount(%d) <= 0\n",
-		       lo->lo_refcnt);
-	else  {
-		int type  = lo->lo_encrypt_type;
-		--lo->lo_refcnt;
-		if (xfer_funcs[type] && xfer_funcs[type]->unlock)
-			xfer_funcs[type]->unlock(lo);
-		MOD_DEC_USE_COUNT;
-	}
+	down(&lo->lo_ctl_mutex);
+	type = lo->lo_encrypt_type;
+	--lo->lo_refcnt;
+	if (xfer_funcs[type] && xfer_funcs[type]->unlock)
+		xfer_funcs[type]->unlock(lo);
+
+	up(&lo->lo_ctl_mutex);
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -732,11 +882,8 @@
 /*
  * And now the modules code and kernel interface.
  */
-#ifdef MODULE
-#define loop_init init_module
 MODULE_PARM(max_loop, "i");
 MODULE_PARM_DESC(max_loop, "Maximum number of loop devices (1-255)");
-#endif
 
 int loop_register_transfer(struct loop_func_table *funcs)
 {
@@ -767,88 +914,88 @@
 EXPORT_SYMBOL(loop_register_transfer);
 EXPORT_SYMBOL(loop_unregister_transfer);
 
-static void no_plug_device(request_queue_t *q, kdev_t device)
-{
-}
-
 int __init loop_init(void) 
 {
 	int	i;
 
-	if (devfs_register_blkdev(MAJOR_NR, "loop", &lo_fops)) {
-		printk(KERN_WARNING "Unable to get major number %d for loop device\n",
-		       MAJOR_NR);
-		return -EIO;
-	}
-	devfs_handle = devfs_mk_dir (NULL, "loop", NULL);
-	devfs_register_series (devfs_handle, "%u", max_loop, DEVFS_FL_DEFAULT,
-			       MAJOR_NR, 0,
-			       S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
-			       &lo_fops, NULL);
-
 	if ((max_loop < 1) || (max_loop > 255)) {
-		printk (KERN_WARNING "loop: invalid max_loop (must be between 1 and 255), using default (8)\n");
+		printk(KERN_WARNING "loop: invalid max_loop (must be between"
+				    " 1 and 255), using default (8)\n");
 		max_loop = 8;
 	}
 
-	printk(KERN_INFO "loop: enabling %d loop devices\n", max_loop);
-
-	loop_dev = kmalloc (max_loop * sizeof(struct loop_device), GFP_KERNEL);
-	if (!loop_dev) {
-		printk (KERN_ERR "loop: Unable to create loop_dev\n");
-		return -ENOMEM;
+	if (devfs_register_blkdev(MAJOR_NR, "loop", &lo_fops)) {
+		printk(KERN_WARNING "Unable to get major number %d for loop"
+				    " device\n", MAJOR_NR);
+		return -EIO;
 	}
 
-	loop_sizes = kmalloc(max_loop * sizeof(int), GFP_KERNEL);
-	if (!loop_sizes) {
-		printk (KERN_ERR "loop: Unable to create loop_sizes\n");
-		kfree (loop_dev);
-		return -ENOMEM;
-	}
+	devfs_handle = devfs_mk_dir(NULL, "loop", NULL);
+	devfs_register_series(devfs_handle, "%u", max_loop, DEVFS_FL_DEFAULT,
+			      MAJOR_NR, 0,
+			      S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
+			      &lo_fops, NULL);
 
-	loop_blksizes = kmalloc (max_loop * sizeof(int), GFP_KERNEL);
-	if (!loop_blksizes) {
-		printk (KERN_ERR "loop: Unable to create loop_blksizes\n");
-		kfree (loop_dev);
-		kfree (loop_sizes);
+	loop_dev = kmalloc(max_loop * sizeof(struct loop_device), GFP_KERNEL);
+	if (!loop_dev)
 		return -ENOMEM;
-	}		
 
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
-	blk_queue_pluggable(BLK_DEFAULT_QUEUE(MAJOR_NR), no_plug_device);
-	blk_queue_headactive(BLK_DEFAULT_QUEUE(MAJOR_NR), 0);
-	for (i=0; i < max_loop; i++) {
-		memset(&loop_dev[i], 0, sizeof(struct loop_device));
-		loop_dev[i].lo_number = i;
+	loop_sizes = kmalloc(max_loop * sizeof(int), GFP_KERNEL);
+	if (!loop_sizes)
+		goto out_sizes;
+
+	loop_blksizes = kmalloc(max_loop * sizeof(int), GFP_KERNEL);
+	if (!loop_blksizes)
+		goto out_blksizes;
+
+	blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), loop_make_request);
+
+	for (i = 0; i < max_loop; i++) {
+		struct loop_device *lo = &loop_dev[i];
+		memset(lo, 0, sizeof(struct loop_device));
+		init_MUTEX(&lo->lo_ctl_mutex);
+		init_MUTEX_LOCKED(&lo->lo_sem);
+		init_MUTEX_LOCKED(&lo->lo_bh_mutex);
+		lo->lo_number = i;
+		spin_lock_init(&lo->lo_lock);
 	}
+
 	memset(loop_sizes, 0, max_loop * sizeof(int));
 	memset(loop_blksizes, 0, max_loop * sizeof(int));
 	blk_size[MAJOR_NR] = loop_sizes;
 	blksize_size[MAJOR_NR] = loop_blksizes;
-	for (i=0; i < max_loop; i++)
-		register_disk(NULL, MKDEV(MAJOR_NR,i), 1, &lo_fops, 0);
+	for (i = 0; i < max_loop; i++)
+		register_disk(NULL, MKDEV(MAJOR_NR, i), 1, &lo_fops, 0);
 
+	printk(KERN_INFO "loop: loaded (max %d devices)\n", max_loop);
 	return 0;
+
+out_sizes:
+	kfree(loop_dev);
+out_blksizes:
+	kfree(loop_sizes);
+	printk(KERN_ERR "loop: ran out of memory\n");
+	return -ENOMEM;
 }
 
-#ifdef MODULE
-void cleanup_module(void) 
+void loop_exit(void) 
 {
-	devfs_unregister (devfs_handle);
-	if (devfs_unregister_blkdev(MAJOR_NR, "loop") != 0)
+	devfs_unregister(devfs_handle);
+	if (devfs_unregister_blkdev(MAJOR_NR, "loop"))
 		printk(KERN_WARNING "loop: cannot unregister blkdev\n");
 
-	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-	kfree (loop_dev);
-	kfree (loop_sizes);
-	kfree (loop_blksizes);
+	kfree(loop_dev);
+	kfree(loop_sizes);
+	kfree(loop_blksizes);
 }
-#endif
+
+module_init(loop_init);
+module_exit(loop_exit);
 
 #ifndef MODULE
 static int __init max_loop_setup(char *str)
 {
-	max_loop = simple_strtol(str,NULL,0);
+	max_loop = simple_strtol(str, NULL, 0);
 	return 1;
 }
 
diff -urN linux-2.4.3-pre1/drivers/char/drm/r128_drv.h linux/drivers/char/drm/r128_drv.h
--- linux-2.4.3-pre1/drivers/char/drm/r128_drv.h	Thu Jan  4 13:03:20 2001
+++ linux/drivers/char/drm/r128_drv.h	Mon Dec  3 22:29:50 2001
@@ -447,6 +447,11 @@
 		DRM_INFO( "ADVANCE_RING() tail=0x%06x wr=0x%06x\n",	\
 			  write, dev_priv->ring.tail );			\
 	}								\
+	if ( write < 32 ) {						\
+		memcpy( dev_priv->ring.end,				\
+			dev_priv->ring.start,				\
+			write * sizeof(u32) );				\
+	}								\
 	r128_flush_write_combine();					\
 	dev_priv->ring.tail = write;					\
 	R128_WRITE( R128_PM4_BUFFER_DL_WPTR, write );			\
diff -urN linux-2.4.3-pre1/drivers/char/dsp56k.c linux/drivers/char/dsp56k.c
--- linux-2.4.3-pre1/drivers/char/dsp56k.c	Fri Feb  9 11:30:22 2001
+++ linux/drivers/char/dsp56k.c	Mon Dec  3 22:29:50 2001
@@ -265,7 +265,7 @@
 	}
 
 	default:
-		printk("DSP56k driver: Unknown minor device: %d\n", dev);
+		printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 		return -ENXIO;
 	}
 }
@@ -327,7 +327,7 @@
 		return -EFAULT;
 	}
 	default:
-		printk("DSP56k driver: Unknown minor device: %d\n", dev);
+		printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 		return -ENXIO;
 	}
 }
@@ -416,7 +416,7 @@
 		return 0;
 
 	default:
-		printk("DSP56k driver: Unknown minor device: %d\n", dev);
+		printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 		return -ENXIO;
 	}
 }
@@ -469,7 +469,7 @@
 		break;
 
 	default:
-		printk("DSP56k driver: Unknown minor device: %d\n", dev);
+		printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 		return -ENXIO;
 	}
 
@@ -490,7 +490,7 @@
 
 		break;
 	default:
-		printk("DSP56k driver: Unknown minor device: %d\n", dev);
+		printk(KERN_ERR "DSP56k driver: Unknown minor device: %d\n", dev);
 		return -ENXIO;
 	}
 
@@ -511,7 +511,9 @@
 
 static devfs_handle_t devfs_handle;
 
-int __init dsp56k_init(void)
+static const char banner[] __initdata = KERN_INFO "DSP56k driver installed\n";
+
+static int __init dsp56k_init_driver(void)
 {
 	if(!MACH_IS_ATARI || !ATARIHW_PRESENT(DSP56K)) {
 		printk("DSP56k driver: Hardware not present\n");
@@ -522,27 +524,19 @@
 		printk("DSP56k driver: Unable to register driver\n");
 		return -ENODEV;
 	}
-	devfs_handle = devfs_register (NULL, "dsp56k", DEVFS_FL_DEFAULT,
-				       DSP56K_MAJOR, 0,
-				       S_IFCHR | S_IRUSR | S_IWUSR,
-				       &dsp56k_fops, NULL);
-
-	dsp56k.in_use = 0;
-
-	printk("DSP56k driver installed\n");
+	devfs_handle = devfs_register(NULL, "dsp56k", DEVFS_FL_DEFAULT,
+				      DSP56K_MAJOR, 0,
+				      S_IFCHR | S_IRUSR | S_IWUSR,
+				      &dsp56k_fops, NULL);
 
+	printk(banner);
 	return 0;
 }
+module_init(dsp56k_init_driver);
 
-#ifdef MODULE
-int init_module(void)
-{
-	return dsp56k_init();
-}
-
-void cleanup_module(void)
+static void __exit dsp56k_cleanup_driver(void)
 {
 	devfs_unregister_chrdev(DSP56K_MAJOR, "dsp56k");
-	devfs_unregister (devfs_handle);
+	devfs_unregister(devfs_handle);
 }
-#endif /* MODULE */
+module_exit(dsp56k_cleanup_driver);
diff -urN linux-2.4.3-pre1/drivers/char/dz.c linux/drivers/char/dz.c
--- linux-2.4.3-pre1/drivers/char/dz.c	Fri Feb 16 16:02:35 2001
+++ linux/drivers/char/dz.c	Mon Dec  3 22:29:50 2001
@@ -847,10 +847,12 @@
   if (!new_info)
     return -EFAULT;
 
-  copy_from_user (&new_serial, new_info, sizeof(new_serial));
+  if(copy_from_user (&new_serial, new_info, sizeof(new_serial)))
+    return -EFAULT;
+    
   old_info = *info;
 
-  if (!suser())
+  if (!capable(CAP_SYS_ADMIN))
     return -EPERM;
 
   if (info->count > 1)
diff -urN linux-2.4.3-pre1/drivers/char/i810_rng.c linux/drivers/char/i810_rng.c
--- linux-2.4.3-pre1/drivers/char/i810_rng.c	Mon Dec  3 22:29:33 2001
+++ linux/drivers/char/i810_rng.c	Mon Dec  3 22:29:50 2001
@@ -1,8 +1,8 @@
 /*
 
 	Hardware driver for Intel i810 Random Number Generator (RNG)
-	Copyright 2000 Jeff Garzik <jgarzik@mandrakesoft.com>
-	Copyright 2000 Philipp Rumpf <prumpf@tux.org>
+	Copyright 2000,2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+	Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
 
 	Driver Web site:  http://sourceforge.net/projects/gkernel/
 
@@ -141,6 +141,16 @@
 	in regular code, and spin_lock in the timer function, to
 	serialize access to the RNG hardware area.
 
+	NOTE: request_mem_region was removed, for two reasons:
+	1) Only one RNG is supported by this driver, 2) The location
+	used by the RNG is a fixed location in MMIO-addressable memory,
+	3) users with properly working BIOS e820 handling will always
+	have the region in which the RNG is located reserved, so
+	request_mem_region calls always fail for proper setups.
+	However, for people who use mem=XX, BIOS e820 information is
+	-not- in /proc/iomem, and request_mem_region(RNG_ADDR) can
+	succeed.
+
 	----------------------------------------------------------
 
 	Change history:
@@ -189,6 +199,10 @@
 	* Call misc_register() from rng_init_one.
 	* Fix O_NONBLOCK to occur before we schedule.
 
+	Version 0.9.4:
+	* Fix: Remove request_mem_region
+	* Fix: Horrible bugs in FIPS calculation and test execution
+
  */
 
 
@@ -212,7 +226,7 @@
 /*
  * core module and version information
  */
-#define RNG_VERSION "0.9.3"
+#define RNG_VERSION "0.9.4"
 #define RNG_MODULE_NAME "i810_rng"
 #define RNG_DRIVER_NAME   RNG_MODULE_NAME " hardware driver " RNG_VERSION
 #define PFX RNG_MODULE_NAME ": "
@@ -221,7 +235,7 @@
 /*
  * debugging macros
  */
-#undef RNG_DEBUG /* define to 1 to enable copious debugging info */
+#undef RNG_DEBUG /* define to enable copious debugging info */
 
 #ifdef RNG_DEBUG
 /* note: prints function name for you */
@@ -230,8 +244,8 @@
 #define DPRINTK(fmt, args...)
 #endif
 
-#define RNG_NDEBUG 0        /* define to 1 to disable lightweight runtime checks */
-#if RNG_NDEBUG
+#undef RNG_NDEBUG        /* define to disable lightweight runtime checks */
+#ifdef RNG_NDEBUG
 #define assert(expr)
 #else
 #define assert(expr) \
@@ -368,8 +382,11 @@
 
 		/* fitness testing via FIPS, if we have enough data */
 		rng_fips_test_store (rng_data);
-		if (rng_fips_counter > RNG_FIPS_TEST_THRESHOLD)
+		rng_fips_counter++;
+		if (rng_fips_counter == RNG_FIPS_TEST_THRESHOLD) {
 			rng_run_fips_test ();
+			rng_fips_counter = 0;
+		}
 	} else {
 		spin_unlock (&rng_lock);
 	}
@@ -734,17 +751,11 @@
 	if (pci_enable_device (dev))
 		return -EIO;
 
-	if (!request_mem_region (RNG_ADDR, RNG_ADDR_LEN, RNG_MODULE_NAME)) {
-		printk (KERN_ERR PFX "cannot reserve RNG region\n");
-		DPRINTK ("EXIT, returning -EBUSY\n");
-		return -EBUSY;
-	}
-
 	rc = misc_register (&rng_miscdev);
 	if (rc) {
 		printk (KERN_ERR PFX "cannot register misc device\n");
 		DPRINTK ("EXIT, returning %d\n", rc);
-		goto err_out_free_res;
+		goto err_out;
 	}
 
 	rng_mem = ioremap (RNG_ADDR, RNG_ADDR_LEN);
@@ -788,8 +799,7 @@
 	iounmap (rng_mem);
 err_out_free_miscdev:
 	misc_deregister (&rng_miscdev);
-err_out_free_res:
-	release_mem_region (RNG_ADDR, RNG_ADDR_LEN);
+err_out:
 	return rc;
 }
 
@@ -866,7 +876,6 @@
 	rng_sysctl (0);
 
 	iounmap (rng_mem);
-	release_mem_region (RNG_ADDR, RNG_ADDR_LEN);
 
 	rng_pdev = NULL;
 
@@ -935,7 +944,7 @@
 
 			/* Check if we just failed longrun test */
 			if (rlength >= 33)
-				rng_test &= 8;
+				rng_test |= 8;
 			rlength = 0;
 			/* flip the current run type */
 			last_bit = current_bit;
@@ -963,16 +972,16 @@
 	else {
 		runs[5 + (6 * current_bit)]++;
 		if (rlength >= 33)
-			rng_test &= 8;
+			rng_test |= 8;
 	}
 	/* Ones test */
 	if ((ones >= 10346) || (ones <= 9654))
-		rng_test &= 1;
+		rng_test |= 1;
 	/* Poker calcs */
 	for (i = 0, j = 0; i < 16; i++)
 		j += poker[i] * poker[i];
 	if ((j >= 1580457) || (j <= 1562821))
-		rng_test &= 2;
+		rng_test |= 2;
 	if ((runs[0] < 2267) || (runs[0] > 2733) ||
 	    (runs[1] < 1079) || (runs[1] > 1421) ||
 	    (runs[2] < 502) || (runs[2] > 748) ||
@@ -985,7 +994,7 @@
 	    (runs[9] < 223) || (runs[9] > 402) ||
 	    (runs[10] < 90) || (runs[10] > 223) ||
 	    (runs[11] < 90) || (runs[11] > 223)) {
-		rng_test &= 4;
+		rng_test |= 4;
 	}
 
 	rng_test = !rng_test;
diff -urN linux-2.4.3-pre1/drivers/char/lp.c linux/drivers/char/lp.c
--- linux-2.4.3-pre1/drivers/char/lp.c	Fri Feb  9 11:30:22 2001
+++ linux/drivers/char/lp.c	Mon Dec  3 22:29:50 2001
@@ -500,7 +500,7 @@
 			if (copy_to_user((int *) arg, &LP_STAT(minor),
 					sizeof(struct lp_stats)))
 				return -EFAULT;
-			if (suser())
+			if (capable(CAP_SYS_ADMIN))
 				memset(&LP_STAT(minor), 0,
 						sizeof(struct lp_stats));
 			break;
diff -urN linux-2.4.3-pre1/drivers/char/mem.c linux/drivers/char/mem.c
--- linux-2.4.3-pre1/drivers/char/mem.c	Fri Feb  9 11:30:22 2001
+++ linux/drivers/char/mem.c	Mon Dec  3 22:29:50 2001
@@ -642,6 +642,9 @@
 #ifdef CONFIG_FTAPE
 	ftape_init();
 #endif
+#if defined(CONFIG_S390_TAPE) && defined(CONFIG_S390_TAPE_CHAR)
+	tapechar_init();
+#endif
 #if defined(CONFIG_ADB)
 	adbdev_init();
 #endif
diff -urN linux-2.4.3-pre1/drivers/char/pc_keyb.c linux/drivers/char/pc_keyb.c
--- linux-2.4.3-pre1/drivers/char/pc_keyb.c	Fri Feb  9 11:30:22 2001
+++ linux/drivers/char/pc_keyb.c	Mon Dec  3 22:29:50 2001
@@ -908,6 +908,8 @@
 							   controller. */
 	aux_write_ack(AUX_ENABLE_DEV); /* Enable aux device */
 	kbd_write_cmd(AUX_INTS_ON); /* Enable controller ints */
+	
+	mdelay(2);			/* Ensure we follow the kbc access delay rules.. */
 
 	send_data(KBD_CMD_ENABLE);	/* try to workaround toshiba4030cdt problem */
 
diff -urN linux-2.4.3-pre1/drivers/char/tpqic02.c linux/drivers/char/tpqic02.c
--- linux-2.4.3-pre1/drivers/char/tpqic02.c	Fri Feb  9 11:30:22 2001
+++ linux/drivers/char/tpqic02.c	Mon Dec  3 22:29:50 2001
@@ -30,6 +30,8 @@
  *
  * You are not allowed to change this line nor the text above.
  *
+ * 2001/02/26	Minor s/suser/capable/
+ *
  * 1996/10/10   Emerald changes
  *
  * 1996/05/21	Misc changes+merges+cleanups + I/O reservations
@@ -208,7 +210,7 @@
  * must ensure that a large enough buffer is passed to the kernel, in order
  * to reduce tape repositioning wear and tear.
  */
-static unsigned long buffaddr;	/* physical address of buffer */
+static void *buffaddr;	/* virtual address of buffer */
 
 /* This translates minor numbers to the corresponding recording format: */
 static const char *format_names[] = {
@@ -1376,7 +1378,7 @@
 	flags=claim_dma_lock();
 	clear_dma_ff(QIC02_TAPE_DMA);
 	set_dma_mode(QIC02_TAPE_DMA, dma_mode);
-	set_dma_addr(QIC02_TAPE_DMA, buffaddr+dma_bytes_done);	/* full address */
+	set_dma_addr(QIC02_TAPE_DMA, virt_to_bus(buffaddr) + dma_bytes_done);
 	set_dma_count(QIC02_TAPE_DMA, TAPE_BLKSIZE);
 
 	/* start tape DMA controller */
@@ -1921,7 +1923,7 @@
 	    /* copy buffer to user-space in one go */
 	    if (bytes_done>0)
 	    {
-		err = copy_to_user( (void *) buf, (void *) bus_to_virt(buffaddr), bytes_done);
+		err = copy_to_user(buf, buffaddr, bytes_done);
 		if (err)
 		{
 		    return -EFAULT;
@@ -2074,7 +2076,7 @@
 	/* copy from user to DMA buffer and initiate transfer. */
 	if (bytes_todo>0)
 	{
-	    err = copy_from_user( (void *) bus_to_virt(buffaddr), (const void *) buf, bytes_todo);
+	    err = copy_from_user(buffaddr, buf, bytes_todo);
 	    if (err)
 	    {
 		return -EFAULT;
@@ -2198,7 +2200,7 @@
 
     if (MINOR(dev)==255)	/* special case for resetting */
     {
-	if (suser())
+	if (capable(CAP_SYS_ADMIN))
 	{
 	    return (tape_reset(1)==TE_OK) ? -EAGAIN : -ENXIO;
 	}
@@ -2607,7 +2609,7 @@
 	
 	CHECK_IOC_SIZE(mtconfiginfo);
 	
-	if (!suser())
+	if (!capable(CAP_SYS_ADMIN))
 	{
 	    return -EPERM;
 	}
@@ -2780,7 +2782,7 @@
     release_region(QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE);
     if (buffaddr)
     {
-	free_pages(buffaddr, get_order(TPQBUF_SIZE));
+	free_pages((unsigned long)buffaddr, get_order(TPQBUF_SIZE));
     }
     buffaddr = 0; /* Better to cause a panic than overwite someone else */
     status_zombie = YES;
@@ -2830,9 +2832,7 @@
     request_region(QIC02_TAPE_PORT, QIC02_TAPE_PORT_RANGE, TPQIC02_NAME);
     
     /* Setup the page-address for the dma transfer. */
-
-    /*** TODO: does _get_dma_pages() really return the physical address?? ****/
-    buffaddr = __get_dma_pages(GFP_KERNEL,get_order(TPQBUF_SIZE));
+    buffaddr = (void *)__get_dma_pages(GFP_KERNEL, get_order(TPQBUF_SIZE));
     
     if (!buffaddr)
     {
@@ -2840,7 +2840,7 @@
 	return -EBUSY; /* Not ideal, EAGAIN perhaps? */
     }
     
-    memset( (void*) buffaddr, 0, TPQBUF_SIZE );
+    memset(buffaddr, 0, TPQBUF_SIZE);
     
     printk(TPQIC02_NAME ": Settings: IRQ %d, DMA %d, IO 0x%x, IFC %s\n",
 	   QIC02_TAPE_IRQ, QIC02_TAPE_DMA,
diff -urN linux-2.4.3-pre1/drivers/ide/ide-probe.c linux/drivers/ide/ide-probe.c
--- linux-2.4.3-pre1/drivers/ide/ide-probe.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/ide/ide-probe.c	Mon Dec  3 22:29:50 2001
@@ -614,9 +614,14 @@
 {
 	unsigned long flags;
 	unsigned int index;
-	ide_hwgroup_t *hwgroup;
+	ide_hwgroup_t *hwgroup, *new_hwgroup;
 	ide_hwif_t *match = NULL;
 
+	
+	/* Allocate the buffer and potentially sleep first */
+	
+	new_hwgroup = kmalloc(sizeof(ide_hwgroup_t),GFP_KERNEL);
+	
 	save_flags(flags);	/* all CPUs */
 	cli();			/* all CPUs */
 
@@ -650,10 +655,14 @@
 	 */
 	if (match) {
 		hwgroup = match->hwgroup;
+		if(new_hwgroup)
+			kfree(new_hwgroup);
 	} else {
-		hwgroup = kmalloc(sizeof(ide_hwgroup_t), GFP_KERNEL);
-		if (!hwgroup)
+		hwgroup = new_hwgroup;
+		if (!hwgroup) {
+			restore_flags(flags);	/* all CPUs */
 			return 1;
+		}
 		memset(hwgroup, 0, sizeof(ide_hwgroup_t));
 		hwgroup->hwif     = hwif->next = hwif;
 		hwgroup->rq       = NULL;
diff -urN linux-2.4.3-pre1/drivers/ieee1394/aic5800.c linux/drivers/ieee1394/aic5800.c
--- linux-2.4.3-pre1/drivers/ieee1394/aic5800.c	Wed Oct 11 16:49:47 2000
+++ linux/drivers/ieee1394/aic5800.c	Mon Dec  3 22:29:50 2001
@@ -657,7 +657,7 @@
 	};
     }
     if ( interruptEvent & INT_BusReset ) {
-	PRINT(KERN_INFO, aic->id, "bus reset occured");
+	PRINT(KERN_INFO, aic->id, "bus reset occurred");
 	if (!host->in_bus_reset) {
 	    hpsb_bus_reset(host);
 	}
diff -urN linux-2.4.3-pre1/drivers/ieee1394/ohci1394.c linux/drivers/ieee1394/ohci1394.c
--- linux-2.4.3-pre1/drivers/ieee1394/ohci1394.c	Sun Feb  4 21:34:18 2001
+++ linux/drivers/ieee1394/ohci1394.c	Mon Dec  3 22:29:50 2001
@@ -1129,7 +1129,7 @@
 		/*
 		 * Problem: How can I ensure that the AT bottom half will be
 		 * executed before the AR bottom half (both events may have
-		 * occured within a single irq event)
+		 * occurred within a single irq event)
 		 * Quick hack: just launch it within the IRQ handler
 		 */
 		if (event & OHCI1394_reqTxComplete) { 
diff -urN linux-2.4.3-pre1/drivers/ieee1394/pcilynx.c linux/drivers/ieee1394/pcilynx.c
--- linux-2.4.3-pre1/drivers/ieee1394/pcilynx.c	Tue Feb 13 14:13:44 2001
+++ linux/drivers/ieee1394/pcilynx.c	Mon Dec  3 22:29:50 2001
@@ -1127,7 +1127,7 @@
 
         if (intmask & PCI_INT_1394) {
                 if (linkint & LINK_INT_PHY_TIMEOUT) {
-                        PRINT(KERN_INFO, lynx->id, "PHY timeout occured");
+                        PRINT(KERN_INFO, lynx->id, "PHY timeout occurred");
                 }
                 if (linkint & LINK_INT_PHY_BUSRESET) {
                         PRINT(KERN_INFO, lynx->id, "bus reset interrupt");
diff -urN linux-2.4.3-pre1/drivers/isdn/hisax/bkm_ax.h linux/drivers/isdn/hisax/bkm_ax.h
--- linux-2.4.3-pre1/drivers/isdn/hisax/bkm_ax.h	Tue Nov 28 21:44:41 2000
+++ linux/drivers/isdn/hisax/bkm_ax.h	Mon Dec  3 22:29:50 2001
@@ -1,9 +1,9 @@
-/* $Id: bkm_ax.h,v 1.5.6.1 2000/11/28 12:02:46 kai Exp $
+/* $Id: bkm_ax.h,v 1.5.6.2 2001/02/16 16:43:25 kai Exp $
  * bkm_ax.h   low level decls for T-Berkom cards A4T and Scitel Quadro (4*S0, passive)
  *
  * Author     Roland Klabunde (R.Klabunde@Berkom.de)
  *
- * This file is (c) under GNU PUBLIC LICENSE
+ * This file is (c) under GNU General Public License
  *
  */
 
diff -urN linux-2.4.3-pre1/drivers/net/3c523.h linux/drivers/net/3c523.h
--- linux-2.4.3-pre1/drivers/net/3c523.h	Fri Feb  9 11:40:02 2001
+++ linux/drivers/net/3c523.h	Mon Dec  3 22:29:50 2001
@@ -60,7 +60,7 @@
   unsigned short cbl_offset;    /* pointeroffset, command block list */
   unsigned short rfa_offset;    /* pointeroffset, receive frame area */
   unsigned short crc_errs;      /* CRC-Error counter */
-  unsigned short aln_errs;      /* allignmenterror counter */
+  unsigned short aln_errs;      /* alignmenterror counter */
   unsigned short rsc_errs;      /* Resourceerror counter */
   unsigned short ovrn_errs;     /* OVerrunerror counter */
 };
diff -urN linux-2.4.3-pre1/drivers/net/3c527.c linux/drivers/net/3c527.c
--- linux-2.4.3-pre1/drivers/net/3c527.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/3c527.c	Mon Dec  3 22:29:50 2001
@@ -1,8 +1,9 @@
-/* 3c527.c: 3Com Etherlink/MC32 driver for Linux
+/* 3c527.c: 3Com Etherlink/MC32 driver for Linux 2.4
  *
  *	(c) Copyright 1998 Red Hat Software Inc
- *	Written by Alan Cox.
+ *	Written by Alan Cox. 
  *	Further debugging by Carl Drougge.
+ *      Modified by Richard Procter (rnp@netlink.co.nz)
  *
  *	Based on skeleton.c written 1993-94 by Donald Becker and ne2.c
  *	(for the MCA stuff) written by Wim Dumon.
@@ -16,7 +17,7 @@
  */
 
 static const char *version =
-	"3c527.c:v0.08 2000/02/22 Alan Cox (alan@redhat.com)\n";
+	"3c527.c:v0.6 2001/03/03 Richard Proctor (rnp@netlink.co.nz)\n";
 
 /**
  * DOC: Traps for the unwary
@@ -24,6 +25,13 @@
  *	The diagram (Figure 1-1) and the POS summary disagree with the
  *	"Interrupt Level" section in the manual.
  *
+ *	The manual contradicts itself when describing the minimum number 
+ *	buffers in the 'configure lists' command. 
+ *	My card accepts a buffer config of 4/4. 
+ *
+ *	Setting the SAV BP bit does not save bad packets, but
+ *	only enables RX on-card stats collection. 
+ *
  *	The documentation in places seems to miss things. In actual fact
  *	I've always eventually found everything is documented, it just
  *	requires careful study.
@@ -35,25 +43,39 @@
  *	Intel NIC. For performance we want to keep the transmit queue deep
  *	as the card can transmit packets while fetching others from main
  *	memory by bus master DMA. Transmission and reception are driven by
- *	ring buffers. When updating the ring we are required to do some
- *	housekeeping work using the mailboxes and the command register.
+ *	circular buffer queues.
  *
- *	The mailboxes provide a method for sending control requests to the
- *	card. The transmit mail box is used to update the transmit ring 
- *	pointers and the receive mail box to update the receive ring
- *	pointers. The exec mailbox allows a variety of commands to be
- *	executed. Each command must complete before the next is executed.
- *	Primarily we use the exec mailbox for controlling the multicast lists.
- *	We have to do a certain amount of interesting hoop jumping as the 
- *	multicast list changes can occur in interrupt state when the card
- *	has an exec command pending. We defer such events until the command
- *	completion interrupt.
- *
- *	The control register is used to pass status information. It tells us
- *	the transmit and receive status for packets and allows us to control
- *	the card operation mode. You must stop the card when emptying the
- *	receive ring, or you will race with the ring buffer and lose packets.
- */
+ *	The mailboxes can be used for controlling how the card traverses
+ *	its buffer rings, but are used only for inital setup in this
+ *	implementation.  The exec mailbox allows a variety of commands to
+ *	be executed. Each command must complete before the next is
+ *	executed. Primarily we use the exec mailbox for controlling the
+ *	multicast lists.  We have to do a certain amount of interesting
+ *	hoop jumping as the multicast list changes can occur in interrupt
+ *	state when the card has an exec command pending. We defer such
+ *	events until the command completion interrupt.
+ *
+ *	A copy break scheme (taken from 3c59x.c) is employed whereby
+ *	received frames exceeding a configurable length are passed
+ *	directly to the higher networking layers without incuring a copy,
+ *	in what amounts to a time/space trade-off.
+ *	 
+ *	The card also keeps a large amount of statistical information
+ *	on-board. In a perfect world, these could be used safely at no
+ *	cost. However, lacking information to the contrary, processing
+ *	them without races would involve so much extra complexity as to
+ *	make it unworthwhile to do so. In the end, a hybrid SW/HW
+ *	implementation was made necessary --- see mc32_update_stats().  
+ *
+ * DOC: Notes
+ *	
+ *	It should be possible to use two or more cards, but at this stage
+ *	only by loading two copies of the same module.
+ *
+ *	The on-board 82586 NIC has trouble receiving multiple
+ *	back-to-back frames and so is likely to drop packets from fast
+ *	senders.
+**/
 
 #include <linux/module.h>
 
@@ -78,6 +100,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
+#include <linux/if_ether.h>
 
 #include "3c527.h"
 
@@ -91,23 +114,37 @@
 #ifndef NET_DEBUG
 #define NET_DEBUG 2
 #endif
+
+#undef DEBUG_IRQ
+
 static unsigned int mc32_debug = NET_DEBUG;
 
 /* The number of low I/O ports used by the ethercard. */
-#define NETCARD_IO_EXTENT	8
+#define MC32_IO_EXTENT	8
+
+/* As implemented, values must be a power-of-2 -- 4/8/16/32 */ 
+#define TX_RING_LEN     32       /* Typically the card supports 37  */
+#define RX_RING_LEN     8        /*     "       "        "          */
+
+/* Copy break point, see above for details. 
+ * Setting to > 1512 effectively disables this feature.	*/	    
+#define RX_COPYBREAK    200      /* Value from 3c59x.c */
+
+/* Issue the 82586 workaround command - this is for "busy lans", but
+ * basically means for all lans now days - has a performance (latency) 
+ * cost, but best set. */ 
+static const int WORKAROUND_82586=1;
 
+/* Pointers to buffers and their on-card records */
 
-struct mc32_mailbox
+struct mc32_ring_desc 
 {
-	u16	mbox __attribute((packed));
-	u16	data[1] __attribute((packed));
+	volatile struct skb_header *p;                    
+	struct sk_buff *skb;          
 };
 
-/* Information that need to be kept for each board. */
-
-#define TX_RING_MAX	16	/* Typically the card supports 37 */
-#define RX_RING_MAX	32	/*        "     "       "         */
 
+/* Information that needs to be kept for each board. */
 struct mc32_local 
 {
 	struct net_device_stats net_stats;
@@ -115,25 +152,28 @@
 	volatile struct mc32_mailbox *rx_box;
 	volatile struct mc32_mailbox *tx_box;
 	volatile struct mc32_mailbox *exec_box;
-	volatile u16 *stats;
-	u16 tx_chain;
-	u16 rx_chain;
-	u16 tx_len;
-	u16 rx_len;
+        volatile struct mc32_stats *stats;    /* Start of on-card statistics */
+        u16 tx_chain;           /* Transmit list start offset */
+	u16 rx_chain;           /* Receive list start offset */
+        u16 tx_len;             /* Transmit list count */ 
+        u16 rx_len;             /* Receive list count */
+
 	u32 base;
-	u16 rx_halted;
-	u16 tx_halted;
-	u16 rx_pending;
 	u16 exec_pending;
 	u16 mc_reload_wait;	/* a multicast load request is pending */
-	atomic_t tx_count;		/* buffers left */
+	u32 mc_list_valid;	/* True when the mclist is set */
+	u16 xceiver_state;      /* Current transceiver state. bitmapped */ 
+	u16 desired_state;      /* The state we want the transceiver to be in */ 
+	atomic_t tx_count;	/* buffers left */
 	wait_queue_head_t event;
-	struct sk_buff *tx_skb[TX_RING_MAX];	/* Transmit ring */
-	u16 tx_skb_top;
-	u16 tx_skb_end;
-	struct sk_buff *rx_skb[RX_RING_MAX];	/* Receive ring */
-	void *rx_ptr[RX_RING_MAX];		/* Data pointers */
-	u32 mc_list_valid;			/* True when the mclist is set */
+
+	struct mc32_ring_desc tx_ring[TX_RING_LEN];	/* Host Transmit ring */
+	struct mc32_ring_desc rx_ring[RX_RING_LEN];	/* Host Receive ring */
+
+	u16 tx_ring_tail;       /* index to tx de-queue end */
+	u16 tx_ring_head;       /* index to tx en-queue end */
+
+	u16 rx_ring_tail;       /* index to rx de-queue end */ 
 };
 
 /* The station (ethernet) address prefix, used for a sanity check. */
@@ -146,18 +186,25 @@
 	char		*name;
 };
 
-static struct mca_adapters_t mc32_adapters[] __initdata = {
+const struct mca_adapters_t mc32_adapters[] = {
 	{ 0x0041, "3COM EtherLink MC/32" },
 	{ 0x8EF5, "IBM High Performance Lan Adapter" },
 	{ 0x0000, NULL }
 };
 
 
-/* Index to functions, as function prototypes. */
+/* Macros for ring index manipulations */ 
+static inline u16 next_rx(u16 rx) { return (rx+1)&(RX_RING_LEN-1); };
+static inline u16 prev_rx(u16 rx) { return (rx-1)&(RX_RING_LEN-1); };
 
+static inline u16 next_tx(u16 tx) { return (tx+1)&(TX_RING_LEN-1); };
+
+
+/* Index to functions, as function prototypes. */
 extern int mc32_probe(struct net_device *dev);
 
 static int	mc32_probe1(struct net_device *dev, int ioaddr);
+static int      mc32_command(struct net_device *dev, u16 cmd, void *data, int len);
 static int	mc32_open(struct net_device *dev);
 static void	mc32_timeout(struct net_device *dev);
 static int	mc32_send_packet(struct sk_buff *skb, struct net_device *dev);
@@ -167,9 +214,8 @@
 static void	mc32_set_multicast_list(struct net_device *dev);
 static void	mc32_reset_multicast_list(struct net_device *dev);
 
-
 /**
- * mc32_probe:
+ * mc32_probe 	-	Search for supported boards
  * @dev: device to probe
  *
  * Because MCA bus is a real bus and we can scan for cards we could do a
@@ -212,7 +258,7 @@
 }
 
 /**
- * mc32_probe1:
+ * mc32_probe1	-	Check a given slot for a board and test the card
  * @dev:  Device structure to fill in
  * @slot: The MCA bus slot being used by this card
  *
@@ -221,11 +267,11 @@
  * in firmware so we have to wait for it to return and post us either a 
  * failure case or some addresses we use to find the board internals.
  */
- 
+
 static int __init mc32_probe1(struct net_device *dev, int slot)
 {
 	static unsigned version_printed = 0;
-	int i;
+	int i, err;
 	u8 POS;
 	u32 base;
 	struct mc32_local *lp;
@@ -258,7 +304,7 @@
 		"82586 initialisation failure",
 		"Adapter list configuration error"
 	};
-	
+
 	/* Time to play MCA games */
 
 	if (mc32_debug  &&  version_printed++ == 0)
@@ -301,6 +347,12 @@
 	
 	dev->irq = ((POS>>2)&3)+9;
 	
+	if(!request_region(dev->base_addr, MC32_IO_EXTENT, cardname))
+	{
+		printk("io 0x%3lX, which is busy.\n", dev->base_addr);
+		return -EBUSY;
+	}
+
 	printk("io 0x%3lX irq %d mem 0x%lX (%dK)\n",
 		dev->base_addr, dev->irq, dev->mem_start, i/1024);
 	
@@ -349,18 +401,20 @@
 	 *	Grab the IRQ
 	 */
 
-	i = request_irq(dev->irq, &mc32_interrupt, 0, dev->name, dev);
+	i = request_irq(dev->irq, &mc32_interrupt, SA_SHIRQ, dev->name, dev);
 	if (i) {
-		printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+		release_region(dev->base_addr, MC32_IO_EXTENT);
+		printk(KERN_ERR "%s: unable to get IRQ %d.\n", dev->name, dev->irq);
 		return i;
 	}
 
+
 	/* Initialize the device structure. */
 	dev->priv = kmalloc(sizeof(struct mc32_local), GFP_KERNEL);
 	if (dev->priv == NULL)
 	{
-		free_irq(dev->irq, dev);
-		return -ENOMEM;
+		err = -ENOMEM;
+		goto err_exit_irq; 
 	}
 
 	memset(dev->priv, 0, sizeof(struct mc32_local));
@@ -371,14 +425,14 @@
 
 	base = inb(dev->base_addr);
 	
-	while(base==0xFF)
+	while(base == 0xFF)
 	{
 		i++;
-		if(i==1000)
+		if(i == 1000)
 		{
-			printk("%s: failed to boot adapter.\n", dev->name);
-			free_irq(dev->irq, dev);
-			return -ENODEV;
+			printk(KERN_ERR "%s: failed to boot adapter.\n", dev->name);
+			err = -ENODEV; 
+			goto err_exit_free;
 		}
 		udelay(1000);
 		if(inb(dev->base_addr+2)&(1<<5))
@@ -388,12 +442,12 @@
 	if(base>0)
 	{
 		if(base < 0x0C)
-			printk("%s: %s%s.\n", dev->name, failures[base-1],
+			printk(KERN_ERR "%s: %s%s.\n", dev->name, failures[base-1],
 				base<0x0A?" test failure":"");
 		else
-			printk("%s: unknown failure %d.\n", dev->name, base);
-		free_irq(dev->irq, dev);
-		return -ENODEV;
+			printk(KERN_ERR "%s: unknown failure %d.\n", dev->name, base);
+		err = -ENODEV; 
+		goto err_exit_free;
 	}
 	
 	base=0;
@@ -408,8 +462,8 @@
 			if(n>100)
 			{
 				printk(KERN_ERR "%s: mailbox read fail (%d).\n", dev->name, i);
-				free_irq(dev->irq, dev);
-				return -ENODEV;
+				err = -ENODEV;
+				goto err_exit_free;
 			}
 		}
 
@@ -418,11 +472,11 @@
 	
 	lp->exec_box=bus_to_virt(dev->mem_start+base);
 	
-	base=lp->exec_box->data[1]<<16|lp->exec_box->data[0];
+	base=lp->exec_box->data[1]<<16|lp->exec_box->data[0];  
 	
 	lp->base = dev->mem_start+base;
 	
-	lp->rx_box=bus_to_virt(lp->base + lp->exec_box->data[2]);
+	lp->rx_box=bus_to_virt(lp->base + lp->exec_box->data[2]); 
 	lp->tx_box=bus_to_virt(lp->base + lp->exec_box->data[3]);
 	
 	lp->stats = bus_to_virt(lp->base + lp->exec_box->data[5]);
@@ -431,18 +485,16 @@
 	 *	Descriptor chains (card relative)
 	 */
 	 
-	lp->tx_chain 		= lp->exec_box->data[8];
-	lp->rx_chain 		= lp->exec_box->data[10];
-	lp->tx_len 		= lp->exec_box->data[9];
-	lp->rx_len 		= lp->exec_box->data[11];
+	lp->tx_chain 		= lp->exec_box->data[8];   /* Transmit list start offset */
+	lp->rx_chain 		= lp->exec_box->data[10];  /* Receive list start offset */
+	lp->tx_len 		= lp->exec_box->data[9];   /* Transmit list count */ 
+	lp->rx_len 		= lp->exec_box->data[11];  /* Receive list count */
+
 	init_waitqueue_head(&lp->event);
 	
-	printk("%s: %d RX buffers, %d TX buffers. Base of 0x%08X.\n",
-		dev->name, lp->rx_len, lp->tx_len, lp->base);
-		
-	if(lp->tx_len > TX_RING_MAX)
-		lp->tx_len = TX_RING_MAX;
-	
+	printk("%s: Firmware Rev %d. %d RX buffers, %d TX buffers. Base of 0x%08X.\n",
+		dev->name, lp->exec_box->data[12], lp->rx_len, lp->tx_len, lp->base);
+
 	dev->open		= mc32_open;
 	dev->stop		= mc32_close;
 	dev->hard_start_xmit	= mc32_send_packet;
@@ -450,57 +502,65 @@
 	dev->set_multicast_list = mc32_set_multicast_list;
 	dev->tx_timeout		= mc32_timeout;
 	dev->watchdog_timeo	= HZ*5;	/* Board does all the work */
+
 	
-	lp->rx_halted		= 1;
-	lp->tx_halted		= 1;
-	lp->rx_pending		= 0;
+	lp->xceiver_state = HALTED; 
+	
+	lp->tx_ring_tail=lp->tx_ring_head=0;
 
 	/* Fill in the fields of the device structure with ethernet values. */
 	ether_setup(dev);
+	
 	return 0;
+
+err_exit_free:
+	kfree(dev->priv);
+err_exit_irq:
+	free_irq(dev->irq, dev);
+	release_region(dev->base_addr, MC32_IO_EXTENT);
+	return err;
 }
 
 
 /**
- *	mc32_ring_poll:
+ *	mc32_ready_poll		-	wait until we can feed it a command
  *	@dev:	The device to wait for
  *	
- *	Wait until a command we issues to the control register is completed.
- *	This actually takes very little time at all, which is fortunate as
- *	we often have to busy wait it.
+ *	Wait until the card becomes ready to accept a command via the
+ *	command register. This tells us nothing about the completion
+ *	status of any pending commands and takes very little time at all.
  */
  
-static void mc32_ring_poll(struct net_device *dev)
+static void mc32_ready_poll(struct net_device *dev)
 {
 	int ioaddr = dev->base_addr;
 	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
 }
 
 
-
 /**
- *	mc32_command_nowait:
+ *	mc32_command_nowait	-	send a command non blocking
  *	@dev: The 3c527 to issue the command to
  *	@cmd: The command word to write to the mailbox
  *	@data: A data block if the command expects one
  *	@len: Length of the data block
  *
- *	Send a command from interrupt state. If there is a command currently
- *	being executed then we return an error of -1. It simply isnt viable
- *	to wait around as commands may be slow. Providing we get in then
- *	we send the command and busy wait for the board to acknowledge that
- *	a command request is pending. We do not wait for the command to 
- *	complete, just for the card to admit to noticing it.  
+ *	Send a command from interrupt state. If there is a command
+ *	currently being executed then we return an error of -1. It simply
+ *	isn't viable to wait around as commands may be slow. Providing we
+ *	get in, we busy wait for the board to become ready to accept the
+ *	command and issue it. We do not wait for the command to complete
+ *	--- the card will interrupt us when it's done.
  */
 
 static int mc32_command_nowait(struct net_device *dev, u16 cmd, void *data, int len)
 {
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	int ioaddr = dev->base_addr;
-	
+
 	if(lp->exec_pending)
 		return -1;
-		
+	
 	lp->exec_pending=3;
 	lp->exec_box->mbox=0;
 	lp->exec_box->mbox=cmd;
@@ -515,7 +575,7 @@
 
 
 /**
- *	mc32_command: 
+ *	mc32_command	-	send a command and sleep until completion
  *	@dev: The 3c527 card to issue the command to
  *	@cmd: The command word to write to the mailbox
  *	@data: A data block if the command expects one
@@ -543,8 +603,6 @@
  *	3	- command issued, trash reply. In which case the irq
  *		  takes it back to state 0
  *
- *	Send command and block for results. On completion spot and reissue
- *	multicasts
  */
   
 static int mc32_command(struct net_device *dev, u16 cmd, void *data, int len)
@@ -580,251 +638,287 @@
 	/* Send the command */
 	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
 	outb(1<<6, ioaddr+HOST_CMD);	
-	
+
 	save_flags(flags);
 	cli();
+
 	while(lp->exec_pending!=2)
 		sleep_on(&lp->event);
 	lp->exec_pending=0;
 	restore_flags(flags);
 	
-	 
-	if(lp->exec_box->data[0]&(1<<13))
+	if(lp->exec_box->mbox&(1<<13))
 		ret = -1;
+
 	/*
 	 *	A multicast set got blocked - do it now
 	 */
 		
 	if(lp->mc_reload_wait)
+	{
 		mc32_reset_multicast_list(dev);
+	}
 
 	return ret;
 }
 
 
 /**
- *	mc32_rx_abort:
- *	@dev: 3c527 to abort
+ *	mc32_start_transceiver	-	tell board to restart tx/rx
+ *	@dev: The 3c527 card to issue the command to
  *
- *	Peforms a receive abort sequence on the card. In fact after some
- *	experimenting we now simply tell the card to suspend reception. When
- *	issuing aborts occasionally odd things happened.
- */
- 
-static void mc32_rx_abort(struct net_device *dev)
-{
-	struct mc32_local *lp = (struct mc32_local *)dev->priv;
-	int ioaddr = dev->base_addr;
+ *	This may be called from the interrupt state, where it is used
+ *	to restart the rx ring if the card runs out of rx buffers. 
+ *	
+ * 	First, we check if it's ok to start the transceiver. We then show
+ * 	the card where to start in the rx ring and issue the
+ * 	commands to start reception and transmission. We don't wait
+ * 	around for these to complete.
+ */ 
 
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-	
-	lp->rx_box->mbox=0;
-	outb(3<<3, ioaddr+HOST_CMD);	/* Suspend reception */
-}
+static void mc32_start_transceiver(struct net_device *dev) {
 
- 
-/**
- *	mc32_rx_begin:
- *	@dev: 3c527 to enable
- *
- *	We wait for any pending command to complete and then issue 
- *	a start reception command to the board itself. At this point 
- *	receive handling continues as it was before.
- */
- 
-static void mc32_rx_begin(struct net_device *dev)
-{
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	int ioaddr = dev->base_addr;
-	
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-	
-	lp->rx_box->mbox=0;
-	outb(1<<3, ioaddr+HOST_CMD);	/* GO */
-	mc32_ring_poll(dev);	
-	
-	lp->rx_halted=0;
-	lp->rx_pending=0;
-}
 
-/**
- *	mc32_tx_abort:
- *	@dev: 3c527 to abort
- *
- *	Peforms a receive abort sequence on the card. In fact after some
- *	experimenting we now simply tell the card to suspend transmits . When
- *	issuing aborts occasionally odd things happened. In theory we want
- *	an abort to be sure we can recycle our buffers. As it happens we
- *	just have to be careful to shut the card down on close, and
- *	boot it carefully from scratch on setup.
- */
- 
-static void mc32_tx_abort(struct net_device *dev)
-{
-	struct mc32_local *lp = (struct mc32_local *)dev->priv;
-	int ioaddr = dev->base_addr;
-	
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-	
+	/* Ignore RX overflow on device closure */ 
+	if (lp->desired_state==HALTED)  
+		return; 
+
+	mc32_ready_poll(dev); 
+
 	lp->tx_box->mbox=0;
-	outb(3, ioaddr+HOST_CMD);	/* Suspend */
-	
-	/* Ring empty */
-	
-	atomic_set(&lp->tx_count, lp->tx_len);
+	lp->rx_box->mbox=0;
+
+	/* Give the card the offset to the post-EOL-bit RX descriptor */ 
+	lp->rx_box->data[0]=lp->rx_ring[prev_rx(lp->rx_ring_tail)].p->next; 
+
+	outb(HOST_CMD_START_RX, ioaddr+HOST_CMD);      
+
+	mc32_ready_poll(dev); 
+	outb(HOST_CMD_RESTRT_TX, ioaddr+HOST_CMD);   /* card ignores this on RX restart */ 
 	
-	/* Flush */
-	if(lp->tx_skb_top!=lp->tx_skb_end)
-	{
-		int i;
-		if(lp->tx_skb_top<=lp->tx_skb_end)
-		{
-			for(i=lp->tx_skb_top;i<lp->tx_skb_end;i++)
-			{
-				dev_kfree_skb(lp->tx_skb[i]);
-				lp->tx_skb[i]=NULL;
-			}
-		}
-		else
-		{
-			for(i=lp->tx_skb_end;i<TX_RING_MAX;i++)
-			{
-				dev_kfree_skb(lp->tx_skb[i]);
-				lp->tx_skb[i]=NULL;
-			}
-			for(i=0;i<lp->tx_skb_top;i++)
-			{
-				dev_kfree_skb(lp->tx_skb[i]);
-				lp->tx_skb[i]=NULL;
-			}
-		}
-	}
-	lp->tx_skb_top=lp->tx_skb_end=0;
+	/* We are not interrupted on start completion */ 
+	lp->xceiver_state=RUNNING; 
 }
 
+
 /**
- *	mc32_tx_begin:
- *	@dev: 3c527 to enable
+ *	mc32_halt_transceiver	-	tell board to stop tx/rx
+ *	@dev: The 3c527 card to issue the command to
  *
- *	We wait for any pending command to complete and then issue 
- *	a start transmit command to the board itself. At this point 
- *	transmit handling continues as it was before. The ring must
- *	be setup before you do this and must have an end marker in it.
- *	It turns out we can avoid issuing this specific command when
- *	doing our setup so we avoid it.
- */
- 
-static void mc32_tx_begin(struct net_device *dev)
+ *	We issue the commands to halt the card's transceiver. In fact,
+ *	after some experimenting we now simply tell the card to
+ *	suspend. When issuing aborts occasionally odd things happened.
+ *
+ *	We then sleep until the card has notified us that both rx and
+ *	tx have been suspended.
+ */ 
+
+static void mc32_halt_transceiver(struct net_device *dev) 
 {
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	int ioaddr = dev->base_addr;
-	
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-	
+	unsigned long flags;
+
+	mc32_ready_poll(dev);	
+
 	lp->tx_box->mbox=0;
-#if 0	
-	outb(5, ioaddr+HOST_CMD);	/* GO */
-	printk("TX=>5\n");
-	mc32_ring_poll(dev);	
-	if(lp->tx_box->mbox&(1<<13))
-		printk("TX begin error!\n");
-#endif		
-	lp->tx_halted=0;
-}
+	lp->rx_box->mbox=0;
+
+	outb(HOST_CMD_SUSPND_RX, ioaddr+HOST_CMD);			
+	mc32_ready_poll(dev); 
+	outb(HOST_CMD_SUSPND_TX, ioaddr+HOST_CMD);	
+		
+	save_flags(flags);
+	cli();
+		
+	while(lp->xceiver_state!=HALTED) 
+		sleep_on(&lp->event); 
+		
+	restore_flags(flags);	
+} 
+
 
-	
 /**
- *	mc32_load_rx_ring:
+ *	mc32_load_rx_ring	-	load the ring of receive buffers
  *	@dev: 3c527 to build the ring for
  *
- *	The card setups up the receive ring for us. We are required to
- *	use the ring it provides although we can change the size of the
- *	ring.
- *
- *	We allocate an sk_buff for each ring entry in turn and set the entry
- *	up for a single non s/g buffer. The first buffer we mark with the
- *	end marker bits. Finally we clear the rx mailbox.
+ *	This initalises the on-card and driver datastructures to
+ *	the point where mc32_start_transceiver() can be called.
+ *
+ *	The card sets up the receive ring for us. We are required to use the
+ *	ring it provides although we can change the size of the ring.
+ *
+ * 	We allocate an sk_buff for each ring entry in turn and
+ * 	initalise its house-keeping info. At the same time, we read
+ * 	each 'next' pointer in our rx_ring array. This reduces slow
+ * 	shared-memory reads and makes it easy to access predecessor
+ * 	descriptors.
+ *
+ *	We then set the end-of-list bit for the last entry so that the
+ * 	card will know when it has run out of buffers.
  */
- 
+	 
 static int mc32_load_rx_ring(struct net_device *dev)
 {
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	int i;
-	u16 base;
+	u16 rx_base;
 	volatile struct skb_header *p;
 	
-	base = lp->rx_box->data[0];
-	
-	/* Fix me - should use card size - also fix flush ! */ 
+	rx_base=lp->rx_chain;
 
-	for(i=0;i<RX_RING_MAX;i++)
+	for(i=0;i<RX_RING_LEN;i++)
 	{
-		lp->rx_skb[i]=alloc_skb(1532, GFP_KERNEL);
-		if(lp->rx_skb[i]==NULL)
+		lp->rx_ring[i].skb=alloc_skb(1532, GFP_KERNEL);
+		skb_reserve(lp->rx_ring[i].skb, 18);  
+
+		if(lp->rx_ring[i].skb==NULL)
 		{
 			for(;i>=0;i--)
-				kfree_skb(lp->rx_skb[i]);
+				kfree_skb(lp->rx_ring[i].skb);
 			return -ENOBUFS;
 		}
-		lp->rx_ptr[i]=lp->rx_skb[i]->data+18;
 		
-		p=bus_to_virt(lp->base+base);
+		p=bus_to_virt(lp->base+rx_base);
+				
 		p->control=0;
-		p->data = virt_to_bus(lp->rx_ptr[i]);
+		p->data=virt_to_bus(lp->rx_ring[i].skb->data);
 		p->status=0;
-		p->length = 1532;
-		base = p->next;
+		p->length=1532;
+	
+		lp->rx_ring[i].p=p; 
+		rx_base=p->next; 
 	}
-	p->control = (1<<6);
-	lp->rx_box->mbox = 0;
+
+	lp->rx_ring[i-1].p->control |= CONTROL_EOL;
+
+	lp->rx_ring_tail=0;
+
 	return 0;
 }	
 
+
 /**
- *	mc32_flush_rx_ring:
+ *	mc32_flush_rx_ring	-	free the ring of receive buffers
  *	@lp: Local data of 3c527 to flush the rx ring of
  *
- *	Free the buffer for each ring slot. Because of the receive 
- *	algorithm we use the ring will always be loaded will a full set
- *	of buffers.
+ *	Free the buffer for each ring slot. This may be called 
+ *      before mc32_load_rx_ring(), eg. on error in mc32_open().
  */
 
-static void mc32_flush_rx_ring(struct mc32_local *lp)
+static void mc32_flush_rx_ring(struct net_device *dev)
 {
-	int i;
-	for(i=0;i<RX_RING_MAX;i++)
-		kfree_skb(lp->rx_skb[i]);
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	
+	struct sk_buff *skb;
+	int i; 
+
+	for(i=0; i < RX_RING_LEN; i++) 
+	{ 
+		skb = lp->rx_ring[i].skb;
+		if (skb!=NULL) {
+			kfree_skb(skb);
+			skb=NULL; 
+		}
+		lp->rx_ring[i].p=NULL; 
+	} 
 }
 
+
 /**
- *	mc32_flush_tx_ring:
+ *	mc32_load_tx_ring	-	load transmit ring
+ *	@dev: The 3c527 card to issue the command to
+ *
+ *	This sets up the host transmit data-structures. 
+ *
+ *	First, we obtain from the card it's current postion in the tx
+ *	ring, so that we will know where to begin transmitting
+ *	packets.
+ * 	
+ * 	Then, we read the 'next' pointers from the on-card tx ring into
+ *  	our tx_ring array to reduce slow shared-mem reads. Finally, we
+ * 	intitalise the tx house keeping variables.
+ * 
+ */ 
+
+static void mc32_load_tx_ring(struct net_device *dev)
+{ 
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	volatile struct skb_header *p;
+	int i; 
+	u16 tx_base;
+
+	tx_base=lp->tx_box->data[0]; 
+
+	for(i=0;i<lp->tx_len;i++) 
+	{
+		p=bus_to_virt(lp->base+tx_base);
+		lp->tx_ring[i].p=p; 
+		lp->tx_ring[i].skb=NULL;
+
+		tx_base=p->next;
+	}
+
+	/* -1 so that tx_ring_head cannot "lap" tx_ring_tail,           */
+	/* which would be bad news for mc32_tx_ring as cur. implemented */ 
+
+	atomic_set(&lp->tx_count, TX_RING_LEN-1); 
+	lp->tx_ring_head=lp->tx_ring_tail=0; 
+} 
+
+
+/**
+ *	mc32_flush_tx_ring 	-	free transmit ring
  *	@lp: Local data of 3c527 to flush the tx ring of
  *
  *	We have to consider two cases here. We want to free the pending
  *	buffers only. If the ring buffer head is past the start then the
- *	ring segment we wish to free wraps through zero.
+ *	ring segment we wish to free wraps through zero. The tx ring 
+ *	house-keeping variables are then reset.
  */
 
-static void mc32_flush_tx_ring(struct mc32_local *lp)
+static void mc32_flush_tx_ring(struct net_device *dev)
 {
-	int i;
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	
-	if(lp->tx_skb_top <= lp->tx_skb_end)
-	{
-		for(i=lp->tx_skb_top;i<lp->tx_skb_end;i++)
-			dev_kfree_skb(lp->tx_skb[i]);
-	}
-	else
+	if(lp->tx_ring_tail!=lp->tx_ring_head)
 	{
-		for(i=0;i<lp->tx_skb_end;i++)
-			dev_kfree_skb(lp->tx_skb[i]);
-		for(i=lp->tx_skb_top;i<TX_RING_MAX;i++)
-			dev_kfree_skb(lp->tx_skb[i]);
+		int i;	
+		if(lp->tx_ring_tail < lp->tx_ring_head)
+		{
+			for(i=lp->tx_ring_tail;i<lp->tx_ring_head;i++)
+			{
+				dev_kfree_skb(lp->tx_ring[i].skb);
+				lp->tx_ring[i].skb=NULL;
+				lp->tx_ring[i].p=NULL; 
+			}
+		}
+		else
+		{
+			for(i=lp->tx_ring_tail; i<TX_RING_LEN; i++) 
+			{
+				dev_kfree_skb(lp->tx_ring[i].skb);
+				lp->tx_ring[i].skb=NULL;
+				lp->tx_ring[i].p=NULL; 
+			}
+			for(i=0; i<lp->tx_ring_head; i++) 
+			{
+				dev_kfree_skb(lp->tx_ring[i].skb);
+				lp->tx_ring[i].skb=NULL;
+				lp->tx_ring[i].p=NULL; 
+			}
+		}
 	}
+	
+	atomic_set(&lp->tx_count, 0); 
+	lp->tx_ring_tail=lp->tx_ring_head=0;
 }
  	
+
 /**
- *	mc32_open
+ *	mc32_open	-	handle 'up' of card
  *	@dev: device to open
  *
  *	The user is trying to bring the card into ready state. This requires
@@ -832,9 +926,10 @@
  *	'indications'. Without these enabled the card doesn't bother telling
  *	us what it has done. This had me puzzled for a week.
  *
- *	We then load the network address and multicast filters. Turn on the
- *	workaround mode. This works around a bug in the 82586 - it asks the
- *	firmware to do so. It has a performance hit but is needed on busy
+ *	We configure the number of card descriptors, then load the network
+ *	address and multicast filters. Turn on the workaround mode. This
+ *	works around a bug in the 82586 - it asks the firmware to do
+ *	so. It has a performance (latency) hit but is needed on busy
  *	[read most] lans. We load the ring with buffers then we kick it
  *	all off.
  */
@@ -842,10 +937,11 @@
 static int mc32_open(struct net_device *dev)
 {
 	int ioaddr = dev->base_addr;
-	u16 zero_word=0;
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
 	u8 one=1;
 	u8 regs;
-	
+	u16 descnumbuffs[2] = {TX_RING_LEN, RX_RING_LEN};
+
 	/*
 	 *	Interrupts enabled
 	 */
@@ -861,46 +957,64 @@
 
 	mc32_command(dev, 4, &one, 2);
 
-	 	
 	/*
-	 *	Send the command sequence "abort, resume" for RX and TX.
-	 *	The abort cleans up the buffer chains if needed.
+	 *	Poke it to make sure it's really dead. 
 	 */
 
-	mc32_rx_abort(dev);
-	mc32_tx_abort(dev);
-	
+	mc32_halt_transceiver(dev); 
+	mc32_flush_tx_ring(dev); 
+
+	/* 
+	 *	Ask card to set up on-card descriptors to our spec 
+	 */ 
+
+	if(mc32_command(dev, 8, descnumbuffs, 4)) { 
+		printk("%s: %s rejected our buffer configuration!\n",
+	 	       dev->name, cardname);
+		mc32_close(dev); 
+		return -ENOBUFS; 
+	}
+	
+	/* Report new configuration */ 
+	mc32_command(dev, 6, NULL, 0); 
+
+	lp->tx_chain 		= lp->exec_box->data[8];   /* Transmit list start offset */
+	lp->rx_chain 		= lp->exec_box->data[10];  /* Receive list start offset */
+	lp->tx_len 		= lp->exec_box->data[9];   /* Transmit list count */ 
+	lp->rx_len 		= lp->exec_box->data[11];  /* Receive list count */
+ 
 	/* Set Network Address */
 	mc32_command(dev, 1, dev->dev_addr, 6);
 	
 	/* Set the filters */
 	mc32_set_multicast_list(dev);
+		   
+	if (WORKAROUND_82586) { 
+		u16 zero_word=0;
+		mc32_command(dev, 0x0D, &zero_word, 2);   /* 82586 bug workaround on  */
+	}
+
+	mc32_load_tx_ring(dev);
 	
-	/* Issue the 82586 workaround command - this is for "busy lans",
-	   but basically means for all lans now days - has a performance
-	   cost but best set */
-	   
-	mc32_command(dev, 0x0D, &zero_word, 2); /* 82586 bug workaround on */
-	
-	/* Load the ring we just initialised */
-	
-	if(mc32_load_rx_ring(dev))
+	if(mc32_load_rx_ring(dev)) 
 	{
 		mc32_close(dev);
 		return -ENOBUFS;
 	}
+
+	lp->desired_state = RUNNING; 
 	
-	/* And the resume command goes last */
-	
-	mc32_rx_begin(dev);
-	mc32_tx_begin(dev);
+	/* And finally, set the ball rolling... */
+	mc32_start_transceiver(dev);
+
+	netif_start_queue(dev);
 
-	netif_start_queue(dev);	
 	return 0;
 }
 
+
 /**
- *	mc32_timeout:
+ *	mc32_timeout	-	handle a timeout from the network layer
  *	@dev: 3c527 that timed out
  *
  *	Handle a timeout on transmit from the 3c527. This normally means
@@ -915,9 +1029,10 @@
 	/* Try to restart the adaptor. */
 	netif_wake_queue(dev);
 }
- 
+
+
 /**
- *	mc32_send_packet:
+ *	mc32_send_packet	-	queue a frame for transmit
  *	@skb: buffer to transmit
  *	@dev: 3c527 to send it out of
  *
@@ -931,18 +1046,16 @@
  *	MCA machine I don't plan to change it. It is probably the top 
  *	performance hit for this driver on SMP however.
  */
- 
+
 static int mc32_send_packet(struct sk_buff *skb, struct net_device *dev)
 {
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
-	int ioaddr = dev->base_addr;
 	unsigned long flags;
-		
-	u16 tx_head;
+
 	volatile struct skb_header *p, *np;
 
 	netif_stop_queue(dev);
-	
+
 	save_flags(flags);
 	cli();
 		
@@ -952,204 +1065,303 @@
 		return 1;
 	}
 
-	tx_head = lp->tx_box->data[0];
-	atomic_dec(&lp->tx_count);
-	/* We will need this to flush the buffer out */
-	
-	lp->tx_skb[lp->tx_skb_end] = skb;
-	lp->tx_skb_end++;
-	lp->tx_skb_end&=(TX_RING_MAX-1);
-
-	/* TX suspend - shouldnt be needed but apparently is.
-	   This is a research item ... */
-		   
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-	lp->tx_box->mbox=0;
-	outb(3, ioaddr+HOST_CMD);
-	
-	/* Transmit now stopped */
+	atomic_dec(&lp->tx_count); 
 
 	/* P is the last sending/sent buffer as a pointer */
-	p=(struct skb_header *)bus_to_virt(lp->base+tx_head);
-	
-	/* NP is the buffer we will be loading */
-	np=(struct skb_header *)bus_to_virt(lp->base+p->next);
-		
-	np->control	|= (1<<6);	/* EOL */
-	wmb();
+	p=lp->tx_ring[lp->tx_ring_head].p; 
 		
-	np->length	= skb->len;
-		
-	if(np->length < 60)
-		np->length = 60;
+	lp->tx_ring_head=next_tx(lp->tx_ring_head); 
+
+	/* NP is the buffer we will be loading */
+	np=lp->tx_ring[lp->tx_ring_head].p; 
+
+	/* We will need this to flush the buffer out */
+	lp->tx_ring[lp->tx_ring_head].skb=skb;
+   	   
+	np->length = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; 
 			
 	np->data	= virt_to_bus(skb->data);
 	np->status	= 0;
-	np->control	= (1<<7)|(1<<6);	/* EOP EOL */
+	np->control     = CONTROL_EOP | CONTROL_EOL;     
 	wmb();
 		
-	p->status	= 0;
-	p->control	&= ~(1<<6);
+	p->control     &= ~CONTROL_EOL;     /* Clear EOL on p */ 
 	
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-	lp->tx_box->mbox=0;
-	outb(5, ioaddr+HOST_CMD);		/* Restart TX */
 	restore_flags(flags);
-	
+
 	netif_wake_queue(dev);
 	return 0;
 }
 
+
 /**
- *	mc32_update_stats:
+ *	mc32_update_stats	-	pull off the on board statistics
  *	@dev: 3c527 to service
  *
- *	When the board signals us that its statistics need attention we
- *	should query the table and clear it. In actual fact we currently
- *	track all our statistics in software and I haven't implemented it yet.
+ * 
+ *	Query and reset the on-card stats. There's the small possibility
+ *	of a race here, which would result in an underestimation of
+ *	actual errors. As such, we'd prefer to keep all our stats
+ *	collection in software. As a rule, we do. However it can't be
+ *	used for rx errors and collisions as, by default, the card discards
+ *	bad rx packets. 
+ *
+ *	Setting the SAV BP in the rx filter command supposedly
+ *	stops this behaviour. However, testing shows that it only seems to
+ *	enable the collation of on-card rx statistics --- the driver
+ *	never sees an RX descriptor with an error status set.
+ *
  */
- 
+
 static void mc32_update_stats(struct net_device *dev)
 {
-}
+	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	volatile struct mc32_stats *st = lp->stats; 
+
+	u32 rx_errors=0; 
+      
+	rx_errors+=lp->net_stats.rx_crc_errors   +=st->rx_crc_errors;         
+	                                           st->rx_crc_errors=0;
+	rx_errors+=lp->net_stats.rx_fifo_errors  +=st->rx_overrun_errors;   
+	                                           st->rx_overrun_errors=0; 
+	rx_errors+=lp->net_stats.rx_frame_errors +=st->rx_alignment_errors; 
+ 	                                           st->rx_alignment_errors=0;
+	rx_errors+=lp->net_stats.rx_length_errors+=st->rx_tooshort_errors; 
+	                                           st->rx_tooshort_errors=0;
+	rx_errors+=lp->net_stats.rx_missed_errors+=st->rx_outofresource_errors;
+	                                           st->rx_outofresource_errors=0; 
+        lp->net_stats.rx_errors=rx_errors; 
+						   
+	/* Number of packets which saw one collision */
+	lp->net_stats.collisions+=st->dataC[10];
+	st->dataC[10]=0; 
+
+	/* Number of packets which saw 2--15 collisions */ 
+	lp->net_stats.collisions+=st->dataC[11]; 
+	st->dataC[11]=0; 
+}	
+
 
 /**
- *	mc32_rx_ring:
+ *	mc32_rx_ring	-	process the receive ring
  *	@dev: 3c527 that needs its receive ring processing
  *
- *	We have received one or more indications from the card that
- *	a receive has completed. The ring buffer thus contains dirty
- *	entries. Firstly we tell the card to stop receiving, then We walk 
- *	the ring from the first filled entry, which is pointed to by the 
- *	card rx mailbox and for each completed packet we will either copy 
- *	it and pass it up the stack or if the packet is near MTU sized we 
- *	allocate another buffer and flip the old one up the stack.
  *
+ *	We have received one or more indications from the card that a
+ *	receive has completed. The buffer ring thus contains dirty
+ *	entries. We walk the ring by iterating over the circular rx_ring
+ *	array, starting at the next dirty buffer (which happens to be the
+ *	one we finished up at last time around).
+ *
+ *	For each completed packet, we will either copy it and pass it up
+ * 	the stack or, if the packet is near MTU sized, we allocate
+ *	another buffer and flip the old one up the stack.
+ * 
  *	We must succeed in keeping a buffer on the ring. If neccessary we
- *	will toss a received packet rather than lose a ring entry. Once the
- *	first packet that is unused is found we reload the mailbox with the
- *	buffer so that the card knows it can use the buffers again. Finally
- *	we set it receiving again. 
- *
- *	We must stop reception during the ring walk. I thought it would be
- *	neat to avoid it by clever tricks, but it turns out the event order
- *	on the card means you have to play by the manual.
+ *	will toss a received packet rather than lose a ring entry. Once
+ *	the first uncompleted descriptor is found, we move the
+ *	End-Of-List bit to include the buffers just processed.
+ *
  */
- 
+
 static void mc32_rx_ring(struct net_device *dev)
 {
-	struct mc32_local *lp=dev->priv;
-	int ioaddr = dev->base_addr;
-	int x=0;
+	struct mc32_local *lp=dev->priv;		
 	volatile struct skb_header *p;
-	u16 base;
-	u16 top;
+	u16 rx_ring_tail = lp->rx_ring_tail;
+	u16 rx_old_tail = rx_ring_tail; 
 
-	/* Halt RX before walking the ring */
-	
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-	outb(3<<3, ioaddr+HOST_CMD);
-	while(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR);
+	int x=0;
 	
-	top = base = lp->rx_box->data[0];
 	do
-	{
-		p=(struct skb_header *)bus_to_virt(base+lp->base);
-		if(!(p->status & (1<<7)))
+	{ 
+		p=lp->rx_ring[rx_ring_tail].p; 
+
+		if(!(p->status & (1<<7))) { /* Not COMPLETED */ 
 			break;
-		if(p->status & (1<<6))
-		{
-			u16 length = p->length;
-			struct sk_buff *skb=dev_alloc_skb(length+2);
-			if(skb!=NULL)
+		} 
+		if(p->status & (1<<6)) /* COMPLETED_OK */
+		{		        
+
+			u16 length=p->length;
+			struct sk_buff *skb; 
+			struct sk_buff *newskb; 
+
+			/* Try to save time by avoiding a copy on big frames */
+
+			if ((length > RX_COPYBREAK) 
+			    && ((newskb=dev_alloc_skb(1532)) != NULL)) 
+			{ 
+				skb=lp->rx_ring[rx_ring_tail].skb;
+				skb_put(skb, length);
+				
+				skb_reserve(newskb,18); 
+				lp->rx_ring[rx_ring_tail].skb=newskb;  
+				p->data=virt_to_bus(newskb->data);  
+			} 
+			else 
 			{
+				skb=dev_alloc_skb(length+2);  
+
+				if(skb==NULL) {
+					lp->net_stats.rx_dropped++; 
+					goto dropped; 
+				}
+
 				skb_reserve(skb,2);
-				/*printk("Frame at %p\n", bus_to_virt(p->data)); */
 				memcpy(skb_put(skb, length),
-					bus_to_virt(p->data), length);
-				skb->protocol=eth_type_trans(skb,dev);
-				skb->dev=dev;
-				netif_rx(skb);
-				dev->last_rx = jiffies;
-				lp->net_stats.rx_packets++;
-				lp->net_stats.rx_bytes += length;
+				       lp->rx_ring[rx_ring_tail].skb->data, length);
 			}
-			else
-				lp->net_stats.rx_dropped++;
+			
+			skb->protocol=eth_type_trans(skb,dev); 
+			skb->dev=dev; 
+			dev->last_rx = jiffies;
+ 			lp->net_stats.rx_packets++; 
+ 			lp->net_stats.rx_bytes += length; 
+			netif_rx(skb);
 		}
-		else
+
+	dropped:
+		p->length = 1532; 
+		p->status = 0;
+		
+		rx_ring_tail=next_rx(rx_ring_tail); 
+	}
+        while(x++<48);  
+
+	/* If there was actually a frame to be processed, place the EOL bit */ 
+	/* at the descriptor prior to the one to be filled next */ 
+
+	if (rx_ring_tail != rx_old_tail) 
+	{ 
+		lp->rx_ring[prev_rx(rx_ring_tail)].p->control |=  CONTROL_EOL; 
+		lp->rx_ring[prev_rx(rx_old_tail)].p->control  &= ~CONTROL_EOL; 
+
+		lp->rx_ring_tail=rx_ring_tail; 
+	}
+}
+
+
+/**
+ *	mc32_tx_ring	-	process completed transmits
+ *	@dev: 3c527 that needs its transmit ring processing
+ *
+ *
+ *	This operates in a similar fashion to mc32_rx_ring. We iterate
+ *	over the transmit ring. For each descriptor which has been
+ *	processed by the card, we free its associated buffer and note
+ *	any errors. This continues until the transmit ring is emptied
+ *	or we reach a descriptor that hasn't yet been processed by the
+ *	card.
+ * 
+ */
+
+static void mc32_tx_ring(struct net_device *dev) 
+{
+	struct mc32_local *lp=(struct mc32_local *)dev->priv;
+	volatile struct skb_header *np;
+
+	/* NB: lp->tx_count=TX_RING_LEN-1 so that tx_ring_head cannot "lap" tail here */
+
+	while (lp->tx_ring_tail != lp->tx_ring_head)  
+	{   
+		u16 t; 
+
+		t=next_tx(lp->tx_ring_tail); 
+		np=lp->tx_ring[t].p; 
+
+		if(!(np->status & (1<<7))) 
+		{
+			/* Not COMPLETED */ 
+			break; 
+		} 
+		lp->net_stats.tx_packets++;
+		if(!(np->status & (1<<6))) /* Not COMPLETED_OK */
 		{
-			lp->net_stats.rx_errors++;
-			switch(p->status&0x0F)
+			lp->net_stats.tx_errors++;   
+
+			switch(np->status&0x0F)
 			{
 				case 1:
-					lp->net_stats.rx_crc_errors++;break;
+					lp->net_stats.tx_aborted_errors++;
+					break; /* Max collisions */ 
 				case 2:
-					lp->net_stats.rx_fifo_errors++;break;
+					lp->net_stats.tx_fifo_errors++;
+					break;
 				case 3:
-					lp->net_stats.rx_frame_errors++;break;
+					lp->net_stats.tx_carrier_errors++;
+					break;
 				case 4:
-					lp->net_stats.rx_missed_errors++;break;
+					lp->net_stats.tx_window_errors++;
+					break;  /* CTS Lost */ 
 				case 5:
-					lp->net_stats.rx_length_errors++;break;
+					lp->net_stats.tx_aborted_errors++;
+					break; /* Transmit timeout */ 
 			}
 		}
-		p->length = 1532;
-		p->control &= ~(1<<6);
-		p->status = 0;
-		base = p->next;
+		/* Packets are sent in order - this is
+		    basically a FIFO queue of buffers matching
+		    the card ring */
+		lp->net_stats.tx_bytes+=lp->tx_ring[t].skb->len;
+		dev_kfree_skb_irq(lp->tx_ring[t].skb);
+		lp->tx_ring[t].skb=NULL;
+		atomic_inc(&lp->tx_count);
+		netif_wake_queue(dev);
+
+		lp->tx_ring_tail=t; 
 	}
-	while(x++<48);
 
-	/*
-	 *	Restart ring processing
-	 */	
-	
-	while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-	lp->rx_box->mbox=0;
-	lp->rx_box->data[0] = top;
-	outb(1<<3, ioaddr+HOST_CMD);	
-	lp->rx_halted=0;
-}
+} 
 
 
 /**
- *	mc32_interrupt:
+ *	mc32_interrupt		-	handle an interrupt from a 3c527
  *	@irq: Interrupt number
  *	@dev_id: 3c527 that requires servicing
  *	@regs: Registers (unused)
  *
- *	The 3c527 interrupts us for four reasons. The command register 
- *	contains the message it wishes to send us packed into a single
- *	byte field. We keep reading status entries until we have processed
- *	all the transmit and control items, but simply count receive
- *	reports. When the receive reports are in we can call the mc32_rx_ring
- *	and empty the ring. This saves the overhead of multiple command requests
+ *
+ *	An interrupt is raised whenever the 3c527 writes to the command
+ *	register. This register contains the message it wishes to send us
+ *	packed into a single byte field. We keep reading status entries
+ *	until we have processed all the control items, but simply count
+ *	transmit and receive reports. When all reports are in we empty the
+ *	transceiver rings as appropriate. This saves the overhead of
+ *	multiple command requests.
+ *
+ *	Because MCA is level-triggered, we shouldn't miss indications.
+ *	Therefore, we needn't ask the card to suspend interrupts within
+ *	this handler. The card receives an implicit acknowledgment of the
+ *	current interrupt when we read the command register.
+ *
  */
- 
+
 static void mc32_interrupt(int irq, void *dev_id, struct pt_regs * regs)
 {
 	struct net_device *dev = dev_id;
 	struct mc32_local *lp;
 	int ioaddr, status, boguscount = 0;
+	int rx_event = 0;
+	int tx_event = 0; 
 	
 	if (dev == NULL) {
 		printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq);
 		return;
 	}
+ 
 	ioaddr = dev->base_addr;
 	lp = (struct mc32_local *)dev->priv;
 
 	/* See whats cooking */
-	
-	while((inb(ioaddr+2)&(1<<5)) && boguscount++<2000)
+
+	while((inb(ioaddr+HOST_STATUS)&HOST_STATUS_CWR) && boguscount++<2000)
 	{
 		status=inb(ioaddr+HOST_CMD);
 
 #ifdef DEBUG_IRQ		
-		printk("Status TX%d RX%d EX%d OV%d\n",
+		printk("Status TX%d RX%d EX%d OV%d BC%d\n",
 			(status&7), (status>>3)&7, (status>>6)&1,
-			(status>>7)&1);
+			(status>>7)&1, boguscount);
 #endif
 			
 		switch(status&7)
@@ -1157,32 +1369,16 @@
 			case 0:
 				break;
 			case 6: /* TX fail */
-				lp->net_stats.tx_errors++;
 			case 2:	/* TX ok */
-				lp->net_stats.tx_packets++;
-				/* Packets are sent in order - this is
-				   basically a FIFO queue of buffers matching
-				   the card ring */
-				lp->net_stats.tx_bytes+=lp->tx_skb[lp->tx_skb_top]->len;
-				dev_kfree_skb_irq(lp->tx_skb[lp->tx_skb_top]);
-				lp->tx_skb[lp->tx_skb_top]=NULL;
-				lp->tx_skb_top++;
-				lp->tx_skb_top&=(TX_RING_MAX-1);
-				atomic_inc(&lp->tx_count);
-				netif_wake_queue(dev);
+				tx_event = 1; 
 				break;
 			case 3: /* Halt */
 			case 4: /* Abort */
-				lp->tx_halted=1;
-				wake_up(&lp->event);
-				break;
-			case 5:
-				lp->tx_halted=0;
+				lp->xceiver_state |= TX_HALTED; 
 				wake_up(&lp->event);
 				break;
 			default:
-				printk("%s: strange tx ack %d\n", 
-					dev->name, status&7);
+				printk("%s: strange tx ack %d\n", dev->name, status&7);
 		}
 		status>>=3;
 		switch(status&7)
@@ -1190,87 +1386,87 @@
 			case 0:
 				break;
 			case 2:	/* RX */
-				lp->rx_pending=1;
-				if(!lp->rx_halted)
-				{
-					/*
-					 *	Halt ring receive
-					 */
-					while(!(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR));
-					outb(3<<3, ioaddr+HOST_CMD);
-				}
+				rx_event=1; 
 				break;
-			case 3:
-			case 4:
-				lp->rx_halted=1;
-				wake_up(&lp->event);
-				break;
-			case 5:
-				lp->rx_halted=0;
+			case 3: /* Halt */
+			case 4: /* Abort */
+				lp->xceiver_state |= RX_HALTED;
 				wake_up(&lp->event);
 				break;
 			case 6:
 				/* Out of RX buffers stat */
+				/* Must restart rx */
 				lp->net_stats.rx_dropped++;
-				lp->rx_pending=1;
-				/* Must restart */
-				lp->rx_halted=1;
+				mc32_rx_ring(dev); 
+				mc32_start_transceiver(dev); 
 				break;
 			default:
 				printk("%s: strange rx ack %d\n", 
-					dev->name, status&7);
-			
+					dev->name, status&7);			
 		}
 		status>>=3;
 		if(status&1)
 		{
+
 			/* 0=no 1=yes 2=replied, get cmd, 3 = wait reply & dump it */
-			if(lp->exec_pending!=3)
+			
+			if(lp->exec_pending!=3) {
 				lp->exec_pending=2;
-			else
-				lp->exec_pending=0;
-			wake_up(&lp->event);
+				wake_up(&lp->event);
+			}
+			else 
+			{				
+			  	lp->exec_pending=0;
+
+				/* A new multicast set may have been
+				   blocked while the old one was
+				   running. If so, do it now. */
+				   
+				if (lp->mc_reload_wait) 
+					mc32_reset_multicast_list(dev);
+				else 
+					wake_up(&lp->event);			       
+			}
 		}
 		if(status&2)
 		{
 			/*
-			 *	Update the stats as soon as
-			 *	we have it flagged and can 
-			 *	send an immediate reply (CRR set)
+			 *	We get interrupted once per
+			 *	counter that is about to overflow. 
 			 */
-			 
-			if(inb(ioaddr+HOST_STATUS)&HOST_STATUS_CRR)
-			{
-				mc32_update_stats(dev);
-				outb(0, ioaddr+HOST_CMD);
-			}
+
+			mc32_update_stats(dev);			
 		}
 	}
-	
+
+
 	/*
-	 *	Process and restart the receive ring. This has some state
-	 *	as we must halt the ring to process it and halting the ring
-	 *	might not occur in the same IRQ handling loop as we issue
-	 *	the halt.
-	 */
+	 *	Process the transmit and receive rings 
+         */
+
+	if(tx_event) 
+		mc32_tx_ring(dev);
 	 
-	if(lp->rx_pending && lp->rx_halted)
-	{
+	if(rx_event) 
 		mc32_rx_ring(dev);
-		lp->rx_pending = 0;
-	}
+
 	return;
 }
 
 
 /**
- *	mc32_close:
+ *	mc32_close	-	user configuring the 3c527 down
  *	@dev: 3c527 card to shut down
  *
  *	The 3c527 is a bus mastering device. We must be careful how we
  *	shut it down. It may also be running shared interrupt so we have
  *	to be sure to silence it properly
  *
+ *	We indicate that the card is closing to the rest of the
+ *	driver.  Otherwise, it is possible that the card may run out
+ *	of receive buffers and restart the transceiver while we're
+ *	trying to close it.
+ * 
  *	We abort any receive and transmits going on and then wait until
  *	any pending exec commands have completed in other code threads.
  *	In theory we can't get here while that is true, in practice I am
@@ -1279,93 +1475,104 @@
  *	We turn off the interrupt enable for the board to be sure it can't
  *	intefere with other devices.
  */
- 
+
 static int mc32_close(struct net_device *dev)
 {
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+
 	int ioaddr = dev->base_addr;
 	u8 regs;
 	u16 one=1;
-
-	netif_stop_queue(dev);
 	
+	lp->desired_state = HALTED;
+	netif_stop_queue(dev);
+
 	/*
 	 *	Send the indications on command (handy debug check)
 	 */
 
 	mc32_command(dev, 4, &one, 2);
 
-	/* Abort RX and Abort TX */
-	
-	mc32_rx_abort(dev);	
-	mc32_tx_abort(dev);
+	/* Shut down the transceiver */
+
+	mc32_halt_transceiver(dev); 
 	
 	/* Catch any waiting commands */
 	
 	while(lp->exec_pending==1)
 		sleep_on(&lp->event);
-		
+	       
 	/* Ok the card is now stopping */	
 	
 	regs=inb(ioaddr+HOST_CTRL);
 	regs&=~HOST_CTRL_INTE;
 	outb(regs, ioaddr+HOST_CTRL);
 
-	mc32_flush_rx_ring(lp);
-	mc32_flush_tx_ring(lp);
-	
-	/* Update the statistics here. */
+	mc32_flush_rx_ring(dev);
+	mc32_flush_tx_ring(dev);
+		
+	mc32_update_stats(dev); 
 
 	return 0;
-
 }
 
+
 /**
- *	mc32_get_stats:
+ *	mc32_get_stats		-	hand back stats to network layer
  *	@dev: The 3c527 card to handle
  *
- *	As we currently handle our statistics in software this one is
- *	easy to handle. With hardware statistics it will get messy
- *	as the get_stats call will need to send exec mailbox messages and
- *	need to lock out the multicast reloads.
+ *	We've collected all the stats we can in software already. Now
+ *	it's time to update those kept on-card and return the lot. 
+ * 
  */
 
 static struct net_device_stats *mc32_get_stats(struct net_device *dev)
 {
-	struct mc32_local *lp = (struct mc32_local *)dev->priv;
+	struct mc32_local *lp;
+	
+	mc32_update_stats(dev); 
+
+	lp = (struct mc32_local *)dev->priv;
+
 	return &lp->net_stats;
 }
 
+
 /**
- *	do_mc32_set_multicast_list:
+ *	do_mc32_set_multicast_list	-	attempt to update multicasts
  *	@dev: 3c527 device to load the list on
  *	@retry: indicates this is not the first call. 
  *
- * Actually set or clear the multicast filter for this adaptor. The locking
- * issues are handled by this routine. We have to track state as it may take
- * multiple calls to get the command sequence completed. We just keep trying
- * to schedule the loads until we manage to process them all.
  *
- * num_addrs == -1	Promiscuous mode, receive all packets
+ * 	Actually set or clear the multicast filter for this adaptor. The
+ *	locking issues are handled by this routine. We have to track
+ *	state as it may take multiple calls to get the command sequence
+ *	completed. We just keep trying to schedule the loads until we
+ *	manage to process them all.
+ * 
+ *	num_addrs == -1	Promiscuous mode, receive all packets
+ * 
+ *	num_addrs == 0	Normal mode, clear multicast list
+ * 
+ *	num_addrs > 0	Multicast mode, receive normal and MC packets, 
+ *			and do best-effort filtering. 
  *
- * num_addrs == 0	Normal mode, clear multicast list
+ *	See mc32_update_stats() regards setting the SAV BP bit. 
  *
- * num_addrs > 0	Multicast mode, receive normal and MC packets,
- *			and do best-effort filtering.
  */
 
 static void do_mc32_set_multicast_list(struct net_device *dev, int retry)
 {
 	struct mc32_local *lp = (struct mc32_local *)dev->priv;
-	u16 filt;
+	u16 filt = (1<<2); /* Save Bad Packets, for stats purposes */ 
 
 	if (dev->flags&IFF_PROMISC)
 		/* Enable promiscuous mode */
-		filt = 1;
+		filt |= 1;
 	else if((dev->flags&IFF_ALLMULTI) || dev->mc_count > 10)
 	{
 		dev->flags|=IFF_PROMISC;
-		filt = 1;
+		filt |= 1;
 	}
 	else if(dev->mc_count)
 	{
@@ -1374,9 +1581,7 @@
 		struct dev_mc_list *dmc=dev->mc_list;
 		
 		int i;
-		
-		filt = 0;
-		
+	       
 		if(retry==0)
 			lp->mc_list_valid = 0;
 		if(!lp->mc_list_valid)
@@ -1399,39 +1604,40 @@
 			lp->mc_list_valid=1;
 		}
 	}
-	else 
-	{
-		filt = 0;
-	}
-	if(mc32_command_nowait(dev, 0, &filt, 2)==-1)
+	
+	if(mc32_command_nowait(dev, 0, &filt, 2)==-1) 
 	{
 		lp->mc_reload_wait = 1;
+	} 
+	else { 
+		lp->mc_reload_wait = 0;
 	}
 }
 
+
 /**
- *	mc32_set_multicast_list:
+ *	mc32_set_multicast_list	-	queue multicast list update
  *	@dev: The 3c527 to use
  *
  *	Commence loading the multicast list. This is called when the kernel
  *	changes the lists. It will override any pending list we are trying to
  *	load.
  */
- 
+
 static void mc32_set_multicast_list(struct net_device *dev)
 {
 	do_mc32_set_multicast_list(dev,0);
 }
 
+
 /**
- *	mc32_reset_multicast_list:
+ *	mc32_reset_multicast_list	-	reset multicast list
  *	@dev: The 3c527 to use
  *
  *	Attempt the next step in loading the multicast lists. If this attempt
  *	fails to complete then it will be scheduled and this function called
  *	again later from elsewhere.
  */
- 
 
 static void mc32_reset_multicast_list(struct net_device *dev)
 {
@@ -1442,19 +1648,18 @@
 
 static struct net_device this_device;
 
-
 /**
- *	init_module:
+ *	init_module		-	entry point
  *
  *	Probe and locate a 3c527 card. This really should probe and locate
  *	all the 3c527 cards in the machine not just one of them. Yes you can
- *	insmod multiple modules for now but its a hack.
+ *	insmod multiple modules for now but it's a hack.
  */
- 
+
 int init_module(void)
 {
 	int result;
-
+	
 	this_device.init = mc32_probe;
 	if ((result = register_netdev(&this_device)) != 0)
 		return result;
@@ -1463,7 +1668,7 @@
 }
 
 /**
- *	cleanup_module:
+ *	cleanup_module	-	free resources for an unload
  *
  *	Unloading time. We release the MCA bus resources and the interrupt
  *	at which point everything is ready to unload. The card must be stopped
@@ -1472,7 +1677,7 @@
  *	initialized it must be rebooted or the rings reloaded before any
  *	transmit operations are allowed to start scribbling into memory.
  */
- 
+
 void cleanup_module(void)
 {
 	int slot;
@@ -1493,6 +1698,7 @@
 		kfree(this_device.priv);
 	}
 	free_irq(this_device.irq, &this_device);
+	release_region(this_device.base_addr, MC32_IO_EXTENT);
 }
 
 #endif /* MODULE */
diff -urN linux-2.4.3-pre1/drivers/net/3c527.h linux/drivers/net/3c527.h
--- linux-2.4.3-pre1/drivers/net/3c527.h	Sun Mar 21 07:11:36 1999
+++ linux/drivers/net/3c527.h	Mon Dec  3 22:29:50 2001
@@ -7,11 +7,19 @@
  */
   
 #define HOST_CMD		0
+#define         HOST_CMD_START_RX   (1<<3)
+#define         HOST_CMD_SUSPND_RX  (3<<3)
+#define         HOST_CMD_RESTRT_RX  (5<<3)
+
+#define         HOST_CMD_SUSPND_TX  3
+#define         HOST_CMD_RESTRT_TX  5
+
 
 #define HOST_STATUS		2
 #define		HOST_STATUS_CRR	(1<<6)
 #define		HOST_STATUS_CWR	(1<<5)
 
+
 #define HOST_CTRL		6
 #define		HOST_CTRL_ATTN	(1<<7)
 #define 	HOST_CTRL_RESET	(1<<6)
@@ -19,6 +27,17 @@
 
 #define HOST_RAMPAGE		8
 
+#define RX_HALTED (1<<0)
+#define TX_HALTED (1<<1)  
+#define HALTED (RX_HALTED | TX_HALTED)
+#define RUNNING 0
+
+struct mc32_mailbox
+{
+	u16	mbox __attribute((packed));
+	u16	data[1] __attribute((packed));
+};
+
 struct skb_header
 {
 	u8	status __attribute((packed));
@@ -28,13 +47,37 @@
 	u32	data __attribute((packed));
 };
 
-#define STATUS_MASK	0x0F
-#define COMPLETED	0x80
-#define COMPLETED_OK	0x40
-#define BUFFER_BUSY	0x20
+struct mc32_stats
+{
+	/* RX Errors */
+	u32     rx_crc_errors       __attribute((packed)); 	
+	u32     rx_alignment_errors  __attribute((packed)); 	
+	u32     rx_overrun_errors    __attribute((packed));
+	u32     rx_tooshort_errors  __attribute((packed));
+	u32     rx_toolong_errors   __attribute((packed));
+	u32     rx_outofresource_errors  __attribute((packed)); 
+
+	u32     rx_discarded   __attribute((packed));  /* via card pattern match filter */ 
+
+	/* TX Errors */
+	u32     tx_max_collisions __attribute((packed)); 
+	u32     tx_carrier_errors __attribute((packed)); 
+	u32     tx_underrun_errors __attribute((packed)); 
+	u32     tx_cts_errors     __attribute((packed)); 
+	u32     tx_timeout_errors __attribute((packed)) ;
+	
+	/* various cruft */
+	u32     dataA[6] __attribute((packed));   
+        u16	dataB[5] __attribute((packed));   
+  	u32     dataC[14] __attribute((packed)); 	
+};
 
-#define CONTROL_EOP	0x80	/* End Of Packet */
-#define CONTROL_EL	0x40	/* End of List */
+#define STATUS_MASK	0x0F
+#define COMPLETED	(1<<7)
+#define COMPLETED_OK	(1<<6)
+#define BUFFER_BUSY	(1<<5)
 
+#define CONTROL_EOP	(1<<7)	/* End Of Packet */
+#define CONTROL_EOL	(1<<6)	/* End of List */
 
-#define MCA_MC32_ID	0x0041	/* Our MCA ident */
\ No newline at end of file
+#define MCA_MC32_ID	0x0041	/* Our MCA ident */
diff -urN linux-2.4.3-pre1/drivers/net/appletalk/cops.c linux/drivers/net/appletalk/cops.c
--- linux-2.4.3-pre1/drivers/net/appletalk/cops.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/appletalk/cops.c	Mon Dec  3 22:29:50 2001
@@ -181,7 +181,7 @@
         int board;			/* Holds what board type is. */
 	int nodeid;			/* Set to 1 once have nodeid. */
         unsigned char node_acquire;	/* Node ID when acquired. */
-        struct at_addr node_addr;	/* Full node addres */
+        struct at_addr node_addr;	/* Full node address */
 };
 
 /* Index to functions, as function prototypes. */
diff -urN linux-2.4.3-pre1/drivers/net/arcnet/arcnet.c linux/drivers/net/arcnet/arcnet.c
--- linux-2.4.3-pre1/drivers/net/arcnet/arcnet.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/arcnet/arcnet.c	Mon Dec  3 22:29:50 2001
@@ -696,6 +696,9 @@
 		       msg, status, lp->intmask, lp->lasttrans_dest);
 		lp->last_timeout = jiffies;
 	}
+
+	if (lp->cur_tx == -1)
+		netif_wake_queue(dev);
 }
 
 
diff -urN linux-2.4.3-pre1/drivers/net/bonding.c linux/drivers/net/bonding.c
--- linux-2.4.3-pre1/drivers/net/bonding.c	Fri Sep 22 14:21:16 2000
+++ linux/drivers/net/bonding.c	Mon Dec  3 22:29:50 2001
@@ -60,12 +60,6 @@
 
 static struct net_device *this_bond;
 
-static int bond_open(struct net_device *dev)
-{
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
 static void release_one_slave(struct net_device *master, slave_t *slave)
 {
 	bonding_t *bond = master->priv;
@@ -81,7 +75,6 @@
 
 	dev_put(slave->dev);
 	kfree(slave);
-	MOD_DEC_USE_COUNT;
 }
 
 static int bond_close(struct net_device *master)
@@ -92,7 +85,6 @@
 	while ((slave = bond->next) != (slave_t*)bond)
 		release_one_slave(master, slave);
 
-	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -142,7 +134,6 @@
 
 	spin_unlock_bh(&master->xmit_lock);
 
-	MOD_INC_USE_COUNT;
 	return 0;
 }
 
@@ -210,9 +201,7 @@
 }
 
 static struct notifier_block bond_netdev_notifier={
-	bond_event,
-	NULL,
-	0
+	notifier_call: bond_event 
 };
 
 static int __init bond_init(struct net_device *dev)
@@ -233,7 +222,6 @@
 	/* Initialize the device structure. */
 	dev->hard_start_xmit = bond_xmit;
 	dev->get_stats	= bond_get_stats;
-	dev->open = bond_open;
 	dev->stop = bond_close;
 	dev->set_multicast_list = bond_set_multicast_list;
 	dev->do_ioctl = bond_ioctl;
@@ -289,20 +277,20 @@
 	return &bond->stats;
 }
 
-static struct net_device dev_bond = {
-		"",
-		0, 0, 0, 0,
-	 	0x0, 0,
-	 	0, 0, 0, NULL, bond_init };
+static struct net_device dev_bond;
 
 static int __init bonding_init(void)
 {
 	/* Find a name for this unit */
-	int err=dev_alloc_name(&dev_bond,"bond%d");
+	int err;
+	
+	dev_bond.init = bond_init;
 
+	err = dev_alloc_name(&dev_bond,"bond%d");
 	if (err<0)
 		return err;
 
+	SET_MODULE_OWNER(&dev_bond);
 	if (register_netdev(&dev_bond) != 0)
 		return -EIO;
 
diff -urN linux-2.4.3-pre1/drivers/net/daynaport.c linux/drivers/net/daynaport.c
--- linux-2.4.3-pre1/drivers/net/daynaport.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/daynaport.c	Mon Dec  3 22:29:50 2001
@@ -275,6 +275,9 @@
 		return -ENODEV;
 
 	dev = init_etherdev(dev, 0);
+	if (!dev)
+		return -ENOMEM;
+	SET_MODULE_OWNER(dev);
 
 	if (!version_printed) {
 		printk(KERN_INFO "%s", version);
@@ -445,7 +448,7 @@
 	}
 
 	/* We should hopefully not get here */
-	printk(KERN_ERR "Probe unsucessful.\n");
+	printk(KERN_ERR "Probe unsuccessful.\n");
 	return -ENODEV;
 
  membad:
@@ -630,7 +633,8 @@
 
 static int ns8390_open(struct net_device *dev)
 {
-	MOD_INC_USE_COUNT;
+	int ret;
+
 	ei_open(dev);
 
 	/* At least on my card (a Focus Enhancements PDS card) I start */
@@ -639,11 +643,10 @@
 	/*                             - funaho@jurai.org (1999-05-17) */
 
 	/* Non-slow interrupt, works around issues with the SONIC driver */
-	if (request_irq(dev->irq, ei_interrupt, 0, "8390 Ethernet", dev)) 
-	{
+	ret = request_irq(dev->irq, ei_interrupt, 0, dev->name, dev); 
+	if (ret) {
 		printk ("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
-		MOD_DEC_USE_COUNT;
-		return -EAGAIN;
+		return ret;
 	}
 	return 0;
 }
@@ -654,7 +657,6 @@
 		printk("Need to reset the NS8390 t=%lu...", jiffies);
 	ei_status.txing = 0;
 	if (ei_debug > 1) printk("reset not supported\n");
-	return;
 }
 
 static int ns8390_close_card(struct net_device *dev)
@@ -663,7 +665,6 @@
 		printk("%s: Shutting down ethercard.\n", dev->name);
 	free_irq(dev->irq, dev);
 	ei_close(dev);
-	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
diff -urN linux-2.4.3-pre1/drivers/net/de600.c linux/drivers/net/de600.c
--- linux-2.4.3-pre1/drivers/net/de600.c	Thu Nov 16 12:51:28 2000
+++ linux/drivers/net/de600.c	Mon Dec  3 22:29:50 2001
@@ -616,11 +616,15 @@
 	for (i = size; i > 0; --i, ++buffer)
 		*buffer = de600_read_byte(READ_DATA, dev);
 
-	((struct net_device_stats *)(dev->priv))->rx_packets++; /* count all receives */
-
 	skb->protocol=eth_type_trans(skb,dev);
 
 	netif_rx(skb);
+
+	/* update stats */
+	dev->last_rx = jiffies;
+	((struct net_device_stats *)(dev->priv))->rx_packets++; /* count all receives */
+	((struct net_device_stats *)(dev->priv))->rx_bytes += size; /* count all received bytes */
+
 	/*
 	 * If any worth-while packets have been received, netif_rx()
 	 * has done a mark_bh(INET_BH) for us and will work on them
diff -urN linux-2.4.3-pre1/drivers/net/de620.c linux/drivers/net/de620.c
--- linux-2.4.3-pre1/drivers/net/de620.c	Thu Nov 16 12:51:28 2000
+++ linux/drivers/net/de620.c	Mon Dec  3 22:29:50 2001
@@ -699,8 +699,10 @@
 			PRINTK(("Read %d bytes\n", size));
 			skb->protocol=eth_type_trans(skb,dev);
 			netif_rx(skb); /* deliver it "upstairs" */
+			dev->last_rx = jiffies;
 			/* count all receives */
 			((struct net_device_stats *)(dev->priv))->rx_packets++;
+			((struct net_device_stats *)(dev->priv))->rx_bytes += size;
 		}
 	}
 
diff -urN linux-2.4.3-pre1/drivers/net/declance.c linux/drivers/net/declance.c
--- linux-2.4.3-pre1/drivers/net/declance.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/declance.c	Mon Dec  3 22:29:50 2001
@@ -999,7 +999,7 @@
 	lance_set_multicast(dev);
 }
 
-static int __init dec_lance_init(struct net_device *dev, const int type)
+static int __init dec_lance_init(const int type)
 {
 	static unsigned version_printed;
 	struct net_device *dev;
@@ -1008,6 +1008,7 @@
 	int i, ret;
 	unsigned long esar_base;
 	unsigned char *esar;
+	struct net_device *dev;
 
 #ifndef CONFIG_TC
 	system_base = KN01_LANCE_BASE;
@@ -1018,12 +1019,12 @@
 	if (dec_lance_debug && version_printed++ == 0)
 		printk(version);
 
-	dev = init_etherdev(0, sizeof(struct lance_private));
+	dev = init_etherdev(NULL, sizeof(struct lance_private));
 	if (!dev)
 		return -ENOMEM;
 
 	/* init_etherdev ensures the data structures used by the LANCE are aligned. */
-	lp = (struct lance_private *) dev->priv;
+	lp = dev->priv;
 	spin_lock_init(&lp->lock);
 
 	switch (type) {
@@ -1206,7 +1207,6 @@
 /* Find all the lance cards on the system and initialize them */
 static int __init dec_lance_probe(void)
 {
-	struct net_device *dev = NULL;
 	static int called = 0;
 
 #ifdef MODULE
@@ -1244,7 +1244,7 @@
 	}
 #endif
 
-	return dec_lance_init(dev, type);
+	return dec_lance_init(type);
 }
 
 static void __exit dec_lance_cleanup(void)
diff -urN linux-2.4.3-pre1/drivers/net/dmfe.c linux/drivers/net/dmfe.c
--- linux-2.4.3-pre1/drivers/net/dmfe.c	Tue Feb 13 13:15:04 2001
+++ linux/drivers/net/dmfe.c	Mon Dec  3 22:29:50 2001
@@ -353,9 +353,19 @@
 	int i;
 	struct net_device *dev;
 	u32 dev_rev;
+	u16 pci_command;
 
 	DMFE_DBUG(0, "dmfe_probe()", 0);
 
+	/* Enable Master/IO access, Disable memory access */
+	i = pci_enable_device(pdev);
+	if (i) return i;
+
+	pci_set_master(pdev);
+	pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+	pci_command &= ~PCI_COMMAND_MEMORY;
+	pci_write_config_word(pdev, PCI_COMMAND, pci_command);
+
 	pci_iobase = pci_resource_start(pdev, 0);
 	pci_irqline = pdev->irq;
 
@@ -372,11 +382,6 @@
 		goto err_out;
 	}
 
-	/* Enable Master/IO access, Disable memory access */
-	if (pci_enable_device(pdev))
-		goto err_out;
-	pci_set_master(pdev);
-
 #if 0	/* pci_{enable_device,set_master} sets minimum latency for us now */
 
 	/* Set Latency Timer 80h */
@@ -511,7 +516,7 @@
 		db->dm910x_chk_mode = 1;	/* Enter the check mode */
 	}
 
-	/* Initilize DM910X board */
+	/* Initialize DM910X board */
 	dmfe_init_dm910x(dev);
 
 	/* set and active a timer process */
@@ -526,9 +531,9 @@
 	return 0;
 }
 
-/* Initilize DM910X board
+/* Initialize DM910X board
    Reset DM910X board
-   Initilize TX/Rx descriptor chain structure
+   Initialize TX/Rx descriptor chain structure
    Send the set-up frame
    Enable Tx/Rx machine
  */
@@ -559,7 +564,7 @@
 		db->op_mode = db->media_mode;
 	dmfe_process_mode(db);
 
-	/* Initiliaze Transmit/Receive decriptor and CR3/4 */
+	/* Initialize Transmit/Receive decriptor and CR3/4 */
 	dmfe_descriptor_init(db, ioaddr);
 
 	/* Init CR6 to program DM910x operation */
@@ -623,10 +628,10 @@
 	if (db->tx_packet_cnt < TX_MAX_SEND_CNT) {
 		txptr->tdes0 = 0x80000000;	/* set owner bit to DM910X */
 		db->tx_packet_cnt++;	/* Ready to send count */
-		outl(0x1, dev->base_addr + DCR1);	/* Issue Tx polling comand */
+		outl(0x1, dev->base_addr + DCR1);	/* Issue Tx polling command */
 	} else {
 		db->tx_queue_cnt++;	/* queue the tx packet */
-		outl(0x1, dev->base_addr + DCR1);	/* Issue Tx polling comand */
+		outl(0x1, dev->base_addr + DCR1);	/* Issue Tx polling command */
 	}
 
 	/* Tx resource check */
@@ -673,7 +678,7 @@
 }
 
 /*
-   DM9102 insterrupt handler
+   DM9102 interrupt handler
    receive the packet to upper layer, free the transmitted packet
  */
 
@@ -999,7 +1004,7 @@
    Stop DM910X board
    Free Tx/Rx allocated memory
    Reset DM910X board
-   Re-initilize DM910X board
+   Re-initialize DM910X board
  */
 static void dmfe_dynamic_reset(struct net_device *dev)
 {
@@ -1027,7 +1032,7 @@
 	db->wait_reset = 0;
 	db->rx_error_cnt = 0;
 
-	/* Re-initilize DM910X board */
+	/* Re-initialize DM910X board */
 	dmfe_init_dm910x(dev);
 
 	/* Leave dynamic reser route */
@@ -1139,7 +1144,7 @@
 }
 
 /* Send a setup frame for DM9132
-   This setup frame initilize DM910X addres filter mode
+   This setup frame initialize DM910X address filter mode
  */
 static void dm9132_id_table(struct net_device *dev, int mc_cnt)
 {
@@ -1180,7 +1185,7 @@
 }
 
 /* Send a setup frame for DM9102/DM9102A
-   This setup frame initilize DM910X addres filter mode
+   This setup frame initialize DM910X address filter mode
  */
 static void send_filter_frame(struct net_device *dev, int mc_cnt)
 {
@@ -1429,11 +1434,11 @@
 		phy_write_1bit(ioaddr, PHY_DATA_0);
 		phy_write_1bit(ioaddr, PHY_DATA_1);
 
-		/* Send Phy addres */
+		/* Send Phy address */
 		for (i = 0x10; i > 0; i = i >> 1)
 			phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
 
-		/* Send register addres */
+		/* Send register address */
 		for (i = 0x10; i > 0; i = i >> 1)
 			phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
 
@@ -1476,11 +1481,11 @@
 		phy_write_1bit(ioaddr, PHY_DATA_1);
 		phy_write_1bit(ioaddr, PHY_DATA_0);
 
-		/* Send Phy addres */
+		/* Send Phy address */
 		for (i = 0x10; i > 0; i = i >> 1)
 			phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0);
 
-		/* Send register addres */
+		/* Send register address */
 		for (i = 0x10; i > 0; i = i >> 1)
 			phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0);
 
@@ -1570,7 +1575,7 @@
 
 /*	Description: 
  *	when user used insmod to add module, system invoked init_module()
- *	to initilize and register.
+ *	to initialize and register.
  */
  
 static int __init dmfe_init_module(void)
@@ -1599,12 +1604,10 @@
 	rc = pci_module_init(&dmfe_driver);
 	if (rc < 0)
 		return rc;
-	if (rc >= 0) {
-		printk (KERN_INFO "Davicom DM91xx net driver loaded, version "
-			DMFE_VERSION "\n");
-		return 0;
-	}
-	return -ENODEV;
+
+	printk (KERN_INFO "Davicom DM91xx net driver loaded, version "
+		DMFE_VERSION "\n");
+	return 0;
 }
 
 /*
diff -urN linux-2.4.3-pre1/drivers/net/eth16i.c linux/drivers/net/eth16i.c
--- linux-2.4.3-pre1/drivers/net/eth16i.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/eth16i.c	Mon Dec  3 22:29:50 2001
@@ -1195,10 +1195,6 @@
 			}
 
 			skb->protocol=eth_type_trans(skb, dev);
-			netif_rx(skb);
-			dev->last_rx = jiffies;
-			lp->stats.rx_packets++;
-			lp->stats.rx_bytes += pkt_len;
 
 			if( eth16i_debug > 5 ) {
 				int i;
@@ -1208,6 +1204,10 @@
 					printk(KERN_DEBUG " %02x", skb->data[i]);
 				printk(KERN_DEBUG ".\n");
 			}
+			netif_rx(skb);
+			dev->last_rx = jiffies;
+			lp->stats.rx_packets++;
+			lp->stats.rx_bytes += pkt_len;
 
 		} /* else */
 
diff -urN linux-2.4.3-pre1/drivers/net/ewrk3.c linux/drivers/net/ewrk3.c
--- linux-2.4.3-pre1/drivers/net/ewrk3.c	Fri Feb 16 16:06:17 2001
+++ linux/drivers/net/ewrk3.c	Mon Dec  3 22:29:50 2001
@@ -997,19 +997,6 @@
 							isa_memcpy_fromio(p, buf, pkt_len);
 						}
 
-						/*
-						   ** Notify the upper protocol layers that there is another
-						   ** packet to handle
-						 */
-						skb->protocol = eth_type_trans(skb, dev);
-						netif_rx(skb);
-
-						/*
-						   ** Update stats
-						 */
-						dev->last_rx = jiffies;
-						lp->stats.rx_packets++;
-						lp->stats.rx_bytes += pkt_len;
 						for (i = 1; i < EWRK3_PKT_STAT_SZ - 1; i++) {
 							if (pkt_len < i * EWRK3_PKT_BIN_SZ) {
 								lp->pktStats.bins[i]++;
@@ -1031,6 +1018,19 @@
 						if (lp->pktStats.bins[0] == 0) {	/* Reset counters */
 							memset(&lp->pktStats, 0, sizeof(lp->pktStats));
 						}
+						/*
+						   ** Notify the upper protocol layers that there is another
+						   ** packet to handle
+						 */
+						skb->protocol = eth_type_trans(skb, dev);
+						netif_rx(skb);
+
+						/*
+						   ** Update stats
+						 */
+						dev->last_rx = jiffies;
+						lp->stats.rx_packets++;
+						lp->stats.rx_bytes += pkt_len;
 					} else {
 						printk("%s: Insufficient memory; nuking packet.\n", dev->name);
 						lp->stats.rx_dropped++;		/* Really, deferred. */
diff -urN linux-2.4.3-pre1/drivers/net/fmv18x.c linux/drivers/net/fmv18x.c
--- linux-2.4.3-pre1/drivers/net/fmv18x.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/fmv18x.c	Mon Dec  3 22:29:50 2001
@@ -52,6 +52,7 @@
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <linux/errno.h>
+#include <linux/spinlock.h>
 
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
@@ -79,6 +80,7 @@
 	uint rx_started:1;			/* Packets are Rxing. */
 	uchar tx_queue;				/* Number of packet on the Tx queue. */
 	ushort tx_queue_len;		/* Current length of the Tx queue. */
+	spinlock_t lock;
 };
 
 
@@ -161,6 +163,7 @@
 	char irqmap[4] = {3, 7, 10, 15};
 	char irqmap_pnp[8] = {3, 4, 5, 7, 9, 10, 11, 15};
 	unsigned int i, irq, retval;
+	struct net_local *lp;
 
 	/* Resetting the chip doesn't reset the ISA interface, so don't bother.
 	   That means we have to be careful with the register values we probe for.
@@ -268,6 +271,8 @@
 		goto out_irq;
 	}
 	memset(dev->priv, 0, sizeof(struct net_local));
+	lp = dev->priv;
+	spin_lock_init(&lp->lock);
 
 	dev->open		= net_open;
 	dev->stop		= net_close;
@@ -292,7 +297,7 @@
 
 static int net_open(struct net_device *dev)
 {
-	struct net_local *lp = (struct net_local *)dev->priv;
+	struct net_local *lp = dev->priv;
 	int ioaddr = dev->base_addr;
 
 	/* Set the configuration register 0 to 32K 100ns. byte-wide memory,
@@ -326,7 +331,7 @@
 
 static void net_timeout(struct net_device *dev)
 {
-	struct net_local *lp = (struct net_local *)dev->priv;
+	struct net_local *lp = dev->priv;
 	int ioaddr = dev->base_addr;
 	unsigned long flags;
 	
@@ -346,8 +351,7 @@
 		htons(inw(ioaddr+FJ_CONFIG0)));
 	lp->stats.tx_errors++;
 	/* ToDo: We should try to restart the adaptor... */
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&lp->lock, flags);
 
 	/* Initialize LAN Controller and LAN Card */
 	outb(0xda, ioaddr + CONFIG_0);   /* Initialize LAN Controller */
@@ -355,20 +359,21 @@
 	outb(0x00, ioaddr + FJ_CONFIG1); /* Disable IRQ of LAN Card */
 	outb(0x00, ioaddr + FJ_BUFCNTL); /* Reset ? I'm not sure */
 	net_open(dev);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&lp->lock, flags);
+
+	netif_wake_queue(dev);
 }
 
 static int net_send_packet(struct sk_buff *skb, struct net_device *dev)
 {
-	struct net_local *lp = (struct net_local *)dev->priv;
+	struct net_local *lp = dev->priv;
 	int ioaddr = dev->base_addr;
 	short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
 	unsigned char *buf = skb->data;
+	unsigned long flags;
 
 	/* Block a transmit from overlapping.  */
 	
-	netif_stop_queue(dev);
-	
 	if (length > ETH_FRAME_LEN) {
 		if (net_debug)
 			printk("%s: Attempting to send a large packet (%d bytes).\n",
@@ -383,6 +388,7 @@
 	   codes we possibly catch a Tx interrupt. Thus we flag off
 	   tx_queue_ready, so that we prevent the interrupt routine
 	   (net_interrupt) to start transmitting. */
+	spin_lock_irqsave(&lp->lock, flags);
 	lp->tx_queue_ready = 0;
 	{
 		outw(length, ioaddr + DATAPORT);
@@ -391,6 +397,8 @@
 		lp->tx_queue_len += length + 2;
 	}
 	lp->tx_queue_ready = 1;
+	spin_unlock_irqrestore(&lp->lock, flags);
+
 	if (lp->tx_started == 0) {
 		/* If the Tx is idle, always trigger a transmit. */
 		outb(0x80 | lp->tx_queue, ioaddr + TX_START);
@@ -398,10 +406,10 @@
 		lp->tx_queue_len = 0;
 		dev->trans_start = jiffies;
 		lp->tx_started = 1;
-		netif_wake_queue(dev);
 	} else if (lp->tx_queue_len < 4096 - 1502)
 		/* Yes, there is room for one more packet. */
-		netif_wake_queue(dev);
+	else
+		netif_stop_queue(dev);
 
 	dev_kfree_skb(skb);
 	return 0;
@@ -417,7 +425,7 @@
 	int ioaddr, status;
 
 	ioaddr = dev->base_addr;
-	lp = (struct net_local *)dev->priv;
+	lp = dev->priv;
 	status = inw(ioaddr + TX_STATUS);
 	outw(status, ioaddr + TX_STATUS);
 
@@ -447,6 +455,7 @@
 			lp->stats.collisions++;
 		}
 		if (status & 0x82) {
+			spin_lock(&lp->lock);
 			lp->stats.tx_packets++;
 			if (lp->tx_queue && lp->tx_queue_ready) {
 				outb(0x80 | lp->tx_queue, ioaddr + TX_START);
@@ -458,6 +467,7 @@
 				lp->tx_started = 0;
 				netif_wake_queue(dev);	/* Inform upper layers. */
 			}
+			spin_unlock(&lp->lock);
 		}
 	}
 	return;
@@ -466,7 +476,7 @@
 /* We have a good packet(s), get it/them out of the buffers. */
 static void net_rx(struct net_device *dev)
 {
-	struct net_local *lp = (struct net_local *)dev->priv;
+	struct net_local *lp = dev->priv;
 	int ioaddr = dev->base_addr;
 	int boguscount = 5;
 
@@ -581,7 +591,7 @@
    closed. */
 static struct net_device_stats *net_get_stats(struct net_device *dev)
 {
-	struct net_local *lp = (struct net_local *)dev->priv;
+	struct net_local *lp = dev->priv;
 	return &lp->stats;
 }
 
diff -urN linux-2.4.3-pre1/drivers/net/hamachi.c linux/drivers/net/hamachi.c
--- linux-2.4.3-pre1/drivers/net/hamachi.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/hamachi.c	Mon Dec  3 22:29:50 2001
@@ -477,6 +477,7 @@
 };
 
 #define PRIV_ALIGN   15    				/* Required alignment mask */
+#define MII_CNT		4
 struct hamachi_private {
 	/* Descriptor rings first for alignment.  Tx requires a second descriptor
 	   for status. */
@@ -503,7 +504,7 @@
 	/* MII transceiver section. */
 	int mii_cnt;								/* MII device addresses. */
 	u16 advertising;							/* NWay media advertisement */
-	unsigned char phys[2];					/* MII device addresses. */
+	unsigned char phys[MII_CNT];		/* MII device addresses, only first one used. */
 	u_int32_t rx_int_var, tx_int_var;	/* interrupt control variables */
 	u_int32_t option;							/* Hold on to a copy of the options */
 	u_int8_t pad[16];							/* Used for 32-byte alignment */
@@ -554,7 +555,7 @@
 	struct hamachi_private *hmp;
 	int option, i, rx_int_var, tx_int_var, boguscnt;
 	int chip_id = ent->driver_data;
-	int irq = pdev->irq;
+	int irq;
 	long ioaddr;
 	static int card_idx = 0;
 	struct net_device *dev;
@@ -562,15 +563,17 @@
 	if (hamachi_debug > 0  &&  did_version++ == 0)
 		printk(version);
 
+	if (pci_enable_device(pdev))
+		return -EIO;
+
 	ioaddr = pci_resource_start(pdev, 0);
 #ifdef __alpha__				/* Really "64 bit addrs" */
 	ioaddr |= (pci_resource_start(pdev, 1) << 32);
 #endif
 
-	if (pci_enable_device(pdev))
-		return -EIO;
 	pci_set_master(pdev);
 
+	irq = pdev->irq;
 	ioaddr = (long) ioremap(ioaddr, 0x400);
 	if (!ioaddr)
 		return -ENOMEM;
@@ -703,7 +706,7 @@
 
 	if (chip_tbl[hmp->chip_id].flags & CanHaveMII) {
 		int phy, phy_idx = 0;
-		for (phy = 0; phy < 32 && phy_idx < 4; phy++) {
+		for (phy = 0; phy < 32 && phy_idx < MII_CNT; phy++) {
 			int mii_status = mdio_read(ioaddr, phy, 1);
 			if (mii_status != 0xffff  &&
 				mii_status != 0x0000) {
@@ -1062,7 +1065,6 @@
 	hmp->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
 	/* Mark the last entry as wrapping the ring. */
 	hmp->rx_ring[RX_RING_SIZE-1].status_n_length |= cpu_to_le32(DescEndRing);
-
 
 	/* Trigger an immediate transmit demand. */
 	dev->trans_start = jiffies;
diff -urN linux-2.4.3-pre1/drivers/net/hp100.c linux/drivers/net/hp100.c
--- linux-2.4.3-pre1/drivers/net/hp100.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/hp100.c	Mon Dec  3 22:29:50 2001
@@ -45,6 +45,8 @@
 **   along with this program; if not, write to the Free Software
 **   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 **
+** 1.57b -> 1.57c - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+**   - release resources on failure in init_module
 **
 ** 1.57 -> 1.57b - Jean II
 **   - fix spinlocks, SMP is now working !
@@ -265,12 +267,14 @@
 
 #define HP100_EISA_IDS_SIZE	(sizeof(hp100_eisa_ids)/sizeof(struct hp100_eisa_id))
 
+#ifdef CONFIG_PCI
 static struct hp100_pci_id hp100_pci_ids[] = {
   { PCI_VENDOR_ID_HP, 		PCI_DEVICE_ID_HP_J2585A },
   { PCI_VENDOR_ID_HP,		PCI_DEVICE_ID_HP_J2585B },
   { PCI_VENDOR_ID_COMPEX,	PCI_DEVICE_ID_COMPEX_ENET100VG4 },
   { PCI_VENDOR_ID_COMPEX2,	PCI_DEVICE_ID_COMPEX2_100VG }
 };
+#endif
 
 #define HP100_PCI_IDS_SIZE	(sizeof(hp100_pci_ids)/sizeof(struct hp100_pci_id))
 
@@ -1896,7 +1900,7 @@
 
   /* First get indication of received lan packet */
   /* RX_PKT_CND indicates the number of packets which have been fully */
-  /* received onto the card but have not been fully transfered of the card */
+  /* received onto the card but have not been fully transferred of the card */
   packets = hp100_inb( RX_PKT_CNT );
 #ifdef HP100_DEBUG_RX
   if ( packets > 1 )
@@ -1967,11 +1971,6 @@
 	    insl( ioaddr + HP100_REG_DATA32, ptr, pkt_len >> 2 );
       
 	  skb->protocol = eth_type_trans( skb, dev );
-
-	  netif_rx( skb );
-	  dev->last_rx = jiffies;
-	  lp->stats.rx_packets++;
-	  lp->stats.rx_bytes += pkt_len;
       
 #ifdef HP100_DEBUG_RX
 	  printk( "hp100: %s: rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
@@ -1979,6 +1978,10 @@
 		  ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ],
 		  ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] );
 #endif
+	  netif_rx( skb );
+	  dev->last_rx = jiffies;
+	  lp->stats.rx_packets++;
+	  lp->stats.rx_bytes += pkt_len;
 	}
   
       /* Indicate the card that we have got the packet */
@@ -3017,16 +3020,34 @@
 #ifdef MODULE
 
 /* Parameters set by insmod */
-int hp100_port[5] = { 0, -1, -1, -1, -1 };
+static int hp100_port[5] = { 0, -1, -1, -1, -1 };
 MODULE_PARM(hp100_port, "1-5i");
 
 /* Allocate 5 string of length IFNAMSIZ, one string for each device */
-char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" };
+static char hp100_name[5][IFNAMSIZ] = { "", "", "", "", "" };
 /* Allow insmod to write those 5 strings individually */
 MODULE_PARM(hp100_name, "1-5c" __MODULE_STRING(IFNAMSIZ));
 
 /* List of devices */
-static struct net_device *hp100_devlist[5] = { NULL, NULL, NULL, NULL, NULL };
+static struct net_device *hp100_devlist[5];
+
+static void release_dev(int i)
+{
+	struct net_device *d = hp100_devlist[i];
+	struct hp100_private *p = (struct hp100_private *)d->priv;
+
+	unregister_netdev(d);
+	release_region(d->base_addr, HP100_REGION_SIZE);
+
+	if (p->mode == 1) /* busmaster */
+		kfree(p->page_vaddr); 
+	if (p->mem_ptr_virt)
+		iounmap(p->mem_ptr_virt);
+	kfree(d->priv);
+	d->priv = NULL;
+	kfree(d);
+	hp100_devlist[i] = NULL;
+}
 
 /*
  * Note: if you have more than five 100vg cards in your pc, feel free to
@@ -3053,6 +3074,8 @@
     {
       /* Create device and set basics args */
       hp100_devlist[i] = kmalloc(sizeof(struct net_device), GFP_KERNEL);
+      if (!hp100_devlist[i])
+	goto fail;
       memset(hp100_devlist[i], 0x00, sizeof(struct net_device));
 #if LINUX_VERSION_CODE >= 0x020362	/* 2.3.99-pre7 */
       memcpy(hp100_devlist[i]->name, hp100_name[i], IFNAMSIZ);	/* Copy name */
@@ -3075,6 +3098,13 @@
     }			/* Loop over all devices */
 
   return cards > 0 ? 0 : -ENODEV;
+ fail:
+  while (cards && --i)
+	  if (hp100_devlist[i]) {
+		release_dev(i);
+		--cards;
+	  }
+  return -ENOMEM;
 }
 
 void cleanup_module( void )
@@ -3084,18 +3114,7 @@
   /* TODO: Check if all skb's are released/freed. */
   for(i = 0; i < 5; i++)
     if(hp100_devlist[i] != (struct net_device *) NULL)
-      {
-	unregister_netdev( hp100_devlist[i] );
-	release_region( hp100_devlist[i]->base_addr, HP100_REGION_SIZE );
-	if( ((struct hp100_private *)hp100_devlist[i]->priv)->mode==1 ) /* busmaster */
-	  kfree( ((struct hp100_private *)hp100_devlist[i]->priv)->page_vaddr ); 
-	if ( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt )
-	  iounmap( ((struct hp100_private *)hp100_devlist[i]->priv) -> mem_ptr_virt );
-	kfree( hp100_devlist[i]->priv );
-	hp100_devlist[i]->priv = NULL;
-	kfree(hp100_devlist[i]);
-	hp100_devlist[i] = (struct net_device *) NULL;
-      }
+	    release_dev(i);
 }
 
 #endif		/* MODULE */
diff -urN linux-2.4.3-pre1/drivers/net/ioc3-eth.c linux/drivers/net/ioc3-eth.c
--- linux-2.4.3-pre1/drivers/net/ioc3-eth.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/ioc3-eth.c	Mon Dec  3 22:29:50 2001
@@ -862,9 +862,6 @@
 	ioc3_init(dev);
 
 	netif_start_queue(dev);
-
-	MOD_INC_USE_COUNT;
-
 	return 0;
 }
 
@@ -880,9 +877,6 @@
 	free_irq(dev->irq, dev);
 
 	ioc3_free_rings(ip);
-
-	MOD_DEC_USE_COUNT;
-
 	return 0;
 }
 
@@ -894,13 +888,14 @@
 	struct ioc3 *ioc3;
 	unsigned long ioc3_base, ioc3_size;
 	u32 vendor, model, rev;
-	int phy;
+	int phy, err;
 
 	dev = init_etherdev(0, sizeof(struct ioc3_private));
 
 	if (!dev)
 		return -ENOMEM;
 
+	SET_MODULE_OWNER(dev);
 	ip = dev->priv;
 	memset(ip, 0, sizeof(*ip));
 
@@ -916,6 +911,11 @@
 	ioc3_base = pdev->resource[0].start;
 	ioc3_size = pdev->resource[0].end - ioc3_base;
 	ioc3 = (struct ioc3 *) ioremap(ioc3_base, ioc3_size);
+	if (!ioc3) {
+		printk(KERN_CRIT"%s: Unable to map device I/O.\n", dev->name);
+		err = -ENOMEM;
+		goto out_free;
+	}
 	ip->regs = ioc3;
 
 	spin_lock_init(&ip->ioc3_lock);
@@ -930,11 +930,8 @@
 	phy = ip->phy;
 	if (phy == -1) {
 		printk(KERN_CRIT"%s: Didn't find a PHY, goodbye.\n", dev->name);
-		ioc3_stop(dev);
-		free_irq(dev->irq, dev);
-		ioc3_free_rings(ip);
-
-		return -ENODEV;
+		err = -ENODEV;
+		goto out_stop;
 	}
 
 	mii0 = mii_read(ioc3, phy, 0);
@@ -967,11 +964,19 @@
 	dev->set_multicast_list	= ioc3_set_multicast_list;
 
 	return 0;
+
+out_stop:
+	ioc3_stop(dev);
+	free_irq(dev->irq, dev);
+	ioc3_free_rings(ip);
+out_free:
+	kfree(dev);
+	return err;
 }
 
 static int __init ioc3_probe(void)
 {
-	static int called = 0;
+	static int called;
 	int cards = 0;
 
 	if (called)
@@ -1192,10 +1197,8 @@
 	}
 }
 
-#ifdef MODULE
 MODULE_AUTHOR("Ralf Baechle <ralf@oss.sgi.com>");
 MODULE_DESCRIPTION("SGI IOC3 Ethernet driver");
-#endif /* MODULE */
 
 module_init(ioc3_probe);
 module_exit(ioc3_cleanup_module);
diff -urN linux-2.4.3-pre1/drivers/net/jazzsonic.c linux/drivers/net/jazzsonic.c
--- linux-2.4.3-pre1/drivers/net/jazzsonic.c	Tue Feb 13 13:15:04 2001
+++ linux/drivers/net/jazzsonic.c	Mon Dec  3 22:29:50 2001
@@ -62,7 +62,7 @@
 #endif
 
 /*
- * Base address and interupt of the SONIC controller on JAZZ boards
+ * Base address and interrupt of the SONIC controller on JAZZ boards
  */
 static struct {
     unsigned int port;
diff -urN linux-2.4.3-pre1/drivers/net/macmace.c linux/drivers/net/macmace.c
--- linux-2.4.3-pre1/drivers/net/macmace.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/macmace.c	Mon Dec  3 22:29:50 2001
@@ -632,7 +632,7 @@
 }
 
 /*
- *	A transmit error has occured. (We kick the transmit side from
+ *	A transmit error has occurred. (We kick the transmit side from
  *	the DMA completion)
  */
  
@@ -661,7 +661,7 @@
 }
 
 /*
- *	A receive interrupt occured.
+ *	A receive interrupt occurred.
  */
  
 static void mace68k_recv_interrupt(struct net_device *dev)
diff -urN linux-2.4.3-pre1/drivers/net/oaknet.c linux/drivers/net/oaknet.c
--- linux-2.4.3-pre1/drivers/net/oaknet.c	Sat Dec 30 11:23:14 2000
+++ linux/drivers/net/oaknet.c	Mon Dec  3 22:29:50 2001
@@ -53,7 +53,7 @@
 
 static const char *name = "National DP83902AV";
 
-static struct net_device *oaknet_devs = NULL;
+static struct net_device *oaknet_devs;
 
 
 /* Function Prototypes */
@@ -94,6 +94,7 @@
 {
 	register int i;
 	int reg0, regd;
+	int ret;
 	struct net_device tmp, *dev = NULL;
 #if 0
 	unsigned long ioaddr = OAKNET_IO_BASE; 
@@ -102,6 +103,8 @@
 #endif
 	bd_t *bip = (bd_t *)__res;
 
+	if (!ioaddr)
+		return -ENOMEM;
 	/*
 	 * This MUST happen here because of the nic_* macros
 	 * which have an implicit dependency on dev->base_addr.
@@ -110,15 +113,15 @@
 	tmp.base_addr = ioaddr;
 	dev = &tmp;
 
+	ret = -EBUSY;
 	if (!request_region(OAKNET_IO_BASE, OAKNET_IO_SIZE, name))
-		return -EBUSY;
+		goto out_unmap;
 
 	/* Quick register check to see if the device is really there. */
 
-	if ((reg0 = ei_ibp(ioaddr)) == 0xFF) {
-		release_region(OAKNET_IO_BASE, OAKNET_IO_SIZE);
-		return (ENODEV);
-	}
+	ret = -ENODEV;
+	if ((reg0 = ei_ibp(ioaddr)) == 0xFF)
+		goto out_region;
 
 	/*
 	 * That worked. Now a more thorough check, using the multicast
@@ -134,13 +137,11 @@
 
 	/* It's no good. Fix things back up and leave. */
 
+	ret = -ENODEV;
 	if (ei_ibp(ioaddr + EN0_COUNTER0) != 0) {
 		ei_obp(reg0, ioaddr);
 		ei_obp(regd, ioaddr + 0x0D);
-		dev->base_addr = 0;
-
-		release_region(dev->base_addr, OAKNET_IO_SIZE);
-		return (-ENODEV);
+		goto out_region;
 	}
 
 	/*
@@ -149,10 +150,9 @@
 	 */
 
 	dev = init_etherdev(NULL, 0);
-	if (!dev) {
-		release_region(dev->base_addr, OAKNET_IO_SIZE);
-		return (-ENOMEM);
-	}
+	ret = -ENOMEM;
+	if (!dev)
+		goto out_region;
 	SET_MODULE_OWNER(dev);
 	oaknet_devs = dev;
 
@@ -166,10 +166,10 @@
 
 	/* Allocate 8390-specific device-private area and fields. */
 
+	ret = -ENOMEM;
 	if (ethdev_init(dev)) {
 		printk(" unable to get memory for dev->priv.\n");
-		release_region(dev->base_addr, OAKNET_IO_SIZE);
-		return (-ENOMEM);
+		goto out_dev;
 	}
 
 	/*
@@ -182,12 +182,11 @@
 
 	/* Attempt to get the interrupt line */
 
+	ret = -EAGAIN;
 	if (request_irq(dev->irq, ei_interrupt, 0, name, dev)) {
 		printk("%s: unable to request interrupt %d.\n",
 		       dev->name, dev->irq);
-		kfree(dev->priv);
-		release_region(dev->base_addr, OAKNET_IO_SIZE);
-		return (EAGAIN);
+		goto out_priv;
 	}
 
 	/* Tell the world about what and where we've found. */
@@ -218,6 +217,16 @@
 	NS8390_init(dev, FALSE);
 
 	return (0);
+out_priv:
+	kfree(dev->priv);
+out_dev:
+	unregister_netdev(dev);
+	kfree(dev);
+out_region:
+	release_region(OAKNET_IO_BASE, OAKNET_IO_SIZE);
+out_unmap:
+	iounmap(ioaddr);
+	return ret;
 }
 
 /*
@@ -303,8 +312,6 @@
 	ei_obp(E8390_STOP | E8390_NODMA | E8390_PAGE0, base + E8390_CMD);
 	ei_status.txing = 0;
 	ei_status.dmaing = 0;
-
-	return;
 }
 
 /*
@@ -365,8 +372,6 @@
 
 	outb_p(ENISR_RDC, base + EN0_ISR);	/* ACK Remote DMA interrupt */
 	ei_status.dmaing &= ~0x01;
-
-	return;
 }
 
 /*
@@ -444,8 +449,6 @@
 #ifdef OAKNET_DISINT
 	restore_flags(flags);
 #endif
-
-	return;
 }
 
 /*
@@ -627,8 +630,6 @@
 	
 	ei_obp(ENISR_RDC, base + EN0_ISR);	/* Ack intr. */
 	ei_status.dmaing &= ~0x01;
-
-	return;
 }
 
 /*
@@ -636,7 +637,7 @@
  *
  * Description:
  *   This routine prints out a last-ditch informative message to the console
- *   indicating that a DMA error occured. If you see this, it's the last
+ *   indicating that a DMA error occurred. If you see this, it's the last
  *   thing you'll see.
  *
  * Input(s):
@@ -658,8 +659,6 @@
 	       "[DMAstat:%d][irqlock:%d][intr:%ld]\n",
 	       dev->name, name, ei_status.dmaing, ei_status.irqlock,
 	       dev->interrupt);
-
-	return;
 }
 
 /*
@@ -686,12 +685,13 @@
 		void *priv = oaknet_devs->priv;
 		free_irq(oaknet_devs->irq, oaknet_devs);
 		release_region(ioaddr, OAKNET_IO_SIZE);
+		iounmap(ioaddr);
 		unregister_netdev(oaknet_dev);
 		kfree(priv);
 	}
 
-	oaknet_devs = NULL;
-
+	/* Convert to loop once driver supports multiple devices. */
+	kfree(oaknet_devs);
 }
 
 module_init(oaknet_init_module);
diff -urN linux-2.4.3-pre1/drivers/net/pci-skeleton.c linux/drivers/net/pci-skeleton.c
--- linux-2.4.3-pre1/drivers/net/pci-skeleton.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/pci-skeleton.c	Mon Dec  3 22:29:50 2001
@@ -609,6 +609,11 @@
 	SET_MODULE_OWNER(dev);
 	tp = dev->priv;
 
+	/* enable device (incl. PCI PM wakeup), and bus-mastering */
+	rc = pci_enable_device (pdev);
+	if (rc)
+		goto err_out;
+
 	pio_start = pci_resource_start (pdev, 0);
 	pio_end = pci_resource_end (pdev, 0);
 	pio_flags = pci_resource_flags (pdev, 0);
@@ -623,8 +628,6 @@
 	 * we talk to the chip directly */
 	DPRINTK("PIO region size == 0x%02X\n", pio_len);
 	DPRINTK("MMIO region size == 0x%02lX\n", mmio_len);
-	if (pio_len == RTL8139B_IO_SIZE)
-		tp->chipset = CH_8139B;
 
 	/* make sure PCI base addr 0 is PIO */
 	if (!(pio_flags & IORESOURCE_IO)) {
@@ -648,24 +651,9 @@
 		goto err_out;
 	}
 
-	/* make sure our PIO region in PCI space is available */
-	if (!request_region (pio_start, pio_len, dev->name)) {
-		printk (KERN_ERR PFX "no I/O resource available, aborting\n");
-		rc = -EBUSY;
-		goto err_out;
-	}
-
-	/* make sure our MMIO region in PCI space is available */
-	if (!request_mem_region (mmio_start, mmio_len, dev->name)) {
-		printk (KERN_ERR PFX "no mem resource available, aborting\n");
-		rc = -EBUSY;
-		goto err_out_free_pio;
-	}
-
-	/* enable device (incl. PCI PM wakeup), and bus-mastering */
-	rc = pci_enable_device (pdev);
+	rc = pci_request_regions (pdev, dev->name);
 	if (rc)
-		goto err_out_free_mmio;
+		goto err_out;
 
 	pci_set_master (pdev);
 
@@ -677,7 +665,7 @@
 	if (ioaddr == NULL) {
 		printk (KERN_ERR PFX "cannot remap MMIO, aborting\n");
 		rc = -EIO;
-		goto err_out_free_mmio;
+		goto err_out_free_res;
 	}
 #endif /* USE_IO_OPS */
 
@@ -692,14 +680,7 @@
 			udelay (10);
 
 	/* Bring the chip out of low-power mode. */
-	if (tp->chipset == CH_8139B) {
-		NETDRV_W8 (Config1, NETDRV_R8 (Config1) & ~(1<<4));
-		NETDRV_W8 (Config4, NETDRV_R8 (Config4) & ~(1<<2));
-	} else {
-		/* handle RTL8139A and RTL8139 cases */
-		/* XXX from becker driver. is this right?? */
-		NETDRV_W8 (Config1, 0);
-	}
+	/* <insert device-specific code here> */
 
 #ifndef USE_IO_OPS
 	/* sanity checks -- ensure PIO and MMIO registers agree */
@@ -709,19 +690,6 @@
 	assert (inb (pio_start+RxConfig) == readb (ioaddr+RxConfig));
 #endif /* !USE_IO_OPS */
 
-	/* make sure chip thinks PIO and MMIO are enabled */
-	tmp8 = NETDRV_R8 (Config1);
-	if ((tmp8 & Cfg1_PIO) == 0) {
-		printk (KERN_ERR PFX "PIO not enabled, Cfg1=%02X, aborting\n", tmp8);
-		rc = -EIO;
-		goto err_out_iounmap;
-	}
-	if ((tmp8 & Cfg1_MMIO) == 0) {
-		printk (KERN_ERR PFX "MMIO not enabled, Cfg1=%02X, aborting\n", tmp8);
-		rc = -EIO;
-		goto err_out_iounmap;
-	}
-
 	/* identify chip attached to board */
 	tmp = NETDRV_R8 (ChipVersion);
 	for (i = ARRAY_SIZE (rtl_chip_info) - 1; i >= 0; i--)
@@ -747,15 +715,10 @@
 	*dev_out = dev;
 	return 0;
 
-err_out_iounmap:
-	assert (ioaddr > 0);
 #ifndef USE_IO_OPS
-	iounmap (ioaddr);
+err_out_free_res:
+	pci_release_regions (pdev);
 #endif /* !USE_IO_OPS */
-err_out_free_mmio:
-	release_mem_region (mmio_start, mmio_len);
-err_out_free_pio:
-	release_region (pio_start, pio_len);
 err_out:
 	unregister_netdev (dev);
 	kfree (dev);
@@ -886,7 +849,7 @@
 
 	assert (dev != NULL);
 
-	np = (struct netdrv_private *) (dev->priv);
+	np = dev->priv;
 	assert (np != NULL);
 
 	unregister_netdev (dev);
@@ -895,10 +858,7 @@
 	iounmap (np->mmio_addr);
 #endif /* !USE_IO_OPS */
 
-	release_region (pci_resource_start (pdev, 0),
-			pci_resource_len (pdev, 0));
-	release_mem_region (pci_resource_start (pdev, 1),
-			    pci_resource_len (pdev, 1));
+	pci_release_regions (pdev);
 
 #ifndef NETDRV_NDEBUG
 	/* poison memory before freeing */
diff -urN linux-2.4.3-pre1/drivers/net/rclanmtl.h linux/drivers/net/rclanmtl.h
--- linux-2.4.3-pre1/drivers/net/rclanmtl.h	Sun Feb  4 10:05:30 2001
+++ linux/drivers/net/rclanmtl.h	Mon Dec  3 22:29:50 2001
@@ -445,7 +445,7 @@
  /*
  ** Disable and Enable I2O interrupts.  I2O interrupts are enabled at Init time
  ** but can be disabled and re-enabled through these two function calls.
- ** Packets will still be put into any posted recieved buffers and packets will
+ ** Packets will still be put into any posted received buffers and packets will
  ** be sent through RCI2OSendPacket() functions.  Disabling I2O interrupts
  ** will prevent hardware interrupt to host even though the outbound I2O msg
  ** queue is not emtpy.
diff -urN linux-2.4.3-pre1/drivers/net/smc-mca.c linux/drivers/net/smc-mca.c
--- linux-2.4.3-pre1/drivers/net/smc-mca.c	Mon Dec 11 13:38:29 2000
+++ linux/drivers/net/smc-mca.c	Mon Dec  3 22:29:50 2001
@@ -213,7 +213,7 @@
 	dev->mem_start = 0;
 	num_pages      = 40;
 
-	switch (j) {	/* 'j' = card-# in const array above [hs] */
+	switch (adapter) {	/* card-# in const array above [hs] */
 		case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A:
 		case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A:
 		{
@@ -373,9 +373,9 @@
 
 #ifdef notdef
 	/* Officially this is what we are doing, but the readl() is faster */
-	memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+	isa_memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
 #else
-	((unsigned int*)hdr)[0] = readl(hdr_start);
+	((unsigned int*)hdr)[0] = isa_readl(hdr_start);
 #endif
 }
 
@@ -390,12 +390,12 @@
 	if (xfer_start + count > dev->rmem_end) {
 		/* We must wrap the input move. */
 		int semi_count = dev->rmem_end - xfer_start;
-		memcpy_fromio(skb->data, xfer_start, semi_count);
+		isa_memcpy_fromio(skb->data, xfer_start, semi_count);
 		count -= semi_count;
-		memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+		isa_memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
 	} else {
 		/* Packet is in one chunk -- we can copy + cksum. */
-		eth_io_copy_and_sum(skb, xfer_start, count, 0);
+		isa_eth_io_copy_and_sum(skb, xfer_start, count, 0);
 	}
 
 }
@@ -405,7 +405,7 @@
 {
 	unsigned long shmem = dev->mem_start + ((start_page - START_PG) << 8);
 
-	memcpy_toio(shmem, buf, count);
+	isa_memcpy_toio(shmem, buf, count);
 }
 
 static int ultramca_close_card(struct net_device *dev)
diff -urN linux-2.4.3-pre1/drivers/net/smc9194.c linux/drivers/net/smc9194.c
--- linux-2.4.3-pre1/drivers/net/smc9194.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/smc9194.c	Mon Dec  3 22:29:50 2001
@@ -238,12 +238,12 @@
  . This is a separate procedure to handle the receipt of a packet, to
  . leave the interrupt code looking slightly cleaner
 */
-inline static void smc_rcv( struct net_device *dev );
+static inline void smc_rcv( struct net_device *dev );
 /*
  . This handles a TX interrupt, which is only called when an error
  . relating to a packet is sent.
 */
-inline static void smc_tx( struct net_device * dev );
+static inline void smc_tx( struct net_device * dev );
 
 /*
  ------------------------------------------------------------
@@ -616,7 +616,7 @@
 	if ( packet_no & 0x80 ) {
 		/* or isn't there?  BAD CHIP! */
 		printk(KERN_DEBUG CARDNAME": Memory allocation failed. \n");
-		dev_kfree_skb_irq(skb);
+		dev_kfree_skb_any(skb);
 		lp->saved_skb = NULL;
 		netif_wake_queue(dev);
 		return;
@@ -679,7 +679,7 @@
 	PRINTK2((CARDNAME": Sent packet of length %d \n",length));
 
 	lp->saved_skb = NULL;
-	dev_kfree_skb_irq (skb);
+	dev_kfree_skb_any (skb);
 
 	dev->trans_start = jiffies;
 
@@ -1341,9 +1341,9 @@
 		skb = dev_alloc_skb( packet_length + 5);
 
 		if ( skb == NULL ) {
-			printk(KERN_NOTICE CARDNAME
-			": Low memory, packet dropped.\n");
+			printk(KERN_NOTICE CARDNAME ": Low memory, packet dropped.\n");
 			lp->stats.rx_dropped++;
+			goto done;
 		}
 
 		/*
@@ -1396,6 +1396,8 @@
 			lp->stats.rx_length_errors++;
 		if ( status & RS_BADCRC)	lp->stats.rx_crc_errors++;
 	}
+
+done:
 	/*  error or good, tell the card to get rid of this packet */
 	outw( MC_RELEASE, ioaddr + MMU_CMD );
 
diff -urN linux-2.4.3-pre1/drivers/net/sonic.h linux/drivers/net/sonic.h
--- linux-2.4.3-pre1/drivers/net/sonic.h	Tue Jul 11 11:12:24 2000
+++ linux/drivers/net/sonic.h	Mon Dec  3 22:29:50 2001
@@ -445,7 +445,7 @@
 struct sonic_local {
     sonic_cda_t   cda;                     /* virtual CPU address of CDA */
     sonic_td_t    tda[SONIC_NUM_TDS];      /* transmit descriptor area */
-    sonic_rr_t    rra[SONIC_NUM_RRS];      /* receive resource arrea */
+    sonic_rr_t    rra[SONIC_NUM_RRS];      /* receive resource area */
     sonic_rd_t    rda[SONIC_NUM_RDS];      /* receive descriptor area */
     struct sk_buff* tx_skb[SONIC_NUM_TDS]; /* skbuffs for packets to transmit */
     unsigned int  tx_laddr[SONIC_NUM_TDS]; /* logical DMA address fro skbuffs */
diff -urN linux-2.4.3-pre1/drivers/net/tokenring/tms380tr.c linux/drivers/net/tokenring/tms380tr.c
--- linux-2.4.3-pre1/drivers/net/tokenring/tms380tr.c	Fri Feb 16 16:02:36 2001
+++ linux/drivers/net/tokenring/tms380tr.c	Mon Dec  3 22:29:50 2001
@@ -2203,6 +2203,7 @@
 				skb_trim(skb,Length);
 				skb->protocol = tr_type_trans(skb,dev);
 				netif_rx(skb);
+				dev->last_rx = jiffies;
 			}
 		}
 		else	/* Invalid frame */
diff -urN linux-2.4.3-pre1/drivers/net/wan/comx-proto-fr.c linux/drivers/net/wan/comx-proto-fr.c
--- linux-2.4.3-pre1/drivers/net/wan/comx-proto-fr.c	Mon Sep 18 15:02:03 2000
+++ linux/drivers/net/wan/comx-proto-fr.c	Mon Dec  3 22:29:50 2001
@@ -505,9 +505,9 @@
 		struct sk_buff *newskb=skb_clone(skb, GFP_ATOMIC);
 		newskb->dev=fr->master;
 		dev_queue_xmit(newskb);
-		dev_kfree_skb(skb);
-		ch->stats.tx_packets++;
 		ch->stats.tx_bytes += skb->len;
+		ch->stats.tx_packets++;
+		dev_kfree_skb(skb);
 	} else {
 		netif_stop_queue(dev);
 		for (; dir ; dir = dir->next) {
diff -urN linux-2.4.3-pre1/drivers/net/wan/comx-proto-lapb.c linux/drivers/net/wan/comx-proto-lapb.c
--- linux-2.4.3-pre1/drivers/net/wan/comx-proto-lapb.c	Sat Nov 11 19:02:39 2000
+++ linux/drivers/net/wan/comx-proto-lapb.c	Mon Dec  3 22:29:50 2001
@@ -311,6 +311,7 @@
 		skb->pkt_type = PACKET_HOST;
 
 		netif_rx(skb);
+		ch->dev->last_rx = jiffies;
 	}
 
 	for (; comxdir; comxdir = comxdir->next) {
@@ -350,6 +351,7 @@
 		skb->pkt_type = PACKET_HOST;
 
 		netif_rx(skb);
+		ch->dev->last_rx = jiffies;
 	}
 
 	for (; comxdir; comxdir = comxdir->next) {
diff -urN linux-2.4.3-pre1/drivers/net/wan/comx.c linux/drivers/net/wan/comx.c
--- linux-2.4.3-pre1/drivers/net/wan/comx.c	Thu Nov 16 14:08:25 2000
+++ linux/drivers/net/wan/comx.c	Mon Dec  3 22:29:50 2001
@@ -380,6 +380,7 @@
 	}
 	if (skb) {
 		netif_rx(skb);
+		dev->last_rx = jiffies;
 	}
 	return 0;
 }
diff -urN linux-2.4.3-pre1/drivers/net/wan/cycx_x25.c linux/drivers/net/wan/cycx_x25.c
--- linux-2.4.3-pre1/drivers/net/wan/cycx_x25.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/wan/cycx_x25.c	Mon Dec  3 22:29:50 2001
@@ -3,7 +3,7 @@
 *
 * Author:	Arnaldo Carvalho de Melo <acme@conectiva.com.br>
 *
-* Copyright:	(c) 1998-2000 Arnaldo Carvalho de Melo
+* Copyright:	(c) 1998-2001 Arnaldo Carvalho de Melo
 *
 * Based on sdla_x25.c by Gene Kozin <genek@compuserve.com>
 *
@@ -12,6 +12,7 @@
 *		as published by the Free Software Foundation; either version
 *		2 of the License, or (at your option) any later version.
 * ============================================================================
+* 2001/01/12	acme		use dev_kfree_skb_irq on interrupt context
 * 2000/04/02	acme		dprintk, cycx_debug
 * 				fixed the bug introduced in get_dev_by_lcn and
 * 				get_dev_by_dte_addr by the anonymous hacker
@@ -794,7 +795,7 @@
 
 	if (skb_tailroom(skb) < pktlen) {
 		/* No room for the packet. Call off the whole thing! */
-		dev_kfree_skb(skb);
+		dev_kfree_skb_irq(skb);
 		chan->rx_skb = NULL;
 
 		if (bitm)
@@ -812,14 +813,14 @@
 	if (bitm)
 		return; /* more data is coming */
 
-	dev->last_rx = jiffies;		/* timestamp */
 	chan->rx_skb = NULL;		/* dequeue packet */
 
 	++chan->ifstats.rx_packets;
-	chan->ifstats.rx_bytes += skb->len;
+	chan->ifstats.rx_bytes += pktlen;
 
 	skb->mac.raw = skb->data;
 	netif_rx(skb);
+	dev->last_rx = jiffies;		/* timestamp */
 }
 
 /* Connect interrupt handler. */
diff -urN linux-2.4.3-pre1/drivers/net/wan/dlci.c linux/drivers/net/wan/dlci.c
--- linux-2.4.3-pre1/drivers/net/wan/dlci.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/wan/dlci.c	Mon Dec  3 22:29:50 2001
@@ -227,8 +227,10 @@
 		/* we've set up the protocol, so discard the header */
 		skb->mac.raw = skb->data; 
 		skb_pull(skb, header);
+		dlp->stats.rx_bytes += skb->len;
 		netif_rx(skb);
 		dlp->stats.rx_packets++;
+		dev->last_rx = jiffies;
 	}
 	else
 		dev_kfree_skb(skb);
diff -urN linux-2.4.3-pre1/drivers/net/wavelan.c linux/drivers/net/wavelan.c
--- linux-2.4.3-pre1/drivers/net/wavelan.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/wavelan.c	Mon Dec  3 22:29:50 2001
@@ -3686,7 +3686,7 @@
 	 * the spinlock. */
 	spin_lock(&lp->spinlock);
 
-	/* Check modem interupt */
+	/* Check modem interrupt */
 	if ((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR) {
 		u8 dce_status;
 
@@ -3913,8 +3913,6 @@
 	}
 	wv_splx(lp, &flags);
 	
-	MOD_INC_USE_COUNT;
-
 #ifdef DEBUG_CALLBACK_TRACE
 	printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name);
 #endif
@@ -3947,8 +3945,6 @@
 
 	free_irq(dev->irq, dev);
 
-	MOD_DEC_USE_COUNT;
-
 #ifdef DEBUG_CALLBACK_TRACE
 	printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name);
 #endif
@@ -4019,8 +4015,10 @@
 
 	/* Initialize device structures */
 	dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL);
-	if (dev->priv == NULL)
+	if (dev->priv == NULL) {
+		release_region(ioaddr, sizeof(ha_t));
 		return -ENOMEM;
+	}
 	memset(dev->priv, 0x00, sizeof(net_local));
 	lp = (net_local *) dev->priv;
 
@@ -4045,6 +4043,7 @@
 	 */
 	ether_setup(dev);
 
+	SET_MODULE_OWNER(dev);
 	dev->open = wavelan_open;
 	dev->stop = wavelan_close;
 	dev->hard_start_xmit = wavelan_packet_xmit;
@@ -4287,7 +4286,7 @@
 
 /*
  * This software may only be used and distributed
- * according to the terms of the GNU Public License.
+ * according to the terms of the GNU General Public License.
  *
  * This software was developed as a component of the
  * Linux operating system.
diff -urN linux-2.4.3-pre1/drivers/net/winbond-840.c linux/drivers/net/winbond-840.c
--- linux-2.4.3-pre1/drivers/net/winbond-840.c	Tue Feb 13 13:15:05 2001
+++ linux/drivers/net/winbond-840.c	Mon Dec  3 22:29:50 2001
@@ -1,6 +1,6 @@
 /* winbond-840.c: A Linux PCI network adapter skeleton device driver. */
 /*
-	Written 1998-2000 by Donald Becker.
+	Written 1998-2001 by Donald Becker.
 
 	This software may be used and distributed according to the terms of
 	the GNU General Public License (GPL), incorporated herein by reference.
@@ -198,7 +198,8 @@
         PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4,
         PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400,
 };
-enum chip_capability_flags {CanHaveMII=1, HasBrokenTx=2};
+enum chip_capability_flags {
+	CanHaveMII=1, HasBrokenTx=2, AlwaysFDX=4, FDXOnNoMII=8,};
 #ifdef USE_IO_OPS
 #define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER)
 #else
@@ -206,8 +207,9 @@
 #endif
 
 static struct pci_device_id w840_pci_tbl[] __devinitdata = {
-	{ 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-	{ 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
+	{ 0x1050, 0x0840, PCI_ANY_ID, 0x8153,     0, 0, 0 },
+	{ 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
+	{ 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 },
 	{ 0, }
 };
 MODULE_DEVICE_TABLE(pci, w840_pci_tbl);
@@ -223,6 +225,9 @@
         int drv_flags;                          /* Driver use, intended as capability flags. */
 };
 static struct pci_id_info pci_id_tbl[] = {
+	{"Winbond W89c840",			/* Sometime a Level-One switch card. */
+	 { 0x08401050, 0xffffffff, 0x81530000, 0xffff0000 },
+	 W840_FLAGS, 128, CanHaveMII | HasBrokenTx | FDXOnNoMII},
 	{"Winbond W89c840", { 0x08401050, 0xffffffff, },
 	 W840_FLAGS, 128, CanHaveMII | HasBrokenTx},
 	{"Compex RL100-ATX", { 0x201111F6, 0xffffffff,},
@@ -305,21 +310,17 @@
 enum desc_status_bits {
 	DescOwn=0x80000000, DescEndRing=0x02000000, DescUseLink=0x01000000,
 	DescWholePkt=0x60000000, DescStartPkt=0x20000000, DescEndPkt=0x40000000,
-};
-
-/* Bits in w840_tx_desc.length */
-enum desc_length_bits {
 	DescIntr=0x80000000,
 };
 
 #define PRIV_ALIGN	15 	/* Required alignment mask */
+#define MII_CNT		1 /* winbond only supports one MII */
 struct netdev_private {
 	struct w840_rx_desc *rx_ring;
 	dma_addr_t	rx_addr[RX_RING_SIZE];
 	struct w840_tx_desc *tx_ring;
 	dma_addr_t	tx_addr[RX_RING_SIZE];
 	dma_addr_t ring_dma_addr;
-	struct pci_dev *pdev;
 	/* The addresses of receive-in-place skbuffs. */
 	struct sk_buff* rx_skbuff[RX_RING_SIZE];
 	/* The saved address of a sent-in-place packet/buffer, for later free(). */
@@ -329,6 +330,7 @@
 	/* Frequently used values: keep some adjacent for cache effect. */
 	spinlock_t lock;
 	int chip_id, drv_flags;
+	struct pci_dev *pci_dev;
 	int csr6;
 	struct w840_rx_desc *rx_head_desc;
 	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
@@ -344,7 +346,7 @@
 	/* MII transceiver section. */
 	int mii_cnt;						/* MII device addresses. */
 	u16 advertising;					/* NWay media advertisement */
-	unsigned char phys[2];				/* MII device addresses. */
+	unsigned char phys[MII_CNT];		/* MII device addresses, but only the first is used */
 };
 
 static int  eeprom_read(long ioaddr, int location);
@@ -377,14 +379,17 @@
 	struct netdev_private *np;
 	static int find_cnt;
 	int chip_idx = ent->driver_data;
-	int irq = pdev->irq;
+	int irq;
 	int i, option = find_cnt < MAX_UNITS ? options[find_cnt] : 0;
 	long ioaddr;
 
-	if (pci_enable_device(pdev))
-		return -EIO;
+	i = pci_enable_device(pdev);
+	if (i) return i;
+
 	pci_set_master(pdev);
 
+	irq = pdev->irq;
+
 	if(!pci_dma_supported(pdev,0xFFFFffff)) {
 		printk(KERN_WARNING "Winbond-840: Device %s disabled due to DMA limitations.\n",
 				pdev->name);
@@ -395,17 +400,16 @@
 		return -ENOMEM;
 	SET_MODULE_OWNER(dev);
 
+	if (pci_request_regions(pdev, dev->name))
+		goto err_out_netdev;
+
 #ifdef USE_IO_OPS
 	ioaddr = pci_resource_start(pdev, 0);
-	if (!request_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name))
-		goto err_out_netdev;
 #else
 	ioaddr = pci_resource_start(pdev, 1);
-	if (!request_mem_region(ioaddr, pci_id_tbl[chip_idx].io_size, dev->name))
-		goto err_out_netdev;
 	ioaddr = (long) ioremap (ioaddr, pci_id_tbl[chip_idx].io_size);
 	if (!ioaddr)
-		goto err_out_iomem;
+		goto err_out_free_res;
 #endif
 
 	printk(KERN_INFO "%s: %s at 0x%lx, ",
@@ -427,14 +431,14 @@
 	dev->irq = irq;
 
 	np = dev->priv;
+	np->pci_dev = pdev;
 	np->chip_id = chip_idx;
 	np->drv_flags = pci_id_tbl[chip_idx].drv_flags;
-	np->pdev = pdev;
 	spin_lock_init(&np->lock);
 	
-	pdev->driver_data = dev;
+	pci_set_drvdata(pdev, dev);
 
-	if (dev->mem_start)
+	if (dev->mem_start && dev->mem_start != ~0)
 		option = dev->mem_start;
 
 	/* The lower four bits are the media type. */
@@ -463,7 +467,7 @@
 
 	if (np->drv_flags & CanHaveMII) {
 		int phy, phy_idx = 0;
-		for (phy = 1; phy < 32 && phy_idx < 4; phy++) {
+		for (phy = 1; phy < 32 && phy_idx < MII_CNT; phy++) {
 			int mii_status = mdio_read(dev, phy, 1);
 			if (mii_status != 0xffff  &&  mii_status != 0x0000) {
 				np->phys[phy_idx++] = phy;
@@ -484,9 +488,8 @@
 	return 0;
 
 #ifndef USE_IO_OPS
-err_out_iomem:
-	release_mem_region(pci_resource_start(pdev, 1),
-			   pci_id_tbl[chip_idx].io_size);
+err_out_free_res:
+	pci_release_regions(pdev);
 #endif
 err_out_netdev:
 	unregister_netdev (dev);
@@ -535,6 +538,7 @@
 		eeprom_delay(ee_addr);
 	}
 	writel(EE_ChipSelect, ee_addr);
+	eeprom_delay(ee_addr);
 
 	for (i = 16; i > 0; i--) {
 		writel(EE_ChipSelect | EE_ShiftClk, ee_addr);
@@ -747,7 +751,7 @@
 		if (skb == NULL)
 			break;
 		skb->dev = dev;			/* Mark as being used by this device. */
-		np->rx_addr[i] = pci_map_single(np->pdev,skb->tail,
+		np->rx_addr[i] = pci_map_single(np->pci_dev,skb->tail,
 					skb->len,PCI_DMA_FROMDEVICE);
 
 		np->rx_ring[i].buffer1 = cpu_to_le32(np->rx_addr[i]);
@@ -778,7 +782,7 @@
 	for (i = 0; i < RX_RING_SIZE; i++) {
 		np->rx_ring[i].status = 0;
 		if (np->rx_skbuff[i]) {
-			pci_unmap_single(np->pdev,
+			pci_unmap_single(np->pci_dev,
 						np->rx_addr[i],
 						np->rx_skbuff[i]->len,
 						PCI_DMA_FROMDEVICE);
@@ -788,7 +792,7 @@
 	}
 	for (i = 0; i < TX_RING_SIZE; i++) {
 		if (np->tx_skbuff[i]) {
-			pci_unmap_single(np->pdev,
+			pci_unmap_single(np->pci_dev,
 						np->tx_addr[i],
 						np->tx_skbuff[i]->len,
 						PCI_DMA_TODEVICE);
@@ -832,7 +836,7 @@
 	if (x86 <= 4)
 		printk(KERN_INFO "%s: This is a 386/486 PCI system, setting cache "
 			   "alignment to %x.\n", dev->name,
-			   (x86 <= 4 ? 0x4810 : 0x8010));
+			   (x86 <= 4 ? 0x4810 : 0xE010));
 #endif
 #else
 	writel(0xE010, ioaddr + PCIBusCfg);
@@ -909,7 +913,7 @@
 
 	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
 
-	np->rx_ring = pci_alloc_consistent(np->pdev,
+	np->rx_ring = pci_alloc_consistent(np->pci_dev,
 			sizeof(struct w840_rx_desc)*RX_RING_SIZE +
 			sizeof(struct w840_tx_desc)*TX_RING_SIZE,
 			&np->ring_dma_addr);
@@ -933,7 +937,7 @@
 	entry = np->cur_tx % TX_RING_SIZE;
 
 	np->tx_skbuff[entry] = skb;
-	np->tx_addr[entry] = pci_map_single(np->pdev,
+	np->tx_addr[entry] = pci_map_single(np->pci_dev,
 				skb->data,skb->len, PCI_DMA_TODEVICE);
 	np->tx_ring[entry].buffer1 = cpu_to_le32(np->tx_addr[entry]);
 	len2 = 0;
@@ -1040,7 +1044,7 @@
 				np->stats.tx_packets++;
 			}
 			/* Free the original skb. */
-			pci_unmap_single(np->pdev,np->tx_addr[entry],
+			pci_unmap_single(np->pci_dev,np->tx_addr[entry],
 						np->tx_skbuff[entry]->len,
 						PCI_DMA_TODEVICE);
 			np->tx_q_bytes -= np->tx_skbuff[entry]->len;
@@ -1136,7 +1140,7 @@
 				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
 				skb->dev = dev;
 				skb_reserve(skb, 2);	/* 16 byte align the IP header */
-				pci_dma_sync_single(np->pdev,np->rx_addr[entry],
+				pci_dma_sync_single(np->pci_dev,np->rx_addr[entry],
 							np->rx_skbuff[entry]->len,
 							PCI_DMA_FROMDEVICE);
 				/* Call copy + cksum if available. */
@@ -1148,7 +1152,7 @@
 					   pkt_len);
 #endif
 			} else {
-				pci_unmap_single(np->pdev,np->rx_addr[entry],
+				pci_unmap_single(np->pci_dev,np->rx_addr[entry],
 							np->rx_skbuff[entry]->len,
 							PCI_DMA_FROMDEVICE);
 				skb_put(skb = np->rx_skbuff[entry], pkt_len);
@@ -1187,7 +1191,7 @@
 			if (skb == NULL)
 				break;			/* Better luck next round. */
 			skb->dev = dev;			/* Mark as being used by this device. */
-			np->rx_addr[entry] = pci_map_single(np->pdev,
+			np->rx_addr[entry] = pci_map_single(np->pci_dev,
 							skb->tail,
 							skb->len, PCI_DMA_FROMDEVICE);
 			np->rx_ring[entry].buffer1 = cpu_to_le32(np->rx_addr[entry]);
@@ -1379,23 +1383,19 @@
 
 static void __devexit w840_remove1 (struct pci_dev *pdev)
 {
-	struct net_device *dev = pdev->driver_data;
+	struct net_device *dev = pci_get_drvdata(pdev);
 	
 	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
 	if (dev) {
-		struct netdev_private *np = (void *)(dev->priv);
 		unregister_netdev(dev);
-#ifdef USE_IO_OPS
-		release_region(dev->base_addr, pci_id_tbl[np->chip_id].io_size);
-#else
-		release_mem_region(pci_resource_start(pdev, 1),
-				   pci_id_tbl[np->chip_id].io_size);
+		pci_release_regions(pdev);
+#ifndef USE_IO_OPS
 		iounmap((char *)(dev->base_addr));
 #endif
 		kfree(dev);
 	}
 
-	pdev->driver_data = NULL;
+	pci_set_drvdata(pdev, NULL);
 }
 
 static struct pci_driver w840_driver = {
diff -urN linux-2.4.3-pre1/drivers/net/zlib.c linux/drivers/net/zlib.c
--- linux-2.4.3-pre1/drivers/net/zlib.c	Mon Jul  5 20:35:18 1999
+++ linux/drivers/net/zlib.c	Mon Dec  3 22:29:50 2001
@@ -433,7 +433,7 @@
     int nice_match; /* Stop searching when current match exceeds this */
 
                 /* used by trees.c: */
-    /* Didn't use ct_data typedef below to supress compiler warning */
+    /* Didn't use ct_data typedef below to suppress compiler warning */
     struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */
     struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
     struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */
@@ -689,7 +689,7 @@
 
 /* ===========================================================================
  * Update a hash value with the given input byte
- * IN  assertion: all calls to to UPDATE_HASH are made with consecutive
+ * IN  assertion: all calls to UPDATE_HASH are made with consecutive
  *    input characters, so that a running hash key can be computed from the
  *    previous key instead of complete recalculation each time.
  */
@@ -700,7 +700,7 @@
  * Insert string str in the dictionary and set match_head to the previous head
  * of the hash chain (the most recent string with same hash key). Return
  * the previous length of the hash chain.
- * IN  assertion: all calls to to INSERT_STRING are made with consecutive
+ * IN  assertion: all calls to INSERT_STRING are made with consecutive
  *    input characters and the first MIN_MATCH bytes of str are valid
  *    (except for the last MIN_MATCH-1 bytes of the input file).
  */
diff -urN linux-2.4.3-pre1/drivers/parport/ieee1284_ops.c linux/drivers/parport/ieee1284_ops.c
--- linux-2.4.3-pre1/drivers/parport/ieee1284_ops.c	Mon May 15 11:16:30 2000
+++ linux/drivers/parport/ieee1284_ops.c	Mon Dec  3 22:29:50 2001
@@ -50,9 +50,6 @@
 	if (port->irq != PARPORT_IRQ_NONE) {
 		parport_enable_irq (port);
 		no_irq = 0;
-
-		/* Clear out previous irqs. */
-		while (!down_trylock (&port->physport->ieee1284.irq));
 	}
 
 	port->physport->ieee1284.phase = IEEE1284_PH_FWD_DATA;
@@ -192,6 +189,7 @@
 			DPRINTK (KERN_DEBUG
 				 "%s: Nibble timeout at event 9 (%d bytes)\n",
 				 port->name, i/2);
+			parport_frob_control (port, PARPORT_CONTROL_AUTOFD, 0);
 			break;
 		}
 
diff -urN linux-2.4.3-pre1/drivers/parport/parport_pc.c linux/drivers/parport/parport_pc.c
--- linux-2.4.3-pre1/drivers/parport/parport_pc.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/parport/parport_pc.c	Mon Dec  3 22:29:50 2001
@@ -89,6 +89,7 @@
 } superios[NR_SUPERIOS] __devinitdata = { {0,},};
 
 static int user_specified __devinitdata = 0;
+static int registered_parport;
 
 /* frob_control, but for ECR */
 static void frob_econtrol (struct parport *pb, unsigned char m,
@@ -2605,8 +2606,10 @@
 	count += parport_pc_find_nonpci_ports (autoirq, autodma);
 
 	r = pci_register_driver (&parport_pc_pci_driver);
-	if (r > 0)
+	if (r >= 0) {
+		registered_parport = 1;
 		count += r;
+	}
 
 	return count;
 }
@@ -2667,6 +2670,7 @@
 	/* Work out how many ports we have, then get parport_share to parse
 	   the irq values. */
 	unsigned int i;
+	int ret;
 	for (i = 0; i < PARPORT_PC_MAX_PORTS && io[i]; i++);
 	if (i) {
 		if (parport_parse_irqs(i, irq, irqval)) return 1;
@@ -2691,7 +2695,11 @@
 			}
 	}
 
-	return !parport_pc_init (io, io_hi, irqval, dmaval);
+	ret = !parport_pc_init (io, io_hi, irqval, dmaval);
+	if (ret && registered_parport)
+		pci_unregister_driver (&parport_pc_pci_driver);
+
+	return ret;
 }
 
 void cleanup_module(void)
diff -urN linux-2.4.3-pre1/drivers/pci/pci.c linux/drivers/pci/pci.c
--- linux-2.4.3-pre1/drivers/pci/pci.c	Mon Dec  3 22:29:33 2001
+++ linux/drivers/pci/pci.c	Mon Dec  3 22:29:50 2001
@@ -63,9 +63,9 @@
 /**
  * pci_find_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id
  * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
- * @device: PCI device id to match, or %PCI_ANY_ID to match all vendor ids
+ * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
  * @ss_vendor: PCI subsystem vendor id to match, or %PCI_ANY_ID to match all vendor ids
- * @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all vendor ids
+ * @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all device ids
  * @from: Previous PCI device found in search, or %NULL for new search.
  *
  * Iterates through the list of known PCI devices.  If a PCI device is
@@ -97,7 +97,7 @@
 /**
  * pci_find_device - begin or continue searching for a PCI device by vendor/device id
  * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids
- * @device: PCI device id to match, or %PCI_ANY_ID to match all vendor ids
+ * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids
  * @from: Previous PCI device found in search, or %NULL for new search.
  *
  * Iterates through the list of known PCI devices.  If a PCI device is
@@ -122,7 +122,8 @@
  * found with a matching @class, a pointer to its device structure is
  * returned.  Otherwise, %NULL is returned.
  * A new search is initiated by passing %NULL to the @from argument.
- * Otherwise if @from is not %NULL, searches continue from next device on the global list.
+ * Otherwise if @from is not %NULL, searches continue from next device
+ * on the global list.
  */
 struct pci_dev *
 pci_find_class(unsigned int class, const struct pci_dev *from)
@@ -144,9 +145,9 @@
  * @cap: capability code
  *
  * Tell if a device supports a given PCI capability.
- * Returns the address of the requested capability structure within the device's PCI 
- * configuration space or 0 in case the device does not support it.
- * Possible values for @flags:
+ * Returns the address of the requested capability structure within the
+ * device's PCI configuration space or 0 in case the device does not
+ * support it.  Possible values for @cap:
  *
  *  %PCI_CAP_ID_PM           Power Management 
  *
@@ -387,10 +388,10 @@
 static LIST_HEAD(pci_drivers);
 
 /**
- * pci_match_device - Tell if a PCI device structure has a matching PCI device
+ * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure
  * @ids: array of PCI device id structures to search in
  * @dev: the PCI device structure to match against
- *
+ * 
  * Used by a driver to check whether a PCI device present in the
  * system is in its list of supported devices.Returns the matching
  * pci_device_id structure or %NULL if there is no match.
@@ -441,7 +442,8 @@
  * 
  * Adds the driver structure to the list of registered drivers
  * Returns the number of pci devices which were claimed by the driver
- * during registration.
+ * during registration.  The driver remains registered even if the
+ * return value is zero.
  */
 int
 pci_register_driver(struct pci_driver *drv)
@@ -462,8 +464,9 @@
  * @drv: the driver structure to unregister
  * 
  * Deletes the driver structure from the list of registered PCI drivers,
- * gives it a chance to clean up and marks the devices for which it
- * was responsible as driverless.
+ * gives it a chance to clean up by calling its remove() function for
+ * each device it was responsible for, and marks those devices as
+ * driverless.
  */
 
 void
@@ -1367,6 +1370,8 @@
 EXPORT_SYMBOL(pci_root_buses);
 EXPORT_SYMBOL(pci_enable_device);
 EXPORT_SYMBOL(pci_find_capability);
+EXPORT_SYMBOL(pci_release_regions);
+EXPORT_SYMBOL(pci_request_regions);
 EXPORT_SYMBOL(pci_find_class);
 EXPORT_SYMBOL(pci_find_device);
 EXPORT_SYMBOL(pci_find_slot);
diff -urN linux-2.4.3-pre1/drivers/pcmcia/cardbus.c linux/drivers/pcmcia/cardbus.c
--- linux-2.4.3-pre1/drivers/pcmcia/cardbus.c	Fri Feb 16 16:02:36 2001
+++ linux/drivers/pcmcia/cardbus.c	Mon Dec  3 22:29:50 2001
@@ -288,7 +288,6 @@
 			if (res->flags)
 				pci_assign_resource(dev, r);
 		}
-		pci_enable_device(dev); /* XXX check return */
 
 		/* Does this function have an interrupt at all? */
 		pci_readb(dev, PCI_INTERRUPT_PIN, &irq_pin);
@@ -297,6 +296,7 @@
 			pci_writeb(dev, PCI_INTERRUPT_LINE, irq);
 		}
 
+		pci_enable_device(dev); /* XXX check return */
 		pci_insert_device(dev, bus);
 	}
 
diff -urN linux-2.4.3-pre1/drivers/pcmcia/i82365.c linux/drivers/pcmcia/i82365.c
--- linux-2.4.3-pre1/drivers/pcmcia/i82365.c	Fri Feb 16 16:02:36 2001
+++ linux/drivers/pcmcia/i82365.c	Mon Dec  3 22:29:50 2001
@@ -210,13 +210,13 @@
 		  PCI_COMMAND_MASTER|PCI_COMMAND_WAIT)
 
 /* These definitions must match the pcic table! */
-typedef enum pcic_id {
 #ifdef CONFIG_ISA
+typedef enum pcic_id {
     IS_I82365A, IS_I82365B, IS_I82365DF,
     IS_IBM, IS_RF5Cx96, IS_VLSI, IS_VG468, IS_VG469,
     IS_PD6710, IS_PD672X, IS_VT83C469,
-#endif
 } pcic_id;
+#endif
 
 /* Flags for classifying groups of controllers */
 #define IS_VADEM	0x0001
diff -urN linux-2.4.3-pre1/drivers/scsi/3w-xxxx.c linux/drivers/scsi/3w-xxxx.c
--- linux-2.4.3-pre1/drivers/scsi/3w-xxxx.c	Mon Dec  3 22:29:33 2001
+++ linux/drivers/scsi/3w-xxxx.c	Mon Dec  3 22:29:50 2001
@@ -5,7 +5,7 @@
    Modifications By: Joel Jacobson <linux@3ware.com>
    		     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
 
-   Copyright (C) 1999-2000 3ware Inc.
+   Copyright (C) 1999-2001 3ware Inc.
 
    Kernel compatablity By: 	Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
@@ -67,6 +67,15 @@
                  systems.
    08/21/00    - release previously allocated resources on failure at
                  tw_allocate_memory (acme)
+   1.02.00.003 - Fix tw_interrupt() to report error to scsi layer when
+                 controller status is non-zero.
+                 Added handling of request_sense opcode.
+                 Fix possible null pointer dereference in 
+                 tw_reset_device_extension()
+   1.02.00.004 - Add support for device id of 3ware 7000 series controllers.
+                 Make tw_setfeature() call with interrupts disabled.
+                 Register interrupt handler before enabling interrupts.
+                 Clear attention interrupt before draining aen queue.
 */
 
 #include <linux/module.h>
@@ -112,7 +121,7 @@
 };
 
 /* Globals */
-char *tw_driver_version="1.02.00.002";
+char *tw_driver_version="1.02.00.004";
 TW_Device_Extension *tw_device_extension_list[TW_MAX_SLOT];
 int tw_device_extension_count = 0;
 
@@ -581,174 +590,179 @@
 	struct pci_dev *tw_pci_dev = NULL;
 	u32 status_reg_value;
 	unsigned char c = 1;
+	int i;
+	u16 device[TW_NUMDEVICES] = { TW_DEVICE_ID, TW_DEVICE_ID2 };
 
 	dprintk(KERN_NOTICE "3w-xxxx: tw_findcards()\n");
-	while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, TW_DEVICE_ID, tw_pci_dev))) {
-		if (pci_enable_device(tw_pci_dev))
-			continue;
-		/* Prepare temporary device extension */
-		tw_dev=(TW_Device_Extension *)kmalloc(sizeof(TW_Device_Extension), GFP_ATOMIC);
-		if (tw_dev == NULL) {
-			printk(KERN_WARNING "3w-xxxx: tw_findcards(): kmalloc() failed for card %d.\n", numcards);
-			continue;
-		}
-		memset(tw_dev, 0, sizeof(TW_Device_Extension));
-
-		error = tw_initialize_device_extension(tw_dev);
-		if (error) {
-			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize device extension for card %d.\n", numcards);
-			tw_free_device_extension(tw_dev);
-			kfree(tw_dev);
-			continue;
-		}
 
-		/* Calculate the cards register addresses */
-		tw_dev->registers.base_addr = pci_resource_start(tw_pci_dev, 0);
-		tw_dev->registers.control_reg_addr = pci_resource_start(tw_pci_dev, 0);
-		tw_dev->registers.status_reg_addr = pci_resource_start(tw_pci_dev, 0) + 0x4;
-		tw_dev->registers.command_que_addr = pci_resource_start(tw_pci_dev, 0) + 0x8;
-		tw_dev->registers.response_que_addr = pci_resource_start(tw_pci_dev, 0) + 0xC;
-		/* Save pci_dev struct to device extension */
-		tw_dev->tw_pci_dev = tw_pci_dev;
-
-		/* Poll status register for 60 secs for 'Controller Ready' flag */
-		if (tw_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY, 60)) {
-			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Microcontroller not ready for card %d.\n", numcards);
-			tw_free_device_extension(tw_dev);
-			kfree(tw_dev);
-			continue;
-		}
-
-		/* Disable interrupts on the card */
-		tw_disable_interrupts(tw_dev);
-
-		while (tries < TW_MAX_RESET_TRIES) {
-			/* Do soft reset */
-			tw_soft_reset(tw_dev);
-
-			error = tw_aen_drain_queue(tw_dev);
-			if (error) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): No attention interrupt for card %d.\n", numcards);
-				tries++;
+	for (i=0;i<TW_NUMDEVICES;i++) {
+		while ((tw_pci_dev = pci_find_device(TW_VENDOR_ID, device[i], tw_pci_dev))) {
+			if (pci_enable_device(tw_pci_dev))
+				continue;
+			/* Prepare temporary device extension */
+			tw_dev=(TW_Device_Extension *)kmalloc(sizeof(TW_Device_Extension), GFP_ATOMIC);
+			if (tw_dev == NULL) {
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): kmalloc() failed for card %d.\n", numcards);
 				continue;
 			}
+			memset(tw_dev, 0, sizeof(TW_Device_Extension));
 
-			/* Check for controller errors */
-			if (tw_check_errors(tw_dev)) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller errors found, soft resetting card %d.\n", numcards);
-				tries++;
+			error = tw_initialize_device_extension(tw_dev);
+			if (error) {
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize device extension for card %d.\n", numcards);
+				tw_free_device_extension(tw_dev);
+				kfree(tw_dev);
 				continue;
 			}
 
-			/* Empty the response queue */
-			error = tw_empty_response_que(tw_dev);
-			if (error) {
-				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't empty response queue for card %d.\n", numcards);
-				tries++;
+			/* Calculate the cards register addresses */
+			tw_dev->registers.base_addr = pci_resource_start(tw_pci_dev, 0);
+			tw_dev->registers.control_reg_addr = pci_resource_start(tw_pci_dev, 0);
+			tw_dev->registers.status_reg_addr = pci_resource_start(tw_pci_dev, 0) + 0x4;
+			tw_dev->registers.command_que_addr = pci_resource_start(tw_pci_dev, 0) + 0x8;
+			tw_dev->registers.response_que_addr = pci_resource_start(tw_pci_dev, 0) + 0xC;
+			/* Save pci_dev struct to device extension */
+			tw_dev->tw_pci_dev = tw_pci_dev;
+
+			/* Poll status register for 60 secs for 'Controller Ready' flag */
+			if (tw_poll_status(tw_dev, TW_STATUS_MICROCONTROLLER_READY, 60)) {
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Microcontroller not ready for card %d.\n", numcards);
+				tw_free_device_extension(tw_dev);
+				kfree(tw_dev);
 				continue;
 			}
 
-			/* Now the controller is in a good state */
-			break;
-		}
+			/* Disable interrupts on the card */
+			tw_disable_interrupts(tw_dev);
+			
+			while (tries < TW_MAX_RESET_TRIES) {
+				/* Do soft reset */
+				tw_soft_reset(tw_dev);
+			  
+				error = tw_aen_drain_queue(tw_dev);
+				if (error) {
+					printk(KERN_WARNING "3w-xxxx: tw_findcards(): No attention interrupt for card %d.\n", numcards);
+					tries++;
+					continue;
+				}
 
-		if (tries >= TW_MAX_RESET_TRIES) {
-			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller error or no attention interrupt: giving up for card %d.\n", numcards);
-			tw_free_device_extension(tw_dev);
-			kfree(tw_dev);
-			continue;
-		}
+				/* Check for controller errors */
+				if (tw_check_errors(tw_dev)) {
+					printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller errors found, soft resetting card %d.\n", numcards);
+					tries++;
+					continue;
+				}
 
-		/* Make sure that io region isn't already taken */
-		if (check_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE)) {
-			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't get io range 0x%lx-0x%lx for card %d.\n", 
-				(tw_dev->tw_pci_dev->resource[0].start), 
-				(tw_dev->tw_pci_dev->resource[0].start) + 
-				TW_IO_ADDRESS_RANGE, numcards);
-			tw_free_device_extension(tw_dev);
-			kfree(tw_dev);
-			continue;
-		}
+				/* Empty the response queue */
+				error = tw_empty_response_que(tw_dev);
+				if (error) {
+					printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't empty response queue for card %d.\n", numcards);
+					tries++;
+					continue;
+				}
+
+				/* Now the controller is in a good state */
+				break;
+			}
+
+			if (tries >= TW_MAX_RESET_TRIES) {
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Controller error or no attention interrupt: giving up for card %d.\n", numcards);
+				tw_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				continue;
+			}
+
+			/* Make sure that io region isn't already taken */
+			if (check_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE)) {
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't get io range 0x%lx-0x%lx for card %d.\n", 
+				       (tw_dev->tw_pci_dev->resource[0].start), 
+				       (tw_dev->tw_pci_dev->resource[0].start) + 
+				       TW_IO_ADDRESS_RANGE, numcards);
+				tw_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				continue;
+			}
     
-		/* Reserve the io address space */
-		request_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE, TW_DEVICE_NAME);
-		error = tw_initialize_units(tw_dev);
-		if (error) {
-			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize units for card %d.\n", numcards);
-			release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
-			tw_free_device_extension(tw_dev);
-			kfree(tw_dev);
-			continue;
-		}
+			/* Reserve the io address space */
+			request_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE, TW_DEVICE_NAME);
+			error = tw_initialize_units(tw_dev);
+			if (error) {
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initialize units for card %d.\n", numcards);
+				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
+				tw_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				continue;
+			}
 
-		error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
-		if (error) {
-			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initconnection for card %d.\n", numcards);
-			release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
-			tw_free_device_extension(tw_dev);
-			kfree(tw_dev);
-			continue;
-		}
+			error = tw_initconnection(tw_dev, TW_INIT_MESSAGE_CREDITS);
+			if (error) {
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Couldn't initconnection for card %d.\n", numcards);
+				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
+				tw_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				continue;
+			}
 
-		/* Calculate max cmds per lun */
-		if (tw_dev->num_units > 0)
-			tw_host->cmd_per_lun = (TW_Q_LENGTH-2)/tw_dev->num_units;
+			/* Calculate max cmds per lun */
+			if (tw_dev->num_units > 0)
+				tw_host->cmd_per_lun = (TW_Q_LENGTH-2)/tw_dev->num_units;
 
 		/* Register the card with the kernel SCSI layer */
-		host = scsi_register(tw_host, sizeof(TW_Device_Extension));
-		if( host == NULL)
-		{
-			release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
-			tw_free_device_extension(tw_dev);
-			kfree(tw_dev);
-			continue;
-		}
+			host = scsi_register(tw_host, sizeof(TW_Device_Extension));
+			if( host == NULL)
+			{
+				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
+				tw_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				continue;
+			}
 
-		status_reg_value = inl(tw_dev->registers.status_reg_addr);
+			status_reg_value = inl(tw_dev->registers.status_reg_addr);
 
-		dprintk(KERN_NOTICE "scsi%d : Found a 3ware Storage Controller at 0x%x, IRQ: %d P-chip: %d.%d\n", host->host_no,
+			dprintk(KERN_NOTICE "scsi%d : Found a 3ware Storage Controller at 0x%x, IRQ: %d P-chip: %d.%d\n", host->host_no,
 				(u32)(tw_pci_dev->resource[0].start), tw_pci_dev->irq, 
 				(status_reg_value & TW_STATUS_MAJOR_VERSION_MASK) >> 28, 
 				(status_reg_value & TW_STATUS_MINOR_VERSION_MASK) >> 24);
 
-		if (host->hostdata) {
-			tw_dev2 = (TW_Device_Extension *)host->hostdata;
-			memcpy(tw_dev2, tw_dev, sizeof(TW_Device_Extension));
-			tw_device_extension_list[tw_device_extension_count] = tw_dev2;
-			numcards++;
-			tw_device_extension_count = numcards;
-			tw_dev2->host = host;
-		} else { 
-			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards-1);
-			scsi_unregister(host);
-			release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
-			tw_free_device_extension(tw_dev);
-			kfree(tw_dev);
-			continue;
-		}
-    
-		/* Re-enable interrupts on the card */
-		tw_enable_interrupts(tw_dev2);
+			if (host->hostdata) {
+				tw_dev2 = (TW_Device_Extension *)host->hostdata;
+				memcpy(tw_dev2, tw_dev, sizeof(TW_Device_Extension));
+				tw_device_extension_list[tw_device_extension_count] = tw_dev2;
+				numcards++;
+				tw_device_extension_count = numcards;
+				tw_dev2->host = host;
+			} else { 
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Bad scsi host data for card %d.\n", numcards-1);
+				scsi_unregister(host);
+				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
+				tw_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				continue;
+			}
 
-		/* Now setup the interrupt handler */
-		error = tw_setup_irq(tw_dev2);
-		if (error) {
-			printk(KERN_WARNING "3w-xxxx: tw_findcards(): Error requesting irq for card %d.\n", numcards-1);
-			scsi_unregister(host);
-			release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
-
-			tw_free_device_extension(tw_dev);
-			kfree(tw_dev);
-			numcards--;
-			continue;
-		}
+			/* Tell the firmware we support shutdown notification*/
+			tw_setfeature(tw_dev2, 2, 1, &c);
 
-		/* Free the temporary device extension */
-		if (tw_dev)
-			kfree(tw_dev);
+			/* Now setup the interrupt handler */
+			error = tw_setup_irq(tw_dev2);
+			if (error) {
+				printk(KERN_WARNING "3w-xxxx: tw_findcards(): Error requesting irq for card %d.\n", numcards-1);
+				scsi_unregister(host);
+				release_region((tw_dev->tw_pci_dev->resource[0].start), TW_IO_ADDRESS_RANGE);
+
+				tw_free_device_extension(tw_dev);
+				kfree(tw_dev);
+				numcards--;
+				continue;
+			}
 
-		/* Tell the firmware we support shutdown notification*/
-		tw_setfeature(tw_dev2, 2, 1, &c);
+			/* Re-enable interrupts on the card */
+			tw_enable_interrupts(tw_dev2);
+
+			/* Free the temporary device extension */
+			if (tw_dev)
+				kfree(tw_dev);
+		}
 	}
 
 	if (numcards == 0) 
@@ -1089,15 +1103,14 @@
 		/* Handle attention interrupt */
 		if (do_attention_interrupt) {
 			dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Received attention interrupt.\n");
+			dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Clearing attention interrupt.\n");
+			tw_clear_attention_interrupt(tw_dev);
 			tw_state_request_start(tw_dev, &request_id);
 			error = tw_aen_read_queue(tw_dev, request_id);
 			if (error) {
 				printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Error reading aen queue.\n");
 				tw_dev->state[request_id] = TW_S_COMPLETED;
 				tw_state_request_finish(tw_dev, request_id);
-			} else {
-				dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Clearing attention interrupt.\n");
-				tw_clear_attention_interrupt(tw_dev);
 			}
 		}
 
@@ -1133,13 +1146,15 @@
 				response_que.value = inl(response_que_addr);
 				request_id = response_que.u.response_id;
 				command_packet = (TW_Command *)tw_dev->command_packet_virtual_address[request_id];
+				error = 0;
 				if (command_packet->status != 0) {
-					printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, status = 0x%x, flags = 0x%x.\n", command_packet->status, command_packet->flags);
+					printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Bad response, status = 0x%x, flags = 0x%x, unit = 0x%x.\n", command_packet->status, command_packet->flags, command_packet->byte3.unit);
+					error = 1;
 				}
 				if (tw_dev->state[request_id] != TW_S_POSTED) {
 					printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Received a request id (%d) (opcode = 0x%x) that wasn't posted.\n", request_id, command_packet->byte0.opcode);
+					error = 1;
 				}
-				error = 0;
 				dprintk(KERN_NOTICE "3w-xxxx: tw_interrupt(): Response queue request id: %d.\n", request_id);
 				/* Check for internal command */
 				if (tw_dev->srb[request_id] == 0) {
@@ -1183,8 +1198,8 @@
 					}
 					if (error) {
 						/* Tell scsi layer there was an error */
-						printk(KERN_WARNING "3w-xxxx: tw_interrupt(): Scsi Error.\n");
-						tw_dev->srb[request_id]->result = (DID_ERROR << 16);
+						dprintk(KERN_WARNING "3w-xxxx: tw_interrupt(): Scsi Error.\n");
+						tw_dev->srb[request_id]->result = (DID_RESET << 16);
 					} else {
 						/* Tell scsi layer command was a success */
 						tw_dev->srb[request_id]->result = (DID_OK << 16);
@@ -1467,8 +1482,11 @@
 		    (tw_dev->state[i] != TW_S_INITIAL) &&
 		    (tw_dev->state[i] != TW_S_COMPLETED)) {
 			srb = tw_dev->srb[i];
-			srb->result = (DID_RESET << 16);
-			tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+			if (srb != NULL) {
+				srb = tw_dev->srb[i];
+				srb->result = (DID_RESET << 16);
+				tw_dev->srb[i]->scsi_done(tw_dev->srb[i]);
+			}
 		}
 	}
 
@@ -1818,6 +1836,10 @@
 			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught READ_CAPACITY.\n");
 			error = tw_scsiop_read_capacity(tw_dev, request_id);
 			break;
+	        case REQUEST_SENSE:
+		        dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught REQUEST_SENSE.\n");
+		        error = tw_scsiop_request_sense(tw_dev, request_id);
+		        break;
 		case TW_IOCTL:
 			dprintk(KERN_NOTICE "3w-xxxx: tw_scsi_queue(): caught TW_SCSI_IOCTL.\n");
 			error = tw_ioctl(tw_dev, request_id);
@@ -2172,6 +2194,23 @@
 
 	return 0;
 } /* End tw_scsiop_read_write() */
+
+/* This function will handle the request sense scsi command */
+int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id)
+{
+       dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n");
+  
+       /* For now we just zero the sense buffer */
+       memset(tw_dev->srb[request_id]->request_buffer, 0, tw_dev->srb[request_id]->request_bufflen);
+       tw_dev->state[request_id] = TW_S_COMPLETED;
+       tw_state_request_finish(tw_dev, request_id);
+  
+       /* If we got a request_sense, we probably want a reset, return error */
+       tw_dev->srb[request_id]->result = (DID_ERROR << 16);
+       tw_dev->srb[request_id]->scsi_done(tw_dev->srb[request_id]);
+  
+       return 0;
+} /* End tw_scsiop_request_sense() */
 
 /* This function will handle test unit ready scsi command */
 int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id)
diff -urN linux-2.4.3-pre1/drivers/scsi/3w-xxxx.h linux/drivers/scsi/3w-xxxx.h
--- linux-2.4.3-pre1/drivers/scsi/3w-xxxx.h	Mon Dec  3 22:29:33 2001
+++ linux/drivers/scsi/3w-xxxx.h	Mon Dec  3 22:29:50 2001
@@ -3,8 +3,9 @@
    
    Written By: Adam Radford <linux@3ware.com>
    Modifications By: Joel Jacobson <linux@3ware.com>
+   		     Arnaldo Carvalho de Melo <acme@conectiva.com.br>
 
-   Copyright (C) 1999 3ware Inc.
+   Copyright (C) 1999-2001 3ware Inc.
 
    Kernel compatablity By:	Andre Hedrick <andre@suse.com>
    Non-Copyright (C) 2000	Andre Hedrick <andre@suse.com>
@@ -97,6 +98,8 @@
 #define TW_DEVICE_NAME			       "3ware Storage Controller"
 #define TW_VENDOR_ID (0x13C1)	/* 3ware */
 #define TW_DEVICE_ID (0x1000)	/* Storage Controller */
+#define TW_DEVICE_ID2 (0x1001)  /* 7000 series controller */
+#define TW_NUMDEVICES 2
 
 /* Command packet opcodes */
 #define TW_OP_NOP	      0x0
@@ -328,6 +331,7 @@
 int tw_scsiop_read_capacity(TW_Device_Extension *tw_dev, int request_id);
 int tw_scsiop_read_capacity_complete(TW_Device_Extension *tw_dev, int request_id);
 int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id);
+int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id);
 int tw_scsiop_test_unit_ready(TW_Device_Extension *tw_dev, int request_id);
 int tw_setfeature(TW_Device_Extension *tw_dev, int parm, int param_size, 
 		  unsigned char *val);
diff -urN linux-2.4.3-pre1/drivers/scsi/AM53C974.c linux/drivers/scsi/AM53C974.c
--- linux-2.4.3-pre1/drivers/scsi/AM53C974.c	Sun Nov 12 20:40:42 2000
+++ linux/drivers/scsi/AM53C974.c	Mon Dec  3 22:29:50 2001
@@ -676,6 +676,10 @@
 #endif
 
 	instance = scsi_register(tpnt, sizeof(struct AM53C974_hostdata));
+	if (!instance) {
+		printk(KERN_WARNING "AM53C974: Unable to register host, aborting.\n");
+		return 0;
+	}
 	hostdata = (struct AM53C974_hostdata *) instance->hostdata;
 	instance->base = 0;
 	instance->io_port = pci_resource_start(pdev, 0);
diff -urN linux-2.4.3-pre1/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- linux-2.4.3-pre1/drivers/scsi/Config.in	Sat Dec 30 18:38:37 2000
+++ linux/drivers/scsi/Config.in	Mon Dec  3 22:29:50 2001
@@ -50,12 +50,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 AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
-if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
-   bool '  Enable Tagged Command Queueing (TCQ) by default' CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
-   int  '  Maximum number of TCQ commands per device' CONFIG_AIC7XXX_CMDS_PER_DEVICE 8
-   bool '  Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS
-   int  '  Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5
+source drivers/scsi/aic7xxx/Config.in
+if [ "$CONFIG_SCSI_AIC7XXX" != "y" ]; then
+   dep_tristate 'Old Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX_OLD $CONFIG_SCSI
+   if [ "$CONFIG_SCSI_AIC7XXX_OLD" != "n" ]; then
+      bool '  Enable Tagged Command Queueing (TCQ) by default' CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT
+      int  '  Maximum number of TCQ commands per device' CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE 8
+      bool '  Collect statistics to report in /proc' CONFIG_AIC7XXX_OLD_PROC_STATS
+   fi
 fi
 dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
 dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
diff -urN linux-2.4.3-pre1/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- linux-2.4.3-pre1/drivers/scsi/Makefile	Sat Dec 30 11:23:14 2000
+++ linux/drivers/scsi/Makefile	Mon Dec  3 22:29:50 2001
@@ -14,6 +14,8 @@
 MOD_IN_SUB_DIRS	:=
 ALL_SUB_DIRS	:= $(SUB_DIRS) pcmcia
 
+subdir-$(CONFIG_SCSI_AIC7XXX)	+= aic7xxx
+
 ifeq ($(CONFIG_PCMCIA),y)
   SUB_DIRS += pcmcia
   MOD_IN_SUB_DIRS += pcmcia
@@ -63,7 +65,7 @@
 obj-$(CONFIG_SCSI_AHA152X)	+= aha152x.o
 obj-$(CONFIG_SCSI_AHA1542)	+= aha1542.o
 obj-$(CONFIG_SCSI_AHA1740)	+= aha1740.o
-obj-$(CONFIG_SCSI_AIC7XXX)	+= aic7xxx.o
+obj-$(CONFIG_SCSI_AIC7XXX_OLD)	+= aic7xxx_old.o
 obj-$(CONFIG_SCSI_IPS)		+= ips.o
 obj-$(CONFIG_SCSI_FD_MCS)	+= fd_mcs.o
 obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
@@ -147,7 +149,6 @@
 
 a100u2w.o: $(a100u2w-objs)
 	$(LD) -r -o $@ $(a100u2w-objs)
-
 
 53c8xx_d.h: 53c7,8xx.scr script_asm.pl
 	ln -sf 53c7,8xx.scr fake8.c
diff -urN linux-2.4.3-pre1/drivers/scsi/README.aic7xxx linux/drivers/scsi/README.aic7xxx
--- linux-2.4.3-pre1/drivers/scsi/README.aic7xxx	Thu Feb 10 19:00:35 2000
+++ linux/drivers/scsi/README.aic7xxx	Wed Dec 31 16:00:00 1969
@@ -1,511 +0,0 @@
-			    AIC7xxx Driver for Linux
-
-Introduction
-----------------------------
-The AIC7xxx SCSI driver adds support for Adaptec (http://www.adaptec.com)
-SCSI controllers and chipsets. Major portions of the driver and driver
-development are shared between both Linux and FreeBSD. Support for the
-AIC-7xxx chipsets have been in the default Linux kernel since approximately
-linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
-2.1.0 or later.
-
-  Supported cards/chipsets
-  ----------------------------
-    Adaptec Cards
-    ----------------------------
-    AHA-274x
-    AHA-274xT               
-    AHA-2842
-    AHA-2910B               
-    AHA-2920C
-    AHA-2930
-    AHA-2930U
-    AHA-2930CU
-    AHA-2930U2
-    AHA-2940               
-    AHA-2940W              
-    AHA-2940U              
-    AHA-2940UW
-    AHA-2940UW-PRO
-    AHA-2940AU 
-    AHA-2940U2W
-    AHA-2940U2
-    AHA-2940U2B
-    AHA-2940U2BOEM
-    AHA-2944D              
-    AHA-2944WD
-    AHA-2944UD
-    AHA-2944UWD
-    AHA-2950U2
-    AHA-2950U2W
-    AHA-2950U2B
-    AHA-29160M
-    AHA-3940
-    AHA-3940U
-    AHA-3940W
-    AHA-3940UW
-    AHA-3940AUW
-    AHA-3940U2W
-    AHA-3950U2B
-    AHA-3950U2D
-    AHA-3960D
-    AHA-39160M
-    AHA-3985
-    AHA-3985U
-    AHA-3985W
-    AHA-3985UW
-
-    Motherboard Chipsets
-    ----------------------------
-    AIC-777x   
-    AIC-785x
-    AIC-786x
-    AIC-787x
-    AIC-788x
-    AIC-789x
-    AIC-3860
-
-    Bus Types
-    ----------------------------
-    W - Wide SCSI, SCSI-3, 16bit bus, 68pin connector, will also support
-        SCSI-1/SCSI-2 50pin devices, transfer rates up to 20MB/s.
-    U - Ultra SCSI, transfer rates up to 40MB/s.
-    U2- Ultra 2 SCSI, transfer rates up to 80MB/s.
-    D - Differential SCSI.
-    T - Twin Channel SCSI. Up to 14 SCSI devices.
-
-    AHA-274x - EISA SCSI controller
-    AHA-284x - VLB SCSI controller
-    AHA-29xx - PCI SCSI controller
-    AHA-394x - PCI controllers with two separate SCSI controllers on-board.
-    AHA-398x - PCI RAID controllers with three separate SCSI controllers
-               on-board.
-
-  Not Supported Devices
-  ------------------------------
-    Adaptec Cards
-    ----------------------------
-    AHA-2920 (Only the cards that use the Future Domain chipset are not
-              supported, any 2920 cards based on Adaptec AIC chipsets,
-	      such as the 2920C, are supported)
-    AAA-13x Raid Adapters
-    AAA-113x Raid Port Card
-
-    Motherboard Chipsets
-    ----------------------------
-    AIC-7810
-
-    Bus Types
-    ----------------------------
-    R - Raid Port busses are not supported.
-
-    The hardware RAID devices sold by Adaptec are *NOT* supported by this
-    driver (and will people please stop emailing me about them, they are
-    a totally separate beast from the bare SCSI controllers and this driver
-    can not be retrofitted in any sane manner to support the hardware RAID
-    features on those cards - Doug Ledford).
-    
-
-  People
-  ------------------------------
-    Justin T Gibbs  gibbs@plutotech.com
-      (BSD Driver Author)
-    Dan Eischen     deischen@iworks.InterWorks.org
-      (Original Linux Driver Co-maintainer)
-    Dean Gehnert    deang@teleport.com
-      (Original Linux FTP/patch maintainer)
-    Jess Johnson    jester@frenzy.com
-      (AIC7xxx FAQ author)
-    Doug Ledford    dledford@redhat.com
-      (Current Linux aic7xxx-5.x.x Driver/Patch/FTP maintainer)
-    
-    Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original
-    author of the driver. John has since retired from the project. Thanks
-    again for all his work!
-    
-  Mailing list
-  ------------------------------
-    There is a mailing list available for users who want to track development
-    and converse with other users and developers. This list is for both
-    FreeBSD and Linux support of the AIC7xxx chipsets.
-
-    To subscribe to the AIC7xxx mailing list send mail to the list server,
-    with "subscribe AIC7xxx" in the body (no Subject: required):
-        To: majordomo@FreeBSD.ORG
-        ---
-        subscribe AIC7xxx
-
-    To unsubscribe from the list, send mail to the list server with:
-        To: majordomo@FreeBSD.ORG
-        ---
-        unsubscribe AIC7xxx
-
-    Send regular messages and replies to: AIC7xxx@FreeBSD.ORG
-    
-  Boot Command line options
-  ------------------------------
-    "aic7xxx=no_reset" -  Eliminate the SCSI bus reset during startup.
-        Some SCSI devices need the initial reset that this option disables
-	in order to work.  If you have problems at bootup, please make sure
-	you aren't using this option.
-	
-    "aic7xxx=reverse_scan" - Certain PCI motherboards scan for devices at
-        bootup by scanning from the highest numbered PCI device to the
-	lowest numbered PCI device, others do just the opposite and scan
-	from lowest to highest numbered PCI device.  There is no reliable
-	way to autodetect this ordering.  So, we default to the most common
-	order, which is lowest to highest.  Then, in case your motherboard
-	scans from highest to lowest, we have this option.  If your BIOS
-	finds the drives on controller A before controller B but the linux
-	kernel finds your drives on controller B before A, then you should
-	use this option.
-	
-    "aic7xxx=extended" - Force the driver to detect extended drive translation
-        on your controller.  This helps those people who have cards without
-        a SEEPROM make sure that linux and all other operating systems think
-        the same way about your hard drives.
-
-    "aic7xxx=scbram" - Some cards have external SCB RAM that can be used to
-        give the card more hardware SCB slots.  This allows the driver to use
-	that SCB RAM.  Without this option, the driver won't touch the SCB
-	RAM because it is known to cause problems on a few cards out there
-	(such as 3985 class cards).
-	
-    "aic7xxx=irq_trigger:x" - Replace x with either 0 or 1 to force the kernel
-        to use the correct IRQ type for your card.  This only applies to EISA
-        based controllers.  On these controllers, 0 is for Edge triggered
-        interrupts, and 1 is for Level triggered interrupts.  If you aren't
-        sure or don't know which IRQ trigger type your EISA card uses, then
-        let the kernel autodetect the trigger type.
-	
-    "aic7xxx=verbose" - This option can be used in one of two ways.  If you
-        simply specify aic7xxx=verbose, then the kernel will automatically
-	pick the default set of verbose messages for you to see.
-	Alternatively, you can specify the command as 
-	"aic7xxx=verbose:0xXXXX" where the X entries are replaced with
-	hexadecimal digits.  This option is a bit field type option.  For
-	a full listing of the available options, search for the 
-	#define VERBOSE_xxxxxx lines in the aic7xxx.c file.  If you want
-	verbose messages, then it is recommended that you simply use the
-	aic7xxx=verbose variant of this command.
-	
-    "aic7xxx=pci_parity:x" - This option controls whether or not the driver
-        enables PCI parity error checking on the PCI bus.  By default, this
-        checking is disabled.  To enable the checks, simply specify pci_parity
-        with no value afterwords.  To reverse the parity from even to odd,
-        supply any number other than 0 or 255.  In short:
-          pci_parity     - Even parity checking (even is the normal PCI parity)
-          pci_parity:x   - Where x > 0, Odd parity checking
-          pci_parity:0   - No check (default)
-        NOTE: In order to get Even PCI parity checking, you must use the
-        version of the option that does not include the : and a number at
-        the end (unless you want to enter exactly 2^32 - 1 as the number).
-	
-    "aic7xxx=no_probe" - This option will disable the probing for any VLB
-        based 2842 controllers and any EISA based controllers.  This is
-	needed on certain newer motherboards where the normal EISA I/O ranges
-	have been claimed by other PCI devices.  Probing on those machines
-	will often result in the machine crashing or spontaneously rebooting
-	during startup.  Examples of machines that need this are the
-	Dell PowerEdge 6300 machines.
-
-    "aic7xxx=seltime:2" - This option controls how long the card waits
-        during a device selection sequence for the device to respond.
-	The original SCSI spec says that this "should be" 256ms.  This
-	is generally not required with modern devices.  However, some
-	very old SCSI I devices need the full 256ms.  Most modern devices
-	can run fine with only 64ms.  The default for this option is
-	64ms.  If you need to change this option, then use the following
-	table to set the proper value in the example above:
-	  0  -  256ms
-	  1  -  128ms
-	  2  -   64ms
-	  3  -   32ms
-	
-    "aic7xxx=panic_on_abort" - This option is for debugging and will cause
-        the driver to panic the linux kernel and freeze the system the first
-	time the drivers abort or reset routines are called.  This is most
-	helpful when some problem causes infinite reset loops that scroll too
-	fast to see.  By using this option, you can write down what the errors
-	actually are and send that information to me so it can be fixed.
-	
-    "aic7xxx=dump_card" - This option will print out the *entire* set of
-        configuration registers on the card during the init sequence.  This
-	is a debugging aid used to see exactly what state the card is in
-	when we finally finish our initialization routines.  If you don't
-	have documentation on the chipsets, this will do you absolutely
-	no good unless you are simply trying to write all the information
-	down in order to send it to me.
-	
-    "aic7xxx=dump_sequencer" - This is the same as the above options except
-        that instead of dumping the register contents on the card, this
-	option dumps the contents of the sequencer program RAM.  This gives
-	the ability to verify that the instructions downloaded to the
-	card's sequencer are indeed what they are suppossed to be.  Again,
-	unless you have documentation to tell you how to interpret these
-	numbers, then it is totally useless.
-	
-    "aic7xxx=override_term:0xffffffff" - This option is used to force the
-    	termination on your SCSI controllers to a particular setting.  This
-	is a bit mask variable that applies for up to 8 aic7xxx SCSI channels.
-	Each channel gets 4 bits, divided as follows:
-	bit   3   2   1   0
-	      |   |   |   Enable/Disable Single Ended Low Byte Termination
-	      |   |   En/Disable Single Ended High Byte Termination
-	      |   En/Disable Low Byte LVD Termination
-	      En/Disable High Byte LVD Termination
-
-	The upper 2 bits that deal with LVD termination only apply to Ultra2
-	controllers.  Futhermore, due to the current Ultra2 controller
-	designs, these bits are tied together such that setting either bit
-	enables both low and high byte LVD termination.  It is not possible
-	to only set high or low byte LVD termination in this manner.  This is
-	an artifact of the BIOS definition on Ultra2 controllers.  For other
-	controllers, the only important bits are the two lowest bits.  Setting
-	the higher bits on non-Ultra2 controllers has no effect.  A few
-	examples of how to use this option:
-
-	Enable low and high byte termination on a non-ultra2 controller that
-	is the first aic7xxx controller (the correct bits are 0011), 
-	aic7xxx=override_term:0x3
-
-	Enable all termination on the third aic7xxx controller, high byte
-	termination on the second aic7xxx controller, and low and high byte
-	SE termination on the first aic7xxx controller 
-	(bits are 1111 0010 0011), 
-	aic7xxx=override_term:0xf23
-	
-	No attempt has been made to make this option non-cryptic.  It really
-	shouldn't be used except in dire circumstances, and if that happens,
-	I'm probably going to be telling you what to set this to anyway :)
-
-    "aic7xxx=stpwlev:0xffffffff" - This option is used to control the STPWLEV
-        bit in the DEVCONFIG PCI register.  Currently, this is one of the
-	very few registers that we have absolutely *no* way of detecting
-	what the variable should be.  It depends entirely on how the chipset
-	and external terminators were coupled by the card/motherboard maker.
-	Further, a chip reset (at power up) always sets this bit to 0.  If
-	there is no BIOS to run on the chipset/card (such as with a 2910C
-	or a motherboard controller with the BIOS totally disabled) then
-	the variable may not get set properly.  Of course, if the proper
-	setting was 0, then that's what it would be after the reset, but if
-	the proper setting is actually 1.....you get the picture.  Now, since
-	we can't detect this at all, I've added this option to force the
-	setting.  If you have a BIOS on your controller then you should never
-	need to use this option.  However, if you are having lots of SCSI
-	reset problems and can't seem to get them knocked out, this may help.
-
-	Here's a test to know for certain if you need this option.  Make
-	a boot floppy that you can use to boot your computer up and that
-	will detect the aic7xxx controller.  Next, power down your computer.
-	While it's down, unplug all SCSI cables from your Adaptec SCSI
-	controller.  Boot the system back up to the Adaptec EZ-SCSI BIOS
-	and then make sure that termination is enabled on your adapter (if
-	you have an Adaptec BIOS of course).  Next, boot up the floppy you
-	made and wait for it to detect the aic7xxx controller.  If the kernel
-	finds the controller fine, says scsi : x hosts and then tries to
-	detect your devices like normal, up to the point where it fails to
-	mount your root file system and panics, then you're fine.  If, on
-	the other hand, the system goes into an infinite reset loop, then
-	you need to use this option and/or the previous option to force the
-	proper termination settings on your controller.   If this happens,
-	then you next need to figure out what your settings should be.
-
-	To find the correct settings, power your machine back down, connect
-	back up the SCSI cables, and boot back into your machine like normal.
-	However, boot with the aic7xxx=verbose:0x39 option.  Record the
-	initial DEVCONFIG values for each of your aic7xxx controllers as
-	they are listed, and also record what the machine is detecting as
-	the proper termination on your controllers.  NOTE: the order in
-	which the initial DEVCONFIG values are printed out is not gauranteed
-	to be the same order as the SCSI controllers are registered.  The
-	above option and this option both work on the order of the SCSI
-	controllers as they are registered, so make sure you match the right
-	DEVCONFIG values with the right controllers if you have more than
-	one aic7xxx controller.
-
-	Once you have the detected termination settings and the initial
-	DEVCONFIG values for each controller, then figure out what the
-	termination on each of the controllers *should* be.  Hopefully, that
-	part is correct, but it could possibly be wrong if there is
-	bogus cable detection logic on your controller or something similar.
-	If all the controllers have the correct termination settings, then
-	don't set the aic7xxx=override_term variable at all, leave it alone.
-	Next, on any controllers that go into an infinite reset loop when
-	you unplug all the SCSI cables, get the starting DEVCONFIG value.
-	If the initial DEVCONFIG value is divisible by 2, then the correct
-	setting for that controller is 0.  If it's an odd number, then
-	the correct setting for that controller is 1.  For any other
-	controllers that didn't have an infinite reset problem, then reverse
-	the above options.  If DEVCONFIG was even, then the correct setting
-	is 1, if not then the correct setting is 0.
-
-	Now that you know what the correct setting was for each controller,
-	we need to encode that into the aic7xxx=stpwlev:0x... variable.
-	This variable is a bit field encoded variable.  Bit 0 is for the first
-	aic7xxx controller, bit 1 for the next, etc.  Put all these bits
-	together and you get a number.  For example, if the third aic7xxx
-	needed a 1, but the second and first both needed a 0, then the bits
-	would be 100 in binary.  This then translates to 0x04.  You would
-	therefore set aic7xxx=stpwlev:0x04.  This is fairly standard binary
-	to hexadecimal conversions here.  If you aren't up to speed on the
-	binary->hex conversion then send an email to the aic7xxx mailing
-	list and someone can help you out.
-
-    "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to disable
-        or enable Tagged Command Queueing (TCQ) on specific devices.  As of
-	driver version 5.1.11, TCQ is now either on or off by default
-	according to the setting you choose during the make config process.
-	In order to en/disable TCQ for certian devices at boot time, a user
-	may use this boot param.  The driver will then parse this message out
-        and en/disable the specific device entries that are present based upon
-        the value given.  The param line is parsed in the following manner:
-
-          { - first instance indicates the start of this parameter values
-              second instance is the start of entries for a particular
-              device entry
-          } - end the entries for a particular host adapter, or end the entire
-              set of parameter entries
-          , - move to next entry.  Inside of a set of device entries, this
-              moves us to the next device on the list.  Outside of device
-              entries, this moves us to the next host adapter
-          . - Same effect as , but is safe to use with insmod.
-          x - the number to enter into the array at this position.  
-              0 = Enable tagged queueing on this device and use the default
-                  queue depth
-              1-254 = Enable tagged queueing on this device and use this
-                      number as the queue depth
-              255 = Disable tagged queueing on this device.
-              Note: anything above 32 for an actual queue depth is wasteful
-                    and not recommended.
-
-        A few examples of how this can be used:
-
-        tag_info:{{8,12,,0,,255,4}}
-          This line will only effect the first aic7xxx card registered.  It
-          will set scsi id 0 to a queue depth of 8, id 1 to 12, leave id 2
-          at the default, set id 3 to tagged queueing enabled and use the
-          default queue depth, id 4 default, id 5 disabled, and id 6 to 4.
-          Any not specified entries stay at the default value, repeated
-          commas with no value specified will simply increment to the next id
-          without changing anything for the missing values.
-
-        tag_info:{,,,{,,,255}}
-          First, second, and third adapters at default values.  Fourth
-          adapter, id 3 is disabled.  Notice that leading commas simply
-	  increment what the first number effects, and there are no need
-	  for trailing commas.  When you close out an adapter, or the
-	  entire entry, anything not explicitly set stays at the default
-	  value.
-
-        A final note on this option.  The scanner I used for this isn't
-        perfect or highly robust.  If you mess the line up, the worst that
-        should happen is that the line will get ignored.  If you don't
-        close out the entire entry with the final bracket, then any other
-        aic7xxx options after this will get ignored.  So, in general, be
-        sure of what you are entering, and after you have it right, just
-        add it to the lilo.conf file so there won't be any mistakes.  As
-        a means of checking this parser, the entire tag_info array for
-        each card is now printed out in the /proc/scsi/aic7xxx/x file.  You
-        can use that to verify that your options were parsed correctly. 
-        
-    Boot command line options may be combined to form the proper set of options
-    a user might need.  For example, the following is valid:
-    
-    aic7xxx=verbose,extended,irq_trigger:1
-    
-    The only requirement is that individual options be separated by a comma or
-    a period on the command line.
-        
-  Module Loading command options
-  ------------------------------
-    When loading the aic7xxx driver as a module, the exact same options are
-    available to the user.  However, the syntax to specify the options changes
-    slightly.  For insmod, you need to wrap the aic7xxx= argument in quotes
-    and replace all ',' with '.'.  So, for example, a valid insmod line
-    would be:
-
-    insmod aic7xxx aic7xxx='verbose.irq_trigger:1.extended'
-
-    This line should result in the *exact* same behaviour as if you typed
-    it in at the lilo prompt and the driver was compiled into the kernel
-    instead of being a module.  The reason for the single quote is so that
-    the shell won't try to interpret anything in the line, such as {. 
-    Insmod assumes any options starting with a letter instead of a number
-    is a character string (which is what we want) and by switching all of
-    the commas to periods, insmod won't interpret this as more than one
-    string and write junk into our binary image.  I consider it a bug in
-    the insmod program that even if you wrap your string in quotes (quotes
-    that pass the shell mind you and that insmod sees) it still treates
-    a comma inside of those quotes as starting a new variable, resulting
-    in memory scribbles if you don't switch the commas to periods.
-
-
-  Kernel Compile options
-  ------------------------------
-    The various kernel compile time options for this driver are now fairly
-    well documented in the file Documentation/Configure.help.  In order to
-    see this documentation, you need to use one of the advanced configuration
-    programs (menuconfig and xconfig).  If you are using the "make menuconfig"
-    method of configuring your kernel, then you would simply highlight the
-    option in question and hit the ? key.  If you are using the "make xconfig"
-    method of configuring your kernel, then simply click on the help button
-    next to the option you have questions about.  The help information from
-    the Configure.help file will then get automatically displayed.
-
-  /proc support
-  ------------------------------
-    The /proc support for the AIC7xxx can be found in the /proc/scsi/aic7xxx/
-    directory. That directory contains a file for each SCSI controller in
-    the system. Each file presents the current configuration and transfer
-    statistics (enabled with #define in aic7xxx.c) for each controller.
-
-    Thanks to Michael Neuffer for his upper-level SCSI help, and
-    Matthew Jacob for statistics support.
-
-  Debugging the driver
-  ------------------------------
-    Should you have problems with this driver, and would like some help in
-    getting them solved, there are a couple debugging items built into
-    the driver to facilitate getting the needed information from the system.
-    In general, I need a complete description of the problem, with as many
-    logs as possible concerning what happens.  To help with this, there is
-    a command option aic7xxx=panic_on_abort.  This option, when set, forces
-    the driver to panic the kernel on the first SCSI abort issued by the
-    mid level SCSI code.  If your system is going to reset loops and you
-    can't read the screen, then this is what you need.  Not only will it
-    stop the system, but it also prints out a large amount of state
-    information in the process.  Second, if you specify the option
-    "aic7xxx=verbose:0x1ffff", the system will print out *SOOOO* much
-    information as it runs that you won't be able to see anything.
-    However, this can actually be very usefull if your machine simply
-    locks up when trying to boot, since it will pin-point what was last
-    happening (in regards to the aic7xxx driver) immediately prior to
-    the lockup.  This is really only usefull if your machine simply can
-    not boot up successfully.  If you can get your machine to run, then
-    this will produce far too much information.
-
-  FTP sites
-  ------------------------------
-    ftp://ftp.redhat.com/pub/aic/
-      - Out of date.  I used to keep stuff here, but too many people
-        complained about having a hard time getting into Red Hat's ftp
-	server.  So use the web site below instead.
-    ftp://ftp.pcnet.com/users/eischen/Linux/
-      - Dan Eischen's driver distribution area
-    ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/
-      - European Linux mirror of Teleport site
-
-  Web sites
-  ------------------------------
-    http://people.redhat.com/dledford/
-      - My web site, also the primary aic7xxx site with several related
-        pages.
-
-Dean W. Gehnert
-deang@teleport.com
-
-$Revision: 3.0 $
-
-Modified by Doug Ledford 1998-2000
-
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/Config.in linux/drivers/scsi/aic7xxx/Config.in
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/Config.in	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/Config.in	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,7 @@
+if [ "$CONFIG_SCSI_AIC7XXX_OLD" != "y" ]; then
+   dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI
+   if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then
+      int  '  Maximum number of TCQ commands per device' CONFIG_AIC7XXX_CMDS_PER_DEVICE 253
+      int  '  Initial bus reset delay in milli-seconds' CONFIG_AIC7XXX_RESET_DELAY 5000
+   fi
+fi
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/Makefile linux/drivers/scsi/aic7xxx/Makefile
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/Makefile	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,32 @@
+#
+# drivers/scsi/aic7xxx/Makefile
+#
+# Makefile for the Linux aic7xxx SCSI driver.
+#
+
+O_TARGET = aic7xxx_drv.o
+
+list-multi	:= aic7xxx_mod.o
+
+obj-$(CONFIG_SCSI_AIC7XXX)	+= aic7xxx_mod.o
+
+# Platform Specific Files
+AIC7XXX_OBJS = aic7xxx_linux.o aic7xxx_linux_pci.o
+AIC7XXX_OBJS += aic7xxx_proc.o aic7770_linux.o
+# Core Files
+AIC7XXX_OBJS += aic7xxx.o aic7xxx_pci.o aic7xxx_93cx6.o aic7770.o
+
+# Override our module desitnation
+MOD_DESTDIR = $(shell cd .. && $(CONFIG_SHELL) $(TOPDIR)/scripts/pathdown.sh)
+MOD_TARGET = aic7xxx.o
+
+include $(TOPDIR)/Rules.make
+
+aic7xxx_mod.o: aic7xxx_seq.h aic7xxx_reg.h $(AIC7XXX_OBJS)
+	$(LD) $(LD_RFLAG) -r -o $@ $(AIC7XXX_OBJS)
+
+aic7xxx_seq.h aic7xxx_reg.h: aic7xxx.seq aic7xxx.reg aicasm/aicasm
+	aicasm/aicasm -I. -r aic7xxx_reg.h -o aic7xxx_seq.h aic7xxx.seq
+
+aicasm/aicasm: aicasm/*.[chyl]
+	$(MAKE) -C aicasm
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7770.c linux/drivers/scsi/aic7xxx/aic7770.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7770.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7770.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,346 @@
+/*
+ * Product specific probe and attach routines for:
+ * 	27/284X and aic7770 motherboard SCSI controllers
+ *
+ * Copyright (c) 1994-1998, 2000, 2001 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 immediately at the beginning of the file, without modification,
+ *    this list of conditions, and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aic7770.c#6 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7770.c,v 1.1 2000/09/16 20:02:27 gibbs Exp $
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+
+#define ID_AIC7770	0x04907770
+#define ID_AHA_274x	0x04907771
+#define ID_AHA_284xB	0x04907756 /* BIOS enabled */
+#define ID_AHA_284x	0x04907757 /* BIOS disabled*/
+
+static void aha2840_load_seeprom(struct ahc_softc *ahc);
+static ahc_device_setup_t ahc_aic7770_VL_setup;
+static ahc_device_setup_t ahc_aic7770_EISA_setup;;
+static ahc_device_setup_t ahc_aic7770_setup;
+
+
+struct aic7770_identity aic7770_ident_table [] =
+{
+	{
+		ID_AHA_274x,
+		0xFFFFFFFF,
+		"Adaptec 274X SCSI adapter",
+		ahc_aic7770_EISA_setup
+	},
+	{
+		ID_AHA_284xB,
+		0xFFFFFFFE,
+		"Adaptec 284X SCSI adapter",
+		ahc_aic7770_VL_setup
+	},
+	/* Generic chip probes for devices we don't know 'exactly' */
+	{
+		ID_AIC7770,
+		0xFFFFFFFF,
+		"Adaptec aic7770 SCSI adapter",
+		ahc_aic7770_EISA_setup
+	}
+};
+const int ahc_num_aic7770_devs = NUM_ELEMENTS(aic7770_ident_table);
+
+struct aic7770_identity *
+aic7770_find_device(uint32_t id)
+{
+	struct	aic7770_identity *entry;
+	int	i;
+
+	for (i = 0; i < ahc_num_aic7770_devs; i++) {
+		entry = &aic7770_ident_table[i];
+		if (entry->full_id == (id & entry->id_mask))
+			return (entry);
+	}
+	return (NULL);
+}
+
+int
+aic7770_config(struct ahc_softc *ahc, struct aic7770_identity *entry)
+{
+	struct	ahc_probe_config probe_config;
+	int	error;
+	u_int	hostconf;
+	u_int   irq;
+	u_int	intdef;
+	u_int	hcntrl;
+	int	shared;
+
+	ahc_init_probe_config(&probe_config);
+	error = entry->setup(ahc->dev_softc, &probe_config);
+	if (error != 0)
+		return (error);
+
+	error = aic7770_map_registers(ahc);
+	if (error != 0)
+		return (error);
+
+	/* Pause the card preseving the IRQ type */
+	hcntrl = ahc_inb(ahc, HCNTRL) & IRQMS;
+	ahc_outb(ahc, HCNTRL, hcntrl | PAUSE);
+	while ((ahc_inb(ahc, HCNTRL) & PAUSE) == 0)
+		;
+
+	/* Make sure we have a valid interrupt vector */
+	intdef = ahc_inb(ahc, INTDEF);
+	shared = (intdef & EDGE_TRIG) ? 0 : 1;
+	irq = intdef & VECTOR;
+	switch (irq) {
+	case 9:
+	case 10:
+	case 11:
+	case 12:
+	case 14:
+	case 15:
+		break;
+	default:
+		printf("aic7770_config: illegal irq setting %d\n", intdef);
+		return (ENXIO);
+	}
+
+	probe_config.description = entry->name;
+	error = ahc_softc_init(ahc, &probe_config);
+
+	error = aic7770_map_int(ahc, irq, shared);
+	if (error != 0)
+		return (error);
+
+	error = ahc_reset(ahc);
+	if (error != 0)
+		return (error);
+
+	switch (probe_config.chip & (AHC_EISA|AHC_VL)) {
+	case AHC_EISA:
+	{
+		u_int biosctrl;
+		u_int scsiconf;
+		u_int scsiconf1;
+
+		biosctrl = ahc_inb(ahc, HA_274_BIOSCTRL);
+		scsiconf = ahc_inb(ahc, SCSICONF);
+		scsiconf1 = ahc_inb(ahc, SCSICONF + 1);
+
+		/* Get the primary channel information */
+		if ((biosctrl & CHANNEL_B_PRIMARY) != 0)
+			ahc->flags |= AHC_CHANNEL_B_PRIMARY;
+
+		if ((biosctrl & BIOSMODE) == BIOSDISABLED) {
+			ahc->flags |= AHC_USEDEFAULTS;
+		} else {
+			if ((ahc->features & AHC_WIDE) != 0) {
+				ahc->our_id = scsiconf1 & HWSCSIID;
+				if (scsiconf & TERM_ENB)
+					ahc->flags |= AHC_TERM_ENB_A;
+			} else {
+				ahc->our_id = scsiconf & HSCSIID;
+				ahc->our_id_b = scsiconf1 & HSCSIID;
+				if (scsiconf & TERM_ENB)
+					ahc->flags |= AHC_TERM_ENB_A;
+				if (scsiconf1 & TERM_ENB)
+					ahc->flags |= AHC_TERM_ENB_B;
+			}
+		}
+		/*
+		 * We have no way to tell, so assume extended
+		 * translation is enabled.
+		 */
+		ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B;
+		break;
+	}
+	case AHC_VL:
+	{
+		aha2840_load_seeprom(ahc);
+		break;
+	}
+	default:
+		break;
+	}
+
+	/*
+	 * Ensure autoflush is enabled
+	 */
+	ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~AUTOFLUSHDIS);
+
+	/* Setup the FIFO threshold and the bus off time */
+	hostconf = ahc_inb(ahc, HOSTCONF);
+	ahc_outb(ahc, BUSSPD, hostconf & DFTHRSH);
+	ahc_outb(ahc, BUSTIME, (hostconf << 2) & BOFF);
+
+	/*
+	 * Generic aic7xxx initialization.
+	 */
+	error = ahc_init(ahc);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * Link this softc in with all other ahc instances.
+	 */
+	ahc_softc_insert(ahc);
+
+	/*
+	 * Enable the board's BUS drivers
+	 */
+	ahc_outb(ahc, BCTL, ENABLE);
+
+	return (0);
+}
+
+/*
+ * Read the 284x SEEPROM.
+ */
+static void
+aha2840_load_seeprom(struct ahc_softc *ahc)
+{
+	struct	  seeprom_descriptor sd;
+	struct	  seeprom_config sc;
+	uint16_t  checksum = 0;
+	uint8_t   scsi_conf;
+	int	  have_seeprom;
+
+	sd.sd_ahc = ahc;
+	sd.sd_control_offset = SEECTL_2840;
+	sd.sd_status_offset = STATUS_2840;
+	sd.sd_dataout_offset = STATUS_2840;		
+	sd.sd_chip = C46;
+	sd.sd_MS = 0;
+	sd.sd_RDY = EEPROM_TF;
+	sd.sd_CS = CS_2840;
+	sd.sd_CK = CK_2840;
+	sd.sd_DO = DO_2840;
+	sd.sd_DI = DI_2840;
+
+	if (bootverbose)
+		printf("%s: Reading SEEPROM...", ahc_name(ahc));
+	have_seeprom = read_seeprom(&sd,
+				    (uint16_t *)&sc,
+				    /*start_addr*/0,
+				    sizeof(sc)/2);
+
+	if (have_seeprom) {
+		/* Check checksum */
+		int i;
+		int maxaddr = (sizeof(sc)/2) - 1;
+		uint16_t *scarray = (uint16_t *)&sc;
+
+		for (i = 0; i < maxaddr; i++)
+			checksum = checksum + scarray[i];
+		if (checksum != sc.checksum) {
+			if(bootverbose)
+				printf ("checksum error\n");
+			have_seeprom = 0;
+		} else if (bootverbose) {
+			printf("done.\n");
+		}
+	}
+
+	if (!have_seeprom) {
+		if (bootverbose)
+			printf("%s: No SEEPROM available\n", ahc_name(ahc));
+		ahc->flags |= AHC_USEDEFAULTS;
+	} else {
+		/*
+		 * Put the data we've collected down into SRAM
+		 * where ahc_init will find it.
+		 */
+		int i;
+		int max_targ = (ahc->features & AHC_WIDE) != 0 ? 16 : 8;
+		uint16_t discenable;
+
+		discenable = 0;
+		for (i = 0; i < max_targ; i++){
+	                uint8_t target_settings;
+			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)
+				discenable |= (0x01 << i);
+			ahc_outb(ahc, TARG_SCSIRATE + i, target_settings);
+		}
+		ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
+		ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
+
+		ahc->our_id = sc.brtime_id & CFSCSIID;
+
+		scsi_conf = (ahc->our_id & 0x7);
+		if (sc.adapter_control & CFSPARITY)
+			scsi_conf |= ENSPCHK;
+		if (sc.adapter_control & CFRESETB)
+			scsi_conf |= RESET_SCSI;
+
+		if (sc.bios_control & CF284XEXTEND)		
+			ahc->flags |= AHC_EXTENDED_TRANS_A;
+		/* Set SCSICONF info */
+		ahc_outb(ahc, SCSICONF, scsi_conf);
+
+		if (sc.adapter_control & CF284XSTERM)
+			ahc->flags |= AHC_TERM_ENB_A;
+	}
+}
+
+static int
+ahc_aic7770_VL_setup(ahc_dev_softc_t dev, struct ahc_probe_config *probe_config)
+{
+	int error;
+
+	error = ahc_aic7770_setup(dev, probe_config);
+	probe_config->chip |= AHC_VL;
+	return (error);
+}
+
+static int
+ahc_aic7770_EISA_setup(ahc_dev_softc_t dev,
+		       struct ahc_probe_config *probe_config)
+{
+	int error;
+
+	error = ahc_aic7770_setup(dev, probe_config);
+	probe_config->chip |= AHC_EISA;
+	return (error);
+}
+
+static int
+ahc_aic7770_setup(ahc_dev_softc_t dev, struct ahc_probe_config *probe_config)
+{
+	probe_config->channel = 'A';
+	probe_config->channel_b = 'B';
+	probe_config->chip = AHC_AIC7770;
+	probe_config->features = AHC_AIC7770_FE;
+	probe_config->bugs |= AHC_TMODE_WIDEODD_BUG;
+	probe_config->flags |= AHC_PAGESCBS;
+	return (0);
+}
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7770_linux.c linux/drivers/scsi/aic7xxx/aic7770_linux.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7770_linux.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7770_linux.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,146 @@
+/*
+ * Linux driver attachment glue for aic7770 based controllers.
+ *
+ * Copyright (c) 2000 Adaptec Inc.
+ * 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/linux/drivers/scsi/aic7xxx/aic7770_linux.c#5 $
+ */
+
+#include "aic7xxx_osm.h"
+
+#define MINSLOT			1
+#define NUMSLOTS		16
+#define IDOFFSET		0x80
+
+int
+aic7770_linux_probe(Scsi_Host_Template *template)
+{
+#if defined(__i386__) || defined(__alpha__)
+	struct aic7770_identity *entry;
+	struct ahc_softc *ahc;
+	int i, slot;
+	int eisaBase;
+	int found;
+
+	if (aic7xxx_no_probe)
+		return (0);
+
+	eisaBase = 0x1000 + AHC_EISA_SLOT_OFFSET;
+	found = 0;
+	for (slot = 1; slot < NUMSLOTS; eisaBase+=0x1000, slot++) {
+		uint32_t eisa_id;
+		size_t	 id_size;
+
+		if (check_region(eisaBase, AHC_EISA_IOSIZE) != 0)
+			continue;
+
+		eisa_id = 0;
+		id_size = sizeof(eisa_id);
+		for (i = 0; i < 4; i++) {
+			/* VLcards require priming*/
+			outb(0x80 + i, eisaBase + IDOFFSET);
+			eisa_id |= inb(eisaBase + IDOFFSET + i)
+				   << ((id_size-i-1) * 8);
+		}
+		if (eisa_id & 0x80000000)
+			continue;  /* no EISA card in slot */
+
+		entry = aic7770_find_device(eisa_id);
+		if (entry != NULL) {
+			char	 buf[80];
+			char	*name;
+			int	 error;
+
+			/*
+			 * Allocate a softc for this card and
+			 * set it up for attachment by our
+			 * common detect routine.
+			 */
+			sprintf(buf, "ahc_eisa:%d", slot);
+			name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+			if (name == NULL)
+				break;
+			strcpy(name, buf);
+			ahc = ahc_alloc(template, name);
+			if (ahc == NULL) {
+				/*
+				 * If we can't allocate this one,
+				 * chances are we won't be able to
+				 * allocate future card structures.
+				 */
+				break;
+			}
+			ahc->tag = BUS_SPACE_PIO;
+			ahc->bsh.ioport = eisaBase;
+			error = aic7770_config(ahc, entry);
+			if (error != 0) {
+				ahc_free(ahc);
+				continue;
+			}
+			found++;
+		}
+	}
+	return (found);
+#else
+	return (0);
+#endif
+}
+
+int
+aic7770_map_registers(struct ahc_softc *ahc)
+{
+	/*
+	 * Lock out other contenders for our i/o space.
+	 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+	request_region(ahc->bsh.ioport, AHC_EISA_IOSIZE, "aic7xxx");
+#else
+	if (request_region(ahc->bsh.ioport, AHC_EISA_IOSIZE, "aic7xxx") == 0)
+		return (ENOMEM);
+#endif
+
+	return (0);
+}
+
+int
+aic7770_map_int(struct ahc_softc *ahc, u_int irq, int shared)
+{
+	int error;
+
+	if (shared)
+		shared = SA_SHIRQ;
+
+	ahc->platform_data->irq = irq;
+	error = request_irq(ahc->platform_data->irq, aic7xxx_isr,
+			    SA_INTERRUPT|shared, "aic7xxx", ahc);
+	if (error < 0)
+		error = request_irq(ahc->platform_data->irq, aic7xxx_isr,
+				    shared, "aic7xxx", ahc);
+	
+	return (-error);
+}
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx.c linux/drivers/scsi/aic7xxx/aic7xxx.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,6673 @@
+/*
+ * Core routines and tables shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aic7xxx.c#32 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.c,v 1.61 2000/11/13 03:35:43 gibbs Exp $
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aicasm/aicasm_insformat.h"
+
+/****************************** Softc Data ************************************/
+struct ahc_softc_tailq ahc_tailq = TAILQ_HEAD_INITIALIZER(ahc_tailq);
+
+/***************************** Lookup Tables **********************************/
+char *ahc_chip_names[] =
+{
+	"NONE",
+	"aic7770",
+	"aic7850",
+	"aic7855",
+	"aic7859",
+	"aic7860",
+	"aic7870",
+	"aic7880",
+	"aic7895",
+	"aic7895C",
+	"aic7890/91",
+	"aic7896/97",
+	"aic7892",
+	"aic7899"
+};
+const u_int num_chip_names = NUM_ELEMENTS(ahc_chip_names);
+
+struct hard_error_entry hard_error[] = {
+	{ ILLHADDR,	"Illegal Host Access" },
+	{ ILLSADDR,	"Illegal Sequencer Address referrenced" },
+	{ ILLOPCODE,	"Illegal Opcode in sequencer program" },
+	{ SQPARERR,	"Sequencer Parity Error" },
+	{ DPARERR,	"Data-path Parity Error" },
+	{ MPARERR,	"Scratch or SCB Memory Parity Error" },
+	{ PCIERRSTAT,	"PCI Error detected" },
+	{ CIOPARERR,	"CIOBUS Parity Error" },
+};
+const u_int num_errors = NUM_ELEMENTS(hard_error);
+
+struct phase_table_entry phase_table[] =
+{
+	{ P_DATAOUT,	MSG_NOOP,		"in Data-out phase"	},
+	{ P_DATAIN,	MSG_INITIATOR_DET_ERR,	"in Data-in phase"	},
+	{ P_DATAOUT_DT,	MSG_NOOP,		"in DT Data-out phase"	},
+	{ P_DATAIN_DT,	MSG_INITIATOR_DET_ERR,	"in DT Data-in phase"	},
+	{ P_COMMAND,	MSG_NOOP,		"in Command phase"	},
+	{ P_MESGOUT,	MSG_NOOP,		"in Message-out phase"	},
+	{ P_STATUS,	MSG_INITIATOR_DET_ERR,	"in Status phase"	},
+	{ P_MESGIN,	MSG_PARITY_ERROR,	"in Message-in phase"	},
+	{ P_BUSFREE,	MSG_NOOP,		"while idle"		},
+	{ 0,		MSG_NOOP,		"in unknown phase"	}
+};
+
+/*
+ * In most cases we only wish to itterate over real phases, so
+ * exclude the last element from the count.
+ */
+const u_int num_phases = NUM_ELEMENTS(phase_table) - 1;
+
+/*
+ * Valid SCSIRATE values.  (p. 3-17)
+ * Provides a mapping of tranfer periods in ns to the proper value to
+ * stick in the scsixfer reg.
+ */
+struct ahc_syncrate ahc_syncrates[] =
+{
+      /* ultra2    fast/ultra  period     rate */
+	{ 0x42,      0x000,      9,      "80.0" },
+	{ 0x03,      0x000,     10,      "40.0" },
+	{ 0x04,      0x000,     11,      "33.0" },
+	{ 0x05,      0x100,     12,      "20.0" },
+	{ 0x06,      0x110,     15,      "16.0" },
+	{ 0x07,      0x120,     18,      "13.4" },
+	{ 0x08,      0x000,     25,      "10.0" },
+	{ 0x19,      0x010,     31,      "8.0"  },
+	{ 0x1a,      0x020,     37,      "6.67" },
+	{ 0x1b,      0x030,     43,      "5.7"  },
+	{ 0x1c,      0x040,     50,      "5.0"  },
+	{ 0x00,      0x050,     56,      "4.4"  },
+	{ 0x00,      0x060,     62,      "4.0"  },
+	{ 0x00,      0x070,     68,      "3.6"  },
+	{ 0x00,      0x000,      0,      NULL   }
+};
+
+/* Our Sequencer Program */
+#include "aic7xxx_seq.h"
+
+/**************************** Function Declarations ***************************/
+static struct tmode_tstate*
+			ahc_alloc_tstate(struct ahc_softc *ahc,
+					 u_int scsi_id, char channel);
+#ifdef AHC_TARGET_MODE
+static void		ahc_free_tstate(struct ahc_softc *ahc,
+					u_int scsi_id, char channel, int force);
+#endif
+static struct ahc_syncrate*
+			ahc_devlimited_syncrate(struct ahc_softc *ahc,
+					        struct ahc_initiator_tinfo *,
+						u_int *period,
+						u_int *ppr_options,
+						role_t role);
+static void		ahc_update_pending_syncrates(struct ahc_softc *ahc);
+static void		ahc_fetch_devinfo(struct ahc_softc *ahc,
+					  struct ahc_devinfo *devinfo);
+static void		ahc_scb_devinfo(struct ahc_softc *ahc,
+					struct ahc_devinfo *devinfo,
+					struct scb *scb);
+static void		ahc_setup_initiator_msgout(struct ahc_softc *ahc,
+						   struct ahc_devinfo *devinfo,
+						   struct scb *scb);
+static void		ahc_build_transfer_msg(struct ahc_softc *ahc,
+					       struct ahc_devinfo *devinfo);
+static void		ahc_construct_sdtr(struct ahc_softc *ahc,
+					   struct ahc_devinfo *devinfo,
+					   u_int period, u_int offset);
+static void		ahc_construct_wdtr(struct ahc_softc *ahc,
+					   struct ahc_devinfo *devinfo,
+					   u_int bus_width);
+static void		ahc_construct_ppr(struct ahc_softc *ahc,
+					  struct ahc_devinfo *devinfo,
+					  u_int period, u_int offset,
+					  u_int bus_width, u_int ppr_options);
+static void		ahc_clear_msg_state(struct ahc_softc *ahc);
+static void		ahc_handle_message_phase(struct ahc_softc *ahc);
+typedef enum {
+	AHCMSG_1B,
+	AHCMSG_2B,
+	AHCMSG_EXT
+} ahc_msgtype;
+static int		ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type,
+				     u_int msgval, int full);
+static int		ahc_parse_msg(struct ahc_softc *ahc,
+				      struct ahc_devinfo *devinfo);
+static int		ahc_handle_msg_reject(struct ahc_softc *ahc,
+					      struct ahc_devinfo *devinfo);
+static void		ahc_handle_ign_wide_residue(struct ahc_softc *ahc,
+						struct ahc_devinfo *devinfo);
+static void		ahc_handle_devreset(struct ahc_softc *ahc,
+					    struct ahc_devinfo *devinfo,
+					    cam_status status, char *message,
+					    int verbose_level);
+
+static bus_dmamap_callback_t	ahc_dmamap_cb; 
+static void			ahc_build_free_scb_list(struct ahc_softc *ahc);
+static int			ahc_init_scbdata(struct ahc_softc *ahc);
+static void			ahc_fini_scbdata(struct ahc_softc *ahc);
+static void		ahc_qinfifo_requeue(struct ahc_softc *ahc,
+					    struct scb *prev_scb,
+					    struct scb *scb);
+static int		ahc_qinfifo_count(struct ahc_softc *ahc);
+static u_int		ahc_rem_scb_from_disc_list(struct ahc_softc *ahc,
+						   u_int prev, u_int scbptr);
+static void		ahc_add_curscb_to_free_list(struct ahc_softc *ahc);
+static u_int		ahc_rem_wscb(struct ahc_softc *ahc,
+				     u_int scbpos, u_int prev);
+static int		ahc_abort_scbs(struct ahc_softc *ahc, int target,
+				       char channel, int lun, u_int tag,
+				       role_t role, uint32_t status);
+static void		ahc_reset_current_bus(struct ahc_softc *ahc);
+static void		ahc_calc_residual(struct scb *scb);
+#ifdef AHC_DUMP_SEQ
+static void		ahc_dumpseq(struct ahc_softc *ahc);
+#endif
+static void		ahc_loadseq(struct ahc_softc *ahc);
+static int		ahc_check_patch(struct ahc_softc *ahc,
+					struct patch **start_patch,
+					u_int start_instr, u_int *skip_addr);
+static void		ahc_download_instr(struct ahc_softc *ahc,
+					   u_int instrptr, uint8_t *dconsts);
+#ifdef AHC_TARGET_MODE
+static void		ahc_queue_lstate_event(struct ahc_softc *ahc,
+					       struct tmode_lstate *lstate,
+					       u_int initiator_id,
+					       u_int event_type,
+					       u_int event_arg);
+static void		ahc_update_scsiid(struct ahc_softc *ahc,
+					  u_int targid_mask);
+static int		ahc_handle_target_cmd(struct ahc_softc *ahc,
+					      struct target_cmd *cmd);
+#endif
+/************************* Sequencer Execution Control ************************/
+/*
+ * Restart the sequencer program from address zero
+ */
+void
+restart_sequencer(struct ahc_softc *ahc)
+{
+
+	pause_sequencer(ahc);
+
+	ahc_outb(ahc, SCSISIGO, 0);		/* De-assert BSY */
+	ahc_outb(ahc, MSG_OUT, MSG_NOOP);	/* No message to send */
+	ahc_outb(ahc, SXFRCTL1, ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
+
+	/*
+	 * Ensure that the sequencer's idea of TQINPOS
+	 * matches our own.  The sequencer increments TQINPOS
+	 * only after it sees a DMA complete and a reset could
+	 * occur before the increment leaving the kernel to believe
+	 * the command arrived but the sequencer to not.
+	 */
+	ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+
+	/* Always allow reselection */
+	ahc_outb(ahc, SCSISEQ,
+		 ahc_inb(ahc, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		/* Ensure that no DMA operations are in progress */
+		ahc_outb(ahc, CCSCBCNT, 0);
+		ahc_outb(ahc, CCSGCTL, 0);
+		ahc_outb(ahc, CCSCBCTL, 0);
+	}
+	/*
+	 * If we were in the process of DMA'ing SCB data into
+	 * an SCB, replace that SCB on the free list.  This prevents
+	 * an SCB leak.
+	 */
+	if ((ahc_inb(ahc, SEQ_FLAGS2) & SCB_DMA) != 0) {
+		ahc_add_curscb_to_free_list(ahc);
+		ahc_outb(ahc, SEQ_FLAGS2,
+			 ahc_inb(ahc, SEQ_FLAGS2) & ~SCB_DMA);
+	}
+	ahc_outb(ahc, MWI_RESIDUAL, 0);
+	ahc_outb(ahc, SEQCTL, FASTMODE);
+	ahc_outb(ahc, SEQADDR0, 0);
+	ahc_outb(ahc, SEQADDR1, 0);
+	unpause_sequencer(ahc);
+}
+
+/************************* Input/Output Queues ********************************/
+void
+ahc_run_qoutfifo(struct ahc_softc *ahc)
+{
+	struct scb *scb;
+	u_int  scb_index;
+
+	while (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL) {
+
+		scb_index = ahc->qoutfifo[ahc->qoutfifonext];
+		if ((ahc->qoutfifonext & 0x03) == 0x03) {
+			u_int modnext;
+
+			/*
+			 * Clear 32bits of QOUTFIFO at a time
+			 * so that we don't clobber an incomming
+			 * byte DMA to the array on architectures
+			 * that only support 32bit load and store
+			 * operations.
+			 */
+			modnext = ahc->qoutfifonext & ~0x3;
+			*((uint32_t *)(&ahc->qoutfifo[modnext])) = 0xFFFFFFFFUL;
+		}
+		ahc->qoutfifonext++;
+
+		scb = ahc_lookup_scb(ahc, scb_index);
+		if (scb == NULL) {
+			printf("%s: WARNING no command for scb %d "
+			       "(cmdcmplt)\nQOUTPOS = %d\n",
+			       ahc_name(ahc), scb_index,
+			       ahc->qoutfifonext - 1);
+			continue;
+		}
+
+		/*
+		 * Save off the residual
+		 * if there is one.
+		 */
+		if (ahc_check_residual(scb) != 0)
+			ahc_calc_residual(scb);
+		else
+			ahc_set_residual(scb, 0);
+		ahc_done(ahc, scb);
+	}
+}
+
+void
+ahc_run_untagged_queues(struct ahc_softc *ahc)
+{
+	int i;
+
+	for (i = 0; i < 16; i++)
+		ahc_run_untagged_queue(ahc, &ahc->untagged_queues[i]);
+}
+
+void
+ahc_run_untagged_queue(struct ahc_softc *ahc, struct scb_tailq *queue)
+{
+	struct scb *scb;
+
+	if (ahc->untagged_queue_lock != 0)
+		return;
+
+	if ((scb = TAILQ_FIRST(queue)) != NULL
+	 && (scb->flags & SCB_ACTIVE) == 0) {
+		scb->flags |= SCB_ACTIVE;
+		ahc_queue_scb(ahc, scb);
+	}
+}
+
+/************************* Interrupt Handling *********************************/
+void
+ahc_handle_brkadrint(struct ahc_softc *ahc)
+{
+	/*
+	 * We upset the sequencer :-(
+	 * Lookup the error message
+	 */
+	int i, error, num_errors;
+
+	error = ahc_inb(ahc, ERROR);
+	num_errors =  sizeof(hard_error)/sizeof(hard_error[0]);
+	for (i = 0; error != 1 && i < num_errors; i++)
+		error >>= 1;
+	printf("%s: brkadrint, %s at seqaddr = 0x%x\n",
+	       ahc_name(ahc), hard_error[i].errmesg,
+	       ahc_inb(ahc, SEQADDR0) |
+	       (ahc_inb(ahc, SEQADDR1) << 8));
+
+	ahc_dump_card_state(ahc);
+
+	/* Tell everyone that this HBA is no longer availible */
+	ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+		       CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
+		       CAM_NO_HBA);
+
+	/* Disable all interrupt sources by resetting the controller */
+	ahc_shutdown(ahc);
+}
+
+void
+ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
+{
+	struct scb *scb;
+	struct ahc_devinfo devinfo;
+	
+	ahc_fetch_devinfo(ahc, &devinfo);
+
+	/*
+	 * Clear the upper byte that holds SEQINT status
+	 * codes and clear the SEQINT bit. We will unpause
+	 * the sequencer, if appropriate, after servicing
+	 * the request.
+	 */
+	ahc_outb(ahc, CLRINT, CLRSEQINT);
+	switch (intstat & SEQINT_MASK) {
+	case BAD_STATUS:
+	{
+		u_int  scb_index;
+		struct hardware_scb *hscb;
+
+		/*
+		 * Set the default return value to 0 (don't
+		 * send sense).  The sense code will change
+		 * this if needed.
+		 */
+		ahc_outb(ahc, RETURN_1, 0);
+
+		/*
+		 * 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 already have
+		 * dma'd the SCB back up to us, so we can reference
+		 * the in kernel copy directly.
+		 */
+		scb_index = ahc_inb(ahc, SCB_TAG);
+		scb = ahc_lookup_scb(ahc, scb_index);
+		if (scb == NULL) {
+			printf("%s:%c:%d: ahc_intr - referenced scb "
+			       "not valid during seqint 0x%x scb(%d)\n",
+			       ahc_name(ahc), devinfo.channel,
+			       devinfo.target, intstat, scb_index);
+			ahc_dump_card_state(ahc);
+			panic("for safety");
+			goto unpause;
+		}
+
+		hscb = scb->hscb; 
+
+		/* Don't want to clobber the original sense code */
+		if ((scb->flags & SCB_SENSE) != 0) {
+			/*
+			 * Clear the SCB_SENSE Flag and have
+			 * the sequencer do a normal command
+			 * complete.
+			 */
+			scb->flags &= ~SCB_SENSE;
+			ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+			break;
+		}
+		ahc_set_transaction_status(scb, CAM_SCSI_STATUS_ERROR);
+		/* Freeze the queue until the client sees the error. */
+		ahc_freeze_devq(ahc, scb);
+		ahc_freeze_scb(scb);
+		ahc_set_scsi_status(scb, hscb->shared_data.status.scsi_status);
+		switch (hscb->shared_data.status.scsi_status) {
+		case SCSI_STATUS_OK:
+			printf("%s: Interrupted for staus of 0???\n",
+			       ahc_name(ahc));
+			break;
+		case SCSI_STATUS_CMD_TERMINATED:
+		case SCSI_STATUS_CHECK_COND:
+#ifdef AHC_DEBUG
+			if (ahc_debug & AHC_SHOWSENSE) {
+				ahc_print_path(ahc, scb);
+				printf("SCB %d: requests Check Status\n",
+				       scb->hscb->tag);
+			}
+#endif
+
+			if (ahc_perform_autosense(scb)) {
+				struct ahc_dma_seg *sg;
+				struct scsi_sense *sc;
+				struct ahc_initiator_tinfo *targ_info;
+				struct tmode_tstate *tstate;
+				struct ahc_transinfo *tinfo;
+
+				targ_info =
+				    ahc_fetch_transinfo(ahc,
+							devinfo.channel,
+							devinfo.our_scsiid,
+							devinfo.target,
+							&tstate);
+				tinfo = &targ_info->current;
+				sg = scb->sg_list;
+				sc = (struct scsi_sense *)
+				     (&hscb->shared_data.cdb); 
+				/*
+				 * Save off the residual if there is one.
+				 */
+				if (ahc_check_residual(scb))
+					ahc_calc_residual(scb);
+				else
+					ahc_set_residual(scb, 0);
+#ifdef AHC_DEBUG
+				if (ahc_debug & AHC_SHOWSENSE) {
+					ahc_print_path(ahc, scb);
+					printf("Sending Sense\n");
+				}
+#endif
+				sg->addr = ahc_get_sense_bufaddr(ahc, scb);
+				sg->len = ahc_get_sense_bufsize(ahc, scb);
+				sg->len |= AHC_DMA_LAST_SEG;
+
+				/* Fixup byte order */
+				sg->addr = ahc_htole32(sg->addr);
+				sg->len = ahc_htole32(sg->len);
+
+				sc->opcode = REQUEST_SENSE;
+				sc->byte2 = 0;
+				if (tinfo->protocol_version <= SCSI_REV_2
+				 && SCB_GET_LUN(scb) < 8)
+					sc->byte2 = SCB_GET_LUN(scb) << 5;
+				sc->unused[0] = 0;
+				sc->unused[1] = 0;
+				sc->length = sg->len;
+				sc->control = 0;
+
+				/*
+				 * XXX Still true???
+				 * Would be nice to preserve DISCENB here,
+				 * but due to the way we manage busy targets,
+				 * we can't.
+				 */
+				hscb->control = 0;
+
+				/*
+				 * This request sense could be because the
+				 * the device lost power or in some other
+				 * way has lost our transfer negotiations.
+				 * Renegotiate if appropriate.  Unit attention
+				 * errors will be reported before any data
+				 * phases occur.
+				 */
+				if (ahc_get_residual(scb) 
+				 == ahc_get_transfer_length(scb)) {
+					ahc_update_target_msg_request(ahc,
+							      &devinfo,
+							      targ_info,
+							      /*force*/TRUE,
+							      /*paused*/TRUE);
+				}
+				hscb->cdb_len = sizeof(*sc);
+				hscb->dataptr = sg->addr; 
+				hscb->datacnt = sg->len;
+				hscb->sgptr = scb->sg_list_phys | SG_FULL_RESID;
+				hscb->sgptr = ahc_htole32(hscb->sgptr);
+				scb->sg_count = 1;
+				scb->flags |= SCB_SENSE;
+				ahc_qinfifo_requeue_tail(ahc, scb);
+				ahc_outb(ahc, RETURN_1, SEND_SENSE);
+#ifdef __FreeBSD__
+				/*
+				 * Ensure we have enough time to actually
+				 * retrieve the sense.
+				 */
+				untimeout(ahc_timeout, (caddr_t)scb,
+					  scb->io_ctx->ccb_h.timeout_ch);
+				scb->io_ctx->ccb_h.timeout_ch =
+				    timeout(ahc_timeout, (caddr_t)scb, 5 * hz);
+#endif
+			}
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+	case NO_MATCH:
+	{
+		/* Ensure we don't leave the selection hardware on */
+		ahc_outb(ahc, SCSISEQ,
+			 ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+		printf("%s:%c:%d: no active SCB for reconnecting "
+		       "target - issuing BUS DEVICE RESET\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target);
+		printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+		       "ARG_1 == 0x%x ACCUM = 0x%x\n",
+		       ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+		       ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+		printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+		       "SINDEX == 0x%x\n",
+		       ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+		       ahc_index_busy_tcl(ahc,
+			    BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+				      ahc_inb(ahc, SAVED_LUN))),
+		       ahc_inb(ahc, SINDEX));
+		printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+		       "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+		       ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+		       ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+		       ahc_inb(ahc, SCB_CONTROL));
+		printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+		       ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+		printf("SXFRCTL0 == 0x%x\n", ahc_inb(ahc, SXFRCTL0));
+		printf("SEQCTL == 0x%x\n", ahc_inb(ahc, SEQCTL));
+		ahc_dump_card_state(ahc);
+		ahc->msgout_buf[0] = MSG_BUS_DEV_RESET;
+		ahc->msgout_len = 1;
+		ahc->msgout_index = 0;
+		ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+		ahc_outb(ahc, MSG_OUT, HOST_MSG);
+		ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, LASTPHASE) | ATNO);
+		break;
+	}
+	case SEND_REJECT: 
+	{
+		u_int rejbyte = ahc_inb(ahc, ACCUM);
+		printf("%s:%c:%d: Warning - unknown message received from "
+		       "target (0x%x).  Rejecting\n", 
+		       ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
+		break; 
+	}
+	case NO_IDENT: 
+	{
+		/*
+		 * The reconnecting target either did not send an identify
+		 * message, or did, but we didn't find an 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;
+
+		printf("%s:%c:%d: Target did not send an IDENTIFY message. "
+		       "LASTPHASE = 0x%x, SAVED_SCSIID == 0x%x\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target,
+		       ahc_inb(ahc, LASTPHASE), ahc_inb(ahc, SAVED_SCSIID));
+		found = ahc_reset_channel(ahc, devinfo.channel, 
+					  /*initiate reset*/TRUE);
+		printf("%s: Issued Channel %c Bus Reset. "
+		       "%d SCBs aborted\n", ahc_name(ahc), devinfo.channel,
+		       found);
+		return;
+	}
+	case IGN_WIDE_RES:
+		ahc_handle_ign_wide_residue(ahc, &devinfo);
+		break;
+	case BAD_PHASE:
+	{
+		u_int lastphase;
+
+		lastphase = ahc_inb(ahc, LASTPHASE);
+		printf("%s:%c:%d: unknown scsi bus phase %x, "
+		       "lastphase = 0x%x.  Attempting to continue\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target,
+		       lastphase, ahc_inb(ahc, SCSISIGI));
+		break;
+	}
+	case MISSED_BUSFREE:
+	{
+		u_int lastphase;
+
+		lastphase = ahc_inb(ahc, LASTPHASE);
+		printf("%s:%c:%d: Missed busfree. "
+		       "Lastphase = 0x%x, Curphase = 0x%x\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target,
+		       lastphase, ahc_inb(ahc, SCSISIGI));
+		restart_sequencer(ahc);
+		return;
+	}
+	case HOST_MSG_LOOP:
+	{
+		/*
+		 * The sequencer has encountered a message phase
+		 * that requires host assistance for completion.
+		 * While handling the message phase(s), we will be
+		 * notified by the sequencer after each byte is
+		 * transfered so we can track bus phase changes.
+		 *
+		 * If this is the first time we've seen a HOST_MSG_LOOP
+		 * interrupt, initialize the state of the host message
+		 * loop.
+		 */
+		if (ahc->msg_type == MSG_TYPE_NONE) {
+			u_int bus_phase;
+
+			bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+			if (bus_phase != P_MESGIN
+			 && bus_phase != P_MESGOUT) {
+				printf("ahc_intr: HOST_MSG_LOOP bad "
+				       "phase 0x%x\n",
+				      bus_phase);
+				/*
+				 * Probably transitioned to bus free before
+				 * we got here.  Just punt the message.
+				 */
+				ahc_clear_intstat(ahc);
+				restart_sequencer(ahc);
+				return;
+			}
+
+			if (devinfo.role == ROLE_INITIATOR) {
+				struct scb *scb;
+				u_int scb_index;
+
+				scb_index = ahc_inb(ahc, SCB_TAG);
+				scb = ahc_lookup_scb(ahc, scb_index);
+
+				if (scb == NULL)
+					panic("HOST_MSG_LOOP with "
+					      "invalid SCB %x\n", scb_index);
+
+				if (bus_phase == P_MESGOUT)
+					ahc_setup_initiator_msgout(ahc,
+								   &devinfo,
+								   scb);
+				else {
+					ahc->msg_type =
+					    MSG_TYPE_INITIATOR_MSGIN;
+					ahc->msgin_index = 0;
+				}
+			} else {
+				if (bus_phase == P_MESGOUT) {
+					ahc->msg_type =
+					    MSG_TYPE_TARGET_MSGOUT;
+					ahc->msgin_index = 0;
+				}
+#if AHC_TARGET_MODE
+				else 
+					ahc_setup_target_msgin(ahc, &devinfo);
+#endif
+			}
+		}
+
+		ahc_handle_message_phase(ahc);
+		break;
+	}
+	case PERR_DETECTED:
+	{
+		/*
+		 * If we've cleared the parity error interrupt
+		 * but the sequencer still believes that SCSIPERR
+		 * is true, it must be that the parity error is
+		 * for the currently presented byte on the bus,
+		 * and we are not in a phase (data-in) where we will
+		 * eventually ack this byte.  Ack the byte and
+		 * throw it away in the hope that the target will
+		 * take us to message out to deliver the appropriate
+		 * error message.
+		 */
+		if ((intstat & SCSIINT) == 0
+		 && (ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0) {
+			u_int curphase;
+
+			/*
+			 * The hardware will only let you ack bytes
+			 * if the expected phase in SCSISIGO matches
+			 * the current phase.  Make sure this is
+			 * currently the case.
+			 */
+			curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+			ahc_outb(ahc, LASTPHASE, curphase);
+			ahc_outb(ahc, SCSISIGO, curphase);
+			ahc_inb(ahc, SCSIDATL);
+		}
+		break;
+	}
+	case DATA_OVERRUN:
+	{
+		/*
+		 * When the sequencer detects an overrun, it
+		 * places the controller in "BITBUCKET" mode
+		 * and allows the target to complete its transfer.
+		 * Unfortunately, none of the counters get updated
+		 * when the controller is in this mode, so we have
+		 * no way of knowing how large the overrun was.
+		 */
+		u_int scbindex = ahc_inb(ahc, SCB_TAG);
+		u_int lastphase = ahc_inb(ahc, LASTPHASE);
+		u_int i;
+
+		scb = ahc_lookup_scb(ahc, scbindex);
+		for (i = 0; i < num_phases; i++) {
+			if (lastphase == phase_table[i].phase)
+				break;
+		}
+		ahc_print_path(ahc, scb);
+		printf("data overrun detected %s."
+		       "  Tag == 0x%x.\n",
+		       phase_table[i].phasemsg,
+  		       scb->hscb->tag);
+		ahc_print_path(ahc, scb);
+		printf("%s seen Data Phase.  Length = %ld.  NumSGs = %d.\n",
+		       ahc_inb(ahc, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+		       ahc_get_transfer_length(scb), scb->sg_count);
+		if (scb->sg_count > 0) {
+			for (i = 0; i < scb->sg_count; i++) {
+				printf("sg[%d] - Addr 0x%x : Length %d\n",
+				       i,
+				       ahc_le32toh(scb->sg_list[i].addr),
+				       ahc_le32toh(scb->sg_list[i].len)
+				       & AHC_SG_LEN_MASK);
+			}
+		}
+		/*
+		 * Set this and it will take effect when the
+		 * target does a command complete.
+		 */
+		ahc_freeze_devq(ahc, scb);
+		ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+		ahc_freeze_scb(scb);
+		break;
+	}
+	case MKMSG_FAILED:
+	{
+		u_int scbindex;
+
+		printf("%s:%c:%d:%d: Attempt to issue message failed\n",
+		       ahc_name(ahc), devinfo.channel, devinfo.target,
+		       devinfo.lun);
+		scbindex = ahc_inb(ahc, SCB_TAG);
+		scb = ahc_lookup_scb(ahc, scbindex);
+		if (scb != NULL
+		 && (scb->flags & SCB_RECOVERY_SCB) != 0)
+			/*
+			 * Ensure that we didn't put a second instance of this
+			 * SCB into the QINFIFO.
+			 */
+			ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+					   SCB_GET_CHANNEL(ahc, scb),
+					   SCB_GET_LUN(scb), scb->hscb->tag,
+					   ROLE_INITIATOR, /*status*/0,
+					   SEARCH_REMOVE);
+		break;
+	}
+	case NO_FREE_SCB:
+	{
+		printf("%s: No free or disconnected SCBs\n", ahc_name(ahc));
+		ahc_dump_card_state(ahc);
+		panic("for safety");
+		break;
+	}
+	case SCB_MISMATCH:
+	{
+		u_int scbptr;
+
+		scbptr = ahc_inb(ahc, SCBPTR);
+		printf("Bogus TAG after DMA.  SCBPTR %d, tag %d, our tag %d\n",
+		       scbptr, ahc_inb(ahc, ARG_1),
+		       ahc->scb_data->hscbs[scbptr].tag);
+		ahc_dump_card_state(ahc);
+		panic("for saftey");
+		break;
+	}
+	case OUT_OF_RANGE:
+	{
+		printf("%s: BTT calculation out of range\n", ahc_name(ahc));
+		printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+		       "ARG_1 == 0x%x ACCUM = 0x%x\n",
+		       ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+		       ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+		printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+		       "SINDEX == 0x%x\n, A == 0x%x\n",
+		       ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+		       ahc_index_busy_tcl(ahc,
+			    BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+				      ahc_inb(ahc, SAVED_LUN))),
+		       ahc_inb(ahc, SINDEX),
+		       ahc_inb(ahc, ACCUM));
+		printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+		       "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+		       ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+		       ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+		       ahc_inb(ahc, SCB_CONTROL));
+		printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+		       ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+		ahc_dump_card_state(ahc);
+		panic("for safety");
+		break;
+	}
+	default:
+		printf("ahc_intr: seqint, "
+		       "intstat == 0x%x, scsisigi = 0x%x\n",
+		       intstat, ahc_inb(ahc, SCSISIGI));
+		break;
+	}
+unpause:
+	/*
+	 *  The sequencer is paused immediately on
+	 *  a SEQINT, so we should restart it when
+	 *  we're done.
+	 */
+	unpause_sequencer(ahc);
+}
+
+void
+ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
+{
+	u_int	scb_index;
+	u_int	status0;
+	u_int	status;
+	struct	scb *scb;
+	char	cur_channel;
+	char	intr_channel;
+
+	/* Make sure the sequencer is in a safe location. */
+	ahc_clear_critical_section(ahc);
+
+	if ((ahc->features & AHC_TWIN) != 0
+	 && ((ahc_inb(ahc, SBLKCTL) & SELBUSB) != 0))
+		cur_channel = 'B';
+	else
+		cur_channel = 'A';
+	intr_channel = cur_channel;
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		status0 = ahc_inb(ahc, SSTAT0) & IOERR;
+	else
+		status0 = 0;
+	status = ahc_inb(ahc, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+	if (status == 0 && status0 == 0) {
+		if ((ahc->features & AHC_TWIN) != 0) {
+			/* Try the other channel */
+		 	ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+			status = ahc_inb(ahc, SSTAT1)
+			       & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+			intr_channel = (cur_channel == 'A') ? 'B' : 'A';
+		}
+		if (status == 0) {
+			printf("%s: Spurious SCSI interrupt\n", ahc_name(ahc));
+			ahc_outb(ahc, CLRINT, CLRSCSIINT);
+			unpause_sequencer(ahc);
+			return;
+		}
+	}
+
+	scb_index = ahc_inb(ahc, SCB_TAG);
+	scb = ahc_lookup_scb(ahc, scb_index);
+	if (scb != NULL
+	 && (ahc_inb(ahc, SEQ_FLAGS) & IDENTIFY_SEEN) == 0)
+		scb = NULL;
+
+	if ((ahc->features & AHC_ULTRA2) != 0
+	 && (status0 & IOERR) != 0) {
+		int now_lvd;
+
+		now_lvd = ahc_inb(ahc, SBLKCTL) & ENAB40;
+		printf("%s: Transceiver State Has Changed to %s mode\n",
+		       ahc_name(ahc), now_lvd ? "LVD" : "SE");
+		ahc_outb(ahc, CLRSINT0, CLRIOERR);
+		/*
+		 * When transitioning to SE mode, the reset line
+		 * glitches, triggering an arbitration bug in some
+		 * Ultra2 controllers.  This bug is cleared when we
+		 * assert the reset line.  Since a reset glitch has
+		 * already occurred with this transition and a
+		 * transceiver state change is handled just like
+		 * a bus reset anyway, asserting the reset line
+		 * ourselves is safe.
+		 */
+		ahc_reset_channel(ahc, intr_channel,
+				 /*Initiate Reset*/now_lvd == 0);
+	} else if ((status & SCSIRSTI) != 0) {
+		printf("%s: Someone reset channel %c\n",
+			ahc_name(ahc), intr_channel);
+		if (intr_channel != cur_channel)
+		 	ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+		ahc_reset_channel(ahc, intr_channel, /*Initiate Reset*/FALSE);
+	} else if ((status & SCSIPERR) != 0) {
+		/*
+		 * Determine the bus phase and queue an appropriate message.
+		 * SCSIPERR is latched true as soon as a parity error
+		 * occurs.  If the sequencer acked the transfer that
+		 * caused the parity error and the currently presented
+		 * transfer on the bus has correct parity, SCSIPERR will
+		 * be cleared by CLRSCSIPERR.  Use this to determine if
+		 * we should look at the last phase the sequencer recorded,
+		 * or the current phase presented on the bus.
+		 */
+		u_int mesg_out;
+		u_int curphase;
+		u_int errorphase;
+		u_int lastphase;
+		u_int scsirate;
+		u_int i;
+		u_int sstat2;
+
+		lastphase = ahc_inb(ahc, LASTPHASE);
+		curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+		sstat2 = ahc_inb(ahc, SSTAT2);
+		ahc_outb(ahc, CLRSINT1, CLRSCSIPERR);
+		/*
+		 * For all phases save DATA, the sequencer won't
+		 * automatically ack a byte that has a parity error
+		 * in it.  So the only way that the current phase
+		 * could be 'data-in' is if the parity error is for
+		 * an already acked byte in the data phase.  During
+		 * synchronous data-in transfers, we may actually
+		 * ack bytes before latching the current phase in
+		 * LASTPHASE, leading to the discrepancy between
+		 * curphase and lastphase.
+		 */
+		if ((ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0
+		 || curphase == P_DATAIN || curphase == P_DATAIN_DT)
+			errorphase = curphase;
+		else
+			errorphase = lastphase;
+
+		for (i = 0; i < num_phases; i++) {
+			if (errorphase == phase_table[i].phase)
+				break;
+		}
+		mesg_out = phase_table[i].mesg_out;
+		if (scb != NULL)
+			ahc_print_path(ahc, scb);
+		else
+			printf("%s:%c:%d: ", ahc_name(ahc), intr_channel,
+			       SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)));
+		scsirate = ahc_inb(ahc, SCSIRATE);
+		printf("parity error detected %s. "
+		       "SEQADDR(0x%x) SCSIRATE(0x%x)\n",
+		       phase_table[i].phasemsg,
+		       ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8),
+		       scsirate);
+
+		if ((ahc->features & AHC_DT) != 0) {
+
+			if ((sstat2 & CRCVALERR) != 0)
+				printf("\tCRC Value Mismatch\n");
+			if ((sstat2 & CRCENDERR) != 0)
+				printf("\tNo terminal CRC packet recevied\n");
+			if ((sstat2 & CRCREQERR) != 0)
+				printf("\tIllegal CRC packet request\n");
+			if ((sstat2 & DUAL_EDGE_ERR) != 0)
+				printf("\tUnexpected %sDT Data Phase\n",
+				       (scsirate & SINGLE_EDGE) ? "" : "non-");
+		}
+
+		/*
+		 * 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) {
+			if (ahc->msg_type != MSG_TYPE_NONE)
+				ahc->send_msg_perror = TRUE;
+			else
+				ahc_outb(ahc, MSG_OUT, mesg_out);
+		}
+		ahc_outb(ahc, CLRINT, CLRSCSIINT);
+		unpause_sequencer(ahc);
+	} else if ((status & BUSFREE) != 0
+		&& (ahc_inb(ahc, SIMODE1) & ENBUSFREE) != 0) {
+		u_int lastphase;
+		u_int saved_scsiid;
+		u_int saved_lun;
+		u_int target;
+		u_int initiator_role_id;
+		char channel;
+		int printerror;
+
+		/*
+		 * Clear our selection hardware as soon as possible.
+		 * We may have an entry in the waiting Q for this target,
+		 * that is affected by this busfree and we don't want to
+		 * go about selecting the target while we handle the event.
+		 */
+		ahc_outb(ahc, SCSISEQ,
+			 ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+		/*
+		 * Disable busfree interrupts and clear the busfree
+		 * interrupt status.  We do this here so that several
+		 * bus transactions occur prior to clearing the SCSIINT
+		 * latch.  It can take a bit for the clearing to take effect.
+		 */
+		ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+		ahc_outb(ahc, CLRSINT1, CLRBUSFREE|CLRSCSIPERR);
+
+		/*
+		 * Look at what phase we were last in.
+		 * If its message out, chances are pretty good
+		 * that the busfree was in response to one of
+		 * our abort requests.
+		 */
+		lastphase = ahc_inb(ahc, LASTPHASE);
+		saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+		saved_lun = ahc_inb(ahc, SAVED_LUN);
+		target = SCSIID_TARGET(ahc, saved_scsiid);
+		initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
+		channel = SCSIID_CHANNEL(ahc, saved_scsiid);
+		printerror = 1;
+
+		if (lastphase == P_MESGOUT) {
+			struct ahc_devinfo devinfo;
+			u_int tag;
+
+			ahc_fetch_devinfo(ahc, &devinfo);
+			tag = SCB_LIST_NULL;
+			if (ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT_TAG, TRUE)
+			 || ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT, TRUE)) {
+				if (ahc->msgout_buf[ahc->msgout_index - 1]
+				 == MSG_ABORT_TAG)
+					tag = scb->hscb->tag;
+				ahc_print_path(ahc, scb);
+				printf("SCB %d - Abort%s Completed.\n",
+				       scb->hscb->tag, tag == SCB_LIST_NULL ?
+				       "" : " Tag");
+				ahc_abort_scbs(ahc, target, channel,
+					       saved_lun, tag,
+					       ROLE_INITIATOR,
+					       CAM_REQ_ABORTED);
+				printerror = 0;
+			} else if (ahc_sent_msg(ahc, AHCMSG_1B,
+						MSG_BUS_DEV_RESET, TRUE)) {
+				struct ahc_devinfo devinfo;
+#ifdef __FreeBSD__
+				/*
+				 * Don't mark the user's request for this BDR
+				 * as completing with CAM_BDR_SENT.  CAM3
+				 * specifies CAM_REQ_CMP.
+				 */
+				if (scb != NULL
+				 && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
+				 && ahc_match_scb(ahc, scb, target, channel,
+						  CAM_LUN_WILDCARD,
+						  SCB_LIST_NULL,
+						  ROLE_INITIATOR)) {
+					ahc_set_transaction_status(scb, CAM_REQ_CMP);
+				}
+#endif
+				ahc_compile_devinfo(&devinfo,
+						    initiator_role_id,
+						    target,
+						    CAM_LUN_WILDCARD,
+						    channel,
+						    ROLE_INITIATOR);
+				ahc_handle_devreset(ahc, &devinfo,
+						    CAM_BDR_SENT,
+						    "Bus Device Reset",
+						    /*verbose_level*/0);
+				printerror = 0;
+			} else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+						MSG_EXT_PPR, FALSE)) {
+				struct ahc_initiator_tinfo *tinfo;
+				struct tmode_tstate *tstate;
+
+				/*
+				 * PPR Rejected.  Try non-ppr negotiation
+				 * and retry command.
+				 */
+				tinfo = ahc_fetch_transinfo(ahc,
+							    devinfo.channel,
+							    devinfo.our_scsiid,
+							    devinfo.target,
+							    &tstate);
+				tinfo->current.transport_version = 2;
+				tinfo->goal.transport_version = 2;
+				tinfo->goal.ppr_options = 0;
+				ahc_qinfifo_requeue_tail(ahc, scb);
+				printerror = 0;
+			} else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+						MSG_EXT_WDTR, FALSE)
+				|| ahc_sent_msg(ahc, AHCMSG_EXT,
+						MSG_EXT_SDTR, FALSE)) {
+				/*
+				 * Negotiation Rejected.  Go-async and
+				 * retry command.
+				 */
+				ahc_set_width(ahc, &devinfo,
+					      MSG_EXT_WDTR_BUS_8_BIT,
+					      AHC_TRANS_CUR|AHC_TRANS_GOAL,
+					      /*paused*/TRUE);
+				ahc_set_syncrate(ahc, &devinfo,
+						/*syncrate*/NULL,
+						/*period*/0, /*offset*/0,
+						/*ppr_options*/0,
+						AHC_TRANS_CUR|AHC_TRANS_GOAL,
+						/*paused*/TRUE);
+				ahc_qinfifo_requeue_tail(ahc, scb);
+				printerror = 0;
+			}
+		}
+		if (printerror != 0) {
+			u_int i;
+
+			if (scb != NULL) {
+				u_int tag;
+
+				if ((scb->hscb->control & TAG_ENB) != 0)
+					tag = scb->hscb->tag;
+				else
+					tag = SCB_LIST_NULL;
+				ahc_print_path(ahc, scb);
+				ahc_abort_scbs(ahc, target, channel,
+					       SCB_GET_LUN(scb), tag,
+					       ROLE_INITIATOR,
+					       CAM_UNEXP_BUSFREE);
+			} else {
+				/*
+				 * We had not fully identified this connection,
+				 * so we cannot abort anything.
+				 */
+				printf("%s: ", ahc_name(ahc));
+			}
+			for (i = 0; i < num_phases; i++) {
+				if (lastphase == phase_table[i].phase)
+					break;
+			}
+			printf("Unexpected busfree %s\n"
+			       "SEQADDR == 0x%x\n",
+			       phase_table[i].phasemsg, ahc_inb(ahc, SEQADDR0)
+				| (ahc_inb(ahc, SEQADDR1) << 8));
+		}
+		ahc_clear_msg_state(ahc);
+		ahc_outb(ahc, CLRINT, CLRSCSIINT);
+		restart_sequencer(ahc);
+	} else if ((status & SELTO) != 0) {
+		u_int scbptr;
+
+		/* Stop the selection */
+		ahc_outb(ahc, SCSISEQ, 0);
+
+		/* No more pending messages */
+		ahc_clear_msg_state(ahc);
+
+		/* Clear interrupt state */
+		ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
+
+		/*
+		 * Although the driver does not care about the
+		 * 'Selection in Progress' status bit, the busy
+		 * LED does.  SELINGO is only cleared by a sucessful
+		 * selection, so we must manually clear it to insure
+		 * the LED turns off just incase no future successful
+		 * selections occur (e.g. no devices on the bus).
+		 */
+		ahc_outb(ahc, CLRSINT0, CLRSELINGO);
+
+		scbptr = ahc_inb(ahc, WAITING_SCBH);
+		ahc_outb(ahc, SCBPTR, scbptr);
+		scb_index = ahc_inb(ahc, SCB_TAG);
+
+		scb = ahc_lookup_scb(ahc, scb_index);
+		if (scb == NULL) {
+			printf("%s: ahc_intr - referenced scb not "
+			       "valid during SELTO scb(%d, %d)\n",
+			       ahc_name(ahc), scbptr, scb_index);
+		} else {
+			ahc_set_transaction_status(scb, CAM_SEL_TIMEOUT);
+			ahc_freeze_devq(ahc, scb);
+		}
+		ahc_outb(ahc, CLRINT, CLRSCSIINT);
+		restart_sequencer(ahc);
+	} else {
+		printf("%s: Missing case in ahc_handle_scsiint. status = %x\n",
+		       ahc_name(ahc), status);
+		ahc_outb(ahc, CLRINT, CLRSCSIINT);
+	}
+}
+
+#define AHC_MAX_STEPS 2000
+void
+ahc_clear_critical_section(struct ahc_softc *ahc)
+{
+	int	stepping;
+	int	steps;
+	u_int	simode0;
+	u_int	simode1;
+
+	if (ahc->num_critical_sections == 0)
+		return;
+
+	stepping = FALSE;
+	steps = 0;
+	simode0 = 0;
+	simode1 = 0;
+	for (;;) {
+		struct	cs *cs;
+		u_int	seqaddr;
+		u_int	i;
+
+		seqaddr = ahc_inb(ahc, SEQADDR0)
+			| (ahc_inb(ahc, SEQADDR1) << 8);
+
+		cs = ahc->critical_sections;
+		for (i = 0; i < ahc->num_critical_sections; i++, cs++) {
+			
+			if (cs->begin < seqaddr && cs->end >= seqaddr)
+				break;
+		}
+
+		if (i == ahc->num_critical_sections)
+			break;
+
+		if (steps > AHC_MAX_STEPS) {
+			printf("%s: Infinite loop in critical section\n",
+			       ahc_name(ahc));
+			ahc_dump_card_state(ahc);
+			panic("critical section loop");
+		}
+
+		steps++;
+		if (stepping == FALSE) {
+
+			/*
+			 * Disable all interrupt sources so that the
+			 * sequencer will not be stuck by a pausing
+			 * interrupt condition while we attempt to
+			 * leave a critical section.
+			 */
+			simode0 = ahc_inb(ahc, SIMODE0);
+			ahc_outb(ahc, SIMODE0, 0);
+			simode1 = ahc_inb(ahc, SIMODE1);
+			ahc_outb(ahc, SIMODE1, 0);
+			ahc_outb(ahc, CLRINT, CLRSCSIINT);
+			ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) | STEP);
+			stepping = TRUE;
+		}
+		ahc_outb(ahc, HCNTRL, ahc->unpause);
+		do {
+			ahc_delay(200);
+		} while (!sequencer_paused(ahc));
+	}
+	if (stepping) {
+		ahc_outb(ahc, SIMODE0, simode0);
+		ahc_outb(ahc, SIMODE1, simode1);
+		ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) & ~STEP);
+	}
+}
+
+/*
+ * Clear any pending interrupt status.
+ */
+void
+ahc_clear_intstat(struct ahc_softc *ahc)
+{
+	/* Clear any interrupt conditions this may have caused */
+	ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+				|CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG|
+				CLRREQINIT);
+	ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO);
+	ahc_outb(ahc, CLRINT, CLRSCSIINT);
+}
+
+/**************************** Debugging Routines ******************************/
+void
+ahc_print_scb(struct scb *scb)
+{
+	int i;
+
+	struct hardware_scb *hscb = scb->hscb;
+
+	printf("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
+	       scb,
+	       hscb->control,
+	       hscb->scsiid,
+	       hscb->lun,
+	       hscb->cdb_len);
+	i = 0;
+	printf("Shared Data: %#02x %#02x %#02x %#02x\n",
+	       hscb->shared_data.cdb[i++],
+	       hscb->shared_data.cdb[i++],
+	       hscb->shared_data.cdb[i++],
+	       hscb->shared_data.cdb[i++]);
+	printf("             %#02x %#02x %#02x %#02x\n",
+	       hscb->shared_data.cdb[i++],
+	       hscb->shared_data.cdb[i++],
+	       hscb->shared_data.cdb[i++],
+	       hscb->shared_data.cdb[i++]);
+	printf("             %#02x %#02x %#02x %#02x\n",
+	       hscb->shared_data.cdb[i++],
+	       hscb->shared_data.cdb[i++],
+	       hscb->shared_data.cdb[i++],
+	       hscb->shared_data.cdb[i++]);
+	printf("        dataptr:%#x datacnt:%#x sgptr:%#x tag:%#x\n",
+		ahc_le32toh(hscb->dataptr),
+		ahc_le32toh(hscb->datacnt),
+		ahc_le32toh(hscb->sgptr),
+		hscb->tag);
+	if (scb->sg_count > 0) {
+		for (i = 0; i < scb->sg_count; i++) {
+			printf("sg[%d] - Addr 0x%x : Length %d\n",
+			       i,
+			       ahc_le32toh(scb->sg_list[i].addr),
+			       ahc_le32toh(scb->sg_list[i].len));
+		}
+	}
+}
+
+/************************* Transfer Negotiation *******************************/
+/*
+ * Allocate per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static struct tmode_tstate *
+ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
+{
+	struct tmode_tstate *master_tstate;
+	struct tmode_tstate *tstate;
+	int i;
+
+	master_tstate = ahc->enabled_targets[ahc->our_id];
+	if (channel == 'B') {
+		scsi_id += 8;
+		master_tstate = ahc->enabled_targets[ahc->our_id_b + 8];
+	}
+	if (ahc->enabled_targets[scsi_id] != NULL
+	 && ahc->enabled_targets[scsi_id] != master_tstate)
+		panic("%s: ahc_alloc_tstate - Target already allocated",
+		      ahc_name(ahc));
+	tstate = malloc(sizeof(*tstate), M_DEVBUF, M_NOWAIT);
+	if (tstate == NULL)
+		return (NULL);
+
+	/*
+	 * If we have allocated a master tstate, copy user settings from
+	 * the master tstate (taken from SRAM or the EEPROM) for this
+	 * channel, but reset our current and goal settings to async/narrow
+	 * until an initiator talks to us.
+	 */
+	if (master_tstate != NULL) {
+		memcpy(tstate, master_tstate, sizeof(*tstate));
+		memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
+		tstate->ultraenb = 0;
+		for (i = 0; i < 16; i++) {
+			memset(&tstate->transinfo[i].current, 0,
+			      sizeof(tstate->transinfo[i].current));
+			memset(&tstate->transinfo[i].goal, 0,
+			      sizeof(tstate->transinfo[i].goal));
+		}
+	} else
+		memset(tstate, 0, sizeof(*tstate));
+	ahc->enabled_targets[scsi_id] = tstate;
+	return (tstate);
+}
+
+#ifdef AHC_TARGET_MODE
+/*
+ * Free per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static void
+ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
+{
+	struct tmode_tstate *tstate;
+
+	/* Don't clean up the entry for our initiator role */
+	if ((ahc->flags & AHC_INITIATORROLE) != 0
+	 && ((channel == 'B' && scsi_id == ahc->our_id_b)
+	  || (channel == 'A' && scsi_id == ahc->our_id))
+	 && force == FALSE)
+		return;
+
+	if (channel == 'B')
+		scsi_id += 8;
+	tstate = ahc->enabled_targets[scsi_id];
+	if (tstate != NULL)
+		free(tstate, M_DEVBUF);
+	ahc->enabled_targets[scsi_id] = NULL;
+}
+#endif
+
+/*
+ * Called when we have an active connection to a target on the bus,
+ * this function finds the nearest syncrate to the input period limited
+ * by the capabilities of the bus connectivity of and sync settings for
+ * the target.
+ */
+struct ahc_syncrate *
+ahc_devlimited_syncrate(struct ahc_softc *ahc,
+			struct ahc_initiator_tinfo *tinfo,
+			u_int *period, u_int *ppr_options, role_t role) {
+	struct	ahc_transinfo *transinfo;
+	u_int	maxsync;
+
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0
+		 && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) {
+			maxsync = AHC_SYNCRATE_DT;
+		} else {
+			maxsync = AHC_SYNCRATE_ULTRA;
+			/* Can't do DT on an SE bus */
+			*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+		}
+	} else if ((ahc->features & AHC_ULTRA) != 0
+		&& (ahc->flags & AHC_ULTRA_DISABLED) == 0) {
+		maxsync = AHC_SYNCRATE_ULTRA;
+	} else {
+		maxsync = AHC_SYNCRATE_FAST;
+	}
+	/*
+	 * Never allow a value higher than our current goal
+	 * period otherwise we may allow a target initiated
+	 * negotiation to go above the limit as set by the
+	 * user.  In the case of an initiator initiated
+	 * sync negotiation, we limit based on the user
+	 * setting.  This allows the system to still accept
+	 * incoming negotiations even if target initiated
+	 * negotiation is not performed.
+	 */
+	if (role == ROLE_TARGET)
+		transinfo = &tinfo->user;
+	else 
+		transinfo = &tinfo->goal;
+	*ppr_options &= transinfo->ppr_options;
+	if (transinfo->period == 0) {
+		*period = 0;
+		*ppr_options = 0;
+		return (NULL);
+	}
+	*period = MAX(*period, transinfo->period);
+	return (ahc_find_syncrate(ahc, period, ppr_options, maxsync));
+}
+
+/*
+ * Look up the valid period to SCSIRATE conversion in our table.
+ * Return the period and offset that should be sent to the target
+ * if this was the beginning of an SDTR.
+ */
+struct ahc_syncrate *
+ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+		  u_int *ppr_options, u_int maxsync)
+{
+	struct ahc_syncrate *syncrate;
+
+	if ((ahc->features & AHC_DT) == 0)
+		*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+	
+	for (syncrate = &ahc_syncrates[maxsync];
+	     syncrate->rate != NULL;
+	     syncrate++) {
+
+		/*
+		 * The Ultra2 table doesn't go as low
+		 * as for the Fast/Ultra cards.
+		 */
+		if ((ahc->features & AHC_ULTRA2) != 0
+		 && (syncrate->sxfr_u2 == 0))
+			break;
+
+		/* Skip any DT entries if DT is not available */
+		if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+		 && (syncrate->sxfr_u2 & DT_SXFR) != 0)
+			continue;
+
+		if (*period <= syncrate->period) {
+			/*
+			 * When responding to a target that requests
+			 * sync, the requested rate may fall between
+			 * two rates that we can output, but still be
+			 * a rate that we can receive.  Because of this,
+			 * we want to respond to the target with
+			 * the same rate that it sent to us even
+			 * if the period we use to send data to it
+			 * is lower.  Only lower the response period
+			 * if we must.
+			 */
+			if (syncrate == &ahc_syncrates[maxsync])
+				*period = syncrate->period;
+
+			/*
+			 * At some speeds, we only support
+			 * ST transfers.
+			 */
+		 	if ((syncrate->sxfr_u2 & ST_SXFR) != 0)
+				*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+			break;
+		}
+	}
+
+	if ((*period == 0)
+	 || (syncrate->rate == NULL)
+	 || ((ahc->features & AHC_ULTRA2) != 0
+	  && (syncrate->sxfr_u2 == 0))) {
+		/* Use asynchronous transfers. */
+		*period = 0;
+		syncrate = NULL;
+		*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+	}
+	return (syncrate);
+}
+
+/*
+ * Convert from an entry in our syncrate table to the SCSI equivalent
+ * sync "period" factor.
+ */
+u_int
+ahc_find_period(struct ahc_softc *ahc, u_int scsirate, u_int maxsync)
+{
+	struct ahc_syncrate *syncrate;
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		scsirate &= SXFR_ULTRA2;
+	else
+		scsirate &= SXFR;
+
+	syncrate = &ahc_syncrates[maxsync];
+	while (syncrate->rate != NULL) {
+
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+			if (syncrate->sxfr_u2 == 0)
+				break;
+			else if (scsirate == (syncrate->sxfr_u2 & SXFR_ULTRA2))
+				return (syncrate->period);
+		} else if (scsirate == (syncrate->sxfr & SXFR)) {
+				return (syncrate->period);
+		}
+		syncrate++;
+	}
+	return (0); /* async */
+}
+
+/*
+ * Truncate the given synchronous offset to a value the
+ * current adapter type and syncrate are capable of.
+ */
+void
+ahc_validate_offset(struct ahc_softc *ahc,
+		    struct ahc_initiator_tinfo *tinfo,
+		    struct ahc_syncrate *syncrate,
+		    u_int *offset, int wide, role_t role)
+{
+	u_int maxoffset;
+
+	/* Limit offset to what we can do */
+	if (syncrate == NULL) {
+		maxoffset = 0;
+	} else if ((ahc->features & AHC_ULTRA2) != 0) {
+		maxoffset = MAX_OFFSET_ULTRA2;
+	} else {
+		if (wide)
+			maxoffset = MAX_OFFSET_16BIT;
+		else
+			maxoffset = MAX_OFFSET_8BIT;
+	}
+	*offset = MIN(*offset, maxoffset);
+	if (tinfo != NULL) {
+		if (role == ROLE_TARGET)
+			*offset = MIN(*offset, tinfo->user.offset);
+		else
+			*offset = MIN(*offset, tinfo->goal.offset);
+	}
+}
+
+/*
+ * Truncate the given transfer width parameter to a value the
+ * current adapter type is capable of.
+ */
+void
+ahc_validate_width(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
+		   u_int *bus_width, role_t role)
+{
+	switch (*bus_width) {
+	default:
+		if (ahc->features & AHC_WIDE) {
+			/* Respond Wide */
+			*bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+			break;
+		}
+		/* FALLTHROUGH */
+	case MSG_EXT_WDTR_BUS_8_BIT:
+		*bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+		break;
+	}
+	if (tinfo != NULL) {
+		if (role == ROLE_TARGET)
+			*bus_width = MIN(tinfo->user.width, *bus_width);
+		else
+			*bus_width = MIN(tinfo->goal.width, *bus_width);
+	}
+}
+
+/*
+ * Update the bitmask of targets for which the controller should
+ * negotiate with at the next convenient oportunity.  This currently
+ * means the next time we send the initial identify messages for
+ * a new transaction.
+ */
+void
+ahc_update_target_msg_request(struct ahc_softc *ahc,
+			      struct ahc_devinfo *devinfo,
+			      struct ahc_initiator_tinfo *tinfo,
+			      int force, int paused)
+{
+	u_int targ_msg_req_orig;
+
+	targ_msg_req_orig = ahc->targ_msg_req;
+	if (tinfo->current.period != tinfo->goal.period
+	 || tinfo->current.width != tinfo->goal.width
+	 || tinfo->current.offset != tinfo->goal.offset
+	 || tinfo->current.ppr_options != tinfo->goal.ppr_options
+	 || (force
+	  && (tinfo->goal.period != 0
+	   || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
+	   || tinfo->goal.ppr_options != 0)))
+		ahc->targ_msg_req |= devinfo->target_mask;
+	else
+		ahc->targ_msg_req &= ~devinfo->target_mask;
+
+	if (ahc->targ_msg_req != targ_msg_req_orig) {
+		/* Update the message request bit for this target */
+		if (!paused)
+			pause_sequencer(ahc);
+
+		ahc_outb(ahc, TARGET_MSG_REQUEST,
+			 ahc->targ_msg_req & 0xFF);
+		ahc_outb(ahc, TARGET_MSG_REQUEST + 1,
+			 (ahc->targ_msg_req >> 8) & 0xFF);
+
+		if (!paused)
+			unpause_sequencer(ahc);
+	}
+}
+
+/*
+ * Update the user/goal/current tables of synchronous negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller.  In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		 struct ahc_syncrate *syncrate, u_int period,
+		 u_int offset, u_int ppr_options, u_int type, int paused)
+{
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	tmode_tstate *tstate;
+	u_int	old_period;
+	u_int	old_offset;
+	u_int	old_ppr;
+	int	active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+
+	if (syncrate == NULL) {
+		period = 0;
+		offset = 0;
+	}
+
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+
+	if ((type & AHC_TRANS_USER) != 0) {
+		tinfo->user.period = period;
+		tinfo->user.offset = offset;
+		tinfo->user.ppr_options = ppr_options;
+	}
+
+	if ((type & AHC_TRANS_GOAL) != 0) {
+		tinfo->goal.period = period;
+		tinfo->goal.offset = offset;
+		tinfo->goal.ppr_options = ppr_options;
+	}
+
+	old_period = tinfo->current.period;
+	old_offset = tinfo->current.offset;
+	old_ppr	   = tinfo->current.ppr_options;
+
+	if ((type & AHC_TRANS_CUR) != 0
+	 && (old_period != period
+	  || old_offset != offset
+	  || old_ppr != ppr_options)) {
+		u_int	scsirate;
+
+		scsirate = tinfo->scsirate;
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+
+			scsirate &= ~(SXFR_ULTRA2|SINGLE_EDGE|ENABLE_CRC);
+			if (syncrate != NULL) {
+				scsirate |= syncrate->sxfr_u2;
+				if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0)
+					scsirate |= ENABLE_CRC;
+				else
+					scsirate |= SINGLE_EDGE;
+			}
+		} else {
+
+			scsirate &= ~(SXFR|SOFS);
+			/*
+			 * Ensure Ultra mode is set properly for
+			 * this target.
+			 */
+			tstate->ultraenb &= ~devinfo->target_mask;
+			if (syncrate != NULL) {
+				if (syncrate->sxfr & ULTRA_SXFR) {
+					tstate->ultraenb |=
+						devinfo->target_mask;
+				}
+				scsirate |= syncrate->sxfr & SXFR;
+				scsirate |= offset & SOFS;
+			}
+			if (active) {
+				u_int sxfrctl0;
+
+				sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+				sxfrctl0 &= ~FAST20;
+				if (tstate->ultraenb & devinfo->target_mask)
+					sxfrctl0 |= FAST20;
+				ahc_outb(ahc, SXFRCTL0, sxfrctl0);
+			}
+		}
+		if (active) {
+			ahc_outb(ahc, SCSIRATE, scsirate);
+			if ((ahc->features & AHC_ULTRA2) != 0)
+				ahc_outb(ahc, SCSIOFFSET, offset);
+		}
+
+		tinfo->scsirate = scsirate;
+		tinfo->current.period = period;
+		tinfo->current.offset = offset;
+		tinfo->current.ppr_options = ppr_options;
+
+		/* Update the syncrates in any pending scbs */
+		ahc_update_pending_syncrates(ahc);
+
+		ahc_send_async(ahc, devinfo->channel, devinfo->target,
+			       CAM_LUN_WILDCARD, AC_TRANSFER_NEG);
+		if (bootverbose) {
+			if (offset != 0) {
+				printf("%s: target %d synchronous at %sMHz%s, "
+				       "offset = 0x%x\n", ahc_name(ahc),
+				       devinfo->target, syncrate->rate,
+				       (ppr_options & MSG_EXT_PPR_DT_REQ)
+				       ? " DT" : "", offset);
+			} else {
+				printf("%s: target %d using "
+				       "asynchronous transfers\n",
+				       ahc_name(ahc), devinfo->target);
+			}
+		}
+	}
+
+	ahc_update_target_msg_request(ahc, devinfo, tinfo,
+				      /*force*/FALSE,
+				      paused);
+}
+
+/*
+ * Update the user/goal/current tables of wide negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller.  In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+	      u_int width, u_int type, int paused)
+{
+	struct ahc_initiator_tinfo *tinfo;
+	struct tmode_tstate *tstate;
+	u_int  oldwidth;
+	int    active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+
+	if ((type & AHC_TRANS_USER) != 0)
+		tinfo->user.width = width;
+
+	if ((type & AHC_TRANS_GOAL) != 0)
+		tinfo->goal.width = width;
+
+	oldwidth = tinfo->current.width;
+	if ((type & AHC_TRANS_CUR) != 0 && oldwidth != width) {
+		u_int	scsirate;
+
+		scsirate =  tinfo->scsirate;
+		scsirate &= ~WIDEXFER;
+		if (width == MSG_EXT_WDTR_BUS_16_BIT)
+			scsirate |= WIDEXFER;
+
+		tinfo->scsirate = scsirate;
+
+		if (active)
+			ahc_outb(ahc, SCSIRATE, scsirate);
+
+		tinfo->current.width = width;
+
+		ahc_send_async(ahc, devinfo->channel, devinfo->target,
+			       CAM_LUN_WILDCARD, AC_TRANSFER_NEG);
+		if (bootverbose) {
+			printf("%s: target %d using %dbit transfers\n",
+			       ahc_name(ahc), devinfo->target,
+			       8 * (0x01 << width));
+		}
+	}
+
+	ahc_update_target_msg_request(ahc, devinfo, tinfo,
+				      /*force*/FALSE, paused);
+}
+
+/*
+ * Update the current state of tagged queuing for a given target.
+ */
+void
+ahc_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, int enable)
+{
+	struct ahc_initiator_tinfo *tinfo;
+	struct tmode_tstate *tstate;
+	uint16_t orig_tagenable;
+
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+
+	orig_tagenable = tstate->tagenable;
+	if (enable)
+		tstate->tagenable |= devinfo->target_mask;
+	else
+		tstate->tagenable &= ~devinfo->target_mask;
+
+	if (orig_tagenable != tstate->tagenable) {
+		ahc_platform_set_tags(ahc, devinfo, enable);
+		ahc_send_async(ahc, devinfo->channel, devinfo->target,
+			       devinfo->lun, AC_TRANSFER_NEG);
+	}
+
+}
+
+/*
+ * When the transfer settings for a connection change, update any
+ * in-transit SCBs to contain the new data so the hardware will
+ * be set correctly during future (re)selections.
+ */
+static void
+ahc_update_pending_syncrates(struct ahc_softc *ahc)
+{
+	struct	scb *pending_scb;
+	int	pending_scb_count;
+	int	i;
+	u_int	saved_scbptr;
+
+	/*
+	 * Traverse the pending SCB list and ensure that all of the
+	 * SCBs there have the proper settings.
+	 */
+	pending_scb_count = 0;
+	LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+		struct ahc_devinfo devinfo;
+		struct hardware_scb *pending_hscb;
+		struct ahc_initiator_tinfo *tinfo;
+		struct tmode_tstate *tstate;
+		
+		ahc_scb_devinfo(ahc, &devinfo, pending_scb);
+		tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
+					    devinfo.our_scsiid,
+					    devinfo.target, &tstate);
+		pending_hscb = pending_scb->hscb;
+		pending_hscb->control &= ~ULTRAENB;
+		if ((tstate->ultraenb & devinfo.target_mask) != 0)
+			pending_hscb->control |= ULTRAENB;
+		pending_hscb->scsirate = tinfo->scsirate;
+		pending_hscb->scsioffset = tinfo->current.offset;
+		pending_scb_count++;
+	}
+
+	if (pending_scb_count == 0)
+		return;
+
+	saved_scbptr = ahc_inb(ahc, SCBPTR);
+	/* Ensure that the hscbs down on the card match the new information */
+	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+		struct	hardware_scb *pending_hscb;
+		u_int	control;
+		u_int	scb_tag;
+
+		ahc_outb(ahc, SCBPTR, i);
+		scb_tag = ahc_inb(ahc, SCB_TAG);
+		pending_scb = ahc_lookup_scb(ahc, scb_tag);
+		if (pending_scb == NULL)
+			continue;
+
+		pending_hscb = pending_scb->hscb;
+		control = ahc_inb(ahc, SCB_CONTROL);
+		control &= ~ULTRAENB;
+		if ((pending_hscb->control & ULTRAENB) != 0)
+			control |= ULTRAENB;
+		ahc_outb(ahc, SCB_CONTROL, control);
+		ahc_outb(ahc, SCB_SCSIRATE, pending_hscb->scsirate);
+		ahc_outb(ahc, SCB_SCSIOFFSET, pending_hscb->scsioffset);
+	}
+	ahc_outb(ahc, SCBPTR, saved_scbptr);
+}
+
+/**************************** Pathing Information *****************************/
+static void
+ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	u_int	saved_scsiid;
+	role_t	role;
+	int	our_id;
+
+	if (ahc_inb(ahc, SSTAT0) & TARGET)
+		role = ROLE_TARGET;
+	else
+		role = ROLE_INITIATOR;
+
+	if (role == ROLE_TARGET
+	 && (ahc->features & AHC_MULTI_TID) != 0
+	 && (ahc_inb(ahc, SEQ_FLAGS) & CMDPHASE_PENDING) != 0) {
+		/* We were selected, so pull our id from TARGIDIN */
+		our_id = ahc_inb(ahc, TARGIDIN) & OID;
+	} else if ((ahc->features & AHC_ULTRA2) != 0)
+		our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+	else
+		our_id = ahc_inb(ahc, SCSIID) & OID;
+
+	saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+	ahc_compile_devinfo(devinfo,
+			    our_id,
+			    SCSIID_TARGET(ahc, saved_scsiid),
+			    ahc_inb(ahc, SAVED_LUN),
+			    SCSIID_CHANNEL(ahc, saved_scsiid),
+			    role);
+}
+
+void
+ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target,
+		    u_int lun, char channel, role_t role)
+{
+	devinfo->our_scsiid = our_id;
+	devinfo->target = target;
+	devinfo->lun = lun;
+	devinfo->target_offset = target;
+	devinfo->channel = channel;
+	devinfo->role = role;
+	if (channel == 'B')
+		devinfo->target_offset += 8;
+	devinfo->target_mask = (0x01 << devinfo->target_offset);
+}
+
+static void
+ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		struct scb *scb)
+{
+	role_t	role;
+	int	our_id;
+
+	our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
+	role = ROLE_INITIATOR;
+	if ((scb->hscb->control & TARGET_SCB) != 0)
+		role = ROLE_TARGET;
+	ahc_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahc, scb),
+			    SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahc, scb), role);
+}
+
+
+/************************ Message Phase Processing ****************************/
+/*
+ * When an initiator transaction with the MK_MESSAGE flag either reconnects
+ * or enters the initial message out phase, we are interrupted.  Fill our
+ * outgoing message buffer with the appropriate message and beging handing
+ * the message phase(s) manually.
+ */
+static void
+ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+			   struct scb *scb)
+{
+	/*
+	 * To facilitate adding multiple messages together,
+	 * each routine should increment the index and len
+	 * variables instead of setting them explicitly.
+	 */
+	ahc->msgout_index = 0;
+	ahc->msgout_len = 0;
+
+	if ((scb->flags & SCB_DEVICE_RESET) == 0
+	 && ahc_inb(ahc, MSG_OUT) == MSG_IDENTIFYFLAG) {
+		u_int identify_msg;
+
+		identify_msg = MSG_IDENTIFYFLAG | SCB_GET_LUN(scb);
+		if ((scb->hscb->control & DISCENB) != 0)
+			identify_msg |= MSG_IDENTIFY_DISCFLAG;
+		ahc->msgout_buf[ahc->msgout_index++] = identify_msg;
+		ahc->msgout_len++;
+
+		if ((scb->hscb->control & TAG_ENB) != 0) {
+			ahc->msgout_buf[ahc->msgout_index++] =
+			    scb->hscb->control & (TAG_ENB|SCB_TAG_TYPE);
+			ahc->msgout_buf[ahc->msgout_index++] = scb->hscb->tag;
+			ahc->msgout_len += 2;
+		}
+	}
+
+	if (scb->flags & SCB_DEVICE_RESET) {
+		ahc->msgout_buf[ahc->msgout_index++] = MSG_BUS_DEV_RESET;
+		ahc->msgout_len++;
+		ahc_print_path(ahc, scb);
+		printf("Bus Device Reset Message Sent\n");
+		/*
+		 * Clear our selection hardware in advance of
+		 * the busfree.  We may have an entry in the waiting
+		 * Q for this target, and we don't want to go about
+		 * selecting while we handle the busfree and blow it
+		 * away.
+		 */
+		ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+	} else if ((scb->flags & SCB_ABORT) != 0) {
+		if ((scb->hscb->control & TAG_ENB) != 0)
+			ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT_TAG;
+		else
+			ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT;
+		ahc->msgout_len++;
+		ahc_print_path(ahc, scb);
+		printf("Abort%s Message Sent\n",
+		       (scb->hscb->control & TAG_ENB) != 0 ? " Tag" : "");
+		/*
+		 * Clear our selection hardware in advance of
+		 * the busfree.  We may have an entry in the waiting
+		 * Q for this target, and we don't want to go about
+		 * selecting while we handle the busfree and blow it
+		 * away.
+		 */
+		ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+	} else if ((ahc->targ_msg_req & devinfo->target_mask) != 0
+		|| (scb->flags & SCB_NEGOTIATE) != 0) {
+		ahc_build_transfer_msg(ahc, devinfo);
+	} else {
+		printf("ahc_intr: AWAITING_MSG for an SCB that "
+		       "does not have a waiting message\n");
+		printf("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid,
+		       devinfo->target_mask);
+		panic("SCB = %d, SCB Control = %x, MSG_OUT = %x "
+		      "SCB flags = %x", scb->hscb->tag, scb->hscb->control,
+		      ahc_inb(ahc, MSG_OUT), scb->flags);
+	}
+
+	/*
+	 * Clear the MK_MESSAGE flag from the SCB so we aren't
+	 * asked to send this message again.
+	 */
+	ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) & ~MK_MESSAGE);
+	ahc->msgout_index = 0;
+	ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+}
+
+/*
+ * Build an appropriate transfer negotiation message for the
+ * currently active target.
+ */
+static void
+ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	/*
+	 * We need to initiate transfer negotiations.
+	 * If our current and goal settings are identical,
+	 * we want to renegotiate due to a check condition.
+	 */
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	tmode_tstate *tstate;
+	struct	ahc_syncrate *rate;
+	int	dowide;
+	int	dosync;
+	int	doppr;
+	int	use_ppr;
+	u_int	period;
+	u_int	ppr_options;
+	u_int	offset;
+
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+	dowide = tinfo->current.width != tinfo->goal.width;
+	dosync = tinfo->current.period != tinfo->goal.period;
+	doppr = tinfo->current.ppr_options != tinfo->goal.ppr_options;
+
+	if (!dowide && !dosync && !doppr) {
+		dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
+		dosync = tinfo->goal.period != 0;
+		doppr = tinfo->goal.ppr_options != 0;
+	}
+
+	if (!dowide && !dosync && !doppr) {
+		panic("ahc_intr: AWAITING_MSG for negotiation, "
+		      "but no negotiation needed\n");	
+	}
+
+	use_ppr = (tinfo->current.transport_version >= 3) || doppr;
+	/* Target initiated PPR is not allowed in the SCSI spec */
+	if (devinfo->role == ROLE_TARGET)
+		use_ppr = 0;
+
+	/*
+	 * Both the PPR message and SDTR message require the
+	 * goal syncrate to be limited to what the target device
+	 * is capable of handling (based on whether an LVD->SE
+	 * expander is on the bus), so combine these two cases.
+	 * Regardless, guarantee that if we are using WDTR and SDTR
+	 * messages that WDTR comes first.
+	 */
+	if (use_ppr || (dosync && !dowide)) {
+
+		period = tinfo->goal.period;
+		ppr_options = tinfo->goal.ppr_options;
+		if (use_ppr == 0)
+			ppr_options = 0;
+		rate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+					       &ppr_options, devinfo->role);
+		offset = tinfo->goal.offset;
+		ahc_validate_offset(ahc, tinfo, rate, &offset,
+				    use_ppr ? tinfo->goal.width
+					    : tinfo->current.width,
+				    devinfo->role);
+		if (use_ppr) {
+			ahc_construct_ppr(ahc, devinfo, period, offset,
+					  tinfo->goal.width, ppr_options);
+		} else {
+			ahc_construct_sdtr(ahc, devinfo, period, offset);
+		}
+	} else {
+		ahc_construct_wdtr(ahc, devinfo, tinfo->goal.width);
+	}
+}
+
+/*
+ * Build a synchronous negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_sdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		   u_int period, u_int offset)
+{
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR_LEN;
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR;
+	ahc->msgout_buf[ahc->msgout_index++] = period;
+	ahc->msgout_buf[ahc->msgout_index++] = offset;
+	ahc->msgout_len += 5;
+	if (bootverbose) {
+		printf("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n",
+		       ahc_name(ahc), devinfo->channel, devinfo->target,
+		       devinfo->lun, period, offset);
+	}
+}
+
+/*
+ * Build a wide negotiateion message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_wdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		   u_int bus_width)
+{
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR_LEN;
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR;
+	ahc->msgout_buf[ahc->msgout_index++] = bus_width;
+	ahc->msgout_len += 4;
+	if (bootverbose) {
+		printf("(%s:%c:%d:%d): Sending WDTR %x\n",
+		       ahc_name(ahc), devinfo->channel, devinfo->target,
+		       devinfo->lun, bus_width);
+	}
+}
+
+/*
+ * Build a parallel protocol request message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_ppr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		  u_int period, u_int offset, u_int bus_width,
+		  u_int ppr_options)
+{
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR_LEN;
+	ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR;
+	ahc->msgout_buf[ahc->msgout_index++] = period;
+	ahc->msgout_buf[ahc->msgout_index++] = 0;
+	ahc->msgout_buf[ahc->msgout_index++] = offset;
+	ahc->msgout_buf[ahc->msgout_index++] = bus_width;
+	ahc->msgout_buf[ahc->msgout_index++] = ppr_options;
+	ahc->msgout_len += 8;
+	if (bootverbose) {
+		printf("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, "
+		       "offset %x, ppr_options %x\n", ahc_name(ahc),
+		       devinfo->channel, devinfo->target, devinfo->lun,
+		       bus_width, period, offset, ppr_options);
+	}
+}
+
+/*
+ * Clear any active message state.
+ */
+static void
+ahc_clear_msg_state(struct ahc_softc *ahc)
+{
+	ahc->msgout_len = 0;
+	ahc->msgin_index = 0;
+	ahc->msg_type = MSG_TYPE_NONE;
+	if ((ahc_inb(ahc, SCSISIGI) & ATNI) == 0) {
+		/*
+		 * The target didn't care to respond to our
+		 * message request, so clear ATN.
+		 */
+		ahc_outb(ahc, CLRSINT1, CLRATNO);
+	}
+	ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+}
+
+/*
+ * Manual message loop handler.
+ */
+static void
+ahc_handle_message_phase(struct ahc_softc *ahc)
+{ 
+	struct	ahc_devinfo devinfo;
+	u_int	bus_phase;
+	int	end_session;
+
+	ahc_fetch_devinfo(ahc, &devinfo);
+	end_session = FALSE;
+	bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+
+reswitch:
+	switch (ahc->msg_type) {
+	case MSG_TYPE_INITIATOR_MSGOUT:
+	{
+		int lastbyte;
+		int phasemis;
+		int msgdone;
+
+		if (ahc->msgout_len == 0)
+			panic("HOST_MSG_LOOP interrupt with no active message");
+
+		phasemis = bus_phase != P_MESGOUT;
+		if (phasemis) {
+			if (bus_phase == P_MESGIN) {
+				/*
+				 * Change gears and see if
+				 * this messages is of interest to
+				 * us or should be passed back to
+				 * the sequencer.
+				 */
+				ahc_outb(ahc, CLRSINT1, CLRATNO);
+				ahc->send_msg_perror = FALSE;
+				ahc->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+				ahc->msgin_index = 0;
+				goto reswitch;
+			}
+			end_session = TRUE;
+			break;
+		}
+
+		if (ahc->send_msg_perror) {
+			ahc_outb(ahc, CLRSINT1, CLRATNO);
+			ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+			ahc_outb(ahc, SCSIDATL, MSG_PARITY_ERROR);
+			break;
+		}
+
+		msgdone	= ahc->msgout_index == ahc->msgout_len;
+		if (msgdone) {
+			/*
+			 * The target has requested a retry.
+			 * Re-assert ATN, reset our message index to
+			 * 0, and try again.
+			 */
+			ahc->msgout_index = 0;
+			ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, SCSISIGO) | ATNO);
+		}
+
+		lastbyte = ahc->msgout_index == (ahc->msgout_len - 1);
+		if (lastbyte) {
+			/* Last byte is signified by dropping ATN */
+			ahc_outb(ahc, CLRSINT1, CLRATNO);
+		}
+
+		/*
+		 * Clear our interrupt status and present
+		 * the next byte on the bus.
+		 */
+		ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+		ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+		break;
+	}
+	case MSG_TYPE_INITIATOR_MSGIN:
+	{
+		int phasemis;
+		int message_done;
+
+		phasemis = bus_phase != P_MESGIN;
+
+		if (phasemis) {
+			ahc->msgin_index = 0;
+			if (bus_phase == P_MESGOUT
+			 && (ahc->send_msg_perror == TRUE
+			  || (ahc->msgout_len != 0
+			   && ahc->msgout_index == 0))) {
+				ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+				goto reswitch;
+			}
+			end_session = TRUE;
+			break;
+		}
+
+		/* Pull the byte in without acking it */
+		ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIBUSL);
+
+		message_done = ahc_parse_msg(ahc, &devinfo);
+
+		if (message_done) {
+			/*
+			 * Clear our incoming message buffer in case there
+			 * is another message following this one.
+			 */
+			ahc->msgin_index = 0;
+
+			/*
+			 * If this message illicited a response,
+			 * assert ATN so the target takes us to the
+			 * message out phase.
+			 */
+			if (ahc->msgout_len != 0)
+				ahc_outb(ahc, SCSISIGO,
+					 ahc_inb(ahc, SCSISIGO) | ATNO);
+		} else 
+			ahc->msgin_index++;
+
+		/* Ack the byte */
+		ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+		ahc_inb(ahc, SCSIDATL);
+		break;
+	}
+	case MSG_TYPE_TARGET_MSGIN:
+	{
+		int msgdone;
+		int msgout_request;
+
+		if (ahc->msgout_len == 0)
+			panic("Target MSGIN with no active message");
+
+		/*
+		 * If we interrupted a mesgout session, the initiator
+		 * will not know this until our first REQ.  So, we
+		 * only honor mesgout requests after we've sent our
+		 * first byte.
+		 */
+		if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0
+		 && ahc->msgout_index > 0)
+			msgout_request = TRUE;
+		else
+			msgout_request = FALSE;
+
+		if (msgout_request) {
+
+			/*
+			 * Change gears and see if
+			 * this messages is of interest to
+			 * us or should be passed back to
+			 * the sequencer.
+			 */
+			ahc->msg_type = MSG_TYPE_TARGET_MSGOUT;
+			ahc_outb(ahc, SCSISIGO, P_MESGOUT | BSYO);
+			ahc->msgin_index = 0;
+			/* Dummy read to REQ for first byte */
+			ahc_inb(ahc, SCSIDATL);
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+			break;
+		}
+
+		msgdone = ahc->msgout_index == ahc->msgout_len;
+		if (msgdone) {
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+			end_session = TRUE;
+			break;
+		}
+
+		/*
+		 * Present the next byte on the bus.
+		 */
+		ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+		ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+		break;
+	}
+	case MSG_TYPE_TARGET_MSGOUT:
+	{
+		int lastbyte;
+		int msgdone;
+
+		/*
+		 * The initiator signals that this is
+		 * the last byte by dropping ATN.
+		 */
+		lastbyte = (ahc_inb(ahc, SCSISIGI) & ATNI) == 0;
+
+		/*
+		 * Read the latched byte, but turn off SPIOEN first
+		 * so that we don't inadvertantly cause a REQ for the
+		 * next byte.
+		 */
+		ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+		ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIDATL);
+		msgdone = ahc_parse_msg(ahc, &devinfo);
+		if (msgdone == MSGLOOP_TERMINATED) {
+			/*
+			 * The message is *really* done in that it caused
+			 * us to go to bus free.  The sequencer has already
+			 * been reset at this point, so pull the ejection
+			 * handle.
+			 */
+			return;
+		}
+		
+		ahc->msgin_index++;
+
+		/*
+		 * XXX Read spec about initiator dropping ATN too soon
+		 *     and use msgdone to detect it.
+		 */
+		if (msgdone == MSGLOOP_MSGCOMPLETE) {
+			ahc->msgin_index = 0;
+
+			/*
+			 * If this message illicited a response, transition
+			 * to the Message in phase and send it.
+			 */
+			if (ahc->msgout_len != 0) {
+				ahc_outb(ahc, SCSISIGO, P_MESGIN | BSYO);
+				ahc_outb(ahc, SXFRCTL0,
+					 ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+				ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+				ahc->msgin_index = 0;
+				break;
+			}
+		}
+
+		if (lastbyte)
+			end_session = TRUE;
+		else {
+			/* Ask for the next byte. */
+			ahc_outb(ahc, SXFRCTL0,
+				 ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+		}
+
+		break;
+	}
+	default:
+		panic("Unknown REQINIT message type");
+	}
+
+	if (end_session) {
+		ahc_clear_msg_state(ahc);
+		ahc_outb(ahc, RETURN_1, EXIT_MSG_LOOP);
+	} else
+		ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP);
+}
+
+/*
+ * See if we sent a particular extended message to the target.
+ * If "full" is true, return true only if the target saw the full
+ * message.  If "full" is false, return true if the target saw at
+ * least the first byte of the message.
+ */
+static int
+ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type, u_int msgval, int full)
+{
+	int found;
+	u_int index;
+
+	found = FALSE;
+	index = 0;
+
+	while (index < ahc->msgout_len) {
+		if (ahc->msgout_buf[index] == MSG_EXTENDED) {
+			u_int end_index;
+
+			end_index = index + 1 + ahc->msgout_buf[index + 1];
+			if (ahc->msgout_buf[index+2] == msgval
+			 && type == AHCMSG_EXT) {
+
+				if (full) {
+					if (ahc->msgout_index > end_index)
+						found = TRUE;
+				} else if (ahc->msgout_index > index)
+					found = TRUE;
+			}
+			index = end_index;
+		} else if (ahc->msgout_buf[index] >= MSG_SIMPLE_Q_TAG
+			&& ahc->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+
+			/* Skip tag type and tag id or residue param*/
+			index += 2;
+		} else {
+			/* Single byte message */
+			if (type == AHCMSG_1B
+			 && ahc->msgout_buf[index] == msgval
+			 && ahc->msgout_index > index)
+				found = TRUE;
+			index++;
+		}
+
+		if (found)
+			break;
+	}
+	return (found);
+}
+
+/*
+ * Wait for a complete incomming message, parse it, and respond accordingly.
+ */
+static int
+ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	tmode_tstate *tstate;
+	int	reject;
+	int	done;
+	int	response;
+	u_int	targ_scsirate;
+
+	done = MSGLOOP_IN_PROG;
+	response = FALSE;
+	reject = FALSE;
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+	targ_scsirate = tinfo->scsirate;
+
+	/*
+	 * Parse as much of the message as is availible,
+	 * rejecting it if we don't support it.  When
+	 * the entire message is availible and has been
+	 * handled, return MSGLOOP_MSGCOMPLETE, indicating
+	 * that we have parsed an entire message.
+	 *
+	 * In the case of extended messages, we accept the length
+	 * byte outright and perform more checking once we know the
+	 * extended message type.
+	 */
+	switch (ahc->msgin_buf[0]) {
+	case MSG_MESSAGE_REJECT:
+		response = ahc_handle_msg_reject(ahc, devinfo);
+		/* FALLTHROUGH */
+	case MSG_NOOP:
+		done = MSGLOOP_MSGCOMPLETE;
+		break;
+	case MSG_EXTENDED:
+	{
+		/* Wait for enough of the message to begin validation */
+		if (ahc->msgin_index < 2)
+			break;
+		switch (ahc->msgin_buf[2]) {
+		case MSG_EXT_SDTR:
+		{
+			struct	 ahc_syncrate *syncrate;
+			u_int	 period;
+			u_int	 ppr_options;
+			u_int	 offset;
+			u_int	 saved_offset;
+			
+			if (ahc->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
+				reject = TRUE;
+				break;
+			}
+
+			/*
+			 * Wait until we have both args before validating
+			 * and acting on this message.
+			 *
+			 * Add one to MSG_EXT_SDTR_LEN to account for
+			 * the extended message preamble.
+			 */
+			if (ahc->msgin_index < (MSG_EXT_SDTR_LEN + 1))
+				break;
+
+			period = ahc->msgin_buf[3];
+			ppr_options = 0;
+			saved_offset = offset = ahc->msgin_buf[4];
+			syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+							   &ppr_options,
+							   devinfo->role);
+			ahc_validate_offset(ahc, tinfo, syncrate, &offset,
+					    targ_scsirate & WIDEXFER,
+					    devinfo->role);
+			if (bootverbose) {
+				printf("(%s:%c:%d:%d): Received "
+				       "SDTR period %x, offset %x\n\t"
+				       "Filtered to period %x, offset %x\n",
+				       ahc_name(ahc), devinfo->channel,
+				       devinfo->target, devinfo->lun,
+				       ahc->msgin_buf[3], saved_offset,
+				       period, offset);
+			}
+			ahc_set_syncrate(ahc, devinfo, 
+					 syncrate, period,
+					 offset, ppr_options,
+					 AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+					 /*paused*/TRUE);
+
+			/*
+			 * See if we initiated Sync Negotiation
+			 * and didn't have to fall down to async
+			 * transfers.
+			 */
+			if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, TRUE)) {
+				/* We started it */
+				if (saved_offset != offset) {
+					/* Went too low - force async */
+					reject = TRUE;
+				}
+			} else {
+				/*
+				 * Send our own SDTR in reply
+				 */
+				if (bootverbose) {
+					printf("(%s:%c:%d:%d): Target "
+					       "Initiated SDTR\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				}
+				ahc->msgout_index = 0;
+				ahc->msgout_len = 0;
+				ahc_construct_sdtr(ahc, devinfo,
+						   period, offset);
+				ahc->msgout_index = 0;
+				response = TRUE;
+			}
+			done = MSGLOOP_MSGCOMPLETE;
+			break;
+		}
+		case MSG_EXT_WDTR:
+		{
+			u_int bus_width;
+			u_int saved_width;
+			u_int sending_reply;
+
+			sending_reply = FALSE;
+			if (ahc->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
+				reject = TRUE;
+				break;
+			}
+
+			/*
+			 * Wait until we have our arg before validating
+			 * and acting on this message.
+			 *
+			 * Add one to MSG_EXT_WDTR_LEN to account for
+			 * the extended message preamble.
+			 */
+			if (ahc->msgin_index < (MSG_EXT_WDTR_LEN + 1))
+				break;
+
+			bus_width = ahc->msgin_buf[3];
+			saved_width = bus_width;
+			ahc_validate_width(ahc, tinfo, &bus_width,
+					   devinfo->role);
+			if (bootverbose) {
+				printf("(%s:%c:%d:%d): Received WDTR "
+				       "%x filtered to %x\n",
+				       ahc_name(ahc), devinfo->channel,
+				       devinfo->target, devinfo->lun,
+				       saved_width, bus_width);
+			}
+
+			if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, TRUE)) {
+				/*
+				 * Don't send a WDTR back to the
+				 * target, since we asked first.
+				 * If the width went higher than our
+				 * request, reject it.
+				 */
+				if (saved_width > bus_width) {
+					reject = TRUE;
+					printf("(%s:%c:%d:%d): requested %dBit "
+					       "transfers.  Rejecting...\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun,
+					       8 * (0x01 << bus_width));
+					bus_width = 0;
+				}
+			} else {
+				/*
+				 * Send our own WDTR in reply
+				 */
+				if (bootverbose) {
+					printf("(%s:%c:%d:%d): Target "
+					       "Initiated WDTR\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				}
+				ahc->msgout_index = 0;
+				ahc->msgout_len = 0;
+				ahc_construct_wdtr(ahc, devinfo, bus_width);
+				ahc->msgout_index = 0;
+				response = TRUE;
+				sending_reply = TRUE;
+			}
+			ahc_set_width(ahc, devinfo, bus_width,
+				      AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+				      /*paused*/TRUE);
+			/* After a wide message, we are async */
+			ahc_set_syncrate(ahc, devinfo,
+					 /*syncrate*/NULL, /*period*/0,
+					 /*offset*/0, /*ppr_options*/0,
+					 AHC_TRANS_ACTIVE, /*paused*/TRUE);
+			if (sending_reply == FALSE && reject == FALSE) {
+
+				if (tinfo->goal.period) {
+					ahc->msgout_index = 0;
+					ahc->msgout_len = 0;
+					ahc_build_transfer_msg(ahc, devinfo);
+					ahc->msgout_index = 0;
+					response = TRUE;
+				}
+			}
+			done = MSGLOOP_MSGCOMPLETE;
+			break;
+		}
+		case MSG_EXT_PPR:
+		{
+			struct	ahc_syncrate *syncrate;
+			u_int	period;
+			u_int	offset;
+			u_int	bus_width;
+			u_int	ppr_options;
+			u_int	saved_width;
+			u_int	saved_offset;
+			u_int	saved_ppr_options;
+
+			if (ahc->msgin_buf[1] != MSG_EXT_PPR_LEN) {
+				reject = TRUE;
+				break;
+			}
+
+			/*
+			 * Wait until we have all args before validating
+			 * and acting on this message.
+			 *
+			 * Add one to MSG_EXT_PPR_LEN to account for
+			 * the extended message preamble.
+			 */
+			if (ahc->msgin_index < (MSG_EXT_PPR_LEN + 1))
+				break;
+
+			period = ahc->msgin_buf[3];
+			offset = ahc->msgin_buf[5];
+			bus_width = ahc->msgin_buf[6];
+			saved_width = bus_width;
+			ppr_options = ahc->msgin_buf[7];
+			/*
+			 * According to the spec, a DT only
+			 * period factor with no DT option
+			 * set implies async.
+			 */
+			if ((ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+			 && period == 9)
+				offset = 0;
+			saved_ppr_options = ppr_options;
+			saved_offset = offset;
+
+			/*
+			 * Mask out any options we don't support
+			 * on any controller.  Transfer options are
+			 * only available if we are negotiating wide.
+			 */
+			ppr_options &= MSG_EXT_PPR_DT_REQ;
+			if (bus_width == 0)
+				ppr_options = 0;
+
+			ahc_validate_width(ahc, tinfo, &bus_width,
+					   devinfo->role);
+			syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+							   &ppr_options,
+							   devinfo->role);
+			ahc_validate_offset(ahc, tinfo, syncrate,
+					    &offset, bus_width,
+					    devinfo->role);
+
+			if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, TRUE)) {
+				/*
+				 * If we are unable to do any of the
+				 * requested options (we went too low),
+				 * then we'll have to reject the message.
+				 */
+				if (saved_width > bus_width
+				 || saved_offset != offset
+				 || saved_ppr_options != ppr_options) {
+					reject = TRUE;
+					period = 0;
+					offset = 0;
+					bus_width = 0;
+					ppr_options = 0;
+					syncrate = NULL;
+				}
+			} else {
+				if (devinfo->role != ROLE_TARGET)
+					printf("(%s:%c:%d:%d): Target "
+					       "Initiated PPR\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				else
+					printf("(%s:%c:%d:%d): Initiator "
+					       "Initiated PPR\n",
+					       ahc_name(ahc), devinfo->channel,
+					       devinfo->target, devinfo->lun);
+				ahc->msgout_index = 0;
+				ahc->msgout_len = 0;
+				ahc_construct_ppr(ahc, devinfo, period, offset,
+						  bus_width, ppr_options);
+				ahc->msgout_index = 0;
+				response = TRUE;
+			}
+			if (bootverbose) {
+				printf("(%s:%c:%d:%d): Received PPR width %x, "
+				       "period %x, offset %x,options %x\n"
+				       "\tFiltered to width %x, period %x, "
+				       "offset %x, options %x\n",
+				       ahc_name(ahc), devinfo->channel,
+				       devinfo->target, devinfo->lun,
+				       ahc->msgin_buf[3], saved_width,
+				       saved_offset, saved_ppr_options,
+				       bus_width, period, offset, ppr_options);
+			}
+			ahc_set_width(ahc, devinfo, bus_width,
+				      AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+				      /*paused*/TRUE);
+			ahc_set_syncrate(ahc, devinfo,
+					 syncrate, period,
+					 offset, ppr_options,
+					 AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+					 /*paused*/TRUE);
+			done = MSGLOOP_MSGCOMPLETE;
+			break;
+		}
+		default:
+			/* Unknown extended message.  Reject it. */
+			reject = TRUE;
+			break;
+		}
+		break;
+	}
+	case MSG_BUS_DEV_RESET:
+		ahc_handle_devreset(ahc, devinfo,
+				    CAM_BDR_SENT,
+				    "Bus Device Reset Received",
+				    /*verbose_level*/0);
+		restart_sequencer(ahc);
+		done = MSGLOOP_TERMINATED;
+		break;
+	case MSG_ABORT_TAG:
+	case MSG_ABORT:
+	case MSG_CLEAR_QUEUE:
+#ifdef AHC_TARGET_MODE
+		/* Target mode messages */
+		if (devinfo->role != ROLE_TARGET) {
+			reject = TRUE;
+			break;
+		}
+		ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+			       devinfo->lun,
+			       ahc->msgin_buf[0] == MSG_ABORT_TAG
+						  ? SCB_LIST_NULL
+						  : ahc_inb(ahc, INITIATOR_TAG),
+			       ROLE_TARGET, CAM_REQ_ABORTED);
+
+		tstate = ahc->enabled_targets[devinfo->our_scsiid];
+		if (tstate != NULL) {
+			struct tmode_lstate* lstate;
+
+			lstate = tstate->enabled_luns[devinfo->lun];
+			if (lstate != NULL) {
+				ahc_queue_lstate_event(ahc, lstate,
+						       devinfo->our_scsiid,
+						       ahc->msgin_buf[0],
+						       /*arg*/0);
+				ahc_send_lstate_events(ahc, lstate);
+			}
+		}
+		done = MSGLOOP_MSGCOMPLETE;
+		break;
+#endif
+	case MSG_TERM_IO_PROC:
+	default:
+		reject = TRUE;
+		break;
+	}
+
+	if (reject) {
+		/*
+		 * Setup to reject the message.
+		 */
+		ahc->msgout_index = 0;
+		ahc->msgout_len = 1;
+		ahc->msgout_buf[0] = MSG_MESSAGE_REJECT;
+		done = MSGLOOP_MSGCOMPLETE;
+		response = TRUE;
+	}
+
+	if (done != MSGLOOP_IN_PROG && !response)
+		/* Clear the outgoing message buffer */
+		ahc->msgout_len = 0;
+
+	return (done);
+}
+
+/*
+ * Process a message reject message.
+ */
+static int
+ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	/*
+	 * 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.
+	 */
+	struct scb *scb;
+	struct ahc_initiator_tinfo *tinfo;
+	struct tmode_tstate *tstate;
+	u_int scb_index;
+	u_int last_msg;
+	int   response = 0;
+
+	scb_index = ahc_inb(ahc, SCB_TAG);
+	scb = ahc_lookup_scb(ahc, scb_index);
+	tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
+				    devinfo->our_scsiid,
+				    devinfo->target, &tstate);
+	/* Might be necessary */
+	last_msg = ahc_inb(ahc, LAST_MSG);
+
+	if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
+		/*
+		 * Target does not support the PPR message.
+		 * Attempt to negotiate SPI-2 style.
+		 */
+		if (bootverbose) {
+			printf("(%s:%c:%d:%d): PPR Rejected. "
+			       "Trying WDTR/SDTR\n",
+			       ahc_name(ahc), devinfo->channel,
+			       devinfo->target, devinfo->lun);
+		}
+		tinfo->goal.ppr_options = 0;
+		tinfo->current.transport_version = 2;
+		tinfo->goal.transport_version = 2;
+		ahc->msgout_index = 0;
+		ahc->msgout_len = 0;
+		ahc_build_transfer_msg(ahc, devinfo);
+		ahc->msgout_index = 0;
+		response = 1;
+	} else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+
+		/* note 8bit xfers */
+		printf("(%s:%c:%d:%d): refuses WIDE negotiation.  Using "
+		       "8bit transfers\n", ahc_name(ahc),
+		       devinfo->channel, devinfo->target, devinfo->lun);
+		ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+			      AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+			      /*paused*/TRUE);
+		/*
+		 * No need to clear the sync rate.  If the target
+		 * did not accept the command, our syncrate is
+		 * unaffected.  If the target started the negotiation,
+		 * but rejected our response, we already cleared the
+		 * sync rate before sending our WDTR.
+		 */
+		if (tinfo->goal.period) {
+
+			/* Start the sync negotiation */
+			ahc->msgout_index = 0;
+			ahc->msgout_len = 0;
+			ahc_build_transfer_msg(ahc, devinfo);
+			ahc->msgout_index = 0;
+			response = 1;
+		}
+	} else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+		/* note asynch xfers and clear flag */
+		ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL, /*period*/0,
+				 /*offset*/0, /*ppr_options*/0,
+				 AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+				 /*paused*/TRUE);
+		printf("(%s:%c:%d:%d): refuses synchronous negotiation. "
+		       "Using asynchronous transfers\n",
+		       ahc_name(ahc), devinfo->channel,
+		       devinfo->target, devinfo->lun);
+	} else if ((scb->hscb->control & MSG_SIMPLE_Q_TAG) != 0) {
+
+		printf("(%s:%c:%d:%d): refuses tagged commands.  Performing "
+		       "non-tagged I/O\n", ahc_name(ahc),
+		       devinfo->channel, devinfo->target, devinfo->lun);
+		ahc_set_tags(ahc, devinfo, FALSE);
+
+		/*
+		 * Resend the identify for this CCB as the target
+		 * may believe that the selection is invalid otherwise.
+		 */
+		ahc_outb(ahc, SCB_CONTROL,
+			 ahc_inb(ahc, SCB_CONTROL) & ~MSG_SIMPLE_Q_TAG);
+	 	scb->hscb->control &= ~MSG_SIMPLE_Q_TAG;
+		ahc_set_transaction_tag(scb, /*enabled*/FALSE,
+					/*type*/MSG_SIMPLE_Q_TAG);
+		ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG);
+		ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, SCSISIGO) | ATNO);
+
+		/*
+		 * This transaction is now at the head of
+		 * the untagged queue for this target.
+		 */
+		if ((ahc->flags & AHC_SCB_BTT) == 0) {
+			struct scb_tailq *untagged_q;
+
+			untagged_q =
+			    &(ahc->untagged_queues[devinfo->target_offset]);
+			TAILQ_INSERT_HEAD(untagged_q, scb, links.tqe);
+			scb->flags |= SCB_UNTAGGEDQ;
+		}
+		ahc_busy_tcl(ahc, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
+			     scb->hscb->tag);
+
+		/*
+		 * Requeue all tagged commands for this target
+		 * currently in our posession so they can be
+		 * converted to untagged commands.
+		 */
+		ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+				   SCB_GET_CHANNEL(ahc, scb),
+				   SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL,
+				   ROLE_INITIATOR, CAM_REQUEUE_REQ,
+				   SEARCH_COMPLETE);
+	} else {
+		/*
+		 * Otherwise, we ignore it.
+		 */
+		printf("%s:%c:%d: Message reject for %x -- ignored\n",
+		       ahc_name(ahc), devinfo->channel, devinfo->target,
+		       last_msg);
+	}
+	return (response);
+}
+
+/*
+ * Process an ingnore wide residue message.
+ */
+static void
+ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	u_int scb_index;
+	struct scb *scb;
+
+	scb_index = ahc_inb(ahc, SCB_TAG);
+	scb = ahc_lookup_scb(ahc, scb_index);
+	/*
+	 * XXX Actually check data direction in the sequencer?
+	 * Perhaps add datadir to some spare bits in the hscb?
+	 */
+	if ((ahc_inb(ahc, SEQ_FLAGS) & DPHASE) == 0
+	 || ahc_get_transfer_dir(scb) != CAM_DIR_IN) {
+		/*
+		 * Ignore the message if we haven't
+		 * seen an appropriate data phase yet.
+		 */
+	} else {
+		/*
+		 * If the residual occurred on the last
+		 * transfer and the transfer request was
+		 * expected to end on an odd count, do
+		 * nothing.  Otherwise, subtract a byte
+		 * and update the residual count accordingly.
+		 */
+		uint32_t sgptr;
+
+		sgptr = ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+		if ((sgptr & SG_LIST_NULL) != 0
+		 && ahc_inb(ahc, DATA_COUNT_ODD) == 1) {
+			/*
+			 * If the residual occurred on the last
+			 * transfer and the transfer request was
+			 * expected to end on an odd count, do
+			 * nothing.
+			 */
+		} else {
+			struct ahc_dma_seg *sg;
+			uint32_t data_cnt;
+			uint32_t data_addr;
+
+			/* Pull in the rest of the sgptr */
+			sgptr |= (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
+			      | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
+			      | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8);
+			sgptr &= SG_PTR_MASK;
+			data_cnt = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+2) << 16)
+				 | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+1) << 8)
+				 | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT));
+
+			data_addr = (ahc_inb(ahc, SHADDR + 3) << 24)
+				  | (ahc_inb(ahc, SHADDR + 2) << 16)
+				  | (ahc_inb(ahc, SHADDR + 1) << 8)
+				  | (ahc_inb(ahc, SHADDR));
+
+			data_cnt += 1;
+			data_addr -= 1;
+
+			sg = ahc_sg_bus_to_virt(scb, sgptr);
+			/*
+			 * The residual sg ptr points to the next S/G
+			 * to load so we must go back one.
+			 */
+			sg--;
+			if (sg != scb->sg_list
+			 && (sg->len & AHC_SG_LEN_MASK) < data_cnt) {
+
+				sg--;
+				data_cnt = 1 | (sg->len & AHC_DMA_LAST_SEG);
+				data_addr = sg->addr
+					  + (sg->len & AHC_SG_LEN_MASK) - 1;
+
+				/*
+				 * Increment sg so it points to the
+				 * "next" sg.
+				 */
+				sg++;
+				sgptr = ahc_sg_virt_to_bus(scb, sg);
+				ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 3,
+					 sgptr >> 24);
+				ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 2,
+					 sgptr >> 16);
+				ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 1,
+					 sgptr >> 8);
+				ahc_outb(ahc, SCB_RESIDUAL_SGPTR, sgptr);
+			}
+
+/* XXX What about high address byte??? */
+			ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 3, data_cnt >> 24);
+			ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 2, data_cnt >> 16);
+			ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 1, data_cnt >> 8);
+			ahc_outb(ahc, SCB_RESIDUAL_DATACNT, data_cnt);
+
+/* XXX Perhaps better to just keep the saved address in sram */
+			if ((ahc->features & AHC_ULTRA2) != 0) {
+				ahc_outb(ahc, HADDR + 3, data_addr >> 24);
+				ahc_outb(ahc, HADDR + 2, data_addr >> 16);
+				ahc_outb(ahc, HADDR + 1, data_addr >> 8);
+				ahc_outb(ahc, HADDR, data_addr);
+				ahc_outb(ahc, DFCNTRL, PRELOADEN);
+				ahc_outb(ahc, SXFRCTL0,
+					 ahc_inb(ahc, SXFRCTL0) | CLRCHN);
+			} else {
+				ahc_outb(ahc, HADDR + 3, data_addr >> 24);
+				ahc_outb(ahc, HADDR + 2, data_addr >> 16);
+				ahc_outb(ahc, HADDR + 1, data_addr >> 8);
+				ahc_outb(ahc, HADDR, data_addr);
+			}
+		}
+	}
+}
+
+/*
+ * Handle the effects of issuing a bus device reset message.
+ */
+static void
+ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+		    cam_status status, char *message, int verbose_level)
+{
+#ifdef AHC_TARGET_MODE
+	struct tmode_tstate* tstate;
+	u_int lun;
+#endif
+	int found;
+
+	found = ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+			       CAM_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role,
+			       status);
+
+#ifdef AHC_TARGET_MODE
+	/*
+	 * Send an immediate notify ccb to all target mord peripheral
+	 * drivers affected by this action.
+	 */
+	tstate = ahc->enabled_targets[devinfo->our_scsiid];
+	if (tstate != NULL) {
+		for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+			struct tmode_lstate* lstate;
+
+			lstate = tstate->enabled_luns[lun];
+			if (lstate == NULL)
+				continue;
+
+			ahc_queue_lstate_event(ahc, lstate, devinfo->our_scsiid,
+					       MSG_BUS_DEV_RESET, /*arg*/0);
+			ahc_send_lstate_events(ahc, lstate);
+		}
+	}
+#endif
+
+	/*
+	 * Go back to async/narrow transfers and renegotiate.
+	 */
+	ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+		      AHC_TRANS_CUR, /*paused*/TRUE);
+	ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL,
+			 /*period*/0, /*offset*/0, /*ppr_options*/0,
+			 AHC_TRANS_CUR, /*paused*/TRUE);
+	
+	ahc_send_async(ahc, devinfo->channel, devinfo->target,
+		       CAM_LUN_WILDCARD, AC_SENT_BDR);
+
+	if (message != NULL
+	 && (verbose_level <= bootverbose))
+		printf("%s: %s on %c:%d. %d SCBs aborted\n", ahc_name(ahc),
+		       message, devinfo->channel, devinfo->target, found);
+}
+
+#ifdef AHC_TARGET_MODE
+void
+ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+	/*              
+	 * To facilitate adding multiple messages together,
+	 * each routine should increment the index and len
+	 * variables instead of setting them explicitly.
+	 */             
+	ahc->msgout_index = 0;
+	ahc->msgout_len = 0;
+
+	if ((ahc->targ_msg_req & devinfo->target_mask) != 0)
+		ahc_build_transfer_msg(ahc, devinfo);
+	else
+		panic("ahc_intr: AWAITING target message with no message");
+
+	ahc->msgout_index = 0;
+	ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+}
+#endif
+/**************************** Initialization **********************************/
+/*
+ * Allocate a controller structure for a new device
+ * and perform initial initializion.
+ */
+struct ahc_softc *
+ahc_alloc(void *platform_arg, char *name)
+{
+	struct  ahc_softc *ahc;
+	int	i;
+
+#ifndef	__FreeBSD__
+	ahc = malloc(sizeof(*ahc), M_DEVBUF, M_NOWAIT);
+	if (!ahc) {
+		printf("aic7xxx: cannot malloc softc!\n");
+		free(name, M_DEVBUF);
+		return NULL;
+	}
+#else
+	ahc = device_get_softc((device_t)platform_arg);
+#endif
+	memset(ahc, 0, sizeof(*ahc));
+	LIST_INIT(&ahc->pending_scbs);
+	/* We don't know our unit number until the OSM sets it */
+	ahc->name = name;
+	for (i = 0; i < 16; i++)
+		TAILQ_INIT(&ahc->untagged_queues[i]);
+	if (ahc_platform_alloc(ahc, platform_arg) != 0) {
+		ahc_free(ahc);
+		ahc = NULL;
+	}
+	return (ahc);
+}
+
+int
+ahc_softc_init(struct ahc_softc *ahc, struct ahc_probe_config *config)
+{
+
+	ahc->chip = config->chip;
+	ahc->features = config->features;
+	ahc->bugs = config->bugs;
+	ahc->flags = config->flags;
+	ahc->channel = config->channel; 
+	ahc->unpause = (ahc_inb(ahc, HCNTRL) & IRQMS) | INTEN;
+	ahc->description = config->description;
+	/* The IRQMS bit is only valid on VL and EISA chips */
+	if ((ahc->chip & AHC_PCI) != 0)
+		ahc->unpause &= ~IRQMS;
+	ahc->pause = ahc->unpause | PAUSE; 
+	/* XXX The shared scb data stuff should be depricated */
+	if (ahc->scb_data == NULL) {
+		ahc->scb_data = malloc(sizeof(*ahc->scb_data),
+				       M_DEVBUF, M_NOWAIT);
+		if (ahc->scb_data == NULL)
+			return (ENOMEM);
+		memset(ahc->scb_data, 0, sizeof(*ahc->scb_data));
+	}
+
+	return (0);
+}
+
+void
+ahc_softc_insert(struct ahc_softc *ahc)
+{
+	struct ahc_softc *list_ahc;
+
+#ifdef AHC_SUPPORT_PCI
+	/*
+	 * Second Function PCI devices need to inherit some
+	 * settings from function 0.  We assume that function 0
+	 * will always be found prior to function 1.
+	 */
+	if ((ahc->chip & AHC_BUS_MASK) == AHC_PCI
+	 && ahc_get_pci_function(ahc->dev_softc) == 1) {
+		TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
+			ahc_dev_softc_t list_pci;
+			ahc_dev_softc_t pci;
+
+			list_pci = list_ahc->dev_softc;
+			pci = ahc->dev_softc;
+			if (ahc_get_pci_bus(list_pci) == ahc_get_pci_bus(pci)
+			 && ahc_get_pci_slot(list_pci) == ahc_get_pci_slot(pci)
+			 && ahc_get_pci_function(list_pci) == 0) {
+				ahc->flags &= ~AHC_BIOS_ENABLED; 
+				ahc->flags |=
+				    list_ahc->flags & AHC_BIOS_ENABLED;
+				ahc->flags &= ~AHC_CHANNEL_B_PRIMARY; 
+				ahc->flags |=
+				    list_ahc->flags & AHC_CHANNEL_B_PRIMARY;
+				break;
+			}
+		}
+	}
+#endif
+
+	/*
+	 * Insertion sort into our list of softcs.
+	 */
+	list_ahc = TAILQ_FIRST(&ahc_tailq);
+	while (list_ahc != NULL
+	    && ahc_softc_comp(list_ahc, ahc) <= 0)
+		list_ahc = TAILQ_NEXT(list_ahc, links);
+	if (list_ahc != NULL)
+		TAILQ_INSERT_BEFORE(list_ahc, ahc, links);
+	else
+		TAILQ_INSERT_TAIL(&ahc_tailq, ahc, links);
+	ahc->init_level++;
+}
+
+void
+ahc_set_unit(struct ahc_softc *ahc, int unit)
+{
+	ahc->unit = unit;
+}
+
+void
+ahc_set_name(struct ahc_softc *ahc, char *name)
+{
+	if (ahc->name != NULL)
+		free(ahc->name, M_DEVBUF);
+	ahc->name = name;
+}
+
+void
+ahc_free(struct ahc_softc *ahc)
+{
+	int i;
+
+	ahc_fini_scbdata(ahc);
+	switch (ahc->init_level) {
+	default:
+	case 5:
+		ahc_shutdown(ahc);
+		TAILQ_REMOVE(&ahc_tailq, ahc, links);
+		/* FALLTHROUGH */
+	case 4:
+		ahc_dmamap_unload(ahc, ahc->shared_data_dmat,
+				  ahc->shared_data_dmamap);
+		/* FALLTHROUGH */
+	case 3:
+		ahc_dmamem_free(ahc, ahc->shared_data_dmat, ahc->qoutfifo,
+				ahc->shared_data_dmamap);
+		ahc_dmamap_destroy(ahc, ahc->shared_data_dmat,
+				   ahc->shared_data_dmamap);
+		/* FALLTHROUGH */
+	case 2:
+		ahc_dma_tag_destroy(ahc, ahc->shared_data_dmat);
+	case 1:
+#ifndef __linux__
+		ahc_dma_tag_destroy(ahc, ahc->buffer_dmat);
+#endif
+		break;
+	case 0:
+		break;
+	}
+
+#ifndef __linux__
+	ahc_dma_tag_destroy(ahc, ahc->parent_dmat);
+#endif
+	ahc_platform_free(ahc);
+	for (i = 0; i < AHC_NUM_TARGETS; i++) {
+		struct tmode_tstate *tstate;
+
+		tstate = ahc->enabled_targets[i];
+		if (tstate != NULL) {
+#if AHC_TARGET_MODE
+			int j;
+
+			for (j = 0; j < AHC_NUM_LUNS; j++) {
+				struct tmode_lstate *lstate;
+
+				lstate = tstate->enabled_luns[j];
+				if (lstate != NULL) {
+					xpt_free_path(lstate->path);
+					free(lstate, M_DEVBUF);
+				}
+			}
+#endif
+			free(tstate, M_DEVBUF);
+		}
+	}
+#if AHC_TARGET_MODE
+	if (ahc->black_hole != NULL) {
+		xpt_free_path(ahc->black_hole->path);
+		free(ahc->black_hole, M_DEVBUF);
+	}
+#endif
+	if (ahc->name != NULL)
+		free(ahc->name, M_DEVBUF);
+#ifndef __FreeBSD__
+	free(ahc, M_DEVBUF);
+#endif
+	return;
+}
+
+void
+ahc_shutdown(void *arg)
+{
+	struct	ahc_softc *ahc;
+	int	i;
+
+	ahc = (struct ahc_softc *)arg;
+
+	/* This will reset most registers to 0, but not all */
+	ahc_reset(ahc);
+	ahc_outb(ahc, SCSISEQ, 0);
+	ahc_outb(ahc, SXFRCTL0, 0);
+	ahc_outb(ahc, DSPCISTATUS, 0);
+
+	for (i = TARG_SCSIRATE; i < HA_274_BIOSCTRL; i++)
+		ahc_outb(ahc, i, 0);
+}
+
+/*
+ * Reset the controller and record some information about it
+ * that is only availabel just after a reset.
+ */
+int
+ahc_reset(struct ahc_softc *ahc)
+{
+	u_int	sblkctl;
+	u_int	sxfrctl1_a, sxfrctl1_b;
+	int	wait;
+	
+	/*
+	 * Preserve the value of the SXFRCTL1 register for all channels.
+	 * It contains settings that affect termination and we don't want
+	 * to disturb the integrity of the bus.
+	 */
+	pause_sequencer(ahc);
+	sxfrctl1_b = 0;
+	if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7770) {
+		u_int sblkctl;
+
+		/*
+		 * Save channel B's settings in case this chip
+		 * is setup for TWIN channel operation.
+		 */
+		sblkctl = ahc_inb(ahc, SBLKCTL);
+		ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+		sxfrctl1_b = ahc_inb(ahc, SXFRCTL1);
+		ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+	}
+	sxfrctl1_a = ahc_inb(ahc, SXFRCTL1);
+
+	ahc_outb(ahc, HCNTRL, CHIPRST | ahc->pause);
+
+	/*
+	 * Ensure that the reset has finished
+	 */
+	wait = 1000;
+	do {
+		ahc_delay(1000);
+	} while (--wait && !(ahc_inb(ahc, HCNTRL) & CHIPRSTACK));
+
+	if (wait == 0) {
+		printf("%s: WARNING - Failed chip reset!  "
+		       "Trying to initialize anyway.\n", ahc_name(ahc));
+	}
+	ahc_outb(ahc, HCNTRL, ahc->pause);
+
+	/* Determine channel configuration */
+	sblkctl = ahc_inb(ahc, SBLKCTL) & (SELBUSB|SELWIDE);
+	/* No Twin Channel PCI cards */
+	if ((ahc->chip & AHC_PCI) != 0)
+		sblkctl &= ~SELBUSB;
+	switch (sblkctl) {
+	case 0:
+		/* Single Narrow Channel */
+		break;
+	case 2:
+		/* Wide Channel */
+		ahc->features |= AHC_WIDE;
+		break;
+	case 8:
+		/* Twin Channel */
+		ahc->features |= AHC_TWIN;
+		break;
+	default:
+		printf(" Unsupported adapter type.  Ignoring\n");
+		return(-1);
+	}
+
+	/*
+	 * Reload sxfrctl1.
+	 *
+	 * We must always initialize STPWEN to 1 before we
+	 * restore the saved values.  STPWEN is initialized
+	 * to a tri-state condition which can only be cleared
+	 * by turning it on.
+	 */
+	if ((ahc->features & AHC_TWIN) != 0) {
+		u_int sblkctl;
+
+		sblkctl = ahc_inb(ahc, SBLKCTL);
+		ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+		ahc_outb(ahc, SXFRCTL1, sxfrctl1_b);
+		ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+	}
+	ahc_outb(ahc, SXFRCTL1, sxfrctl1_a);
+
+#ifdef AHC_DUMP_SEQ
+	if (ahc->init_level == 0)
+		ahc_dumpseq(ahc);
+#endif
+
+	return (0);
+}
+
+/*
+ * Determine the number of SCBs available on the controller
+ */
+int
+ahc_probe_scbs(struct ahc_softc *ahc) {
+	int i;
+
+	for (i = 0; i < AHC_SCB_MAX; i++) {
+
+		ahc_outb(ahc, SCBPTR, i);
+		ahc_outb(ahc, SCB_BASE, i);
+		if (ahc_inb(ahc, SCB_BASE) != i)
+			break;
+		ahc_outb(ahc, SCBPTR, 0);
+		if (ahc_inb(ahc, SCB_BASE) != 0)
+			break;
+	}
+	return (i);
+}
+
+void
+ahc_init_probe_config(struct ahc_probe_config *probe_config)
+{
+	probe_config->description = NULL;
+	probe_config->channel = 'A';
+	probe_config->channel_b = 'B';
+	probe_config->chip = AHC_NONE;
+	probe_config->features = AHC_FENONE;
+	probe_config->bugs = AHC_BUGNONE;
+	probe_config->flags = AHC_FNONE;
+}
+
+static void
+ahc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) 
+{
+	bus_addr_t *baddr;
+
+	baddr = (bus_addr_t *)arg;
+	*baddr = segs->ds_addr;
+}
+
+static void
+ahc_build_free_scb_list(struct ahc_softc *ahc)
+{
+	int i;
+
+	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+		ahc_outb(ahc, SCBPTR, i);
+
+		/* Clear the control byte. */
+		ahc_outb(ahc, SCB_CONTROL, 0);
+
+		/* Set the next pointer */
+		if ((ahc->flags & AHC_PAGESCBS) != 0)
+			ahc_outb(ahc, SCB_NEXT, i+1);
+		else 
+			ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+
+		/* Make the tag number invalid */
+		ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+	}
+
+	/* Make sure that the last SCB terminates the free list */
+	ahc_outb(ahc, SCBPTR, i-1);
+	ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+
+	/* Ensure we clear the 0 SCB's control byte. */
+	ahc_outb(ahc, SCBPTR, 0);
+	ahc_outb(ahc, SCB_CONTROL, 0);
+}
+
+static int
+ahc_init_scbdata(struct ahc_softc *ahc)
+{
+	struct scb_data *scb_data;
+
+	scb_data = ahc->scb_data;
+	SLIST_INIT(&scb_data->free_scbs);
+	SLIST_INIT(&scb_data->sg_maps);
+
+	/* Allocate SCB resources */
+	scb_data->scbarray =
+	    (struct scb *)malloc(sizeof(struct scb) * AHC_SCB_MAX,
+				 M_DEVBUF, M_NOWAIT);
+	if (scb_data->scbarray == NULL)
+		return (ENOMEM);
+	memset(scb_data->scbarray, 0, sizeof(struct scb) * AHC_SCB_MAX);
+
+	/* Determine the number of hardware SCBs and initialize them */
+
+	scb_data->maxhscbs = ahc_probe_scbs(ahc);
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		/* SCB 0 heads the free list */
+		ahc_outb(ahc, FREE_SCBH, 0);
+	} else {
+		ahc_outb(ahc, FREE_SCBH, SCB_LIST_NULL);
+	}
+
+	if (ahc->scb_data->maxhscbs == 0) {
+		printf("%s: No SCB space found\n", ahc_name(ahc));
+		return (ENXIO);
+	}
+
+	ahc_build_free_scb_list(ahc);
+
+	/*
+	 * Create our DMA tags.  These tags define the kinds of device
+	 * accessible memory allocations and memory mappings we will
+	 * need to perform during normal operation.
+	 *
+	 * Unless we need to further restrict the allocation, we rely
+	 * on the restrictions of the parent dmat, hence the common
+	 * use of MAXADDR and MAXSIZE.
+	 */
+
+	/* DMA tag for our hardware scb structures */
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       AHC_SCB_MAX * sizeof(struct hardware_scb),
+			       /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &scb_data->hscb_dmat) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* Allocation for our ccbs */
+	if (ahc_dmamem_alloc(ahc, scb_data->hscb_dmat,
+			     (void **)&scb_data->hscbs,
+			     BUS_DMA_NOWAIT, &scb_data->hscb_dmamap) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* And permanently map them */
+	ahc_dmamap_load(ahc, scb_data->hscb_dmat, scb_data->hscb_dmamap,
+			scb_data->hscbs,
+			AHC_SCB_MAX * sizeof(struct hardware_scb),
+			ahc_dmamap_cb, &scb_data->hscb_busaddr, /*flags*/0);
+
+	scb_data->init_level++;
+
+	/* DMA tag for our sense buffers */
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       AHC_SCB_MAX * sizeof(struct scsi_sense_data),
+			       /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &scb_data->sense_dmat) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* Allocate them */
+	if (ahc_dmamem_alloc(ahc, scb_data->sense_dmat,
+			     (void **)&scb_data->sense,
+			     BUS_DMA_NOWAIT, &scb_data->sense_dmamap) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* And permanently map them */
+	ahc_dmamap_load(ahc, scb_data->sense_dmat, scb_data->sense_dmamap,
+			scb_data->sense,
+			AHC_SCB_MAX * sizeof(struct scsi_sense_data),
+			ahc_dmamap_cb, &scb_data->sense_busaddr, /*flags*/0);
+
+	scb_data->init_level++;
+
+	/* DMA tag for our S/G structures.  We allocate in page sized chunks */
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       PAGE_SIZE, /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &scb_data->sg_dmat) != 0) {
+		goto error_exit;
+	}
+
+	scb_data->init_level++;
+
+	/* Perform initial CCB allocation */
+	memset(scb_data->hscbs, 0, AHC_SCB_MAX * sizeof(struct hardware_scb));
+	ahc_alloc_scbs(ahc);
+
+	if (scb_data->numscbs == 0) {
+		printf("%s: ahc_init_scbdata - "
+		       "Unable to allocate initial scbs\n",
+		       ahc_name(ahc));
+		goto error_exit;
+	}
+
+	/*
+	 * Tell the sequencer which SCB will be the next one it receives.
+	 */
+	ahc->next_queued_scb = ahc_get_scb(ahc);
+	ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+	/*
+	 * Note that we were successfull
+	 */
+	return (0); 
+
+error_exit:
+
+	return (ENOMEM);
+}
+
+static void
+ahc_fini_scbdata(struct ahc_softc *ahc)
+{
+	struct scb_data *scb_data;
+
+	scb_data = ahc->scb_data;
+	if (scb_data == NULL)
+		return;
+
+	switch (scb_data->init_level) {
+	default:
+	case 7:
+	{
+		struct sg_map_node *sg_map;
+
+		while ((sg_map = SLIST_FIRST(&scb_data->sg_maps))!= NULL) {
+			SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
+			ahc_dmamap_unload(ahc, scb_data->sg_dmat,
+					  sg_map->sg_dmamap);
+			ahc_dmamem_free(ahc, scb_data->sg_dmat,
+					sg_map->sg_vaddr,
+					sg_map->sg_dmamap);
+			free(sg_map, M_DEVBUF);
+		}
+		ahc_dma_tag_destroy(ahc, scb_data->sg_dmat);
+	}
+	case 6:
+		ahc_dmamap_unload(ahc, scb_data->sense_dmat,
+				  scb_data->sense_dmamap);
+	case 5:
+		ahc_dmamem_free(ahc, scb_data->sense_dmat, scb_data->sense,
+				scb_data->sense_dmamap);
+		ahc_dmamap_destroy(ahc, scb_data->sense_dmat,
+				   scb_data->sense_dmamap);
+	case 4:
+		ahc_dma_tag_destroy(ahc, scb_data->sense_dmat);
+	case 3:
+		ahc_dmamap_unload(ahc, scb_data->hscb_dmat,
+				  scb_data->hscb_dmamap);
+	case 2:
+		ahc_dmamem_free(ahc, scb_data->hscb_dmat, scb_data->hscbs,
+				scb_data->hscb_dmamap);
+		ahc_dmamap_destroy(ahc, scb_data->hscb_dmat,
+				   scb_data->hscb_dmamap);
+	case 1:
+		ahc_dma_tag_destroy(ahc, scb_data->hscb_dmat);
+		break;
+	case 0:
+		break;
+	}
+	if (scb_data->scbarray != NULL)
+		free(scb_data->scbarray, M_DEVBUF);
+}
+
+void
+ahc_alloc_scbs(struct ahc_softc *ahc)
+{
+	struct scb_data *scb_data;
+	struct scb *next_scb;
+	struct sg_map_node *sg_map;
+	bus_addr_t physaddr;
+	struct ahc_dma_seg *segs;
+	int newcount;
+	int i;
+
+	scb_data = ahc->scb_data;
+	if (scb_data->numscbs >= AHC_SCB_MAX)
+		/* Can't allocate any more */
+		return;
+
+	next_scb = &scb_data->scbarray[scb_data->numscbs];
+
+	sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);
+
+	if (sg_map == NULL)
+		return;
+
+	/* Allocate S/G space for the next batch of SCBS */
+	if (ahc_dmamem_alloc(ahc, scb_data->sg_dmat,
+			     (void **)&sg_map->sg_vaddr,
+			     BUS_DMA_NOWAIT, &sg_map->sg_dmamap) != 0) {
+		free(sg_map, M_DEVBUF);
+		return;
+	}
+
+	SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
+
+	ahc_dmamap_load(ahc, scb_data->sg_dmat, sg_map->sg_dmamap,
+			sg_map->sg_vaddr, PAGE_SIZE, ahc_dmamap_cb,
+			&sg_map->sg_physaddr, /*flags*/0);
+
+	segs = sg_map->sg_vaddr;
+	physaddr = sg_map->sg_physaddr;
+
+	newcount = (PAGE_SIZE / (AHC_NSEG * sizeof(struct ahc_dma_seg)));
+	for (i = 0; scb_data->numscbs < AHC_SCB_MAX && i < newcount; i++) {
+		struct scb_platform_data *pdata;
+#ifndef __linux__
+		int error;
+#endif
+		pdata = (struct scb_platform_data *)malloc(sizeof(*pdata),
+							   M_DEVBUF, M_NOWAIT);
+		if (pdata == NULL)
+			break;
+		next_scb->platform_data = pdata;
+		next_scb->sg_list = segs;
+		/*
+		 * The sequencer always starts with the second entry.
+		 * The first entry is embedded in the scb.
+		 */
+		next_scb->sg_list_phys = physaddr + sizeof(struct ahc_dma_seg);
+		next_scb->ahc_softc = ahc;
+		next_scb->flags = SCB_FREE;
+#ifndef __linux__
+		error = ahc_dmamap_create(ahc, ahc->buffer_dmat, /*flags*/0,
+					  &next_scb->dmamap);
+		if (error != 0)
+			break;
+#endif
+		next_scb->hscb = &scb_data->hscbs[scb_data->numscbs];
+		next_scb->hscb->tag = ahc->scb_data->numscbs;
+		SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs,
+				  next_scb, links.sle);
+		segs += AHC_NSEG;
+		physaddr += (AHC_NSEG * sizeof(struct ahc_dma_seg));
+		next_scb++;
+		ahc->scb_data->numscbs++;
+	}
+}
+
+void
+ahc_controller_info(struct ahc_softc *ahc, char *buf)
+{
+	int len;
+
+	len = sprintf(buf, "%s: ", ahc_chip_names[ahc->chip & AHC_CHIPID_MASK]);
+	buf += len;
+	if ((ahc->features & AHC_TWIN) != 0)
+ 		len = sprintf(buf, "Twin Channel, A SCSI Id=%d, "
+			      "B SCSI Id=%d, primary %c, ",
+			      ahc->our_id, ahc->our_id_b,
+			      ahc->flags & AHC_CHANNEL_B_PRIMARY ? 'B': 'A');
+	else {
+		const char *type;
+
+		if ((ahc->features & AHC_WIDE) != 0) {
+			type = "Wide";
+		} else {
+			type = "Single";
+		}
+		len = sprintf(buf, "%s Channel %c, SCSI Id=%d, ",
+			      type, ahc->channel, ahc->our_id);
+	}
+	buf += len;
+
+	if ((ahc->flags & AHC_PAGESCBS) != 0)
+		sprintf(buf, "%d/%d SCBs",
+			ahc->scb_data->maxhscbs, AHC_SCB_MAX);
+	else
+		sprintf(buf, "%d SCBs", ahc->scb_data->maxhscbs);
+}
+
+/*
+ * Start the board, ready for normal operation
+ */
+int
+ahc_init(struct ahc_softc *ahc)
+{
+	int	 max_targ;
+	int	 i;
+	int	 term;
+	u_int	 scsi_conf;
+	u_int	 scsiseq_template;
+	u_int	 ultraenb;
+	u_int	 discenable;
+	u_int	 tagenable;
+	size_t	 driver_data_size;
+	uint32_t physaddr;
+
+#ifdef AHC_DEBUG_SEQUENCER
+	ahc->flags |= AHC_SEQUENCER_DEBUG;
+#endif
+
+#ifdef AHC_PRINT_SRAM
+	printf("Scratch Ram:");
+	for (i = 0x20; i < 0x5f; i++) {
+		if (((i % 8) == 0) && (i != 0)) {
+			printf ("\n              ");
+		}
+		printf (" 0x%x", ahc_inb(ahc, i));
+	}
+	if ((ahc->features & AHC_MORE_SRAM) != 0) {
+		for (i = 0x70; i < 0x7f; i++) {
+			if (((i % 8) == 0) && (i != 0)) {
+				printf ("\n              ");
+			}
+			printf (" 0x%x", ahc_inb(ahc, i));
+		}
+	}
+	printf ("\n");
+#endif
+	max_targ = 15;
+
+	/*
+	 * Assume we have a board at this stage and it has been reset.
+	 */
+	if ((ahc->flags & AHC_USEDEFAULTS) != 0)
+		ahc->our_id = ahc->our_id_b = 7;
+	
+	/*
+	 * Default to allowing initiator operations.
+	 */
+	ahc->flags |= AHC_INITIATORROLE;
+
+	/*
+	 * Only allow target mode features if this unit has them enabled.
+	 */
+	if ((AHC_TMODE_ENABLE & (0x1 << ahc->unit)) == 0)
+		ahc->features &= ~AHC_TARGETMODE;
+
+#ifndef __linux__
+	/* DMA tag for mapping buffers into device visible space. */
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       /*maxsize*/MAXBSIZE, /*nsegments*/AHC_NSEG,
+			       /*maxsegsz*/AHC_MAXTRANSFER_SIZE,
+			       /*flags*/BUS_DMA_ALLOCNOW,
+			       &ahc->buffer_dmat) != 0) {
+		return (ENOMEM);
+	}
+#endif
+
+	ahc->init_level++;
+
+	/*
+	 * DMA tag for our command fifos and other data in system memory
+	 * the card's sequencer must be able to access.  For initiator
+	 * roles, we need to allocate space for the the qinfifo and qoutfifo.
+	 * The qinfifo and qoutfifo are composed of 256 1 byte elements. 
+	 * When providing for the target mode role, we must additionally
+	 * provide space for the incoming target command fifo and an extra
+	 * byte to deal with a dma bug in some chip versions.
+	 */
+	driver_data_size = 2 * 256 * sizeof(uint8_t);
+	if ((ahc->features & AHC_TARGETMODE) != 0)
+		driver_data_size += AHC_TMODE_CMDS * sizeof(struct target_cmd)
+				 + /*DMA WideOdd Bug Buffer*/1;
+	if (ahc_dma_tag_create(ahc, ahc->parent_dmat, /*alignment*/1,
+			       /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR,
+			       /*highaddr*/BUS_SPACE_MAXADDR,
+			       /*filter*/NULL, /*filterarg*/NULL,
+			       driver_data_size,
+			       /*nsegments*/1,
+			       /*maxsegsz*/BUS_SPACE_MAXSIZE_32BIT,
+			       /*flags*/0, &ahc->shared_data_dmat) != 0) {
+		return (ENOMEM);
+	}
+
+	ahc->init_level++;
+
+	/* Allocation of driver data */
+	if (ahc_dmamem_alloc(ahc, ahc->shared_data_dmat,
+			     (void **)&ahc->qoutfifo,
+			     BUS_DMA_NOWAIT, &ahc->shared_data_dmamap) != 0) {
+		return (ENOMEM);
+	}
+
+	ahc->init_level++;
+
+	/* And permanently map it in */
+	ahc_dmamap_load(ahc, ahc->shared_data_dmat, ahc->shared_data_dmamap,
+			ahc->qoutfifo, driver_data_size, ahc_dmamap_cb,
+			&ahc->shared_data_busaddr, /*flags*/0);
+
+	if ((ahc->features & AHC_TARGETMODE) != 0) {
+		ahc->targetcmds = (struct target_cmd *)ahc->qoutfifo;
+		ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[AHC_TMODE_CMDS];
+		ahc->dma_bug_buf = ahc->shared_data_busaddr
+				 + driver_data_size - 1;
+		/* All target command blocks start out invalid. */
+		for (i = 0; i < AHC_TMODE_CMDS; i++)
+			ahc->targetcmds[i].cmd_valid = 0;
+		ahc->tqinfifonext = 1;
+		ahc_outb(ahc, KERNEL_TQINPOS, ahc->tqinfifonext - 1);
+		ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+		ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[256];
+	}
+	ahc->qinfifo = &ahc->qoutfifo[256];
+
+	ahc->init_level++;
+
+	/* Allocate SCB data now that buffer_dmat is initialized */
+	if (ahc->scb_data->maxhscbs == 0)
+		if (ahc_init_scbdata(ahc) != 0)
+			return (ENOMEM);
+
+	/*
+	 * Allocate a tstate to house information for our
+	 * initiator presence on the bus as well as the user
+	 * data for any target mode initiator.
+	 */
+	if (ahc_alloc_tstate(ahc, ahc->our_id, 'A') == NULL) {
+		printf("%s: unable to allocate tmode_tstate.  "
+		       "Failing attach\n", ahc_name(ahc));
+		return (-1);
+	}
+
+	if ((ahc->features & AHC_TWIN) != 0) {
+		if (ahc_alloc_tstate(ahc, ahc->our_id_b, 'B') == NULL) {
+			printf("%s: unable to allocate tmode_tstate.  "
+			       "Failing attach\n", ahc_name(ahc));
+			return (-1);
+		}
+	}
+
+	ahc_outb(ahc, SEQ_FLAGS, 0);
+	ahc_outb(ahc, SEQ_FLAGS2, 0);
+
+	if (ahc->scb_data->maxhscbs < AHC_SCB_MAX) {
+		ahc->flags |= AHC_PAGESCBS;
+	} else {
+		ahc->flags &= ~AHC_PAGESCBS;
+	}
+
+#ifdef AHC_DEBUG
+	if (ahc_debug & AHC_SHOWMISC) {
+		printf("%s: hardware scb %d bytes; kernel scb %d bytes; "
+		       "ahc_dma %d bytes\n",
+			ahc_name(ahc),
+			sizeof(struct hardware_scb),
+			sizeof(struct scb),
+			sizeof(struct ahc_dma_seg));
+	}
+#endif /* AHC_DEBUG */
+
+	/* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels*/
+	if (ahc->features & AHC_TWIN) {
+
+		/*
+		 * The device is gated to channel B after a chip reset,
+		 * so set those values first
+		 */
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+		term = (ahc->flags & AHC_TERM_ENB_B) != 0 ? STPWEN : 0;
+		ahc_outb(ahc, SCSIID, ahc->our_id_b);
+		scsi_conf = ahc_inb(ahc, SCSICONF + 1);
+		ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+					|term|ahc->seltime_b|ENSTIMER|ACTNEGEN);
+		if ((ahc->features & AHC_ULTRA2) != 0)
+			ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
+		ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+		ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+		if ((scsi_conf & RESET_SCSI) != 0
+		 && (ahc->flags & AHC_INITIATORROLE) != 0)
+			ahc->flags |= AHC_RESET_BUS_B;
+
+		/* Select Channel A */
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+	}
+	term = (ahc->flags & AHC_TERM_ENB_A) != 0 ? STPWEN : 0;
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
+	else
+		ahc_outb(ahc, SCSIID, ahc->our_id);
+	scsi_conf = ahc_inb(ahc, SCSICONF);
+	ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+				|term|ahc->seltime
+				|ENSTIMER|ACTNEGEN);
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
+	ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+	ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
+
+	if ((scsi_conf & RESET_SCSI) != 0
+	 && (ahc->flags & AHC_INITIATORROLE) != 0)
+		ahc->flags |= AHC_RESET_BUS_A;
+
+	/*
+	 * Look at the information that board initialization or
+	 * the board bios has left us.
+	 */
+	ultraenb = 0;	
+	tagenable = ALL_TARGETS_MASK;
+
+	/* Grab the disconnection disable table and invert it for our needs */
+	if (ahc->flags & AHC_USEDEFAULTS) {
+		printf("%s: Host Adapter Bios disabled.  Using default SCSI "
+			"device parameters\n", ahc_name(ahc));
+		ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B|
+			      AHC_TERM_ENB_A|AHC_TERM_ENB_B;
+		discenable = ALL_TARGETS_MASK;
+		if ((ahc->features & AHC_ULTRA) != 0)
+			ultraenb = ALL_TARGETS_MASK;
+	} else {
+		discenable = ~((ahc_inb(ahc, DISC_DSB + 1) << 8)
+			   | ahc_inb(ahc, DISC_DSB));
+		if ((ahc->features & (AHC_ULTRA|AHC_ULTRA2)) != 0)
+			ultraenb = (ahc_inb(ahc, ULTRA_ENB + 1) << 8)
+				      | ahc_inb(ahc, ULTRA_ENB);
+	}
+	if ((ahc->flags & AHC_ULTRA_DISABLED) != 0)
+		ultraenb = 0;
+
+	if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
+		max_targ = 7;
+
+	for (i = 0; i <= max_targ; i++) {
+		struct ahc_initiator_tinfo *tinfo;
+		struct tmode_tstate *tstate;
+		u_int our_id;
+		u_int target_id;
+		char channel;
+
+		channel = 'A';
+		our_id = ahc->our_id;
+		target_id = i;
+		if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+			channel = 'B';
+			our_id = ahc->our_id_b;
+			target_id = i % 8;
+		}
+		tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+					    target_id, &tstate);
+		/* Default to async narrow across the board */
+		memset(tinfo, 0, sizeof(*tinfo));
+		if (ahc->flags & AHC_USEDEFAULTS) {
+			if ((ahc->features & AHC_WIDE) != 0)
+				tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+
+			/*
+			 * These will be truncated when we determine the
+			 * connection type we have with the target.
+			 */
+			tinfo->user.period = ahc_syncrates->period;
+			tinfo->user.offset = ~0;
+		} else {
+			u_int scsirate;
+			uint16_t mask;
+
+			/* Take the settings leftover in scratch RAM. */
+			scsirate = ahc_inb(ahc, TARG_SCSIRATE + i);
+			mask = (0x01 << i);
+			if ((ahc->features & AHC_ULTRA2) != 0) {
+				u_int offset;
+				u_int maxsync;
+
+				if ((scsirate & SOFS) == 0x0F) {
+					/*
+					 * Haven't negotiated yet,
+					 * so the format is different.
+					 */
+					scsirate = (scsirate & SXFR) >> 4
+						 | (ultraenb & mask)
+						  ? 0x08 : 0x0
+						 | (scsirate & WIDEXFER);
+					offset = MAX_OFFSET_ULTRA2;
+				} else
+					offset = ahc_inb(ahc, TARG_OFFSET + i);
+				if ((scsirate & ~WIDEXFER) == 0 && offset != 0)
+					/* Set to the lowest sync rate, 5MHz */
+					scsirate |= 0x1c;
+				maxsync = AHC_SYNCRATE_ULTRA2;
+				if ((ahc->features & AHC_DT) != 0)
+					maxsync = AHC_SYNCRATE_DT;
+				tinfo->user.period =
+				    ahc_find_period(ahc, scsirate, maxsync);
+				if (offset == 0)
+					tinfo->user.period = 0;
+				else
+					tinfo->user.offset = ~0;
+				if ((scsirate & SXFR_ULTRA2) <= 8/*10MHz*/
+				 && (ahc->features & AHC_DT) != 0)
+					tinfo->user.ppr_options =
+					    MSG_EXT_PPR_DT_REQ;
+			} else if ((scsirate & SOFS) != 0) {
+				if ((scsirate & SXFR) == 0x40
+				 && (ultraenb & mask) != 0) {
+					/* Treat 10MHz as a non-ultra speed */
+					scsirate &= ~SXFR;
+				 	ultraenb &= ~mask;
+				}
+				tinfo->user.period = 
+				    ahc_find_period(ahc, scsirate,
+						    (ultraenb & mask)
+						   ? AHC_SYNCRATE_ULTRA
+						   : AHC_SYNCRATE_FAST);
+				if (tinfo->user.period != 0)
+					tinfo->user.offset = ~0;
+			}
+			if (tinfo->user.period == 0)
+				tinfo->user.offset = 0;
+			if ((scsirate & WIDEXFER) != 0
+			 && (ahc->features & AHC_WIDE) != 0)
+				tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+			tinfo->user.protocol_version = 4;
+			if ((ahc->features & AHC_DT) != 0)
+				tinfo->user.transport_version = 3;
+			else
+				tinfo->user.transport_version = 2;
+			tinfo->goal.protocol_version = 2;
+			tinfo->goal.transport_version = 2;
+			tinfo->current.protocol_version = 2;
+			tinfo->current.transport_version = 2;
+		}
+		tstate->ultraenb = ultraenb;
+		tstate->discenable = discenable;
+		tstate->tagenable = 0; /* Wait until the XPT says its okay */
+	}
+	ahc->user_discenable = discenable;
+	ahc->user_tagenable = tagenable;
+
+	/* There are no untagged SCBs active yet. */
+	for (i = 0; i < 16; i++) {
+		ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, 0));
+		if ((ahc->flags & AHC_SCB_BTT) != 0) {
+			int lun;
+
+			/*
+			 * The SCB based BTT allows an entry per
+			 * target and lun pair.
+			 */
+			for (lun = 1; lun < AHC_NUM_LUNS; lun++)
+				ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, lun));
+		}
+	}
+
+	/* All of our queues are empty */
+	for (i = 0; i < 256; i++)
+		ahc->qoutfifo[i] = SCB_LIST_NULL;
+
+	for (i = 0; i < 256; i++)
+		ahc->qinfifo[i] = SCB_LIST_NULL;
+
+	if ((ahc->features & AHC_MULTI_TID) != 0) {
+		ahc_outb(ahc, TARGID, 0);
+		ahc_outb(ahc, TARGID + 1, 0);
+	}
+
+	/*
+	 * Tell the sequencer where it can find our arrays in memory.
+	 */
+	physaddr = ahc->scb_data->hscb_busaddr;
+	ahc_outb(ahc, HSCB_ADDR, physaddr & 0xFF);
+	ahc_outb(ahc, HSCB_ADDR + 1, (physaddr >> 8) & 0xFF);
+	ahc_outb(ahc, HSCB_ADDR + 2, (physaddr >> 16) & 0xFF);
+	ahc_outb(ahc, HSCB_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+	physaddr = ahc->shared_data_busaddr;
+	ahc_outb(ahc, SHARED_DATA_ADDR, physaddr & 0xFF);
+	ahc_outb(ahc, SHARED_DATA_ADDR + 1, (physaddr >> 8) & 0xFF);
+	ahc_outb(ahc, SHARED_DATA_ADDR + 2, (physaddr >> 16) & 0xFF);
+	ahc_outb(ahc, SHARED_DATA_ADDR + 3, (physaddr >> 24) & 0xFF);
+
+	/*
+	 * Initialize the group code to command length table.
+	 * This overrides the values in TARG_SCSIRATE, so only
+	 * setup the table after we have processed that information.
+	 */
+	ahc_outb(ahc, CMDSIZE_TABLE, 5);
+	ahc_outb(ahc, CMDSIZE_TABLE + 1, 9);
+	ahc_outb(ahc, CMDSIZE_TABLE + 2, 9);
+	ahc_outb(ahc, CMDSIZE_TABLE + 3, 0);
+	ahc_outb(ahc, CMDSIZE_TABLE + 4, 15);
+	ahc_outb(ahc, CMDSIZE_TABLE + 5, 11);
+	ahc_outb(ahc, CMDSIZE_TABLE + 6, 0);
+	ahc_outb(ahc, CMDSIZE_TABLE + 7, 0);
+		
+	/* Tell the sequencer of our initial queue positions */
+	ahc_outb(ahc, KERNEL_QINPOS, 0);
+	ahc_outb(ahc, QINPOS, 0);
+	ahc_outb(ahc, QOUTPOS, 0);
+
+	/* Don't have any special messages to send to targets */
+	ahc_outb(ahc, TARGET_MSG_REQUEST, 0);
+	ahc_outb(ahc, TARGET_MSG_REQUEST + 1, 0);
+
+	/*
+	 * Use the built in queue management registers
+	 * if they are available.
+	 */
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		ahc_outb(ahc, QOFF_CTLSTA, SCB_QSIZE_256);
+		ahc_outb(ahc, SDSCB_QOFF, 0);
+		ahc_outb(ahc, SNSCB_QOFF, 0);
+		ahc_outb(ahc, HNSCB_QOFF, 0);
+	}
+
+
+	/* We don't have any waiting selections */
+	ahc_outb(ahc, WAITING_SCBH, SCB_LIST_NULL);
+
+	/* Our disconnection list is empty too */
+	ahc_outb(ahc, DISCONNECTED_SCBH, SCB_LIST_NULL);
+
+	/* Message out buffer starts empty */
+	ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+
+	/*
+	 * Setup the allowed SCSI Sequences based on operational mode.
+	 * If we are a target, we'll enalbe select in operations once
+	 * we've had a lun enabled.
+	 */
+	scsiseq_template = ENSELO|ENAUTOATNO|ENAUTOATNP;
+	if ((ahc->flags & AHC_INITIATORROLE) != 0)
+		scsiseq_template |= ENRSELI;
+	ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq_template);
+
+	/*
+	 * Load the Sequencer program and Enable the adapter
+	 * in "fast" mode.
+	 */
+	if (bootverbose)
+		printf("%s: Downloading Sequencer Program...",
+		       ahc_name(ahc));
+
+	ahc_loadseq(ahc);
+
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		int wait;
+
+		/*
+		 * Wait for up to 500ms for our transceivers
+		 * to settle.  If the adapter does not have
+		 * a cable attached, the tranceivers may
+		 * never settle, so don't complain if we
+		 * fail here.
+		 */
+		pause_sequencer(ahc);
+		for (wait = 5000;
+		     (ahc_inb(ahc, SBLKCTL) & (ENAB40|ENAB20)) == 0 && wait;
+		     wait--)
+			ahc_delay(100);
+		unpause_sequencer(ahc);
+	}
+	return (0);
+}
+
+/*
+ * Ensure that the card is paused in a location
+ * outside of all critical sections and that all
+ * pending work is completed prior to returning.
+ * This routine should only be called from outside
+ * an interrupt context.
+ */
+void
+ahc_pause_and_flushwork(struct ahc_softc *ahc)
+{
+	int intstat;
+	int maxloops;
+
+	maxloops = 1000;
+	ahc->flags |= AHC_ALL_INTERRUPTS;
+	intstat = 0;
+	do {
+		ahc_intr(ahc);
+		pause_sequencer(ahc);
+		ahc_clear_critical_section(ahc);
+		if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0)
+			break;
+		maxloops--;
+	} while (((intstat = ahc_inb(ahc, INTSTAT)) & INT_PEND) && --maxloops);
+	if (maxloops == 0) {
+		printf("Infinite interrupt loop, INTSTAT = %x",
+		      ahc_inb(ahc, INTSTAT));
+	}
+	ahc_platform_flushwork(ahc);
+	ahc->flags &= ~AHC_ALL_INTERRUPTS;
+}
+
+int
+ahc_suspend(struct ahc_softc *ahc)
+{
+	uint8_t *ptr;
+	int	 i;
+
+	ahc_pause_and_flushwork(ahc);
+
+	if (LIST_FIRST(&ahc->pending_scbs) != NULL)
+		return (EBUSY);
+
+#if AHC_TARGET_MODE
+	/*
+	 * XXX What about ATIOs that have not yet been serviced?
+	 * Perhaps we should just refuse to be suspended if we
+	 * are acting in a target role.
+	 */
+	if (ahc->pending_device != NULL)
+		return (EBUSY);
+#endif
+
+	/* Save volatile registers */
+	if ((ahc->features & AHC_TWIN) != 0) {
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+		ahc->suspend_state.channel[1].scsiseq = ahc_inb(ahc, SCSISEQ);
+		ahc->suspend_state.channel[1].sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+		ahc->suspend_state.channel[1].sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
+		ahc->suspend_state.channel[1].simode0 = ahc_inb(ahc, SIMODE0);
+		ahc->suspend_state.channel[1].simode1 = ahc_inb(ahc, SIMODE1);
+		ahc->suspend_state.channel[1].seltimer = ahc_inb(ahc, SELTIMER);
+		ahc->suspend_state.channel[1].seqctl = ahc_inb(ahc, SEQCTL);
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+	}
+	ahc->suspend_state.channel[0].scsiseq = ahc_inb(ahc, SCSISEQ);
+	ahc->suspend_state.channel[0].sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+	ahc->suspend_state.channel[0].sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
+	ahc->suspend_state.channel[0].simode0 = ahc_inb(ahc, SIMODE0);
+	ahc->suspend_state.channel[0].simode1 = ahc_inb(ahc, SIMODE1);
+	ahc->suspend_state.channel[0].seltimer = ahc_inb(ahc, SELTIMER);
+	ahc->suspend_state.channel[0].seqctl = ahc_inb(ahc, SEQCTL);
+
+	if ((ahc->chip & AHC_PCI) != 0) {
+		ahc->suspend_state.dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+		ahc->suspend_state.dspcistatus = ahc_inb(ahc, DSPCISTATUS);
+	}
+
+	if ((ahc->features & AHC_DT) != 0) {
+		u_int sfunct;
+
+		sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+		ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+		ahc->suspend_state.optionmode = ahc_inb(ahc, OPTIONMODE);
+		ahc_outb(ahc, SFUNCT, sfunct);
+		ahc->suspend_state.crccontrol1 = ahc_inb(ahc, CRCCONTROL1);
+	}
+
+	if ((ahc->features & AHC_MULTI_FUNC) != 0)
+		ahc->suspend_state.scbbaddr = ahc_inb(ahc, SCBBADDR);
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc->suspend_state.dff_thrsh = ahc_inb(ahc, DFF_THRSH);
+
+	ptr = ahc->suspend_state.scratch_ram;
+	for (i = 0; i < 64; i++)
+		*ptr++ = ahc_inb(ahc, SRAM_BASE + i);
+
+	if ((ahc->features & AHC_MORE_SRAM) != 0) {
+		for (i = 0; i < 16; i++)
+			*ptr++ = ahc_inb(ahc, TARG_OFFSET + i);
+	}
+
+	ptr = ahc->suspend_state.btt;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		for (i = 0;i < AHC_NUM_TARGETS; i++) {
+			int j;
+
+			for (j = 0;j < AHC_NUM_LUNS; j++) {
+				u_int tcl;
+
+				tcl = BUILD_TCL(i << 4, j);
+				*ptr = ahc_index_busy_tcl(ahc, tcl);
+			}
+		}
+	}
+	ahc_shutdown(ahc);
+	return (0);
+}
+
+int
+ahc_resume(struct ahc_softc *ahc)
+{
+	uint8_t *ptr;
+	int	 i;
+
+	ahc_reset(ahc);
+
+	ahc_build_free_scb_list(ahc);
+
+	/* Restore volatile registers */
+	if ((ahc->features & AHC_TWIN) != 0) {
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+		ahc_outb(ahc, SCSIID, ahc->our_id);
+		ahc_outb(ahc, SCSISEQ, ahc->suspend_state.channel[1].scsiseq);
+		ahc_outb(ahc, SXFRCTL0, ahc->suspend_state.channel[1].sxfrctl0);
+		ahc_outb(ahc, SXFRCTL1, ahc->suspend_state.channel[1].sxfrctl1);
+		ahc_outb(ahc, SIMODE0, ahc->suspend_state.channel[1].simode0);
+		ahc_outb(ahc, SIMODE1, ahc->suspend_state.channel[1].simode1);
+		ahc_outb(ahc, SELTIMER, ahc->suspend_state.channel[1].seltimer);
+		ahc_outb(ahc, SEQCTL, ahc->suspend_state.channel[1].seqctl);
+		ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+	}
+	ahc_outb(ahc, SCSISEQ, ahc->suspend_state.channel[0].scsiseq);
+	ahc_outb(ahc, SXFRCTL0, ahc->suspend_state.channel[0].sxfrctl0);
+	ahc_outb(ahc, SXFRCTL1, ahc->suspend_state.channel[0].sxfrctl1);
+	ahc_outb(ahc, SIMODE0, ahc->suspend_state.channel[0].simode0);
+	ahc_outb(ahc, SIMODE1, ahc->suspend_state.channel[0].simode1);
+	ahc_outb(ahc, SELTIMER, ahc->suspend_state.channel[0].seltimer);
+	ahc_outb(ahc, SEQCTL, ahc->suspend_state.channel[0].seqctl);
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
+	else
+		ahc_outb(ahc, SCSIID, ahc->our_id);
+
+	if ((ahc->chip & AHC_PCI) != 0) {
+		ahc_outb(ahc, DSCOMMAND0, ahc->suspend_state.dscommand0);
+		ahc_outb(ahc, DSPCISTATUS, ahc->suspend_state.dspcistatus);
+	}
+
+	if ((ahc->features & AHC_DT) != 0) {
+		u_int sfunct;
+
+		sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+		ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+		ahc_outb(ahc, OPTIONMODE, ahc->suspend_state.optionmode);
+		ahc_outb(ahc, SFUNCT, sfunct);
+		ahc_outb(ahc, CRCCONTROL1, ahc->suspend_state.crccontrol1);
+	}
+
+	if ((ahc->features & AHC_MULTI_FUNC) != 0)
+		ahc_outb(ahc, SCBBADDR, ahc->suspend_state.scbbaddr);
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, DFF_THRSH, ahc->suspend_state.dff_thrsh);
+
+	ptr = ahc->suspend_state.scratch_ram;
+	for (i = 0; i < 64; i++)
+		ahc_outb(ahc, SRAM_BASE + i, *ptr++);
+
+	if ((ahc->features & AHC_MORE_SRAM) != 0) {
+		for (i = 0; i < 16; i++)
+			ahc_outb(ahc, TARG_OFFSET + i, *ptr++);
+	}
+
+	ptr = ahc->suspend_state.btt;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		for (i = 0;i < AHC_NUM_TARGETS; i++) {
+			int j;
+
+			for (j = 0;j < AHC_NUM_LUNS; j++) {
+				u_int tcl;
+
+				tcl = BUILD_TCL(i << 4, j);
+				ahc_busy_tcl(ahc, tcl, *ptr);
+			}
+		}
+	}
+	return (0);
+}
+
+/************************** Busy Target Table *********************************/
+/*
+ * Return the untagged transaction id for a given target/channel lun.
+ * Optionally, clear the entry.
+ */
+u_int
+ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+	u_int scbid;
+	u_int target_offset;
+
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		u_int saved_scbptr;
+		
+		saved_scbptr = ahc_inb(ahc, SCBPTR);
+		ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+		scbid = ahc_inb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl));
+		ahc_outb(ahc, SCBPTR, saved_scbptr);
+	} else {
+		target_offset = TCL_TARGET_OFFSET(tcl);
+		scbid = ahc_inb(ahc, BUSY_TARGETS + target_offset);
+	}
+
+	return (scbid);
+}
+
+void
+ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+	u_int target_offset;
+
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		u_int saved_scbptr;
+		
+		saved_scbptr = ahc_inb(ahc, SCBPTR);
+		ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+		ahc_outb(ahc, SCB_64_BTT+TCL_TARGET_OFFSET(tcl), SCB_LIST_NULL);
+		ahc_outb(ahc, SCBPTR, saved_scbptr);
+	} else {
+		target_offset = TCL_TARGET_OFFSET(tcl);
+		ahc_outb(ahc, BUSY_TARGETS + target_offset, SCB_LIST_NULL);
+	}
+}
+
+void
+ahc_busy_tcl(struct ahc_softc *ahc, u_int tcl, u_int scbid)
+{
+	u_int target_offset;
+
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		u_int saved_scbptr;
+		
+		saved_scbptr = ahc_inb(ahc, SCBPTR);
+		ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+		ahc_outb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl), scbid);
+		ahc_outb(ahc, SCBPTR, saved_scbptr);
+	} else {
+		target_offset = TCL_TARGET_OFFSET(tcl);
+		ahc_outb(ahc, BUSY_TARGETS + target_offset, scbid);
+	}
+}
+
+/************************** SCB and SCB queue management **********************/
+int
+ahc_match_scb(struct ahc_softc *ahc, struct scb *scb, int target,
+	      char channel, int lun, u_int tag, role_t role)
+{
+	int targ = SCB_GET_TARGET(ahc, scb);
+	char chan = SCB_GET_CHANNEL(ahc, scb);
+	int slun = SCB_GET_LUN(scb);
+	int match;
+
+	match = ((chan == channel) || (channel == ALL_CHANNELS));
+	if (match != 0)
+		match = ((targ == target) || (target == CAM_TARGET_WILDCARD));
+	if (match != 0)
+		match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
+	if (match != 0) {
+#if AHC_TARGET_MODE
+		int group;
+
+		group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
+		if (role == ROLE_INITIATOR) {
+			match = (group != XPT_FC_GROUP_TMODE)
+			      && ((tag == scb->hscb->tag)
+			       || (tag == SCB_LIST_NULL));
+		} else if (role == ROLE_TARGET) {
+			match = (group == XPT_FC_GROUP_TMODE)
+			      && ((tag == scb->io_ctx->csio.tag_id)
+			       || (tag == SCB_LIST_NULL));
+		}
+#else /* !AHC_TARGET_MODE */
+		match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+#endif /* AHC_TARGET_MODE */
+	}
+
+	return match;
+}
+
+void
+ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+	int	target;
+	char	channel;
+	int	lun;
+
+	target = SCB_GET_TARGET(ahc, scb);
+	lun = SCB_GET_LUN(scb);
+	channel = SCB_GET_CHANNEL(ahc, scb);
+	
+	ahc_search_qinfifo(ahc, target, channel, lun,
+			   /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
+			   CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+	ahc_platform_freeze_devq(ahc, scb);
+}
+
+void
+ahc_qinfifo_requeue_tail(struct ahc_softc *ahc, struct scb *scb)
+{
+	struct scb *prev_scb;
+
+	prev_scb = NULL;
+	if (ahc_qinfifo_count(ahc) != 0) {
+		u_int prev_tag;
+		uint8_t prev_pos;
+
+		prev_pos = ahc->qinfifonext - 1;
+		prev_tag = ahc->qinfifo[prev_pos];
+		prev_scb = ahc_lookup_scb(ahc, prev_tag);
+	}
+	ahc_qinfifo_requeue(ahc, prev_scb, scb);
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+	} else {
+		ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+	}
+}
+
+static void
+ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
+		    struct scb *scb)
+{
+	if (prev_scb == NULL)
+		ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+	else
+		prev_scb->hscb->next = scb->hscb->tag;
+	ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+	scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+}
+
+static int
+ahc_qinfifo_count(struct ahc_softc *ahc)
+{
+	u_int8_t qinpos;
+	u_int8_t diff;
+
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		qinpos = ahc_inb(ahc, SNSCB_QOFF);
+		ahc_outb(ahc, SNSCB_QOFF, qinpos);
+	} else
+		qinpos = ahc_inb(ahc, QINPOS);
+	diff = ahc->qinfifonext - qinpos;
+	return (diff);
+}
+
+int
+ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
+		   int lun, u_int tag, role_t role, uint32_t status,
+		   ahc_search_action action)
+{
+	struct	scb *scb;
+	struct	scb *prev_scb;
+	uint8_t qinstart;
+	uint8_t qinpos;
+	uint8_t qintail;
+	uint8_t next, prev;
+	uint8_t curscbptr;
+	int	found;
+	int	maxtarget;
+	int	i;
+	int	have_qregs;
+
+	qintail = ahc->qinfifonext;
+	have_qregs = (ahc->features & AHC_QUEUE_REGS) != 0;
+	if (have_qregs) {
+		qinstart = ahc_inb(ahc, SNSCB_QOFF);
+		ahc_outb(ahc, SNSCB_QOFF, qinstart);
+	} else
+		qinstart = ahc_inb(ahc, QINPOS);
+	qinpos = qinstart;
+	next = ahc_inb(ahc, NEXT_QUEUED_SCB);
+	found = 0;
+	prev_scb = NULL;
+
+	if (action == SEARCH_COMPLETE) {
+		/*
+		 * Don't attempt to run any queued untagged transactions
+		 * until we are done with the abort process.
+		 */
+		ahc_freeze_untagged_queues(ahc);
+	}
+
+	/*
+	 * Start with an empty queue.  Entries that are not chosen
+	 * for removal will be re-added to the queue as we go.
+	 */
+	ahc->qinfifonext = qinpos;
+	ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+	while (qinpos != qintail) {
+		scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinpos]);
+		if (ahc_match_scb(ahc, scb, target, channel, lun, tag, role)) {
+			/*
+			 * We found an scb that needs to be acted on.
+			 */
+			found++;
+			switch (action) {
+			case SEARCH_COMPLETE:
+			{
+				cam_status ostat;
+				cam_status cstat;
+
+				ostat = ahc_get_transaction_status(scb);
+				if (ostat == CAM_REQ_INPROG)
+					ahc_set_transaction_status(scb,
+								   status);
+				cstat = ahc_get_transaction_status(scb);
+				if (cstat != CAM_REQ_CMP)
+					ahc_freeze_scb(scb);
+				if ((scb->flags & SCB_ACTIVE) == 0)
+					printf("Inactive SCB in qinfifo\n");
+				ahc_done(ahc, scb);
+
+				/* FALLTHROUGH */
+			case SEARCH_REMOVE:
+				break;
+			}
+			case SEARCH_COUNT:
+				ahc_qinfifo_requeue(ahc, prev_scb, scb);
+				prev_scb = scb;
+				break;
+			}
+		} else {
+			ahc_qinfifo_requeue(ahc, prev_scb, scb);
+			prev_scb = scb;
+		}
+		qinpos++;
+	}
+
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+	} else {
+		ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+	}
+
+	if (action != SEARCH_COUNT
+	 && (found != 0)
+	 && (qinstart != ahc->qinfifonext)) {
+		/*
+		 * The sequencer may be in the process of dmaing
+		 * down the SCB at the beginning of the queue.
+		 * This could be problematic if either the first,
+		 * or the second SCB is removed from the queue
+		 * (the first SCB includes a pointer to the "next"
+		 * SCB to dma). If we have removed any entries, swap
+		 * the first element in the queue with the next HSCB
+		 * so the sequencer will notice that NEXT_QUEUED_SCB
+		 * has changed during its dma attempt and will retry
+		 * the DMA.
+		 */
+		scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinstart]);
+
+		/*
+		 * ahc_swap_with_next_hscb forces our next pointer to
+		 * point to the reserved SCB for future commands.  Save
+		 * and restore our original next pointer to maintain
+		 * queue integrity.
+		 */
+		next = scb->hscb->next;
+		ahc->scb_data->scbindex[scb->hscb->tag] = NULL;
+		ahc_swap_with_next_hscb(ahc, scb);
+		scb->hscb->next = next;
+		ahc->qinfifo[qinstart] = scb->hscb->tag;
+
+		/* Tell the card about the new head of the qinfifo. */
+		ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+
+		/* Fixup the tail "next" pointer. */
+		qintail = ahc->qinfifonext - 1;
+		scb = ahc_lookup_scb(ahc, ahc->qinfifo[qintail]);
+		scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+	}
+
+	/*
+	 * Search waiting for selection list.
+	 */
+	curscbptr = ahc_inb(ahc, SCBPTR);
+	next = ahc_inb(ahc, WAITING_SCBH);  /* Start at head of list. */
+	prev = SCB_LIST_NULL;
+
+	while (next != SCB_LIST_NULL) {
+		uint8_t scb_index;
+
+		ahc_outb(ahc, SCBPTR, next);
+		scb_index = ahc_inb(ahc, SCB_TAG);
+		if (scb_index >= ahc->scb_data->numscbs) {
+			printf("Waiting List inconsistency. "
+			       "SCB index == %d, yet numscbs == %d.",
+			       scb_index, ahc->scb_data->numscbs);
+			ahc_dump_card_state(ahc);
+			panic("for safety");
+		}
+		scb = ahc_lookup_scb(ahc, scb_index);
+		if (ahc_match_scb(ahc, scb, target, channel,
+				  lun, SCB_LIST_NULL, role)) {
+			/*
+			 * We found an scb that needs to be acted on.
+			 */
+			found++;
+			switch (action) {
+			case SEARCH_COMPLETE:
+			{
+				cam_status ostat;
+				cam_status cstat;
+
+				ostat = ahc_get_transaction_status(scb);
+				if (ostat == CAM_REQ_INPROG)
+					ahc_set_transaction_status(scb,
+								   status);
+				cstat = ahc_get_transaction_status(scb);
+				if (cstat != CAM_REQ_CMP)
+					ahc_freeze_scb(scb);
+				if ((scb->flags & SCB_ACTIVE) == 0)
+					printf("Inactive SCB in Waiting List\n");
+				ahc_done(ahc, scb);
+				/* FALLTHROUGH */
+			}
+			case SEARCH_REMOVE:
+				next = ahc_rem_wscb(ahc, next, prev);
+				break;
+			case SEARCH_COUNT:
+				prev = next;
+				next = ahc_inb(ahc, SCB_NEXT);
+				break;
+			}
+		} else {
+			
+			prev = next;
+			next = ahc_inb(ahc, SCB_NEXT);
+		}
+	}
+	ahc_outb(ahc, SCBPTR, curscbptr);
+
+	/*
+	 * And lastly, the untagged holding queues.
+	 */
+	i = 0;
+	if ((ahc->flags & AHC_SCB_BTT) == 0) {
+
+		maxtarget = 16;
+		if (target != CAM_TARGET_WILDCARD) {
+
+			i = target;
+			if (channel == 'B')
+				i += 8;
+			maxtarget = i + 1;
+		}
+	} else {
+		maxtarget = 0;
+	}
+
+	for (; i < maxtarget; i++) {
+		struct scb_tailq *untagged_q;
+		struct scb *next_scb;
+
+		untagged_q = &(ahc->untagged_queues[i]);
+		next_scb = TAILQ_FIRST(untagged_q);
+		while (next_scb != NULL) {
+
+			scb = next_scb;
+			next_scb = TAILQ_NEXT(scb, links.tqe);
+
+			/*
+			 * The head of the list may be the currently
+			 * active untagged command for a device.
+			 * We're only searching for commands that
+			 * have not been started.  A transaction
+			 * marked active but still in the qinfifo
+			 * is removed by the qinfifo scanning code
+			 * above.
+			 */
+			if ((scb->flags & SCB_ACTIVE) != 0)
+				continue;
+
+			if (ahc_match_scb(ahc, scb, target, channel,
+					  lun, SCB_LIST_NULL, role)) {
+				/*
+				 * We found an scb that needs to be acted on.
+				 */
+				found++;
+				switch (action) {
+				case SEARCH_COMPLETE:
+				{
+					cam_status ostat;
+					cam_status cstat;
+
+					ostat = ahc_get_transaction_status(scb);
+					if (ostat == CAM_REQ_INPROG)
+						ahc_set_transaction_status(scb,
+								   status);
+					cstat = ahc_get_transaction_status(scb);
+					if (cstat != CAM_REQ_CMP)
+						ahc_freeze_scb(scb);
+					if ((scb->flags & SCB_ACTIVE) == 0)
+						printf("Inactive SCB in untaggedQ\n");
+					ahc_done(ahc, scb);
+					break;
+				}
+				case SEARCH_REMOVE:
+					TAILQ_REMOVE(untagged_q, scb,
+						     links.tqe);
+					break;
+				case SEARCH_COUNT:
+					break;
+				}
+			}
+		}
+	}
+
+	if (action == SEARCH_COMPLETE)
+		ahc_release_untagged_queues(ahc);
+	return (found);
+}
+
+int
+ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel,
+		     int lun, u_int tag, int stop_on_first, int remove,
+		     int save_state)
+{
+	struct	scb *scbp;
+	u_int	next;
+	u_int	prev;
+	u_int	count;
+	u_int	active_scb;
+
+	count = 0;
+	next = ahc_inb(ahc, DISCONNECTED_SCBH);
+	prev = SCB_LIST_NULL;
+
+	if (save_state) {
+		/* restore this when we're done */
+		active_scb = ahc_inb(ahc, SCBPTR);
+	} else
+		/* Silence compiler */
+		active_scb = SCB_LIST_NULL;
+
+	while (next != SCB_LIST_NULL) {
+		u_int scb_index;
+
+		ahc_outb(ahc, SCBPTR, next);
+		scb_index = ahc_inb(ahc, SCB_TAG);
+		if (scb_index >= ahc->scb_data->numscbs) {
+			printf("Disconnected List inconsistency. "
+			       "SCB index == %d, yet numscbs == %d.",
+			       scb_index, ahc->scb_data->numscbs);
+			ahc_dump_card_state(ahc);
+			panic("for safety");
+		}
+
+		if (next == prev) {
+			panic("Disconnected List Loop. "
+			      "cur SCBPTR == %x, prev SCBPTR == %x.",
+			      next, prev);
+		}
+		scbp = ahc_lookup_scb(ahc, scb_index);
+		if (ahc_match_scb(ahc, scbp, target, channel, lun,
+				  tag, ROLE_INITIATOR)) {
+			count++;
+			if (remove) {
+				next =
+				    ahc_rem_scb_from_disc_list(ahc, prev, next);
+			} else {
+				prev = next;
+				next = ahc_inb(ahc, SCB_NEXT);
+			}
+			if (stop_on_first)
+				break;
+		} else {
+			prev = next;
+			next = ahc_inb(ahc, SCB_NEXT);
+		}
+	}
+	if (save_state)
+		ahc_outb(ahc, SCBPTR, active_scb);
+	return (count);
+}
+
+/*
+ * Remove an SCB from the on chip list of disconnected transactions.
+ * This is empty/unused if we are not performing SCB paging.
+ */
+static u_int
+ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int prev, u_int scbptr)
+{
+	u_int next;
+
+	ahc_outb(ahc, SCBPTR, scbptr);
+	next = ahc_inb(ahc, SCB_NEXT);
+
+	ahc_outb(ahc, SCB_CONTROL, 0);
+
+	ahc_add_curscb_to_free_list(ahc);
+
+	if (prev != SCB_LIST_NULL) {
+		ahc_outb(ahc, SCBPTR, prev);
+		ahc_outb(ahc, SCB_NEXT, next);
+	} else
+		ahc_outb(ahc, DISCONNECTED_SCBH, next);
+
+	return (next);
+}
+
+/*
+ * Add the SCB as selected by SCBPTR onto the on chip list of
+ * free hardware SCBs.  This list is empty/unused if we are not
+ * performing SCB paging.
+ */
+static void
+ahc_add_curscb_to_free_list(struct ahc_softc *ahc)
+{
+	/*
+	 * Invalidate the tag so that our abort
+	 * routines don't think it's active.
+	 */
+	ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH));
+		ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR));
+	}
+}
+
+/*
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ */
+static u_int
+ahc_rem_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
+{       
+	u_int curscb, next;
+
+	/*
+	 * Select the SCB we want to abort and
+	 * pull the next pointer out of it.
+	 */
+	curscb = ahc_inb(ahc, SCBPTR);
+	ahc_outb(ahc, SCBPTR, scbpos);
+	next = ahc_inb(ahc, SCB_NEXT);
+
+	/* Clear the necessary fields */
+	ahc_outb(ahc, SCB_CONTROL, 0);
+
+	ahc_add_curscb_to_free_list(ahc);
+
+	/* update the waiting list */
+	if (prev == SCB_LIST_NULL) {
+		/* First in the list */
+		ahc_outb(ahc, WAITING_SCBH, next); 
+
+		/*
+		 * Ensure we aren't attempting to perform
+		 * selection for this entry.
+		 */
+		ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+	} else {
+		/*
+		 * Select the scb that pointed to us 
+		 * and update its next pointer.
+		 */
+		ahc_outb(ahc, SCBPTR, prev);
+		ahc_outb(ahc, SCB_NEXT, next);
+	}
+
+	/*
+	 * Point us back at the original scb position.
+	 */
+	ahc_outb(ahc, SCBPTR, curscb);
+	return next;
+}
+
+/******************************** Error Handling ******************************/
+/*
+ * Abort all SCBs that match the given description (target/channel/lun/tag),
+ * setting their status to the passed in status if the status has not already
+ * been modified from CAM_REQ_INPROG.  This routine assumes that the sequencer
+ * is paused before it is called.
+ */
+int
+ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+	       int lun, u_int tag, role_t role, uint32_t status)
+{
+	struct	scb *scbp;
+	struct	scb *scbp_next;
+	u_int	active_scb;
+	int	i, j;
+	int	maxtarget;
+	int	minlun;
+	int	maxlun;
+
+	int	found;
+
+	/*
+	 * Don't attempt to run any queued untagged transactions
+	 * until we are done with the abort process.
+	 */
+	ahc_freeze_untagged_queues(ahc);
+
+	/* restore this when we're done */
+	active_scb = ahc_inb(ahc, SCBPTR);
+
+	found = ahc_search_qinfifo(ahc, target, channel, lun, SCB_LIST_NULL,
+				   role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+	/*
+	 * Clean out the busy target table for any untagged commands.
+	 */
+	i = 0;
+	maxtarget = 16;
+	if (target != CAM_TARGET_WILDCARD) {
+		i = target;
+		if (channel == 'B')
+			i += 8;
+		maxtarget = i + 1;
+	}
+
+	if (lun == CAM_LUN_WILDCARD) {
+
+		/*
+		 * Unless we are using an SCB based
+		 * busy targets table, there is only
+		 * one table entry for all luns of
+		 * a target.
+		 */
+		minlun = 0;
+		maxlun = 1;
+		if ((ahc->flags & AHC_SCB_BTT) != 0)
+			maxlun = AHC_NUM_LUNS;
+	} else {
+		minlun = lun;
+		maxlun = lun + 1;
+	}
+
+	for (;i < maxtarget; i++) {
+		for (j = minlun;j < maxlun; j++)
+			ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, j));
+	}
+
+	/*
+	 * Go through the disconnected list and remove any entries we
+	 * have queued for completion, 0'ing their control byte too.
+	 * We save the active SCB and restore it ourselves, so there
+	 * is no reason for this search to restore it too.
+	 */
+	ahc_search_disc_list(ahc, target, channel, lun, tag,
+			     /*stop_on_first*/FALSE, /*remove*/TRUE,
+			     /*save_state*/FALSE);
+
+	/*
+	 * Go through the hardware SCB array looking for commands that
+	 * were active but not on any list.
+	 */
+	for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+		u_int scbid;
+
+		ahc_outb(ahc, SCBPTR, i);
+		scbid = ahc_inb(ahc, SCB_TAG);
+		scbp = ahc_lookup_scb(ahc, scbid);
+		if (scbp != NULL
+		 && ahc_match_scb(ahc, scbp, target, channel, lun, tag, role))
+			ahc_add_curscb_to_free_list(ahc);
+	}
+
+	/*
+	 * Go through the pending CCB list and look for
+	 * commands for this target that are still active.
+	 * These are other tagged commands that were
+	 * disconnected when the reset occured.
+	 */
+	scbp_next = LIST_FIRST(&ahc->pending_scbs);
+	while (scbp_next != NULL) {
+		scbp = scbp_next;
+		scbp_next = LIST_NEXT(scbp, pending_links);
+		if (ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)) {
+			cam_status ostat;
+
+			ostat = ahc_get_transaction_status(scbp);
+			if (ostat == CAM_REQ_INPROG)
+				ahc_set_transaction_status(scbp, status);
+			if (ahc_get_transaction_status(scbp) != CAM_REQ_CMP)
+				ahc_freeze_scb(scbp);
+			if ((scbp->flags & SCB_ACTIVE) == 0)
+				printf("Inactive SCB on pending list\n");
+			ahc_done(ahc, scbp);
+			found++;
+		}
+	}
+	ahc_outb(ahc, SCBPTR, active_scb);
+	ahc_platform_abort_scbs(ahc, target, channel, lun, tag, role, status);
+	ahc_release_untagged_queues(ahc);
+	return found;
+}
+
+static void
+ahc_reset_current_bus(struct ahc_softc *ahc)
+{
+	uint8_t scsiseq;
+
+	ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENSCSIRST);
+	scsiseq = ahc_inb(ahc, SCSISEQ);
+	ahc_outb(ahc, SCSISEQ, scsiseq | SCSIRSTO);
+	ahc_delay(AHC_BUSRESET_DELAY);
+	/* Turn off the bus reset */
+	ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO);
+
+	ahc_clear_intstat(ahc);
+
+	/* Re-enable reset interrupts */
+	ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST);
+}
+
+int
+ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
+{
+	struct	ahc_devinfo devinfo;
+	u_int	initiator, target, max_scsiid;
+	u_int	sblkctl;
+	int	found;
+	int	restart_needed;
+	char	cur_channel;
+
+	ahc->pending_device = NULL;
+
+	ahc_compile_devinfo(&devinfo,
+			    CAM_TARGET_WILDCARD,
+			    CAM_TARGET_WILDCARD,
+			    CAM_LUN_WILDCARD,
+			    channel, ROLE_UNKNOWN);
+	pause_sequencer(ahc);
+
+	/* Make sure the sequencer is in a safe location. */
+	ahc_clear_critical_section(ahc);
+
+	/*
+	 * Run our command complete fifos to ensure that we perform
+	 * completion processing on any commands that 'completed'
+	 * before the reset occurred.
+	 */
+	ahc_run_qoutfifo(ahc);
+#if AHC_TARGET_MODE
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		ahc_run_tqinfifo(ahc, /*paused*/TRUE);
+	}
+#endif
+
+	/*
+	 * Reset the bus if we are initiating this reset
+	 */
+	sblkctl = ahc_inb(ahc, SBLKCTL);
+	cur_channel = 'A';
+	if ((ahc->features & AHC_TWIN) != 0
+	 && ((sblkctl & SELBUSB) != 0))
+	    cur_channel = 'B';
+	if (cur_channel != channel) {
+		/* Case 1: Command for another bus is active
+		 * Stealthily reset the other bus without
+		 * upsetting the current bus.
+		 */
+		ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB);
+		ahc_outb(ahc, SIMODE1,
+			 ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST));
+		ahc_outb(ahc, SCSISEQ,
+			 ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+		if (initiate_reset)
+			ahc_reset_current_bus(ahc);
+		ahc_clear_intstat(ahc);
+		ahc_outb(ahc, SBLKCTL, sblkctl);
+		restart_needed = FALSE;
+	} else {
+		/* Case 2: A command from this bus is active or we're idle */
+		ahc_clear_msg_state(ahc);
+		ahc_outb(ahc, SIMODE1,
+			 ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST));
+		ahc_outb(ahc, SCSISEQ,
+			 ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+		if (initiate_reset)
+			ahc_reset_current_bus(ahc);
+		ahc_clear_intstat(ahc);
+		restart_needed = TRUE;
+	}
+
+	/*
+	 * Clean up all the state information for the
+	 * pending transactions on this bus.
+	 */
+	found = ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, channel,
+			       CAM_LUN_WILDCARD, SCB_LIST_NULL,
+			       ROLE_UNKNOWN, CAM_SCSI_BUS_RESET);
+
+	max_scsiid = (ahc->features & AHC_WIDE) ? 15 : 7;
+
+#ifdef AHC_TARGET_MODE
+	/*
+	 * Send an immediate notify ccb to all target more peripheral
+	 * drivers affected by this action.
+	 */
+	for (target = 0; target <= max_scsiid; target++) {
+		struct tmode_tstate* tstate;
+		u_int lun;
+
+		tstate = ahc->enabled_targets[target];
+		if (tstate == NULL)
+			continue;
+		for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+			struct tmode_lstate* lstate;
+
+			lstate = tstate->enabled_luns[lun];
+			if (lstate == NULL)
+				continue;
+
+			ahc_queue_lstate_event(ahc, lstate, CAM_TARGET_WILDCARD,
+					       EVENT_TYPE_BUS_RESET, /*arg*/0);
+			ahc_send_lstate_events(ahc, lstate);
+		}
+	}
+#endif
+	/* Notify the XPT that a bus reset occurred */
+	ahc_send_async(ahc, devinfo.channel, CAM_TARGET_WILDCARD,
+		       CAM_LUN_WILDCARD, AC_BUS_RESET);
+
+	/*
+	 * Revert to async/narrow transfers until we renegotiate.
+	 */
+	for (target = 0; target <= max_scsiid; target++) {
+
+		if (ahc->enabled_targets[target] == NULL)
+			continue;
+		for (initiator = 0; initiator <= max_scsiid; initiator++) {
+			struct ahc_devinfo devinfo;
+
+			ahc_compile_devinfo(&devinfo, target, initiator,
+					    CAM_LUN_WILDCARD,
+					    channel, ROLE_UNKNOWN);
+			ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+				      AHC_TRANS_CUR, /*paused*/TRUE);
+			ahc_set_syncrate(ahc, &devinfo, /*syncrate*/NULL,
+					 /*period*/0, /*offset*/0,
+					 /*ppr_options*/0, AHC_TRANS_CUR,
+					 /*paused*/TRUE);
+		}
+	}
+
+	if (restart_needed)
+		restart_sequencer(ahc);
+	else
+		unpause_sequencer(ahc);
+	return found;
+}
+
+
+/***************************** Residual Processing ****************************/
+/*
+ * Calculate the residual for a just completed SCB.
+ */
+static void
+ahc_calc_residual(struct scb *scb)
+{
+	struct hardware_scb *hscb;
+	struct status_pkt *spkt;
+	uint32_t sgptr;
+	uint32_t resid_sgptr;
+	uint32_t resid;
+
+	/*
+	 * 5 cases.
+	 * 1) No residual.
+	 *    SG_RESID_VALID clear in sgptr.
+	 * 2) Transferless command
+	 * 3) Never performed any transfers.
+	 *    sgptr has SG_FULL_RESID set.
+	 * 4) No residual but target did not
+	 *    save data pointers after the
+	 *    last transfer, so sgptr was
+	 *    never updated.
+	 * 5) We have a partial residual.
+	 *    Use residual_sgptr to determine
+	 *    where we are.
+	 */
+
+	hscb = scb->hscb;
+	sgptr = ahc_le32toh(hscb->sgptr);
+	if ((sgptr & SG_RESID_VALID) == 0)
+		/* Case 1 */
+		return;
+	sgptr &= ~SG_RESID_VALID;
+
+	if ((sgptr & SG_LIST_NULL) != 0)
+		/* Case 2 */
+		return;
+
+	spkt = &hscb->shared_data.status;
+	resid_sgptr = ahc_le32toh(spkt->residual_sg_ptr);
+	if ((sgptr & SG_FULL_RESID) != 0) {
+		/* Case 3 */
+		resid = ahc_get_transfer_length(scb);
+	} else if ((resid_sgptr & SG_LIST_NULL) != 0) {
+		/* Case 4 */
+		return;
+	} else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {
+		panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);
+	} else {
+		struct ahc_dma_seg *sg;
+
+		/*
+		 * Remainder of the SG where the transfer
+		 * stopped.  
+		 */
+		resid = ahc_le32toh(spkt->residual_datacnt) & AHC_SG_LEN_MASK;
+		sg = ahc_sg_bus_to_virt(scb, resid_sgptr & SG_PTR_MASK);
+
+		/* The residual sg_ptr always points to the next sg */
+		sg--;
+
+		/*
+		 * Add up the contents of all residual
+		 * SG segments that are after the SG where
+		 * the transfer stopped.
+		 */
+		while ((ahc_le32toh(sg->len) & AHC_DMA_LAST_SEG) == 0) {
+			sg++;
+			resid += ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+		}
+	}
+	if ((scb->flags & SCB_SENSE) == 0)
+		ahc_set_residual(scb, resid);
+	else
+		ahc_set_sense_residual(scb, resid);
+
+#ifdef AHC_DEBUG
+	if (ahc_debug & AHC_SHOWMISC) {
+		ahc_print_path(ahc, scb);
+		printf("Handled Residual of %d bytes\n", resid);
+	}
+#endif
+}
+
+/******************************* Target Mode **********************************/
+#ifdef AHC_TARGET_MODE
+/*
+ * Add a target mode event to this lun's queue
+ */
+static void
+ahc_queue_lstate_event(struct ahc_softc *ahc, struct tmode_lstate *lstate,
+		       u_int initiator_id, u_int event_type, u_int event_arg)
+{
+	struct ahc_tmode_event *event;
+	int pending;
+
+	xpt_freeze_devq(lstate->path, /*count*/1);
+	if (lstate->event_w_idx >= lstate->event_r_idx)
+		pending = lstate->event_w_idx - lstate->event_r_idx;
+	else
+		pending = AHC_TMODE_EVENT_BUFFER_SIZE + 1
+			- (lstate->event_r_idx - lstate->event_w_idx);
+
+	if (event_type == EVENT_TYPE_BUS_RESET
+	 || event_type == MSG_BUS_DEV_RESET) {
+		/*
+		 * Any earlier events are irrelevant, so reset our buffer.
+		 * This has the effect of allowing us to deal with reset
+		 * floods (an external device holding down the reset line)
+		 * without losing the event that is really interesting.
+		 */
+		lstate->event_r_idx = 0;
+		lstate->event_w_idx = 0;
+		xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE);
+	}
+
+	if (pending == AHC_TMODE_EVENT_BUFFER_SIZE) {
+		xpt_print_path(lstate->path);
+		printf("immediate event %x:%x lost\n",
+		       lstate->event_buffer[lstate->event_r_idx].event_type,
+		       lstate->event_buffer[lstate->event_r_idx].event_arg);
+		lstate->event_r_idx++;
+		if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+			lstate->event_r_idx = 0;
+		xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE);
+	}
+
+	event = &lstate->event_buffer[lstate->event_w_idx];
+	event->initiator_id = initiator_id;
+	event->event_type = event_type;
+	event->event_arg = event_arg;
+	lstate->event_w_idx++;
+	if (lstate->event_w_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+		lstate->event_w_idx = 0;
+}
+
+/*
+ * Send any target mode events queued up waiting
+ * for immediate notify resources.
+ */
+void
+ahc_send_lstate_events(struct ahc_softc *ahc, struct tmode_lstate *lstate)
+{
+	struct ccb_hdr *ccbh;
+	struct ccb_immed_notify *inot;
+
+	while (lstate->event_r_idx != lstate->event_w_idx
+	    && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
+		struct ahc_tmode_event *event;
+
+		event = &lstate->event_buffer[lstate->event_r_idx];
+		SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
+		inot = (struct ccb_immed_notify *)ccbh;
+		switch (event->event_type) {
+		case EVENT_TYPE_BUS_RESET:
+			ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
+			break;
+		default:
+			ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
+			inot->message_args[0] = event->event_type;
+			inot->message_args[1] = event->event_arg;
+			break;
+		}
+		inot->initiator_id = event->initiator_id;
+		inot->sense_len = 0;
+		xpt_done((union ccb *)inot);
+		lstate->event_r_idx++;
+		if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+			lstate->event_r_idx = 0;
+	}
+}
+#endif
+
+/******************** Sequencer Program Patching/Download *********************/
+
+#ifdef AHC_DUMP_SEQ
+void
+ahc_dumpseq(struct ahc_softc* ahc)
+{
+	int i;
+	int max_prog;
+
+	if ((ahc->chip & AHC_BUS_MASK) < AHC_PCI)
+		max_prog = 448;
+	else if ((ahc->features & AHC_ULTRA2) != 0)
+		max_prog = 768;
+	else
+		max_prog = 512;
+
+	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+	ahc_outb(ahc, SEQADDR0, 0);
+	ahc_outb(ahc, SEQADDR1, 0);
+	for (i = 0; i < max_prog; i++) {
+		uint8_t ins_bytes[4];
+
+		ahc_insb(ahc, SEQRAM, ins_bytes, 4);
+		printf("0x%08x\n", ins_bytes[0] << 24
+				 | ins_bytes[1] << 16
+				 | ins_bytes[2] << 8
+				 | ins_bytes[3]);
+	}
+}
+#endif
+
+static void
+ahc_loadseq(struct ahc_softc *ahc)
+{
+	struct	cs cs_table[num_critical_sections];
+	u_int	begin_set[num_critical_sections];
+	u_int	end_set[num_critical_sections];
+	struct	patch *cur_patch;
+	u_int	cs_count;
+	u_int	cur_cs;
+	u_int	i;
+	int	downloaded;
+	u_int	skip_addr;
+	u_int	sg_prefetch_cnt;
+	uint8_t	download_consts[7];
+
+	/*
+	 * Start out with 0 critical sections
+	 * that apply to this firmware load.
+	 */
+	cs_count = 0;
+	cur_cs = 0;
+	memset(begin_set, 0, sizeof(begin_set));
+	memset(end_set, 0, sizeof(end_set));
+
+	/* Setup downloadable constant table */
+	download_consts[QOUTFIFO_OFFSET] = 0;
+	if (ahc->targetcmds != NULL)
+		download_consts[QOUTFIFO_OFFSET] += 32;
+	download_consts[QINFIFO_OFFSET] = download_consts[QOUTFIFO_OFFSET] + 1;
+	download_consts[CACHESIZE_MASK] = ahc->pci_cachesize - 1;
+	download_consts[INVERTED_CACHESIZE_MASK] = ~(ahc->pci_cachesize - 1);
+	sg_prefetch_cnt = ahc->pci_cachesize;
+	if (sg_prefetch_cnt < (2 * sizeof(struct ahc_dma_seg)))
+		sg_prefetch_cnt = 2 * sizeof(struct ahc_dma_seg);
+	download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt;
+	download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_cnt - 1);
+	download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_cnt - 1);
+
+	cur_patch = patches;
+	downloaded = 0;
+	skip_addr = 0;
+	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+	ahc_outb(ahc, SEQADDR0, 0);
+	ahc_outb(ahc, SEQADDR1, 0);
+
+	for (i = 0; i < sizeof(seqprog)/4; i++) {
+		if (ahc_check_patch(ahc, &cur_patch, i, &skip_addr) == 0) {
+			/*
+			 * Don't download this instruction as it
+			 * is in a patch that was removed.
+			 */
+			continue;
+		}
+		/*
+		 * Move through the CS table until we find a CS
+		 * that might apply to this instruction.
+		 */
+		for (; cur_cs < num_critical_sections; cur_cs++) {
+			if (critical_sections[cur_cs].end <= i) {
+				if (begin_set[cs_count] == TRUE
+				 && end_set[cs_count] == FALSE) {
+					cs_table[cs_count].end = downloaded;
+				 	end_set[cs_count] = TRUE;
+					cs_count++;
+				}
+				continue;
+			}
+			if (critical_sections[cur_cs].begin <= i
+			 && begin_set[cs_count] == FALSE) {
+				cs_table[cs_count].begin = downloaded;
+				begin_set[cs_count] = TRUE;
+			}
+			break;
+		}
+		ahc_download_instr(ahc, i, download_consts);
+		downloaded++;
+	}
+
+	ahc->num_critical_sections = cs_count;
+	if (cs_count != 0) {
+
+		cs_count *= sizeof(struct cs);
+		ahc->critical_sections = malloc(cs_count, M_DEVBUF, M_NOWAIT);
+		if (ahc->critical_sections == NULL)
+			panic("ahc_loadseq: Could not malloc");
+		memcpy(ahc->critical_sections, cs_table, cs_count);
+	}
+	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
+	restart_sequencer(ahc);
+
+	if (bootverbose)
+		printf(" %d instructions downloaded\n", downloaded);
+}
+
+static int
+ahc_check_patch(struct ahc_softc *ahc, struct patch **start_patch,
+		u_int start_instr, u_int *skip_addr)
+{
+	struct	patch *cur_patch;
+	struct	patch *last_patch;
+	u_int	num_patches;
+
+	num_patches = sizeof(patches)/sizeof(struct patch);
+	last_patch = &patches[num_patches];
+	cur_patch = *start_patch;
+
+	while (cur_patch < last_patch && start_instr == cur_patch->begin) {
+
+		if (cur_patch->patch_func(ahc) == 0) {
+
+			/* Start rejecting code */
+			*skip_addr = start_instr + cur_patch->skip_instr;
+			cur_patch += cur_patch->skip_patch;
+		} else {
+			/* Accepted this patch.  Advance to the next
+			 * one and wait for our intruction pointer to
+			 * hit this point.
+			 */
+			cur_patch++;
+		}
+	}
+
+	*start_patch = cur_patch;
+	if (start_instr < *skip_addr)
+		/* Still skipping */
+		return (0);
+
+	return (1);
+}
+
+static void
+ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
+{
+	union	ins_formats instr;
+	struct	ins_format1 *fmt1_ins;
+	struct	ins_format3 *fmt3_ins;
+	u_int	opcode;
+
+	/*
+	 * The firmware is always compiled into a little endian format.
+	 */
+	instr.integer = ahc_le32toh(*(uint32_t*)&seqprog[instrptr * 4]);
+
+	fmt1_ins = &instr.format1;
+	fmt3_ins = NULL;
+
+	/* Pull the opcode */
+	opcode = instr.format1.opcode;
+	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:
+	{
+		struct patch *cur_patch;
+		int address_offset;
+		u_int address;
+		u_int skip_addr;
+		u_int i;
+
+		fmt3_ins = &instr.format3;
+		address_offset = 0;
+		address = fmt3_ins->address;
+		cur_patch = patches;
+		skip_addr = 0;
+
+		for (i = 0; i < address;) {
+
+			ahc_check_patch(ahc, &cur_patch, i, &skip_addr);
+
+			if (skip_addr > i) {
+				int end_addr;
+
+				end_addr = MIN(address, skip_addr);
+				address_offset += end_addr - i;
+				i = skip_addr;
+			} else {
+				i++;
+			}
+		}
+		address -= address_offset;
+		fmt3_ins->address = address;
+		/* FALLTHROUGH */
+	}
+	case AIC_OP_OR:
+	case AIC_OP_AND:
+	case AIC_OP_XOR:
+	case AIC_OP_ADD:
+	case AIC_OP_ADC:
+	case AIC_OP_BMOV:
+		if (fmt1_ins->parity != 0) {
+			fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+		}
+		fmt1_ins->parity = 0;
+		if ((ahc->features & AHC_CMD_CHAN) == 0
+		 && opcode == AIC_OP_BMOV) {
+			/*
+			 * Block move was added at the same time
+			 * as the command channel.  Verify that
+			 * this is only a move of a single element
+			 * and convert the BMOV to a MOV
+			 * (AND with an immediate of FF).
+			 */
+			if (fmt1_ins->immediate != 1)
+				panic("%s: BMOV not supported\n",
+				      ahc_name(ahc));
+			fmt1_ins->opcode = AIC_OP_AND;
+			fmt1_ins->immediate = 0xff;
+		}
+		/* FALLTHROUGH */
+	case AIC_OP_ROL:
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+			int i, count;
+
+			/* Calculate odd parity for the instruction */
+			for (i = 0, count = 0; i < 31; i++) {
+				uint32_t mask;
+
+				mask = 0x01 << i;
+				if ((instr.integer & mask) != 0)
+					count++;
+			}
+			if ((count & 0x01) == 0)
+				instr.format1.parity = 1;
+		} else {
+			/* Compress the instruction for older sequencers */
+			if (fmt3_ins != NULL) {
+				instr.integer =
+					fmt3_ins->immediate
+				      | (fmt3_ins->source << 8)
+				      | (fmt3_ins->address << 16)
+				      |	(fmt3_ins->opcode << 25);
+			} else {
+				instr.integer =
+					fmt1_ins->immediate
+				      | (fmt1_ins->source << 8)
+				      | (fmt1_ins->destination << 16)
+				      |	(fmt1_ins->ret << 24)
+				      |	(fmt1_ins->opcode << 25);
+			}
+		}
+		/* The sequencer is a little endian cpu */
+		instr.integer = ahc_htole32(instr.integer);
+		ahc_outsb(ahc, SEQRAM, instr.bytes, 4);
+		break;
+	default:
+		panic("Unknown opcode encountered in seq program");
+		break;
+	}
+}
+
+void
+ahc_dump_card_state(struct ahc_softc *ahc)
+{
+	struct scb *scb;
+	struct scb_tailq *untagged_q;
+	int target;
+	int maxtarget;
+	int i;
+	uint8_t qinpos;
+	uint8_t qintail;
+	uint8_t qoutpos;
+	uint8_t scb_index;
+	uint8_t saved_scbptr;
+
+	saved_scbptr = ahc_inb(ahc, SCBPTR);
+
+	printf("%s: Dumping Card State at SEQADDR 0x%x\n",
+	       ahc_name(ahc),
+	       ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+
+	printf("SCSISEQ = 0x%x, SBLKCTL = 0x%x, SSTAT0 0x%x\n",
+	       ahc_inb(ahc, SCSISEQ), ahc_inb(ahc, SBLKCTL),
+	       ahc_inb(ahc, SSTAT0));
+	printf("SCB count = %d\n", ahc->scb_data->numscbs);
+	printf("Kernel NEXTQSCB = %d\n", ahc->next_queued_scb->hscb->tag);
+	printf("Card NEXTQSCB = %d\n", ahc_inb(ahc, NEXT_QUEUED_SCB));
+	/* QINFIFO */
+	printf("QINFIFO entries: ");
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		qinpos = ahc_inb(ahc, SNSCB_QOFF);
+		ahc_outb(ahc, SNSCB_QOFF, qinpos);
+	} else
+		qinpos = ahc_inb(ahc, QINPOS);
+	qintail = ahc->qinfifonext;
+	while (qinpos != qintail) {
+		printf("%d ", ahc->qinfifo[qinpos]);
+		qinpos++;
+	}
+	printf("\n");
+
+	printf("Waiting Queue entries: ");
+	scb_index = ahc_inb(ahc, WAITING_SCBH);
+	i = 0;
+	while (scb_index != SCB_LIST_NULL && i++ < 256) {
+		ahc_outb(ahc, SCBPTR, scb_index);
+		printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+		scb_index = ahc_inb(ahc, SCB_NEXT);
+	}
+	printf("\n");
+
+	printf("Disconnected Queue entries: ");
+	scb_index = ahc_inb(ahc, DISCONNECTED_SCBH);
+	i = 0;
+	while (scb_index != SCB_LIST_NULL && i++ < 256) {
+		ahc_outb(ahc, SCBPTR, scb_index);
+		printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+		scb_index = ahc_inb(ahc, SCB_NEXT);
+	}
+	printf("\n");
+		
+	printf("QOUTFIFO entries: ");
+	qoutpos = ahc->qoutfifonext;
+	i = 0;
+	while (ahc->qoutfifo[qoutpos] != SCB_LIST_NULL && i++ < 256) {
+		printf("%d ", ahc->qoutfifo[qoutpos]);
+		qoutpos++;
+	}
+	printf("\n");
+
+	printf("Sequencer Free SCB List: ");
+	scb_index = ahc_inb(ahc, FREE_SCBH);
+	i = 0;
+	while (scb_index != SCB_LIST_NULL && i++ < 256) {
+		ahc_outb(ahc, SCBPTR, scb_index);
+		printf("%d ", scb_index);
+		scb_index = ahc_inb(ahc, SCB_NEXT);
+	}
+	printf("\n");
+
+	printf("Pending list: ");
+	i = 0;
+	LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+		if (i++ > 256)
+			break;
+		printf("%d ", scb->hscb->tag);
+	}
+	printf("\n");
+
+	printf("Kernel Free SCB list: ");
+	i = 0;
+	SLIST_FOREACH(scb, &ahc->scb_data->free_scbs, links.sle) {
+		if (i++ > 256)
+			break;
+		printf("%d ", scb->hscb->tag);
+	}
+	printf("\n");
+
+	maxtarget = (ahc->features & (AHC_WIDE|AHC_TWIN)) ? 15 : 7;
+	for (target = 0; target <= maxtarget; target++) {
+		untagged_q = &ahc->untagged_queues[target];
+		if (TAILQ_FIRST(untagged_q) == NULL)
+			continue;
+		printf("Untagged Q(%d): ", target);
+		i = 0;
+		TAILQ_FOREACH(scb, untagged_q, links.tqe) {
+			if (i++ > 256)
+				break;
+			printf("%d ", scb->hscb->tag);
+		}
+		printf("\n");
+	}
+
+	ahc_platform_dump_card_state(ahc);
+	ahc_outb(ahc, SCBPTR, saved_scbptr);
+}
+
+/************************* Target Mode ****************************************/
+#ifdef AHC_TARGET_MODE
+cam_status
+ahc_find_tmode_devs(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb,
+		    struct tmode_tstate **tstate, struct tmode_lstate **lstate,
+		    int notfound_failure)
+{
+
+	if ((ahc->features & AHC_TARGETMODE) == 0)
+		return (CAM_REQ_INVALID);
+
+	/*
+	 * Handle the 'black hole' device that sucks up
+	 * requests to unattached luns on enabled targets.
+	 */
+	if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
+	 && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+		*tstate = NULL;
+		*lstate = ahc->black_hole;
+	} else {
+		u_int max_id;
+
+		max_id = (ahc->features & AHC_WIDE) ? 15 : 7;
+		if (ccb->ccb_h.target_id > max_id)
+			return (CAM_TID_INVALID);
+
+		if (ccb->ccb_h.target_lun >= AHC_NUM_LUNS)
+			return (CAM_LUN_INVALID);
+
+		*tstate = ahc->enabled_targets[ccb->ccb_h.target_id];
+		*lstate = NULL;
+		if (*tstate != NULL)
+			*lstate =
+			    (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+	}
+
+	if (notfound_failure != 0 && *lstate == NULL)
+		return (CAM_PATH_INVALID);
+
+	return (CAM_REQ_CMP);
+}
+
+void
+ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
+{
+	struct	   tmode_tstate *tstate;
+	struct	   tmode_lstate *lstate;
+	struct	   ccb_en_lun *cel;
+	cam_status status;
+	u_int	   target;
+	u_int	   lun;
+	u_int	   target_mask;
+	u_long	   s;
+	char	   channel;
+
+	status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate,
+				     /*notfound_failure*/FALSE);
+
+	if (status != CAM_REQ_CMP) {
+		ccb->ccb_h.status = status;
+		return;
+	}
+
+	if ((ahc->features & AHC_MULTIROLE) != 0) {
+		u_int	   our_id;
+
+		if (cam_sim_bus(sim) == 0)
+			our_id = ahc->our_id;
+		else
+			our_id = ahc->our_id_b;
+
+		if (ccb->ccb_h.target_id != our_id) {
+			if ((ahc->features & AHC_MULTI_TID) != 0
+		   	 && (ahc->flags & AHC_INITIATORROLE) != 0) {
+				/*
+				 * Only allow additional targets if
+				 * the initiator role is disabled.
+				 * The hardware cannot handle a re-select-in
+				 * on the initiator id during a re-select-out
+				 * on a different target id.
+				 */
+				status = CAM_TID_INVALID;
+			} else if ((ahc->flags & AHC_INITIATORROLE) != 0
+				|| ahc->enabled_luns > 0) {
+				/*
+				 * Only allow our target id to change
+				 * if the initiator role is not configured
+				 * and there are no enabled luns which
+				 * are attached to the currently registered
+				 * scsi id.
+				 */
+				status = CAM_TID_INVALID;
+			}
+		}
+	}
+
+	if (status != CAM_REQ_CMP) {
+		ccb->ccb_h.status = status;
+		return;
+	}
+
+	/*
+	 * We now have an id that is valid.
+	 * If we aren't in target mode, switch modes.
+	 */
+	if ((ahc->flags & AHC_TARGETROLE) == 0
+	 && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+		u_long	s;
+
+		printf("Configuring Target Mode\n");
+		ahc_lock(ahc, &s);
+		if (LIST_FIRST(&ahc->pending_scbs) != NULL) {
+			ccb->ccb_h.status = CAM_BUSY;
+			ahc_unlock(ahc, &s);
+			return;
+		}
+		ahc->flags |= AHC_TARGETROLE;
+		if ((ahc->features & AHC_MULTIROLE) == 0)
+			ahc->flags &= ~AHC_INITIATORROLE;
+		pause_sequencer(ahc);
+		ahc_loadseq(ahc);
+		ahc_unlock(ahc, &s);
+	}
+	cel = &ccb->cel;
+	target = ccb->ccb_h.target_id;
+	lun = ccb->ccb_h.target_lun;
+	channel = SIM_CHANNEL(ahc, sim);
+	target_mask = 0x01 << target;
+	if (channel == 'B')
+		target_mask <<= 8;
+
+	if (cel->enable != 0) {
+		u_int scsiseq;
+
+		/* Are we already enabled?? */
+		if (lstate != NULL) {
+			xpt_print_path(ccb->ccb_h.path);
+			printf("Lun already enabled\n");
+			ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+			return;
+		}
+
+		if (cel->grp6_len != 0
+		 || cel->grp7_len != 0) {
+			/*
+			 * Don't (yet?) support vendor
+			 * specific commands.
+			 */
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+			printf("Non-zero Group Codes\n");
+			return;
+		}
+
+		/*
+		 * Seems to be okay.
+		 * Setup our data structures.
+		 */
+		if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
+			tstate = ahc_alloc_tstate(ahc, target, channel);
+			if (tstate == NULL) {
+				xpt_print_path(ccb->ccb_h.path);
+				printf("Couldn't allocate tstate\n");
+				ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+				return;
+			}
+		}
+		lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
+		if (lstate == NULL) {
+			xpt_print_path(ccb->ccb_h.path);
+			printf("Couldn't allocate lstate\n");
+			ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+			return;
+		}
+		memset(lstate, 0, sizeof(*lstate));
+		status = xpt_create_path(&lstate->path, /*periph*/NULL,
+					 xpt_path_path_id(ccb->ccb_h.path),
+					 xpt_path_target_id(ccb->ccb_h.path),
+					 xpt_path_lun_id(ccb->ccb_h.path));
+		if (status != CAM_REQ_CMP) {
+			free(lstate, M_DEVBUF);
+			xpt_print_path(ccb->ccb_h.path);
+			printf("Couldn't allocate path\n");
+			ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+			return;
+		}
+		SLIST_INIT(&lstate->accept_tios);
+		SLIST_INIT(&lstate->immed_notifies);
+		ahc_lock(ahc, &s);
+		pause_sequencer(ahc);
+		if (target != CAM_TARGET_WILDCARD) {
+			tstate->enabled_luns[lun] = lstate;
+			ahc->enabled_luns++;
+
+			if ((ahc->features & AHC_MULTI_TID) != 0) {
+				u_int targid_mask;
+
+				targid_mask = ahc_inb(ahc, TARGID)
+					    | (ahc_inb(ahc, TARGID + 1) << 8);
+
+				targid_mask |= target_mask;
+				ahc_outb(ahc, TARGID, targid_mask);
+				ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
+				
+				ahc_update_scsiid(ahc, targid_mask);
+			} else {
+				u_int our_id;
+				char  channel;
+
+				channel = SIM_CHANNEL(ahc, sim);
+				our_id = SIM_SCSI_ID(ahc, sim);
+
+				/*
+				 * This can only happen if selections
+				 * are not enabled
+				 */
+				if (target != our_id) {
+					u_int sblkctl;
+					char  cur_channel;
+					int   swap;
+
+					sblkctl = ahc_inb(ahc, SBLKCTL);
+					cur_channel = (sblkctl & SELBUSB)
+						    ? 'B' : 'A';
+					if ((ahc->features & AHC_TWIN) == 0)
+						cur_channel = 'A';
+					swap = cur_channel != channel;
+					if (channel == 'A')
+						ahc->our_id = target;
+					else
+						ahc->our_id_b = target;
+
+					if (swap)
+						ahc_outb(ahc, SBLKCTL,
+							 sblkctl ^ SELBUSB);
+
+					ahc_outb(ahc, SCSIID, target);
+
+					if (swap)
+						ahc_outb(ahc, SBLKCTL, sblkctl);
+				}
+			}
+		} else
+			ahc->black_hole = lstate;
+		/* Allow select-in operations */
+		if (ahc->black_hole != NULL && ahc->enabled_luns > 0) {
+			scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+			scsiseq |= ENSELI;
+			ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+			scsiseq = ahc_inb(ahc, SCSISEQ);
+			scsiseq |= ENSELI;
+			ahc_outb(ahc, SCSISEQ, scsiseq);
+		}
+		unpause_sequencer(ahc);
+		ahc_unlock(ahc, &s);
+		ccb->ccb_h.status = CAM_REQ_CMP;
+		xpt_print_path(ccb->ccb_h.path);
+		printf("Lun now enabled for target mode\n");
+	} else {
+		struct scb *scb;
+		int i, empty;
+
+		if (lstate == NULL) {
+			ccb->ccb_h.status = CAM_LUN_INVALID;
+			return;
+		}
+
+		ahc_lock(ahc, &s);
+		
+		ccb->ccb_h.status = CAM_REQ_CMP;
+		LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+			struct ccb_hdr *ccbh;
+
+			ccbh = &scb->io_ctx->ccb_h;
+			if (ccbh->func_code == XPT_CONT_TARGET_IO
+			 && !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
+				printf("CTIO pending\n");
+				ccb->ccb_h.status = CAM_REQ_INVALID;
+				ahc_unlock(ahc, &s);
+				return;
+			}
+		}
+
+		if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+			printf("ATIOs pending\n");
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+		}
+
+		if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+			printf("INOTs pending\n");
+			ccb->ccb_h.status = CAM_REQ_INVALID;
+		}
+
+		if (ccb->ccb_h.status != CAM_REQ_CMP) {
+			ahc_unlock(ahc, &s);
+			return;
+		}
+
+		xpt_print_path(ccb->ccb_h.path);
+		printf("Target mode disabled\n");
+		xpt_free_path(lstate->path);
+		free(lstate, M_DEVBUF);
+
+		pause_sequencer(ahc);
+		/* Can we clean up the target too? */
+		if (target != CAM_TARGET_WILDCARD) {
+			tstate->enabled_luns[lun] = NULL;
+			ahc->enabled_luns--;
+			for (empty = 1, i = 0; i < 8; i++)
+				if (tstate->enabled_luns[i] != NULL) {
+					empty = 0;
+					break;
+				}
+
+			if (empty) {
+				ahc_free_tstate(ahc, target, channel,
+						/*force*/FALSE);
+				if (ahc->features & AHC_MULTI_TID) {
+					u_int targid_mask;
+
+					targid_mask = ahc_inb(ahc, TARGID)
+						    | (ahc_inb(ahc, TARGID + 1)
+						       << 8);
+
+					targid_mask &= ~target_mask;
+					ahc_outb(ahc, TARGID, targid_mask);
+					ahc_outb(ahc, TARGID+1,
+					 	 (targid_mask >> 8));
+					ahc_update_scsiid(ahc, targid_mask);
+				}
+			}
+		} else {
+
+			ahc->black_hole = NULL;
+
+			/*
+			 * We can't allow selections without
+			 * our black hole device.
+			 */
+			empty = TRUE;
+		}
+		if (ahc->enabled_luns == 0) {
+			/* Disallow select-in */
+			u_int scsiseq;
+
+			scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+			scsiseq &= ~ENSELI;
+			ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+			scsiseq = ahc_inb(ahc, SCSISEQ);
+			scsiseq &= ~ENSELI;
+			ahc_outb(ahc, SCSISEQ, scsiseq);
+
+			if ((ahc->features & AHC_MULTIROLE) == 0) {
+				printf("Configuring Initiator Mode\n");
+				ahc->flags &= ~AHC_TARGETROLE;
+				ahc->flags |= AHC_INITIATORROLE;
+				pause_sequencer(ahc);
+				ahc_loadseq(ahc);
+			}
+		}
+		unpause_sequencer(ahc);
+		ahc_unlock(ahc, &s);
+	}
+}
+
+static void
+ahc_update_scsiid(struct ahc_softc *ahc, u_int targid_mask)
+{
+	u_int scsiid_mask;
+	u_int scsiid;
+
+	if ((ahc->features & AHC_MULTI_TID) == 0)
+		panic("ahc_update_scsiid called on non-multitid unit\n");
+
+	/*
+	 * Since we will rely on the the TARGID mask
+	 * for selection enables, ensure that OID
+	 * in SCSIID is not set to some other ID
+	 * that we don't want to allow selections on.
+	 */
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		scsiid = ahc_inb(ahc, SCSIID_ULTRA2);
+	else
+		scsiid = ahc_inb(ahc, SCSIID);
+	scsiid_mask = 0x1 << (scsiid & OID);
+	if ((targid_mask & scsiid_mask) == 0) {
+		u_int our_id;
+
+		/* ffs counts from 1 */
+		our_id = ffs(targid_mask);
+		if (our_id == 0)
+			our_id = ahc->our_id;
+		else
+			our_id--;
+		scsiid &= TID;
+		scsiid |= our_id;
+	}
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ahc_outb(ahc, SCSIID_ULTRA2, scsiid);
+	else
+		ahc_outb(ahc, SCSIID, scsiid);
+}
+
+void
+ahc_run_tqinfifo(struct ahc_softc *ahc, int paused)
+{
+	struct target_cmd *cmd;
+
+	/*
+	 * If the card supports auto-access pause,
+	 * we can access the card directly regardless
+	 * of whether it is paused or not.
+	 */
+	if ((ahc->features & AHC_AUTOPAUSE) != 0)
+		paused = TRUE;
+
+	while ((cmd = &ahc->targetcmds[ahc->tqinfifonext])->cmd_valid != 0) {
+
+		/*
+		 * Only advance through the queue if we
+		 * have the resources to process the command.
+		 */
+		if (ahc_handle_target_cmd(ahc, cmd) != 0)
+			break;
+
+		ahc->tqinfifonext++;
+		cmd->cmd_valid = 0;
+
+		/*
+		 * Lazily update our position in the target mode incomming
+		 * command queue as seen by the sequencer.
+		 */
+		if ((ahc->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
+			if ((ahc->features & AHC_HS_MAILBOX) != 0) {
+				u_int hs_mailbox;
+
+				hs_mailbox = ahc_inb(ahc, HS_MAILBOX);
+				hs_mailbox &= ~HOST_TQINPOS;
+				hs_mailbox |= ahc->tqinfifonext & HOST_TQINPOS;
+				ahc_outb(ahc, HS_MAILBOX, hs_mailbox);
+			} else {
+				if (!paused)
+					pause_sequencer(ahc);	
+				ahc_outb(ahc, KERNEL_TQINPOS,
+					 ahc->tqinfifonext & HOST_TQINPOS);
+				if (!paused)
+					unpause_sequencer(ahc);
+			}
+		}
+	}
+}
+
+static int
+ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
+{
+	struct	  tmode_tstate *tstate;
+	struct	  tmode_lstate *lstate;
+	struct	  ccb_accept_tio *atio;
+	uint8_t *byte;
+	int	  initiator;
+	int	  target;
+	int	  lun;
+
+	initiator = SCSIID_TARGET(ahc, cmd->scsiid);
+	target = SCSIID_OUR_ID(cmd->scsiid);
+	lun    = (cmd->identify & MSG_IDENTIFY_LUNMASK);
+
+	byte = cmd->bytes;
+	tstate = ahc->enabled_targets[target];
+	lstate = NULL;
+	if (tstate != NULL)
+		lstate = tstate->enabled_luns[lun];
+
+	/*
+	 * Commands for disabled luns go to the black hole driver.
+	 */
+	if (lstate == NULL)
+		lstate = ahc->black_hole;
+
+	atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+	if (atio == NULL) {
+		ahc->flags |= AHC_TQINFIFO_BLOCKED;
+		/*
+		 * Wait for more ATIOs from the peripheral driver for this lun.
+		 */
+		return (1);
+	} else
+		ahc->flags &= ~AHC_TQINFIFO_BLOCKED;
+#if 0
+	printf("Incoming command from %d for %d:%d%s\n",
+	       initiator, target, lun,
+	       lstate == ahc->black_hole ? "(Black Holed)" : "");
+#endif
+	SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+
+	if (lstate == ahc->black_hole) {
+		/* Fill in the wildcards */
+		atio->ccb_h.target_id = target;
+		atio->ccb_h.target_lun = lun;
+	}
+
+	/*
+	 * Package it up and send it off to
+	 * whomever has this lun enabled.
+	 */
+	atio->sense_len = 0;
+	atio->init_id = initiator;
+	if (byte[0] != 0xFF) {
+		/* Tag was included */
+		atio->tag_action = *byte++;
+		atio->tag_id = *byte++;
+		atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+	} else {
+		atio->ccb_h.flags = 0;
+	}
+	byte++;
+
+	/* Okay.  Now determine the cdb size based on the command code */
+	switch (*byte >> CMD_GROUP_CODE_SHIFT) {
+	case 0:
+		atio->cdb_len = 6;
+		break;
+	case 1:
+	case 2:
+		atio->cdb_len = 10;
+		break;
+	case 4:
+		atio->cdb_len = 16;
+		break;
+	case 5:
+		atio->cdb_len = 12;
+		break;
+	case 3:
+	default:
+		/* Only copy the opcode. */
+		atio->cdb_len = 1;
+		printf("Reserved or VU command code type encountered\n");
+		break;
+	}
+	
+	memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
+
+	atio->ccb_h.status |= CAM_CDB_RECVD;
+
+	if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
+		/*
+		 * We weren't allowed to disconnect.
+		 * We're hanging on the bus until a
+		 * continue target I/O comes in response
+		 * to this accept tio.
+		 */
+#if 0
+		printf("Received Immediate Command %d:%d:%d - %p\n",
+		       initiator, target, lun, ahc->pending_device);
+#endif
+		ahc->pending_device = lstate;
+		ahc_freeze_ccb((union ccb *)atio);
+		atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
+	}
+	xpt_done((union ccb*)atio);
+	return (0);
+}
+
+#endif
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx.h linux/drivers/scsi/aic7xxx/aic7xxx.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,1202 @@
+/*
+ * Core definitions and data structures shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aic7xxx.h#19 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.h,v 1.30 2000/11/10 20:13:40 gibbs Exp $
+ */
+
+#ifndef _AIC7XXX_H_
+#define _AIC7XXX_H_
+
+/* Register Definitions */
+#include "aic7xxx_reg.h"
+
+/************************* Forward Declarations *******************************/
+struct ahc_platform_data;
+struct scb_platform_data;
+
+/****************************** Useful Macros *********************************/
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(*array))
+
+#define ALL_CHANNELS '\0'
+#define ALL_TARGETS_MASK 0xFFFF
+#define INITIATOR_WILDCARD	(~0)
+
+#define SCSIID_TARGET(ahc, scsiid) \
+	(((scsiid) & ((((ahc)->features & AHC_TWIN) != 0) ? TWIN_TID : TID)) \
+	>> TID_SHIFT)
+#define SCSIID_OUR_ID(scsiid) \
+	((scsiid) & OID)
+#define SCSIID_CHANNEL(ahc, scsiid) \
+	((((ahc)->features & AHC_TWIN) != 0) \
+        ? ((((scsiid) & TWIN_CHNLB) != 0) ? 'B' : 'A') \
+       : 'A')
+#define	SCB_IS_SCSIBUS_B(ahc, scb) \
+	(SCSIID_CHANNEL(ahc, (scb)->hscb->scsiid) == 'B')
+#define	SCB_GET_OUR_ID(scb) \
+	SCSIID_OUR_ID((scb)->hscb->scsiid)
+#define	SCB_GET_TARGET(ahc, scb) \
+	SCSIID_TARGET((ahc), (scb)->hscb->scsiid)
+#define	SCB_GET_CHANNEL(ahc, scb) \
+	SCSIID_CHANNEL(ahc, (scb)->hscb->scsiid)
+#define	SCB_GET_LUN(scb) \
+	((scb)->hscb->lun)
+#define SCB_GET_TARGET_OFFSET(ahc, scb)	\
+	(SCB_GET_TARGET(ahc, scb) + (SCB_IS_SCSIBUS_B(ahc, scb) ? 8 : 0))
+#define SCB_GET_TARGET_MASK(ahc, scb) \
+	(0x01 << (SCB_GET_TARGET_OFFSET(ahc, scb)))
+#define TCL_TARGET_OFFSET(tcl) \
+	((((tcl) >> 4) & TID) >> 4)
+#define TCL_LUN(tcl) \
+	(tcl & (AHC_NUM_LUNS - 1))
+#define BUILD_TCL(scsiid, lun) \
+	((lun) | (((scsiid) & TID) << 4))
+
+#ifndef	AHC_TARGET_MODE
+#undef	AHC_TMODE_ENABLE
+#define	AHC_TMODE_ENABLE 0
+#endif
+
+/**************************** Driver Constants ********************************/
+/*
+ * The maximum number of supported targets.
+ */
+#define AHC_NUM_TARGETS 16
+
+/*
+ * The maximum number of supported luns.
+ * The identify message only supports 64 luns in SPI3.
+ * You can have 2^64 luns when information unit transfers are enabled,
+ * but it is doubtful this driver will ever support IUTs.
+ */
+#define AHC_NUM_LUNS 64
+
+/*
+ * The maximum transfer per S/G segment.
+ */
+#define AHC_MAXTRANSFER_SIZE	 0x00ffffff	/* limited by 24bit counter */
+
+/*
+ * The maximum amount of SCB storage in hardware on a controller.
+ * This value represents an upper bound.  Controllers vary in the number
+ * they actually support.
+ */
+#define AHC_SCB_MAX	255
+
+/*
+ * The maximum number of concurrent transactions supported per driver instance.
+ * Sequencer Control Blocks (SCBs) store per-transaction information.  Although
+ * the space for SCBs on the host adapter varies by model, the driver will
+ * page the SCBs between host and controller memory as needed.  We are limited
+ * to 253 because:
+ * 	1) The 8bit nature of the RISC engine holds us to an 8bit value.
+ * 	2) We reserve one value, 255, to represent the invalid element.
+ *	3) Our input queue scheme requires one SCB to always be reserved
+ *	   in advance of queuing any SCBs.  This takes us down to 254.
+ *	4) To handle our output queue correctly on machines that only
+ * 	   support 32bit stores, we must clear the array 4 bytes at a
+ *	   time.  To avoid colliding with a DMA write from the sequencer,
+ *	   we must be sure that 4 slots are empty when we write to clear
+ *	   the queue.  This reduces us to 253 SCBs: 1 that just completed
+ *	   and the known three additional empty slots in the queue that
+ *	   preceed it.
+ */
+#define AHC_MAX_QUEUE	253
+
+/*
+ * Ring Buffer of incoming target commands.
+ * We allocate 256 to simplify the logic in the sequencer
+ * by using the natural wrap point of an 8bit counter.
+ */
+#define AHC_TMODE_CMDS	256
+
+/* Reset line assertion time in us */
+#define AHC_BUSRESET_DELAY	250
+
+/******************* Chip Characteristics/Operating Settings  *****************/
+/*
+ * Chip Type
+ * The chip order is from least sophisticated to most sophisticated.
+ */
+typedef enum {
+	AHC_NONE	= 0x0000,
+	AHC_CHIPID_MASK	= 0x00FF,
+	AHC_AIC7770	= 0x0001,
+	AHC_AIC7850	= 0x0002,
+	AHC_AIC7855	= 0x0003,
+	AHC_AIC7859	= 0x0004,
+	AHC_AIC7860	= 0x0005,
+	AHC_AIC7870	= 0x0006,
+	AHC_AIC7880	= 0x0007,
+	AHC_AIC7895	= 0x0008,
+	AHC_AIC7895C	= 0x0009,
+	AHC_AIC7890	= 0x000a,
+	AHC_AIC7896	= 0x000b,
+	AHC_AIC7892	= 0x000c,
+	AHC_AIC7899	= 0x000d,
+	AHC_VL		= 0x0100,	/* Bus type VL */
+	AHC_EISA	= 0x0200,	/* Bus type EISA */
+	AHC_PCI		= 0x0400,	/* Bus type PCI */
+	AHC_BUS_MASK	= 0x0F00
+} ahc_chip;
+
+/*
+ * Features available in each chip type.
+ */
+typedef enum {
+	AHC_FENONE	= 0x00000,
+	AHC_ULTRA	= 0x00001,	/* Supports 20MHz Transfers */
+	AHC_ULTRA2	= 0x00002,	/* Supports 40MHz Transfers */
+	AHC_WIDE  	= 0x00004,	/* Wide Channel */
+	AHC_TWIN	= 0x00008,	/* Twin Channel */
+	AHC_MORE_SRAM	= 0x00010,	/* 80 bytes instead of 64 */
+	AHC_CMD_CHAN	= 0x00020,	/* Has a Command DMA Channel */
+	AHC_QUEUE_REGS	= 0x00040,	/* Has Queue management registers */
+	AHC_SG_PRELOAD	= 0x00080,	/* Can perform auto-SG preload */
+	AHC_SPIOCAP	= 0x00100,	/* Has a Serial Port I/O Cap Register */
+	AHC_MULTI_TID	= 0x00200,	/* Has bitmask of TIDs for select-in */
+	AHC_HS_MAILBOX	= 0x00400,	/* Has HS_MAILBOX register */
+	AHC_DT		= 0x00800,	/* Double Transition transfers */
+	AHC_NEW_TERMCTL	= 0x01000,	/* Newer termination scheme */
+	AHC_MULTI_FUNC	= 0x02000,	/* Multi-Function Twin Channel Device */
+	AHC_LARGE_SCBS	= 0x04000,	/* 64byte SCBs */
+	AHC_AUTORATE	= 0x08000,	/* Automatic update of SCSIRATE/OFFSET*/
+	AHC_AUTOPAUSE	= 0x10000,	/* Automatic pause on register access */
+	AHC_TARGETMODE	= 0x20000,	/* Has tested target mode support */
+	AHC_MULTIROLE	= 0x40000,	/* Space for two roles at a time */
+	AHC_REMOVABLE	= 0x80000,	/* Hot-Swap supported */
+	AHC_AIC7770_FE	= AHC_FENONE,
+	AHC_AIC7850_FE	= AHC_SPIOCAP|AHC_AUTOPAUSE|AHC_TARGETMODE,
+	AHC_AIC7855_FE	= AHC_AIC7850_FE,
+	AHC_AIC7860_FE	= AHC_AIC7850_FE|AHC_ULTRA,
+	AHC_AIC7870_FE	= AHC_TARGETMODE,
+	AHC_AIC7880_FE	= AHC_AIC7870_FE|AHC_ULTRA,
+	/*
+	 * Although we have space for both the initiator and
+	 * target roles on ULTRA2 chips, we currently disable
+	 * the initiator role to allow multi-scsi-id target mode
+	 * configurations.  We can only respond on the same SCSI
+	 * ID as our initiator role if we allow initiator operation.
+	 * At some point, we should add a configuration knob to
+	 * allow both roles to be loaded.
+	 */
+	AHC_AIC7890_FE	= AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2
+			  |AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_MULTI_TID
+			  |AHC_HS_MAILBOX|AHC_NEW_TERMCTL|AHC_LARGE_SCBS
+			  |AHC_TARGETMODE,
+	AHC_AIC7892_FE	= AHC_AIC7890_FE|AHC_DT|AHC_AUTORATE|AHC_AUTOPAUSE,
+	AHC_AIC7895_FE	= AHC_AIC7880_FE|AHC_MORE_SRAM|AHC_AUTOPAUSE
+			  |AHC_CMD_CHAN|AHC_MULTI_FUNC|AHC_LARGE_SCBS,
+	AHC_AIC7895C_FE	= AHC_AIC7895_FE|AHC_MULTI_TID,
+	AHC_AIC7896_FE	= AHC_AIC7890_FE|AHC_MULTI_FUNC,
+	AHC_AIC7899_FE	= AHC_AIC7892_FE|AHC_MULTI_FUNC
+} ahc_feature;
+
+/*
+ * Bugs in the silicon that we work around in software.
+ */
+typedef enum {
+	AHC_BUGNONE		= 0x00,
+	/*
+	 * On all chips prior to the U2 product line,
+	 * the WIDEODD S/G segment feature does not
+	 * work during scsi->HostBus transfers.
+	 */
+	AHC_TMODE_WIDEODD_BUG	= 0x01,
+	/*
+	 * On the aic7890/91 Rev 0 chips, the autoflush
+	 * feature does not work.  A manual flush of
+	 * the DMA FIFO is required.
+	 */
+	AHC_AUTOFLUSH_BUG	= 0x02,
+	/*
+	 * On many chips, cacheline streaming does not work.
+	 */
+	AHC_CACHETHEN_BUG	= 0x04,
+	/*
+	 * On the aic7896/97 chips, cacheline
+	 * streaming must be enabled.
+	 */
+	AHC_CACHETHEN_DIS_BUG	= 0x08,
+	/*
+	 * PCI 2.1 Retry failure on non-empty data fifo.
+	 */
+	AHC_PCI_2_1_RETRY_BUG	= 0x10,
+	/*
+	 * Controller does not handle cacheline residuals
+	 * properly on S/G segments if PCI MWI instructions
+	 * are allowed.
+	 */
+	AHC_PCI_MWI_BUG		= 0x20,
+	/*
+	 * An SCB upload using the SCB channel's
+	 * auto array entry copy feature may 
+	 * corrupt data.  This appears to only
+	 * occur on 66MHz systems.
+	 */
+	AHC_SCBCHAN_UPLOAD_BUG	= 0x40
+} ahc_bug;
+
+/*
+ * Configuration specific settings.
+ * The driver determines these settings by probing the
+ * chip/controller's configuration.
+ */
+typedef enum {
+	AHC_FNONE		= 0x000,
+	AHC_PAGESCBS		= 0x001,/* Enable SCB paging */
+	AHC_CHANNEL_B_PRIMARY	= 0x002,/*
+					 * On twin channel adapters, probe
+					 * channel B first since it is the
+					 * primary bus.
+					 */
+	AHC_USEDEFAULTS		= 0x004,/*
+					 * For cards without an seeprom
+					 * or a BIOS to initialize the chip's
+					 * SRAM, we use the default target
+					 * settings.
+					 */
+	AHC_SEQUENCER_DEBUG	= 0x008,
+	AHC_SHARED_SRAM		= 0x010,
+	AHC_LARGE_SEEPROM	= 0x020,/* Uses C56_66 not C46 */
+	AHC_RESET_BUS_A		= 0x040,
+	AHC_RESET_BUS_B		= 0x080,
+	AHC_EXTENDED_TRANS_A	= 0x100,
+	AHC_EXTENDED_TRANS_B	= 0x200,
+	AHC_TERM_ENB_A		= 0x400,
+	AHC_TERM_ENB_B		= 0x800,
+	AHC_INITIATORROLE	= 0x1000,/*
+					  * Allow initiator operations on
+					  * this controller.
+					  */
+	AHC_TARGETROLE		= 0x2000,/*
+					  * Allow target operations on this
+					  * controller.
+					  */
+	AHC_NEWEEPROM_FMT	= 0x4000,
+	AHC_RESOURCE_SHORTAGE	= 0x8000,
+	AHC_TQINFIFO_BLOCKED	= 0x10000,/* Blocked waiting for ATIOs */
+	AHC_INT50_SPEEDFLEX	= 0x20000,/*
+					   * Internal 50pin connector
+					   * sits behind an aic3860
+					   */
+	AHC_SCB_BTT		= 0x40000,/*
+					   * The busy targets table is
+					   * stored in SCB space rather
+					   * than SRAM.
+					   */
+	AHC_BIOS_ENABLED	= 0x80000,
+	AHC_ALL_INTERRUPTS	= 0x100000,
+	AHC_ULTRA_DISABLED	= 0x200000/*
+					   * The precision resistor for
+					   * ultra transmission speeds is
+					   * missing, so we must limit
+					   * ourselves to fast SCSI.
+					   */
+} ahc_flag;
+
+/*
+ * Controller  Information composed at probe time.
+ */
+struct ahc_probe_config {
+	const char	*description;
+	char		 channel;
+	char		 channel_b;
+	ahc_chip	 chip;
+	ahc_feature	 features;
+	ahc_bug		 bugs;
+	ahc_flag	 flags;
+};
+
+/************************* Hardware  SCB Definition ***************************/
+
+/*
+ * The driver keeps up to MAX_SCB scb structures per card in memory.  The SCB
+ * consists of a "hardware SCB" mirroring the fields availible on the card
+ * and additional information the kernel stores for each transaction.
+ *
+ * To minimize space utilization, a portion of the hardware scb stores
+ * different data during different portions of a SCSI transaction.
+ * As initialized by the host driver for the initiator role, this area
+ * contains the SCSI cdb (or a pointer to the  cdb) to be executed.  After
+ * the cdb has been presented to the target, this area serves to store
+ * residual transfer information and the SCSI status byte.
+ * For the target role, the contents of this area do not change, but
+ * still serve a different purpose than for the initiator role.  See
+ * struct target_data for details.
+ */
+
+/*
+ * Status information embedded in the shared poriton of
+ * an SCB after passing the cdb to the target.  The kernel
+ * driver will only read this data for transactions that
+ * complete abnormally (non-zero status byte).
+ */
+struct status_pkt {
+	uint32_t residual_datacnt;	/* Residual in the current S/G seg */
+	uint32_t residual_sg_ptr;	/* The next S/G for this transfer */
+	uint8_t	 scsi_status;		/* Standard SCSI status byte */
+};
+
+/*
+ * Target mode version of the shared data SCB segment.
+ */
+struct target_data {
+	uint8_t	target_phases;		/* Bitmap of phases to execute */
+	uint8_t	data_phase;		/* Data-In or Data-Out */
+	uint8_t	scsi_status;		/* SCSI status to give to initiator */
+	uint8_t	initiator_tag;		/* Initiator's transaction tag */
+};
+
+struct hardware_scb {
+/*0*/	union {
+		/*
+		 * If the cdb is 12 bytes or less, we embed it directly
+		 * in the SCB.  For longer cdbs, we embed the address
+		 * of the cdb payload as seen by the chip and a DMA
+		 * is used to pull it in.
+		 */
+		uint8_t	cdb[12];
+		uint32_t	cdb_ptr;
+		struct		status_pkt status;
+		struct		target_data tdata;
+	} shared_data;
+/*
+ * A word about residuals.
+ * The scb is presented to the sequencer with the dataptr and datacnt
+ * fields initialized to the contents of the first S/G element to
+ * transfer.  The sgptr field is initialized to the bus address for
+ * the S/G element that follows the first in the in core S/G array
+ * or'ed with the SG_FULL_RESID flag.  Sgptr may point to an invalid
+ * S/G entry for this transfer (single S/G element transfer with the
+ * first elements address and length preloaded in the dataptr/datacnt
+ * fields).  If no transfer is to occur, sgptr is set to SG_LIST_NULL.
+ * The SG_FULL_RESID flag ensures that the residual will be correctly
+ * noted even if no data transfers occur.  Once the data phase is entered,
+ * the residual sgptr and datacnt are loaded from the sgptr and the
+ * datacnt fields.  After each S/G element's dataptr and length are
+ * loaded into the hardware, the residual sgptr is advanced.  After
+ * each S/G element is expired, its datacnt field is checked to see
+ * if the LAST_SEG flag is set.  If so, SG_LIST_NULL is set in the
+ * residual sg ptr and the transfer is considered complete.  If the
+ * sequencer determines that there is a residual in the tranfer, it
+ * will set the SG_RESID_VALID flag in sgptr and dma the scb back into
+ * host memory.  To sumarize:
+ *
+ * Sequencer:
+ *	o A residual has occurred if SG_FULL_RESID is set in sgptr,
+ *	  or residual_sgptr does not have SG_LIST_NULL set.
+ *
+ *	o We are transfering the last segment if residual_datacnt has
+ *	  the SG_LAST_SEG flag set.
+ *
+ * Host:
+ *	o A residual has occurred if a completed scb has the
+ *	  SG_RESID_VALID flag set.
+ *
+ *	o residual_sgptr and sgptr refer to the "next" sg entry
+ *	  and so may point beyond the last valid sg entry for the
+ *	  transfer.
+ */ 
+/*12*/	uint32_t dataptr;
+/*16*/	uint32_t datacnt;		/*
+					 * Byte 3 (numbered from 0) of
+					 * the datacnt is really the
+					 * 4th byte in that data address.
+					 */
+/*20*/	uint32_t sgptr;
+#define SG_PTR_MASK	0xFFFFFFF8
+/*24*/	uint8_t  control;	/* See SCB_CONTROL in aic7xxx.reg for details */
+/*25*/	uint8_t  scsiid;	/* what to load in the SCSIID register */
+/*26*/	uint8_t  lun;
+/*27*/	uint8_t  tag;			/*
+					 * Index into our kernel SCB array.
+					 * Also used as the tag for tagged I/O
+					 */
+/*28*/	uint8_t  cdb_len;
+/*29*/	uint8_t  scsirate;		/* Value for SCSIRATE register */
+/*30*/	uint8_t  scsioffset;		/* Value for SCSIOFFSET register */
+/*31*/	uint8_t  next;			/*
+					 * Used for threading SCBs in the
+					 * "Waiting for Selection" and
+					 * "Disconnected SCB" lists down
+					 * in the sequencer.
+					 */
+/*32*/	uint8_t  cdb32[32];		/*
+					 * CDB storage for cdbs of size
+					 * 13->32.  We store them here
+					 * because hardware scbs are
+					 * allocated from DMA safe
+					 * memory so we are guaranteed
+					 * the controller can access
+					 * this data.
+					 */
+};
+
+/************************ Kernel SCB Definitions ******************************/
+/*
+ * Some fields of the SCB are OS dependent.  Here we collect the
+ * definitions for elements that all OS platforms need to include
+ * in there SCB definition.
+ */
+
+/*
+ * Definition of a scatter/gather element as transfered to the controller.
+ * The aic7xxx chips only support a 24bit length.  We use the top byte of
+ * the length to store additional address bits and a flag to indicate
+ * that a given segment terminates the transfer.  This gives us an
+ * addressable range of 512GB on machines with 64bit PCI or with chips
+ * that can support dual address cycles on 32bit PCI busses.
+ */
+struct ahc_dma_seg {
+	uint32_t	addr;
+	uint32_t	len;
+#define	AHC_DMA_LAST_SEG	0x80000000
+#define	AHC_SG_HIGH_ADDR_MASK	0x7F000000
+#define	AHC_SG_LEN_MASK		0x00FFFFFF
+};
+
+/*
+ * The current state of this SCB.
+ */
+typedef enum {
+	SCB_FREE		= 0x0000,
+	SCB_OTHERTCL_TIMEOUT	= 0x0002,/*
+					  * Another device was active
+					  * during the first timeout for
+					  * this SCB so we gave ourselves
+					  * an additional timeout period
+					  * in case it was hogging the
+					  * bus.
+				          */
+	SCB_DEVICE_RESET	= 0x0004,
+	SCB_SENSE		= 0x0008,
+	SCB_CDB32_PTR		= 0x0010,
+	SCB_RECOVERY_SCB	= 0x0040,
+	SCB_NEGOTIATE		= 0x0080,
+	SCB_ABORT		= 0x1000,
+	SCB_UNTAGGEDQ		= 0x2000,
+	SCB_ACTIVE		= 0x4000,
+	SCB_TARGET_IMMEDIATE	= 0x8000
+} scb_flag;
+
+struct scb {
+	struct	hardware_scb	 *hscb;
+	union {
+		SLIST_ENTRY(scb)  sle;
+		TAILQ_ENTRY(scb)  tqe;
+	} links;
+	LIST_ENTRY(scb)		  pending_links;
+	ahc_io_ctx_t		  io_ctx;
+	struct ahc_softc	 *ahc_softc;
+	scb_flag		  flags;
+#ifndef __linux__
+	bus_dmamap_t		  dmamap;
+#endif
+	struct scb_platform_data *platform_data;
+	struct	ahc_dma_seg 	 *sg_list;
+	bus_addr_t		  sg_list_phys;
+	u_int			  sg_count;/* How full ahc_dma_seg is */
+};
+
+struct sg_map_node {
+	bus_dmamap_t		 sg_dmamap;
+	bus_addr_t		 sg_physaddr;
+	struct ahc_dma_seg*	 sg_vaddr;
+	SLIST_ENTRY(sg_map_node) links;
+};
+
+struct scb_data {
+	SLIST_HEAD(, scb) free_scbs;	/*
+					 * Pool of SCBs ready to be assigned
+					 * commands to execute.
+					 */
+	struct	scb *scbindex[AHC_SCB_MAX + 1];/* Mapping from tag to SCB */
+	struct	hardware_scb	*hscbs;	/* Array of hardware SCBs */
+	struct	scb *scbarray;		/* Array of kernel SCBs */
+	struct	scsi_sense_data *sense; /* Per SCB sense data */
+
+	/*
+	 * "Bus" addresses of our data structures.
+	 */
+	bus_dma_tag_t	 hscb_dmat;	/* dmat for our hardware SCB array */
+	bus_dmamap_t	 hscb_dmamap;
+	bus_addr_t	 hscb_busaddr;
+	bus_dma_tag_t	 sense_dmat;
+	bus_dmamap_t	 sense_dmamap;
+	bus_addr_t	 sense_busaddr;
+	bus_dma_tag_t	 sg_dmat;	/* dmat for our sg segments */
+	SLIST_HEAD(, sg_map_node) sg_maps;
+	uint8_t	numscbs;
+	uint8_t	maxhscbs;		/* Number of SCBs on the card */
+	uint8_t	init_level;		/*
+					 * How far we've initialized
+					 * this structure.
+					 */
+};
+
+/************************ Target Mode Definitions *****************************/
+
+/*
+ * Connection desciptor for select-in requests in target mode.
+ */
+struct target_cmd {
+	uint8_t scsiid;		/* Our ID and the initiator's ID */
+	uint8_t identify;	/* Identify message */
+	uint8_t bytes[22];	/* 
+				 * Bytes contains any additional message
+				 * bytes terminated by 0xFF.  The remainder
+				 * is the cdb to execute.
+				 */
+	uint8_t cmd_valid;	/*
+				 * When a command is complete, the firmware
+				 * will set cmd_valid to all bits set.
+				 * After the host has seen the command,
+				 * the bits are cleared.  This allows us
+				 * to just peek at host memory to determine
+				 * if more work is complete. cmd_valid is on
+				 * an 8 byte boundary to simplify setting
+				 * it on aic7880 hardware which only has
+				 * limited direct access to the DMA FIFO.
+				 */
+	uint8_t pad[7];
+};
+
+/*
+ * Number of events we can buffer up if we run out
+ * of immediate notify ccbs.
+ */
+#define AHC_TMODE_EVENT_BUFFER_SIZE 8
+struct ahc_tmode_event {
+	uint8_t initiator_id;
+	uint8_t event_type;	/* MSG type or EVENT_TYPE_BUS_RESET */
+#define	EVENT_TYPE_BUS_RESET 0xFF
+	uint8_t event_arg;
+};
+
+/*
+ * Per enabled lun target mode state.
+ * As this state is directly influenced by the host OS'es target mode
+ * environment, we let the OS module define it.  Forward declare the
+ * structure here so we can store arrays of them, etc. in OS neutral
+ * data structures.
+ */
+#ifdef AHC_TARGET_MODE 
+struct tmode_lstate {
+	struct cam_path *path;
+	struct ccb_hdr_slist accept_tios;
+	struct ccb_hdr_slist immed_notifies;
+	struct ahc_tmode_event event_buffer[AHC_TMODE_EVENT_BUFFER_SIZE];
+	uint8_t event_r_idx;
+	uint8_t event_w_idx;
+};
+#else
+struct tmode_lstate;
+#endif
+
+/******************** Transfer Negotiation Datastructures *********************/
+#define AHC_TRANS_CUR		0x01	/* Modify current neogtiation status */
+#define AHC_TRANS_ACTIVE	0x03	/* Assume this target is on the bus */
+#define AHC_TRANS_GOAL		0x04	/* Modify negotiation goal */
+#define AHC_TRANS_USER		0x08	/* Modify user negotiation settings */
+
+/*
+ * Transfer Negotiation Information.
+ */
+struct ahc_transinfo {
+	uint8_t protocol_version;	/* SCSI Revision level */
+	uint8_t transport_version;	/* SPI Revision level */
+	uint8_t width;			/* Bus width */
+	uint8_t period;			/* Sync rate factor */
+	uint8_t offset;			/* Sync offset */
+	uint8_t ppr_options;		/* Parallel Protocol Request options */
+};
+
+/*
+ * Per-initiator current, goal and user transfer negotiation information. */
+struct ahc_initiator_tinfo {
+	uint8_t scsirate;		/* Computed value for SCSIRATE reg */
+	struct ahc_transinfo current;
+	struct ahc_transinfo goal;
+	struct ahc_transinfo user;
+};
+
+/*
+ * Per enabled target ID state.
+ * Pointers to lun target state as well as sync/wide negotiation information
+ * for each initiator<->target mapping.  For the initiator role we pretend
+ * that we are the target and the targets are the initiators since the
+ * negotiation is the same regardless of role.
+ */
+struct tmode_tstate {
+	struct tmode_lstate*		enabled_luns[AHC_NUM_LUNS];
+	struct ahc_initiator_tinfo	transinfo[AHC_NUM_TARGETS];
+
+	/*
+	 * Per initiator state bitmasks.
+	 */
+	uint16_t		 ultraenb;	/* Using ultra sync rate  */
+	uint16_t	 	 discenable;	/* Disconnection allowed  */
+	uint16_t		 tagenable;	/* Tagged Queuing allowed */
+};
+
+/*
+ * Data structure for our table of allowed synchronous transfer rates.
+ */
+struct ahc_syncrate {
+	u_int sxfr_u2;	/* Value of the SXFR parameter for Ultra2+ Chips */
+	u_int sxfr;	/* Value of the SXFR parameter for <= Ultra Chips */
+#define		ULTRA_SXFR 0x100	/* Rate Requires Ultra Mode set */
+#define		ST_SXFR	   0x010	/* Rate Single Transition Only */
+#define		DT_SXFR	   0x040	/* Rate Double Transition Only */
+	uint8_t period; /* Period to send to SCSI target */
+	char *rate;
+};
+
+/*
+ * The synchronouse transfer rate table.
+ */
+extern struct ahc_syncrate ahc_syncrates[];
+
+/*
+ * Indexes into our table of syncronous transfer rates.
+ */
+#define AHC_SYNCRATE_DT		0
+#define AHC_SYNCRATE_ULTRA2	1
+#define AHC_SYNCRATE_ULTRA	3
+#define AHC_SYNCRATE_FAST	6
+
+/***************************** Lookup Tables **********************************/
+/*
+ * Textual descriptions of the different chips indexed by chip type.
+ */
+extern char *ahc_chip_names[];
+extern const u_int num_chip_names;
+
+/*
+ * Hardware error codes.
+ */
+struct hard_error_entry {
+        uint8_t errno;
+	char *errmesg;
+};
+extern struct hard_error_entry hard_error[];
+extern const u_int num_errors;
+
+/*
+ * Phase -> name and message out response
+ * to parity errors in each phase table. 
+ */
+struct phase_table_entry {
+        uint8_t phase;
+        uint8_t mesg_out; /* Message response to parity errors */
+	char *phasemsg;
+};
+extern struct phase_table_entry phase_table[];
+extern const u_int num_phases;
+
+/************************** Serial EEPROM Format ******************************/
+
+struct seeprom_config {
+/*
+ * Per SCSI ID Configuration Flags
+ */
+	uint16_t device_flags[16];	/* words 0-15 */
+#define		CFXFER		0x0007	/* synchronous transfer rate */
+#define		CFSYNCH		0x0008	/* enable synchronous transfer */
+#define		CFDISC		0x0010	/* enable disconnection */
+#define		CFWIDEB		0x0020	/* wide bus device */
+#define		CFSYNCHISULTRA	0x0040	/* CFSYNCH is an ultra offset (2940AU)*/
+#define		CFSYNCSINGLE	0x0080	/* Single-Transition signalling */
+#define		CFSTART		0x0100	/* send start unit SCSI command */
+#define		CFINCBIOS	0x0200	/* include in BIOS scan */
+#define		CFRNFOUND	0x0400	/* report even if not found */
+#define		CFMULTILUNDEV	0x0800	/* Probe multiple luns in BIOS scan */
+#define		CFWBCACHEENB	0x4000	/* Enable W-Behind Cache on disks */
+#define		CFWBCACHENOP	0xc000	/* Don't touch W-Behind Cache */
+
+/*
+ * BIOS Control Bits
+ */
+	uint16_t bios_control;		/* word 16 */
+#define		CFSUPREM	0x0001	/* support all removeable drives */
+#define		CFSUPREMB	0x0002	/* support removeable boot drives */
+#define		CFBIOSEN	0x0004	/* BIOS enabled */
+/*		UNUSED		0x0008	*/
+#define		CFSM2DRV	0x0010	/* support more than two drives */
+#define		CF284XEXTEND	0x0020	/* extended translation (284x cards) */	
+#define		CFSTPWLEVEL	0x0010	/* Termination level control */
+#define		CFEXTEND	0x0080	/* extended translation enabled */
+#define		CFSCAMEN	0x0100	/* SCAM enable */
+/*		UNUSED		0xff00	*/
+
+/*
+ * Host Adapter Control Bits
+ */
+	uint16_t adapter_control;	/* word 17 */	
+#define		CFAUTOTERM	0x0001	/* Perform Auto termination */
+#define		CFULTRAEN	0x0002	/* Ultra SCSI speed enable */
+#define		CF284XSELTO     0x0003	/* Selection timeout (284x cards) */
+#define		CF284XFIFO      0x000C	/* FIFO Threshold (284x cards) */
+#define		CFSTERM		0x0004	/* SCSI low byte termination */
+#define		CFWSTERM	0x0008	/* SCSI high byte termination */
+#define		CFSPARITY	0x0010	/* SCSI parity */
+#define		CF284XSTERM     0x0020	/* SCSI low byte term (284x cards) */	
+#define		CFMULTILUN	0x0020	/* SCSI low byte term (284x cards) */	
+#define		CFRESETB	0x0040	/* reset SCSI bus at boot */
+#define		CFCLUSTERENB	0x0080	/* Cluster Enable */
+#define		CFCHNLBPRIMARY	0x0100	/* aic7895 probe B channel first */
+#define		CFSEAUTOTERM	0x0400	/* Ultra2 Perform secondary Auto Term*/
+#define		CFSELOWTERM	0x0800	/* Ultra2 secondary low term */
+#define		CFSEHIGHTERM	0x1000	/* Ultra2 secondary high term */
+#define		CFDOMAINVAL	0x4000	/* Perform Domain Validation*/
+
+/*
+ * Bus Release Time, Host Adapter ID
+ */
+	uint16_t brtime_id;		/* word 18 */
+#define		CFSCSIID	0x000f	/* host adapter SCSI ID */
+/*		UNUSED		0x00f0	*/
+#define		CFBRTIME	0xff00	/* bus release time */
+
+/*
+ * Maximum targets
+ */
+	uint16_t max_targets;		/* word 19 */	
+#define		CFMAXTARG	0x00ff	/* maximum targets */
+#define		CFBOOTLUN	0x0f00	/* Lun to boot from */
+#define		CFBOOTID	0xf000	/* Target to boot from */
+	uint16_t res_1[10];		/* words 20-29 */
+	uint16_t signature;		/* Signature == 0x250 */
+#define		CFSIGNATURE	0x250
+	uint16_t checksum;		/* word 31 */
+};
+
+/****************************  Message Buffer *********************************/
+typedef enum {
+	MSG_TYPE_NONE			= 0x00,
+	MSG_TYPE_INITIATOR_MSGOUT	= 0x01,
+	MSG_TYPE_INITIATOR_MSGIN	= 0x02,
+	MSG_TYPE_TARGET_MSGOUT		= 0x03,
+	MSG_TYPE_TARGET_MSGIN		= 0x04
+} ahc_msg_type;
+
+typedef enum {
+	MSGLOOP_IN_PROG,
+	MSGLOOP_MSGCOMPLETE,
+	MSGLOOP_TERMINATED
+} msg_loop_stat;
+
+/*********************** Software Configuration Structure *********************/
+TAILQ_HEAD(scb_tailq, scb);
+
+struct ahc_suspend_channel_state {
+	uint8_t	scsiseq;
+	uint8_t	sxfrctl0;
+	uint8_t	sxfrctl1;
+	uint8_t	simode0;
+	uint8_t	simode1;
+	uint8_t	seltimer;
+	uint8_t	seqctl;
+};
+
+struct ahc_suspend_state {
+	struct	ahc_suspend_channel_state channel[2];
+	uint8_t	optionmode;
+	uint8_t	dscommand0;
+	uint8_t	dspcistatus;
+	/* hsmailbox */
+	uint8_t	crccontrol1;
+	uint8_t	scbbaddr;
+	/* Host and sequencer SCB counts */
+	uint8_t	dff_thrsh;
+	uint8_t	*scratch_ram;
+	uint8_t	*btt;
+};
+
+struct ahc_softc {
+	bus_space_tag_t           tag;
+	bus_space_handle_t        bsh;
+#ifndef __linux__
+	bus_dma_tag_t		  buffer_dmat;   /* dmat for buffer I/O */
+#endif
+	struct scb_data		 *scb_data;
+
+	struct scb		 *next_queued_scb;
+
+	/*
+	 * SCBs that have been sent to the controller
+	 */
+	LIST_HEAD(, scb)	 pending_scbs;
+
+	/*
+	 * Counting lock for deferring the release of additional
+	 * untagged transactions from the untagged_queues.  When
+	 * the lock is decremented to 0, all queues in the
+	 * untagged_queues array are run.
+	 */
+	u_int			  untagged_queue_lock;
+
+	/*
+	 * Per-target queue of untagged-transactions.  The
+	 * transaction at the head of the queue is the
+	 * currently pending untagged transaction for the
+	 * target.  The driver only allows a single untagged
+	 * transaction per target.
+	 */
+	struct scb_tailq	  untagged_queues[AHC_NUM_TARGETS];
+
+	/*
+	 * Platform specific data.
+	 */
+	struct ahc_platform_data *platform_data;
+
+	/*
+	 * Platform specific device information.
+	 */
+	ahc_dev_softc_t		  dev_softc;
+
+	/*
+	 * Target mode related state kept on a per enabled lun basis.
+	 * Targets that are not enabled will have null entries.
+	 * As an initiator, we keep one target entry for our initiator
+	 * ID to store our sync/wide transfer settings.
+	 */
+	struct tmode_tstate*	  enabled_targets[AHC_NUM_TARGETS];
+
+	/*
+	 * The black hole device responsible for handling requests for
+	 * disabled luns on enabled targets.
+	 */
+	struct tmode_lstate*	  black_hole;
+
+	/*
+	 * Device instance currently on the bus awaiting a continue TIO
+	 * for a command that was not given the disconnect priveledge.
+	 */
+	struct tmode_lstate*	  pending_device;
+
+	/*
+	 * Card characteristics
+	 */
+	ahc_chip		  chip;
+	ahc_feature		  features;
+	ahc_bug			  bugs;
+	ahc_flag		  flags;
+
+	/* Values to store in the SEQCTL register for pause and unpause */
+	uint8_t			  unpause;
+	uint8_t			  pause;
+
+	/* Command Queues */
+	uint8_t			  qoutfifonext;
+	uint8_t			  qinfifonext;
+	uint8_t			 *qoutfifo;
+	uint8_t			 *qinfifo;
+
+	/* Critical Section Data */
+	struct cs		 *critical_sections;
+	u_int			  num_critical_sections;
+
+	/* Links for chaining softcs */
+	TAILQ_ENTRY(ahc_softc)	  links;
+
+	/* Channel Names ('A', 'B', etc.) */
+	char			  channel;
+	char			  channel_b;
+
+	/* Initiator Bus ID */
+	uint8_t			  our_id;
+	uint8_t			  our_id_b;
+
+	/* Targets that need negotiation messages */
+	uint16_t		  targ_msg_req;
+
+	/*
+	 * PCI error detection.
+	 */
+	int			  unsolicited_ints;
+
+	/*
+	 * Target incoming command FIFO.
+	 */
+	struct target_cmd	 *targetcmds;
+	uint8_t			  tqinfifonext;
+
+	/*
+	 * Incoming and outgoing message handling.
+	 */
+	uint8_t			  send_msg_perror;
+	ahc_msg_type		  msg_type;
+	uint8_t			  msgout_buf[12];/* Message we are sending */
+	uint8_t			  msgin_buf[12];/* Message we are receiving */
+	u_int			  msgout_len;	/* Length of message to send */
+	u_int			  msgout_index;	/* Current index in msgout */
+	u_int			  msgin_index;	/* Current index in msgin */
+
+	/*
+	 * Mapping information for data structures shared
+	 * between the sequencer and kernel.
+	 */
+	bus_dma_tag_t		  parent_dmat;
+	bus_dma_tag_t		  shared_data_dmat;
+	bus_dmamap_t		  shared_data_dmamap;
+	bus_addr_t		  shared_data_busaddr;
+
+	/*
+	 * Bus address of the one byte buffer used to
+	 * work-around a DMA bug for chips <= aic7880
+	 * in target mode.
+	 */
+	bus_addr_t		  dma_bug_buf;
+
+	/* Information saved through suspend/resume cycles */
+	struct ahc_suspend_state  suspend_state;
+
+	/* Number of enabled target mode device on this card */
+	u_int			  enabled_luns;
+
+	/* Initialization level of this data structure */
+	u_int			  init_level;
+
+	/* PCI cacheline size. */
+	u_int			  pci_cachesize;
+
+	/* Per-Unit descriptive information */
+	const char		 *description;
+	char			 *name;
+	int			  unit;
+
+	/* Selection Timer settings */
+	int			  seltime;
+	int			  seltime_b;
+
+	uint16_t	 	  user_discenable;/* Disconnection allowed  */
+	uint16_t		  user_tagenable;/* Tagged Queuing allowed */
+};
+
+TAILQ_HEAD(ahc_softc_tailq, ahc_softc);
+extern struct ahc_softc_tailq ahc_tailq;
+
+/************************ Active Device Information ***************************/
+typedef enum {
+	ROLE_UNKNOWN,
+	ROLE_INITIATOR,
+	ROLE_TARGET
+} role_t;
+
+struct ahc_devinfo {
+	int	 our_scsiid;
+	int	 target_offset;
+	uint16_t target_mask;
+	u_int	 target;
+	u_int	 lun;
+	char	 channel;
+	role_t	 role;		/*
+				 * Only guaranteed to be correct if not
+				 * in the busfree state.
+				 */
+};
+
+/****************************** PCI Structures ********************************/
+typedef int (ahc_device_setup_t)(ahc_dev_softc_t,
+				 struct ahc_probe_config *);
+
+struct ahc_pci_identity {
+	uint64_t		 full_id;
+	uint64_t		 id_mask;
+	char			*name;
+	ahc_device_setup_t	*setup;
+};
+extern struct ahc_pci_identity ahc_pci_ident_table [];
+extern const u_int ahc_num_pci_devs;
+
+/***************************** VL/EISA Declarations ***************************/
+struct aic7770_identity {
+	uint32_t		 full_id;
+	uint32_t		 id_mask;
+	char			*name;
+	ahc_device_setup_t	*setup;
+};
+extern struct aic7770_identity aic7770_ident_table [];
+extern const int ahc_num_aic7770_devs;
+
+#define AHC_EISA_SLOT_OFFSET	0xc00
+#define AHC_EISA_IOSIZE		0x100
+
+/*************************** Function Declarations ****************************/
+/******************************************************************************/
+u_int			ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl);
+void			ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl);
+void			ahc_busy_tcl(struct ahc_softc *ahc,
+				     u_int tcl, u_int busyid);
+
+/***************************** PCI Front End *********************************/
+struct ahc_pci_identity	*ahc_find_pci_device(ahc_dev_softc_t);
+int			 ahc_pci_config(struct ahc_softc *,
+					struct ahc_pci_identity *);
+
+/*************************** EISA/VL Front End ********************************/
+struct aic7770_identity *aic7770_find_device(uint32_t);
+int			 aic7770_config(struct ahc_softc *ahc,
+					struct aic7770_identity *);
+
+/************************** SCB and SCB queue management **********************/
+int		ahc_probe_scbs(struct ahc_softc *);
+void		ahc_run_untagged_queues(struct ahc_softc *ahc);
+void		ahc_run_untagged_queue(struct ahc_softc *ahc,
+				       struct scb_tailq *queue);
+void		ahc_qinfifo_requeue_tail(struct ahc_softc *ahc,
+					 struct scb *scb);
+int		ahc_match_scb(struct ahc_softc *ahc, struct scb *scb,
+			      int target, char channel, int lun,
+			      u_int tag, role_t role);
+
+/****************************** Initialization ********************************/
+void			 ahc_init_probe_config(struct ahc_probe_config *);
+struct ahc_softc	*ahc_alloc(void *platform_arg, char *name);
+int			 ahc_softc_init(struct ahc_softc *,
+					struct ahc_probe_config*);
+void			 ahc_controller_info(struct ahc_softc *ahc, char *buf);
+int			 ahc_init(struct ahc_softc *ahc);
+void			 ahc_pause_and_flushwork(struct ahc_softc *ahc);
+int			 ahc_suspend(struct ahc_softc *ahc); 
+int			 ahc_resume(struct ahc_softc *ahc);
+void			 ahc_softc_insert(struct ahc_softc *);
+void			 ahc_set_unit(struct ahc_softc *, int);
+void			 ahc_set_name(struct ahc_softc *, char *);
+void			 ahc_alloc_scbs(struct ahc_softc *ahc);
+void			 ahc_free(struct ahc_softc *ahc);
+int			 ahc_reset(struct ahc_softc *ahc);
+void			 ahc_shutdown(void *arg);
+
+/*************************** Interrupt Services *******************************/
+void			ahc_pci_intr(struct ahc_softc *ahc);
+void			ahc_clear_intstat(struct ahc_softc *ahc);
+void			ahc_run_qoutfifo(struct ahc_softc *ahc);
+#ifdef AHC_TARGET_MODE
+void			ahc_run_tqinfifo(struct ahc_softc *ahc, int paused);
+#endif
+void			ahc_handle_brkadrint(struct ahc_softc *ahc);
+void			ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat);
+void			ahc_handle_scsiint(struct ahc_softc *ahc,
+					   u_int intstat);
+void			ahc_clear_critical_section(struct ahc_softc *ahc);
+
+/***************************** Error Recovery *********************************/
+typedef enum {
+	SEARCH_COMPLETE,
+	SEARCH_COUNT,
+	SEARCH_REMOVE
+} ahc_search_action;
+int			ahc_search_qinfifo(struct ahc_softc *ahc, int target,
+					   char channel, int lun, u_int tag,
+					   role_t role, uint32_t status,
+					   ahc_search_action action);
+int			ahc_search_disc_list(struct ahc_softc *ahc, int target,
+					     char channel, int lun, u_int tag,
+					     int stop_on_first, int remove,
+					     int save_state);
+void			ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb);
+int			ahc_reset_channel(struct ahc_softc *ahc, char channel,
+					  int initiate_reset);
+void			restart_sequencer(struct ahc_softc *ahc);
+/*************************** Utility Functions ********************************/
+void			ahc_compile_devinfo(struct ahc_devinfo *devinfo,
+					    u_int our_id, u_int target,
+					    u_int lun, char channel,
+					    role_t role);
+/************************** Transfer Negotiation ******************************/
+struct ahc_syncrate*	ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+					  u_int *ppr_options, u_int maxsync);
+u_int			ahc_find_period(struct ahc_softc *ahc,
+					u_int scsirate, u_int maxsync);
+void			ahc_validate_offset(struct ahc_softc *ahc,
+					    struct ahc_initiator_tinfo *tinfo,
+					    struct ahc_syncrate *syncrate,
+					    u_int *offset, int wide,
+					    role_t role);
+void			ahc_validate_width(struct ahc_softc *ahc,
+					   struct ahc_initiator_tinfo *tinfo,
+					   u_int *bus_width,
+					   role_t role);
+void			ahc_update_target_msg_request(struct ahc_softc *ahc,
+					struct ahc_devinfo *dinfo,
+					struct ahc_initiator_tinfo *tinfo,
+					int force, int paused);
+void			ahc_set_width(struct ahc_softc *ahc,
+				      struct ahc_devinfo *devinfo,
+				      u_int width, u_int type, int paused);
+void			ahc_set_syncrate(struct ahc_softc *ahc,
+					 struct ahc_devinfo *devinfo,
+					 struct ahc_syncrate *syncrate,
+					 u_int period, u_int offset,
+					 u_int ppr_options,
+					 u_int type, int paused);
+void			ahc_set_tags(struct ahc_softc *ahc,
+				     struct ahc_devinfo *devinfo, int enable);
+
+/**************************** Target Mode *************************************/
+#ifdef AHC_TARGET_MODE
+void		ahc_send_lstate_events(struct ahc_softc *,
+				       struct tmode_lstate *);
+void		ahc_handle_en_lun(struct ahc_softc *ahc,
+				  struct cam_sim *sim, union ccb *ccb);
+cam_status	ahc_find_tmode_devs(struct ahc_softc *ahc,
+				    struct cam_sim *sim, union ccb *ccb,
+				    struct tmode_tstate **tstate,
+				    struct tmode_lstate **lstate,
+				    int notfound_failure);
+void		ahc_setup_target_msgin(struct ahc_softc *ahc,
+				       struct ahc_devinfo *devinfo);
+#ifndef AHC_TMODE_ENABLE
+#define AHC_TMODE_ENABLE 0
+#endif
+#endif
+/******************************* Debug ***************************************/
+void			ahc_print_scb(struct scb *scb);
+void			ahc_dump_card_state(struct ahc_softc *ahc);
+#endif /* _AIC7XXX_H_ */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx.reg linux/drivers/scsi/aic7xxx/aic7xxx.reg
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx.reg	Thu Feb 10 19:00:35 2000
+++ linux/drivers/scsi/aic7xxx/aic7xxx.reg	Mon Dec  3 22:29:50 2001
@@ -1,7 +1,7 @@
 /*
  * Aic7xxx register and scratch ram definitions.
  *
- * Copyright (c) 1994-1998 Justin Gibbs.
+ * Copyright (c) 1994-2001 Justin Gibbs.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -9,16 +9,12 @@
  * 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.
+ *    without modification.
  * 2. 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.
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("GPL").
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -32,7 +28,9 @@
  * 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 $
+ * $Id: //depot/src/aic7xxx/aic7xxx.reg#13 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.reg,v 1.31 2000/11/10 20:13:40 gibbs Exp $
  */
 
 /*
@@ -114,6 +112,8 @@
 	mask	PHASE_MASK	CDI|IOI|MSGI
 	mask	P_DATAOUT	0x00
 	mask	P_DATAIN	IOI
+	mask	P_DATAOUT_DT	P_DATAOUT|MSGI
+	mask	P_DATAIN_DT	P_DATAIN|MSGI
 	mask	P_COMMAND	CDI
 	mask	P_MESGOUT	CDI|MSGI
 	mask	P_STATUS	CDI|IOI
@@ -160,8 +160,10 @@
 	address			0x004
 	access_mode RW
 	bit	WIDEXFER	0x80		/* Wide transfer control */
+	bit	ENABLE_CRC	0x40		/* CRC for D-Phases */
+	bit	SINGLE_EDGE	0x10		/* Disable DT Transfers */
 	mask	SXFR		0x70		/* Sync transfer rate */
-	mask	SXFR_ULTRA2	0x7f		/* Sync transfer rate */
+	mask	SXFR_ULTRA2	0x0f		/* Sync transfer rate */
 	mask	SOFS		0x0f		/* Sync offset */
 }
 
@@ -174,6 +176,8 @@
 	address			0x005
 	access_mode RW
 	mask	TID		0xf0		/* Target ID mask */
+	mask	TWIN_TID	0x70
+	bit	TWIN_CHNLB	0x80
 	mask	OID		0x0f		/* Our ID mask */
 	/*
 	 * SCSI Maximum Offset (p. 4-61 aic7890/91 Data Book)
@@ -213,24 +217,27 @@
 	access_mode RW
 }
 
-/*
- * Option Mode Register (Alternate Mode) (p. 5-198)
- * This register is used to set certain options on Ultra3 based chips.
- * The chip must be in alternate mode (bit ALT_MODE in SFUNCT must be set)
- */
+/* ALT_MODE register on Ultra160 chips */
 register OPTIONMODE {
 	address			0x008
 	access_mode RW
-	bit	AUTORATEEN	0x80
-	bit	AUTOACKEN	0x40
-	bit	ATNMGMNTEN	0x20
-	bit	BUSFREEREV	0x10
-	bit	EXPPHASEDIS	0x08
-	bit	SCSIDATL_IMGEN	0x04
-	bit	AUTO_MSGOUT_DE	0x02
+	bit	AUTORATEEN		0x80
+	bit	AUTOACKEN		0x40
+	bit	ATNMGMNTEN		0x20
+	bit	BUSFREEREV		0x10
+	bit	EXPPHASEDIS		0x08
+	bit	SCSIDATL_IMGEN		0x04
+	bit	AUTO_MSGOUT_DE		0x02
 	bit	DIS_MSGIN_DUALEDGE	0x01
+	mask	OPTIONMODE_DEFAULTS	AUTO_MSGOUT_DE|DIS_MSGIN_DUALEDGE
 }
 
+/* ALT_MODE register on Ultra160 chips */
+register TARGCRCCNT {
+	address			0x00a
+	size	2
+	access_mode RW
+}
 
 /*
  * Clear SCSI Interrupt 0 (p. 3-20)
@@ -243,6 +250,7 @@
 	bit	CLRSELDI	0x20
 	bit	CLRSELINGO	0x10
 	bit	CLRSWRAP	0x08
+	bit	CLRIOERR	0x08	/* Ultra2 Only */
 	bit	CLRSPIORDY	0x02
 }
 
@@ -304,13 +312,12 @@
 	address			0x00d
 	access_mode RO
 	bit	OVERRUN		0x80
-	bit	SHVALID		0x40
-	bit	WIDE_RES	0x20
+	bit	SHVALID		0x40	/* Shaddow Layer non-zero */
 	bit	EXP_ACTIVE	0x10	/* SCSI Expander Active */
-	bit	CRCVALERR	0x08	/* CRC Value Error */
-	bit	CRCENDERR	0x04	/* CRC End Error */
-	bit	CRCREQERR	0x02	/* CRC REQ Error */
-	bit	DUAL_EDGE_ERROR	0x01	/* Invalid pins for Dual Edge phase */
+	bit	CRCVALERR	0x08	/* CRC doesn't match (U3 only) */
+	bit	CRCENDERR	0x04	/* No terminal CRC packet (U3 only) */
+	bit	CRCREQERR	0x02	/* Illegal CRC packet req (U3 only) */
+	bit	DUAL_EDGE_ERR	0x01	/* Incorrect data phase (U3 only) */
 	mask	SFCNT		0x1f
 }
 
@@ -376,12 +383,12 @@
  */
 register SCSIBUSL {
 	address			0x012
-	access_mode RO
+	access_mode RW
 }
 
 register SCSIBUSH {
 	address			0x013
-	access_mode RO
+	access_mode RW
 }
 
 /*
@@ -410,6 +417,7 @@
 	bit	STAGE3		0x04
 	bit	STAGE2		0x02
 	bit	STAGE1		0x01
+	alias	TARGIDIN
 }
 
 /*
@@ -424,6 +432,25 @@
 	bit	ONEBIT		0x08
 }
 
+register SCAMCTL {
+	address			0x01a
+	access_mode RW
+	bit	ENSCAMSELO	0x80
+	bit	CLRSCAMSELID	0x40
+	bit	ALTSTIM		0x20
+	bit	DFLTTID		0x10
+	mask	SCAMLVL		0x03
+}
+
+/*
+ * Target Mode Selecting in ID bitmask (aic7890/91/96/97)
+ */
+register TARGID {
+	address			0x01b
+	size			2
+	access_mode RW
+}
+
 /*
  * Serial Port I/O Cabability register (p. 4-95 aic7860 Data Book)
  * Indicates if external logic has been attached to the chip to
@@ -445,6 +472,59 @@
 	bit	SSPIOCPS	0x01	/* Termination and cable detection */
 }
 
+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
+	/* 7890 Definitions */
+	bit	BRDDAT4		0x10
+	bit	BRDDAT3		0x08
+	bit	BRDDAT2		0x04
+	bit	BRDRW_ULTRA2	0x02
+	bit	BRDSTB_ULTRA2	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
+}
 /*
  * SCSI Block Control (p. 3-32)
  * Controls Bus type and channel selection.  In a twin channel configuration
@@ -585,30 +665,22 @@
 	bit	ENABLE		0x01
 }
 
-register DSCOMMAND0 {
-	address			0x084
-	access_mode RW
-	bit	CACHETHEN	0x80
-	bit	DPARCKEN	0x40
-	bit	MPARCKEN	0x20
-	bit	EXTREQLCK	0x10
-	bit	INTSCBRAMSEL	0x08
-	bit	RAMPS		0x04
-	bit	USCBSIZE32	0x02
-	bit	CIOPARCKEN	0x01
-}
-
 /*
  * On the aic78X0 chips, Board Control is replaced by the DSCommand
  * register (p. 4-64)
  */
-register DSCOMMAND {
+register DSCOMMAND0 {
 	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 */
+	/* aic7890/91/96/97 only */
+	bit	INTSCBRAMSEL	0x08	/* Internal SCB RAM Select */
+	bit	RAMPS		0x04	/* External SCB RAM Present */
+	bit	USCBSIZE32	0x02	/* Use 32byte SCB Page Size */
+	bit	CIOPARCKEN	0x01	/* Internal bus parity error enable */
 }
 
 /*
@@ -622,7 +694,7 @@
 }
 
 /*
- * Bus Speed (p. 3-45)
+ * Bus Speed (p. 3-45) aic7770 only
  */
 register BUSSPD {
 	address			0x086
@@ -631,8 +703,26 @@
 	mask	STBOFF		0x38
 	mask	STBON		0x07
 	mask	DFTHRSH_100	0xc0
+	mask	DFTHRSH_75	0x80
+}
+
+/* aic7850/55/60/70/80/95 only */
+register DSPCISTATUS {
+	address			0x086
+	mask	DFTHRSH_100	0xc0
+}
+
+/* aic7890/91/96/97 only */
+register HS_MAILBOX {
+	address			0x086
+	mask	HOST_MAILBOX	0xF0
+	mask	SEQ_MAILBOX	0x0F
+	mask	HOST_TQINPOS	0x80	/* Boundary at either 0 or 128 */
 }
 
+const	HOST_MAILBOX_SHIFT	4
+const	SEQ_MAILBOX_SHIFT	0
+
 /*
  * Host Control (p. 3-47) R/W
  * Overall host control of the device.
@@ -668,7 +758,7 @@
 
 /*
  * SCB Pointer (p. 3-49)
- * Gate one of the four SCBs into the SCBARRAY window.
+ * Gate one of the SCBs into the SCBARRAY window.
  */
 register SCBPTR {
 	address			0x090
@@ -690,31 +780,48 @@
 	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	WIDE_RESIDUE	0x50|SEQINT	/* need kernel to back up */
-						/* the SG array for us */
-	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	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	IGN_WIDE_RES	0x40|SEQINT	/* Complex IGN Wide Res Msg */
+	mask	RESIDUAL	0x50|SEQINT	/* Residual byte count != 0 */
+	mask	HOST_MSG_LOOP	0x60|SEQINT	/*
+						 * The bus is ready for the
+						 * host to perform another
+						 * message transaction.  This
+						 * mechanism is used for things
+						 * like sync/wide negotiation
+						 * that require a kernel based
+						 * message state engine.
 						 */
-	mask	TRACEPOINT	0xb0|SEQINT
-	mask	TRACEPOINT2	0xc0|SEQINT
-	mask	MSGIN_PHASEMIS	0xd0|SEQINT	/*
-						 * Target changed phase on us
-						 * when we were expecting
-						 * another msgin byte.
+	mask	BAD_STATUS	0x70|SEQINT	/* Bad status from target */
+	mask	PERR_DETECTED	0x80|SEQINT	/*
+						 * Either the phase_lock
+						 * or inb_next routine has
+						 * noticed a parity error.
 						 */
-	mask	DATA_OVERRUN	0xe0|SEQINT	/*
+	mask	DATA_OVERRUN	0x90|SEQINT	/*
 						 * Target attempted to write
 						 * beyond the bounds of its
 						 * command.
 						 */
+	mask	MKMSG_FAILED	0xa0|SEQINT	/*
+						 * Target completed command
+						 * without honoring our ATN
+						 * request to issue a message. 
+						 */
+	mask	MISSED_BUSFREE	0xb0|SEQINT	/*
+						 * The sequencer never saw
+						 * the bus go free after
+						 * either a command complete
+						 * or disconnect message.
+						 */
+	mask	SCB_MISMATCH	0xc0|SEQINT	/*
+						 * Downloaded SCB's tag does
+						 * not match the entry we
+						 * intended to download.
+						 */
+	mask	NO_FREE_SCB	0xd0|SEQINT	/*
+						 * get_free_or_disc_scb failed.
+						 */
+	mask	OUT_OF_RANGE	0xe0|SEQINT
 
 	mask	SEQINT_MASK	0xf0|SEQINT	/* SEQINT Status Codes */
 	mask	INT_PEND  (BRKADRINT|SEQINT|SCSIINT|CMDCMPLT)
@@ -735,7 +842,6 @@
 	bit	SQPARERR	0x08
 	bit	ILLOPCODE	0x04
 	bit	ILLSADDR	0x02
-	bit	DSCTMOUT	0x02	/* Ultra3 only */
 	bit	ILLHADDR	0x01
 }
 
@@ -779,6 +885,16 @@
 	bit	FIFOEMP		0x01
 }
 
+register DFWADDR {
+	address			0x95
+	access_mode RW
+}
+
+register DFRADDR {
+	address			0x97
+	access_mode RW
+}
+
 register DFDAT {
 	address			0x099
 	access_mode RW
@@ -815,17 +931,6 @@
 }
 
 /*
- * SCSIDATL IMAGE Register (p. 5-104)
- * Write to this register also go to SCSIDATL but this register will preserve
- * the data for later reading as long as the SCSIDATL_IMGEN bit in the
- * OPTIONMODE register is set.
- */
-register SCSIDATL_IMG {
-	address			0x09c
-	access_mode RW
-}
-
-/*
  * Queue Out FIFO (p. 3-61)
  * Queue of SCBs that have completed and await the host
  */
@@ -834,21 +939,18 @@
 	access_mode WO
 }
 
-/*
- * CRC Control 1 Register (p. 5-105)
- * Control bits for the Ultra 160/m CRC facilities
- */
 register CRCCONTROL1 {
 	address			0x09d
 	access_mode RW
-	bit	CRCONSEEN	0x80 /* CRC ON Single Edge ENable */
-	bit	CRCVALCHKEN	0x40 /* CRC Value Check Enable */
-	bit	CRCENDCHKEN	0x20 /* CRC End Check Enable */
-	bit	CRCREQCHKEN	0x10
-	bit	TARGCRCENDEN	0x08 /* Enable End CRC transfer when target */
-	bit	TARGCRCCNTEN	0x04 /* Enable CRC transfer when target */
+	bit	CRCONSEEN		0x80
+	bit	CRCVALCHKEN		0x40
+	bit	CRCENDCHKEN		0x20
+	bit	CRCREQCHKEN		0x10
+	bit	TARGCRCENDEN		0x08
+	bit	TARGCRCCNTEN		0x04
 }
 
+
 /*
  * Queue Out Count (p. 3-61)
  * Number of queued SCBs in the Out FIFO
@@ -858,19 +960,15 @@
 	access_mode RO
 }
 
-/*
- * SCSI Phase Register (p. 5-106)
- * Current bus phase
- */
 register SCSIPHASE {
 	address			0x09e
 	access_mode RO
-	bit	SP_STATUS		0x20
-	bit	SP_COMMAND		0x10
-	bit	SP_MSG_IN		0x08
-	bit	SP_MSG_OUT		0x04
-	bit	SP_DATA_IN		0x02
-	bit	SP_DATA_OUT	0x01
+	bit	STATUS_PHASE	0x20
+	bit	COMMAND_PHASE	0x10
+	bit	MSG_IN_PHASE	0x08
+	bit	MSG_OUT_PHASE	0x04
+	bit	DATA_IN_PHASE	0x02
+	bit	DATA_OUT_PHASE	0x01
 }
 
 /*
@@ -887,33 +985,19 @@
  */
 scb {
 	address			0x0a0
-	SCB_CONTROL {
-		size	1
-		bit	MK_MESSAGE      0x80
-		bit	DISCENB         0x40
-		bit	TAG_ENB		0x20
-		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_CDB_PTR {
+		size	4
+		alias	SCB_RESIDUAL_DATACNT
+		alias	SCB_CDB_STORE
+		alias	SCB_TARGET_INFO
 	}
-	SCB_SGPTR {
+	SCB_RESIDUAL_SGPTR {
 		size	4
 	}
-	SCB_RESID_SGCNT {
+	SCB_SCSI_STATUS {
 		size	1
 	}
-	SCB_RESID_DCNT	{
+	SCB_CDB_STORE_PAD {
 		size	3
 	}
 	SCB_DATAPTR {
@@ -921,31 +1005,67 @@
 	}
 	SCB_DATACNT {
 		/*
-		 * Really only 3 bytes, but padded to make
-		 * the kernel's job easier.
+		 * The last byte is really the high address bits for
+		 * the data address.
 		 */
 		size	4
+		bit	SG_LAST_SEG		0x80	/* In the fourth byte */
+		mask	SG_HIGH_ADDR_BITS	0x7F	/* In the fourth byte */
 	}
-	SCB_CMDPTR {
+	SCB_SGPTR {
 		size	4
+		bit	SG_RESID_VALID	0x04	/* In the first byte */
+		bit	SG_FULL_RESID	0x02	/* In the first byte */
+		bit	SG_LIST_NULL	0x01	/* In the first byte */
+	}
+	SCB_CONTROL {
+		size	1
+		bit	TARGET_SCB			0x80
+		bit	DISCENB				0x40
+		bit	TAG_ENB				0x20
+		bit	MK_MESSAGE			0x10
+		bit	ULTRAENB			0x08
+		bit	DISCONNECTED			0x04
+		mask	SCB_TAG_TYPE			0x03
 	}
-	SCB_CMDLEN {
+	SCB_SCSIID {
+		size	1
+		bit	TWIN_CHNLB			0x80
+		mask	TWIN_TID			0x70
+		mask	TID				0xf0
+		mask	OID				0x0f
+	}
+	SCB_LUN {
+		mask	LID				0xff
 		size	1
 	}
 	SCB_TAG {
 		size	1
 	}
-	SCB_NEXT {
+	SCB_CDB_LEN {
 		size	1
 	}
-	SCB_PREV {
+	SCB_SCSIRATE {
 		size	1
 	}
-	SCB_BUSYTARGETS {
-		size	4
+	SCB_SCSIOFFSET {
+		size	1
+	}
+	SCB_NEXT {
+		size	1
+	}
+	SCB_64_SPARE {
+		size	16
+	}
+	SCB_64_BTT {
+		size	16
 	}
 }
 
+const	SCB_UPLOAD_SIZE		32
+const	SCB_DOWNLOAD_SIZE	32
+const	SCB_DOWNLOAD_SIZE_64	48
+
 const	SG_SIZEOF	0x08		/* sizeof(struct ahc_dma) */
 
 /* --------------------- AHA-2840-only definitions -------------------- */
@@ -969,11 +1089,6 @@
 
 /* --------------------- AIC-7870-only definitions -------------------- */
 
-register DSPCISTATUS {
-	address			0x086
-	mask	DFTHRSH_100	0xc0
-}
-
 register CCHADDR {
 	address			0x0E0
 	size 8
@@ -995,7 +1110,7 @@
 	address			0x0EB
 	bit	CCSGDONE	0x80
 	bit	CCSGEN		0x08
-	bit	FLAG		0x02
+	bit	SG_FETCH_NEEDED 0x02	/* Bit used for software state */
 	bit	CCSGRESET	0x01
 }
 
@@ -1021,6 +1136,14 @@
 	address			0xEC
 }
 
+/*
+ * SCB bank address (7895/7896/97 only)
+ */
+register SCBBADDR {
+	address			0x0F0
+	access_mode RW
+}
+
 register CCSCBPTR {
 	address			0x0F1
 }
@@ -1029,29 +1152,19 @@
 	address			0x0F4
 }
 
-register HESCB_QOFF {
-	address			0x0F5
-}
-
 register SNSCB_QOFF {
 	address			0x0F6
 }
 
-register SESCB_QOFF {
-	address			0x0F7
-}
-
 register SDSCB_QOFF {
 	address			0x0F8
 }
 
 register QOFF_CTLSTA {
 	address			0x0FA
-	bit	ESTABLISH_SCB_AVAIL	0x80
 	bit	SCB_AVAIL	0x40
 	bit	SNSCB_ROLLOVER	0x20
 	bit	SDSCB_ROLLOVER	0x10
-	bit	SESCB_ROLLOVER	0x08
 	mask	SCB_QSIZE	0x07
 	mask	SCB_QSIZE_256	0x06
 }
@@ -1078,66 +1191,22 @@
 	mask	WR_DFTHRSH_MAX	0x70
 }
 
-register SG_CACHEPTR {
-	access_mode RW
+register SG_CACHE_PRE {
+	access_mode WO
 	address			0x0fc
-	mask	SG_USER_DATA	0xfc
+	mask	SG_ADDR_MASK	0xf8
+	bit	ODD_SEG		0x04
 	bit	LAST_SEG	0x02
 	bit	LAST_SEG_DONE	0x01
 }
 
-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
-	/* 7890 Definitions */
-	bit	BRDDAT4		0x10
-	bit	BRDDAT3		0x08
-	bit	BRDDAT2		0x04
-	bit	BRDRW_ULTRA2	0x02
-	bit	BRDSTB_ULTRA2	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
+register SG_CACHE_SHADOW {
+	access_mode RO
+	address			0x0fc
+	mask	SG_ADDR_MASK	0xf8
+	bit	ODD_SEG		0x04
+	bit	LAST_SEG	0x02
+	bit	LAST_SEG_DONE	0x01
 }
 /* ---------------------- Scratch RAM Offsets ------------------------- */
 /* These offsets are either to values that are initialized by the board's
@@ -1160,21 +1229,46 @@
 	/*
 	 * 1 byte per target starting at this address for configuration values
 	 */
-	TARG_SCSIRATE {
+	BUSY_TARGETS {
+		alias		TARG_SCSIRATE
 		size		16
 	}
 	/*
-	 * Bit vector of targets that have ULTRA enabled.
+	 * Bit vector of targets that have ULTRA enabled as set by
+	 * the BIOS.  The Sequencer relies on a per-SCB field to
+	 * control whether to enable Ultra transfers or not.  During
+	 * initialization, we read this field and reuse it for 2
+	 * entries in the busy target table.
 	 */
 	ULTRA_ENB {
+		alias		CMDSIZE_TABLE
 		size		2
 	}
 	/*
-	 * Bit vector of targets that have disconnection disabled.
+	 * Bit vector of targets that have disconnection disabled as set by
+	 * the BIOS.  The Sequencer relies in a per-SCB field to control the
+	 * disconnect priveldge.  During initialization, we read this field
+	 * and reuse it for 2 entries in the busy target table.
 	 */
 	DISC_DSB {
 		size		2
 	}
+	CMDSIZE_TABLE_TAIL {
+		size		4
+	}
+	/*
+	 * Partial transfer past cacheline end to be
+	 * transferred using an extra S/G.
+	 */
+	MWI_RESIDUAL {
+		size		1
+	}
+	/*
+	 * SCBID of the next SCB to be started by the controller.
+	 */
+	NEXT_QUEUED_SCB {
+		size		1
+	}
 	/*
 	 * Single byte buffer used to designate the type or message
 	 * to send to a target.
@@ -1198,29 +1292,27 @@
 	}
 	SEQ_FLAGS {
 		size		1
-		bit	IDENTIFY_SEEN	0x80
-		bit	SCBPTR_VALID	0x20
-		bit	DPHASE		0x10
-		bit	AMTARGET	0x08
-		bit	WIDE_BUS	0x02
-		bit	TWIN_BUS	0x01
+		bit	IDENTIFY_SEEN		0x80
+		bit	TARGET_CMD_IS_TAGGED	0x40
+		bit	DPHASE			0x20
+		/* Target flags */
+		bit	TARG_CMD_PENDING	0x10
+		bit	CMDPHASE_PENDING	0x08
+		bit	DPHASE_PENDING		0x04
+		bit	SPHASE_PENDING		0x02
+		bit	NO_DISCONNECT		0x01
 	}
 	/*
 	 * Temporary storage for the
 	 * target/channel/lun of a
 	 * reconnecting target
 	 */
-	SAVED_TCL {
+	SAVED_SCSIID {
 		size		1
 	}
-	/* Working value of the number of SG segments left */
-	SG_COUNT {
+	SAVED_LUN {
 		size		1
 	}
-	/* Working value of SG pointer */
-	SG_NEXT	{
-		size		4
-	}
 	/*
 	 * The last bus phase as seen by the sequencer. 
 	 */
@@ -1261,23 +1353,25 @@
 		size		1
 	}
 	/*
-	 * Address of the hardware scb array in the host.
+	 * head of list of SCBs that have
+	 * completed but have not been
+	 * put into the qoutfifo.
 	 */
-	HSCB_ADDR {
-		size		4
+	COMPLETE_SCBH {
+		size		1
 	}
 	/*
-	 * Address of the 256 byte array storing the SCBID of outstanding
-	 * untagged SCBs indexed by TCL.
+	 * Address of the hardware scb array in the host.
 	 */
-	SCBID_ADDR {
+	HSCB_ADDR {
 		size		4
 	}
 	/*
-	 * Address of the array of command descriptors used to store
-	 * information about incoming selections.
+	 * Base address of our shared data with the kernel driver in host
+	 * memory.  This includes the qoutfifo and target mode
+	 * incoming command queue.
 	 */
-	TMODE_CMDADDR {
+	SHARED_DATA_ADDR {
 		size		4
 	}
 	KERNEL_QINPOS {
@@ -1290,18 +1384,25 @@
 		size		1
 	}
 	/*
-	 * Offset into the command descriptor array for the next
-	 * available desciptor to use.
+	 * Kernel and sequencer offsets into the queue of
+	 * incoming target mode command descriptors.  The
+	 * queue is full when the KERNEL_TQINPOS == TQINPOS.
 	 */
-	TMODE_CMDADDR_NEXT {
+	KERNEL_TQINPOS {
+		size		1
+	}
+	TQINPOS {                
 		size		1
 	}
 	ARG_1 {
 		size		1
-		mask	SEND_MSG	0x80
-		mask	SEND_SENSE	0x40
-		mask	SEND_REJ	0x20
-		mask	MSGOUT_PHASEMIS	0x10
+		mask	SEND_MSG		0x80
+		mask	SEND_SENSE		0x40
+		mask	SEND_REJ		0x20
+		mask	MSGOUT_PHASEMIS		0x10
+		mask	EXIT_MSG_LOOP		0x08
+		mask	CONT_MSG_LOOP		0x04
+		mask	CONT_TARG_SESSION	0x02
 		alias	RETURN_1
 	}
 	ARG_2 {
@@ -1317,15 +1418,49 @@
 	}
 
 	/*
-	 * Number of times we have filled the CCSGRAM with prefetched
-	 * SG elements.
+	 * Interrupt kernel for a message to this target on
+	 * the next transaction.  This is usually used for
+	 * negotiation requests.
+	 */
+	TARGET_MSG_REQUEST {
+		size		2
+	}
+
+	/*
+	 * Sequences the kernel driver has okayed for us.  This allows
+	 * the driver to do things like prevent initiator or target
+	 * operations.
 	 */
-	PREFETCH_CNT {
+	SCSISEQ_TEMPLATE {
 		size		1
+		bit	ENSELO		0x40
+		bit	ENSELI		0x20
+		bit	ENRSELI		0x10
+		bit	ENAUTOATNO	0x08
+		bit	ENAUTOATNI	0x04
+		bit	ENAUTOATNP	0x02
 	}
 
+	/*
+	 * Track whether the transfer byte count for
+	 * the current data phase is odd.
+	 */
+	DATA_COUNT_ODD {
+		size		1
+	}
 
 	/*
+	 * The initiator specified tag for this target mode transaction.
+	 */
+	INITIATOR_TAG {
+		size		1
+	}
+
+	SEQ_FLAGS2 {
+		size		1
+		bit	SCB_DMA		0x01
+	}
+	/*
 	 * 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.
@@ -1335,9 +1470,16 @@
 		size		1
 		bit	TERM_ENB	0x80
 		bit	RESET_SCSI	0x40
+		bit	ENSPCHK		0x20
 		mask	HSCSIID		0x07	/* our SCSI ID */
 		mask	HWSCSIID	0x0f	/* our SCSI ID if Wide Bus */
 	}
+	INTDEF {
+		address		0x05c
+		size		1
+		bit	EDGE_TRIG	0x80
+		mask	VECTOR		0x0f
+	}
 	HOSTCONF {
 		address		0x05d
 		size		1
@@ -1358,16 +1500,13 @@
 	}
 }
 
+const TID_SHIFT		4
 const SCB_LIST_NULL	0xff
+const TARGET_CMD_CMPLT	0xfe
 
 const CCSGADDR_MAX	0x80
 const CCSGRAM_MAXSEGS	16
 
-/* Offsets into the SCBID array where different data is stored */
-const UNTAGGEDSCB_OFFSET	0
-const QOUTFIFO_OFFSET		1
-const QINFIFO_OFFSET		2
-
 /* WDTR Message values */
 const BUS_8_BIT			0x00
 const BUS_16_BIT		0x01
@@ -1381,16 +1520,23 @@
 
 /* Target mode command processing constants */
 const CMD_GROUP_CODE_SHIFT	0x05
-const CMD_GROUP0_BYTE_DELTA	-4
-const CMD_GROUP2_BYTE_DELTA	-6
-const CMD_GROUP4_BYTE_DELTA	4
-const CMD_GROUP5_BYTE_DELTA	11
 
-/*
- * Downloaded (kernel inserted) constants
- */
+const STATUS_BUSY		0x08
+const STATUS_QUEUE_FULL	0x28
+const SCB_TARGET_PHASES		0
+const SCB_TARGET_DATA_DIR	1
+const SCB_TARGET_STATUS		2
+const SCB_INITIATOR_TAG		3
+const TARGET_DATA_IN		1
 
 /*
- * Number of command descriptors in the command descriptor array.
+ * Downloaded (kernel inserted) constants
  */
-const TMODE_NUMCMDS	download
+/* Offsets into the SCBID array where different data is stored */
+const QOUTFIFO_OFFSET download
+const QINFIFO_OFFSET download
+const CACHESIZE_MASK download
+const INVERTED_CACHESIZE_MASK download
+const SG_PREFETCH_CNT download
+const SG_PREFETCH_ALIGN_MASK download
+const SG_PREFETCH_ADDR_MASK download
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx.seq linux/drivers/scsi/aic7xxx/aic7xxx.seq
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx.seq	Wed Jun 21 17:25:03 2000
+++ linux/drivers/scsi/aic7xxx/aic7xxx.seq	Mon Dec  3 22:29:50 2001
@@ -1,7 +1,7 @@
 /*
  * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
  *
- * Copyright (c) 1994-1999 Justin Gibbs.
+ * Copyright (c) 1994-2001 Justin Gibbs.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -9,16 +9,12 @@
  * 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.
+ *    without modification.
  * 2. 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.
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("GPL").
  *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
@@ -32,7 +28,9 @@
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- *	$Id: aic7xxx.seq,v 1.77 1998/06/28 02:58:57 gibbs Exp $
+ * $Id: //depot/src/aic7xxx/aic7xxx.seq#22 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.seq,v 1.106 2000/11/12 05:19:46 gibbs Exp $
  */
 
 #include "aic7xxx.reg"
@@ -48,7 +46,7 @@
  * 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
+ * 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
@@ -56,200 +54,386 @@
  * automatically consume the entries.
  */
 
-reset:
-	clr	SCSISIGO;		/* De-assert BSY */
-	and	SXFRCTL1, ~BITBUCKET;
-	/* Always allow reselection */
-	mvi	SCSISEQ, ENRSELI|ENAUTOATNP;
-
-	if ((p->features & AHC_CMD_CHAN) != 0) {
-		/* Ensure that no DMA operations are in progress */
-		clr	CCSGCTL;
-		clr	CCSCBCTL;
-	}
-
-	call	clear_target_state;
+bus_free_sel:
+	/*
+	 * Turn off the selection hardware.  We need to reset the
+	 * selection request in order to perform a new selection.
+	 */
+	and	SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
+	and	SIMODE1, ~ENBUSFREE;
 poll_for_work:
+	call	clear_target_state;
 	and	SXFRCTL0, ~SPIOEN;
-	if ((p->features & AHC_QUEUE_REGS) == 0) {
-		mov	A, QINPOS;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		clr	SCSIBUSL;
 	}
-poll_for_work_loop:
-	if ((p->features & AHC_QUEUE_REGS) == 0) {
-		and	SEQCTL, ~PAUSEDIS;
-	}
-	test	SSTAT0, SELDO|SELDI	jnz selection;
-	test	SCSISEQ, ENSELO	jnz poll_for_work;
-	if ((p->features & AHC_TWIN) != 0) {
-		/*
-		 * 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.
-		 */
+	test	SCSISEQ, ENSELO	jnz poll_for_selection;
+	if ((ahc->features & AHC_TWIN) != 0) {
 		xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
-		test	SSTAT0, SELDO|SELDI	jnz selection;
-		test	SCSISEQ, ENSELO	jnz poll_for_work;
-		xor	SBLKCTL,SELBUSB;	/* Toggle back */
+		test	SCSISEQ, ENSELO		jnz poll_for_selection;
 	}
 	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
+poll_for_work_loop:
+	if ((ahc->features & AHC_TWIN) != 0) {
+		xor	SBLKCTL,SELBUSB;	/* Toggle to the other bus */
+	}
+	test	SSTAT0, SELDO|SELDI	jnz selection;
 test_queue:
 	/* Has the driver posted any work for us? */
-	if ((p->features & AHC_QUEUE_REGS) != 0) {
+BEGIN_CRITICAL
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
 		test	QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
-		mov	NONE, SNSCB_QOFF;
-		inc	QINPOS;
 	} else {
-		or	SEQCTL, PAUSEDIS;
+		mov	A, QINPOS;
 		cmp	KERNEL_QINPOS, A je poll_for_work_loop;
-		inc	QINPOS;
-		and	SEQCTL, ~PAUSEDIS;
 	}
+	mov	ARG_1, NEXT_QUEUED_SCB;
 
-/*
- * 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 ((p->flags & AHC_PAGESCBS) != 0) {
+	/*
+	 * We have at least one queued SCB now and we don't have any 
+	 * SCBs in the list of SCBs awaiting selection.  Allocate a
+	 * card SCB for the host's SCB and get to work on it.
+	 */
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
 		mov	ALLZEROS	call	get_free_or_disc_scb;
-	}
-
-dequeue_scb:
-	add	A, -1, QINPOS;
-	mvi	QINFIFO_OFFSET call fetch_byte;
-
-	if ((p->flags & AHC_PAGESCBS) == 0) {
+	} else {
 		/* In the non-paging case, the SCBID == hardware SCB index */
-		mov	SCBPTR, RETURN_2;
+		mov	SCBPTR, ARG_1;
 	}
+	or	SEQ_FLAGS2, SCB_DMA;
+END_CRITICAL
 dma_queued_scb:
-/*
- * DMA the SCB from host ram into the current SCB location.
- */
+	/*
+	 * DMA the SCB from host ram into the current SCB location.
+	 */
 	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
-	mov	RETURN_2	 call dma_scb;
-
-/*
- * Preset the residual fields in case we never go through a data phase.
- * This isn't done by the host so we can avoid a DMA to clear these
- * fields for the normal case of I/O that completes without underrun
- * or overrun conditions.
- */
-	if ((p->features & AHC_CMD_CHAN) != 0) {
-		bmov    SCB_RESID_DCNT, SCB_DATACNT, 3;
-	} else {
-		mov     SCB_RESID_DCNT[0],SCB_DATACNT[0];
-		mov     SCB_RESID_DCNT[1],SCB_DATACNT[1];
-		mov     SCB_RESID_DCNT[2],SCB_DATACNT[2];
-	}
-	mov     SCB_RESID_SGCNT, SCB_SGCOUNT;
-
-start_scb:
+	mov	ARG_1	call dma_scb;
 	/*
-	 * Place us on the waiting list in case our selection
-	 * doesn't win during bus arbitration.
+	 * Check one last time to see if this SCB was canceled
+	 * before we completed the DMA operation.  If it was,
+	 * the QINFIFO next pointer will not match our saved
+	 * value.
 	 */
+	mov	A, ARG_1;
+BEGIN_CRITICAL
+	cmp	NEXT_QUEUED_SCB, A jne abort_qinscb;
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		cmp	SCB_TAG, A je . + 2;
+		mvi	SCB_MISMATCH call set_seqint;
+	}
+	mov	NEXT_QUEUED_SCB, SCB_NEXT;
 	mov	SCB_NEXT,WAITING_SCBH;
 	mov	WAITING_SCBH, SCBPTR;
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		mov	NONE, SNSCB_QOFF;
+	} else {
+		inc	QINPOS;
+	}
+	and	SEQ_FLAGS2, ~SCB_DMA;
+END_CRITICAL
 start_waiting:
 	/*
-	 * Pull the first entry off of the waiting SCB list.
+	 * Start the first entry on the waiting SCB list.
 	 */
 	mov	SCBPTR, WAITING_SCBH;
 	call	start_selection;
-	jmp	poll_for_work;
+
+poll_for_selection:
+	/*
+	 * Twin channel devices cannot handle things like SELTO
+	 * interrupts on the "background" channel.  So, while
+	 * selecting, keep polling the current channel until
+	 * either a selection or reselection occurs.
+	 */
+	test	SSTAT0, SELDO|SELDI	jz poll_for_selection;
+
+selection:
+	/*
+	 * We aren't expecting a bus free, so interrupt
+	 * the kernel driver if it happens.
+	 */
+	mvi	CLRSINT1,CLRBUSFREE;
+	or	SIMODE1, ENBUSFREE;
+
+	/*
+	 * Guard against a bus free after (re)selection
+	 * but prior to enabling the busfree interrupt.  SELDI
+	 * and SELDO will be cleared in that case.
+	 */
+	test	SSTAT0, SELDI|SELDO	jz bus_free_sel;
+	test	SSTAT0,SELDO	jnz select_out;
+select_in:
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+			test	SSTAT0, TARGET	jz initiator_reselect;
+		}
+		mvi	CLRSINT0, CLRSELDI;
+
+		/*
+		 * We've just been selected.  Assert BSY and
+		 * setup the phase for receiving messages
+		 * from the target.
+		 *
+		 * If bus reset interrupts have been disabled (from a
+		 * previous reset), re-enable them now.  Resets are only
+		 * of interest when we have outstanding transactions, so
+		 * we can safely defer re-enabling the interrupt until,
+		 * as a target, we start receiving transactions again.
+		 */
+		test	SIMODE1, ENSCSIRST	jnz . + 3;
+		mvi	CLRSINT1, CLRSCSIRSTI;
+		or	SIMODE1, ENSCSIRST;
+		mvi	SCSISIGO, P_MESGOUT|BSYO;
+
+		/*
+		 * Setup the DMA for sending the identify and
+		 * command information.
+		 */
+		or	SEQ_FLAGS, CMDPHASE_PENDING;
+
+		mov     A, TQINPOS;
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mvi	DINDEX, CCHADDR;
+			mvi	SHARED_DATA_ADDR call set_32byte_addr;
+			mvi	CCSCBCTL, CCSCBRESET;
+		} else {
+			mvi	DINDEX, HADDR;
+			mvi	SHARED_DATA_ADDR call set_32byte_addr;
+			mvi	DFCNTRL, FIFORESET;
+		}
+
+		/* Initiator that selected us */
+		and	SAVED_SCSIID, SELID_MASK, SELID;
+		/* The Target ID we were selected at */
+		if ((ahc->features & AHC_MULTI_TID) != 0) {
+			and	A, OID, TARGIDIN;
+		} else if ((ahc->features & AHC_ULTRA2) != 0) {
+			and	A, OID, SCSIID_ULTRA2;
+		} else {
+			and	A, OID, SCSIID;
+		}
+		or	SAVED_SCSIID, A;
+		if ((ahc->features & AHC_TWIN) != 0) {
+			test 	SBLKCTL, SELBUSB jz . + 2;
+			or	SAVED_SCSIID, TWIN_CHNLB;
+		}
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mov	CCSCBRAM, SAVED_SCSIID;
+		} else {
+			mov	DFDAT, SAVED_SCSIID;
+		}
+
+		/*
+		 * If ATN isn't asserted, the target isn't interested
+		 * in talking to us.  Go directly to bus free.
+		 * XXX SCSI-1 may require us to assume lun 0 if
+		 * ATN is false.
+		 */
+		test	SCSISIGI, ATNI	jz	target_busfree;
+
+		/*
+		 * Watch ATN closely now as we pull in messages from the
+		 * initiator.  We follow the guidlines from section 6.5
+		 * of the SCSI-2 spec for what messages are allowed when.
+		 */
+		call	target_inb;
+
+		/*
+		 * Our first message must be one of IDENTIFY, ABORT, or
+		 * BUS_DEVICE_RESET.
+		 */
+		test	DINDEX, MSG_IDENTIFYFLAG jz host_target_message_loop;
+		/* Store for host */
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mov	CCSCBRAM, DINDEX;
+		} else {
+			mov	DFDAT, DINDEX;
+		}
+
+		/* Remember for disconnection decision */
+		test	DINDEX, MSG_IDENTIFY_DISCFLAG jnz . + 2;
+		/* XXX Honor per target settings too */
+		or	SEQ_FLAGS, NO_DISCONNECT;
+
+		test	SCSISIGI, ATNI	jz	ident_messages_done;
+		call	target_inb;
+		/*
+		 * If this is a tagged request, the tagged message must
+		 * immediately follow the identify.  We test for a valid
+		 * tag message by seeing if it is >= MSG_SIMPLE_Q_TAG and
+		 * < MSG_IGN_WIDE_RESIDUE.
+		 */
+		add	A, -MSG_SIMPLE_Q_TAG, DINDEX;
+		jnc	ident_messages_done;
+		add	A, -MSG_IGN_WIDE_RESIDUE, DINDEX;
+		jc	ident_messages_done;
+		/* Store for host */
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mov	CCSCBRAM, DINDEX;
+		} else {
+			mov	DFDAT, DINDEX;
+		}
+		
+		/*
+		 * If the initiator doesn't feel like providing a tag number,
+		 * we've got a failed selection and must transition to bus
+		 * free.
+		 */
+		test	SCSISIGI, ATNI	jz	target_busfree;
+
+		/*
+		 * Store the tag for the host.
+		 */
+		call	target_inb;
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mov	CCSCBRAM, DINDEX;
+		} else {
+			mov	DFDAT, DINDEX;
+		}
+		mov	INITIATOR_TAG, DINDEX;
+		or	SEQ_FLAGS, TARGET_CMD_IS_TAGGED;
+		test	SCSISIGI, ATNI	jz . + 2;
+		/* Initiator still wants to give us messages */
+		call	target_inb;
+		jmp	ident_messages_done;
+
+		/*
+		 * Pushed message loop to allow the kernel to
+		 * run it's own target mode message state engine.
+		 */
+host_target_message_loop:
+		mvi	HOST_MSG_LOOP call set_seqint;
+		cmp	RETURN_1, EXIT_MSG_LOOP	je target_ITloop;
+		test	SSTAT0, SPIORDY jz .;
+		jmp	host_target_message_loop;
+
+ident_messages_done:
+		/* If ring buffer is full, return busy or queue full */
+		if ((ahc->features & AHC_HS_MAILBOX) != 0) {
+			and	A, HOST_TQINPOS, HS_MAILBOX;
+		} else {
+			mov	A, KERNEL_TQINPOS;
+		}
+		cmp	TQINPOS, A jne tqinfifo_has_space;
+		mvi	P_STATUS|BSYO call change_phase;
+		test	SEQ_FLAGS, TARGET_CMD_IS_TAGGED jz . + 3;
+		mvi	STATUS_QUEUE_FULL call target_outb;
+		jmp	target_busfree_wait;
+		mvi	STATUS_BUSY call target_outb;
+		jmp	target_busfree_wait;
+tqinfifo_has_space:	
+		/* Terminate the ident list */
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mvi	CCSCBRAM, SCB_LIST_NULL;
+		} else {
+			mvi	DFDAT, SCB_LIST_NULL;
+		}
+		or	SEQ_FLAGS, TARG_CMD_PENDING|IDENTIFY_SEEN;
+		test	SCSISIGI, ATNI	jnz target_mesgout_pending;
+		jmp	target_ITloop;
+	}
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+/*
+ * 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.
+ */
+initiator_reselect:
+	/* XXX test for and handle ONE BIT condition */
+	or	SXFRCTL0, SPIOEN|CLRSTCNT|CLRCHN;
+	and	SAVED_SCSIID, SELID_MASK, SELID;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		and	A, OID, SCSIID_ULTRA2;
+	} else {
+		and	A, OID, SCSIID;
+	}
+	or	SAVED_SCSIID, A;
+	if ((ahc->features & AHC_TWIN) != 0) {
+		test	SBLKCTL, SELBUSB	jz . + 2;
+		or	SAVED_SCSIID, TWIN_CHNLB;
+	}
+	mvi	CLRSINT0, CLRSELDI;
+	jmp	ITloop;
+}
+
+abort_qinscb:
+	call	add_scb_to_free_list;
+	jmp	poll_for_work_loop;
 
 start_selection:
-	if ((p->features & AHC_TWIN) != 0) {
+	/*
+	 * If bus reset interrupts have been disabled (from a previous
+	 * reset), re-enable them now.  Resets are only of interest
+	 * when we have outstanding transactions, so we can safely
+	 * defer re-enabling the interrupt until, as an initiator,
+	 * we start sending out transactions again.
+	 */
+	test	SIMODE1, ENSCSIRST	jnz . + 3;
+	mvi	CLRSINT1, CLRSCSIRSTI;
+	or	SIMODE1, ENSCSIRST;
+	if ((ahc->features & AHC_TWIN) != 0) {
 		and	SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
-		and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
-		or	SINDEX,A;
+		test	SCB_SCSIID, TWIN_CHNLB jz . + 2;
+		or	SINDEX, SELBUSB;
 		mov	SBLKCTL,SINDEX;		/* select channel */
 	}
 initialize_scsiid:
-	if ((p->features & AHC_ULTRA2) != 0) {
-		and	A, TID, SCB_TCL;	/* Get target ID */
-		and	SCSIID_ULTRA2, OID;	/* Clear old target */
-		or	SCSIID_ULTRA2, A;
-	} else {
-		and	A, TID, SCB_TCL;	/* Get target ID */
-		and	SCSIID, OID;		/* Clear old target */
-		or	SCSIID, A;
-	}
-	mov	SCSIDATL, ALLZEROS;		/* clear out the latched */
-						/* data register, this */
-						/* fixes a bug on some */
-						/* controllers where the */
-						/* last byte written to */
-						/* this register can leak */
-						/* onto the data bus at */
-						/* bad times, such as during */
-						/* selection timeouts */
-	mvi	SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		mov	SCSIID_ULTRA2, SCB_SCSIID;
+	} else if ((ahc->features & AHC_TWIN) != 0) {
+		and	SCSIID, TWIN_TID|OID, SCB_SCSIID;
+	} else {
+		mov	SCSIID, SCB_SCSIID;
+	}
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		mov	SINDEX, SCSISEQ_TEMPLATE;
+		test	SCB_CONTROL, TARGET_SCB jz . + 2;
+		or	SINDEX, TEMODE;
+		mov	SCSISEQ, SINDEX ret;
+	} else {
+		mov	SCSISEQ, SCSISEQ_TEMPLATE ret;
+	}
 
 /*
- * Initialize Ultra mode setting and clear the SCSI channel.
+ * Initialize transfer settings and clear the SCSI channel.
  * SINDEX should contain any additional bit's the client wants
- * set in SXFRCTL0.
+ * set in SXFRCTL0.  We also assume that the current SCB is
+ * a valid SCB for the target we wish to talk to.
  */
 initialize_channel:
-	or	SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
-	if ((p->features & AHC_ULTRA) != 0) {
-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, SPIOEN|CLRSTCNT|CLRCHN;
+set_transfer_settings:
+	if ((ahc->features & AHC_ULTRA) != 0) {
+		test	SCB_CONTROL, ULTRAENB jz . + 2;
 		or	SXFRCTL0, FAST20;
 	} 
-/*
- * Initialize SCSIRATE with the appropriate value for this target.
- * The SCSIRATE settings for each target are stored in an array
- * based at TARG_SCSIRATE.
- */
-ndx_dtr:
-	shr	A,4,SAVED_TCL;
-	if ((p->features & AHC_TWIN) != 0) {
-		test	SBLKCTL,SELBUSB	jz ndx_dtr_2;
-		or	SAVED_TCL, SELBUSB; 
-		or	A,0x08;			/* Channel B entries add 8 */
-ndx_dtr_2:
-	}
-
-	if ((p->features & AHC_ULTRA2) != 0) {
-		add	SINDEX, TARG_OFFSET, A;
-		mov	SCSIOFFSET, SINDIR;
+	/*
+	 * Initialize SCSIRATE with the appropriate value for this target.
+	 */
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		bmov	SCSIRATE, SCB_SCSIRATE, 2 ret;
+	} else {
+		mov	SCSIRATE, SCB_SCSIRATE ret;
 	}
 
-	add	SINDEX,TARG_SCSIRATE,A;
-	mov	SCSIRATE,SINDIR ret;
-
-
-selection:
-	test	SSTAT0,SELDO	jnz select_out;
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
 /*
- * 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.
+ * We carefully toggle SPIOEN to allow us to return the 
+ * message byte we receive so it can be checked prior to
+ * driving REQ on the bus for the next byte.
  */
-initiator_reselect:
-	mvi	CLRSINT0, CLRSELDI;
-	/* XXX test for and handle ONE BIT condition */
-	and	SAVED_TCL, SELID_MASK, SELID;
-	mvi	CLRSINT1,CLRBUSFREE;
-	or	SIMODE1, ENBUSFREE;		/*
-						 * We aren't expecting a
-						 * bus free, so interrupt
-						 * the kernel driver if it
-						 * happens.
-						 */
-	mvi	SPIOEN call	initialize_channel;
-	mvi	MSG_OUT, MSG_NOOP;		/* No message to send */
-	jmp	ITloop;
+target_inb:
+	/*
+	 * Drive REQ on the bus by enabling SCSI PIO.
+	 */
+	or	SXFRCTL0, SPIOEN;
+	/* Wait for the byte */
+	test	SSTAT0, SPIORDY jz .;
+	/* Prevent our read from triggering another REQ */
+	and	SXFRCTL0, ~SPIOEN;
+	/* Save latched contents */
+	mov	DINDEX, SCSIDATL ret;
+}
 
 /*
  * After the selection, remove this SCB from the "waiting SCB"
@@ -259,33 +443,198 @@
  */
 select_out:
 	/* Turn off the selection hardware */
-	mvi	SCSISEQ, ENRSELI|ENAUTOATNP;	/*
-						 * ATN on parity errors
-						 * for "in" phases
-						 */
+	and	SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
 	mvi	CLRSINT0, CLRSELDO;
 	mov	SCBPTR, WAITING_SCBH;
 	mov	WAITING_SCBH,SCB_NEXT;
-	mov	SAVED_TCL, SCB_TCL;
-	mvi	CLRSINT1,CLRBUSFREE;
-	or	SIMODE1, ENBUSFREE;		/*
-						 * We aren't expecting a
-						 * bus free, so interrupt
-						 * the kernel driver if it
-						 * happens.
-						 */
-	mvi	SPIOEN call	initialize_channel;
-/*
- * As soon as we get a successful selection, the target should go
- * into the message out phase since we have ATN asserted.
- */
+	mov	SAVED_SCSIID, SCB_SCSIID;
+	mov	SAVED_LUN, SCB_LUN;
+	call	initialize_channel;
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		test	SSTAT0, TARGET	jz initiator_select;
+
+		/*
+		 * We've just re-selected an initiator.
+		 * Assert BSY and setup the phase for
+		 * sending our identify messages.
+		 */
+		mvi	P_MESGIN|BSYO call change_phase;
+
+		/*
+		 * Start out with a simple identify message.
+		 */
+		or	SCB_LUN, MSG_IDENTIFYFLAG call target_outb;
+
+		/*
+		 * If we are the result of a tagged command, send
+		 * a simple Q tag and the tag id.
+		 */
+		test	SCB_CONTROL, TAG_ENB	jz . + 3;
+		mvi	MSG_SIMPLE_Q_TAG call target_outb;
+		mov	SCB_TARGET_INFO[SCB_INITIATOR_TAG] call target_outb;
+target_synccmd:
+		/*
+		 * Now determine what phases the host wants us
+		 * to go through.
+		 */
+		mov	SEQ_FLAGS, SCB_TARGET_INFO[SCB_TARGET_PHASES];
+		
+target_ITloop:
+		/*
+		 * Start honoring ATN signals now that
+		 * we properly identified ourselves.
+		 */
+		test	SCSISIGI, ATNI			jnz target_mesgout;
+		test	SEQ_FLAGS, CMDPHASE_PENDING	jnz target_cmdphase;
+		test	SEQ_FLAGS, DPHASE_PENDING	jnz target_dphase;
+		test	SEQ_FLAGS, SPHASE_PENDING	jnz target_sphase;
+
+		/*
+		 * No more work to do.  Either disconnect or not depending
+		 * on the state of NO_DISCONNECT.
+		 */
+		test	SEQ_FLAGS, NO_DISCONNECT jz target_disconnect; 
+		if ((ahc->flags & AHC_PAGESCBS) != 0) {
+			mov	ALLZEROS	call	get_free_or_disc_scb;
+		}
+		mov	RETURN_1, ALLZEROS;
+		call	complete_target_cmd;
+		cmp	RETURN_1, CONT_MSG_LOOP jne .;
+		mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+		mov	SCB_TAG	 call dma_scb;
+		jmp	target_synccmd;
+
+target_mesgout:
+		mvi	SCSISIGO, P_MESGOUT|BSYO;
+target_mesgout_continue:
+		call	target_inb;
+target_mesgout_pending:
+		/* Local Processing goes here... */
+		jmp	host_target_message_loop;
+		
+target_disconnect:
+		mvi	P_MESGIN|BSYO call change_phase;
+		test	SEQ_FLAGS, DPHASE	jz . + 2;
+		mvi	MSG_SAVEDATAPOINTER call target_outb;
+		mvi	MSG_DISCONNECT call target_outb;
+
+target_busfree_wait:
+		/* Wait for preceeding I/O session to complete. */
+		test	SCSISIGI, ACKI jnz .;
+target_busfree:
+		and	SIMODE1, ~ENBUSFREE;
+		if ((ahc->features & AHC_ULTRA2) != 0) {
+			clr	SCSIBUSL;
+		}
+		clr	SCSISIGO;
+		mvi	LASTPHASE, P_BUSFREE;
+		call	complete_target_cmd;
+		jmp	poll_for_work;
+
+target_cmdphase:
+		mvi	P_COMMAND|BSYO call change_phase;
+		call	target_inb;
+		mov	A, DINDEX;
+		/* Store for host */
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mov	CCSCBRAM, A;
+		} else {
+			mov	DFDAT, A;
+		}
+
+		/*
+		 * Determine the number of bytes to read
+		 * based on the command group code via table lookup.
+		 * We reuse the first 8 bytes of the TARG_SCSIRATE
+		 * BIOS array for this table. Count is one less than
+		 * the total for the command since we've already fetched
+		 * the first byte.
+		 */
+		shr	A, CMD_GROUP_CODE_SHIFT;
+		add	SINDEX, CMDSIZE_TABLE, A;
+		mov	A, SINDIR;
+
+		test	A, 0xFF jz command_phase_done;
+		or	SXFRCTL0, SPIOEN;
+command_loop:
+		test	SSTAT0, SPIORDY jz .;
+		cmp	A, 1 jne . + 2;
+		and	SXFRCTL0, ~SPIOEN;	/* Last Byte */
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			mov	CCSCBRAM, SCSIDATL;
+		} else {
+			mov	DFDAT, SCSIDATL;
+		}
+		dec	A;
+		test	A, 0xFF jnz command_loop;
+
+command_phase_done:
+		and	SEQ_FLAGS, ~CMDPHASE_PENDING;
+		jmp	target_ITloop;
+
+target_dphase:
+		/*
+		 * Data phases on the bus are from the
+		 * perspective of the initiator.  The dma
+		 * code looks at LASTPHASE to determine the
+		 * data direction of the DMA.  Toggle it for
+		 * target transfers.
+		 */
+		xor	LASTPHASE, IOI, SCB_TARGET_INFO[SCB_TARGET_DATA_DIR];
+		or	SCB_TARGET_INFO[SCB_TARGET_DATA_DIR], BSYO
+			call change_phase;
+		jmp	p_data;
+
+target_sphase:
+		mvi	P_STATUS|BSYO call change_phase;
+		mvi	LASTPHASE, P_STATUS;
+		mov	SCB_TARGET_INFO[SCB_TARGET_STATUS] call target_outb;
+		/* XXX Watch for ATN or parity errors??? */
+		mvi	SCSISIGO, P_MESGIN|BSYO;
+		/* MSG_CMDCMPLT is 0, but we can't do an immediate of 0 */
+		mov	ALLZEROS call target_outb;
+		jmp	target_busfree_wait;
+	
+complete_target_cmd:
+		test	SEQ_FLAGS, TARG_CMD_PENDING	jnz . + 2;
+		mov	SCB_TAG jmp complete_post;
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			/* Set the valid byte */
+			mvi	CCSCBADDR, 24;
+			mov	CCSCBRAM, ALLONES;
+			mvi	CCHCNT, 28;
+			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
+			test	CCSCBCTL, CCSCBDONE jz .;
+			clr	CCSCBCTL;
+		} else {
+			/* Set the valid byte */
+			or	DFCNTRL, FIFORESET;
+			mvi	DFWADDR, 3; /* Third 64bit word or byte 24 */
+			mov	DFDAT, ALLONES;
+			mvi	28	call set_hcnt;
+			or	DFCNTRL, HDMAEN|FIFOFLUSH;
+			call	dma_finish;
+		}
+		inc	TQINPOS;
+		mvi	INTSTAT,CMDCMPLT ret;
+	}
+
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+initiator_select:
+	/*
+	 * As soon as we get a successful selection, the target
+	 * should go into the message out phase since we have ATN
+	 * asserted.
+	 */
 	mvi	MSG_OUT, MSG_IDENTIFYFLAG;
 	or	SEQ_FLAGS, IDENTIFY_SEEN;
 
-/*
- * Main loop for information transfer phases.  Wait for the target
- * to assert REQ before checking MSG, C/D and I/O for the bus phase.
- */
+	/*
+	 * Main loop for information transfer phases.  Wait for the
+	 * target to assert REQ before checking MSG, C/D and I/O for
+	 * the bus phase.
+	 */
+mesgin_phasemis:
 ITloop:
 	call	phase_lock;
 
@@ -297,17 +646,20 @@
 	cmp	A,P_STATUS	je p_status;
 	cmp	A,P_MESGIN	je p_mesgin;
 
-	mvi	INTSTAT,BAD_PHASE;	/* unknown phase - signal driver */
+	mvi	BAD_PHASE call set_seqint;
 	jmp	ITloop;			/* Try reading the bus again. */
 
 await_busfree:
 	and	SIMODE1, ~ENBUSFREE;
-	call	clear_target_state;
 	mov	NONE, SCSIDATL;		/* Ack the last byte */
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		clr	SCSIBUSL;	/* Prevent bit leakage durint SELTO */
+	}
 	and	SXFRCTL0, ~SPIOEN;
 	test	SSTAT1,REQINIT|BUSFREE	jz .;
 	test	SSTAT1, BUSFREE jnz poll_for_work;
-	mvi	INTSTAT, BAD_PHASE;
+	mvi	MISSED_BUSFREE call set_seqint;
+}
 	
 clear_target_state:
 	/*
@@ -316,42 +668,160 @@
 	 * clear DFCNTRL too.
 	 */
 	clr	DFCNTRL;
+	or	SXFRCTL0, CLRSTCNT|CLRCHN;
 
 	/*
 	 * We don't know the target we will connect to,
 	 * so default to narrow transfers to avoid
 	 * parity problems.
 	 */
-	if ((p->features & AHC_ULTRA2) != 0) {
-		bmov    SCSIRATE, ALLZEROS, 2;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		bmov	SCSIRATE, ALLZEROS, 2;
 	} else {
-		clr     SCSIRATE;
-		and     SXFRCTL0, ~(FAST20);
+		clr	SCSIRATE;
+		if ((ahc->features & AHC_ULTRA) != 0) {
+			and	SXFRCTL0, ~(FAST20);
+		}
 	}
 	mvi	LASTPHASE, P_BUSFREE;
 	/* clear target specific flags */
 	clr	SEQ_FLAGS ret;
 
+sg_advance:
+	clr	A;			/* add sizeof(struct scatter) */
+	add	SCB_RESIDUAL_SGPTR[0],SG_SIZEOF;
+	adc	SCB_RESIDUAL_SGPTR[1],A;
+	adc	SCB_RESIDUAL_SGPTR[2],A;
+	adc	SCB_RESIDUAL_SGPTR[3],A ret;
+
+if ((ahc->features & AHC_CMD_CHAN) != 0) {
+disable_ccsgen:
+	test	CCSGCTL, CCSGEN jz return;
+	test	CCSGCTL, CCSGDONE jz .;
+disable_ccsgen_fetch_done:
+	clr	CCSGCTL;
+	test	CCSGCTL, CCSGEN jnz .;
+	ret;
+idle_loop:
+	/* Did we just finish fetching segs? */
+	cmp	CCSGCTL, CCSGEN|CCSGDONE je idle_sgfetch_complete;
+
+	/* Are we actively fetching segments? */
+	test	CCSGCTL, CCSGEN jnz return;
+
+	/*
+	 * Do we need any more segments?
+	 */
+	test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jnz return;
+
+	/*
+	 * Do we have any prefetch left???
+	 */
+	cmp	CCSGADDR, SG_PREFETCH_CNT jne idle_sg_avail;
+
+	/*
+	 * Need to fetch segments, but we can only do that
+	 * if the command channel is completely idle.  Make
+	 * sure we don't have an SCB prefetch going on.
+	 */
+	test	CCSCBCTL, CCSCBEN jnz return;
+
+	/*
+	 * We fetch a "cacheline aligned" and sized amount of data
+	 * so we don't end up referencing a non-existant page.
+	 * Cacheline aligned is in quotes because the kernel will
+	 * set the prefetch amount to a reasonable level if the
+	 * cacheline size is unknown.
+	 */
+	mvi	CCHCNT, SG_PREFETCH_CNT;
+	and	CCHADDR[0], SG_PREFETCH_ALIGN_MASK, SCB_RESIDUAL_SGPTR;
+	bmov	CCHADDR[1], SCB_RESIDUAL_SGPTR[1], 3;
+	mvi	CCSGCTL, CCSGEN|CCSGRESET ret;
+idle_sgfetch_complete:
+	call	disable_ccsgen_fetch_done;
+	and	CCSGADDR, SG_PREFETCH_ADDR_MASK, SCB_RESIDUAL_SGPTR;
+idle_sg_avail:
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		/* Does the hardware have space for another SG entry? */
+		test	DFSTATUS, PRELOAD_AVAIL jz return;
+		bmov 	HADDR, CCSGRAM, 4;
+		bmov	SINDEX, CCSGRAM, 1;
+		test	SINDEX, 0x1 jz . + 2;
+		xor	DATA_COUNT_ODD, 0x1;
+		bmov	HCNT[0], SINDEX, 1;
+		bmov	HCNT[1], CCSGRAM, 2;
+		bmov	SCB_RESIDUAL_DATACNT[3], CCSGRAM, 1;
+		call	sg_advance;
+		mov	SINDEX, SCB_RESIDUAL_SGPTR[0];
+		test	DATA_COUNT_ODD, 0x1 jz . + 2;
+		or	SINDEX, ODD_SEG;
+		test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2;
+		or	SINDEX, LAST_SEG;
+		mov	SG_CACHE_PRE, SINDEX;
+		/* Load the segment by writing DFCNTRL again */
+		mov	DFCNTRL, DMAPARAMS;
+	}
+	ret;
+}
+
+if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) {
+/*
+ * Calculate the trailing portion of this S/G segment that cannot
+ * be transferred using memory write and invalidate PCI transactions.  
+ * XXX Can we optimize this for PCI writes only???
+ */
+calc_mwi_residual:
+	/*
+	 * If the ending address is on a cacheline boundary,
+	 * there is no need for an extra segment.
+	 */
+	mov	A, HCNT[0];
+	add	A, A, HADDR[0];
+	and	A, CACHESIZE_MASK;
+	test	A, 0xFF jz return;
+
+	/*
+	 * If the transfer is less than a cachline,
+	 * there is no need for an extra segment.
+	 */
+	test	HCNT[1], 0xFF	jnz calc_mwi_residual_final;
+	test	HCNT[2], 0xFF	jnz calc_mwi_residual_final;
+	add	NONE, INVERTED_CACHESIZE_MASK, HCNT[0];
+	jnc	return;
+
+calc_mwi_residual_final:
+	mov	MWI_RESIDUAL, A;
+	not	A;
+	inc	A;
+	add	HCNT[0], A;
+	adc	HCNT[1], -1;
+	adc	HCNT[2], -1 ret;
+}
+
 /*
  * 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:
-	if ((p->features & AHC_ULTRA2) != 0) {
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		/*
+		 * The preload circuitry requires us to
+		 * reload the address too, so pull it from
+		 * the shaddow address.
+		 */
 		bmov	HADDR, SHADDR, 4;
-		bmov    HCNT, SCB_RESID_DCNT, 3;
-	}
-	if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
-		bmov    STCNT, SCB_RESID_DCNT, 3;
-	}
-	if ((p->features & AHC_CMD_CHAN) == 0) {
+		bmov	HCNT, SCB_RESIDUAL_DATACNT, 3;
+	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		bmov	STCNT, SCB_RESIDUAL_DATACNT, 3;
+	} else {
 		mvi	DINDEX, STCNT;
-		mvi	SCB_RESID_DCNT	call bcopy_3;
+		mvi	SCB_RESIDUAL_DATACNT call bcopy_3;
 	}
+	and	DATA_COUNT_ODD, 0x1, SCB_RESIDUAL_DATACNT[0];
 	jmp	data_phase_loop;
 
 p_data:
-	if ((p->features & AHC_ULTRA2) != 0) {
+	if ((ahc->features & AHC_ULTRA2) != 0) {
 		mvi	DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
 	} else {
 		mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
@@ -362,8 +832,9 @@
 					 * Ensure entering a data
 					 * phase is okay - seen identify, etc.
 					 */
-	if ((p->features & AHC_CMD_CHAN) != 0) {
-		mvi	CCSGADDR, CCSGADDR_MAX;
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		/* We don't have any valid S/G elements */
+		mvi	CCSGADDR, SG_PREFETCH_CNT;
 	}
 	test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
 
@@ -372,256 +843,471 @@
 
 	/*
 	 * 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.
+	 * Also set SCB_RESIDUAL_SGPTR, including the LAST_SEG
+	 * flag in the highest byte of the data count.  We cannot
+	 * modify the saved values in the SCB until we see a save
+	 * data pointers message.
 	 */
-	if ((p->features & AHC_CMD_CHAN) != 0) {
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
 		bmov	HADDR, SCB_DATAPTR, 7;
-		bmov    STCNT, HCNT, 3;
-		bmov    SG_COUNT, SCB_SGCOUNT, 5;
+		bmov	SCB_RESIDUAL_DATACNT[3], SCB_DATACNT[3], 5;
 	} else {
 		mvi	DINDEX, HADDR;
 		mvi	SCB_DATAPTR	call bcopy_7;
-		call	set_stcnt_from_hcnt;
-		mvi	DINDEX, SG_COUNT;
-		mvi	SCB_SGCOUNT	call bcopy_5;
+		mvi	DINDEX, SCB_RESIDUAL_DATACNT + 3;
+		mvi	SCB_DATACNT + 3 call bcopy_5;
+	}
+	if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0) {
+		call	calc_mwi_residual;
+	}
+	and	SCB_RESIDUAL_SGPTR[0], ~SG_FULL_RESID;
+	and	DATA_COUNT_ODD, 0x1, HCNT[0];
+
+	if ((ahc->features & AHC_ULTRA2) == 0) {
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			bmov	STCNT, HCNT, 3;
+		} else {
+			call	set_stcnt_from_hcnt;
+		}
 	}
 
 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.
- */
+	/* Guard against overruns */
+	test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz data_phase_inbounds;
+
+	/*
+	 * Turn on `Bit Bucket' mode, wait until the target takes
+	 * us to another phase, and then notify the host.
+	 */
+	and	DMAPARAMS, DIRECTION;
+	mov	DFCNTRL, DMAPARAMS;
 	or	SXFRCTL1,BITBUCKET;
-	and	DMAPARAMS, ~(HDMAEN|SDMAEN);
-	if ((p->features & AHC_CMD_CHAN) != 0) {
-		if ((p->features & AHC_ULTRA2) != 0) {
-			bmov	HCNT, ALLONES, 3;
-		}
-		bmov	STCNT, ALLONES, 3;
-	} else {
-		mvi	STCNT[0], 0xFF;
-		mvi	STCNT[1], 0xFF;
-		mvi	STCNT[2], 0xFF;
-	}
+	test	SSTAT1,PHASEMIS	jz .;
+	and	SXFRCTL1, ~BITBUCKET;
+	mvi	DATA_OVERRUN call set_seqint;
+	jmp	ITloop;
+
 data_phase_inbounds:
-/* If we are the last SG block, tell the hardware. */
-	cmp	SG_COUNT,0x01 jne data_phase_wideodd;
-	if ((p->features & AHC_ULTRA2) == 0) {
-		and	DMAPARAMS, ~WIDEODD;
-	} else {
-		mvi	SG_CACHEPTR, LAST_SEG;
-	}
-data_phase_wideodd:
-	if ((p->features & AHC_ULTRA2) != 0) {
-		mov	SINDEX, ALLONES;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		mov	SINDEX, SCB_RESIDUAL_SGPTR[0];
+		test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz . + 2;
+		or	SINDEX, LAST_SEG;
+		test	DATA_COUNT_ODD, 0x1 jz . + 2;
+		or	SINDEX, ODD_SEG;
+		mov	SG_CACHE_PRE, SINDEX;
 		mov	DFCNTRL, DMAPARAMS;
-		test	SSTAT0, SDONE jnz .;
-data_phase_dma_loop:
-		test	SSTAT0, SDONE jnz data_phase_dma_done;
-		test	SSTAT1,PHASEMIS	jz data_phase_dma_loop;	/* ie. underrun */
-data_phase_dma_phasemis:
-		test	SSTAT0,SDONE	jnz data_phase_dma_done;
-		clr	SINDEX;			/* Remember the phasemiss */
-	} else {
-		mov	DMAPARAMS  call dma;
-	}
+ultra2_dma_loop:
+		call	idle_loop;
+		/*
+		 * The transfer is complete if either the last segment
+		 * completes or the target changes phase.
+		 */
+		test	SG_CACHE_SHADOW, LAST_SEG_DONE jnz ultra2_dmafinish;
+		if ((ahc->flags & AHC_TARGETROLE) != 0) {
+			 /*
+			  * As a target, we control the phases,
+			  * so ignore PHASEMIS.
+			  */
+			test	SSTAT0, TARGET jnz ultra2_dma_loop;
+		}
+		if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+			test	SSTAT1,PHASEMIS	jz ultra2_dma_loop;
+		}
 
-data_phase_dma_done:
-/* Go tell the host about any overruns */
-	test	SXFRCTL1,BITBUCKET jnz data_phase_overrun;
+ultra2_dmafinish:
+		test	DFCNTRL, DIRECTION jnz ultra2_dmafifoempty;
+		if ((ahc->features & AHC_DT) == 0) {
+			and	DFCNTRL, ~SCSIEN;
+			test	DFCNTRL, SCSIEN jnz .;
+		}
+ultra2_dmafifoflush:
+		if ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0) {
+			/*
+			 * On Rev A of the aic7890, the autoflush
+			 * features doesn't function correctly.
+			 * Perform an explicit manual flush.  During
+			 * a manual flush, the FIFOEMP bit becomes
+			 * true every time the PCI FIFO empties
+			 * regardless of the state of the SCSI FIFO.
+			 * It can take up to 4 clock cycles for the
+			 * SCSI FIFO to get data into the PCI FIFO
+			 * and for FIFOEMP to de-assert.  Here we
+			 * guard against this condition by making
+			 * sure the FIFOEMP bit stays on for 5 full
+			 * clock cycles.
+			 */
+			or	DFCNTRL, FIFOFLUSH;
+			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+			test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+		}
+		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+ultra2_dmafifoempty:
+		/* Don't clobber an inprogress host data transfer */
+		test	DFSTATUS, MREQPEND	jnz ultra2_dmafifoempty;
+ultra2_dmahalt:
+		and     DFCNTRL, ~(SCSIEN|HDMAEN);
+		test	DFCNTRL, HDMAEN jnz .;
 
-/* Exit if we had an underrun.  dma clears SINDEX in this case. */
-	test	SINDEX,0xff	jz data_phase_finish;
+		/*
+		 * If, by chance, we stopped before being able
+		 * to fetch additional segments for this transfer,
+		 * yet the last S/G was completely exhausted,
+		 * call our idle loop until it is able to load
+		 * another segment.  This will allow us to immediately
+		 * pickup on the next segment on the next data phase.
+		 *
+		 * If we happened to stop on the last segment, then
+		 * our residual information is still correct from
+		 * the idle loop and there is no need to perform
+		 * any fixups.  Just jump to data_phase_finish.
+		 */
+ultra2_ensure_sg:
+		test	SG_CACHE_SHADOW, LAST_SEG jz ultra2_shvalid;
+		/* Record if we've consumed all S/G entries */
+		test	SG_CACHE_SHADOW, LAST_SEG_DONE jz data_phase_finish;
+		or	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
+		jmp	data_phase_finish;
+
+ultra2_shvalid:
+                test    SSTAT2, SHVALID	jnz sgptr_fixup;
+		call	idle_loop;
+		jmp	ultra2_ensure_sg;
 
-/*
- * Advance the scatter-gather pointers if needed 
- */
-sg_advance:
-	dec	SG_COUNT;	/* one less segment to go */
+sgptr_fixup:
+		/*
+		 * Fixup the residual next S/G pointer.  The S/G preload
+		 * feature of the chip allows us to load two elements
+		 * in addition to the currently active element.  We
+		 * store the bottom byte of the next S/G pointer in
+		 * the SG_CACEPTR register so we can restore the
+		 * correct value when the DMA completes.  If the next
+		 * sg ptr value has advanced to the point where higher
+		 * bytes in the address have been affected, fix them
+		 * too.
+		 */
+		test	SG_CACHE_SHADOW, 0x80 jz sgptr_fixup_done;
+		test	SCB_RESIDUAL_SGPTR[0], 0x80 jnz sgptr_fixup_done;
+		add	SCB_RESIDUAL_SGPTR[1], -1;
+		adc	SCB_RESIDUAL_SGPTR[2], -1; 
+		adc	SCB_RESIDUAL_SGPTR[3], -1;
+sgptr_fixup_done:
+		and	SCB_RESIDUAL_SGPTR[0], SG_ADDR_MASK, SG_CACHE_SHADOW;
+		clr	DATA_COUNT_ODD;
+		test	SG_CACHE_SHADOW, ODD_SEG jz . + 2;
+		or	DATA_COUNT_ODD, 0x1;
+		clr	SCB_RESIDUAL_DATACNT[3]; /* We are not the last seg */
+	} else {
+		/* If we are the last SG block, tell the hardware. */
+		if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+		  && ahc->pci_cachesize != 0) {
+			test	MWI_RESIDUAL, 0xFF jnz dma_mid_sg;
+		}
+		test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz dma_mid_sg;
+		if ((ahc->flags & AHC_TARGETROLE) != 0) {
+			test	SSTAT0, TARGET jz dma_last_sg;
+			if ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0) {
+				test	DMAPARAMS, DIRECTION jz dma_mid_sg;
+			}
+		}
+dma_last_sg:
+		and	DMAPARAMS, ~WIDEODD;
+dma_mid_sg:
+		/* Start DMA data transfer. */
+		mov	DFCNTRL, DMAPARAMS;
+dma_loop:
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			call	idle_loop;
+		}
+		test	SSTAT0,DMADONE	jnz dma_dmadone;
+		test	SSTAT1,PHASEMIS	jz dma_loop;	/* ie. underrun */
+dma_phasemis:
+		/*
+		 * 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;
 
-	test	SG_COUNT, 0xff	jz data_phase_finish; /* Are we done? */
-/*
- * 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:
-	if ((p->features & AHC_CMD_CHAN) != 0) {
 		/*
-		 * Do we have any prefetch left???
+		 * Now shut off the DMA and make sure that the DMA
+		 * hardware has actually stopped.  Touching the DMA
+		 * counters, etc. while a DMA is active will result
+		 * in an ILLSADDR exception.
+		 */
+dma_dmadone:
+		and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+dma_halt:
+		/*
+		 * Some revisions of the aic78XX have a problem where, if the
+		 * data fifo is full, but the PCI input latch is not empty, 
+		 * HDMAEN cannot be cleared.  The fix used here is to drain
+		 * the prefetched but unused data from the data fifo until
+		 * there is space for the input latch to drain.
 		 */
-		cmp	CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail;
+		if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) {
+			mov	NONE, DFDAT;
+		}
+		test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
+
+		/* See if we have completed this last segment */
+		test	STCNT[0], 0xff	jnz data_phase_finish;
+		test	STCNT[1], 0xff	jnz data_phase_finish;
+		test	STCNT[2], 0xff	jnz data_phase_finish;
 
 		/*
-		 * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
+		 * Advance the scatter-gather pointers if needed 
 		 */
-		add	A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
-		mvi	A, CCSGADDR_MAX;
-		jc	. + 2;
-		shl	A, 3, SG_COUNT;
-		mov	CCHCNT, A;
-		bmov	CCHADDR, SG_NEXT, 4;
-		mvi	CCSGCTL, CCSGEN|CCSGRESET;
-		test	CCSGCTL, CCSGDONE jz .;
-		and	CCSGCTL, ~CCSGEN;
-		test	CCSGCTL, CCSGEN jnz .;
-		mvi	CCSGCTL, CCSGRESET;
-prefetched_segs_avail:
-		bmov 	HADDR, CCSGRAM, 8;
-		if ((p->features & AHC_ULTRA2) == 0) {
-			bmov    STCNT, HCNT, 3;
+		if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+		  && ahc->pci_cachesize != 0) {
+			test	MWI_RESIDUAL, 0xFF jz no_mwi_resid;
+			/*
+			 * Reload HADDR from SHADDR and setup the
+			 * count to be the size of our residual.
+			 */
+			if ((ahc->features & AHC_CMD_CHAN) != 0) {
+				bmov	HADDR, SHADDR, 4;
+				mov	HCNT, MWI_RESIDUAL;
+				bmov	HCNT[1], ALLZEROS, 2;
+			} else {
+				mvi	DINDEX, HADDR;
+				mvi	SHADDR call bcopy_4;
+				mov	MWI_RESIDUAL call set_hcnt;
+			}
+			clr	MWI_RESIDUAL;
+			jmp	sg_load_done;
+no_mwi_resid:
 		}
-	} else {
-		mvi	DINDEX, HADDR;
-		mvi	SG_NEXT	call bcopy_4;
+		test	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG jz sg_load;
+		or	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL;
+		jmp	data_phase_finish;
+sg_load:
+		/*
+		 * Load the next SG element's data address and length
+		 * into the DMA engine.  If we don't have hardware
+		 * to perform a prefetch, we'll have to fetch the
+		 * segment from host memory first.
+		 */
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			/* Wait for the idle loop to complete */
+			test	CCSGCTL, CCSGEN jz . + 3;
+			call	idle_loop;
+			test	CCSGCTL, CCSGEN jnz . - 1;
+			bmov 	HADDR, CCSGRAM, 7;
+			test	CCSGRAM, SG_LAST_SEG jz . + 2;
+			or	SCB_RESIDUAL_DATACNT[3], SG_LAST_SEG;
+		} else {
+			mvi	DINDEX, HADDR;
+			mvi	SCB_RESIDUAL_SGPTR	call bcopy_4;
 
-		mvi	HCNT[0],SG_SIZEOF;
-		clr	HCNT[1];
-		clr	HCNT[2];
+			mvi	SG_SIZEOF	call set_hcnt;
 
-		or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+			or	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
 
-		call	dma_finish;
+			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;
-		call	set_stcnt_from_hcnt;
-	}
+			mvi	DINDEX, HADDR;
+			call	dfdat_in_7;
+			mov	SCB_RESIDUAL_DATACNT[3], DFDAT;
+		}
 
-/* Advance the SG pointer */
-	clr	A;			/* add sizeof(struct scatter) */
-	add	SG_NEXT[0],SG_SIZEOF;
-	adc	SG_NEXT[1],A;
+		if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+		  && ahc->pci_cachesize != 0) {
+			call calc_mwi_residual;
+		}
 
-	test    SSTAT1, REQINIT jz .;
-	test	SSTAT1,PHASEMIS	jz data_phase_loop;
+		/* Point to the new next sg in memory */
+		call	sg_advance;
 
-/* This drops the last SG segment down to the shadow layer for us */
-	if ((p->features & AHC_ULTRA2) != 0) {
-		mov	DFCNTRL, DMAPARAMS;
-		test	SSTAT0, SDONE	jnz .;
-	}
+sg_load_done:
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			bmov	STCNT, HCNT, 3;
+		} else {
+			call	set_stcnt_from_hcnt;
+		}
+		/* Track odd'ness */
+		test	HCNT[0], 0x1 jz . + 2;
+		xor	DATA_COUNT_ODD, 0x1;
 
+		if ((ahc->flags & AHC_TARGETROLE) != 0) {
+			test	SSTAT0, TARGET jnz 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.
- */
-	if ((p->features & AHC_ULTRA2) != 0) {
-		call	ultra2_dmafinish;
-	}
-	if ((p->features & AHC_ULTRA2) == 0) {
-		if ((p->features & AHC_CMD_CHAN) != 0) {
-			bmov    SCB_RESID_DCNT, STCNT, 3;
-			mov	SCB_RESID_SGCNT, SG_COUNT;
-		} else {
-			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;
+	/*
+	 * If the target has left us in data phase, loop through
+	 * the dma code again.  In the case of ULTRA2 adapters,
+	 * we should only loop if there is a data overrun.  For
+	 * all other adapters, we'll loop after each S/G element
+	 * is loaded as well as if there is an overrun.
+	 */
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		test	SSTAT0, TARGET jnz data_phase_done;
+	}
+	if ((ahc->flags & AHC_INITIATORROLE) != 0) {
+		test	SSTAT1, REQINIT jz .;
+		test	SSTAT1,PHASEMIS	jz data_phase_loop;
+	}
+
+data_phase_done:
+	/*
+	 * 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.
+	 */
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		/* Kill off any pending prefetch */
+		call	disable_ccsgen;
+	}
+
+	if ((ahc->bugs & AHC_PCI_MWI_BUG) != 0
+	  && ahc->pci_cachesize != 0) {
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			test	MWI_RESIDUAL, 0xFF jz bmov_resid;
+		}
+		mov	A, MWI_RESIDUAL;
+		add	SCB_RESIDUAL_DATACNT[0], A, STCNT[0];
+		clr	A;
+		adc	SCB_RESIDUAL_DATACNT[1], A, STCNT[1];
+		adc	SCB_RESIDUAL_DATACNT[2], A, STCNT[2];
+		clr	MWI_RESIDUAL;
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			jmp	. + 2;
+bmov_resid:
+			bmov	SCB_RESIDUAL_DATACNT, STCNT, 3;
 		}
+	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		bmov	SCB_RESIDUAL_DATACNT, STCNT, 3;
+	} else {
+		mov	SCB_RESIDUAL_DATACNT[0], STCNT[0];
+		mov	SCB_RESIDUAL_DATACNT[1], STCNT[1];
+		mov	SCB_RESIDUAL_DATACNT[2], STCNT[2];
 	}
 
-	jmp	ITloop;
+	/*
+	 * Since we've been through a data phase, the SCB_RESID* fields
+	 * are now initialized.  Clear the full residual flag.
+	 */
+	and	SCB_SGPTR[0], ~SG_FULL_RESID;
 
-data_phase_overrun:
-	if ((p->features & AHC_ULTRA2) != 0) {
-		call	ultra2_dmafinish;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		/* Clear the channel in case we return to data phase later */
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
 	}
-/*
- * Turn off BITBUCKET mode and notify the host
- */
-	and	SXFRCTL1, ~BITBUCKET;
-	mvi	INTSTAT,DATA_OVERRUN;
-	jmp	ITloop;
 
-ultra2_dmafinish:
-	if ((p->features & AHC_ULTRA2) != 0) {
-		test	DFCNTRL, DIRECTION jnz ultra2_dmahalt;
-		and	DFCNTRL, ~SCSIEN;
-		test	DFCNTRL, SCSIEN jnz .;
-ultra2_dmafifoflush:
-		or	DFCNTRL, FIFOFLUSH;
-		test	DFSTATUS, FIFOEMP jz . - 1;
+	if ((ahc->flags & AHC_TARGETROLE) != 0) {
+		test	SEQ_FLAGS, DPHASE_PENDING jz ITloop;
+		and	SEQ_FLAGS, ~DPHASE_PENDING;
 		/*
-		 * hardware bug alert!  This needless set of jumps is to
-		 * protect against a FIFOEMP status bit glitch in the
-		 * silicon.
+		 * For data-in phases, wait for any pending acks from the
+		 * initiator before changing phase.
 		 */
-		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
-		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
-		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
-		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
-		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
-		test	DFSTATUS, MREQPEND	jnz .;
-ultra2_dmahalt:
-		test	SCSIOFFSET, 0x7f	jnz ultra2_shutdown;
-ultra2_await_nreq:
-		test	SCSISIGI, REQI	jz ultra2_shutdown;
-		test	SSTAT1, (PHASEMIS|REQINIT)	jz ultra2_await_nreq;
-ultra2_shutdown:
-		and     DFCNTRL, ~(HDMAEN|SCSIEN);
-		test	DFCNTRL, (HDMAEN|SCSIEN) jnz .;
-		bmov	SCB_RESID_DCNT, STCNT, 3;
-		mov	SCB_RESID_SGCNT, SG_COUNT;
-		or	SXFRCTL0, CLRSTCNT|CLRCHN;
-		ret;
+		test	DFCNTRL, DIRECTION jz target_ITloop;
+		test	SSTAT1, REQINIT	jnz .;
+		jmp	target_ITloop;
+	} else {
+		jmp	ITloop;
 	}
 
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
 /*
  * Command phase.  Set up the DMA registers and let 'er rip.
  */
 p_command:
 	call	assert;
 
-/*
- * Load HADDR and HCNT.
- */
-	if ((p->features & AHC_CMD_CHAN) != 0) {
-		bmov	HADDR, SCB_CMDPTR, 5;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		bmov	HCNT[0], SCB_CDB_LEN,  1;
 		bmov	HCNT[1], ALLZEROS, 2;
-		if ((p->features & AHC_ULTRA2) == 0) {
-			bmov	STCNT, HCNT, 3;
-		}
+		mvi	SG_CACHE_PRE, LAST_SEG;
+	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		bmov	STCNT[0], SCB_CDB_LEN, 1;
+		bmov	STCNT[1], ALLZEROS, 2;
 	} else {
-		mvi	DINDEX, HADDR;
-		mvi	SCB_CMDPTR	call bcopy_5;
-		clr	HCNT[1];
-		clr	HCNT[2];
-		call	set_stcnt_from_hcnt;
+		mov	STCNT[0], SCB_CDB_LEN;
+		clr	STCNT[1];
+		clr	STCNT[2];
+	}
+	add	NONE, -13, SCB_CDB_LEN;
+	mvi	SCB_CDB_STORE jnc p_command_embedded;
+p_command_from_host:
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		bmov	HADDR[0], SCB_CDB_PTR, 4;
+		mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
+	} else {
+		if ((ahc->features & AHC_CMD_CHAN) != 0) {
+			bmov	HADDR[0], SCB_CDB_PTR, 4;
+			bmov	HCNT, STCNT, 3;
+		} else {
+			mvi	DINDEX, HADDR;
+			mvi	SCB_CDB_PTR call bcopy_4;
+			mov	SCB_CDB_LEN call set_hcnt;
+		}
+		mvi	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET);
 	}
-
-	if ((p->features & AHC_ULTRA2) == 0) {
-		mvi	(SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
+	jmp	p_command_loop;
+p_command_embedded:
+	/*
+	 * The data fifo seems to require 4 byte alligned
+	 * transfers from the sequencer.  Force this to
+	 * be the case by clearing HADDR[0] even though
+	 * we aren't going to touch host memeory.
+	 */
+	clr	HADDR[0];
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		mvi	DFCNTRL, (PRELOADEN|SCSIEN|DIRECTION);
+		bmov	DFDAT, SCB_CDB_STORE, 12; 
+	} else if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		if ((ahc->flags & AHC_SCB_BTT) != 0) {
+			/*
+			 * On the 7895 the data FIFO will
+			 * get corrupted if you try to dump
+			 * data from external SCB memory into
+			 * the FIFO while it is enabled.  So,
+			 * fill the fifo and then enable SCSI
+			 * transfers.
+			 */
+			mvi	DFCNTRL, (DIRECTION|FIFORESET);
+		} else {
+			mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
+		}
+		bmov	DFDAT, SCB_CDB_STORE, 12; 
+		if ((ahc->flags & AHC_SCB_BTT) != 0) {
+			mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFOFLUSH);
+		} else {
+			or	DFCNTRL, FIFOFLUSH;
+		}
 	} else {
-		mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
-		test	SSTAT0, SDONE jnz .;
-p_command_dma_loop:
-		test	SSTAT0, SDONE jnz p_command_ultra2_dma_done;
-		test	SSTAT1,PHASEMIS	jz p_command_dma_loop;	/* ie. underrun */
-p_command_ultra2_dma_done:
-		test	SCSISIGI, REQI	jz p_command_ultra2_shutdown;
-		test	SSTAT1, (PHASEMIS|REQINIT)	jz p_command_ultra2_dma_done;
-p_command_ultra2_shutdown:
-		and     DFCNTRL, ~(HDMAEN|SCSIEN);
-		test	DFCNTRL, (HDMAEN|SCSIEN) jnz .;
-		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+		mvi	DFCNTRL, (SCSIEN|SDMAEN|DIRECTION|FIFORESET);
+		call	copy_to_fifo_6;
+		call	copy_to_fifo_6;
+		or	DFCNTRL, FIFOFLUSH;
+	}
+p_command_loop:
+	test	SSTAT0, SDONE jnz . + 2;
+	test    SSTAT1, PHASEMIS jz p_command_loop;
+	/*
+	 * Wait for our ACK to go-away on it's own
+	 * instead of being killed by SCSIEN getting cleared.
+	 */
+	test	SCSISIGI, ACKI jnz .;
+	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz .;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		/* Drop any residual from the S/G Preload queue */
+		or	SXFRCTL0, CLRSTCNT;
 	}
 	jmp	ITloop;
 
@@ -632,21 +1318,26 @@
 p_status:
 	call	assert;
 
-	mov	SCB_TARGET_STATUS, SCSIDATL;
+	mov	SCB_SCSI_STATUS, SCSIDATL;
 	jmp	ITloop;
 
 /*
- * Message out phase.  If MSG_OUT is 0x80, build I full indentify message
- * sequence and send it to the target.  In addition, if the MK_MESSAGE bit
- * is set in the SCB_CONTROL byte, interrupt the host and allow it to send
- * it's own message.
+ * Message out phase.  If MSG_OUT is MSG_IDENTIFYFLAG, build a full
+ * indentify message sequence and send it to the target.  The host may
+ * override this behavior by setting the MK_MESSAGE bit in the SCB
+ * control byte.  This will cause us to interrupt the host and allow
+ * it to handle the message phase completely on its own.  If the bit
+ * associated with this target is set, we will also interrupt the host,
+ * thereby allowing it to send a message on the next selection regardless
+ * of the transaction being sent.
  * 
  * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
- * This is done to allow the hsot to send messages outside of an identify
+ * This is done to allow the host to send messages outside of an identify
  * sequence while protecting the seqencer from testing the MK_MESSAGE bit
  * on an SCB that might not be for the current nexus. (For example, a
  * BDR message in responce to a bad reselection would leave us pointed to
  * an SCB that doesn't have anything to do with the current target).
+ *
  * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
  * bus device reset).
  *
@@ -655,23 +1346,27 @@
  * reason.
  */
 p_mesgout_retry:
-	or      SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
+	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
 p_mesgout:
 	mov	SINDEX, MSG_OUT;
 	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
-p_mesgout_identify:
-	if ((p->features & AHC_WIDE) != 0) {
-		and	SINDEX,0xf,SCB_TCL;	/* lun */
-	} else {
-		and	SINDEX,0x7,SCB_TCL;	/* lun */
+	test	SCB_CONTROL,MK_MESSAGE	jnz host_message_loop;
+	mov	FUNCTION1, SCB_SCSIID;
+	mov	A, FUNCTION1;
+	mov	SINDEX, TARGET_MSG_REQUEST[0];
+	if ((ahc->features & AHC_TWIN) != 0) {
+		/* Second Channel uses high byte bits */
+		test	SCB_SCSIID, TWIN_CHNLB jz . + 2;
+		mov	SINDEX, TARGET_MSG_REQUEST[1];
+	} else if ((ahc->features & AHC_WIDE) != 0) {
+		test	SCB_SCSIID, 0x80	jz . + 2; /* target > 7 */
+		mov	SINDEX, TARGET_MSG_REQUEST[1];
 	}
-	and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
-	or	SINDEX,A;		/* or in disconnect privledge */
-	or	SINDEX,MSG_IDENTIFYFLAG;
-p_mesgout_mk_message:
-	test	SCB_CONTROL,MK_MESSAGE  jz p_mesgout_tag;
-	mov	SCSIDATL, SINDEX;	/* Send the last byte */
-	jmp	p_mesgout_from_host + 1;/* Skip HOST_MSG test */
+	test	SINDEX, A	jnz host_message_loop;
+p_mesgout_identify:
+	or	SINDEX, MSG_IDENTIFYFLAG|DISCENB, SCB_LUN;
+	test	SCB_CONTROL, DISCENB jnz . + 2;
+	and	SINDEX, ~DISCENB;
 /*
  * 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.
@@ -686,34 +1381,27 @@
 	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
 	mov	SCB_TAG	jmp p_mesgout_onebyte;
 /*
- * Interrupt the driver, and allow it to send a message
- * if it asks.
+ * Interrupt the driver, and allow it to handle this message
+ * phase and any required retries.
  */
 p_mesgout_from_host:
 	cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
-	mvi     INTSTAT,AWAITING_MSG;
-	nop;
-	/*
-	 * Did the host detect a phase change?
-	 */
-	cmp	RETURN_1, MSGOUT_PHASEMIS je p_mesgout_done;
+	jmp	host_message_loop;
 
 p_mesgout_onebyte:
 	mvi	CLRSINT1, CLRATNO;
 	mov	SCSIDATL, SINDEX;
 
 /*
- * If the next bus phase after ATN drops is a message out, it means
+ * If the next bus phase after ATN drops is message out, it means
  * that the target is requesting that the last message(s) be resent.
  */
 	call	phase_lock;
-	cmp     LASTPHASE, P_MESGOUT    je p_mesgout_retry;
+	cmp	LASTPHASE, P_MESGOUT	je p_mesgout_retry;
 
 p_mesgout_done:
 	mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
 	mov	LAST_MSG, MSG_OUT;
-	cmp	MSG_OUT, MSG_IDENTIFYFLAG jne . + 2;
-	and	SCB_CONTROL, ~MK_MESSAGE;
 	mvi	MSG_OUT, MSG_NOOP;	/* No message left */
 	jmp	ITloop;
 
@@ -728,113 +1416,137 @@
 	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_IGN_WIDE_RESIDUE	je mesgin_ign_wide_residue;
 	cmp	A,MSG_NOOP		je mesgin_done;
-	cmp	A,MSG_IGN_WIDE_RESIDUE	je mesgin_wide_residue;
 
-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.
+ * Pushed message loop to allow the kernel to
+ * run it's own message state engine.  To avoid an
+ * extra nop instruction after signaling the kernel,
+ * we perform the phase_lock before checking to see
+ * if we should exit the loop and skip the phase_lock
+ * in the ITloop.  Performing back to back phase_locks
+ * shouldn't hurt, but why do it twice...
  */
-	mvi	INTSTAT,SEND_REJECT;		/* let driver know */
+host_message_loop:
+	mvi	HOST_MSG_LOOP call set_seqint;
+	call	phase_lock;
+	cmp	RETURN_1, EXIT_MSG_LOOP	je ITloop + 1;
+	jmp	host_message_loop;
 
-	mvi	MSG_MESSAGE_REJECT	call mk_mesg;
+mesgin_ign_wide_residue:
+if ((ahc->features & AHC_WIDE) != 0) {
+	test	SCSIRATE, WIDEXFER jz mesgin_reject;
+	/* Pull the residue byte */
+	mvi	ARG_1	call inb_next;
+	cmp	ARG_1, 0x01 jne mesgin_reject;
+	test	SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2;
+	test	DATA_COUNT_ODD, 0x1	jz mesgin_done;
+	mvi	IGN_WIDE_RES call set_seqint;
+	jmp	mesgin_done;
+}
 
+mesgin_reject:
+	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,
+ * We received a "command complete" message.  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 STATUS_GOOD (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.
+ * sense, it will fill the kernel SCB with a request sense command, requeue
+ * it to the QINFIFO and tell us not to post to the QOUTFIFO by setting 
+ * RETURN_1 to SEND_SENSE.
+ */
+
+/*
+ * If ATN is raised, we still want to give the target a message.
+ * Perhaps there was a parity error on this last message byte.
+ * Either way, the target should take us to message out phase
+ * and then attempt to complete the command again.  We should use a
+ * critical section here to guard against a timeout triggering
+ * for this command and setting ATN while we are still processing
+ * the completion.
+	test	SCSISIGI, ATNI jnz mesgin_done;
  */
 
 /*
- * First check for residuals
+ * See if we attempted to deliver a message but the target ingnored us.
  */
-	test	SCB_RESID_SGCNT,0xff	jnz upload_scb;
-	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Good Status? */
+	test	SCB_CONTROL, MK_MESSAGE jz . + 2;
+	mvi	MKMSG_FAILED call set_seqint;
+
+/*
+ * Check for residuals
+ */
+	test	SCB_SGPTR, SG_LIST_NULL jnz check_status;/* No xfer */
+	test	SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
+	test	SCB_RESIDUAL_SGPTR, SG_LIST_NULL jz upload_scb;
+check_status:
+	test	SCB_SCSI_STATUS,0xff	jz complete;	/* Good Status? */
 upload_scb:
+	or	SCB_SGPTR, SG_RESID_VALID;
 	mvi	DMAPARAMS, FIFORESET;
 	mov	SCB_TAG		call dma_scb;
-check_status:
-	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Just a residual? */
-	mvi	INTSTAT,BAD_STATUS;			/* let driver know */
-	nop;
+	test	SCB_SCSI_STATUS, 0xff	jz complete;	/* Just a residual? */
+	mvi	BAD_STATUS call set_seqint;		/* let driver know */
 	cmp	RETURN_1, SEND_SENSE	jne complete;
-	/* This SCB becomes the next to execute as it will retrieve sense */
-	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
-	mov	SCB_TAG		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;
+	call	add_scb_to_free_list;
 	jmp	await_busfree;
-
 complete:
-	/* If we are untagged, clear our address up in host ram */
-	test	SCB_CONTROL, TAG_ENB jnz complete_post;
-	mov	A, SAVED_TCL;
-	mvi	UNTAGGEDSCB_OFFSET call post_byte_setup;
-	mvi	SCB_LIST_NULL call post_byte;
+	mov	SCB_TAG call complete_post;
+	jmp	await_busfree;
+}
 
 complete_post:
-	/* Post the SCB and issue an interrupt */
-	if ((p->features & AHC_QUEUE_REGS) != 0) {
+	/* Post the SCBID in SINDEX and issue an interrupt */
+	call	add_scb_to_free_list;
+	mov	ARG_1, SINDEX;
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
 		mov	A, SDSCB_QOFF;
 	} else {
 		mov	A, QOUTPOS;
 	}
 	mvi	QOUTFIFO_OFFSET call post_byte_setup;
-	mov	SCB_TAG call post_byte;
-	if ((p->features & AHC_QUEUE_REGS) == 0) {
+	mov	ARG_1 call post_byte;
+	if ((ahc->features & AHC_QUEUE_REGS) == 0) {
 		inc 	QOUTPOS;
 	}
-	mvi	INTSTAT,CMDCMPLT;
-
-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	INTSTAT,EXTENDED_MSG;		/* let driver know */
-	jmp	ITloop;
+	mvi	INTSTAT,CMDCMPLT ret;
 
+if ((ahc->flags & AHC_INITIATORROLE) != 0) {
 /*
  * Is it a disconnect message?  Set a flag in the SCB to remind us
- * and await the bus going free.
+ * and await the bus going free.  If this is an untagged transaction
+ * store the SCB id for it in our untagged target table for lookup on
+ * a reselction.
  */
 mesgin_disconnect:
+	/*
+	 * If ATN is raised, we still want to give the target a message.
+	 * Perhaps there was a parity error on this last message byte
+	 * or we want to abort this command.  Either way, the target
+	 * should take us to message out phase and then attempt to
+	 * disconnect again.
+	 * XXX - Wait for more testing.
+	test	SCSISIGI, ATNI jnz mesgin_done;
+	 */
+
 	or	SCB_CONTROL,DISCONNECTED;
-	call	add_scb_to_disc_list;
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		call	add_scb_to_disc_list;
+	}
+	test	SCB_CONTROL, TAG_ENB jnz await_busfree;
+	mov	ARG_1, SCB_TAG;
+	mov	SAVED_LUN, SCB_LUN;
+	mov	SCB_SCSIID	call set_busy_target;
 	jmp	await_busfree;
 
 /*
@@ -846,22 +1558,20 @@
  */
 mesgin_sdptrs:
 	test	SEQ_FLAGS, DPHASE	jz mesgin_done;
+
 	/*
-	 * The SCB SGPTR becomes the next one we'll download,
-	 * and the SCB DATAPTR becomes the current SHADDR.
+	 * The SCB_SGPTR becomes the next one we'll download,
+	 * and the SCB_DATAPTR becomes the current SHADDR.
 	 * Use the residual number since STCNT is corrupted by
 	 * any message transfer.
 	 */
-	if ((p->features & AHC_CMD_CHAN) != 0) {
-		bmov    SCB_SGCOUNT, SG_COUNT, 5;
-		bmov    SCB_DATAPTR, SHADDR, 4;
-		bmov    SCB_DATACNT, SCB_RESID_DCNT, 3;
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
+		bmov	SCB_DATAPTR, SHADDR, 4;
+		bmov	SCB_DATACNT, SCB_RESIDUAL_DATACNT, 8;
 	} else {
-		mvi	DINDEX, SCB_SGCOUNT;
-		mvi	SG_COUNT	call bcopy_5;
 		mvi	DINDEX, SCB_DATAPTR;
-		mvi	SHADDR		call bcopy_4;
-		mvi	SCB_RESID_DCNT	call	bcopy_3;
+		mvi	SHADDR call bcopy_4;
+		mvi	SCB_RESIDUAL_DATACNT call bcopy_8;
 	}
 	jmp	mesgin_done;
 
@@ -880,109 +1590,166 @@
 	jmp	mesgin_done;
 
 /*
+ * Index into our Busy Target table.  SINDEX and DINDEX are modified
+ * upon return.  SCBPTR may be modified by this action.
+ */
+set_busy_target:
+	shr	DINDEX, 4, SINDEX;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		mov	SCBPTR, SAVED_LUN;
+		add	DINDEX, SCB_64_BTT;
+	} else {
+		add	DINDEX, BUSY_TARGETS;
+	}
+	mov	DINDIR, ARG_1 ret;
+
+/*
  * 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:
-	
-	if ((p->features & AHC_WIDE) != 0) {
-		and	A,0x0f;		/* lun in lower four bits */
+	/*
+	 * Determine whether a target is using tagged or non-tagged
+	 * transactions by first looking at the transaction stored in
+	 * the busy target array.  If there is no untagged transaction
+	 * for this target or the transaction is for a different lun, then
+	 * this must be an untagged transaction.
+	 */
+	shr	SINDEX, 4, SAVED_SCSIID;
+	and	SAVED_LUN, MSG_IDENTIFY_LUNMASK, A;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		add	SINDEX, SCB_64_BTT;
+		mov	SCBPTR, SAVED_LUN;
+		if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+			add	NONE, -SCB_64_BTT, SINDEX;
+			jc	. + 2;
+			mvi	INTSTAT, OUT_OF_RANGE;
+			nop;
+			add	NONE, -(SCB_64_BTT + 16), SINDEX;
+			jnc	. + 2;
+			mvi	INTSTAT, OUT_OF_RANGE;
+			nop;
+		}
 	} else {
-		and	A,0x07;		/* lun in lower three bits */
+		add	SINDEX, BUSY_TARGETS;
+		if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+			add	NONE, -BUSY_TARGETS, SINDEX;
+			jc	. + 2;
+			mvi	INTSTAT, OUT_OF_RANGE;
+			nop;
+			add	NONE, -(BUSY_TARGETS + 16), SINDEX;
+			jnc	. + 2;
+			mvi	INTSTAT, OUT_OF_RANGE;
+			nop;
+		}
 	}
-	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */
-
-	mvi     ARG_2, SCB_LIST_NULL;   /* SCBID of prev SCB in disc List */
-	call	get_untagged_SCBID;
+	mov	ARG_1, SINDIR;
 	cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
-	if ((p->flags & AHC_PAGESCBS) != 0) {
-		test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		mov	ARG_1 call findSCB;
+	} else {
+		mov	SCBPTR, ARG_1;
 	}
-	/*
-	 * If the SCB was found in the disconnected list (as is
-	 * always the case in non-paging scenarios), SCBPTR is already
-	 * set to the correct SCB.  So, simply setup the SCB and get
-	 * on with things.
-	 */
-	mov	SCBPTR	call rem_scb_from_disc_list;
-	jmp	setup_SCB;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		jmp setup_SCB_id_lun_okay;
+	} else {
+		/*
+		 * We only allow one untagged command per-target
+		 * at a time.  So, if the lun doesn't match, look
+		 * for a tag message.
+		 */
+		mov	A, SCB_LUN;
+		cmp	SAVED_LUN, A	je setup_SCB_id_lun_okay;
+		if ((ahc->flags & AHC_PAGESCBS) != 0) {
+			/*
+			 * findSCB removes the SCB from the
+			 * disconnected list, so we must replace
+			 * it there should this SCB be for another
+			 * lun.
+			 */
+			call	cleanup_scb;
+		}
+	}
+
 /*
  * 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 search 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.
+ * SCB.  With SCB paging, we must search for 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:
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x80;
+	}
 	mov	NONE,SCSIDATL;		/* ACK Identify MSG */
-snoop_tag_loop:
 	call	phase_lock;
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x1;
+	}
 	cmp	LASTPHASE, P_MESGIN	jne not_found;
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x2;
+	}
 	cmp	SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
 get_tag:
-	mvi	ARG_1	call inb_next;	/* tag value */
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		mvi	ARG_1	call inb_next;	/* tag value */
+		mov	ARG_1	call findSCB;
+	} else {
+		mvi	ARG_1	call inb_next;	/* tag value */
+		mov	SCBPTR, ARG_1;
+	}
 
-use_retrieveSCB:
-	call	retrieveSCB;
+/*
+ * Ensure that the SCB the tag points to is for
+ * an SCB transaction to the reconnecting target.
+ */
 setup_SCB:
-	mov	A, SAVED_TCL;
-	cmp	SCB_TCL, A	jne not_found_cleanup_scb;
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x4;
+	}
+	mov	A, SCB_SCSIID;
+	cmp	SAVED_SCSIID, A	jne not_found_cleanup_scb;
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x8;
+	}
+setup_SCB_id_okay:
+	mov	A, SCB_LUN;
+	cmp	SAVED_LUN, A	jne not_found_cleanup_scb;
+setup_SCB_id_lun_okay:
+	if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
+		or	SEQ_FLAGS, 0x10;
+	}
 	test	SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
 	and	SCB_CONTROL,~DISCONNECTED;
-	or	SEQ_FLAGS,IDENTIFY_SEEN;	  /* make note of IDENTIFY */
+	test	SCB_CONTROL, TAG_ENB	jnz setup_SCB_tagged;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		mov	A, SCBPTR;
+	}
+	mvi	ARG_1, SCB_LIST_NULL;
+	mov	SAVED_SCSIID	call	set_busy_target;
+	if ((ahc->flags & AHC_SCB_BTT) != 0) {
+		mov	SCBPTR, A;
+	}
+setup_SCB_tagged:
+	mvi	SEQ_FLAGS,IDENTIFY_SEEN;	/* make note of IDENTIFY */
+	call	set_transfer_settings;
 	/* See if the host wants to send a message upon reconnection */
 	test	SCB_CONTROL, MK_MESSAGE jz mesgin_done;
-	and	SCB_CONTROL, ~MK_MESSAGE;
 	mvi	HOST_MSG	call mk_mesg;
 	jmp	mesgin_done;
 
 not_found_cleanup_scb:
-	test	SCB_CONTROL, DISCONNECTED jz . + 3;
-	call	add_scb_to_disc_list;
-	jmp	not_found;
-	call	add_scb_to_free_list;
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		call	cleanup_scb;
+	}
 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;
-
-/*
- * Wide Residue.  We handle the simple cases, but pass of the one hard case
- * to the kernel (when the residue byte happened to cause us to advance our
- * sg element array, so we know have to back that advance out).
- */
-mesgin_wide_residue:
-	mvi	ARG_1	call inb_next; /* ACK the wide_residue and get */
-				       /* the size byte */
-/*
- * In order for this to be reliable, we have to do all sorts of horrible
- * magic in terms of resetting the datafifo and reloading the shadow layer
- * with the correct new values (so that a subsequent save data pointers
- * message will do the right thing).  We let the kernel do that work.
- */
- 	mvi	INTSTAT, WIDE_RESIDUE;
+	mvi	NO_MATCH call set_seqint;
 	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:
 	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
 	mov	MSG_OUT,SINDEX ret;
@@ -1002,7 +1769,9 @@
  * and that REQ is already set when inb_first is called.  inb_{first,next}
  * use the same calling convention as inb.
  */
-
+inb_next_wait_perr:
+	mvi	PERR_DETECTED call set_seqint;
+	jmp	inb_next_wait;
 inb_next:
 	mov	NONE,SCSIDATL;		/*dummy read from latch to ACK*/
 inb_next_wait:
@@ -1012,7 +1781,8 @@
 	 * before continuing.
 	 */
 	test	SSTAT1, REQINIT	jz inb_next_wait;
-	test	SSTAT1, SCSIPERR jnz .;
+	test	SSTAT1, SCSIPERR jnz inb_next_wait_perr;
+inb_next_check_phase:
 	and	LASTPHASE, PHASE_MASK, SCSISIGI;
 	cmp	LASTPHASE, P_MESGIN jne mesgin_phasemis;
 inb_first:
@@ -1020,71 +1790,48 @@
 	mov	DINDIR,SCSIBUSL	ret;		/*read byte directly from bus*/
 inb_last:
 	mov	NONE,SCSIDATL ret;		/*dummy read from latch to ACK*/
+}
 
-mesgin_phasemis:
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
 /*
- * We expected to receive another byte, but the target changed phase
+ * Change to a new phase.  If we are changing the state of the I/O signal,
+ * from out to in, wait an additional data release delay before continuing.
  */
-	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.
- */
-if ((p->features & AHC_ULTRA2) == 0) {
-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 */
+change_phase:
+	/* Wait for preceeding I/O session to complete. */
+	test	SCSISIGI, ACKI jnz .;
+
+	/* Change the phase */
+	and	DINDEX, IOI, SCSISIGI;
+	mov	SCSISIGO, SINDEX;
+	and	A, IOI, SINDEX;
 
-/*
- * 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;
+	/*
+	 * If the data direction has changed, from
+	 * out (initiator driving) to in (target driving),
+	 * we must wait at least a data release delay plus
+	 * the normal bus settle delay. [SCSI III SPI 10.11.0]
+	 */
+	cmp 	DINDEX, A je change_phase_wait;
+	test	SINDEX, IOI jz change_phase_wait;
+	call	change_phase_wait;
+change_phase_wait:
+	nop;
+	nop;
+	nop;
+	nop ret;
 
-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.
+ * Send a byte to an initiator in Automatic PIO mode.
  */
-dma_dmadone:
-	cmp	LASTPHASE, P_COMMAND	je dma_await_nreq;
-	test	SCSIRATE, 0x0f	jnz dma_shutdown;
-dma_await_nreq:
-	test	SCSISIGI, REQI	jz dma_shutdown;
-	test	SSTAT1, (PHASEMIS|REQINIT)	jz dma_await_nreq;
-dma_shutdown:
-	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
-dma_halt:
-	/*
-	 * Some revisions of the aic7880 have a problem where, if the
-	 * data fifo is full, but the PCI input latch is not empty, 
-	 * HDMAEN cannot be cleared.  The fix used here is to attempt
-	 * to drain the data fifo until there is space for the input
-	 * latch to drain and HDMAEN de-asserts.
-	 */
-	mov	NONE, DFDAT;
-	test	DFCNTRL, (SCSIEN|SDMAEN|HDMAEN) jnz dma_halt;
+target_outb:
+	or	SXFRCTL0, SPIOEN;
+	test	SSTAT0, SPIORDY	jz .;
+	mov	SCSIDATL, SINDEX;
+	test	SSTAT0, SPIORDY	jz .;
+	and	SXFRCTL0, ~SPIOEN ret;
 }
-return:
-	ret;
+	
 
 /*
  * Assert that if we've been reselected, then we've seen an IDENTIFY
@@ -1093,138 +1840,69 @@
 assert:
 	test	SEQ_FLAGS,IDENTIFY_SEEN	jnz return;	/* seen IDENTIFY? */
 
-	mvi	INTSTAT,NO_IDENT 	ret;	/* no - tell the kernel */
+	mvi	NO_IDENT jmp set_seqint;	/* no - tell the kernel */
 
 /*
- * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
- * or by the SCBID ARG_1.  The search begins at the SCB index passed in
- * via SINDEX which is an SCB that must be on the disconnected list.  If
- * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR
- * is set to the proper SCB.
+ * Locate a disconnected SCB by SCBID.  Upon return, SCBPTR and SINDEX will
+ * be set to the position of the SCB.  If the SCB cannot be found locally,
+ * it will be paged in from host memory.  RETURN_2 stores the address of the
+ * preceding SCB in the disconnected list which can be used to speed up
+ * removal of the found SCB from the disconnected list.
  */
+if ((ahc->flags & AHC_PAGESCBS) != 0) {
+BEGIN_CRITICAL
 findSCB:
-	mov	SCBPTR,SINDEX;			/* Initialize SCBPTR */
-	cmp	ARG_1, SCB_LIST_NULL	jne findSCB_by_SCBID;
-	mov	A, SAVED_TCL;
-	mvi	SCB_TCL	jmp findSCB_loop;	/* &SCB_TCL -> SINDEX */
-findSCB_by_SCBID:
-	mov	A, ARG_1;			/* Tag passed in ARG_1 */
-	mvi	SCB_TAG	jmp findSCB_loop;	/* &SCB_TAG -> SINDEX */
+	mov	A, SINDEX;			/* Tag passed in SINDEX */
+	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL je findSCB_notFound;
+	mov	SCBPTR, DISCONNECTED_SCBH;	/* Initialize SCBPTR */
+	mvi	ARG_2, SCB_LIST_NULL;		/* Head of list */
+	jmp	findSCB_loop;
 findSCB_next:
-	mov     ARG_2, SCBPTR;
-	cmp	SCB_NEXT, SCB_LIST_NULL je notFound;
+	cmp	SCB_NEXT, SCB_LIST_NULL je findSCB_notFound;
+	mov	ARG_2, SCBPTR;
 	mov	SCBPTR,SCB_NEXT;
-	dec	SINDEX;		/* Last comparison moved us too far */
 findSCB_loop:
-	cmp	SINDIR, A	jne findSCB_next;
-	mov	SINDEX, SCBPTR 	ret;
-notFound:
-	mvi	SINDEX, SCB_LIST_NULL	ret;
-
-/*
- * Retrieve an SCB by SCBID first searching the disconnected list falling
- * back to DMA'ing the SCB down from the host.  This routine assumes that
- * ARG_1 is the SCBID of interrest and that SINDEX is the position in the
- * disconnected list to start the search from.  If SINDEX is SCB_LIST_NULL,
- * we go directly to the host for the SCB.
- */
-retrieveSCB:
-	test	SEQ_FLAGS, SCBPTR_VALID	jz retrieve_from_host;
-	mov	SCBPTR	call findSCB;	/* Continue the search */
-	cmp	SINDEX, SCB_LIST_NULL	je retrieve_from_host;
-
-/*
- * This routine expects SINDEX to contain the index of the SCB to be
- * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the
- * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL
- * if it is at the head.
- */
+	cmp	SCB_TAG, A	jne findSCB_next;
 rem_scb_from_disc_list:
-/* Remove this SCB from the disconnection list */
-	cmp     ARG_2, SCB_LIST_NULL    je rHead;
+	cmp	ARG_2, SCB_LIST_NULL	je rHead;
 	mov	DINDEX, SCB_NEXT;
+	mov	SINDEX, SCBPTR;
 	mov	SCBPTR, ARG_2;
 	mov	SCB_NEXT, DINDEX;
 	mov	SCBPTR, SINDEX ret;
 rHead:
 	mov	DISCONNECTED_SCBH,SCB_NEXT ret;
-
-retrieve_from_host:
-/*
- * We didn't find it.  Pull an SCB and DMA down the one we want.
- * We should never get here in the non-paging case.
- */
-	mov	ALLZEROS	call	get_free_or_disc_scb;
+END_CRITICAL
+findSCB_notFound:
+	/*
+	 * We didn't find it.  Page in the SCB.
+	 */
+	mov	ARG_1, A; /* Save tag */
+	mov	ALLZEROS call get_free_or_disc_scb;
 	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
-	/* Jump instead of call as we want to return anyway */
 	mov	ARG_1	jmp dma_scb;
-
-/*
- * Determine whether a target is using tagged or non-tagged transactions
- * by first looking for a matching transaction based on the TCL and if
- * that fails, looking up this device in the host's untagged SCB array.
- * The TCL to search for is assumed to be in SAVED_TCL.  The value is
- * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged).
- * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information
- * in an SCB instead of having to go to the host.
- */
-get_untagged_SCBID:
-	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
-	mvi	ARG_1, SCB_LIST_NULL;
-	mov	DISCONNECTED_SCBH call findSCB;
-	cmp	SINDEX, SCB_LIST_NULL	je get_SCBID_from_host;
-	or	SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
-	test	SCB_CONTROL, TAG_ENB	jnz . + 2;
-	mov	ARG_1, SCB_TAG	ret;
-	mvi	ARG_1, SCB_LIST_NULL ret;
-
-/*
- * Fetch a byte from host memory given an index of (A + (256 * SINDEX))
- * and a base address of SCBID_ADDR.  The byte is returned in RETURN_2.
- */
-fetch_byte:
-	mov	ARG_2, SINDEX;
-	if ((p->features & AHC_CMD_CHAN) != 0) {
-		mvi	DINDEX, CCHADDR;
-		mvi	SCBID_ADDR call set_1byte_addr;
-		mvi	CCHCNT, 1;
-		mvi	CCSGCTL, CCSGEN|CCSGRESET;
-		test	CCSGCTL, CCSGDONE jz .;
-		mvi	CCSGCTL, CCSGRESET;
-		bmov	RETURN_2, CCSGRAM, 1 ret;
-	} else {
-		mvi	DINDEX, HADDR;
-		mvi	SCBID_ADDR call set_1byte_addr;
-		mvi	HCNT[0], 1;
-		clr	HCNT[1];
-		clr	HCNT[2];
-		mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
-		call	dma_finish;
-		mov	RETURN_2, DFDAT ret;
-	}
+}
 
 /*
  * Prepare the hardware to post a byte to host memory given an
- * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
+ * index of (A + (256 * SINDEX)) and a base address of SHARED_DATA_ADDR.
  */
 post_byte_setup:
 	mov	ARG_2, SINDEX;
-	if ((p->features & AHC_CMD_CHAN) != 0) {
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
 		mvi	DINDEX, CCHADDR;
-		mvi	SCBID_ADDR call	set_1byte_addr;
+		mvi	SHARED_DATA_ADDR call	set_1byte_addr;
 		mvi	CCHCNT, 1;
 		mvi	CCSCBCTL, CCSCBRESET ret;
 	} else {
 		mvi	DINDEX, HADDR;
-		mvi	SCBID_ADDR call	set_1byte_addr;
-		mvi	HCNT[0], 1;
-		clr	HCNT[1];
-		clr	HCNT[2];
+		mvi	SHARED_DATA_ADDR call	set_1byte_addr;
+		mvi	1	call set_hcnt;
 		mvi	DFCNTRL, FIFORESET ret;
 	}
 
 post_byte:
-	if ((p->features & AHC_CMD_CHAN) != 0) {
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
 		bmov	CCSCBRAM, SINDEX, 1;
 		or	CCSCBCTL, CCSCBEN|CCSCBRESET;
 		test	CCSCBCTL, CCSCBDONE jz .;
@@ -1235,23 +1913,34 @@
 		jmp	dma_finish;
 	}
 
-get_SCBID_from_host:
-	mov	A, SAVED_TCL;
-	mvi	UNTAGGEDSCB_OFFSET call fetch_byte;
-	mov	RETURN_1,  RETURN_2 ret;
-
+phase_lock_perr:
+	mvi	PERR_DETECTED call set_seqint;
 phase_lock:     
+	/*
+	 * 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 phase_lock;
-	test	SSTAT1, SCSIPERR jnz phase_lock;
+	test	SSTAT1, SCSIPERR jnz phase_lock_perr;
+phase_lock_latch_phase:
 	and	SCSISIGO, PHASE_MASK, SCSISIGI;
 	and	LASTPHASE, PHASE_MASK, SCSISIGI ret;
 
-if ((p->features & AHC_CMD_CHAN) == 0) {
+if ((ahc->features & AHC_CMD_CHAN) == 0) {
+set_hcnt:
+	mov	HCNT[0], SINDEX;
+clear_hcnt:
+	clr	HCNT[1];
+	clr	HCNT[2] ret;
+
 set_stcnt_from_hcnt:
 	mov	STCNT[0], HCNT[0];
 	mov	STCNT[1], HCNT[1];
 	mov	STCNT[2], HCNT[2] ret;
 
+bcopy_8:
+	mov	DINDIR, SINDIR;
 bcopy_7:
 	mov	DINDIR, SINDIR;
 	mov	DINDIR, SINDIR;
@@ -1265,6 +1954,7 @@
 	mov	DINDIR, SINDIR ret;
 }
 
+if ((ahc->flags & AHC_TARGETROLE) != 0) {
 /*
  * Setup addr assuming that A is an index into
  * an array of 32byte objects, SINDEX contains
@@ -1275,16 +1965,30 @@
 set_32byte_addr:
 	shr	ARG_2, 3, A;
 	shl	A, 5;
+	jmp	set_1byte_addr;
+}
+
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 64byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_64byte_addr:
+	shr	ARG_2, 2, A;
+	shl	A, 6;
+
 /*
- * Setup addr assuming that A + (ARG_1 * 256) is an
+ * Setup addr assuming that A + (ARG_2 * 256) is an
  * index into an array of 1byte objects, SINDEX contains
  * the base address of that array, and DINDEX contains
  * the base address of the location to store the computed
  * address.
  */
 set_1byte_addr:
-	add	DINDIR, A, SINDIR;
-	mov	A, ARG_2;
+	add     DINDIR, A, SINDIR;
+	mov     A, ARG_2;
 	adc	DINDIR, A, SINDIR;
 	clr	A;
 	adc	DINDIR, A, SINDIR;
@@ -1296,21 +2000,32 @@
  */
 dma_scb:
 	mov	A, SINDEX;
-	if ((p->features & AHC_CMD_CHAN) != 0) {
+	if ((ahc->features & AHC_CMD_CHAN) != 0) {
 		mvi	DINDEX, CCHADDR;
-		mvi	HSCB_ADDR call set_32byte_addr;
+		mvi	HSCB_ADDR call set_64byte_addr;
 		mov	CCSCBPTR, SCBPTR;
-		mvi	CCHCNT, 32;
 		test	DMAPARAMS, DIRECTION jz dma_scb_tohost;
+		if ((ahc->flags & AHC_SCB_BTT) != 0) {
+			mvi	CCHCNT, SCB_DOWNLOAD_SIZE_64;
+		} else {
+			mvi	CCHCNT, SCB_DOWNLOAD_SIZE;
+		}
 		mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
 		cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
 		jmp	dma_scb_finish;
 dma_scb_tohost:
-		if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+		mvi	CCHCNT, SCB_UPLOAD_SIZE;
+		if ((ahc->features & AHC_ULTRA2) == 0) {
 			mvi	CCSCBCTL, CCSCBRESET;
-			bmov	CCSCBRAM, SCB_CONTROL, 32;
+			bmov	CCSCBRAM, SCB_BASE, SCB_UPLOAD_SIZE;
 			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
 			test	CCSCBCTL, CCSCBDONE jz .;
+		} else if ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0) {
+			mvi	CCSCBCTL, CCARREN|CCSCBRESET;
+			cmp	CCSCBCTL, ARRDONE|CCARREN jne .;
+			mvi	CCHCNT, SCB_UPLOAD_SIZE;
+			mvi	CCSCBCTL, CCSCBEN|CCSCBRESET;
+			cmp	CCSCBCTL, CCSCBDONE|CCSCBEN jne .;
 		} else {
 			mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
 			cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
@@ -1321,85 +2036,161 @@
 		ret;
 	} else {
 		mvi	DINDEX, HADDR;
-		mvi	HSCB_ADDR call set_32byte_addr;
-		mvi	HCNT[0], 32;
-		clr	HCNT[1];
-		clr	HCNT[2];
+		mvi	HSCB_ADDR call set_64byte_addr;
+		mvi	SCB_DOWNLOAD_SIZE call set_hcnt;
 		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, 32, SINDEX;
+		mvi	SINDEX, SCB_BASE;
+		add	A, SCB_DOWNLOAD_SIZE, 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;
-		mov	DFDAT,SINDIR;
+		call	copy_to_fifo_8;
 		cmp	SINDEX, A jne copy_scb_tofifo_loop;
 		or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		jmp	dma_finish;
 dma_scb_fromhost:
-		call	dma_finish;
+		mvi	DINDEX, SCB_BASE;
+		if ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0) {
+			/*
+			 * The PCI module will only issue a PCI
+			 * retry if the data FIFO is empty.  If the
+			 * host disconnects in the middle of a
+			 * transfer, we must empty the fifo of all
+			 * available data to force the chip to
+			 * continue the transfer.  This does not
+			 * happen for SCSI transfers as the SCSI module
+			 * will drain the FIFO as data is made available.
+			 * When the hang occurs, we know that a multiple
+			 * of 8 bytes are in the FIFO because the PCI
+			 * module has an 8 byte input latch that only
+			 * dumps to the FIFO when HCNT == 0 or the
+			 * latch is full.
+			 */
+			clr	A;
+			/* Wait for some data to arrive. */
+dma_scb_hang_fifo:
+			test	DFSTATUS, FIFOEMP jnz dma_scb_hang_fifo;
+dma_scb_hang_wait:
+			test	DFSTATUS, MREQPEND jnz dma_scb_hang_wait;
+			test	DFSTATUS, HDONE	jnz dma_scb_hang_dma_done;
+			test	DFSTATUS, HDONE	jnz dma_scb_hang_dma_done;
+			test	DFSTATUS, HDONE	jnz dma_scb_hang_dma_done;
+			/*
+			 * The PCI module no longer intends to perform
+			 * a PCI transaction and HDONE has not come true.
+			 * We are hung.  Drain the fifo.
+			 */
+dma_scb_hang_empty_fifo:
+			/*
+			 * Skip lines not yet transfered into the FIFO.
+			 */
+			add	SINDEX, 7, HCNT;
+			shr	SINDEX, 3;
+
+			/*
+			 * Skip lines already copied out of the FIFO.
+			 */
+			add	A, A, SINDEX;
+
+			call	dma_scb_hang_dma_drain_fifo;
+
+			/*
+			 * Set the lines transferred to all but
+			 * those yet to reach the FIFO.
+			 */
+			not	SINDEX;
+			add	A, 5, SINDEX;
+			jmp	dma_scb_hang_fifo;
+dma_scb_hang_dma_done:
+			and	DFCNTRL, ~HDMAEN;
+			test	DFCNTRL, HDMAEN jnz .;
+dma_scb_hang_dma_drain_fifo:
+			add	SEQADDR0, A;
+		} else {
+			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;
+		call	dfdat_in_8;
+		call	dfdat_in_8;
+		call	dfdat_in_8;
+dfdat_in_8:
+		mov	DINDIR,DFDAT;
 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;
+dfdat_in_2:
 		mov	DINDIR,DFDAT;
 		mov	DINDIR,DFDAT ret;
 	}
 
+copy_to_fifo_8:
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+copy_to_fifo_6:
+	mov	DFDAT,SINDIR;
+copy_to_fifo_5:
+	mov	DFDAT,SINDIR;
+copy_to_fifo_4:
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR;
+	mov	DFDAT,SINDIR 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.
  */
-if ((p->features & AHC_CMD_CHAN) == 0) {
 dma_finish:
 	test	DFSTATUS,HDONE	jz dma_finish;
 	/* Turn off DMA */
 	and	DFCNTRL, ~HDMAEN;
 	test	DFCNTRL, HDMAEN jnz .;
 	ret;
-}
 
+/*
+ * Restore an SCB that failed to match an incoming reselection
+ * to the correct/safe state.  If the SCB is for a disconnected
+ * transaction, it must be returned to the disconnected list.
+ * If it is not in the disconnected state, it must be free.
+ */
+cleanup_scb:
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+		test	SCB_CONTROL,DISCONNECTED jnz add_scb_to_disc_list;
+	}
 add_scb_to_free_list:
-	if ((p->flags & AHC_PAGESCBS) != 0) {
+	if ((ahc->flags & AHC_PAGESCBS) != 0) {
+BEGIN_CRITICAL
 		mov	SCB_NEXT, FREE_SCBH;
-		mov	FREE_SCBH, SCBPTR;
+		mvi	SCB_TAG, SCB_LIST_NULL;
+		mov	FREE_SCBH, SCBPTR ret;
+END_CRITICAL
+	} else {
+		mvi	SCB_TAG, SCB_LIST_NULL ret;
 	}
-	mvi	SCB_TAG, SCB_LIST_NULL ret;
 
-if ((p->flags & AHC_PAGESCBS) != 0) {
+if ((ahc->flags & AHC_PAGESCBS) != 0) {
 get_free_or_disc_scb:
+BEGIN_CRITICAL
 	cmp	FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
 	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
 return_error:
+	mvi	NO_FREE_SCB call set_seqint;
 	mvi	SINDEX, SCB_LIST_NULL	ret;
 dequeue_disc_scb:
 	mov	SCBPTR, DISCONNECTED_SCBH;
-dma_up_scb:
+	mov	DISCONNECTED_SCBH, SCB_NEXT;
+END_CRITICAL
 	mvi	DMAPARAMS, FIFORESET;
-	mov	SCB_TAG		call dma_scb;
-unlink_disc_scb:
-	mov	DISCONNECTED_SCBH, SCB_NEXT ret;
+	mov	SCB_TAG	jmp dma_scb;
+BEGIN_CRITICAL
 dequeue_free_scb:
 	mov	SCBPTR, FREE_SCBH;
 	mov	FREE_SCBH, SCB_NEXT ret;
-}
+END_CRITICAL
 
 add_scb_to_disc_list:
 /*
@@ -1407,5 +2198,13 @@
  * candidates for paging out an SCB if one is needed for a new command.
  * Modifying the disconnected list is a critical(pause dissabled) section.
  */
+BEGIN_CRITICAL
 	mov	SCB_NEXT, DISCONNECTED_SCBH;
 	mov	DISCONNECTED_SCBH, SCBPTR ret;
+END_CRITICAL
+}
+set_seqint:
+	mov	INTSTAT, SINDEX;
+	nop;
+return:
+	ret;
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_93cx6.c linux/drivers/scsi/aic7xxx/aic7xxx_93cx6.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_93cx6.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx_93cx6.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,205 @@
+/*
+ * Interface for the 93C66/56/46/26/06 serial eeprom parts.
+ *
+ * Copyright (c) 1995, 1996 Daniel M. Eischen
+ * 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aic7xxx_93cx6.c#7 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_93cx6.c,v 1.9 2000/11/10 20:13:41 gibbs Exp $
+ */
+
+/*
+ *   The instruction set of the 93C66/56/46/26/06 chips are as follows:
+ *
+ *               Start  OP	    *
+ *     Function   Bit  Code  Address**  Data     Description
+ *     -------------------------------------------------------------------
+ *     READ        1    10   A5 - A0             Reads data stored in memory,
+ *                                               starting at specified address
+ *     EWEN        1    00   11XXXX              Write enable must preceed
+ *                                               all programming modes
+ *     ERASE       1    11   A5 - A0             Erase register A5A4A3A2A1A0
+ *     WRITE       1    01   A5 - A0   D15 - D0  Writes register
+ *     ERAL        1    00   10XXXX              Erase all registers
+ *     WRAL        1    00   01XXXX    D15 - D0  Writes to all registers
+ *     EWDS        1    00   00XXXX              Disables all programming
+ *                                               instructions
+ *     *Note: A value of X for address is a don't care condition.
+ *    **Note: There are 8 address bits for the 93C56/66 chips unlike
+ *	      the 93C46/26/06 chips which have 6 address bits.
+ *
+ *   The 93C46 has a four wire interface: clock, chip select, data in, and
+ *   data out.  In order to perform one of the above functions, you need
+ *   to enable the chip select for a clock period (typically a minimum of
+ *   1 usec, with the clock high and low a minimum of 750 and 250 nsec
+ *   respectively).  While the chip select remains high, you can clock in
+ *   the instructions (above) starting with the start bit, followed by the
+ *   OP code, Address, and Data (if needed).  For the READ instruction, the
+ *   requested 16-bit register contents is read from the data out line but
+ *   is preceded by an initial zero (leading 0, followed by 16-bits, MSB
+ *   first).  The clock cycling from low to high initiates the next data
+ *   bit to be sent from the chip.
+ *
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+
+/*
+ * Right now, we only have to read the SEEPROM.  But we make it easier to
+ * add other 93Cx6 functions.
+ */
+static struct seeprom_cmd {
+  	uint8_t len;
+ 	uint8_t bits[3];
+} seeprom_read = {3, {1, 1, 0}};
+
+/*
+ * Wait for the SEERDY to go high; about 800 ns.
+ */
+#define CLOCK_PULSE(sd, rdy)				\
+	while ((SEEPROM_STATUS_INB(sd) & rdy) == 0) {	\
+		;  /* Do nothing */			\
+	}						\
+	(void)SEEPROM_INB(sd);	/* Clear clock */
+
+/*
+ * Read the serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ */
+int
+read_seeprom(sd, buf, start_addr, count)
+	struct seeprom_descriptor *sd;
+	uint16_t *buf;
+	u_int start_addr;
+	u_int count;
+{
+	int i = 0;
+	u_int k = 0;
+	uint16_t v;
+	uint8_t temp;
+
+	/*
+	 * Read the requested registers of the seeprom.  The loop
+	 * will range from 0 to count-1.
+	 */
+	for (k = start_addr; k < count + start_addr; k++) {
+		/* Send chip select for one clock cycle. */
+		temp = sd->sd_MS ^ sd->sd_CS;
+		SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+		CLOCK_PULSE(sd, sd->sd_RDY);
+
+		/*
+		 * Now we're ready to send the read command followed by the
+		 * address of the 16-bit register we want to read.
+		 */
+		for (i = 0; i < seeprom_read.len; i++) {
+			if (seeprom_read.bits[i] != 0)
+				temp ^= sd->sd_DO;
+			SEEPROM_OUTB(sd, temp);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			if (seeprom_read.bits[i] != 0)
+				temp ^= sd->sd_DO;
+		}
+		/* Send the 6 or 8 bit address (MSB first, LSB last). */
+		for (i = (sd->sd_chip - 1); i >= 0; i--) {
+			if ((k & (1 << i)) != 0)
+				temp ^= sd->sd_DO;
+			SEEPROM_OUTB(sd, temp);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			if ((k & (1 << i)) != 0)
+				temp ^= sd->sd_DO;
+		}
+
+		/*
+		 * Now read the 16 bit register.  An initial 0 precedes the
+		 * register contents which begins with bit 15 (MSB) and ends
+		 * with bit 0 (LSB).  The initial 0 will be shifted off the
+		 * top of our word as we let the loop run from 0 to 16.
+		 */
+		v = 0;
+		for (i = 16; i >= 0; i--) {
+			SEEPROM_OUTB(sd, temp);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+			v <<= 1;
+			if (SEEPROM_DATA_INB(sd) & sd->sd_DI)
+				v |= 1;
+			SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+			CLOCK_PULSE(sd, sd->sd_RDY);
+		}
+
+		buf[k - start_addr] = v;
+
+		/* Reset the chip select for the next command cycle. */
+		temp = sd->sd_MS;
+		SEEPROM_OUTB(sd, temp);
+		CLOCK_PULSE(sd, sd->sd_RDY);
+		SEEPROM_OUTB(sd, temp ^ sd->sd_CK);
+		CLOCK_PULSE(sd, sd->sd_RDY);
+		SEEPROM_OUTB(sd, temp);
+		CLOCK_PULSE(sd, sd->sd_RDY);
+	}
+#ifdef AHC_DUMP_EEPROM
+	printf("\nSerial EEPROM:\n\t");
+	for (k = 0; k < count; k = k + 1) {
+		if (((k % 8) == 0) && (k != 0)) {
+			printf ("\n\t");
+		}
+		printf (" 0x%x", buf[k]);
+	}
+	printf ("\n");
+#endif
+	return (1);
+}
+
+int
+verify_cksum(struct seeprom_config *sc)
+{
+	int i;
+	int maxaddr;
+	uint32_t checksum;
+	uint16_t *scarray;
+
+	maxaddr = (sizeof(*sc)/2) - 1;
+	checksum = 0;
+	scarray = (uint16_t *)sc;
+
+	for (i = 0; i < maxaddr; i++)
+		checksum = checksum + scarray[i];
+	if (checksum == 0
+	 || (checksum & 0xFFFF) != sc->checksum) {
+		return (0);
+	} else {
+		return(1);
+	}
+}
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_93cx6.h linux/drivers/scsi/aic7xxx/aic7xxx_93cx6.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_93cx6.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx_93cx6.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,92 @@
+/*
+ * Interface to the 93C46/56 serial EEPROM that is used to store BIOS
+ * settings for the aic7xxx based adaptec SCSI controllers.  It can
+ * also be used for 93C26 and 93C06 serial EEPROMS.
+ *
+ * Copyright (c) 1994, 1995, 2000 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aic7xxx_93cx6.h#5 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_93cx6.h,v 1.8 2000/11/10 20:13:41 gibbs Exp $
+ */
+#ifndef _AIC7XXX_93CX6_H_
+#define _AIC7XXX_93CX6_H_
+
+typedef enum {
+	C46 = 6,
+	C56_66 = 8
+} seeprom_chip_t;
+
+struct seeprom_descriptor {
+	struct ahc_softc *sd_ahc;
+	u_int sd_control_offset;
+	u_int sd_status_offset;
+	u_int sd_dataout_offset;
+	seeprom_chip_t sd_chip;
+	uint16_t sd_MS;
+	uint16_t sd_RDY;
+	uint16_t sd_CS;
+	uint16_t sd_CK;
+	uint16_t sd_DO;
+	uint16_t sd_DI;
+};
+
+/*
+ * This function will read count 16-bit words from the serial EEPROM and
+ * return their value in buf.  The port address of the aic7xxx serial EEPROM
+ * control register is passed in as offset.  The following parameters are
+ * also passed in:
+ *
+ *   CS  - Chip select
+ *   CK  - Clock
+ *   DO  - Data out
+ *   DI  - Data in
+ *   RDY - SEEPROM ready
+ *   MS  - Memory port mode select
+ *
+ *  A failed read attempt returns 0, and a successful read returns 1.
+ */
+
+#define	SEEPROM_INB(sd) \
+	ahc_inb(sd->sd_ahc, sd->sd_control_offset)
+#define	SEEPROM_OUTB(sd, value)					\
+do {								\
+	ahc_outb(sd->sd_ahc, sd->sd_control_offset, value);	\
+	ahc_flush_device_writes(sd->sd_ahc);			\
+} while(0)
+
+#define	SEEPROM_STATUS_INB(sd) \
+	ahc_inb(sd->sd_ahc, sd->sd_status_offset)
+#define	SEEPROM_DATA_INB(sd) \
+	ahc_inb(sd->sd_ahc, sd->sd_dataout_offset)
+
+int read_seeprom(struct seeprom_descriptor *sd, uint16_t *buf,
+		 u_int start_addr, u_int count);
+int verify_cksum(struct seeprom_config *sc);
+
+#endif /* _AIC7XXX_93CX6_H_ */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_inline.h linux/drivers/scsi/aic7xxx/aic7xxx_inline.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_inline.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx_inline.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,478 @@
+/*
+ * Inline routines shareable across OS platforms.
+ *
+ * Copyright (c) 1994-2001 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aic7xxx_inline.h#15 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_inline.h,v 1.8 2000/11/12 05:19:46 gibbs Exp $
+ */
+
+#ifndef _AIC7XXX_INLINE_H_
+#define _AIC7XXX_INLINE_H_
+
+/************************* Sequencer Execution Control ************************/
+static __inline int  sequencer_paused(struct ahc_softc *ahc);
+static __inline void ahc_pause_bug_fix(struct ahc_softc *ahc);
+static __inline void pause_sequencer(struct ahc_softc *ahc);
+static __inline void unpause_sequencer(struct ahc_softc *ahc);
+
+/*
+ * Work around any chip bugs related to halting sequencer execution.
+ * On Ultra2 controllers, we must clear the CIOBUS stretch signal by
+ * reading a register that will set this signal and deassert it.
+ * Without this workaround, if the chip is paused, by an interrupt or
+ * manual pause while accessing scb ram, accesses to certain registers
+ * will hang the system (infinite pci retries).
+ */
+static __inline void
+ahc_pause_bug_fix(struct ahc_softc *ahc)
+{
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		(void)ahc_inb(ahc, CCSCBCTL);
+}
+
+/*
+ * Determine whether the sequencer has halted code execution.
+ * Returns non-zero status if the sequencer is stopped.
+ */
+static __inline int
+sequencer_paused(struct ahc_softc *ahc)
+{
+	return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0);
+}
+
+/*
+ * Request that the sequencer stop and wait, indefinitely, for it
+ * to stop.  The sequencer will only acknowledge that it is paused
+ * once it has reached an instruction boundary and PAUSEDIS is
+ * cleared in the SEQCTL register.  The sequencer may use PAUSEDIS
+ * for critical sections.
+ */
+static __inline void
+pause_sequencer(struct ahc_softc *ahc)
+{
+	ahc_outb(ahc, HCNTRL, ahc->pause);
+
+	/*
+	 * Since the sequencer can disable pausing in a critical section, we
+	 * must loop until it actually stops.
+	 */
+	while (sequencer_paused(ahc) == 0)
+		;
+
+	ahc_pause_bug_fix(ahc);
+}
+
+/*
+ * Allow the sequencer to continue program execution.
+ * We check here to ensure that no additional interrupt
+ * sources that would cause the sequencer to halt have been
+ * asserted.  If, for example, a SCSI bus reset is detected
+ * while we are fielding a different, pausing, interrupt type,
+ * we don't want to release the sequencer before going back
+ * into our interrupt handler and dealing with this new
+ * condition.
+ */
+static __inline void
+unpause_sequencer(struct ahc_softc *ahc)
+{
+	if ((ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)
+		ahc_outb(ahc, HCNTRL, ahc->unpause);
+}
+
+/*********************** Untagged Transaction Routines ************************/
+static __inline void	ahc_freeze_untagged_queues(struct ahc_softc *ahc);
+static __inline void	ahc_release_untagged_queues(struct ahc_softc *ahc);
+
+/*
+ * Block our completion routine from starting the next untagged
+ * transaction for this target or target lun.
+ */
+static __inline void
+ahc_freeze_untagged_queues(struct ahc_softc *ahc)
+{
+	if ((ahc->flags & AHC_SCB_BTT) == 0)
+		ahc->untagged_queue_lock++;
+}
+
+/*
+ * Allow the next untagged transaction for this target or target lun
+ * to be executed.  We use a counting semaphore to allow the lock
+ * to be acquired recursively.  Once the count drops to zero, the
+ * transaction queues will be run.
+ */
+static __inline void
+ahc_release_untagged_queues(struct ahc_softc *ahc)
+{
+	if ((ahc->flags & AHC_SCB_BTT) == 0) {
+		ahc->untagged_queue_lock--;
+		if (ahc->untagged_queue_lock == 0)
+			ahc_run_untagged_queues(ahc);
+	}
+}
+
+/************************** Memory mapping routines ***************************/
+static __inline struct ahc_dma_seg *
+			ahc_sg_bus_to_virt(struct scb *scb,
+					   uint32_t sg_busaddr);
+static __inline uint32_t
+			ahc_sg_virt_to_bus(struct scb *scb,
+					   struct ahc_dma_seg *sg);
+static __inline uint32_t
+			ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index);
+
+static __inline struct ahc_dma_seg *
+ahc_sg_bus_to_virt(struct scb *scb, uint32_t sg_busaddr)
+{
+	int sg_index;
+
+	sg_index = (sg_busaddr - scb->sg_list_phys)/sizeof(struct ahc_dma_seg);
+	/* sg_list_phys points to entry 1, not 0 */
+	sg_index++;
+
+	return (&scb->sg_list[sg_index]);
+}
+
+static __inline uint32_t
+ahc_sg_virt_to_bus(struct scb *scb, struct ahc_dma_seg *sg)
+{
+	int sg_index;
+
+	/* sg_list_phys points to entry 1, not 0 */
+	sg_index = sg - &scb->sg_list[1];
+
+	return (scb->sg_list_phys + (sg_index * sizeof(*scb->sg_list)));
+}
+
+static __inline uint32_t
+ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index)
+{
+	return (ahc->scb_data->hscb_busaddr
+		+ (sizeof(struct hardware_scb) * index));
+}
+
+/******************************** Debugging ***********************************/
+static __inline char *ahc_name(struct ahc_softc *ahc);
+
+static __inline char *
+ahc_name(struct ahc_softc *ahc)
+{
+	return (ahc->name);
+}
+
+/*********************** Miscelaneous Support Functions ***********************/
+
+static __inline int	ahc_check_residual(struct scb *scb);
+static __inline struct ahc_initiator_tinfo *
+			ahc_fetch_transinfo(struct ahc_softc *ahc,
+					    char channel, u_int our_id,
+					    u_int remote_id,
+					    struct tmode_tstate **tstate);
+static __inline struct scb*
+			ahc_get_scb(struct ahc_softc *ahc);
+static __inline void	ahc_free_scb(struct ahc_softc *ahc, struct scb *scb);
+static __inline void	ahc_swap_with_next_hscb(struct ahc_softc *ahc,
+						struct scb *scb);
+static __inline void	ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb);
+static __inline struct scsi_sense_data *
+			ahc_get_sense_buf(struct ahc_softc *ahc,
+					  struct scb *scb);
+static __inline uint32_t
+			ahc_get_sense_bufaddr(struct ahc_softc *ahc,
+					      struct scb *scb);
+
+/*
+ * Determine whether the sequencer reported a residual
+ * for this SCB/transaction.
+ */
+static __inline int
+ahc_check_residual(struct scb *scb)
+{
+	struct status_pkt *sp;
+
+	sp = &scb->hscb->shared_data.status;
+	if ((scb->hscb->sgptr & SG_RESID_VALID) != 0)
+		return (1);
+	return (0);
+}
+
+/*
+ * Return pointers to the transfer negotiation information
+ * for the specified our_id/remote_id pair.
+ */
+static __inline struct ahc_initiator_tinfo *
+ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id,
+		    u_int remote_id, struct tmode_tstate **tstate)
+{
+	/*
+	 * Transfer data structures are stored from the perspective
+	 * of the target role.  Since the parameters for a connection
+	 * in the initiator role to a given target are the same as
+	 * when the roles are reversed, we pretend we are the target.
+	 */
+	if (channel == 'B')
+		our_id += 8;
+	*tstate = ahc->enabled_targets[our_id];
+	return (&(*tstate)->transinfo[remote_id]);
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new SCB.
+ */
+static __inline struct scb *
+ahc_get_scb(struct ahc_softc *ahc)
+{
+	struct scb *scb;
+
+	if ((scb = SLIST_FIRST(&ahc->scb_data->free_scbs)) == NULL) {
+		ahc_alloc_scbs(ahc);
+		scb = SLIST_FIRST(&ahc->scb_data->free_scbs);
+		if (scb == NULL)
+			return (NULL);
+	}
+	SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle);
+	return (scb);
+}
+
+/*
+ * Return an SCB resource to the free list.
+ */
+static __inline void
+ahc_free_scb(struct ahc_softc *ahc, struct scb *scb)
+{       
+	struct hardware_scb *hscb;
+
+	hscb = scb->hscb;
+	/* Clean up for the next user */
+	ahc->scb_data->scbindex[hscb->tag] = NULL;
+	scb->flags = SCB_FREE;
+	hscb->control = 0;
+
+	SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs, scb, links.sle);
+
+	/* Notify the OSM that a resource is now available. */
+	ahc_platform_scb_free(ahc, scb);
+}
+
+static __inline struct scb *
+ahc_lookup_scb(struct ahc_softc *ahc, u_int tag)
+{
+	return (ahc->scb_data->scbindex[tag]);
+
+}
+
+static __inline void
+ahc_swap_with_next_hscb(struct ahc_softc *ahc, struct scb *scb)
+{
+	struct hardware_scb *q_hscb;
+	u_int  saved_tag;
+
+	/*
+	 * Our queuing method is a bit tricky.  The card
+	 * knows in advance which HSCB to download, and we
+	 * can't disappoint it.  To achieve this, the next
+	 * SCB to download is saved off in ahc->next_queued_scb.
+	 * When we are called to queue "an arbitrary scb",
+	 * we copy the contents of the incoming HSCB to the one
+	 * the sequencer knows about, swap HSCB pointers and
+	 * finally assign the SCB to the tag indexed location
+	 * in the scb_array.  This makes sure that we can still
+	 * locate the correct SCB by SCB_TAG.
+	 */
+	q_hscb = ahc->next_queued_scb->hscb;
+	saved_tag = q_hscb->tag;
+	memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb));
+	if ((scb->flags & SCB_CDB32_PTR) != 0) {
+		q_hscb->shared_data.cdb_ptr =
+		    ahc_hscb_busaddr(ahc, q_hscb->tag)
+		  + offsetof(struct hardware_scb, cdb32);	
+	}
+	q_hscb->tag = saved_tag;
+	q_hscb->next = scb->hscb->tag;
+
+	/* Now swap HSCB pointers. */
+	ahc->next_queued_scb->hscb = scb->hscb;
+	scb->hscb = q_hscb;
+
+	/* Now define the mapping from tag to SCB in the scbindex */
+	ahc->scb_data->scbindex[scb->hscb->tag] = scb;
+}
+
+/*
+ * Tell the sequencer about a new transaction to execute.
+ */
+static __inline void
+ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+	ahc_swap_with_next_hscb(ahc, scb);
+
+	if (scb->hscb->tag == SCB_LIST_NULL
+	 || scb->hscb->next == SCB_LIST_NULL)
+		panic("Attempt to queue invalid SCB tag %x:%x\n",
+		      scb->hscb->tag, scb->hscb->next);
+
+	/*
+	 * Keep a history of SCBs we've downloaded in the qinfifo.
+	 */
+	ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+		ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+	} else {
+		if ((ahc->features & AHC_AUTOPAUSE) == 0)
+			pause_sequencer(ahc);
+		ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+		if ((ahc->features & AHC_AUTOPAUSE) == 0)
+			unpause_sequencer(ahc);
+	}
+}
+
+static __inline struct scsi_sense_data *
+ahc_get_sense_buf(struct ahc_softc *ahc, struct scb *scb)
+{
+	int offset;
+
+	offset = scb - ahc->scb_data->scbarray;
+	return (&ahc->scb_data->sense[offset]);
+}
+
+static __inline uint32_t
+ahc_get_sense_bufaddr(struct ahc_softc *ahc, struct scb *scb)
+{
+	int offset;
+
+	offset = scb - ahc->scb_data->scbarray;
+	return (ahc->scb_data->sense_busaddr
+	      + (offset * sizeof(struct scsi_sense_data)));
+}
+
+/************************** Interrupt Processing ******************************/
+static __inline u_int ahc_check_cmdcmpltqueues(struct ahc_softc *ahc);
+static __inline void ahc_intr(struct ahc_softc *ahc);
+
+/*
+ * See if the firmware has posted any completed commands
+ * into our in-core command complete fifos.
+ */
+#define AHC_RUN_QOUTFIFO 0x1
+#define AHC_RUN_TQINFIFO 0x2
+static __inline u_int
+ahc_check_cmdcmpltqueues(struct ahc_softc *ahc)
+{
+	u_int retval;
+
+	retval = 0;
+	if (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL)
+		retval |= AHC_RUN_QOUTFIFO;
+#ifdef AHC_TARGET_MODE
+	if ((ahc->flags & AHC_TARGETROLE) != 0
+	 && ahc->targetcmds[ahc->tqinfifonext].cmd_valid != 0)
+		retval |= AHC_RUN_TQINFIFO;
+#endif
+	return (retval);
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+static __inline void
+ahc_intr(struct ahc_softc *ahc)
+{
+	u_int	intstat;
+	u_int 	queuestat;
+
+	/*
+	 * Instead of directly reading the interrupt status register,
+	 * infer the cause of the interrupt by checking our in-core
+	 * completion queues.  This avoids a costly PCI bus read in
+	 * most cases.
+	 */
+	intstat = 0;
+	if ((queuestat = ahc_check_cmdcmpltqueues(ahc)) != 0)
+		intstat = CMDCMPLT;
+
+	if ((intstat & INT_PEND) == 0
+	 || (ahc->flags & AHC_ALL_INTERRUPTS) != 0) {
+
+		intstat = ahc_inb(ahc, INTSTAT);
+#if AHC_PCI_CONFIG > 0
+		if (ahc->unsolicited_ints > 500
+		 && (ahc->chip & AHC_PCI) != 0
+		 && (ahc_inb(ahc, ERROR) & PCIERRSTAT) != 0)
+			ahc_pci_intr(ahc);
+#endif
+	}
+
+	if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0)
+		/* Hot eject */
+		return;
+
+	if ((intstat & INT_PEND) == 0) {
+		ahc->unsolicited_ints++;
+		return;
+	}
+	ahc->unsolicited_ints = 0;
+
+	if (intstat & CMDCMPLT) {
+		ahc_outb(ahc, CLRINT, CLRCMDINT);
+
+		/*
+		 * Ensure that the chip sees that we've cleared
+		 * this interrupt before we walk the output fifo.
+		 * Otherwise, we may, due to posted bus writes,
+		 * clear the interrupt after we finish the scan,
+		 * and after the sequencer has added new entries
+		 * and asserted the interrupt again.
+		 */
+		ahc_flush_device_writes(ahc);
+#ifdef AHC_TARGET_MODE
+		if ((queuestat & AHC_RUN_QOUTFIFO) != 0)
+#endif
+			ahc_run_qoutfifo(ahc);
+#ifdef AHC_TARGET_MODE
+		if ((queuestat & AHC_RUN_TQINFIFO) != 0)
+			ahc_run_tqinfifo(ahc, /*paused*/FALSE);
+#endif
+	}
+	if (intstat & BRKADRINT) {
+		ahc_handle_brkadrint(ahc);
+		/* Fatal error, no more interrupts to handle. */
+		return;
+	}
+
+	if ((intstat & (SEQINT|SCSIINT)) != 0)
+		ahc_pause_bug_fix(ahc);
+
+	if ((intstat & SEQINT) != 0)
+		ahc_handle_seqint(ahc, intstat);
+
+	if ((intstat & SCSIINT) != 0)
+		ahc_handle_scsiint(ahc, intstat);
+}
+
+#endif  /* _AIC7XXX_INLINE_H_ */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_linux.c linux/drivers/scsi/aic7xxx/aic7xxx_linux.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_linux.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx_linux.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,2650 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * $Id: //depot/src/linux/drivers/scsi/aic7xxx/aic7xxx_linux.c#54 $
+ *
+ * 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.
+ *
+ * Sources include the Adaptec 1740 driver (aha1740.c), the Ultrastor 24F
+ * driver (ultrastor.c), various Linux kernel source, the Adaptec EISA
+ * config file (!adp7771.cfg), the Adaptec AHA-2740A Series User's Guide,
+ * the Linux Kernel Hacker's Guide, Writing a SCSI Device Driver for Linux,
+ * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA overlay file
+ * (adp7770.ovl), the Adaptec AHA-2740 Series Technical Reference Manual,
+ * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
+ * ANSI SCSI-2 specification (draft 10c), ...
+ *
+ * --------------------------------------------------------------------------
+ *
+ *  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.
+ *
+ * --------------------------------------------------------------------------
+ * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000 Justin T. Gibbs.
+ * Copyright (c) 2000 Adaptec Inc.
+ * 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ *  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
+ *           aic7xxx=no_reset
+ *           aic7xxx=ultra
+ *           aic7xxx=irq_trigger:[0,1]  # 0 edge, 1 level
+ *           aic7xxx=verbose
+ *
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
+ *
+ *  Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp
+ */
+
+/*
+ * Further driver modifications made by Doug Ledford <dledford@redhat.com>
+ *
+ * Copyright (c) 1997-1999 Doug Ledford
+ *
+ * These changes are released under the same licensing terms as the FreeBSD
+ * driver written by Justin Gibbs.  Please see his Copyright notice above
+ * for the exact terms and conditions covering my changes as well as the
+ * warranty statement.
+ *
+ * Modifications made to the aic7xxx.c,v 4.1 driver from Dan Eischen include
+ * but are not limited to:
+ *
+ *  1: Import of the latest FreeBSD sequencer code for this driver
+ *  2: Modification of kernel code to accomodate different sequencer semantics
+ *  3: Extensive changes throughout kernel portion of driver to improve
+ *     abort/reset processing and error hanndling
+ *  4: Other work contributed by various people on the Internet
+ *  5: Changes to printk information and verbosity selection code
+ *  6: General reliability related changes, especially in IRQ management
+ *  7: Modifications to the default probe/attach order for supported cards
+ *  8: SMP friendliness has been improved
+ *
+ */
+
+/*
+ * The next three defines are user configurable.  These should be the only
+ * defines a user might need to get in here and change.  There are other
+ * defines buried deeper in the code, but those really shouldn't need touched
+ * under normal conditions.
+ */
+
+#if defined(MODULE) || defined(PCMCIA)
+#include <linux/module.h>
+#endif
+
+#if defined(PCMCIA)
+#undef MODULE
+#endif
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+#include <linux/init.h>		/* __setup */
+#endif
+
+#include "../sd.h"		/* For geometry detection */
+
+/*
+ * To generate the correct addresses for the controller to issue
+ * on the bus.  Originally added for DEC Alpha support.
+ */
+#define VIRT_TO_BUS(a) (uint32_t)virt_to_bus((void *)(a))
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+struct proc_dir_entry proc_scsi_aic7xxx = {
+	PROC_SCSI_AIC7XXX, 7, "aic7xxx",
+	S_IFDIR | S_IRUGO | S_IXUGO, 2,
+	0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL
+};
+#endif
+
+/*
+ * Set this to the delay in seconds after SCSI bus reset.
+ * Note, we honor this only for the initial bus reset.
+ * The scsi error recovery code performs its own bus settle
+ * delay handling for error recovery actions.
+ */
+#ifdef CONFIG_AIC7XXX_RESET_DELAY
+#define AIC7XXX_RESET_DELAY CONFIG_AIC7XXX_RESET_DELAY
+#else
+#define AIC7XXX_RESET_DELAY 5000
+#endif
+
+/*
+ * 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.
+ */
+#ifdef CONFIG_AIC7XXX_PROC_STATS
+#define AIC7XXX_PROC_STATS
+#endif
+
+/*
+ * To change the default number of tagged transactions allowed per-device,
+ * add a line to the lilo.conf file like:
+ * append="aic7xxx=verbose,tag_info:{{32,32,32,32},{32,32,32,32}}"
+ * which will result in the first four devices on the first two
+ * controllers being set to a tagged queue depth of 32.
+ *
+ * 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.
+ */
+typedef struct {
+	uint8_t tag_commands[16];	/* Allow for wide/twin adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Modify this as you see fit for your system.
+ *
+ * 0			tagged queuing disabled
+ * 1 <= n <= 253	n == max tags ever dispatched.
+ *
+ * The driver will throttle the number of commands dispatched to a
+ * device if it returns queue full.  For devices with a fixed maximum
+ * queue depth, the driver will eventually determine this depth and
+ * lock it in (a console message is printed to indicate that a lock
+ * has occurred).  On some devices, queue full is returned for a temporary
+ * resource shortage.  These devices will return queue full at varying
+ * depths.  The driver will throttle back when the queue fulls occur and
+ * attempt to slowly increase the depth over time as the device recovers
+ * from the resource shortage.
+ *
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter.
+ *
+ * The second line enables tagged queueing with 4 commands/LUN for IDs
+ * (0, 2-11, 13-15), disables tagged queueing for ID 12, and tells the
+ * driver to attempt to use up to 64 tags 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.
+ */
+
+/*
+ * NOTE: The below structure is for reference only, the actual structure
+ *       to modify in order to change things is just below this comment block.
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+	{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+	{{4, 64, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4}},
+	{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
+	{{0, 16, 4, 0, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+*/
+
+#ifdef CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_CMDS_PER_DEVICE
+#else
+#define AIC7XXX_CMDS_PER_DEVICE AHC_MAX_QUEUE
+#endif
+
+#define AIC7XXX_CONFIGED_TAG_COMMANDS {					\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE,		\
+	AIC7XXX_CMDS_PER_DEVICE, AIC7XXX_CMDS_PER_DEVICE		\
+}
+
+/*
+ * By default, use the number of commands specified by
+ * the users kernel configuration.
+ */
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS},
+	{AIC7XXX_CONFIGED_TAG_COMMANDS}
+};
+
+/*
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW   DID_ERROR
+
+void
+ahc_print_path(struct ahc_softc *ahc, struct scb *scb)
+{
+	printk("(scsi%d:%c:%d:%d): ",
+	       ahc->platform_data->host->host_no,
+	       scb != NULL ? SCB_GET_CHANNEL(ahc, scb) : 'X',
+	       scb != NULL ? SCB_GET_TARGET(ahc, scb) : -1,
+	       scb != NULL ? SCB_GET_LUN(scb) : -1);
+}
+
+/*
+ * XXX - these options apply unilaterally to _all_ 274x/284x/294x
+ *       cards in the system.  This should be fixed.  Exceptions to this
+ *       rule are noted in the comments.
+ */
+
+/*
+ * Skip the scsi bus reset.  Non 0 make us skip the reset at startup.  This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static uint32_t aic7xxx_no_reset;
+
+/*
+ * Certain PCI motherboards will scan PCI devices from highest to lowest,
+ * others scan from lowest to highest, and they tend to do all kinds of
+ * strange things when they come into contact with PCI bridge chips.  The
+ * net result of all this is that the PCI card that is actually used to boot
+ * the machine is very hard to detect.  Most motherboards go from lowest
+ * PCI slot number to highest, and the first SCSI controller found is the
+ * one you boot from.  The only exceptions to this are when a controller
+ * has its BIOS disabled.  So, we by default sort all of our SCSI controllers
+ * from lowest PCI slot number to highest PCI slot number.  We also force
+ * all controllers with their BIOS disabled to the end of the list.  This
+ * works on *almost* all computers.  Where it doesn't work, we have this
+ * option.  Setting this option to non-0 will reverse the order of the sort
+ * to highest first, then lowest, but will still leave cards with their BIOS
+ * disabled at the very end.  That should fix everyone up unless there are
+ * really strange cirumstances.
+ */
+static int aic7xxx_reverse_scan = 0;
+
+/*
+ * Should we force EXTENDED translation on a controller.
+ *     0 == Use whatever is in the SEEPROM or default to off
+ *     1 == Use whatever is in the SEEPROM or default to on
+ */
+static uint32_t aic7xxx_extended = 0;
+
+/*
+ * The IRQ trigger method used on EISA controllers. Does not effect PCI cards.
+ *   -1 = Use detected settings.
+ *    0 = Force Edge triggered mode.
+ *    1 = Force Level triggered mode.
+ */
+static int aic7xxx_irq_trigger = -1;
+
+/*
+ * This variable is used to override the termination settings on a controller.
+ * This should not be used under normal conditions.  However, in the case
+ * that a controller does not have a readable SEEPROM (so that we can't
+ * read the SEEPROM settings directly) and that a controller has a buggered
+ * version of the cable detection logic, this can be used to force the
+ * correct termination.  It is preferable to use the manual termination
+ * settings in the BIOS if possible, but some motherboard controllers store
+ * those settings in a format we can't read.  In other cases, auto term
+ * should also work, but the chipset was put together with no auto term
+ * logic (common on motherboard controllers).  In those cases, we have
+ * 32 bits here to work with.  That's good for 8 controllers/channels.  The
+ * bits are organized as 4 bits per channel, with scsi0 getting the lowest
+ * 4 bits in the int.  A 1 in a bit position indicates the termination setting
+ * that corresponds to that bit should be enabled, a 0 is disabled.
+ * It looks something like this:
+ *
+ *    0x0f =  1111-Single Ended Low Byte Termination on/off
+ *            ||\-Single Ended High Byte Termination on/off
+ *            |\-LVD Low Byte Termination on/off
+ *            \-LVD High Byte Termination on/off
+ *
+ * For non-Ultra2 controllers, the upper 2 bits are not important.  So, to
+ * enable both high byte and low byte termination on scsi0, I would need to
+ * make sure that the override_term variable was set to 0x03 (bits 0011).
+ * To make sure that all termination is enabled on an Ultra2 controller at
+ * scsi2 and only high byte termination on scsi1 and high and low byte
+ * termination on scsi0, I would set override_term=0xf23 (bits 1111 0010 0011)
+ *
+ * For the most part, users should never have to use this, that's why I
+ * left it fairly cryptic instead of easy to understand.  If you need it,
+ * most likely someone will be telling you what your's needs to be set to.
+ */
+static int aic7xxx_override_term = -1;
+
+/*
+ * Certain motherboard chipset controllers tend to screw
+ * up the polarity of the term enable output pin.  Use this variable
+ * to force the correct polarity for your system.  This is a bitfield variable
+ * similar to the previous one, but this one has one bit per channel instead
+ * of four.
+ *    0 = Force the setting to active low.
+ *    1 = Force setting to active high.
+ * Most Adaptec cards are active high, several motherboards are active low.
+ * To force a 2940 card at SCSI 0 to active high and a motherboard 7895
+ * controller at scsi1 and scsi2 to active low, and a 2910 card at scsi3
+ * to active high, you would need to set stpwlev=0x9 (bits 1001).
+ *
+ * People shouldn't need to use this, but if you are experiencing lots of
+ * SCSI timeout problems, this may help.  There is one sure way to test what
+ * this option needs to be.  Using a boot floppy to boot the system, configure
+ * your system to enable all SCSI termination (in the Adaptec SCSI BIOS) and
+ * if needed then also pass a value to override_term to make sure that the
+ * driver is enabling SCSI termination, then set this variable to either 0
+ * or 1.  When the driver boots, make sure there are *NO* SCSI cables
+ * connected to your controller.  If it finds and inits the controller
+ * without problem, then the setting you passed to stpwlev was correct.  If
+ * the driver goes into a reset loop and hangs the system, then you need the
+ * other setting for this variable.  If neither setting lets the machine
+ * boot then you have definite termination problems that may not be fixable.
+ */
+static int aic7xxx_stpwlev = -1;
+
+/*
+ * Set this to non-0 in order to force the driver to panic the kernel
+ * and print out debugging info on a SCSI abort or reset cycle.
+ */
+static int aic7xxx_panic_on_abort = 0;
+
+/*
+ * PCI bus parity checking of the Adaptec controllers.  This is somewhat
+ * dubious at best.  To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations, it can generate tons of false error messages.
+ * It's included in the driver for completeness.
+ *   0 = Shut off PCI parity check
+ *  -1 = Normal polarity pci parity checking
+ *   1 = reverse polarity pci parity checking
+ *
+ * NOTE: you can't actually pass -1 on the lilo prompt.  So, to set this
+ * variable to -1 you would actually want to simply pass the variable
+ * name without a number.  That will invert the 0 which will result in
+ * -1.
+ */
+static int aic7xxx_pci_parity = 0;
+
+/*
+ * Set this to a non-0 value to make us dump out the 32 bit instruction
+ * registers on the card after completing the sequencer download.  This
+ * allows the actual sequencer download to be verified.  It is possible
+ * to use this option and still boot up and run your system.  This is
+ * only intended for debugging purposes.
+ */
+static int aic7xxx_dump_sequencer = 0;
+
+/*
+ * Certain newer motherboards have put new PCI based devices into the
+ * IO spaces that used to typically be occupied by VLB or EISA cards.
+ * This overlap can cause these newer motherboards to lock up when scanned
+ * for older EISA and VLB devices.  Setting this option to non-0 will
+ * cause the driver to skip scanning for any VLB or EISA controllers and
+ * only support the PCI controllers.  NOTE: this means that if the kernel
+ * os compiled with PCI support disabled, then setting this to non-0
+ * would result in never finding any devices :)
+ */
+int aic7xxx_no_probe;
+
+/*
+ * aic7xxx_detect() has been run, so register all device arrivals
+ * immediately with the system rather than deferring to the sorted
+ * attachment performed by aic7xxx_detect().
+ */
+int aic7xxx_detect_complete;
+
+/*
+ * So that we can set how long each device is given as a selection timeout.
+ * The table of values goes like this:
+ *   0 - 256ms
+ *   1 - 128ms
+ *   2 - 64ms
+ *   3 - 32ms
+ * We default to 256ms because some older devices need a longer time
+ * to respond to initial selection.
+ */
+static int aic7xxx_seltime = 0x00;
+
+/*
+ * So that insmod can find the variable and make it point to something
+ */
+#ifdef MODULE
+static char *aic7xxx = NULL;
+
+MODULE_PARM(aic7xxx, "s");
+
+/*
+ * Just in case someone uses commas to separate items on the insmod
+ * command line, we define a dummy buffer here to avoid having insmod
+ * write wild stuff into our code segment
+ */
+static char dummy_buffer[60] = "Please don't trounce on me insmod!!\n";
+
+#endif
+
+static void ahc_handle_scsi_status(struct ahc_softc *,
+				   struct ahc_linux_device *, struct scb *);
+static void ahc_filter_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd);
+static void ahc_sem_timeout(u_long arg);
+static void ahc_release_sim_queue(u_long arg);
+static int aic7xxx_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag);
+static void aic7xxx_initialize_scsi_bus(struct ahc_softc *ahc);
+static void aic7xxx_select_queue_depth(struct Scsi_Host *host,
+				       Scsi_Device *scsi_devs);
+static void aic7xxx_device_queue_depth(struct ahc_softc *ahc,
+				       Scsi_Device *device);
+static struct ahc_linux_target*	ahc_alloc_target(struct ahc_softc *,
+						 u_int, u_int);
+static void			ahc_free_target(struct ahc_softc *ahc,
+						struct ahc_linux_target *targ);
+static struct ahc_linux_device*	ahc_alloc_device(struct ahc_softc *,
+						 struct ahc_linux_target *,
+						 u_int);
+static void			ahc_free_device(struct ahc_softc *ahc,
+						struct ahc_linux_device *dev);
+static void ahc_run_device_queue(struct ahc_softc *ahc,
+				 struct ahc_linux_device *dev);
+static void ahc_setup_tag_info(char *p, char *end);
+static int ahc_next_unit(void);
+
+static __inline struct ahc_linux_device*
+		     ahc_get_device(struct ahc_softc *ahc, u_int channel,
+				    u_int target, u_int lun, int /*alloc*/);
+static __inline void ahc_queue_cmd_complete(struct ahc_softc *ahc,
+					    Scsi_Cmnd *cmd);
+static __inline void ahc_run_complete_queue(struct ahc_softc *ahc,
+					    struct ahc_cmd *acmd);
+static __inline void ahc_check_device_queue(struct ahc_softc *ahc,
+					  struct ahc_linux_device *dev);
+static __inline void ahc_sniff_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd);
+static __inline void ahc_unmap_scb(struct ahc_softc *ahc, struct scb *scb);
+
+static __inline struct ahc_linux_device*
+ahc_get_device(struct ahc_softc *ahc, u_int channel, u_int target,
+	       u_int lun, int alloc)
+{
+	struct ahc_linux_target *targ;
+	struct ahc_linux_device *dev;
+	u_int target_offset;
+
+	target_offset = target;
+	if (channel != 0)
+		target_offset += 8;
+	targ = ahc->platform_data->targets[target_offset];
+	if (targ == NULL) {
+		if (alloc != 0) {
+			targ = ahc_alloc_target(ahc, channel, target);
+			if (targ == NULL)
+				return (NULL);
+		} else
+			return (NULL);
+	}
+	dev = targ->devices[lun];
+	if (dev == NULL && alloc != 0)
+		dev = ahc_alloc_device(ahc, targ, lun);
+	return (dev);
+}
+
+static __inline void
+ahc_queue_cmd_complete(struct ahc_softc *ahc, Scsi_Cmnd *cmd)
+{
+	/*
+	 * Typically, the complete queue has very few entries
+	 * queued to it before the queue is emptied by
+	 * ahc_run_complete_queue, so sorting the entries
+	 * by generation number should be inexpensive.
+	 * We perform the sort so that commands that complete
+	 * with an error are retuned in the order origionally
+	 * queued to the controller so that any subsequent retries
+	 * are performed in order.  The underlying ahc routines do
+	 * not guarantee the order that aborted commands will be
+	 * returned to us.
+	 */
+	struct ahc_completeq *completeq;
+	struct ahc_cmd *list_cmd;
+	struct ahc_cmd *acmd;
+
+	/*
+	 * If we want the request requeued, make sure there
+	 * are sufficent retries.  In the old scsi error code,
+	 * we used to be able to specify a result code that
+	 * bypassed the retry count.  Now we must use this
+	 * hack.
+	 */
+	if (cmd->result == (CAM_REQUEUE_REQ << 16))
+		cmd->retries--;
+	completeq = &ahc->platform_data->completeq;
+	list_cmd = TAILQ_FIRST(completeq);
+	acmd = (struct ahc_cmd *)cmd;
+	while (list_cmd != NULL
+	    && acmd_scsi_cmd(list_cmd).serial_number
+	     < acmd_scsi_cmd(acmd).serial_number)
+		list_cmd = TAILQ_NEXT(list_cmd, acmd_links.tqe);
+	if (list_cmd != NULL)
+		TAILQ_INSERT_BEFORE(list_cmd, acmd, acmd_links.tqe);
+	else
+		TAILQ_INSERT_TAIL(completeq, acmd, acmd_links.tqe);
+}
+
+static __inline void
+ahc_run_complete_queue(struct ahc_softc *ahc, struct ahc_cmd *acmd)
+{	
+	u_long done_flags;
+
+	ahc_done_lock(ahc, &done_flags);
+	while (acmd != NULL) {
+		Scsi_Cmnd *cmd;
+
+		cmd = &acmd_scsi_cmd(acmd);
+		acmd = TAILQ_NEXT(acmd, acmd_links.tqe);
+		cmd->host_scribble = NULL;
+		cmd->scsi_done(cmd);
+	}
+	ahc_done_unlock(ahc, &done_flags);
+}
+
+static __inline void
+ahc_check_device_queue(struct ahc_softc *ahc, struct ahc_linux_device *dev)
+{
+	if ((dev->flags & AHC_DEV_FREEZE_TIL_EMPTY) != 0
+	 && dev->active == 0) {
+		dev->flags &= ~AHC_DEV_FREEZE_TIL_EMPTY;
+		dev->qfrozen--;
+	}
+
+	if (TAILQ_FIRST(&dev->busyq) == NULL
+	 || dev->openings == 0 || dev->qfrozen != 0)
+		return;
+
+	ahc_run_device_queue(ahc, dev);
+}
+
+static __inline void
+ahc_run_device_queues(struct ahc_softc *ahc)
+{
+	struct ahc_linux_device *dev;
+
+	while ((ahc->flags & AHC_RESOURCE_SHORTAGE) == 0
+	    && ahc->platform_data->qfrozen == 0
+	    && (dev = LIST_FIRST(&ahc->platform_data->device_runq)) != NULL) {
+		LIST_REMOVE(dev, links);
+		dev->flags &= ~AHC_DEV_ON_RUN_LIST;
+		ahc_check_device_queue(ahc, dev);
+	}
+}
+
+static __inline void
+ahc_sniff_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd)
+{
+	/*
+	 * Determine whether we care to filter
+	 * information out of this command.  If so,
+	 * pass it on to ahc_filter_command() for more
+	 * heavy weight processing.
+	 */
+	if (cmd->cmnd[0] == INQUIRY)
+		ahc_filter_command(ahc, cmd);
+}
+
+static __inline void
+ahc_unmap_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+	Scsi_Cmnd *cmd;
+
+	cmd = scb->io_ctx;
+	if (cmd->use_sg != 0) {
+		struct scatterlist *sg;
+
+		sg = (struct scatterlist *)cmd->request_buffer;
+		pci_unmap_sg(ahc->dev_softc, sg, cmd->use_sg,
+			     scsi_to_pci_dma_dir(cmd->sc_data_direction));
+	} else if (cmd->request_bufflen != 0)
+		pci_unmap_single(ahc->dev_softc,
+				 ahc_le32toh(scb->sg_list[0].addr),
+				 cmd->request_bufflen,
+				 scsi_to_pci_dma_dir(cmd->sc_data_direction));
+}
+
+/******************************** Macros **************************************/
+#define BUILD_SCSIID(ahc, cmd)						\
+	((((cmd)->target << TID_SHIFT) & TID)				\
+	| (((cmd)->channel == 0) ? (ahc)->our_id : (ahc)->our_id_b)	\
+	| (((cmd)->channel == 0) ? 0 : TWIN_CHNLB))
+
+/******************************** Bus DMA *************************************/
+int
+ahc_dma_tag_create(struct ahc_softc *ahc, bus_dma_tag_t parent,
+		   bus_size_t alignment, bus_size_t boundary,
+		   bus_addr_t lowaddr, bus_addr_t highaddr,
+		   bus_dma_filter_t *filter, void *filterarg,
+		   bus_size_t maxsize, int nsegments,
+		   bus_size_t maxsegsz, int flags, bus_dma_tag_t *ret_tag)
+{
+	bus_dma_tag_t dmat;
+
+	dmat = malloc(sizeof(*dmat), M_DEVBUF, M_NOWAIT);
+	if (dmat == NULL)
+		return (ENOMEM);
+
+	/*
+	 * Linux is very simplistic about DMA memory.  For now don't
+	 * maintain all specification information.  Once Linux supplies
+	 * better facilities for doing these operations, or the
+	 * needs of this particular driver change, we might need to do
+	 * more here.
+	 */
+	dmat->alignment = alignment;
+	dmat->boundary = boundary;
+	dmat->maxsize = maxsize;
+	*ret_tag = dmat;
+	return (0);
+}
+
+void
+ahc_dma_tag_destroy(struct ahc_softc *ahc, bus_dma_tag_t dmat)
+{
+	free(dmat, M_DEVBUF);
+}
+
+int
+ahc_dmamem_alloc(struct ahc_softc *ahc, bus_dma_tag_t dmat, void** vaddr,
+		 int flags, bus_dmamap_t *mapp)
+{
+	bus_dmamap_t map;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+	map = malloc(sizeof(*map), M_DEVBUF, M_NOWAIT);
+	if (map == NULL)
+		return (ENOMEM);
+	*vaddr = pci_alloc_consistent(ahc->dev_softc,
+				      dmat->maxsize, &map->bus_addr);
+#else
+	/*
+	 * At least in 2.2.14, malloc is a slab allocator so all
+	 * allocations are aligned.  We assume, for these kernel versions
+	 * that all allocations will be bellow 4Gig, physically contiguous,
+	 * and accessable via DMA by the controller.
+	 */
+	map = NULL; /* No additional information to store */
+	*vaddr = malloc(dmat->maxsize, M_DEVBUF, M_NOWAIT);
+#endif
+	if (*vaddr == NULL)
+		return (ENOMEM);
+	*mapp = map;
+	return(0);
+}
+
+void
+ahc_dmamem_free(struct ahc_softc *ahc, bus_dma_tag_t dmat,
+		void* vaddr, bus_dmamap_t map)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+	pci_free_consistent(ahc->dev_softc, dmat->maxsize,
+			    vaddr, map->bus_addr);
+#else
+	free(vaddr, M_DEVBUF);
+#endif
+}
+
+int
+ahc_dmamap_load(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map,
+		void *buf, bus_size_t buflen, bus_dmamap_callback_t *cb,
+		void *cb_arg, int flags)
+{
+	/*
+	 * Assume for now that this will only be used during
+	 * initialization and not for per-transaction buffer mapping.
+	 */
+	bus_dma_segment_t stack_sg;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+	stack_sg.ds_addr = map->bus_addr;
+#else
+	stack_sg.ds_addr = VIRT_TO_BUS(buf);
+#endif
+	stack_sg.ds_len = dmat->maxsize;
+	cb(cb_arg, &stack_sg, /*nseg*/1, /*error*/0);
+	return (0);
+}
+
+void
+ahc_dmamap_destroy(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+	/*
+	 * The map may is NULL in our < 2.3.X implementation.
+	 */
+	if (map != NULL)
+		free(map, M_DEVBUF);
+}
+
+int
+ahc_dmamap_unload(struct ahc_softc *ahc, bus_dma_tag_t dmat, bus_dmamap_t map)
+{
+	/* Nothing to do */
+	return (0);
+}
+
+/********************* Platform Dependent Functions ***************************/
+int
+ahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc)
+{
+	int	value;
+	int	rvalue;
+	int	lvalue;
+
+	/*
+	 * Under Linux, cards are ordered as follows:
+	 *	1) VLB/EISA BIOS enabled devices sorted by BIOS address.
+	 *	2) PCI devices with BIOS enabled sorted by bus/slot/func.
+	 *	3) All remaining VLB/EISA devices sorted by ioport.
+	 *	4) All remaining PCI devices sorted by bus/slot/func.
+	 */
+	value = (lahc->flags & AHC_BIOS_ENABLED)
+	      - (rahc->flags & AHC_BIOS_ENABLED);
+	if (value != 0)
+		/* Controllers with BIOS enabled have a *higher* priority */
+		return (-value);
+
+	/*
+	 * Same BIOS setting, now sort based on bus type.
+	 * EISA and VL controllers sort together.  EISA/VL
+	 * have higher priority than PCI.
+	 */
+	rvalue = (rahc->chip & AHC_BUS_MASK);
+ 	if (rvalue == AHC_VL)
+		rvalue = AHC_EISA;
+	lvalue = (lahc->chip & AHC_BUS_MASK);
+ 	if (lvalue == AHC_VL)
+		lvalue = AHC_EISA;
+	value = lvalue - rvalue;
+	if (value != 0)
+		return (value);
+
+	/* Still equal.  Sort by BIOS address, ioport, or bus/slot/func. */
+	switch (rvalue) {
+	case AHC_PCI:
+		value = ahc_get_pci_bus(lahc->dev_softc)
+		      - ahc_get_pci_bus(rahc->dev_softc);
+		if (value != 0)
+			break;
+		value = ahc_get_pci_slot(lahc->dev_softc)
+		      - ahc_get_pci_slot(rahc->dev_softc);
+		if (value != 0)
+			break;
+		value = ahc_get_pci_function(lahc->dev_softc)
+		      - ahc_get_pci_function(rahc->dev_softc);
+		/*
+		 * On multi-function devices, the user can choose
+		 * to have function 1 probed before function 0.
+		 * Function 0 is the only one that will have
+		 * CHANNEL_B_PRIMARY set.
+		 */
+		if (value < 0
+		 && (lahc->flags & AHC_CHANNEL_B_PRIMARY) != 0)
+			/* Swap the two */
+			value = -value;
+		break;
+	case AHC_EISA:
+		if ((rahc->flags & AHC_BIOS_ENABLED) != 0) {
+			value = lahc->platform_data->bios_address
+			      - rahc->platform_data->bios_address; 
+		} else {
+			value = lahc->bsh.ioport
+			      - rahc->bsh.ioport; 
+		}
+		break;
+	default:
+		panic("ahc_softc_sort: invalid bus type");
+	}
+	return (value);
+}
+
+static void
+ahc_setup_tag_info(char *p, char *end)
+{
+	char	*base;
+	char	*tok;
+	char	*tok_end;
+	char	*tok_end2;
+	int      i;
+	int      instance;
+	int	 device;
+	int	 done;
+	char	 tok_list[] = {'.', ',', '{', '}', '\0'};
+
+	if (*p != ':')
+		return;
+
+	instance = -1;
+	device = -1;
+	done = FALSE;
+	base = p;
+	/* Forward us just past the ':' */
+	tok = base + 1;
+	tok_end = strchr(tok, '\0');
+	if (tok_end < end)
+		*tok_end = ',';
+	while (!done) {
+		switch (*tok) {
+		case '{':
+			if (instance == -1)
+				instance = 0;
+			else if (device == -1)
+				device = 0;
+			tok++;
+			break;
+		case '}':
+			if (device != -1)
+				device = -1;
+			else if (instance != -1)
+				instance = -1;
+			tok++;
+			break;
+		case ',':
+		case '.':
+			if (instance == -1)
+				done = TRUE;
+			else if (device >= 0)
+				device++;
+			else if (instance >= 0)
+				instance++;
+			if ((device >= AHC_NUM_TARGETS) ||
+			    (instance >= NUM_ELEMENTS(aic7xxx_tag_info)))
+				done = TRUE;
+			tok++;
+			if (!done) {
+				base = tok;
+			}
+			break;
+		case '\0':
+			done = TRUE;
+			break;
+		default:
+			done = TRUE;
+			tok_end = strchr(tok, '\0');
+			for (i = 0; tok_list[i]; i++) {
+				tok_end2 = strchr(tok, tok_list[i]);
+				if ((tok_end2) && (tok_end2 < tok_end)) {
+					tok_end = tok_end2;
+					done = FALSE;
+				}
+			}
+			if ((instance >= 0) && (device >= 0) &&
+			    (instance < NUM_ELEMENTS(aic7xxx_tag_info)) &&
+			    (device < AHC_NUM_TARGETS))
+				aic7xxx_tag_info[instance].tag_commands[device] =
+				    simple_strtoul(tok, NULL, 0) & 0xff;
+			tok = tok_end;
+			break;
+		}
+	}
+	while ((p != base) && (p != NULL))
+		p = strtok(NULL, ",.");
+}
+
+/*
+ * Handle Linux boot parameters. This routine allows for assigning a value
+ * to a parameter with a ':' between the parameter and the value.
+ * ie. aic7xxx=unpause:0x0A,extended
+ */
+int
+aic7xxx_setup(char *s)
+{
+	int	i, n;
+	char   *p;
+	char   *end;
+
+	static struct {
+		const char *name;
+		uint32_t *flag;
+	} options[] = {
+		{ "extended", &aic7xxx_extended },
+		{ "no_reset", &aic7xxx_no_reset },
+		{ "irq_trigger", &aic7xxx_irq_trigger },
+		{ "verbose", &aic7xxx_verbose },
+		{ "reverse_scan", &aic7xxx_reverse_scan },
+		{ "override_term", &aic7xxx_override_term },
+		{ "stpwlev", &aic7xxx_stpwlev },
+		{ "no_probe", &aic7xxx_no_probe },
+		{ "panic_on_abort", &aic7xxx_panic_on_abort },
+		{ "pci_parity", &aic7xxx_pci_parity },
+		{ "dump_sequencer", &aic7xxx_dump_sequencer },
+		{ "seltime", &aic7xxx_seltime },
+		{ "tag_info", NULL }
+	};
+
+	end = strchr(s, '\0');
+
+	for (p = strtok(s, ",."); p; p = strtok(NULL, ",.")) {
+		for (i = 0; i < NUM_ELEMENTS(options); i++) {
+			n = strlen(options[i].name);
+
+			if (strncmp(options[i].name, p, n) != 0)
+				continue;
+
+			if (strncmp(p, "tag_info", n) == 0) {
+				ahc_setup_tag_info(p + n, end);
+			} else if (p[n] == ':') {
+				*(options[i].flag) =
+				    simple_strtoul(p + n + 1, NULL, 0);
+			} else if (!strncmp(p, "verbose", n)) {
+				*(options[i].flag) = 1;
+			} else {
+				*(options[i].flag) = ~(*(options[i].flag));
+			}
+		}
+	}
+	return 1;
+}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
+__setup("aic7xxx=", aic7xxx_setup);
+#endif
+
+int aic7xxx_verbose;
+
+/*
+ * Try to detect an Adaptec 7XXX controller.
+ */
+int
+aic7xxx_detect(Scsi_Host_Template *template)
+{
+	struct	ahc_softc *ahc;
+	int     found;
+
+	/*
+	 * Sanity checking of Linux SCSI data structures so
+	 * that some of our hacks^H^H^H^H^Hassumptions aren't
+	 * violated.
+	 */
+	if (offsetof(struct ahc_cmd_internal, end)
+	  > offsetof(struct scsi_cmnd, host_scribble)) {
+		printf("aic7xxx_detect: SCSI data structures changed.\n");
+		printf("aic7xxx_detect: Unable to attach\n");
+		return (0);
+	}
+#ifdef MODULE
+	/*
+	 * If we've been passed any parameters, process them now.
+	 */
+	if (aic7xxx)
+		aic7xxx_setup(aic7xxx);
+	if (dummy_buffer[0] != 'P')
+		printk(KERN_WARNING
+"aic7xxx: Please read the file /usr/src/linux/drivers/scsi/README.aic7xxx\n"
+"aic7xxx: to see the proper way to specify options to the aic7xxx module\n"
+"aic7xxx: Specifically, don't use any commas when passing arguments to\n"
+"aic7xxx: insmod or else it might trash certain memory areas.\n");
+#endif
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0)
+	template->proc_name = "aic7xxx";
+#else
+	template->proc_dir = &proc_scsi_aic7xxx;
+#endif
+	template->sg_tablesize = AHC_NSEG;
+
+#ifdef CONFIG_PCI
+	ahc_linux_pci_probe(template);
+#endif
+
+	aic7770_linux_probe(template);
+
+	/*
+	 * Register with the SCSI layer all
+	 * controllers we've found.
+	 */
+	found = 0;
+	TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+
+		if (aic7xxx_register_host(ahc, template) == 0)
+			found++;
+	}
+	aic7xxx_detect_complete++;
+	return (found);
+}
+
+int
+aic7xxx_register_host(struct ahc_softc *ahc, Scsi_Host_Template *template)
+{
+	char  buf[80];
+	struct Scsi_Host *host;
+	char *new_name;
+	u_long s;
+
+
+	template->name = ahc->description;
+	host = scsi_register(template, sizeof(struct ahc_softc *));
+	if (host == NULL)
+		return (ENOMEM);
+
+	ahc_lock(ahc, &s);
+	*((struct ahc_softc **)host->hostdata) = ahc;
+	ahc->platform_data->host = host;
+	host->can_queue = AHC_MAX_QUEUE;
+	host->cmd_per_lun = 2;
+	host->sg_tablesize = AHC_NSEG;
+	host->select_queue_depths = aic7xxx_select_queue_depth;
+	host->this_id = ahc->our_id;
+	host->irq = ahc->platform_data->irq;
+	host->max_id = (ahc->features & AHC_WIDE) ? 16 : 8;
+	host->max_channel = (ahc->features & AHC_TWIN) ? 1 : 0;
+	ahc_set_unit(ahc, ahc_next_unit());
+	sprintf(buf, "scsi%d", host->host_no);
+	new_name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+	if (new_name != NULL) {
+		strcpy(new_name, buf);
+		ahc_set_name(ahc, new_name);
+	}
+	host->unique_id = ahc->unit;
+	aic7xxx_initialize_scsi_bus(ahc);
+	ahc_unlock(ahc, &s);
+	return (0);
+}
+
+/*
+ * Find the smallest available unit number to use
+ * for a new device.  We don't just use a static
+ * count to handle the "repeated hot-(un)plug"
+ * scenario.
+ */
+static int
+ahc_next_unit()
+{
+	struct ahc_softc *ahc;
+	int unit;
+
+	unit = 0;
+retry:
+	TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+		if (ahc->unit == unit) {
+			unit++;
+			goto retry;
+		}
+	}
+	return (unit);
+}
+
+/*
+ * Place the SCSI bus into a known state by either resetting it,
+ * or forcing transfer negotiations on the next command to any
+ * target.
+ */
+void
+aic7xxx_initialize_scsi_bus(struct ahc_softc *ahc)
+{
+	int i;
+	int numtarg;
+
+	i = 0;
+	numtarg = 0;
+
+	if (aic7xxx_no_reset != 0)
+		ahc->flags &= ~(AHC_RESET_BUS_A|AHC_RESET_BUS_B);
+
+	if ((ahc->flags & AHC_RESET_BUS_A) != 0)
+		ahc_reset_channel(ahc, 'A', /*initiate_reset*/TRUE);
+	else
+		numtarg = (ahc->features & AHC_WIDE) ? 16 : 8;
+
+	if ((ahc->features & AHC_TWIN) != 0) {
+
+		if ((ahc->flags & AHC_RESET_BUS_B) != 0) {
+			ahc_reset_channel(ahc, 'B', /*initiate_reset*/TRUE);
+		} else {
+			if (numtarg == 0)
+				i = 8;
+			numtarg += 8;
+		}
+	}
+
+	for (; i < numtarg; i++) {
+		struct ahc_devinfo devinfo;
+		struct ahc_initiator_tinfo *tinfo;
+		struct tmode_tstate *tstate;
+		u_int our_id;
+		u_int target_id;
+		char channel;
+
+		channel = 'A';
+		our_id = ahc->our_id;
+		target_id = i;
+		if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+			channel = 'B';
+			our_id = ahc->our_id_b;
+			target_id = i % 8;
+		}
+		tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+					    target_id, &tstate);
+		tinfo->goal = tinfo->user;
+		ahc_compile_devinfo(&devinfo, our_id, target_id,
+				   CAM_LUN_WILDCARD, channel, ROLE_INITIATOR);
+		ahc_update_target_msg_request(ahc, &devinfo, tinfo,
+					     /*force*/FALSE, /*paused*/FALSE);
+	}
+	/* Give the bus some time to recover */
+	if ((ahc->flags & (AHC_RESET_BUS_A|AHC_RESET_BUS_B)) != 0) {
+		ahc->platform_data->qfrozen++;
+		init_timer(&ahc->platform_data->reset_timer);
+		ahc->platform_data->reset_timer.data = (u_long)ahc;
+		ahc->platform_data->reset_timer.expires =
+		    jiffies + (AIC7XXX_RESET_DELAY * HZ)/1000;
+		ahc->platform_data->reset_timer.function =
+		    ahc_release_sim_queue;
+		add_timer(&ahc->platform_data->reset_timer);
+	}
+}
+
+int
+ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg)
+{
+	ahc->platform_data =
+	    malloc(sizeof(struct ahc_platform_data), M_DEVBUF, M_NOWAIT);
+	if (ahc->platform_data == NULL)
+		return (ENOMEM);
+	memset(ahc->platform_data, 0, sizeof(struct ahc_platform_data));
+	TAILQ_INIT(&ahc->platform_data->completeq);
+	LIST_INIT(&ahc->platform_data->device_runq);
+	ahc_lockinit(ahc);
+	ahc_done_lockinit(ahc);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+	init_MUTEX_LOCKED(&ahc->platform_data->eh_sem);
+#else
+	ahc->platform_data->eh_sem = MUTEX_LOCKED;
+#endif
+	ahc->seltime = (aic7xxx_seltime & 0x3) << 4;
+	ahc->seltime_b = (aic7xxx_seltime & 0x3) << 4;
+	return (0);
+}
+
+void
+ahc_platform_free(struct ahc_softc *ahc)
+{
+	if (ahc->platform_data != NULL) {
+		if (ahc->platform_data->host != NULL)
+			scsi_unregister(ahc->platform_data->host);
+		if (ahc->platform_data->irq)
+			free_irq(ahc->platform_data->irq, ahc);
+		if (ahc->tag == BUS_SPACE_PIO
+		 && ahc->bsh.ioport != 0)
+			release_region(ahc->bsh.ioport, 256);
+		if (ahc->tag == BUS_SPACE_MEMIO
+		 && ahc->bsh.maddr != NULL) {
+			u_long base_addr;
+
+			base_addr = (u_long)ahc->bsh.maddr;
+			base_addr &= PAGE_MASK;
+			iounmap((void *)base_addr);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+			release_mem_region(ahc->platform_data->mem_busaddr,
+					   0x1000);
+#endif
+		}
+		free(ahc->platform_data, M_DEVBUF);
+	}
+}
+
+void
+ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+	ahc_platform_abort_scbs(ahc, SCB_GET_TARGET(ahc, scb),
+				SCB_GET_CHANNEL(ahc, scb),
+				SCB_GET_LUN(scb), SCB_LIST_NULL,
+				ROLE_UNKNOWN, CAM_REQUEUE_REQ);
+}
+
+void
+ahc_platform_set_tags(struct ahc_softc *ahc,
+		      struct ahc_devinfo *devinfo, int enable)
+{
+	struct ahc_linux_device *dev;
+
+	dev = ahc_get_device(ahc, devinfo->channel - 'A',
+			     devinfo->target,
+			     devinfo->lun, /*alloc*/FALSE);
+	if ((dev->flags & AHC_DEV_FREEZE_TIL_EMPTY) == 0
+	 && (dev->active != 0)) {
+		dev->flags |= AHC_DEV_FREEZE_TIL_EMPTY;
+		dev->qfrozen++;
+	}
+
+	if (enable) {
+		/*
+		 * Start out agressively and allow our
+		 * dynamic queue depth algorithm take
+		 * care of the rest.
+		 */
+		dev->maxtags = AHC_MAX_QUEUE;
+		dev->openings = dev->maxtags - dev->active;
+	} else {
+		/* We can only have one opening */
+		dev->maxtags = 0;
+		dev->openings =  1 - dev->active;
+	}
+}
+
+int
+ahc_platform_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+			int lun, u_int tag, role_t role, uint32_t status)
+{
+	int chan;
+	int maxchan;
+	int targ;
+	int maxtarg;
+	int clun;
+	int maxlun;
+	int count;
+
+	if (tag != SCB_LIST_NULL)
+		return (0);
+
+	chan = 0;
+	if (channel != ALL_CHANNELS) {
+		chan = channel - 'A';
+		maxchan = chan + 1;
+	} else {
+		maxchan = (ahc->features & AHC_TWIN) ? 2 : 1;
+	}
+	targ = 0;
+	if (target != CAM_TARGET_WILDCARD) {
+		targ = target;
+		maxtarg = targ + 1;
+	} else {
+		maxtarg = (ahc->features & AHC_WIDE) ? 16 : 8;
+	}
+	clun = 0;
+	if (lun != CAM_LUN_WILDCARD) {
+		clun = lun;
+		maxlun = clun + 1;
+	} else {
+		maxlun = 16;
+	}
+
+	count = 0;
+	for (; chan < maxchan; chan++) {
+		for (; targ < maxtarg; targ++) {
+			for (; clun < maxlun; clun++) {
+				struct ahc_linux_device *dev;
+				struct ahc_busyq *busyq;
+				struct ahc_cmd *acmd;
+
+				dev = ahc_get_device(ahc, chan, targ,
+						     clun, /*alloc*/FALSE);
+
+				if (dev == NULL)
+					continue;
+
+				busyq = &dev->busyq;
+				while ((acmd = TAILQ_FIRST(busyq)) != NULL) {
+					Scsi_Cmnd *cmd;
+
+					cmd = &acmd_scsi_cmd(acmd);
+					TAILQ_REMOVE(busyq, acmd,
+						     acmd_links.tqe);
+					count++;
+					cmd->result = status << 16;
+					ahc_queue_cmd_complete(ahc, cmd);
+				}
+			}
+		}
+	}
+
+	return (count);
+}
+
+/*
+ * Sets the queue depth for each SCSI device hanging
+ * off the input host adapter.
+ */
+static void
+aic7xxx_select_queue_depth(struct Scsi_Host * host,
+			   Scsi_Device * scsi_devs)
+{
+	Scsi_Device *device;
+	struct	ahc_softc *ahc;
+	u_long	flags;
+	int	scbnum;
+
+	ahc = *((struct ahc_softc **)host->hostdata);
+	ahc_lock(ahc, &flags);
+	scbnum = 0;
+	for (device = scsi_devs; device != NULL; device = device->next) {
+		if (device->host == host) {
+			aic7xxx_device_queue_depth(ahc, device);
+			scbnum += device->queue_depth;
+		}
+	}
+	ahc_unlock(ahc, &flags);
+}
+
+/*
+ * Determines the queue depth for a given device.
+ */
+static void
+aic7xxx_device_queue_depth(struct ahc_softc *ahc, Scsi_Device * device)
+{
+	struct	ahc_devinfo devinfo;
+	struct	ahc_initiator_tinfo *targ_info;
+	struct	tmode_tstate *tstate;
+	uint8_t tags;
+
+	ahc_compile_devinfo(&devinfo,
+			    device->channel == 0 ? ahc->our_id : ahc->our_id_b,
+			    device->id, device->lun,
+			    device->channel == 0 ? 'A' : 'B',
+			    ROLE_INITIATOR);
+	targ_info = ahc_fetch_transinfo(ahc, devinfo.channel,
+					devinfo.our_scsiid,
+					devinfo.target, &tstate);
+
+	tags = 0;
+	if (device->tagged_supported != 0
+	 && (tstate->discenable & devinfo.target_mask) != 0) {
+		if (ahc->unit >= NUM_ELEMENTS(aic7xxx_tag_info)) {
+
+			printf("aic7xxx: WARNING, insufficient "
+			       "tag_info instances for installed "
+			       "controllers. Using defaults\n");
+			printf("aic7xxx: Please update the "
+			       "aic7xxx_tag_info array in the "
+			       "aic7xxx.c source file.\n");
+			tags = AHC_MAX_QUEUE;
+		} else {
+			adapter_tag_info_t *tag_info;
+
+			tag_info = &aic7xxx_tag_info[ahc->unit];
+			tags = tag_info->tag_commands[devinfo.target_offset];
+			if (tags > AHC_MAX_QUEUE)
+				tags = AHC_MAX_QUEUE;
+		}
+	}
+	if (tags != 0) {
+		device->queue_depth = tags;
+		ahc_set_tags(ahc, &devinfo, TRUE);
+		printf("scsi%d:%d:%d:%d: Tagged Queuing enabled.  Depth %d\n",
+	       	       ahc->platform_data->host->host_no, device->channel,
+		       device->id, device->lun, tags);
+	} else {
+		/*
+		 * We allow the OS to queue 2 untagged transactions to
+		 * us at any time even though we can only execute them
+		 * serially on the controller/device.  This should remove
+		 * some latency.
+		 */
+		device->queue_depth = 2;
+	}
+}
+
+/*
+ * Queue an SCB to the controller.
+ */
+int
+aic7xxx_queue(Scsi_Cmnd * cmd, void (*scsi_done) (Scsi_Cmnd *))
+{
+	struct	 ahc_softc *ahc;
+	struct	 ahc_linux_device *dev;
+	u_long	 flags;
+
+	ahc = *(struct ahc_softc **)cmd->host->hostdata;
+
+	/*
+	 * Save the callback on completion function.
+	 */
+	cmd->scsi_done = scsi_done;
+
+	ahc_lock(ahc, &flags);
+	dev = ahc_get_device(ahc, cmd->channel, cmd->target,
+			     cmd->lun, /*alloc*/TRUE);
+	if (dev == NULL) {
+		ahc_unlock(ahc, &flags);
+		printf("aic7xxx_queue: Unable to allocate device!\n");
+		return (-ENOMEM);
+	}
+	cmd->result = CAM_REQ_INPROG << 16;
+	TAILQ_INSERT_TAIL(&dev->busyq, (struct ahc_cmd *)cmd, acmd_links.tqe);
+	ahc_run_device_queue(ahc, dev);
+	ahc_unlock(ahc, &flags);
+	return (0);
+}
+
+static void
+ahc_run_device_queue(struct ahc_softc *ahc, struct ahc_linux_device *dev)
+{
+	struct	 ahc_cmd *acmd;
+	struct	 scsi_cmnd *cmd;
+	struct	 scb *scb;
+	struct	 hardware_scb *hscb;
+	struct	 ahc_initiator_tinfo *tinfo;
+	struct	 tmode_tstate *tstate;
+	uint16_t mask;
+
+	while ((acmd = TAILQ_FIRST(&dev->busyq)) != NULL
+	    && dev->openings > 0 && dev->qfrozen == 0) {
+
+		/*
+		 * Schedule us to run later.  The only reason we are not
+		 * running is because the whole controller Q is frozen.
+		 */
+		if (ahc->platform_data->qfrozen != 0) {
+			if ((dev->flags & AHC_DEV_ON_RUN_LIST) != 0)
+				return;
+
+			LIST_INSERT_HEAD(&ahc->platform_data->device_runq,
+					 dev, links);
+			dev->flags |= AHC_DEV_ON_RUN_LIST;
+			return;
+		}
+		/*
+		 * Get an scb to use.
+		 */
+		if ((scb = ahc_get_scb(ahc)) == NULL) {
+			if ((dev->flags & AHC_DEV_ON_RUN_LIST) != 0)
+				panic("running device on run list");
+			LIST_INSERT_HEAD(&ahc->platform_data->device_runq,
+					 dev, links);
+			dev->flags |= AHC_DEV_ON_RUN_LIST;
+			ahc->flags |= AHC_RESOURCE_SHORTAGE;
+			printf("%s: Temporary Resource Shortage\n",
+			       ahc_name(ahc));
+			return;
+		}
+		TAILQ_REMOVE(&dev->busyq, acmd, acmd_links.tqe);
+		cmd = &acmd_scsi_cmd(acmd);
+		scb->io_ctx = cmd;
+		scb->platform_data->dev = dev;
+		hscb = scb->hscb;
+		cmd->host_scribble = (char *)scb;
+
+		/*
+		 * Fill out basics of the HSCB.
+		 */
+		hscb->control = 0;
+		hscb->scsiid = BUILD_SCSIID(ahc, cmd);
+		hscb->lun = cmd->lun;
+		mask = SCB_GET_TARGET_MASK(ahc, scb);
+		tinfo = ahc_fetch_transinfo(ahc, SCB_GET_CHANNEL(ahc, scb),
+					    SCB_GET_OUR_ID(scb),
+					    SCB_GET_TARGET(ahc, scb), &tstate);
+		hscb->scsirate = tinfo->scsirate;
+		hscb->scsioffset = tinfo->current.offset;
+		if ((tstate->ultraenb & mask) != 0)
+			hscb->control |= ULTRAENB;
+
+		if ((tstate->discenable & mask) != 0)
+			hscb->control |= DISCENB;
+
+		if ((tstate->tagenable & mask) != 0) {
+			/* XXX What about devices that dislike ordered tags? */
+			if ((dev->num_commands % 256) == 0)
+				hscb->control |= MSG_ORDERED_Q_TAG;
+			else
+				hscb->control |= MSG_SIMPLE_Q_TAG;
+		}
+
+		hscb->cdb_len = cmd->cmd_len;
+		if (hscb->cdb_len <= 12) {
+			memcpy(hscb->shared_data.cdb, cmd->cmnd, hscb->cdb_len);
+		} else {
+			memcpy(hscb->cdb32, cmd->cmnd, hscb->cdb_len);
+			scb->flags |= SCB_CDB32_PTR;
+		}
+
+		scb->platform_data->xfer_len = 0;
+		if (cmd->use_sg != 0) {
+			struct	ahc_dma_seg *sg;
+			struct	scatterlist *cur_seg;
+			struct	scatterlist *end_seg;
+			int	nseg;
+
+			cur_seg = (struct scatterlist *)cmd->request_buffer;
+			nseg = pci_map_sg(ahc->dev_softc, cur_seg, cmd->use_sg,
+				 scsi_to_pci_dma_dir(cmd ->sc_data_direction));
+			end_seg = cur_seg + nseg;
+			/* Copy the segments into the SG list. */
+			sg = scb->sg_list;
+			while(cur_seg < end_seg) {
+				sg->addr = ahc_htole32(sg_dma_address(cur_seg));
+/* XXX Add in the 5th byte of the address later.*/
+				sg->len = ahc_htole32(sg_dma_len(cur_seg));
+				scb->platform_data->xfer_len +=
+				    sg_dma_len(cur_seg);
+				sg++;
+				cur_seg++;
+			}
+			sg--;
+			sg->len |= ahc_htole32(AHC_DMA_LAST_SEG);
+
+			/*
+			 * Reset the sg list pointer.
+			 */
+			scb->hscb->sgptr =
+			    ahc_htole32(scb->sg_list_phys | SG_FULL_RESID);
+
+			/*
+			 * Copy the first SG into the "current"
+			 * data pointer area.
+			 */
+			scb->hscb->dataptr = scb->sg_list->addr;
+			scb->hscb->datacnt = scb->sg_list->len;
+
+			/*
+			 * Remember the number of segments for later
+			 * residual calculations.
+			 */
+			scb->sg_count = nseg;
+		} else if (cmd->request_bufflen != 0) {
+			struct	 ahc_dma_seg *sg;
+			uint32_t baddr;
+
+			sg = scb->sg_list;
+			baddr = pci_map_single(ahc->dev_softc,
+			       cmd->request_buffer,
+			       cmd->request_bufflen,
+			       scsi_to_pci_dma_dir(cmd->sc_data_direction));
+			sg->addr = ahc_htole32(baddr);
+			sg->len = ahc_htole32(cmd->request_bufflen
+					      | AHC_DMA_LAST_SEG);
+
+			/*
+			 * Reset the sg list pointer.
+			 */
+			scb->hscb->sgptr =
+			    ahc_htole32(scb->sg_list_phys | SG_FULL_RESID);
+
+			/*
+			 * Copy the first SG into the "current"
+			 * data pointer area.
+			 */
+			scb->hscb->dataptr = sg->addr;
+			scb->hscb->datacnt = sg->len;
+			scb->platform_data->xfer_len = cmd->request_bufflen;
+
+			/*
+			 * Remember the number of segments for later
+			 * residual calculations.
+			 */
+			scb->sg_count = 1;
+		} else {
+			scb->hscb->sgptr = ahc_htole32(SG_LIST_NULL);
+			scb->hscb->dataptr = 0;
+			scb->hscb->datacnt = 0;
+			scb->sg_count = 0;
+		}
+
+		LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pending_links);
+		dev->openings--;
+		dev->active++;
+		dev->num_commands++;
+
+		/*
+		 * We only allow one untagged transaction
+		 * per target in the initiator role unless
+		 * we are storing a full busy target *lun*
+		 * table in SCB space.
+		 */
+		if ((scb->hscb->control & (TARGET_SCB|TAG_ENB)) == 0
+		 && (ahc->features & AHC_SCB_BTT) == 0) {
+			struct scb_tailq *untagged_q;
+			int target_offset;
+
+			target_offset = SCB_GET_TARGET_OFFSET(ahc, scb);
+			untagged_q = &(ahc->untagged_queues[target_offset]);
+			TAILQ_INSERT_TAIL(untagged_q, scb, links.tqe);
+			scb->flags |= SCB_UNTAGGEDQ;
+			if (TAILQ_FIRST(untagged_q) != scb)
+				continue;
+		}
+		scb->flags |= SCB_ACTIVE;
+		ahc_queue_scb(ahc, scb);
+	}
+}
+
+/*
+ * SCSI controller interrupt handler.
+ */
+void
+aic7xxx_isr(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct ahc_softc *ahc;
+	struct ahc_cmd *acmd;
+	u_long flags;
+
+	ahc = (struct ahc_softc *) dev_id;
+	ahc_lock(ahc, &flags); 
+	ahc_intr(ahc);
+	/*
+	 * It would be nice to run the device queues from a
+	 * bottom half handler, but as there is no way to
+	 * dynamically register one, we'll have to postpone
+	 * that until we get integrated into the kernel.
+	 */
+	ahc_run_device_queues(ahc);
+	acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+	TAILQ_INIT(&ahc->platform_data->completeq);
+	ahc_unlock(ahc, &flags);
+	if (acmd != NULL)
+		ahc_run_complete_queue(ahc, acmd);
+}
+
+void
+ahc_platform_flushwork(struct ahc_softc *ahc)
+{
+	struct ahc_cmd *acmd;
+
+	acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+	TAILQ_INIT(&ahc->platform_data->completeq);
+	if (acmd != NULL)
+		ahc_run_complete_queue(ahc, acmd);
+}
+
+static struct ahc_linux_target*
+ahc_alloc_target(struct ahc_softc *ahc, u_int channel, u_int target)
+{
+	struct ahc_linux_target *targ;
+	u_int target_offset;
+
+	targ = malloc(sizeof(*targ), M_DEVBUG, M_NOWAIT);
+	if (targ == NULL)
+		return (NULL);
+	memset(targ, 0, sizeof(*targ));
+	targ->channel = channel;
+	targ->target = target;
+	target_offset = target;
+	if (channel != 0)
+		target_offset += 8;
+	ahc->platform_data->targets[target_offset] = targ;
+	return (targ);
+}
+
+static void
+ahc_free_target(struct ahc_softc *ahc, struct ahc_linux_target *targ)
+{
+	u_int target_offset;
+
+	target_offset = targ->target;
+	if (targ->channel != 0)
+		target_offset += 8;
+	ahc->platform_data->targets[target_offset] = NULL;
+	free(targ, M_DEVBUF);
+}
+
+static struct ahc_linux_device*
+ahc_alloc_device(struct ahc_softc *ahc,
+		 struct ahc_linux_target *targ, u_int lun)
+{
+	struct ahc_linux_device *dev;
+
+	dev = malloc(sizeof(*dev), M_DEVBUG, M_NOWAIT);
+	if (dev == NULL)
+		return (NULL);
+	memset(dev, 0, sizeof(*dev));
+	TAILQ_INIT(&dev->busyq);
+	dev->flags = AHC_DEV_UNCONFIGURED;
+	dev->lun = lun;
+	dev->target = targ;
+
+	/*
+	 * We start out life using untagged
+	 * transactions of which we allow one.
+	 */
+	dev->openings = 1;
+
+	/*
+	 * Set maxtags to 0.  This will be changed if we
+	 * later determine that we are dealing with
+	 * a tagged queuing capable device.
+	 */
+	dev->maxtags = 0;
+	
+	targ->refcount++;
+	targ->devices[lun] = dev;
+	return (dev);
+}
+
+static void
+ahc_free_device(struct ahc_softc *ahc, struct ahc_linux_device *dev)
+{
+	struct ahc_linux_target *targ;
+
+	targ = dev->target;
+	targ->devices[dev->lun] = NULL;
+	free(dev, M_DEVBUF);
+	targ->refcount--;
+	if (targ->refcount == 0)
+		ahc_free_target(ahc, targ);
+}
+
+/*
+ * Return a string describing the driver.
+ */
+const char *
+aic7xxx_info(struct Scsi_Host *host)
+{
+	static char buffer[512];
+	char	ahc_info[256];
+	char   *bp;
+	struct ahc_softc *ahc;
+
+	bp = &buffer[0];
+	ahc = *(struct ahc_softc **)host->hostdata;
+	memset(bp, 0, sizeof(buffer));
+	strcpy(bp, "Adaptec AIC7XXX EISA/VLB/PCI SCSI HBA DRIVER, Rev ");
+	strcat(bp, AIC7XXX_DRIVER_VERSION);
+	strcat(bp, "\n");
+	strcat(bp, "        <");
+	strcat(bp, ahc->description);
+	strcat(bp, ">\n");
+	strcat(bp, "        ");
+	ahc_controller_info(ahc, ahc_info);
+	strcat(bp, ahc_info);
+	strcat(bp, "\n");
+
+	return (bp);
+}
+
+void
+ahc_send_async(struct ahc_softc *ahc, char channel,
+	       u_int target, u_int lun, ac_code code)
+{
+	switch (code) {
+	case AC_TRANSFER_NEG:
+	{
+		char	buf[80];
+		struct	ahc_linux_target *targ;
+		struct	info_str info;
+		struct	ahc_initiator_tinfo *tinfo;
+		struct	tmode_tstate *tstate;
+		int	target_offset;
+
+		info.buffer = buf;
+		info.length = sizeof(buf);
+		info.offset = 0;
+		info.pos = 0;
+		tinfo = ahc_fetch_transinfo(ahc, channel,
+						channel == 'A' ? ahc->our_id
+							       : ahc->our_id_b,
+						target, &tstate);
+
+		/*
+		 * Don't bother reporting results while
+		 * negotiations are still pending.
+		 */
+		if (tinfo->current.period != tinfo->goal.period
+		 || tinfo->current.width != tinfo->goal.width
+		 || tinfo->current.offset != tinfo->goal.offset
+		 || tinfo->current.ppr_options != tinfo->goal.ppr_options)
+			if (bootverbose == 0)
+				break;
+
+		/*
+		 * Don't bother reporting results that
+		 * are identical to those last reported.
+		 */
+		target_offset = target;
+		if (channel == 'B')
+			target_offset += 8;
+		targ = ahc->platform_data->targets[target_offset];
+		if (targ != NULL
+		 && tinfo->current.period == targ->last_tinfo.period
+		 && tinfo->current.width == targ->last_tinfo.width
+		 && tinfo->current.offset == targ->last_tinfo.offset
+		 && tinfo->current.ppr_options == targ->last_tinfo.ppr_options)
+			if (bootverbose == 0)
+				break;
+
+		targ->last_tinfo.period = tinfo->current.period;
+		targ->last_tinfo.width = tinfo->current.width;
+		targ->last_tinfo.offset = tinfo->current.offset;
+		targ->last_tinfo.ppr_options = tinfo->current.ppr_options;
+
+		printf("(%s:%c:", ahc_name(ahc), channel);
+		if (target == CAM_TARGET_WILDCARD)
+			printf("*): ");
+		else
+			printf("%d): ", target);
+		ahc_format_transinfo(&info, &tinfo->current);
+		if (info.pos < info.length)
+			*info.buffer = '\0';
+		else
+			buf[info.length - 1] = '\0';
+		printf("%s", buf);
+		break;
+	}
+        case AC_SENT_BDR:
+		break;
+        case AC_BUS_RESET:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+		scsi_report_bus_reset(ahc->platform_data->host, channel - 'A');
+#endif
+                break;
+        default:
+                panic("ahc_send_async: Unexpected async event");
+        }
+}
+
+/*
+ * Calls the higher level scsi done function and frees the scb.
+ */
+void
+ahc_done(struct ahc_softc *ahc, struct scb * scb)
+{
+	Scsi_Cmnd *cmd;
+	struct ahc_linux_device *dev;
+
+	LIST_REMOVE(scb, pending_links);
+	if ((scb->flags & SCB_UNTAGGEDQ) != 0) {
+		struct scb_tailq *untagged_q;
+		int target_offset;
+
+		target_offset = SCB_GET_TARGET_OFFSET(ahc, scb);
+		untagged_q = &(ahc->untagged_queues[target_offset]);
+		TAILQ_REMOVE(untagged_q, scb, links.tqe);
+		ahc_run_untagged_queue(ahc, untagged_q);
+	}
+
+	if ((scb->flags & SCB_ACTIVE) == 0) {
+		printf("SCB %d done'd twice\n", scb->hscb->tag);
+		ahc_dump_card_state(ahc);
+		panic("Stopping for safety");
+	}
+	cmd = scb->io_ctx;
+	dev = scb->platform_data->dev;
+	dev->active--;
+	dev->openings++;
+	ahc_unmap_scb(ahc, scb);
+	if (scb->flags & SCB_SENSE) {
+		memcpy(cmd->sense_buffer, ahc_get_sense_buf(ahc, scb),
+		       MIN(sizeof(struct scsi_sense_data),
+			   sizeof(cmd->sense_buffer)));
+	}
+	if (ahc_get_transaction_status(scb) == CAM_REQ_INPROG) {
+		uint32_t amount_xferred;
+
+		amount_xferred =
+		    ahc_get_transfer_length(scb) - ahc_get_residual(scb);
+		if (amount_xferred < scb->io_ctx->underflow) {
+			printf("Saw underflow (%ld of %ld bytes). "
+			       "Treated as error\n",
+				ahc_get_residual(scb),
+				ahc_get_transfer_length(scb));
+			ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+		} else {
+			ahc_set_transaction_status(scb, CAM_REQ_CMP);
+			ahc_sniff_command(ahc, cmd);
+		}
+	} else if (ahc_get_transaction_status(scb) == DID_OK) {
+		ahc_handle_scsi_status(ahc, dev, scb);
+	}
+
+	if (dev->openings == 1
+	 && ahc_get_transaction_status(scb) == CAM_REQ_CMP
+	 && ahc_get_scsi_status(scb) != SCSI_STATUS_QUEUE_FULL)
+		dev->tag_success_count++;
+	/*
+	 * Some devices deal with temporary internal resource
+	 * shortages by returning queue full.  When the queue
+	 * full occurrs, we throttle back.  Slowly try to get
+	 * back to our previous queue depth.
+	 */
+	if ((dev->openings + dev->active) < dev->maxtags
+	 && dev->tag_success_count > AHC_TAG_SUCCESS_INTERVAL) {
+		dev->tag_success_count = 0;
+		dev->openings++;
+	}
+
+	if (dev->active == 0
+	 && (dev->flags & AHC_DEV_UNCONFIGURED) != 0)
+		ahc_free_device(ahc, dev);
+	else if ((dev->flags & AHC_DEV_ON_RUN_LIST) == 0) {
+		LIST_INSERT_HEAD(&ahc->platform_data->device_runq, dev, links);
+		dev->flags |= AHC_DEV_ON_RUN_LIST;
+	}
+
+	if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+		printf("Recovery SCB completes\n");
+		up(&ahc->platform_data->eh_sem);
+	}
+
+	ahc_free_scb(ahc, scb);
+	ahc_queue_cmd_complete(ahc, cmd);
+}
+
+static void
+ahc_handle_scsi_status(struct ahc_softc *ahc,
+		       struct ahc_linux_device *dev, struct scb *scb)
+{
+	/*
+	 * We don't currently trust the mid-layer to
+	 * properly deal with queue full or busy.  So,
+	 * when one occurs, we tell the mid-layer to
+	 * unconditionally requeue the command to us
+	 * so that we can retry it ourselves.  We also
+	 * implement our own throttling mechanism so
+	 * we don't clobber the device with too many
+	 * commands.
+	 */
+	switch (ahc_get_scsi_status(scb)) {
+	default:
+		break;
+	case SCSI_STATUS_QUEUE_FULL:
+	{
+		/*
+		 * By the time the core driver has returned this
+		 * command, all other commands that were queued
+		 * to us but not the device have been returned.
+		 * This ensures that dev->active is equal to
+		 * the number of commands actually queued to
+		 * the device.
+		 */
+		dev->tag_success_count = 0;
+		if (dev->active != 0) {
+			/*
+			 * Drop our opening count to the number
+			 * of commands currently outstanding.
+			 */
+			dev->openings = 0;
+/*
+			ahc_print_path(ahc, scb);
+			printf("Dropping tag count to %d\n", dev->active);
+ */
+			if (dev->active == dev->tags_on_last_queuefull) {
+
+				dev->last_queuefull_same_count++;
+				/*
+				 * If we repeatedly see a queue full
+				 * at the same queue depth, this
+				 * device has a fixed number of tag
+				 * slots.  Lock in this tag depth
+				 * so we stop seeing queue fulls from
+				 * this device.
+				 */
+				if (dev->last_queuefull_same_count
+				 == AHC_LOCK_TAGS_COUNT) {
+					dev->maxtags = dev->active;
+					ahc_print_path(ahc, scb);
+					printf("Locking max tag count at %d\n",
+					       dev->active);
+				}
+			} else {
+				dev->tags_on_last_queuefull = dev->active;
+				dev->last_queuefull_same_count = 0;
+			}
+			ahc_set_transaction_status(scb, CAM_REQUEUE_REQ);
+			ahc_set_scsi_status(scb, SCSI_STATUS_OK);
+			break;
+		}
+		/*
+		 * Drop down to a single opening, and treat this
+		 * as if the target return BUSY SCSI status.
+		 */
+		dev->openings = 1;
+		/* FALLTHROUGH */
+	}
+	case SCSI_STATUS_BUSY:
+		/*
+		 * XXX Set a timer and handle ourselves????
+		 * For now we pray that the mid-layer does something
+		 * sane for devices that are busy.
+		 */
+		ahc_set_scsi_status(scb, SCSI_STATUS_BUSY);
+		break;
+	}
+}
+
+static void
+ahc_filter_command(struct ahc_softc *ahc, Scsi_Cmnd *cmd)
+{
+	switch (cmd->cmnd[0]) {
+	case INQUIRY:
+	{
+		struct	ahc_devinfo devinfo;
+		struct	scsi_inquiry_data *sid;
+		struct	ahc_initiator_tinfo *targ_info;
+		struct	tmode_tstate *tstate;
+		struct	ahc_syncrate *syncrate;
+		u_int	scsiid;
+		u_int	maxsync;
+		int	minlen;
+		u_int	width;
+		u_int	period;
+		u_int	offset;
+		u_int	ppr_options;
+
+		if (cmd->use_sg != 0) {
+			printf("%s: SG Inquiry response ignored\n",
+			       ahc_name(ahc));
+			break;
+		}
+		sid = (struct scsi_inquiry_data *)cmd->request_buffer;
+
+		/*
+		 * Determine if this lun actually exists.  If so,
+		 * hold on to its corresponding device structure.
+		 */
+		if (cmd->request_bufflen >= 1
+		 && SID_QUAL(sid) == SID_QUAL_LU_CONNECTED) {
+			struct ahc_linux_device *dev;
+
+			dev = ahc_get_device(ahc, cmd->channel,
+					     cmd->target, cmd->lun,
+					     /*alloc*/FALSE);
+			dev->flags &= ~AHC_DEV_UNCONFIGURED;
+		}
+
+		/*
+		 * Update our notion of this device's transfer
+		 * negotiation capabilities.
+		 */
+		scsiid = BUILD_SCSIID(ahc, cmd);
+		ahc_compile_devinfo(&devinfo, SCSIID_OUR_ID(scsiid),
+				    cmd->target, cmd->lun,
+				    SCSIID_CHANNEL(ahc, scsiid),
+				    ROLE_INITIATOR);
+		targ_info = ahc_fetch_transinfo(ahc, devinfo.channel,
+						devinfo.our_scsiid,
+						devinfo.target, &tstate);
+		/* Structure copy */
+		width = targ_info->user.width;
+		period = targ_info->user.period;
+		offset = targ_info->user.offset;
+		ppr_options = targ_info->user.ppr_options;
+		minlen = offsetof(struct scsi_inquiry_data, version) + 1;
+		if (cmd->request_bufflen >= minlen) {
+			targ_info->current.protocol_version = SID_ANSI_REV(sid);
+
+			/*
+			 * Only attempt SPI3 once we've verified that
+			 * the device claims to support SPI3 features.
+			 */
+			if (targ_info->current.protocol_version < SCSI_REV_2)
+				targ_info->current.transport_version =
+				    SID_ANSI_REV(sid);
+			else
+				targ_info->current.transport_version =
+				     SCSI_REV_2;
+		}
+
+		minlen = offsetof(struct scsi_inquiry_data, flags) + 1;
+		if (cmd->request_bufflen >= minlen
+		 && (sid->additional_length + 4) >= minlen) {
+			if ((sid->flags & SID_WBus16) == 0)
+				width = MSG_EXT_WDTR_BUS_8_BIT;
+			if ((sid->flags & SID_Sync) == 0) {
+				period = 0;
+				offset = 0;
+				ppr_options = 0;
+			}
+		} else {
+			/* Keep current settings */
+			break;
+		}
+		minlen = offsetof(struct scsi_inquiry_data, spi3data) + 1;
+		if (cmd->request_bufflen >= minlen
+		 && (sid->additional_length + 4) >= minlen) {
+			if ((sid->spi3data & SID_SPI_CLOCK_DT) == 0)
+				ppr_options = 0;
+
+			if ((sid->spi3data & SID_SPI_MASK) != 0
+			 && targ_info->current.protocol_version > SCSI_REV_2)
+				targ_info->current.transport_version = 3;
+		} else {
+			ppr_options = 0;
+		}
+
+		ahc_validate_width(ahc, /*tinfo limit*/NULL, &width,
+				   ROLE_UNKNOWN);
+		if ((ahc->features & AHC_ULTRA2) != 0)
+			maxsync = AHC_SYNCRATE_DT;
+		else if ((ahc->features & AHC_ULTRA) != 0)
+			maxsync = AHC_SYNCRATE_ULTRA;
+		else
+			maxsync = AHC_SYNCRATE_FAST;
+
+		syncrate = ahc_find_syncrate(ahc, &period,
+					     &ppr_options, maxsync);
+		ahc_validate_offset(ahc, /*tinfo limit*/NULL, syncrate,
+				    &offset, width, ROLE_UNKNOWN);
+		/* Apply our filtered user settings. */
+		ahc_set_width(ahc, &devinfo, width,
+			      AHC_TRANS_GOAL, /*paused*/FALSE);
+		ahc_set_syncrate(ahc, &devinfo, syncrate, period,
+				 offset, ppr_options, AHC_TRANS_GOAL,
+				 /*paused*/FALSE);
+		break;
+	}
+	default:
+		panic("ahc_filter_command: Unexpected Command type  %x\n",
+		      cmd->cmnd[0]);
+		break;
+	}
+}
+
+static void
+ahc_sem_timeout(u_long arg)
+{
+	struct semaphore *sem;
+
+	sem = (struct semaphore *)arg;
+	up(sem);
+}
+
+static void
+ahc_release_sim_queue(u_long arg)
+{
+	struct ahc_softc *ahc;
+	u_long s;
+
+	ahc = (struct ahc_softc *)arg;
+	ahc_lock(ahc, &s);
+	if (ahc->platform_data->qfrozen > 0)
+		ahc->platform_data->qfrozen--;
+	if (ahc->platform_data->qfrozen == 0)
+		ahc_run_device_queues(ahc);
+	ahc_unlock(ahc, &s);
+}
+
+static int
+aic7xxx_queue_recovery_cmd(Scsi_Cmnd *cmd, scb_flag flag)
+{
+	struct ahc_softc *ahc;
+	struct ahc_cmd *acmd;
+	struct ahc_cmd *list_acmd;
+	struct ahc_linux_device *dev;
+	struct scb *pending_scb;
+	u_long s;
+	u_int  saved_scbptr;
+	u_int  active_scb_index;
+	u_int  last_phase;
+	int    retval;
+	int    paused;
+	int    wait;
+	int    disconnected;
+
+	paused = FALSE;
+	wait = FALSE;
+	ahc = *(struct ahc_softc **)cmd->host->hostdata;
+	acmd = (struct ahc_cmd *)cmd;
+
+	printf("%s:%d:%d:%d: Attempting to queue a%s message\n",
+	       ahc_name(ahc), cmd->channel, cmd->target, cmd->lun,
+	       flag == SCB_ABORT ? "n ABORT" : " TARGET RESET");
+
+	/*
+	 * It is a bug that the upper layer takes
+	 * this lock just prior to calling us.
+	 */
+	spin_unlock_irq(&io_request_lock);
+
+	ahc_lock(ahc, &s);
+
+	/*
+	 * First determine if we currently own this command.
+	 * Start by searching the device queue.  If not found
+	 * there, check the pending_scb list.  If not found
+	 * at all, and the system wanted us to just abort the
+	 * command return success.
+	 */
+	dev = ahc_get_device(ahc, cmd->channel, cmd->target,
+			     cmd->lun, /*alloc*/FALSE);
+
+	if (dev == NULL) {
+		/*
+		 * No target device for this command exists,
+		 * so we must not still own the command.
+		 */
+		printf("%s:%d:%d:%d: Is not an active device\n",
+		       ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+		retval = SUCCESS;
+		goto no_cmd;
+	}
+
+	TAILQ_FOREACH(list_acmd, &dev->busyq, acmd_links.tqe) {
+		if (list_acmd == acmd)
+			break;
+	}
+
+	if (list_acmd != NULL) {
+		printf("%s:%d:%d:%d: Command found on device queue\n",
+		       ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+		if (flag == SCB_ABORT) {
+			TAILQ_REMOVE(&dev->busyq, list_acmd, acmd_links.tqe);
+			cmd->result = DID_ABORT << 16;
+			ahc_queue_cmd_complete(ahc, cmd);
+			retval = SUCCESS;
+			goto done;
+		}
+	}
+
+	/*
+	 * See if we can find a matching cmd in the pending list.
+	 */
+	LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+		if (pending_scb->io_ctx == cmd)
+			break;
+	}
+
+	if (pending_scb == NULL && flag == SCB_DEVICE_RESET) {
+
+		/* Any SCB for this device will do for a target reset */
+		LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+		  	if (ahc_match_scb(ahc, pending_scb, cmd->target,
+					  cmd->channel, CAM_LUN_WILDCARD,
+					  SCB_LIST_NULL, ROLE_INITIATOR) == 0)
+				break;
+		}
+	}
+
+	if (pending_scb == NULL) {
+		printf("%s:%d:%d:%d: Command not found\n",
+		       ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+		goto no_cmd;
+	}
+
+	if ((pending_scb->flags & SCB_RECOVERY_SCB) != 0) {
+		/*
+		 * We can't queue two recovery actions using the same SCB
+		 */
+		retval = FAILED;
+		goto  done;
+	}
+
+	/*
+	 * Ensure that the card doesn't do anything
+	 * behind our back.  Also make sure that we
+	 * didn't "just" miss an interrupt that would
+	 * affect this cmd.
+	 */
+	ahc->flags |= AHC_ALL_INTERRUPTS;
+	do {
+		ahc_intr(ahc);
+		pause_sequencer(ahc);
+		ahc_clear_critical_section(ahc);
+	} while (ahc_inb(ahc, INTSTAT) & INT_PEND);
+	ahc->flags &= ~AHC_ALL_INTERRUPTS;
+	paused = TRUE;
+
+	if (bootverbose)
+		ahc_dump_card_state(ahc);
+
+	if ((pending_scb->flags & SCB_ACTIVE) == 0) {
+		printf("%s:%d:%d:%d: Command already completed\n",
+		       ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+		goto no_cmd;
+	}
+
+	disconnected = TRUE;
+	if (flag == SCB_ABORT) {
+		if (ahc_search_qinfifo(ahc, cmd->target, cmd->channel + 'A',
+				       cmd->lun, pending_scb->hscb->tag,
+				       ROLE_INITIATOR, CAM_REQ_ABORTED,
+				       SEARCH_COMPLETE) > 0) {
+			printf("%s:%d:%d:%d: Cmd aborted from QINFIFO\n",
+			       ahc_name(ahc), cmd->channel, cmd->target,
+					cmd->lun);
+			retval = SUCCESS;
+			goto done;
+		}
+	} else if (ahc_search_qinfifo(ahc, cmd->target, cmd->channel + 'A',
+				      cmd->lun, pending_scb->hscb->tag,
+				      ROLE_INITIATOR, /*status*/0,
+				      SEARCH_COUNT) > 0) {
+		disconnected = FALSE;
+	}
+
+	/*
+	 * At this point, pending_scb is the scb associated with the
+	 * passed in command.  That command is currently active on the
+	 * bus, is in the disconnected state, or we're hoping to find
+	 * a command for the same target active on the bus to abuse to
+	 * send a BDR.  Queue the appropriate message based on which of
+	 * these states we are in.
+	 */
+	last_phase = ahc_inb(ahc, LASTPHASE);
+	saved_scbptr = ahc_inb(ahc, SCBPTR);
+	active_scb_index = ahc_inb(ahc, SCB_TAG);
+	if (last_phase != P_BUSFREE
+	 && (pending_scb->hscb->tag == active_scb_index
+	  || (flag == SCB_DEVICE_RESET
+	   && SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)) == cmd->target))) {
+
+		/*
+		 * We're active on the bus, so assert ATN
+		 * and hope that the target responds.
+		 */
+		pending_scb = ahc_lookup_scb(ahc, active_scb_index);
+		pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+		ahc_outb(ahc, MSG_OUT, HOST_MSG);
+		ahc_outb(ahc, SCSISIGO, last_phase|ATNO);
+		printf("%s:%d:%d:%d: Device is active, asserting ATN\n",
+		       ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+		wait = TRUE;
+	} else if (disconnected) {
+
+		/*
+		 * Actually re-queue this SCB in an attempt
+		 * to select the device before it reconnects.
+		 * In either case (selection or reselection),
+		 * we will now issue a the approprate message
+		 * to the timed-out device.
+		 *
+		 * Set the MK_MESSAGE control bit indicating
+		 * that we desire to send a message.  We
+		 * also set the disconnected flag since
+		 * in the paging case there is no guarantee
+		 * that our SCB control byte matches the
+		 * version on the card.  We don't want the
+		 * sequencer to abort the command thinking
+		 * an unsolicited reselection occurred.
+		 */
+		pending_scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
+		pending_scb->flags |= SCB_RECOVERY_SCB|flag;
+
+		/*
+		 * Remove any cached copy of this SCB in the
+		 * disconnected list in preparation for the
+		 * queuing of our abort SCB.  We use the
+		 * same element in the SCB, SCB_NEXT, for
+		 * both the qinfifo and the disconnected list.
+		 */
+		ahc_search_disc_list(ahc, cmd->target, cmd->channel + 'A',
+				     cmd->lun, pending_scb->hscb->tag,
+				     /*stop_on_first*/TRUE,
+				     /*remove*/TRUE,
+				     /*save_state*/FALSE);
+
+		/*
+		 * In the non-paging case, the sequencer will
+		 * never re-reference the in-core SCB.
+		 * To make sure we are notified during
+		 * reslection, set the MK_MESSAGE flag in
+		 * the card's copy of the SCB.
+		 */
+		if ((ahc->flags & AHC_PAGESCBS) == 0) {
+			ahc_outb(ahc, SCBPTR, pending_scb->hscb->tag);
+			ahc_outb(ahc, SCB_CONTROL,
+				 ahc_inb(ahc, SCB_CONTROL)|MK_MESSAGE);
+		}
+
+		/*
+		 * Clear out any entries in the QINFIFO first
+		 * so we are the next SCB for this target
+		 * to run.
+		 */
+		ahc_search_qinfifo(ahc, cmd->target, cmd->channel + 'A',
+				   cmd->lun, SCB_LIST_NULL, ROLE_INITIATOR,
+				   CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+		ahc_print_path(ahc, pending_scb);
+		printf("Queuing a recovery SCB\n");
+		ahc_qinfifo_requeue_tail(ahc, pending_scb);
+		ahc_outb(ahc, SCBPTR, saved_scbptr);
+		printf("%s:%d:%d:%d: Device is disconnected, re-queuing SCB\n",
+		       ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+		wait = TRUE;
+	} else {
+		printf("%s:%d:%d:%d: Unable to deliver message\n",
+		       ahc_name(ahc), cmd->channel, cmd->target, cmd->lun);
+		retval = FAILED;
+		goto done;
+	}
+
+no_cmd:
+	/*
+	 * Our assumption is that if we don't have the command, no
+	 * recovery action was required, so we return success.  Again,
+	 * the semantics of the mid-layer recovery engine are not
+	 * well defined, so this may change in time.
+	 */
+	retval = SUCCESS;
+done:
+	if (paused)
+		unpause_sequencer(ahc);
+	if (wait) {
+    		struct timer_list timer;
+		int ret;
+
+		ahc_unlock(ahc, &s);
+		init_timer(&timer);
+		timer.data = (u_long)&ahc->platform_data->eh_sem;
+		timer.expires = jiffies + (5 * HZ);
+		timer.function = ahc_sem_timeout;
+		add_timer(&timer);
+		printf("Recovery code sleeping\n");
+		down(&ahc->platform_data->eh_sem);
+		printf("Recovery code awake\n");
+        	ret = del_timer(&timer);
+		if (ret == 0) {
+			printf("Timer Expired\n");
+			retval = FAILED;
+		}
+		ahc_lock(ahc, &s);
+	}
+	ahc_run_device_queues(ahc);
+	acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+	TAILQ_INIT(&ahc->platform_data->completeq);
+	ahc_unlock(ahc, &s);
+	if (acmd != NULL)
+		ahc_run_complete_queue(ahc, acmd);
+	spin_lock_irq(&io_request_lock);
+	return (retval);
+}
+
+/*
+ * Abort the current SCSI command(s).
+ */
+int
+aic7xxx_abort(Scsi_Cmnd *cmd)
+{
+	int error;
+
+	error = aic7xxx_queue_recovery_cmd(cmd, SCB_ABORT);
+	if (error != 0)
+		printf("aic7xxx_abort returns %d\n", error);
+	return (error);
+}
+
+/*
+ * Attempt to send a target reset message to the device that timed out.
+ */
+int
+aic7xxx_dev_reset(Scsi_Cmnd *cmd)
+{
+	int error;
+
+	error = aic7xxx_queue_recovery_cmd(cmd, SCB_DEVICE_RESET);
+	if (error != 0)
+		printf("aic7xxx_dev_reset returns %d\n", error);
+	return (error);
+}
+
+/*
+ * Reset the SCSI bus.
+ */
+int
+aic7xxx_bus_reset(Scsi_Cmnd *cmd)
+{
+	struct ahc_softc *ahc;
+	struct ahc_cmd *acmd;
+	u_long s;
+	int    found;
+
+	/*
+	 * It is a bug that the upper layer takes
+	 * this lock just prior to calling us.
+	 */
+	spin_unlock_irq(&io_request_lock);
+
+	ahc = *(struct ahc_softc **)cmd->host->hostdata;
+	ahc_lock(ahc, &s);
+	found = ahc_reset_channel(ahc, cmd->channel + 'A',
+				  /*initiate reset*/TRUE);
+	acmd = TAILQ_FIRST(&ahc->platform_data->completeq);
+	TAILQ_INIT(&ahc->platform_data->completeq);
+	ahc_unlock(ahc, &s);
+	if (bootverbose)
+		printf("%s: SCSI bus reset delivered. "
+		       "%d SCBs aborted.\n", ahc_name(ahc), found);
+
+	if (acmd != NULL)
+		ahc_run_complete_queue(ahc, acmd);
+
+	spin_lock_irq(&io_request_lock);
+	return SUCCESS;
+}
+
+/*
+ * Return the disk geometry for the given SCSI device.
+ */
+int
+aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[])
+{
+	int	heads;
+	int	sectors;
+	int	cylinders;
+	int	ret;
+	int	extended;
+	struct	ahc_softc *ahc;
+	struct	buffer_head *bh;
+
+	ahc = *((struct ahc_softc **)disk->device->host->hostdata);
+	bh = bread(MKDEV(MAJOR(dev), MINOR(dev) & ~0xf), 0, 1024);
+
+	if (bh) {
+		ret = scsi_partsize(bh, disk->capacity,
+				    &geom[2], &geom[0], &geom[1]);
+		brelse(bh);
+		if (ret != -1)
+			return (ret);
+	}
+	heads = 64;
+	sectors = 32;
+	cylinders = disk->capacity / (heads * sectors);
+
+	if (disk->device->channel == 0)
+		extended = (ahc->flags & AHC_EXTENDED_TRANS_A) != 0;
+	else
+		extended = (ahc->flags & AHC_EXTENDED_TRANS_B) != 0;
+	if (extended && cylinders >= 1024) {
+		heads = 255;
+		sectors = 63;
+		cylinders = disk->capacity / (heads * sectors);
+	}
+	geom[0] = heads;
+	geom[1] = sectors;
+	geom[2] = cylinders;
+	return (0);
+}
+
+/*
+ * Free the passed in Scsi_Host memory structures prior to unloading the
+ * module.
+ */
+int
+aic7xxx_release(struct Scsi_Host * host)
+{
+	struct ahc_softc *ahc;
+
+	if (host != NULL) {
+		ahc = *(struct ahc_softc **)host->hostdata;
+		ahc_free(ahc);
+	}
+	return (0);
+}
+
+void
+ahc_platform_dump_card_state(struct ahc_softc *ahc)
+{
+	struct ahc_linux_device *dev;
+	int channel;
+	int maxchannel;
+	int target;
+	int maxtarget;
+	int lun;
+	int i;
+
+	maxchannel = (ahc->features & AHC_TWIN) ? 1 : 0;
+	maxtarget = (ahc->features & AHC_WIDE) ? 15 : 7;
+	for (channel = 0; channel <= maxchannel; channel++) {
+		for (target = 0; target <=maxtarget; target++) {
+			for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+				struct ahc_cmd *acmd;
+
+				dev = ahc_get_device(ahc, channel, target,
+						     lun, /*alloc*/FALSE);
+				if (dev == NULL)
+					continue;
+
+				printf("DevQ(%d:%d:%d): ",
+				       channel, target, lun);
+				i = 0;
+				TAILQ_FOREACH(acmd, &dev->busyq,
+					      acmd_links.tqe) {
+					if (i++ > 256)
+						break;
+				}
+				printf("%d waiting\n", i);
+			}
+		}
+	}
+}
+
+#if defined(MODULE) || LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+static Scsi_Host_Template driver_template = AIC7XXX;
+Scsi_Host_Template *aic7xxx_driver_template = &driver_template;
+#include "../scsi_module.c"
+#endif
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_linux_host.h linux/drivers/scsi/aic7xxx/aic7xxx_linux_host.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_linux_host.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx_linux_host.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,87 @@
+/*
+ * Adaptec AIC7xxx device driver host template for Linux.
+ *
+ * Copyright (c) 2000 Adaptec Inc.
+ * 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/linux/drivers/scsi/aic7xxx/aic7xxx_linux_host.h#2 $
+ */
+
+#ifndef _AIC7XXX_LINUX_HOST_H_
+#define _AIC7XXX_LINUX_HOST_H_
+
+int		 aic7xxx_proc_info(char *, char **, off_t, int, int, int);
+int		 aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+int		 aic7xxx_detect(Scsi_Host_Template *);
+int		 aic7xxx_release(struct Scsi_Host *);
+const char	*aic7xxx_info(struct Scsi_Host *);
+int		 aic7xxx_biosparam(Disk *, kdev_t, int[]);
+int		 aic7xxx_bus_reset(Scsi_Cmnd *);
+int		 aic7xxx_dev_reset(Scsi_Cmnd *);
+int		 aic7xxx_abort(Scsi_Cmnd *);
+
+#if defined(__i386__)
+#  define AIC7XXX_BIOSPARAM aic7xxx_biosparam
+#else
+#  define AIC7XXX_BIOSPARAM NULL
+#endif
+
+/*
+ * Scsi_Host_Template (see hosts.h) for AIC-7xxx - some fields
+ * to do with card config are filled in after the card is detected.
+ */
+#define AIC7XXX	{						\
+	next: NULL,						\
+	module: NULL,						\
+	proc_dir: NULL,						\
+	proc_info: aic7xxx_proc_info,				\
+	name: NULL,						\
+	detect: aic7xxx_detect,					\
+	release: aic7xxx_release,				\
+	info: aic7xxx_info,					\
+	command: NULL,						\
+	queuecommand: aic7xxx_queue,				\
+	eh_strategy_handler: NULL,				\
+	eh_abort_handler: aic7xxx_abort,			\
+	eh_device_reset_handler: aic7xxx_dev_reset,		\
+	eh_bus_reset_handler: aic7xxx_bus_reset,		\
+	eh_host_reset_handler: NULL,				\
+	abort: NULL,						\
+	reset: NULL,						\
+	slave_attach: NULL,					\
+	bios_param: AIC7XXX_BIOSPARAM,				\
+	can_queue: 254,		/* max simultaneous cmds      */\
+	this_id: -1,		/* scsi id of host adapter    */\
+	sg_tablesize: 0,	/* max scatter-gather cmds    */\
+	cmd_per_lun: 2,		/* cmds per lun		      */\
+	present: 0,		/* number of 7xxx's present   */\
+	unchecked_isa_dma: 0,	/* no memory DMA restrictions */\
+	use_clustering: ENABLE_CLUSTERING,			\
+	use_new_eh_code: 1					\
+}
+
+#endif /* _AIC7XXX_LINUX_HOST_H_ */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c linux/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,314 @@
+/*
+ * Linux driver attachment glue for PCI based controllers.
+ *
+ * Copyright (c) 2000 Adaptec Inc.
+ * 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/linux/drivers/scsi/aic7xxx/aic7xxx_linux_pci.c#15 $
+ */
+
+#include "aic7xxx_osm.h"
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+struct pci_device_id
+{
+};
+#endif
+
+static int	ahc_linux_pci_dev_probe(struct pci_dev *pdev,
+					const struct pci_device_id *ent);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+static void	ahc_linux_pci_dev_remove(struct pci_dev *pdev);
+
+/* We do our own ID filtering.  So, grab all SCSI storage class devices. */
+static struct pci_device_id ahc_linux_pci_id_table[] = {
+	{
+		0x9004, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+		PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
+	},
+	{
+		0x9005, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+		PCI_CLASS_STORAGE_SCSI << 8, 0xFFFF00, 0
+	},
+	{ 0 }
+};
+
+static struct pci_driver aic7xxx_pci_driver = {
+	name:		"aic7xxx",
+	probe:		ahc_linux_pci_dev_probe,
+	remove:		ahc_linux_pci_dev_remove,
+	id_table:	ahc_linux_pci_id_table
+};
+
+static void
+ahc_linux_pci_dev_remove(struct pci_dev *pdev)
+{
+	struct ahc_softc *ahc;
+	struct ahc_softc *list_ahc;
+
+	/*
+	 * We should be able to just perform
+	 * the free directly, but check our
+	 * list for extra sanity.
+	 */
+	ahc = (struct ahc_softc *)pdev->driver_data;
+	TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
+		if (list_ahc == ahc) {
+			ahc_free(ahc);
+			break;
+		}
+	}
+}
+#endif /* !LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) */
+
+static int
+ahc_linux_pci_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	char	 buf[80];
+	struct	 ahc_softc *ahc;
+	ahc_dev_softc_t pci;
+	struct	 ahc_pci_identity *entry;
+	char	*name;
+	int	 error;
+
+	pci = pdev;
+	entry = ahc_find_pci_device(pci);
+	if (entry == NULL)
+		return (-ENODEV);
+
+	/*
+	 * Allocate a softc for this card and
+	 * set it up for attachment by our
+	 * common detect routine.
+	 */
+	sprintf(buf, "ahc_pci:%d:%d:%d",
+		ahc_get_pci_bus(pci),
+		ahc_get_pci_slot(pci),
+		ahc_get_pci_function(pci));
+	name = malloc(strlen(buf) + 1, M_DEVBUF, M_NOWAIT);
+	if (name == NULL)
+		return (-ENOMEM);
+	strcpy(name, buf);
+	ahc = ahc_alloc(NULL, name);
+	if (ahc == NULL)
+		return (-ENOMEM);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+	if (pci_enable_device(pdev)) {
+		ahc_free(ahc);
+		return (-ENODEV);
+	}
+	pci_set_master(pdev);
+#endif
+	ahc->dev_softc = pci;
+	ahc->platform_data->irq = pdev->irq;
+	error = ahc_pci_config(ahc, entry);
+	if (error != 0) {
+		ahc_free(ahc);
+		return (-error);
+	}
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+	pdev->driver_data = ahc;
+	if (aic7xxx_detect_complete)
+		aic7xxx_register_host(ahc, aic7xxx_driver_template);
+#endif
+	return (0);
+}
+
+int
+ahc_linux_pci_probe(Scsi_Host_Template *template)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+	return (pci_module_init(&aic7xxx_pci_driver));
+#else
+	struct pci_dev *pdev;
+	u_int class;
+	int found;
+
+	/* If we don't have a PCI bus, we can't find any adapters. */
+	if (pci_present() == 0)
+		return (0);
+
+	found = 0;
+	pdev = NULL;
+	class = PCI_CLASS_STORAGE_SCSI << 8;
+	while ((pdev = pci_find_class(class, pdev)) != NULL) {
+		struct ahc_softc *ahc;
+		ahc_dev_softc_t pci;
+		int error;
+
+		pci = pdev;
+
+		/*
+		 * Some BIOSen report the same device multiple times.
+		 */
+		TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+			struct pci_dev *probed_pdev;
+
+			probed_pdev = ahc->dev_softc;
+			if (probed_pdev->bus->number == pdev->bus->number
+			 && probed_pdev->devfn == pdev->devfn)
+				break;
+		}
+		if (ahc != NULL) {
+			/* Skip duplicate. */
+			continue;
+		}
+
+		error = ahc_linux_pci_dev_probe(pdev, /*pci_devid*/NULL);
+		if (error == 0)
+			found++;
+	}
+	return (found);
+#endif
+}
+
+int
+ahc_pci_map_registers(struct ahc_softc *ahc)
+{
+	uint32_t command;
+	u_long	 base;
+	u_long	 start;
+	u_long	 base_page;
+	u_long	 base_offset;
+	uint8_t *maddr;
+
+	command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, 4);
+	base = 0;
+	maddr = NULL;
+#ifdef MMAPIO
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+	start = pci_resource_start(ahc->dev_softc, 1);
+	base_page = start & PAGE_MASK;
+	base_offset = start - base_page;
+#else
+	start = ahc_pci_read_config(ahc->dev_softc, PCIR_MAPS+4, 4);
+	base_offset = start & PCI_BASE_ADDRESS_MEM_MASK;
+	base_page = base_offset & PAGE_MASK;
+	base_offset -= base_page;
+#endif
+	if (start != 0) {
+		ahc->platform_data->mem_busaddr = start;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+		if (request_mem_region(start, 0x1000, "aic7xxx") == 0) {
+			printf("aic7xxx: PCI%d:%d:%d MEM region 0x%lx "
+			       "in use. Cannot map device.\n",
+			       ahc_get_pci_bus(ahc->dev_softc),
+			       ahc_get_pci_slot(ahc->dev_softc),
+			       ahc_get_pci_function(ahc->dev_softc),
+			       start);
+		} else
+#endif
+			maddr = ioremap_nocache(base_page, base_offset + 256);
+		if (maddr != NULL) {
+			ahc->tag = BUS_SPACE_MEMIO;
+			ahc->bsh.maddr = maddr + base_offset;
+			command |= PCIM_CMD_MEMEN;
+			ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND,
+					     command, 4);
+
+			/*
+			 * Do a quick test to see if memory mapped
+			 * I/O is functioning correctly.
+			 */
+			if (ahc_inb(ahc, HCNTRL) == 0xFF) {
+				printf("aic7xxx: PCI Device %d:%d:%d "
+				       "failed memory mapped test\n",
+				       ahc_get_pci_bus(ahc->dev_softc),
+				       ahc_get_pci_slot(ahc->dev_softc),
+				       ahc_get_pci_function(ahc->dev_softc));
+				iounmap((void *)base_page);
+				maddr = NULL;
+			} else {
+				command &= ~PCIM_CMD_PORTEN;
+				ahc_pci_write_config(ahc->dev_softc,
+						    PCIR_COMMAND, command, 4);
+			}
+		}
+	}
+#endif
+
+	/*
+	 * We always prefer memory mapped access.  Only
+	 * complain about our ioport conflicting with
+	 * another device if we are going to use it.
+	 */
+	if (maddr == NULL) {
+		ahc->tag = BUS_SPACE_PIO;
+		command &= ~(PCIM_CMD_MEMEN|PCIM_CMD_PORTEN);
+		ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, 4);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+		base = pci_resource_start(ahc->dev_softc, 0);
+#else
+		base = ahc_pci_read_config(ahc->dev_softc, PCIR_MAPS, 4);
+		base &= PCI_BASE_ADDRESS_IO_MASK;
+#endif
+		if (base == 0) {
+			printf("aic7xxx: PCI%d:%d:%d No mapping available. "
+			       "Cannot map device.\n",
+			       ahc_get_pci_bus(ahc->dev_softc),
+			       ahc_get_pci_slot(ahc->dev_softc),
+			       ahc_get_pci_function(ahc->dev_softc));
+			return (ENXIO);
+		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+		if (check_region(base, 256) != 0) {
+#else
+		if (request_region(base, 256, "aic7xxx") == 0) {
+#endif
+			printf("aic7xxx: PCI%d:%d:%d IO region 0x%lx[0..255] "
+			       "in use. Cannot map device.\n",
+			       ahc_get_pci_bus(ahc->dev_softc),
+			       ahc_get_pci_slot(ahc->dev_softc),
+			       ahc_get_pci_function(ahc->dev_softc),
+			       base);
+			base = 0;
+			return (EBUSY);
+		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
+		request_region(base, 256, "aic7xxx");
+#endif
+		ahc->bsh.ioport = base;
+		command |= PCIM_CMD_PORTEN;
+		ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, 4);
+	}
+	return (0);
+}
+
+int
+ahc_pci_map_int(struct ahc_softc *ahc)
+{
+	int error;
+
+	ahc->platform_data->irq = ahc->dev_softc->irq;
+	error = request_irq(ahc->platform_data->irq, aic7xxx_isr,
+			    SA_INTERRUPT|SA_SHIRQ, "aic7xxx", ahc);
+	if (error < 0)
+		error = request_irq(ahc->platform_data->irq, aic7xxx_isr,
+				    SA_SHIRQ, "aic7xxx", ahc);
+	
+	return (-error);
+}
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_osm.h linux/drivers/scsi/aic7xxx/aic7xxx_osm.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_osm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx_osm.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,1071 @@
+/*
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * 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.
+ * 
+ * $Id: //depot/src/linux/drivers/scsi/aic7xxx/aic7xxx_linux.h#45 $
+ *
+ * Copyright (c) 2000, 2001 Adaptec Inc.
+ * 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/linux/drivers/scsi/aic7xxx/aic7xxx_linux.h#45 $
+ *
+ */
+#ifndef _AIC7XXX_LINUX_H_
+#define _AIC7XXX_LINUX_H_
+
+#include <linux/types.h>
+#include <linux/blk.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/pci.h>
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+#include <linux/config.h>
+#endif
+
+/* Core SCSI definitions */
+#include "../scsi.h"
+#include "../hosts.h"
+
+/* Name space conflict with BSD queue macros */
+#ifdef LIST_HEAD
+#undef LIST_HEAD
+#endif
+
+#include "cam.h"
+#include "queue.h"
+#include "scsi_message.h"
+
+/*
+ * We never have to reference the current task, and the driver core
+ * makes ample use of this "name".
+ */
+#ifdef current
+#undef current
+#endif
+
+/************************* Forward Declarations *******************************/
+struct ahc_softc;
+typedef struct pci_dev *ahc_dev_softc_t;
+typedef Scsi_Cmnd      *ahc_io_ctx_t;
+
+/******************************* Byte Order ***********************************/
+#define ahc_htobe16(x)	cpu_to_be16(x)
+#define ahc_htobe32(x)	cpu_to_be32(x)
+#define ahc_htobe64(x)	cpu_to_be64(x)
+#define ahc_htole16(x)	cpu_to_le16(x)
+#define ahc_htole32(x)	cpu_to_le32(x)
+#define ahc_htole64(x)	cpu_to_le64(x)
+
+#define ahc_be16toh(x)	be16_to_cpu(x)
+#define ahc_be32toh(x)	be32_to_cpu(x)
+#define ahc_be64toh(x)	be64_to_cpu(x)
+#define ahc_le16toh(x)	le16_to_cpu(x)
+#define ahc_le32toh(x)	le32_to_cpu(x)
+#define ahc_le64toh(x)	le64_to_cpu(x)
+
+/************************* Configuration Data *********************************/
+extern int aic7xxx_no_probe;
+extern int aic7xxx_detect_complete;
+extern Scsi_Host_Template* aic7xxx_driver_template;
+
+/***************************** Bus Space/DMA **********************************/
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,2,16)
+typedef dma_addr_t bus_addr_t;
+#else
+typedef uint32_t bus_addr_t;
+#endif
+typedef uint32_t bus_size_t;
+
+typedef enum {
+	BUS_SPACE_MEMIO,
+	BUS_SPACE_PIO
+} bus_space_tag_t;
+
+typedef union {
+	u_long		  ioport;
+	volatile uint8_t *maddr;
+} bus_space_handle_t;
+
+typedef struct bus_dma_segment
+{
+	bus_addr_t	ds_addr;
+	bus_size_t	ds_len;
+} bus_dma_segment_t;
+
+struct ahc_linux_dma_tag
+{
+	bus_size_t	alignment;
+	bus_size_t	boundary;
+	bus_size_t	maxsize;
+};
+typedef struct ahc_linux_dma_tag* bus_dma_tag_t;
+
+struct ahc_linux_dmamap
+{
+	bus_addr_t	bus_addr;
+};
+typedef struct ahc_linux_dmamap* bus_dmamap_t;
+
+typedef int bus_dma_filter_t(void*, bus_addr_t);
+typedef void bus_dmamap_callback_t(void *, bus_dma_segment_t *, int, int);
+
+#define BUS_DMA_WAITOK		0x0
+#define BUS_DMA_NOWAIT		0x1
+#define BUS_DMA_ALLOCNOW	0x2
+#define BUS_DMA_LOAD_SEGS	0x4	/*
+					 * Argument is an S/G list not
+					 * a single buffer.
+					 */
+
+#define BUS_SPACE_MAXADDR	0xFFFFFFFF
+#define BUS_SPACE_MAXSIZE_32BIT	0xFFFFFFFF
+
+int	ahc_dma_tag_create(struct ahc_softc *, bus_dma_tag_t /*parent*/,
+			   bus_size_t /*alignment*/, bus_size_t /*boundary*/,
+			   bus_addr_t /*lowaddr*/, bus_addr_t /*highaddr*/,
+			   bus_dma_filter_t*/*filter*/, void */*filterarg*/,
+			   bus_size_t /*maxsize*/, int /*nsegments*/,
+			   bus_size_t /*maxsegsz*/, int /*flags*/,
+			   bus_dma_tag_t */*dma_tagp*/);
+
+void	ahc_dma_tag_destroy(struct ahc_softc *, bus_dma_tag_t /*tag*/);
+
+int	ahc_dmamem_alloc(struct ahc_softc *, bus_dma_tag_t /*dmat*/,
+			 void** /*vaddr*/, int /*flags*/,
+			 bus_dmamap_t* /*mapp*/);
+
+void	ahc_dmamem_free(struct ahc_softc *, bus_dma_tag_t /*dmat*/,
+			void* /*vaddr*/, bus_dmamap_t /*map*/);
+
+void	ahc_dmamap_destroy(struct ahc_softc *, bus_dma_tag_t /*tag*/,
+			   bus_dmamap_t /*map*/);
+
+int	ahc_dmamap_load(struct ahc_softc *ahc, bus_dma_tag_t /*dmat*/,
+			bus_dmamap_t /*map*/, void * /*buf*/,
+			bus_size_t /*buflen*/, bus_dmamap_callback_t *,
+			void */*callback_arg*/, int /*flags*/);
+
+int	ahc_dmamap_unload(struct ahc_softc *, bus_dma_tag_t, bus_dmamap_t);
+
+/* XXX May do selective memory barrier operations on certain platforms */
+#define ahc_dmamap_sync(ahc, dma_tag, dmamap, op)
+
+/************************** SCSI Constants/Structures *************************/
+#define SCSI_REV_2 2
+#define	SCSI_STATUS_OK			0x00
+#define	SCSI_STATUS_CHECK_COND		0x02
+#define	SCSI_STATUS_COND_MET		0x04
+#define	SCSI_STATUS_BUSY		0x08
+#define SCSI_STATUS_INTERMED		0x10
+#define SCSI_STATUS_INTERMED_COND_MET	0x14
+#define SCSI_STATUS_RESERV_CONFLICT	0x18
+#define SCSI_STATUS_CMD_TERMINATED	0x22
+#define SCSI_STATUS_QUEUE_FULL		0x28
+
+/*
+ * 6 byte request sense CDB format.
+ */
+struct scsi_sense
+{
+	uint8_t opcode;
+	uint8_t byte2;
+	uint8_t unused[2];
+	uint8_t length;
+	uint8_t control;
+};
+
+struct scsi_sense_data
+{
+	uint8_t	error_code;
+	uint8_t	segment;
+	uint8_t	flags;
+	uint8_t	info[4];
+	uint8_t	extra_len;
+	uint8_t	cmd_spec_info[4];
+	uint8_t add_sense_code;
+	uint8_t add_sense_code_qual;
+	uint8_t	fru;
+	uint8_t	sense_key_spec[3];
+	uint8_t	extra_bytes[14];
+};
+
+struct scsi_inquiry_data
+{
+	uint8_t device;
+#define	SID_TYPE(inq_data) ((inq_data)->device & 0x1f)
+#define	SID_QUAL(inq_data) (((inq_data)->device & 0xE0) >> 5)
+#define	SID_QUAL_LU_CONNECTED	0x00	/*
+					 * The specified peripheral device
+					 * type is currently connected to
+					 * logical unit.  If the target cannot
+					 * determine whether or not a physical
+					 * device is currently connected, it
+					 * shall also use this peripheral
+					 * qualifier when returning the INQUIRY
+					 * data.  This peripheral qualifier
+					 * does not mean that the device is
+					 * ready for access by the initiator.
+					 */
+#define	SID_QUAL_LU_OFFLINE	0x01	/*
+					 * The target is capable of supporting
+					 * the specified peripheral device type
+					 * on this logical unit; however, the
+					 * physical device is not currently
+					 * connected to this logical unit.
+					 */
+#define SID_QUAL_RSVD		0x02
+#define	SID_QUAL_BAD_LU		0x03	/*
+					 * The target is not capable of
+					 * supporting a physical device on
+					 * this logical unit. For this
+					 * peripheral qualifier the peripheral
+					 * device type shall be set to 1Fh to
+					 * provide compatibility with previous
+					 * versions of SCSI. All other
+					 * peripheral device type values are
+					 * reserved for this peripheral
+					 * qualifier.
+					 */
+#define	SID_QUAL_IS_VENDOR_UNIQUE(inq_data) ((SID_QUAL(inq_data) & 0x08) != 0)
+	uint8_t dev_qual2;
+#define	SID_QUAL2	0x7F
+#define	SID_IS_REMOVABLE(inq_data) (((inq_data)->dev_qual2 & 0x80) != 0)
+	uint8_t version;
+#define SID_ANSI_REV(inq_data) ((inq_data)->version & 0x07)
+#define		SCSI_REV_0		0
+#define		SCSI_REV_CCS		1
+#define		SCSI_REV_2		2
+#define		SCSI_REV_SPC		3
+#define		SCSI_REV_SPC2		4
+
+#define SID_ECMA	0x38
+#define SID_ISO		0xC0
+	uint8_t response_format;
+#define SID_AENC	0x80
+#define SID_TrmIOP	0x40
+	uint8_t additional_length;
+	uint8_t reserved[2];
+	uint8_t flags;
+#define	SID_SftRe	0x01
+#define	SID_CmdQue	0x02
+#define	SID_Linked	0x08
+#define	SID_Sync	0x10
+#define	SID_WBus16	0x20
+#define	SID_WBus32	0x40
+#define	SID_RelAdr	0x80
+#define SID_VENDOR_SIZE   8
+	char	 vendor[SID_VENDOR_SIZE];
+#define SID_PRODUCT_SIZE  16
+	char	 product[SID_PRODUCT_SIZE];
+#define SID_REVISION_SIZE 4
+	char	 revision[SID_REVISION_SIZE];
+	/*
+	 * The following fields were taken from SCSI Primary Commands - 2
+	 * (SPC-2) Revision 14, Dated 11 November 1999
+	 */
+#define	SID_VENDOR_SPECIFIC_0_SIZE	20
+	u_int8_t vendor_specific0[SID_VENDOR_SPECIFIC_0_SIZE];
+	/*
+	 * An extension of SCSI Parallel Specific Values
+	 */
+#define	SID_SPI_IUS		0x01
+#define	SID_SPI_QAS		0x02
+#define	SID_SPI_CLOCK_ST	0x00
+#define	SID_SPI_CLOCK_DT	0x04
+#define	SID_SPI_CLOCK_DT_ST	0x0C
+#define	SID_SPI_MASK		0x0F
+	uint8_t spi3data;
+	uint8_t reserved2;
+	/*
+	 * Version Descriptors, stored 2 byte values.
+	 */
+	uint8_t version1[2];
+	uint8_t version2[2];
+	uint8_t version3[2];
+	uint8_t version4[2];
+	uint8_t version5[2];
+	uint8_t version6[2];
+	uint8_t version7[2];
+	uint8_t version8[2];
+
+	uint8_t reserved3[22];
+
+#define	SID_VENDOR_SPECIFIC_1_SIZE	160
+	uint8_t vendor_specific1[SID_VENDOR_SPECIFIC_1_SIZE];
+};
+
+/********************************** Includes **********************************/
+/* Host template and function declarations referenced by the template. */
+#include "aic7xxx_linux_host.h"
+
+/* Core driver definitions */
+#include "aic7xxx.h"
+
+/* SMP support */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,17)
+#include <linux/spinlock.h>
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93)
+#include <linux/smp.h>
+#endif
+
+#define AIC7XXX_DRIVER_VERSION  "6.1.5"
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif
+
+#ifndef KERNEL_VERSION
+#define KERNEL_VERSION(x,y,z) (((x)<<16)+((y)<<8)+(z))
+#endif
+
+/**************************** Front End Queues ********************************/
+/*
+ * Data structure used to cast the Linux struct scsi_cmnd to something
+ * that allows us to use the queue macros.  The linux structure has
+ * plenty of space to hold the links fields as required by the queue
+ * macros, but the queue macors require them to have the correct type.
+ */
+struct ahc_cmd_internal {
+	/* Area owned by the Linux scsi layer. */
+	uint8_t	private[offsetof(struct scsi_cmnd, SCp.Status)];
+	union {
+		STAILQ_ENTRY(ahc_cmd)	ste;
+		LIST_ENTRY(ahc_cmd)	le;
+		TAILQ_ENTRY(ahc_cmd)	tqe;
+	} links;
+	uint32_t			end;
+};
+
+struct ahc_cmd {
+	union {
+		struct ahc_cmd_internal	icmd;
+		struct scsi_cmnd	scsi_cmd;
+	} un;
+};
+
+#define acmd_icmd(cmd) ((cmd)->un.icmd)
+#define acmd_scsi_cmd(cmd) ((cmd)->un.scsi_cmd)
+#define acmd_links un.icmd.links
+
+/*************************** Device Data Structures ***************************/
+/*
+ * A per probed device structure used to deal with some error recovery
+ * scenarios that the Linux mid-layer code just doesn't know how to
+ * handle.  The structure allocated for a device only becomes persistant
+ * after a successfully completed inquiry command to the target when
+ * that inquiry data indicates a lun is present.
+ */
+TAILQ_HEAD(ahc_busyq, ahc_cmd);
+typedef enum {
+	AHC_DEV_UNCONFIGURED	 = 0x01,
+	AHC_DEV_FREEZE_TIL_EMPTY = 0x02, /* Freeze queue until active == 0 */
+	AHC_DEV_TIMER_ACTIVE	 = 0x04, /* Our timer is active */
+	AHC_DEV_ON_RUN_LIST	 = 0x08	 /* Queued to be run later */
+} ahc_dev_flags;
+
+struct ahc_linux_target;
+struct ahc_linux_device {
+	LIST_ENTRY(ahc_linux_device) links;
+	struct		ahc_busyq busyq;
+
+	/*
+	 * The number of transactions currently
+	 * queued to the device.
+	 */
+	int		active;
+
+	/*
+	 * The currently allowed number of 
+	 * transactions that can be queued to
+	 * the device.  Must be signed for
+	 * conversion from tagged to untagged
+	 * mode where the device may have more
+	 * than one outstanding active transaction.
+	 */
+	int		openings;
+
+	/*
+	 * A positive count indicates that this
+	 * device's queue is halted.
+	 */
+	u_int		qfrozen;
+	
+	/*
+	 * Cumulative command counter.
+	 */
+	u_int		num_commands;
+
+	/*
+	 * The number of tagged transactions when
+	 * running at our current opening level
+	 * that have been successfully received by
+	 * this device since the last QUEUE FULL.
+	 */
+	u_int		tag_success_count;
+#define AHC_TAG_SUCCESS_INTERVAL 50
+
+	ahc_dev_flags	flags;
+
+	/*
+	 * The high limit for the tags variable.
+	 */
+	u_int		maxtags;
+
+	/*
+	 * The computed number of tags outstanding
+	 * at the time of the last QUEUE FULL event.
+	 */
+	u_int		tags_on_last_queuefull;
+
+	/*
+	 * How many times we have seen a queue full
+	 * with the same number of tags.  This is used
+	 * to stop our adaptive queue depth algorithm
+	 * on devices with a fixed number of tags.
+	 */
+	u_int		last_queuefull_same_count;
+
+#define AHC_LOCK_TAGS_COUNT 50
+	int		lun;
+	struct		ahc_linux_target *target;
+};
+
+struct ahc_linux_target {
+	struct	ahc_linux_device *devices[AHC_NUM_LUNS];
+	int	channel;
+	int	target;
+	int	refcount;
+	struct	ahc_transinfo last_tinfo;
+};
+
+/********************* Definitions Required by the Core ***********************/
+/*
+ * Number of SG segments we require.  So long as the S/G segments for
+ * a particular transaction are allocated in a physically contiguous
+ * manner and are allocated below 4GB, the number of S/G segments is
+ * unrestricted.
+ */
+#define        AHC_NSEG 128
+
+/*
+ * Per-SCB OSM storage.
+ */
+struct scb_platform_data {
+	struct ahc_linux_device	*dev;
+	uint32_t		 xfer_len;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+	uint32_t		 resid;		/* Transfer residual */
+#endif
+};
+
+/*
+ * Define a structure used for each host adapter.  All members are
+ * aligned on a boundary >= the size of the member to honor the
+ * alignment restrictions of the various platforms supported by
+ * this driver.
+ */
+TAILQ_HEAD(ahc_completeq, ahc_cmd);
+struct ahc_platform_data {
+	/*
+	 * Fields accessed from interrupt context.
+	 */
+	struct ahc_linux_target *targets[AHC_NUM_TARGETS]; 
+	LIST_HEAD(, ahc_linux_device) device_runq;
+	struct ahc_completeq	 completeq;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)
+	spinlock_t		 spin_lock;
+#endif
+	u_int			 qfrozen;
+	struct timer_list	 reset_timer;
+	struct semaphore	 eh_sem;
+	struct Scsi_Host        *host;		/* pointer to scsi host */
+	uint32_t		 irq;		/* IRQ for this adapter */
+	uint32_t		 bios_address;
+	uint32_t		 mem_busaddr;	/* Mem Base Addr */
+};
+
+/************************** OS Utility Wrappers *******************************/
+#define printf printk
+#define M_NOWAIT GFP_ATOMIC
+#define M_WAITOK 0
+#define malloc(size, type, flags) kmalloc(size, flags)
+#define free(ptr, type) kfree(ptr)
+
+static __inline void ahc_delay(long);
+static __inline void
+ahc_delay(long usec)
+{
+	/*
+	 * udelay on Linux can have problems for
+	 * multi-millisecond waits.  Wait at most
+	 * 1024us per call.
+	 */
+	while (usec > 0) {
+		udelay(usec % 1024);
+		usec -= 1024;
+	}
+}
+
+
+/***************************** Low Level I/O **********************************/
+#if defined(__powerpc__)
+#define MMAPIO
+#ifdef mb
+#undef mb
+#endif
+#define mb() \
+	__asm__ __volatile__("eieio" ::: "memory")
+#elif defined(__i386__)
+#define MMAPIO
+#ifdef mb
+#undef mb
+#endif
+#define mb() \
+	do { ; } while(0)
+#elif defined(__alpha__)
+#ifdef mb
+#undef mb
+#endif
+#define mb() \
+	__asm__ __volatile__("mb": : :"memory")
+#endif
+
+static __inline uint8_t ahc_inb(struct ahc_softc * ahc, long port);
+static __inline void ahc_outb(struct ahc_softc * ahc, long port, uint8_t val);
+static __inline void ahc_outsb(struct ahc_softc * ahc, long port,
+			       uint8_t *, int count);
+static __inline void ahc_insb(struct ahc_softc * ahc, long port,
+			       uint8_t *, int count);
+
+static __inline uint8_t
+ahc_inb(struct ahc_softc * ahc, long port)
+{
+#ifdef MMAPIO
+	uint8_t x;
+
+	if (ahc->tag == BUS_SPACE_MEMIO) {
+		x = readb(ahc->bsh.maddr + port);
+	} else {
+		x = inb(ahc->bsh.ioport + port);
+	}
+	mb();
+	return (x);
+#else
+	return (inb(ahc->bsh.ioport + port));
+#endif
+}
+
+static __inline void
+ahc_outb(struct ahc_softc * ahc, long port, uint8_t val)
+{
+#ifdef MMAPIO
+	if (ahc->tag == BUS_SPACE_MEMIO) {
+		writeb(val, ahc->bsh.maddr + port);
+	} else {
+		outb(val, ahc->bsh.ioport + port);
+	}
+	mb();
+#else
+	outb(val, ahc->bsh.ioport + port);
+#endif
+}
+
+static __inline void
+ahc_outsb(struct ahc_softc * ahc, long port, uint8_t *array, int count)
+{
+	int i;
+
+	/*
+	 * There is probably a more efficient way to do this on Linux
+	 * but we don't use this for anything speed critical and this
+	 * should work.
+	 */
+	for (i = 0; i < count; i++)
+		ahc_outb(ahc, port, *array++);
+}
+
+static __inline void
+ahc_insb(struct ahc_softc * ahc, long port, uint8_t *array, int count)
+{
+	int i;
+
+	/*
+	 * There is probably a more efficient way to do this on Linux
+	 * but we don't use this for anything speed critical and this
+	 * should work.
+	 */
+	for (i = 0; i < count; i++)
+		*array++ = ahc_inb(ahc, port);
+}
+
+/**************************** Initialization **********************************/
+int	aic7xxx_register_host(struct ahc_softc *ahc,
+			      Scsi_Host_Template *template);
+
+/*************************** Pretty Printing **********************************/
+struct info_str {
+	char *buffer;
+	int length;
+	off_t offset;
+	int pos;
+};
+
+void	ahc_format_transinfo(struct info_str *info,
+			     struct ahc_transinfo *tinfo);
+
+/******************************** Locking *************************************/
+/* Lock protecting internal data structures */
+static __inline void ahc_lockinit(struct ahc_softc *);
+static __inline void ahc_lock(struct ahc_softc *, unsigned long *flags);
+static __inline void ahc_unlock(struct ahc_softc *, unsigned long *flags);
+
+/* Lock held during command compeletion to the upper layer */
+static __inline void ahc_done_lockinit(struct ahc_softc *);
+static __inline void ahc_done_lock(struct ahc_softc *, unsigned long *flags);
+static __inline void ahc_done_unlock(struct ahc_softc *, unsigned long *flags);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,93)
+static __inline void
+ahc_lockinit(struct ahc_softc *ahc)
+{
+	spin_lock_init(&ahc->platform_data->spin_lock);
+}
+
+static __inline void
+ahc_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+	*flags = 0;
+	spin_lock_irqsave(&ahc->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+	spin_unlock_irqrestore(&ahc->platform_data->spin_lock, *flags);
+}
+
+static __inline void
+ahc_done_lockinit(struct ahc_softc *ahc)
+{
+	/* We don't own the iorequest lock, so we don't initialize it. */
+}
+
+static __inline void
+ahc_done_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+	*flags = 0;
+	spin_lock_irqsave(&io_request_lock, *flags);
+}
+
+static __inline void
+ahc_done_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+	spin_unlock_irqrestore(&io_request_lock, *flags);
+}
+
+#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) */
+
+ahc_lockinit(struct ahc_softc *ahc)
+{
+}
+
+static __inline void
+ahc_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+	*flags = 0;
+	save_flags(*flags);
+	cli();
+}
+
+static __inline void
+ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+	restore_flags(*flags);
+}
+
+ahc_done_lockinit(struct ahc_softc *ahc)
+{
+}
+
+static __inline void
+ahc_done_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+	/*
+	 * The done lock is always held while
+	 * the ahc lock is held so blocking
+	 * interrupts again would have no effect.
+	 */
+}
+
+static __inline void
+ahc_done_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+}
+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) */
+
+/******************************* PCI Definitions ******************************/
+/*
+ * PCIM_xxx: mask to locate subfield in register
+ * PCIR_xxx: config register offset
+ * PCIC_xxx: device class
+ * PCIS_xxx: device subclass
+ * PCIP_xxx: device programming interface
+ * PCIV_xxx: PCI vendor ID (only required to fixup ancient devices)
+ * PCID_xxx: device ID
+ */
+#define PCIR_DEVVENDOR		0x00
+#define PCIR_VENDOR		0x00
+#define PCIR_DEVICE		0x02
+#define PCIR_COMMAND		0x04
+#define PCIM_CMD_PORTEN		0x0001
+#define PCIM_CMD_MEMEN		0x0002
+#define PCIM_CMD_BUSMASTEREN	0x0004
+#define PCIM_CMD_MWRICEN	0x0010
+#define PCIM_CMD_PERRESPEN	0x0040
+#define PCIR_STATUS		0x06
+#define PCIR_REVID		0x08
+#define PCIR_PROGIF		0x09
+#define PCIR_SUBCLASS		0x0a
+#define PCIR_CLASS		0x0b
+#define PCIR_CACHELNSZ		0x0c
+#define PCIR_LATTIMER		0x0d
+#define PCIR_HEADERTYPE		0x0e
+#define PCIM_MFDEV		0x80
+#define PCIR_BIST		0x0f
+#define PCIR_CAP_PTR		0x34
+
+/* config registers for header type 0 devices */
+#define PCIR_MAPS	0x10
+#define PCIR_SUBVEND_0	0x2c
+#define PCIR_SUBDEV_0	0x2e
+
+/**************************** VL/EISA Routines ********************************/
+int			 aic7770_linux_probe(Scsi_Host_Template *);
+int			 aic7770_map_registers(struct ahc_softc *ahc);
+int			 aic7770_map_int(struct ahc_softc *ahc,
+					 u_int irq, int shared);
+
+/******************************* PCI Routines *********************************/
+/*
+ * We need to use the bios32.h routines if we are kernel version 2.1.92 or less.
+ */
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,1,92)
+#if defined(__sparc_v9__) || defined(__powerpc__)
+#error "PPC and Sparc platforms are only support under 2.1.92 and above"
+#endif
+#include <linux/bios32.h>
+#endif
+
+int			 ahc_linux_pci_probe(Scsi_Host_Template *);
+int			 ahc_pci_map_registers(struct ahc_softc *ahc);
+int			 ahc_pci_map_int(struct ahc_softc *ahc);
+
+static __inline uint32_t ahc_pci_read_config(ahc_dev_softc_t pci,
+					     int reg, int width);
+
+static __inline uint32_t
+ahc_pci_read_config(ahc_dev_softc_t pci, int reg, int width)
+{
+	switch (width) {
+	case 1:
+	{
+		uint8_t retval;
+
+		pci_read_config_byte(pci, reg, &retval);
+		return (retval);
+	}
+	case 2:
+	{
+		uint16_t retval;
+		pci_read_config_word(pci, reg, &retval);
+		return (retval);
+	}
+	case 4:
+	{
+		uint32_t retval;
+		pci_read_config_dword(pci, reg, &retval);
+		return (retval);
+	}
+	default:
+		panic("ahc_pci_read_config: Read size too big");
+		/* NOTREACHED */
+	}
+}
+
+static __inline void ahc_pci_write_config(ahc_dev_softc_t pci,
+					  int reg, uint32_t value,
+					  int width);
+
+static __inline void
+ahc_pci_write_config(ahc_dev_softc_t pci, int reg, uint32_t value, int width)
+{
+	switch (width) {
+	case 1:
+		pci_write_config_byte(pci, reg, value);
+		break;
+	case 2:
+		pci_write_config_word(pci, reg, value);
+		break;
+	case 4:
+		pci_write_config_dword(pci, reg, value);
+		break;
+	default:
+		panic("ahc_pci_write_config: Write size too big");
+		/* NOTREACHED */
+	}
+}
+
+static __inline int ahc_get_pci_function(ahc_dev_softc_t);
+static __inline int
+ahc_get_pci_function(ahc_dev_softc_t pci)
+{
+	return (PCI_FUNC(pci->devfn));
+}
+
+static __inline int ahc_get_pci_slot(ahc_dev_softc_t);
+static __inline int
+ahc_get_pci_slot(ahc_dev_softc_t pci)
+{
+	return (PCI_SLOT(pci->devfn));
+}
+
+static __inline int ahc_get_pci_bus(ahc_dev_softc_t);
+static __inline int
+ahc_get_pci_bus(ahc_dev_softc_t pci)
+{
+	return (pci->bus->number);
+}
+
+static __inline void ahc_flush_device_writes(struct ahc_softc *);
+static __inline void
+ahc_flush_device_writes(struct ahc_softc *ahc)
+{
+	/* XXX Is this sufficient for all architectures??? */
+	ahc_inb(ahc, INTSTAT);
+}
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,0)
+#define pci_map_sg(pdev, sg_list, nseg, direction) (nseg)
+#define pci_unmap_sg(pdev, sg_list, nseg, direction)
+#define sg_dma_address(sg) (VIRT_TO_BUS((sg)->address))
+#define sg_dma_len(sg) ((sg)->length)
+#define pci_map_single(pdev, buffer, bufflen, direction) \
+	(VIRT_TO_BUS(buffer))
+#define pci_unmap_single(pdev, buffer, buflen, direction)
+#endif
+
+/*********************** Transaction Access Wrappers **************************/
+static __inline void ahc_set_transaction_status(struct scb *, uint32_t);
+static __inline
+void ahc_set_transaction_status(struct scb *scb, uint32_t status)
+{
+	scb->io_ctx->result &= ~(CAM_STATUS_MASK << 16);
+	scb->io_ctx->result |= status << 16;
+}
+
+static __inline void ahc_set_scsi_status(struct scb *, uint32_t);
+static __inline
+void ahc_set_scsi_status(struct scb *scb, uint32_t status)
+{
+	scb->io_ctx->result &= ~0xFFFF;
+	scb->io_ctx->result |= status;
+}
+
+static __inline uint32_t ahc_get_transaction_status(struct scb *);
+static __inline
+uint32_t ahc_get_transaction_status(struct scb *scb)
+{
+	return ((scb->io_ctx->result >> 16) & CAM_STATUS_MASK);
+}
+
+static __inline uint32_t ahc_get_scsi_status(struct scb *);
+static __inline
+uint32_t ahc_get_scsi_status(struct scb *scb)
+{
+	return (scb->io_ctx->result & 0xFFFF);
+}
+
+static __inline void ahc_set_transaction_tag(struct scb *, int, u_int);
+static __inline
+void ahc_set_transaction_tag(struct scb *scb, int enabled, u_int type)
+{
+	/*
+	 * Nothing to do for linux as the incoming transaction
+	 * has no concept of tag/non tagged, etc.
+	 */
+}
+
+static __inline u_long ahc_get_transfer_length(struct scb *);
+static __inline
+u_long ahc_get_transfer_length(struct scb *scb)
+{
+	return (scb->platform_data->xfer_len);
+}
+
+static __inline int ahc_get_transfer_dir(struct scb *);
+static __inline
+int ahc_get_transfer_dir(struct scb *scb)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,40)
+	return (scb->io_ctx->sc_data_direction);
+#else
+	if (scb->io_ctx->bufflen == 0)
+		return (CAM_DIR_NONE);
+
+	switch(scb->io_ctx->cmnd[0]) {
+	case 0x08:  /* READ(6)  */
+	case 0x28:  /* READ(10) */
+	case 0xA8:  /* READ(12) */
+		return (CAM_DIR_IN);
+        case 0x0A:  /* WRITE(6)  */
+        case 0x2A:  /* WRITE(10) */
+        case 0xAA:  /* WRITE(12) */
+		return (CAM_DIR_OUT);
+        default:
+		return (CAM_DIR_NONE);
+        }
+#endif
+}
+
+static __inline void ahc_set_residual(struct scb *, u_long);
+static __inline
+void ahc_set_residual(struct scb *scb, u_long resid)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+	scb->io_ctx->resid = resid;
+#else
+	scb->platform_data->resid = resid;
+#endif
+}
+
+static __inline void ahc_set_sense_residual(struct scb *, u_long);
+static __inline
+void ahc_set_sense_residual(struct scb *scb, u_long resid)
+{
+	/* This can't be reported in Linux */
+}
+
+static __inline u_long ahc_get_residual(struct scb *);
+static __inline
+u_long ahc_get_residual(struct scb *scb)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+	return (scb->io_ctx->resid);
+#else
+	return (scb->platform_data->resid);
+#endif
+}
+
+static __inline int ahc_perform_autosense(struct scb *);
+static __inline
+int ahc_perform_autosense(struct scb *scb)
+{
+	/*
+	 * We always perform autosense in Linux.
+	 * On other platforms this is set on a
+	 * per-transaction basis.
+	 */
+	return (1);
+}
+
+static __inline uint32_t
+ahc_get_sense_bufsize(struct ahc_softc *ahc, struct scb *scb)
+{
+	return (sizeof(struct scsi_sense_data));
+}
+
+static __inline void ahc_notify_xfer_settings_change(struct ahc_softc *,
+						     struct ahc_devinfo *);
+static __inline void
+ahc_notify_xfer_settings_change(struct ahc_softc *ahc,
+				struct ahc_devinfo *devinfo)
+{
+	/* Nothing to do here for linux */
+}
+
+static __inline void ahc_platform_scb_free(struct ahc_softc *ahc,
+					   struct scb *scb);
+static __inline void
+ahc_platform_scb_free(struct ahc_softc *ahc, struct scb *scb)
+{
+	ahc->flags &= ~AHC_RESOURCE_SHORTAGE;
+}
+
+int	ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg);
+void	ahc_platform_free(struct ahc_softc *ahc);
+void	ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb);
+static __inline void	ahc_freeze_scb(struct scb *scb);
+static __inline void
+ahc_freeze_scb(struct scb *scb)
+{
+	/* Noting to do here for linux */
+}
+
+void	ahc_platform_set_tags(struct ahc_softc *ahc,
+			      struct ahc_devinfo *devinfo, int enable);
+int	ahc_platform_abort_scbs(struct ahc_softc *ahc, int target,
+				char channel, int lun, u_int tag,
+				role_t role, uint32_t status);
+void	aic7xxx_isr(int irq, void *dev_id, struct pt_regs * regs);
+void	ahc_platform_flushwork(struct ahc_softc *ahc);
+int	ahc_softc_comp(struct ahc_softc *, struct ahc_softc *);
+void	ahc_done(struct ahc_softc*, struct scb*);
+void	ahc_send_async(struct ahc_softc *, char channel,
+		       u_int target, u_int lun, ac_code);
+void	ahc_print_path(struct ahc_softc *, struct scb *);
+void	ahc_platform_dump_card_state(struct ahc_softc *ahc);
+
+#ifdef CONFIG_PCI
+#define AHC_PCI_CONFIG 1
+#else
+#define AHC_PCI_CONFIG 0
+#endif
+#define bootverbose aic7xxx_verbose
+extern int aic7xxx_verbose;
+#endif /* _AIC7XXX_LINUX_H_ */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_pci.c linux/drivers/scsi/aic7xxx/aic7xxx_pci.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_pci.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx_pci.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,2137 @@
+/*
+ * Product specific probe and attach routines for:
+ *      3940, 2940, aic7895, aic7890, aic7880,
+ *	aic7870, aic7860 and aic7850 SCSI controllers
+ *
+ * Copyright (c) 1995-2000 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aic7xxx_pci.c#17 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_pci.c,v 1.6 2000/11/10 20:13:41 gibbs Exp $
+ */
+
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+#include "aic7xxx_93cx6.h"
+
+#define AHC_PCI_IOADDR	PCIR_MAPS	/* I/O Address */
+#define AHC_PCI_MEMADDR	(PCIR_MAPS + 4)	/* Mem I/O Address */
+
+static __inline uint64_t
+ahc_compose_id(u_int device, u_int vendor, u_int subdevice, u_int subvendor)
+{
+	uint64_t id;
+
+	id = subvendor
+	   | (subdevice << 16)
+	   | ((uint64_t)vendor << 32)
+	   | ((uint64_t)device << 48);
+
+	return (id);
+}
+
+#define ID_ALL_MASK			0xFFFFFFFFFFFFFFFFull
+#define ID_DEV_VENDOR_MASK		0xFFFFFFFF00000000ull
+#define ID_9005_GENERIC_MASK		0xFFF0FFFF00000000ull
+#define ID_9005_SISL_MASK		0x000FFFFF00000000ull
+#define ID_9005_SISL_ID			0x0005900500000000ull
+#define ID_AIC7850			0x5078900400000000ull
+#define ID_AHA_2902_04_10_15_20_30C	0x5078900478509004ull
+#define ID_AIC7855			0x5578900400000000ull
+#define ID_AIC7859			0x3860900400000000ull
+#define ID_AHA_2930CU			0x3860900438699004ull
+#define ID_AIC7860			0x6078900400000000ull
+#define ID_AIC7860C			0x6078900478609004ull
+#define ID_AHA_1480A			0x6075900400000000ull
+#define ID_AHA_2940AU_0			0x6178900400000000ull
+#define ID_AHA_2940AU_1			0x6178900478619004ull
+#define ID_AHA_2940AU_CN		0x2178900478219004ull
+#define ID_AHA_2930C_VAR		0x6038900438689004ull
+
+#define ID_AIC7870			0x7078900400000000ull
+#define ID_AHA_2940			0x7178900400000000ull
+#define ID_AHA_3940			0x7278900400000000ull
+#define ID_AHA_398X			0x7378900400000000ull
+#define ID_AHA_2944			0x7478900400000000ull
+#define ID_AHA_3944			0x7578900400000000ull
+#define ID_AHA_4944			0x7678900400000000ull
+
+#define ID_AIC7880			0x8078900400000000ull
+#define ID_AIC7880_B			0x8078900478809004ull
+#define ID_AHA_2940U			0x8178900400000000ull
+#define ID_AHA_3940U			0x8278900400000000ull
+#define ID_AHA_2944U			0x8478900400000000ull
+#define ID_AHA_3944U			0x8578900400000000ull
+#define ID_AHA_398XU			0x8378900400000000ull
+#define ID_AHA_4944U			0x8678900400000000ull
+#define ID_AHA_2940UB			0x8178900478819004ull
+#define ID_AHA_2930U			0x8878900478889004ull
+#define ID_AHA_2940U_PRO		0x8778900478879004ull
+#define ID_AHA_2940U_CN			0x0078900478009004ull
+
+#define ID_AIC7895			0x7895900478959004ull
+#define ID_AIC7895_ARO			0x7890900478939004ull
+#define ID_AIC7895_ARO_MASK		0xFFF0FFFFFFFFFFFFull
+#define ID_AHA_2940U_DUAL		0x7895900478919004ull
+#define ID_AHA_3940AU			0x7895900478929004ull
+#define ID_AHA_3944AU			0x7895900478949004ull
+
+#define ID_AIC7890			0x001F9005000F9005ull
+#define ID_AIC7890_ARO			0x00139005000F9005ull
+#define ID_AAA_131U2			0x0013900500039005ull
+#define ID_AHA_2930U2			0x0011900501819005ull
+#define ID_AHA_2940U2B			0x00109005A1009005ull
+#define ID_AHA_2940U2_OEM		0x0010900521809005ull
+#define ID_AHA_2940U2			0x00109005A1809005ull
+#define ID_AHA_2950U2B			0x00109005E1009005ull
+
+#define ID_AIC7892			0x008F9005FFFF9005ull
+#define ID_AIC7892_ARO			0x00839005FFFF9005ull
+#define ID_AHA_29160			0x00809005E2A09005ull
+#define ID_AHA_29160_CPQ		0x00809005E2A00E11ull
+#define ID_AHA_29160N			0x0080900562A09005ull
+#define ID_AHA_29160C			0x0080900562209005ull
+#define ID_AHA_29160B			0x00809005E2209005ull
+#define ID_AHA_19160B			0x0081900562A19005ull
+
+#define ID_AIC7896			0x005F9005FFFF9005ull
+#define ID_AIC7896_ARO			0x00539005FFFF9005ull
+#define ID_AHA_3950U2B_0		0x00509005FFFF9005ull
+#define ID_AHA_3950U2B_1		0x00509005F5009005ull
+#define ID_AHA_3950U2D_0		0x00519005FFFF9005ull
+#define ID_AHA_3950U2D_1		0x00519005B5009005ull
+
+#define ID_AIC7899			0x00CF9005FFFF9005ull
+#define ID_AIC7899_ARO			0x00C39005FFFF9005ull
+#define ID_AHA_3960D			0x00C09005F6209005ull
+#define ID_AHA_3960D_CPQ		0x00C09005F6200E11ull
+
+#define ID_AIC7810			0x1078900400000000ull
+#define ID_AIC7815			0x7815900400000000ull
+
+#define DEVID_9005_TYPE(id) ((id) & 0xF)
+#define		DEVID_9005_TYPE_HBA		0x0	/* Standard Card */
+#define		DEVID_9005_TYPE_AAA		0x3	/* RAID Card */
+#define		DEVID_9005_TYPE_SISL		0x5	/* Low Cost Card */
+#define		DEVID_9005_TYPE_MB		0xF	/* On Motherboard */
+
+#define DEVID_9005_MAXRATE(id) (((id) & 0x30) >> 4)
+#define		DEVID_9005_MAXRATE_U160		0x0
+#define		DEVID_9005_MAXRATE_ULTRA2	0x1
+#define		DEVID_9005_MAXRATE_ULTRA	0x2
+#define		DEVID_9005_MAXRATE_FAST		0x3
+
+#define DEVID_9005_MFUNC(id) (((id) & 0x40) >> 6)
+
+#define DEVID_9005_CLASS(id) (((id) & 0xFF00) >> 8)
+#define		DEVID_9005_CLASS_SPI		0x0	/* Parallel SCSI */
+
+#define SUBID_9005_TYPE(id) ((id) & 0xF)
+#define		SUBID_9005_TYPE_MB		0xF	/* On Motherboard */
+#define		SUBID_9005_TYPE_CARD		0x0	/* Standard Card */
+#define		SUBID_9005_TYPE_LCCARD		0x1	/* Low Cost Card */
+#define		SUBID_9005_TYPE_RAID		0x3	/* Combined with Raid */
+
+#define SUBID_9005_TYPE_KNOWN(id)			\
+	  ((((id) & 0xF) == SUBID_9005_TYPE_MB)		\
+	|| (((id) & 0xF) == SUBID_9005_TYPE_CARD)	\
+	|| (((id) & 0xF) == SUBID_9005_TYPE_LCCARD)	\
+	|| (((id) & 0xF) == SUBID_9005_TYPE_RAID))
+
+#define SUBID_9005_MAXRATE(id) (((id) & 0x30) >> 4)
+#define		SUBID_9005_MAXRATE_ULTRA2	0x0
+#define		SUBID_9005_MAXRATE_ULTRA	0x1
+#define		SUBID_9005_MAXRATE_U160		0x2
+#define		SUBID_9005_MAXRATE_RESERVED	0x3
+
+#define SUBID_9005_SEEPTYPE(id)						\
+	((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)			\
+	 ? ((id) & 0xC0) >> 6						\
+	 : ((id) & 0x300) >> 8)
+#define		SUBID_9005_SEEPTYPE_NONE	0x0
+#define		SUBID_9005_SEEPTYPE_1K		0x1
+#define		SUBID_9005_SEEPTYPE_2K_4K	0x2
+#define		SUBID_9005_SEEPTYPE_RESERVED	0x3
+#define SUBID_9005_AUTOTERM(id)						\
+	((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)			\
+	 ? (((id) & 0x400) >> 10) == 0					\
+	 : (((id) & 0x40) >> 6) == 0)
+
+#define SUBID_9005_NUMCHAN(id)						\
+	((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)			\
+	 ? ((id) & 0x300) >> 8						\
+	 : ((id) & 0xC00) >> 10)
+
+#define SUBID_9005_LEGACYCONN(id)					\
+	((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)			\
+	 ? 0								\
+	 : ((id) & 0x80) >> 7)
+
+#define SUBID_9005_MFUNCENB(id)						\
+	((SUBID_9005_TYPE(id) == SUBID_9005_TYPE_MB)			\
+	 ? ((id) & 0x800) >> 11						\
+	 : ((id) & 0x1000) >> 12)
+/*
+ * Informational only. Should use chip register to be
+ * ceratian, but may be use in identification strings.
+ */
+#define SUBID_9005_CARD_SCSIWIDTH_MASK	0x2000
+#define SUBID_9005_CARD_PCIWIDTH_MASK	0x4000
+#define SUBID_9005_CARD_SEDIFF_MASK	0x8000
+
+static ahc_device_setup_t ahc_aic785X_setup;
+static ahc_device_setup_t ahc_aic7860_setup;
+static ahc_device_setup_t ahc_apa1480_setup;
+static ahc_device_setup_t ahc_aic7870_setup;
+static ahc_device_setup_t ahc_aha394X_setup;
+static ahc_device_setup_t ahc_aha494X_setup;
+static ahc_device_setup_t ahc_aha398X_setup;
+static ahc_device_setup_t ahc_aic7880_setup;
+static ahc_device_setup_t ahc_aha2940Pro_setup;
+static ahc_device_setup_t ahc_aha394XU_setup;
+static ahc_device_setup_t ahc_aha398XU_setup;
+static ahc_device_setup_t ahc_aic7890_setup;
+static ahc_device_setup_t ahc_aic7892_setup;
+static ahc_device_setup_t ahc_aic7895_setup;
+static ahc_device_setup_t ahc_aic7896_setup;
+static ahc_device_setup_t ahc_aic7899_setup;
+static ahc_device_setup_t ahc_aha29160C_setup;
+static ahc_device_setup_t ahc_raid_setup;
+static ahc_device_setup_t ahc_aha394XX_setup;
+static ahc_device_setup_t ahc_aha494XX_setup;
+static ahc_device_setup_t ahc_aha398XX_setup;
+
+struct ahc_pci_identity ahc_pci_ident_table [] =
+{
+	/* aic7850 based controllers */
+	{
+		ID_AHA_2902_04_10_15_20_30C,
+		ID_ALL_MASK,
+		"Adaptec 2902/04/10/15/20/30C SCSI adapter",
+		ahc_aic785X_setup
+	},
+	/* aic7860 based controllers */
+	{
+		ID_AHA_2930CU,
+		ID_ALL_MASK,
+		"Adaptec 2930CU SCSI adapter",
+		ahc_aic7860_setup
+	},
+	{
+		ID_AHA_1480A & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 1480A Ultra SCSI adapter",
+		ahc_apa1480_setup
+	},
+	{
+		ID_AHA_2940AU_0 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2940A Ultra SCSI adapter",
+		ahc_aic7860_setup
+	},
+	{
+		ID_AHA_2940AU_CN & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2940A/CN Ultra SCSI adapter",
+		ahc_aic7860_setup
+	},
+	{
+		ID_AHA_2930C_VAR & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2930C SCSI adapter (VAR)",
+		ahc_aic7860_setup
+	},
+	/* aic7870 based controllers */
+	{
+		ID_AHA_2940,
+		ID_ALL_MASK,
+		"Adaptec 2940 SCSI adapter",
+		ahc_aic7870_setup
+	},
+	{
+		ID_AHA_3940,
+		ID_ALL_MASK,
+		"Adaptec 3940 SCSI adapter",
+		ahc_aha394X_setup
+	},
+	{
+		ID_AHA_398X,
+		ID_ALL_MASK,
+		"Adaptec 398X SCSI RAID adapter",
+		ahc_aha398X_setup
+	},
+	{
+		ID_AHA_2944,
+		ID_ALL_MASK,
+		"Adaptec 2944 SCSI adapter",
+		ahc_aic7870_setup
+	},
+	{
+		ID_AHA_3944,
+		ID_ALL_MASK,
+		"Adaptec 3944 SCSI adapter",
+		ahc_aha394X_setup
+	},
+	{
+		ID_AHA_4944,
+		ID_ALL_MASK,
+		"Adaptec 4944 SCSI adapter",
+		ahc_aha494X_setup
+	},
+	/* aic7880 based controllers */
+	{
+		ID_AHA_2940U & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2940 Ultra SCSI adapter",
+		ahc_aic7880_setup
+	},
+	{
+		ID_AHA_3940U & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 3940 Ultra SCSI adapter",
+		ahc_aha394XU_setup
+	},
+	{
+		ID_AHA_2944U & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2944 Ultra SCSI adapter",
+		ahc_aic7880_setup
+	},
+	{
+		ID_AHA_3944U & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 3944 Ultra SCSI adapter",
+		ahc_aha394XU_setup
+	},
+	{
+		ID_AHA_398XU & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 398X Ultra SCSI RAID adapter",
+		ahc_aha398XU_setup
+	},
+	{
+		/*
+		 * XXX Don't know the slot numbers
+		 * so we can't identify channels
+		 */
+		ID_AHA_4944U & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 4944 Ultra SCSI adapter",
+		ahc_aic7880_setup
+	},
+	{
+		ID_AHA_2930U & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2930 Ultra SCSI adapter",
+		ahc_aic7880_setup
+	},
+	{
+		ID_AHA_2940U_PRO & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2940 Pro Ultra SCSI adapter",
+		ahc_aha2940Pro_setup
+	},
+	{
+		ID_AHA_2940U_CN & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec 2940/CN Ultra SCSI adapter",
+		ahc_aic7880_setup
+	},
+	/* Ignore all SISL (AAC on MB) based controllers. */
+	{
+		ID_9005_SISL_ID,
+		ID_9005_SISL_MASK,
+		NULL,
+		NULL
+	},
+	/* aic7890 based controllers */
+	{
+		ID_AHA_2930U2,
+		ID_ALL_MASK,
+		"Adaptec 2930 Ultra2 SCSI adapter",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AHA_2940U2B,
+		ID_ALL_MASK,
+		"Adaptec 2940B Ultra2 SCSI adapter",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AHA_2940U2_OEM,
+		ID_ALL_MASK,
+		"Adaptec 2940 Ultra2 SCSI adapter (OEM)",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AHA_2940U2,
+		ID_ALL_MASK,
+		"Adaptec 2940 Ultra2 SCSI adapter",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AHA_2950U2B,
+		ID_ALL_MASK,
+		"Adaptec 2950 Ultra2 SCSI adapter",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AIC7890_ARO,
+		ID_ALL_MASK,
+		"Adaptec aic7890/91 Ultra2 SCSI adapter (ARO)",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AAA_131U2,
+		ID_ALL_MASK,
+		"Adaptec AAA-131 Ultra2 RAID adapter",
+		ahc_aic7890_setup
+	},
+	/* aic7892 based controllers */
+	{
+		ID_AHA_29160,
+		ID_ALL_MASK,
+		"Adaptec 29160 Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AHA_29160_CPQ,
+		ID_ALL_MASK,
+		"Adaptec (Compaq OEM) 29160 Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AHA_29160N,
+		ID_ALL_MASK,
+		"Adaptec 29160N Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AHA_29160C,
+		ID_ALL_MASK,
+		"Adaptec 29160C Ultra160 SCSI adapter",
+		ahc_aha29160C_setup
+	},
+	{
+		ID_AHA_29160B,
+		ID_ALL_MASK,
+		"Adaptec 29160B Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AHA_19160B,
+		ID_ALL_MASK,
+		"Adaptec 19160B Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AIC7892_ARO,
+		ID_ALL_MASK,
+		"Adaptec aic7892 Ultra2 SCSI adapter (ARO)",
+		ahc_aic7892_setup
+	},
+	/* aic7895 based controllers */	
+	{
+		ID_AHA_2940U_DUAL,
+		ID_ALL_MASK,
+		"Adaptec 2940/DUAL Ultra SCSI adapter",
+		ahc_aic7895_setup
+	},
+	{
+		ID_AHA_3940AU,
+		ID_ALL_MASK,
+		"Adaptec 3940A Ultra SCSI adapter",
+		ahc_aic7895_setup
+	},
+	{
+		ID_AHA_3944AU,
+		ID_ALL_MASK,
+		"Adaptec 3944A Ultra SCSI adapter",
+		ahc_aic7895_setup
+	},
+	{
+		ID_AIC7895_ARO,
+		ID_AIC7895_ARO_MASK,
+		"Adaptec aic7895 Ultra SCSI adapter (ARO)",
+		ahc_aic7895_setup
+	},
+	/* aic7896/97 based controllers */	
+	{
+		ID_AHA_3950U2B_0,
+		ID_ALL_MASK,
+		"Adaptec 3950B Ultra2 SCSI adapter",
+		ahc_aic7896_setup
+	},
+	{
+		ID_AHA_3950U2B_1,
+		ID_ALL_MASK,
+		"Adaptec 3950B Ultra2 SCSI adapter",
+		ahc_aic7896_setup
+	},
+	{
+		ID_AHA_3950U2D_0,
+		ID_ALL_MASK,
+		"Adaptec 3950D Ultra2 SCSI adapter",
+		ahc_aic7896_setup
+	},
+	{
+		ID_AHA_3950U2D_1,
+		ID_ALL_MASK,
+		"Adaptec 3950D Ultra2 SCSI adapter",
+		ahc_aic7896_setup
+	},
+	{
+		ID_AIC7896_ARO,
+		ID_ALL_MASK,
+		"Adaptec aic7896/97 Ultra2 SCSI adapter (ARO)",
+		ahc_aic7896_setup
+	},
+	/* aic7899 based controllers */	
+	{
+		ID_AHA_3960D,
+		ID_ALL_MASK,
+		"Adaptec 3960D Ultra160 SCSI adapter",
+		ahc_aic7899_setup
+	},
+	{
+		ID_AHA_3960D_CPQ,
+		ID_ALL_MASK,
+		"Adaptec (Compaq OEM) 3960D Ultra160 SCSI adapter",
+		ahc_aic7899_setup
+	},
+	{
+		ID_AIC7899_ARO,
+		ID_ALL_MASK,
+		"Adaptec aic7899 Ultra160 SCSI adapter (ARO)",
+		ahc_aic7899_setup
+	},
+	/* Generic chip probes for devices we don't know 'exactly' */
+	{
+		ID_AIC7850 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7850 SCSI adapter",
+		ahc_aic785X_setup
+	},
+	{
+		ID_AIC7855 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7855 SCSI adapter",
+		ahc_aic785X_setup
+	},
+	{
+		ID_AIC7859 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7859 Ultra SCSI adapter",
+		ahc_aic7860_setup
+	},
+	{
+		ID_AIC7860 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7860 SCSI adapter",
+		ahc_aic7860_setup
+	},
+	{
+		ID_AIC7870 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7870 SCSI adapter",
+		ahc_aic7870_setup
+	},
+	{
+		ID_AIC7880 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7880 Ultra SCSI adapter",
+		ahc_aic7880_setup
+	},
+	{
+		ID_AIC7890 & ID_9005_GENERIC_MASK,
+		ID_9005_GENERIC_MASK,
+		"Adaptec aic7890/91 Ultra2 SCSI adapter",
+		ahc_aic7890_setup
+	},
+	{
+		ID_AIC7892 & ID_9005_GENERIC_MASK,
+		ID_9005_GENERIC_MASK,
+		"Adaptec aic7892 Ultra160 SCSI adapter",
+		ahc_aic7892_setup
+	},
+	{
+		ID_AIC7895 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7895 Ultra SCSI adapter",
+		ahc_aic7895_setup
+	},
+	{
+		ID_AIC7896 & ID_9005_GENERIC_MASK,
+		ID_9005_GENERIC_MASK,
+		"Adaptec aic7896/97 Ultra2 SCSI adapter",
+		ahc_aic7896_setup
+	},
+	{
+		ID_AIC7899 & ID_9005_GENERIC_MASK,
+		ID_9005_GENERIC_MASK,
+		"Adaptec aic7899 Ultra160 SCSI adapter",
+		ahc_aic7899_setup
+	},
+	{
+		ID_AIC7810 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7810 RAID memory controller",
+		ahc_raid_setup
+	},
+	{
+		ID_AIC7815 & ID_DEV_VENDOR_MASK,
+		ID_DEV_VENDOR_MASK,
+		"Adaptec aic7815 RAID memory controller",
+		ahc_raid_setup
+	}
+};
+
+const u_int ahc_num_pci_devs = NUM_ELEMENTS(ahc_pci_ident_table);
+		
+#define AHC_394X_SLOT_CHANNEL_A	4
+#define AHC_394X_SLOT_CHANNEL_B	5
+
+#define AHC_398X_SLOT_CHANNEL_A	4
+#define AHC_398X_SLOT_CHANNEL_B	8
+#define AHC_398X_SLOT_CHANNEL_C	12
+
+#define AHC_494X_SLOT_CHANNEL_A	4
+#define AHC_494X_SLOT_CHANNEL_B	5
+#define AHC_494X_SLOT_CHANNEL_C	6
+#define AHC_494X_SLOT_CHANNEL_D	7
+
+#define	DEVCONFIG		0x40
+#define		SCBSIZE32	0x00010000ul	/* aic789X only */
+#define		REXTVALID	0x00001000ul	/* ultra cards only */
+#define		MPORTMODE	0x00000400ul	/* aic7870 only */
+#define		RAMPSM		0x00000200ul	/* aic7870 only */
+#define		VOLSENSE	0x00000100ul
+#define		SCBRAMSEL	0x00000080ul
+#define		MRDCEN		0x00000040ul
+#define		EXTSCBTIME	0x00000020ul	/* aic7870 only */
+#define		EXTSCBPEN	0x00000010ul	/* aic7870 only */
+#define		BERREN		0x00000008ul
+#define		DACEN		0x00000004ul
+#define		STPWLEVEL	0x00000002ul
+#define		DIFACTNEGEN	0x00000001ul	/* aic7870 only */
+
+#define	CSIZE_LATTIME		0x0c
+#define		CACHESIZE	0x0000003ful	/* only 5 bits */
+#define		LATTIME		0x0000ff00ul
+
+typedef enum
+{
+	AHC_POWER_STATE_D0,
+	AHC_POWER_STATE_D1,
+	AHC_POWER_STATE_D2,
+	AHC_POWER_STATE_D3
+} ahc_power_state;
+
+static void ahc_power_state_change(struct ahc_softc *ahc,
+				   ahc_power_state new_state);
+static int ahc_ext_scbram_present(struct ahc_softc *ahc);
+static void ahc_scbram_config(struct ahc_softc *ahc, int enable,
+				  int pcheck, int fast, int large);
+static void ahc_probe_ext_scbram(struct ahc_softc *ahc);
+static void check_extport(struct ahc_softc *ahc, u_int *sxfrctl1);
+static void configure_termination(struct ahc_softc *ahc,
+				  struct seeprom_descriptor *sd,
+				  u_int adapter_control,
+	 			  u_int *sxfrctl1);
+
+static void ahc_new_term_detect(struct ahc_softc *ahc,
+				int *enableSEC_low,
+				int *enableSEC_high,
+				int *enablePRI_low,
+				int *enablePRI_high,
+				int *eeprom_present);
+static void aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+				 int *internal68_present,
+				 int *externalcable_present,
+				 int *eeprom_present);
+static void aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+				 int *externalcable_present,
+				 int *eeprom_present);
+static int acquire_seeprom(struct ahc_softc *ahc,
+			   struct seeprom_descriptor *sd);
+static void release_seeprom(struct seeprom_descriptor *sd);
+static void write_brdctl(struct ahc_softc *ahc, uint8_t value);
+static uint8_t read_brdctl(struct ahc_softc *ahc);
+
+struct ahc_pci_identity *
+ahc_find_pci_device(ahc_dev_softc_t pci)
+{
+	uint64_t  full_id;
+	uint16_t  device;
+	uint16_t  vendor;
+	uint16_t  subdevice;
+	uint16_t  subvendor;
+	struct	  ahc_pci_identity *entry;
+	u_int	  i;
+
+	vendor = ahc_pci_read_config(pci, PCIR_DEVVENDOR, /*bytes*/2);
+	device = ahc_pci_read_config(pci, PCIR_DEVICE, /*bytes*/2);
+	subvendor = ahc_pci_read_config(pci, PCIR_SUBVEND_0, /*bytes*/2);
+	subdevice = ahc_pci_read_config(pci, PCIR_SUBDEV_0, /*bytes*/2);
+	full_id = ahc_compose_id(device,
+				 vendor,
+				 subdevice,
+				 subvendor);
+
+	/* If the second function is not hooked up, ignore it. */
+	if (ahc_get_pci_function(pci) > 0
+	 && subvendor == 0x9005
+	 && SUBID_9005_TYPE_KNOWN(subdevice) != 0
+	 && SUBID_9005_MFUNCENB(subdevice) == 0)
+		return (NULL);
+
+	for (i = 0; i < ahc_num_pci_devs; i++) {
+		entry = &ahc_pci_ident_table[i];
+		if (entry->full_id == (full_id & entry->id_mask)) {
+			/* Honor exclusion entries. */
+			if (entry->name == NULL)
+				return (NULL);
+			return (entry);
+		}
+	}
+	return (NULL);
+}
+
+int
+ahc_pci_config(struct ahc_softc *ahc, struct ahc_pci_identity *entry)
+{
+	struct		 ahc_probe_config probe_config;
+	struct scb_data *shared_scb_data;
+	u_int		 command;
+	u_int		 our_id = 0;
+	u_int		 sxfrctl1;
+	u_int		 scsiseq;
+	u_int		 dscommand0;
+	int		 error;
+	uint8_t		 sblkctl;
+
+	shared_scb_data = NULL;
+	ahc_init_probe_config(&probe_config);
+	error = entry->setup(ahc->dev_softc, &probe_config);
+	if (error != 0)
+		return (error);
+	probe_config.chip |= AHC_PCI;
+	probe_config.description = entry->name;
+
+	error = ahc_pci_map_registers(ahc);
+	if (error != 0)
+		return (error);
+
+	ahc_power_state_change(ahc, AHC_POWER_STATE_D0);
+
+	/* Ensure busmastering is enabled */
+	command = ahc_pci_read_config(ahc->dev_softc, PCIR_COMMAND, /*bytes*/1);
+	command |= PCIM_CMD_BUSMASTEREN;
+	ahc_pci_write_config(ahc->dev_softc, PCIR_COMMAND, command, /*bytes*/1);
+
+	/* On all PCI adapters, we allow SCB paging */
+	probe_config.flags |= AHC_PAGESCBS;
+
+	error = ahc_softc_init(ahc, &probe_config);
+	if (error != 0)
+		return (error);
+
+	/* Remeber how the card was setup in case there is no SEEPROM */
+	if ((ahc_inb(ahc, HCNTRL) & POWRDN) == 0) {
+		pause_sequencer(ahc);
+		if ((ahc->features & AHC_ULTRA2) != 0)
+			our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+		else
+			our_id = ahc_inb(ahc, SCSIID) & OID;
+		sxfrctl1 = ahc_inb(ahc, SXFRCTL1) & STPWEN;
+		scsiseq = ahc_inb(ahc, SCSISEQ);
+	} else {
+		sxfrctl1 = STPWEN;
+		our_id = 7;
+		scsiseq = 0;
+	}
+
+	error = ahc_reset(ahc);
+	if (error != 0)
+		return (ENXIO);
+
+	if ((ahc->features & AHC_DT) != 0) {
+		u_int sfunct;
+
+		/* Perform ALT-Mode Setup */
+		sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+		ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+		ahc_outb(ahc, OPTIONMODE, OPTIONMODE_DEFAULTS);
+		ahc_outb(ahc, SFUNCT, sfunct);
+
+		/* Normal mode setup */
+		ahc_outb(ahc, CRCCONTROL1, CRCVALCHKEN|CRCENDCHKEN|CRCREQCHKEN
+					  |TARGCRCENDEN);
+	}
+
+	error = ahc_pci_map_int(ahc);
+	if (error != 0)
+		return (error);
+	dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+	dscommand0 |= MPARCKEN|CACHETHEN;
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+
+		/*
+		 * DPARCKEN doesn't work correctly on
+		 * some MBs so don't use it.
+		 */
+		dscommand0 &= ~DPARCKEN;
+	}
+
+	/*
+	 * Handle chips that must have cache line
+	 * streaming (dis/en)abled.
+	 */
+	if ((ahc->bugs & AHC_CACHETHEN_DIS_BUG) != 0)
+		dscommand0 |= CACHETHEN;
+
+	if ((ahc->bugs & AHC_CACHETHEN_BUG) != 0)
+		dscommand0 &= ~CACHETHEN;
+
+	ahc_outb(ahc, DSCOMMAND0, dscommand0);
+
+	ahc->pci_cachesize =
+	    ahc_pci_read_config(ahc->dev_softc, CSIZE_LATTIME,
+				/*bytes*/1) & CACHESIZE;
+	ahc->pci_cachesize *= 4;
+
+	/* See if we have a SEEPROM and perform auto-term */
+	check_extport(ahc, &sxfrctl1);
+
+	/*
+	 * Take the LED out of diagnostic mode
+	 */
+	sblkctl = ahc_inb(ahc, SBLKCTL);
+	ahc_outb(ahc, SBLKCTL, (sblkctl & ~(DIAGLEDEN|DIAGLEDON)));
+
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		ahc_outb(ahc, DFF_THRSH, RD_DFTHRSH_MAX|WR_DFTHRSH_MAX);
+	} else {
+		ahc_outb(ahc, DSPCISTATUS, DFTHRSH_100);
+	}
+
+	if (ahc->flags & AHC_USEDEFAULTS) {
+		/*
+		 * PCI Adapter default setup
+		 * Should only be used if the adapter does not have
+		 * a SEEPROM.
+		 */
+		/* See if someone else set us up already */
+		if (scsiseq != 0) {
+			printf("%s: Using left over BIOS settings\n",
+				ahc_name(ahc));
+			ahc->flags &= ~AHC_USEDEFAULTS;
+			ahc->flags |= AHC_BIOS_ENABLED;
+		} else {
+			/*
+			 * Assume only one connector and always turn
+			 * on termination.
+			 */
+ 			our_id = 0x07;
+			sxfrctl1 = STPWEN;
+		}
+		ahc_outb(ahc, SCSICONF, our_id|ENSPCHK|RESET_SCSI);
+
+		ahc->our_id = our_id;
+	}
+
+	/*
+	 * Take a look to see if we have external SRAM.
+	 * We currently do not attempt to use SRAM that is
+	 * shared among multiple controllers.
+	 */
+	ahc_probe_ext_scbram(ahc);
+
+	/*
+	 * Record our termination setting for the
+	 * generic initialization routine.
+	 */
+	if ((sxfrctl1 & STPWEN) != 0)
+		ahc->flags |= AHC_TERM_ENB_A;
+
+	/*
+	 * We cannot perform ULTRA speeds without
+	 * the presense of the external precision
+	 * resistor.
+	 */
+	if ((ahc->features & AHC_ULTRA) != 0) {
+		uint32_t devconfig;
+
+		devconfig = ahc_pci_read_config(ahc->dev_softc,
+						DEVCONFIG, /*bytes*/4);
+		if ((devconfig & REXTVALID) == 0)
+			ahc->flags |= AHC_ULTRA_DISABLED;
+	}
+
+	/* Core initialization */
+	error = ahc_init(ahc);
+	if (error != 0)
+		return (error);
+
+	/*
+	 * Link this softc in with all other ahc instances.
+	 */
+	ahc_softc_insert(ahc);
+
+	return (0);
+}
+
+static void
+ahc_power_state_change(struct ahc_softc *ahc, ahc_power_state new_state)
+{
+	uint32_t cap;
+	u_int cap_offset;
+
+	/*
+	 * Traverse the capability list looking for
+	 * the power management capability.
+	 */
+	cap = 0;
+	cap_offset = ahc_pci_read_config(ahc->dev_softc,
+					 PCIR_CAP_PTR, /*bytes*/1);
+	while (cap_offset != 0) {
+
+		cap = ahc_pci_read_config(ahc->dev_softc,
+					  cap_offset, /*bytes*/4);
+		if ((cap & 0xFF) == 1
+		 && ((cap >> 16) & 0x3) > 0) {
+			uint32_t pm_control;
+
+			pm_control = ahc_pci_read_config(ahc->dev_softc,
+							 cap_offset + 4,
+							 /*bytes*/4);
+			pm_control &= ~0x3;
+			pm_control |= new_state;
+			ahc_pci_write_config(ahc->dev_softc,
+					     cap_offset + 4,
+					     pm_control, /*bytes*/2);
+			break;
+		}
+		cap_offset = (cap >> 8) & 0xFF;
+	}
+}
+
+/*
+ * Test for the presense of external sram in an
+ * "unshared" configuration.
+ */
+static int
+ahc_ext_scbram_present(struct ahc_softc *ahc)
+{
+	u_int chip;
+	int ramps;
+	int single_user;
+	uint32_t devconfig;
+
+	chip = ahc->chip & AHC_CHIPID_MASK;
+	devconfig = ahc_pci_read_config(ahc->dev_softc,
+					DEVCONFIG, /*bytes*/4);
+	single_user = (devconfig & MPORTMODE) != 0;
+
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		ramps = (ahc_inb(ahc, DSCOMMAND0) & RAMPS) != 0;
+	else if (chip >= AHC_AIC7870)
+		ramps = (devconfig & RAMPSM) != 0;
+	else
+		ramps = 0;
+
+	if (ramps && single_user)
+		return (1);
+	return (0);
+}
+
+/*
+ * Enable external scbram.
+ */
+static void
+ahc_scbram_config(struct ahc_softc *ahc, int enable, int pcheck,
+		  int fast, int large)
+{
+	uint32_t devconfig;
+
+	if (ahc->features & AHC_MULTI_FUNC) {
+		/*
+		 * Set the SCB Base addr (highest address bit)
+		 * depending on which channel we are.
+		 */
+		ahc_outb(ahc, SCBBADDR, ahc_get_pci_function(ahc->dev_softc));
+	}
+
+	devconfig = ahc_pci_read_config(ahc->dev_softc, DEVCONFIG, /*bytes*/4);
+	if ((ahc->features & AHC_ULTRA2) != 0) {
+		u_int dscommand0;
+
+		dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+		if (enable)
+			dscommand0 &= ~INTSCBRAMSEL;
+		else
+			dscommand0 |= INTSCBRAMSEL;
+		if (large)
+			dscommand0 &= ~USCBSIZE32;
+		else
+			dscommand0 |= USCBSIZE32;
+		ahc_outb(ahc, DSCOMMAND0, dscommand0);
+	} else {
+		if (fast)
+			devconfig &= ~EXTSCBTIME;
+		else
+			devconfig |= EXTSCBTIME;
+		if (enable)
+			devconfig &= ~SCBRAMSEL;
+		else
+			devconfig |= SCBRAMSEL;
+		if (large)
+			devconfig &= ~SCBSIZE32;
+		else
+			devconfig |= SCBSIZE32;
+	}
+	if (pcheck)
+		devconfig |= EXTSCBPEN;
+	else
+		devconfig &= ~EXTSCBPEN;
+
+	ahc_pci_write_config(ahc->dev_softc, DEVCONFIG, devconfig, /*bytes*/4);
+}
+
+/*
+ * Take a look to see if we have external SRAM.
+ * We currently do not attempt to use SRAM that is
+ * shared among multiple controllers.
+ */
+static void
+ahc_probe_ext_scbram(struct ahc_softc *ahc)
+{
+	int num_scbs;
+	int test_num_scbs;
+	int enable;
+	int pcheck;
+	int fast;
+	int large;
+
+	enable = FALSE;
+	pcheck = FALSE;
+	fast = FALSE;
+	large = FALSE;
+	num_scbs = 0;
+	
+	if (ahc_ext_scbram_present(ahc) == 0)
+		goto done;
+
+	/*
+	 * Probe for the best parameters to use.
+	 */
+	ahc_scbram_config(ahc, /*enable*/TRUE, pcheck, fast, large);
+	num_scbs = ahc_probe_scbs(ahc);
+	if (num_scbs == 0) {
+		/* The SRAM wasn't really present. */
+		goto done;
+	}
+	enable = TRUE;
+
+	/*
+	 * Clear any outstanding parity error
+	 * and ensure that parity error reporting
+	 * is enabled.
+	 */
+	ahc_outb(ahc, SEQCTL, 0);
+	ahc_outb(ahc, CLRINT, CLRPARERR);
+	ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+
+	/* Now see if we can do parity */
+	ahc_scbram_config(ahc, enable, /*pcheck*/TRUE, fast, large);
+	num_scbs = ahc_probe_scbs(ahc);
+	if ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
+	 || (ahc_inb(ahc, ERROR) & MPARERR) == 0)
+		pcheck = TRUE;
+
+	/* Clear any resulting parity error */
+	ahc_outb(ahc, CLRINT, CLRPARERR);
+	ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+
+	/* Now see if we can do fast timing */
+	ahc_scbram_config(ahc, enable, pcheck, /*fast*/TRUE, large);
+	test_num_scbs = ahc_probe_scbs(ahc);
+	if (test_num_scbs == num_scbs
+	 && ((ahc_inb(ahc, INTSTAT) & BRKADRINT) == 0
+	  || (ahc_inb(ahc, ERROR) & MPARERR) == 0))
+		fast = TRUE;
+
+	/*
+	 * See if we can use large SCBs and still maintain
+	 * the same overall count of SCBs.
+	 */
+	if ((ahc->features & AHC_LARGE_SCBS) != 0) {
+		ahc_scbram_config(ahc, enable, pcheck, fast, /*large*/TRUE);
+		test_num_scbs = ahc_probe_scbs(ahc);
+		if (test_num_scbs >= num_scbs) {
+			large = TRUE;
+			num_scbs = test_num_scbs;
+	 		if (num_scbs >= 64) {
+				/*
+				 * We have enough space to move the
+				 * "busy targets table" into SCB space
+				 * and make it qualify all the way to the
+				 * lun level.
+				 */
+				ahc->flags |= AHC_SCB_BTT;
+			}
+		}
+	}
+done:
+	/*
+	 * Disable parity error reporting until we
+	 * can load instruction ram.
+	 */
+	ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS);
+	/* Clear any latched parity error */
+	ahc_outb(ahc, CLRINT, CLRPARERR);
+	ahc_outb(ahc, CLRINT, CLRBRKADRINT);
+	if (bootverbose && enable) {
+		printf("%s: External SRAM, %s access%s, %dbytes/SCB\n",
+		       ahc_name(ahc), fast ? "fast" : "slow", 
+		       pcheck ? ", parity checking enabled" : "",
+		       large ? 64 : 32);
+	}
+	ahc_scbram_config(ahc, enable, pcheck, fast, large);
+}
+
+/*
+ * Check the external port logic for a serial eeprom
+ * and termination/cable detection contrls.
+ */
+static void
+check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
+{
+	struct	seeprom_descriptor sd;
+	struct	seeprom_config sc;
+	u_int	scsi_conf;
+	u_int	adapter_control;
+	int	have_seeprom;
+	int	have_autoterm;
+
+	sd.sd_ahc = ahc;
+	sd.sd_control_offset = SEECTL;		
+	sd.sd_status_offset = SEECTL;		
+	sd.sd_dataout_offset = SEECTL;		
+
+	/*
+	 * For some multi-channel devices, the c46 is simply too
+	 * small to work.  For the other controller types, we can
+	 * get our information from either SEEPROM type.  Set the
+	 * type to start our probe with accordingly.
+	 */
+	if (ahc->flags & AHC_LARGE_SEEPROM)
+		sd.sd_chip = C56_66;
+	else
+		sd.sd_chip = C46;
+
+	sd.sd_MS = SEEMS;
+	sd.sd_RDY = SEERDY;
+	sd.sd_CS = SEECS;
+	sd.sd_CK = SEECK;
+	sd.sd_DO = SEEDO;
+	sd.sd_DI = SEEDI;
+
+	have_seeprom = acquire_seeprom(ahc, &sd);
+	if (have_seeprom) {
+
+		if (bootverbose) 
+			printf("%s: Reading SEEPROM...", ahc_name(ahc));
+
+		for (;;) {
+			u_int start_addr;
+
+			start_addr = 32 * (ahc->channel - 'A');
+
+			have_seeprom = read_seeprom(&sd, (uint16_t *)&sc,
+						    start_addr, sizeof(sc)/2);
+
+			if (have_seeprom)
+				have_seeprom = verify_cksum(&sc);
+
+			if (have_seeprom != 0 || sd.sd_chip == C56_66) {
+				if (bootverbose) {
+					if (have_seeprom == 0)
+						printf ("checksum error\n");
+					else
+						printf ("done.\n");
+				}
+				break;
+			}
+			sd.sd_chip = C56_66;
+		}
+	}
+
+#if 0
+	if (!have_seeprom) {
+		/*
+		 * Pull scratch ram settings and treat them as
+		 * if they are the contents of an seeprom if
+		 * the 'ADPT' signature is found in SCB2.
+		 */
+		ahc_outb(ahc, SCBPTR, 2);
+		if (ahc_inb(ahc, SCB_BASE) == 'A'
+		 && ahc_inb(ahc, SCB_BASE + 1) == 'D'
+		 && ahc_inb(ahc, SCB_BASE + 2) == 'P'
+		 && ahc_inb(ahc, SCB_BASE + 3) == 'T') {
+			uint8_t *sc_bytes;
+			int	  i;
+
+			sc_bytes = (uint8_t *)&sc;
+			for (i = 0; i < 64; i++)
+				sc_bytes[i] = ahc_inb(ahc, TARG_SCSIRATE + i);
+			/* Byte 0x1c is stored in byte 4 of SCB2 */
+			sc_bytes[0x1c] = ahc_inb(ahc, SCB_BASE + 4);
+			have_seeprom = verify_cksum(&sc);
+		}
+	}
+#endif
+
+	if (!have_seeprom) {
+		if (bootverbose)
+			printf("%s: No SEEPROM available.\n", ahc_name(ahc));
+		ahc->flags |= AHC_USEDEFAULTS;
+	} else {
+		/*
+		 * Put the data we've collected down into SRAM
+		 * where ahc_init will find it.
+		 */
+		int i;
+		int max_targ = sc.max_targets & CFMAXTARG;
+		uint16_t discenable;
+		uint16_t ultraenb;
+
+		discenable = 0;
+		ultraenb = 0;
+		if ((sc.adapter_control & CFULTRAEN) != 0) {
+			/*
+			 * Determine if this adapter has a "newstyle"
+			 * SEEPROM format.
+			 */
+			for (i = 0; i < max_targ; i++) {
+				if ((sc.device_flags[i] & CFSYNCHISULTRA) != 0){
+					ahc->flags |= AHC_NEWEEPROM_FMT;
+					break;
+				}
+			}
+		}
+
+		for (i = 0; i < max_targ; i++) {
+			u_int     scsirate;
+			uint16_t target_mask;
+
+			target_mask = 0x01 << i;
+			if (sc.device_flags[i] & CFDISC)
+				discenable |= target_mask;
+			if ((ahc->flags & AHC_NEWEEPROM_FMT) != 0) {
+				if ((sc.device_flags[i] & CFSYNCHISULTRA) != 0)
+					ultraenb |= target_mask;
+			} else if ((sc.adapter_control & CFULTRAEN) != 0) {
+				ultraenb |= target_mask;
+			}
+			if ((sc.device_flags[i] & CFXFER) == 0x04
+			 && (ultraenb & target_mask) != 0) {
+				/* Treat 10MHz as a non-ultra speed */
+				sc.device_flags[i] &= ~CFXFER;
+			 	ultraenb &= ~target_mask;
+			}
+			if ((ahc->features & AHC_ULTRA2) != 0) {
+				u_int offset;
+
+				if (sc.device_flags[i] & CFSYNCH)
+					offset = MAX_OFFSET_ULTRA2;
+				else 
+					offset = 0;
+				ahc_outb(ahc, TARG_OFFSET + i, offset);
+
+				/*
+				 * The ultra enable bits contain the
+				 * high bit of the ultra2 sync rate
+				 * field.
+				 */
+				scsirate = (sc.device_flags[i] & CFXFER)
+					 | ((ultraenb & target_mask)
+					    ? 0x8 : 0x0);
+				if (sc.device_flags[i] & CFWIDEB)
+					scsirate |= WIDEXFER;
+			} else {
+				scsirate = (sc.device_flags[i] & CFXFER) << 4;
+				if (sc.device_flags[i] & CFSYNCH)
+					scsirate |= SOFS;
+				if (sc.device_flags[i] & CFWIDEB)
+					scsirate |= WIDEXFER;
+			}
+			ahc_outb(ahc, TARG_SCSIRATE + i, scsirate);
+		}
+		ahc->our_id = sc.brtime_id & CFSCSIID;
+
+		scsi_conf = (ahc->our_id & 0x7);
+		if (sc.adapter_control & CFSPARITY)
+			scsi_conf |= ENSPCHK;
+		if (sc.adapter_control & CFRESETB)
+			scsi_conf |= RESET_SCSI;
+
+		if ((sc.adapter_control & CFCHNLBPRIMARY) != 0
+		 && (ahc->features & AHC_MULTI_FUNC) != 0)
+			ahc->flags |= AHC_CHANNEL_B_PRIMARY;
+
+		if (sc.bios_control & CFEXTEND)
+			ahc->flags |= AHC_EXTENDED_TRANS_A;
+
+		if (sc.bios_control & CFBIOSEN)
+			ahc->flags |= AHC_BIOS_ENABLED;
+		if (ahc->features & AHC_ULTRA
+		 && (ahc->flags & AHC_NEWEEPROM_FMT) == 0) {
+			/* Should we enable Ultra mode? */
+			if (!(sc.adapter_control & CFULTRAEN))
+				/* Treat us as a non-ultra card */
+				ultraenb = 0;
+		}
+
+		if (sc.signature == CFSIGNATURE) {
+			uint32_t devconfig;
+
+			/* Honor the STPWLEVEL settings */
+			devconfig = ahc_pci_read_config(ahc->dev_softc,
+							DEVCONFIG, /*bytes*/4);
+			devconfig &= ~STPWLEVEL;
+			if ((sc.bios_control & CFSTPWLEVEL) != 0)
+				devconfig |= STPWLEVEL;
+			ahc_pci_write_config(ahc->dev_softc, DEVCONFIG,
+					     devconfig, /*bytes*/4);
+		}
+		/* Set SCSICONF info */
+		ahc_outb(ahc, SCSICONF, scsi_conf);
+		ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
+		ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
+		ahc_outb(ahc, ULTRA_ENB, ultraenb & 0xff);
+		ahc_outb(ahc, ULTRA_ENB + 1, (ultraenb >> 8) & 0xff);
+	}
+
+	/*
+	 * Cards that have the external logic necessary to talk to
+	 * a SEEPROM, are almost certain to have the remaining logic
+	 * necessary for auto-termination control.  This assumption
+	 * hasn't failed yet...
+	 */
+	have_autoterm = have_seeprom;
+	if (have_seeprom)
+		adapter_control = sc.adapter_control;
+	else
+		adapter_control = CFAUTOTERM;
+
+	/*
+	 * Some low-cost chips have SEEPROM and auto-term control built
+	 * in, instead of using a GAL.  They can tell us directly
+	 * if the termination logic is enabled.
+	 */
+	if ((ahc->features & AHC_SPIOCAP) != 0) {
+		if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) != 0)
+			have_autoterm = TRUE;
+		else
+			have_autoterm = FALSE;
+	}
+
+	if (have_autoterm)
+		configure_termination(ahc, &sd, adapter_control, sxfrctl1);
+
+	release_seeprom(&sd);
+}
+
+static void
+configure_termination(struct ahc_softc *ahc,
+		      struct seeprom_descriptor *sd,
+		      u_int adapter_control,
+		      u_int *sxfrctl1)
+{
+	uint8_t brddat;
+	
+	brddat = 0;
+
+	/*
+	 * Update the settings in sxfrctl1 to match the
+	 * termination settings 
+	 */
+	*sxfrctl1 = 0;
+	
+	/*
+	 * SEECS must be on for the GALS to latch
+	 * the data properly.  Be sure to leave MS
+	 * on or we will release the seeprom.
+	 */
+	SEEPROM_OUTB(sd, sd->sd_MS | sd->sd_CS);
+	if ((adapter_control & CFAUTOTERM) != 0
+	 || (ahc->features & AHC_NEW_TERMCTL) != 0) {
+		int internal50_present;
+		int internal68_present;
+		int externalcable_present;
+		int eeprom_present;
+		int enableSEC_low;
+		int enableSEC_high;
+		int enablePRI_low;
+		int enablePRI_high;
+		int sum;
+
+		enableSEC_low = 0;
+		enableSEC_high = 0;
+		enablePRI_low = 0;
+		enablePRI_high = 0;
+		if ((ahc->features & AHC_NEW_TERMCTL) != 0) {
+			ahc_new_term_detect(ahc, &enableSEC_low,
+					       &enableSEC_high,
+					       &enablePRI_low,
+					       &enablePRI_high,
+					       &eeprom_present);
+			if ((adapter_control & CFSEAUTOTERM) == 0) {
+				if (bootverbose)
+					printf("%s: Manual SE Termination\n",
+					       ahc_name(ahc));
+				enableSEC_low = (adapter_control & CFSELOWTERM);
+				enableSEC_high =
+				    (adapter_control & CFSEHIGHTERM);
+			}
+			if ((adapter_control & CFAUTOTERM) == 0) {
+				if (bootverbose)
+					printf("%s: Manual LVD Termination\n",
+					       ahc_name(ahc));
+				enablePRI_low = (adapter_control & CFSTERM);
+				enablePRI_high = (adapter_control & CFWSTERM);
+			}
+			/* Make the table calculations below happy */
+			internal50_present = 0;
+			internal68_present = 1;
+			externalcable_present = 1;
+		} else if ((ahc->features & AHC_SPIOCAP) != 0) {
+			aic785X_cable_detect(ahc, &internal50_present,
+					     &externalcable_present,
+					     &eeprom_present);
+		} else {
+			aic787X_cable_detect(ahc, &internal50_present,
+					     &internal68_present,
+					     &externalcable_present,
+					     &eeprom_present);
+		}
+
+		if ((ahc->features & AHC_WIDE) == 0)
+			internal68_present = 0;
+
+		if (bootverbose
+		 && (ahc->features & AHC_ULTRA2) == 0) {
+			printf("%s: internal 50 cable %s present",
+			       ahc_name(ahc),
+			       internal50_present ? "is":"not");
+
+			if ((ahc->features & AHC_WIDE) != 0)
+				printf(", internal 68 cable %s present",
+				       internal68_present ? "is":"not");
+			printf("\n%s: external cable %s present\n",
+			       ahc_name(ahc),
+			       externalcable_present ? "is":"not");
+		}
+		if (bootverbose)
+			printf("%s: BIOS eeprom %s present\n",
+			       ahc_name(ahc), eeprom_present ? "is" : "not");
+
+		if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) {
+			/*
+			 * The 50 pin connector is a separate bus,
+			 * so force it to always be terminated.
+			 * In the future, perform current sensing
+			 * to determine if we are in the middle of
+			 * a properly terminated bus.
+			 */
+			internal50_present = 0;
+		}
+
+		/*
+		 * Now set the termination based on what
+		 * we found.
+		 * Flash Enable = BRDDAT7
+		 * Secondary High Term Enable = BRDDAT6
+		 * Secondary Low Term Enable = BRDDAT5 (7890)
+		 * Primary High Term Enable = BRDDAT4 (7890)
+		 */
+		if ((ahc->features & AHC_ULTRA2) == 0
+		 && (internal50_present != 0)
+		 && (internal68_present != 0)
+		 && (externalcable_present != 0)) {
+			printf("%s: Illegal cable configuration!!. "
+			       "Only two connectors on the "
+			       "adapter may be used at a "
+			       "time!\n", ahc_name(ahc));
+		}
+
+		if ((ahc->features & AHC_WIDE) != 0
+		 && ((externalcable_present == 0)
+		  || (internal68_present == 0)
+		  || (enableSEC_high != 0))) {
+			brddat |= BRDDAT6;
+			if (bootverbose) {
+				if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
+					printf("%s: 68 pin termination "
+					       "Enabled\n", ahc_name(ahc));
+				else
+					printf("%s: %sHigh byte termination "
+					       "Enabled\n", ahc_name(ahc),
+					       enableSEC_high ? "Secondary "
+							      : "");
+			}
+		}
+
+		sum = internal50_present + internal68_present
+		    + externalcable_present;
+		if (sum < 2 || (enableSEC_low != 0)) {
+			if ((ahc->features & AHC_ULTRA2) != 0)
+				brddat |= BRDDAT5;
+			else
+				*sxfrctl1 |= STPWEN;
+			if (bootverbose) {
+				if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
+					printf("%s: 50 pin termination "
+					       "Enabled\n", ahc_name(ahc));
+				else
+					printf("%s: %sLow byte termination "
+					       "Enabled\n", ahc_name(ahc),
+					       enableSEC_low ? "Secondary "
+							     : "");
+			}
+		}
+
+		if (enablePRI_low != 0) {
+			*sxfrctl1 |= STPWEN;
+			if (bootverbose)
+				printf("%s: Primary Low Byte termination "
+				       "Enabled\n", ahc_name(ahc));
+		}
+
+		/*
+		 * Setup STPWEN before setting up the rest of
+		 * the termination per the tech note on the U160 cards.
+		 */
+		ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
+
+		if (enablePRI_high != 0) {
+			brddat |= BRDDAT4;
+			if (bootverbose)
+				printf("%s: Primary High Byte "
+				       "termination Enabled\n",
+				       ahc_name(ahc));
+		}
+		
+		write_brdctl(ahc, brddat);
+
+	} else {
+		if ((adapter_control & CFSTERM) != 0) {
+			*sxfrctl1 |= STPWEN;
+
+			if (bootverbose)
+				printf("%s: %sLow byte termination Enabled\n",
+				       ahc_name(ahc),
+				       (ahc->features & AHC_ULTRA2) ? "Primary "
+								    : "");
+		}
+
+		if ((adapter_control & CFWSTERM) != 0
+		 && (ahc->features & AHC_WIDE) != 0) {
+			brddat |= BRDDAT6;
+			if (bootverbose)
+				printf("%s: %sHigh byte termination Enabled\n",
+				       ahc_name(ahc),
+				       (ahc->features & AHC_ULTRA2)
+				     ? "Secondary " : "");
+		}
+
+		/*
+		 * Setup STPWEN before setting up the rest of
+		 * the termination per the tech note on the U160 cards.
+		 */
+		ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
+
+		if ((ahc->features & AHC_WIDE) != 0)
+			write_brdctl(ahc, brddat);
+	}
+	SEEPROM_OUTB(sd, sd->sd_MS); /* Clear CS */
+}
+
+static void
+ahc_new_term_detect(struct ahc_softc *ahc, int *enableSEC_low,
+		    int *enableSEC_high, int *enablePRI_low,
+		    int *enablePRI_high, int *eeprom_present)
+{
+	uint8_t brdctl;
+
+	/*
+	 * BRDDAT7 = Eeprom
+	 * BRDDAT6 = Enable Secondary High Byte termination
+	 * BRDDAT5 = Enable Secondary Low Byte termination
+	 * BRDDAT4 = Enable Primary high byte termination
+	 * BRDDAT3 = Enable Primary low byte termination
+	 */
+	brdctl = read_brdctl(ahc);
+	*eeprom_present = brdctl & BRDDAT7;
+	*enableSEC_high = (brdctl & BRDDAT6);
+	*enableSEC_low = (brdctl & BRDDAT5);
+	*enablePRI_high = (brdctl & BRDDAT4);
+	*enablePRI_low = (brdctl & BRDDAT3);
+}
+
+static void
+aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+		     int *internal68_present, int *externalcable_present,
+		     int *eeprom_present)
+{
+	uint8_t brdctl;
+
+	/*
+	 * 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.
+	 */
+	write_brdctl(ahc, 0);
+
+	/*
+	 * Now read the state of the internal
+	 * connectors.  BRDDAT6 is INT50 and
+	 * BRDDAT7 is INT68.
+	 */
+	brdctl = read_brdctl(ahc);
+	*internal50_present = (brdctl & BRDDAT6) ? 0 : 1;
+	*internal68_present = (brdctl & BRDDAT7) ? 0 : 1;
+
+	/*
+	 * Set the rom bank to 1 and determine
+	 * the other signals.
+	 */
+	write_brdctl(ahc, BRDDAT5);
+
+	/*
+	 * Now read the state of the external
+	 * connectors.  BRDDAT6 is EXT68 and
+	 * BRDDAT7 is EPROMPS.
+	 */
+	brdctl = read_brdctl(ahc);
+	*externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;
+	*eeprom_present = (brdctl & BRDDAT7) ? 1 : 0;
+}
+
+static void
+aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
+		     int *externalcable_present, int *eeprom_present)
+{
+	uint8_t brdctl;
+
+	ahc_outb(ahc, BRDCTL, BRDRW|BRDCS);
+	ahc_outb(ahc, BRDCTL, 0);
+	brdctl = ahc_inb(ahc, BRDCTL);
+	*internal50_present = (brdctl & BRDDAT5) ? 0 : 1;
+	*externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;
+
+	*eeprom_present = (ahc_inb(ahc, SPIOCAP) & EEPROM) ? 1 : 0;
+}
+	
+static int
+acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd)
+{
+	int wait;
+
+	if ((ahc->features & AHC_SPIOCAP) != 0
+	 && (ahc_inb(ahc, SPIOCAP) & SEEPROM) == 0)
+		return (0);
+
+	/*
+	 * 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 chip reset, there
+	 * should be no contention.
+	 */
+	SEEPROM_OUTB(sd, sd->sd_MS);
+	wait = 1000;  /* 1 second timeout in msec */
+	while (--wait && ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0)) {
+		ahc_delay(1000);  /* delay 1 msec */
+	}
+	if ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0) {
+		SEEPROM_OUTB(sd, 0); 
+		return (0);
+	}
+	return(1);
+}
+
+static void
+release_seeprom(struct seeprom_descriptor *sd)
+{
+	/* Release access to the memory port and the serial EEPROM. */
+	SEEPROM_OUTB(sd, 0);
+}
+
+static void
+write_brdctl(struct ahc_softc *ahc, uint8_t value)
+{
+	uint8_t brdctl;
+
+	if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+		brdctl = BRDSTB;
+	 	if (ahc->channel == 'B')
+			brdctl |= BRDCS;
+	} else if ((ahc->features & AHC_ULTRA2) != 0) {
+		brdctl = 0;
+	} else {
+		brdctl = BRDSTB|BRDCS;
+	}
+	ahc_outb(ahc, BRDCTL, brdctl);
+	ahc_flush_device_writes(ahc);
+	brdctl |= value;
+	ahc_outb(ahc, BRDCTL, brdctl);
+	ahc_flush_device_writes(ahc);
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		brdctl |= BRDSTB_ULTRA2;
+	else
+		brdctl &= ~BRDSTB;
+	ahc_outb(ahc, BRDCTL, brdctl);
+	ahc_flush_device_writes(ahc);
+	if ((ahc->features & AHC_ULTRA2) != 0)
+		brdctl = 0;
+	else
+		brdctl &= ~BRDCS;
+	ahc_outb(ahc, BRDCTL, brdctl);
+}
+
+static uint8_t
+read_brdctl(ahc)
+	struct 	ahc_softc *ahc;
+{
+	uint8_t brdctl;
+	uint8_t value;
+
+	if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+		brdctl = BRDRW;
+	 	if (ahc->channel == 'B')
+			brdctl |= BRDCS;
+	} else if ((ahc->features & AHC_ULTRA2) != 0) {
+		brdctl = BRDRW_ULTRA2;
+	} else {
+		brdctl = BRDRW|BRDCS;
+	}
+	ahc_outb(ahc, BRDCTL, brdctl);
+	ahc_flush_device_writes(ahc);
+	value = ahc_inb(ahc, BRDCTL);
+	ahc_outb(ahc, BRDCTL, 0);
+	return (value);
+}
+
+#define	DPE	0x80
+#define SSE	0x40
+#define	RMA	0x20
+#define	RTA	0x10
+#define STA	0x08
+#define DPR	0x01
+
+void
+ahc_pci_intr(struct ahc_softc *ahc)
+{
+	u_int error;
+	u_int status1;
+
+	error = ahc_inb(ahc, ERROR);
+	if ((error & PCIERRSTAT) == 0)
+		return;
+
+	status1 = ahc_pci_read_config(ahc->dev_softc,
+				      PCIR_STATUS + 1, /*bytes*/1);
+
+	printf("%s: PCI error Interrupt at seqaddr = 0x%x\n",
+	      ahc_name(ahc),
+	      ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+
+	if (status1 & DPE) {
+		printf("%s: Data Parity Error Detected during address "
+		       "or write data phase\n", ahc_name(ahc));
+	}
+	if (status1 & SSE) {
+		printf("%s: Signal System Error Detected\n", ahc_name(ahc));
+	}
+	if (status1 & RMA) {
+		printf("%s: Received a Master Abort\n", ahc_name(ahc));
+	}
+	if (status1 & RTA) {
+		printf("%s: Received a Target Abort\n", ahc_name(ahc));
+	}
+	if (status1 & STA) {
+		printf("%s: Signaled a Target Abort\n", ahc_name(ahc));
+	}
+	if (status1 & DPR) {
+		printf("%s: Data Parity Error has been reported via PERR#\n",
+		       ahc_name(ahc));
+	}
+	if ((status1 & (DPE|SSE|RMA|RTA|STA|DPR)) == 0) {
+		printf("%s: Latched PCIERR interrupt with "
+		       "no status bits set\n", ahc_name(ahc)); 
+	}
+	ahc_pci_write_config(ahc->dev_softc, PCIR_STATUS + 1,
+			     status1, /*bytes*/1);
+
+	if (status1 & (DPR|RMA|RTA)) {
+		ahc_outb(ahc, CLRINT, CLRPARERR);
+	}
+
+	unpause_sequencer(ahc);
+}
+
+static int
+ahc_aic785X_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	uint8_t rev;
+
+	probe_config->channel = 'A';
+	probe_config->chip = AHC_AIC7850;
+	probe_config->features = AHC_AIC7850_FE;
+	probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG
+			   |  AHC_PCI_MWI_BUG;
+	rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+	if (rev >= 1)
+		probe_config->bugs |= AHC_PCI_2_1_RETRY_BUG;
+	return (0);
+}
+
+static int
+ahc_aic7860_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	uint8_t rev;
+
+	probe_config->channel = 'A';
+	probe_config->chip = AHC_AIC7860;
+	probe_config->features = AHC_AIC7860_FE;
+	probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG
+			   |  AHC_PCI_MWI_BUG;
+	rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+	if (rev >= 1)
+		probe_config->bugs |= AHC_PCI_2_1_RETRY_BUG;
+	return (0);
+}
+
+static int
+ahc_apa1480_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	int error;
+
+	error = ahc_aic7860_setup(pci, probe_config);
+	if (error != 0)
+		return (error);
+	probe_config->features |= AHC_REMOVABLE;
+	return (0);
+}
+
+static int
+ahc_aic7870_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	probe_config->channel = 'A';
+	probe_config->chip = AHC_AIC7870;
+	probe_config->features = AHC_AIC7870_FE;
+	probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_CACHETHEN_BUG
+			   |  AHC_PCI_MWI_BUG;
+	return (0);
+}
+
+static int
+ahc_aha394X_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	int error;
+
+	error = ahc_aic7870_setup(pci, probe_config);
+	if (error == 0)
+		error = ahc_aha394XX_setup(pci, probe_config);
+	return (error);
+}
+
+static int
+ahc_aha398X_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	int error;
+
+	error = ahc_aic7870_setup(pci, probe_config);
+	if (error == 0)
+		error = ahc_aha398XX_setup(pci, probe_config);
+	return (error);
+}
+
+static int
+ahc_aha494X_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	int error;
+
+	error = ahc_aic7870_setup(pci, probe_config);
+	if (error == 0)
+		error = ahc_aha494XX_setup(pci, probe_config);
+	return (error);
+}
+
+static int
+ahc_aic7880_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	uint8_t rev;
+
+	probe_config->channel = 'A';
+	probe_config->chip = AHC_AIC7880;
+	probe_config->features = AHC_AIC7880_FE;
+	probe_config->bugs |= AHC_TMODE_WIDEODD_BUG;
+	rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+	if (rev >= 1) {
+		probe_config->bugs |= AHC_PCI_2_1_RETRY_BUG;
+	} else {
+		probe_config->bugs |= AHC_CACHETHEN_BUG|AHC_PCI_MWI_BUG;
+	}
+	return (0);
+}
+
+static int
+ahc_aha2940Pro_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	int error;
+
+	probe_config->flags |= AHC_INT50_SPEEDFLEX;
+	error = ahc_aic7880_setup(pci, probe_config);
+	return (0);
+}
+
+static int
+ahc_aha394XU_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	int error;
+
+	error = ahc_aic7880_setup(pci, probe_config);
+	if (error == 0)
+		error = ahc_aha394XX_setup(pci, probe_config);
+	return (error);
+}
+
+static int
+ahc_aha398XU_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	int error;
+
+	error = ahc_aic7880_setup(pci, probe_config);
+	if (error == 0)
+		error = ahc_aha398XX_setup(pci, probe_config);
+	return (error);
+}
+
+static int
+ahc_aic7890_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	uint8_t rev;
+
+	probe_config->channel = 'A';
+	probe_config->chip = AHC_AIC7890;
+	probe_config->features = AHC_AIC7890_FE;
+	probe_config->flags |= AHC_NEWEEPROM_FMT;
+	rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+	if (rev == 0)
+		probe_config->bugs |= AHC_AUTOFLUSH_BUG|AHC_CACHETHEN_BUG;
+	return (0);
+}
+
+static int
+ahc_aic7892_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	probe_config->channel = 'A';
+	probe_config->chip = AHC_AIC7892;
+	probe_config->features = AHC_AIC7892_FE;
+	probe_config->flags |= AHC_NEWEEPROM_FMT;
+	probe_config->bugs |= AHC_SCBCHAN_UPLOAD_BUG;
+	return (0);
+}
+
+static int
+ahc_aic7895_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	uint8_t rev;
+
+	probe_config->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+	/*
+	 * The 'C' revision of the aic7895 has a few additional features.
+	 */
+	rev = ahc_pci_read_config(pci, PCIR_REVID, /*bytes*/1);
+	if (rev >= 4) {
+		probe_config->chip = AHC_AIC7895C;
+		probe_config->features = AHC_AIC7895C_FE;
+	} else  {
+		u_int command;
+
+		probe_config->chip = AHC_AIC7895;
+		probe_config->features = AHC_AIC7895_FE;
+
+		/*
+		 * The BIOS disables the use of MWI transactions
+		 * since it does not have the MWI bug work around
+		 * we have.  Disabling MWI reduces performance, so
+		 * turn it on again.
+		 */
+		command = ahc_pci_read_config(pci, PCIR_COMMAND, /*bytes*/1);
+		command |= PCIM_CMD_MWRICEN;
+		ahc_pci_write_config(pci, PCIR_COMMAND, command, /*bytes*/1);
+		probe_config->bugs |= AHC_PCI_MWI_BUG;
+	}
+	/*
+	 * XXX Does CACHETHEN really not work???  What about PCI retry?
+	 * on C level chips.  Need to test, but for now, play it safe.
+	 */
+	probe_config->bugs |= AHC_TMODE_WIDEODD_BUG|AHC_PCI_2_1_RETRY_BUG
+			   |  AHC_CACHETHEN_BUG;
+
+#if 0
+	uint32_t devconfig;
+
+	/*
+	 * Cachesize must also be zero due to stray DAC
+	 * problem when sitting behind some bridges.
+	 */
+	ahc_pci_write_config(pci, CSIZE_LATTIME, 0, /*bytes*/1);
+	devconfig = ahc_pci_read_config(pci, DEVCONFIG, /*bytes*/1);
+	devconfig |= MRDCEN;
+	ahc_pci_write_config(pci, DEVCONFIG, devconfig, /*bytes*/1);
+#endif
+	probe_config->flags |= AHC_NEWEEPROM_FMT;
+	return (0);
+}
+
+static int
+ahc_aic7896_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	probe_config->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+	probe_config->chip = AHC_AIC7896;
+	probe_config->features = AHC_AIC7896_FE;
+	probe_config->flags |= AHC_NEWEEPROM_FMT;
+	probe_config->bugs |= AHC_CACHETHEN_DIS_BUG;
+	return (0);
+}
+
+static int
+ahc_aic7899_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	probe_config->channel = ahc_get_pci_function(pci) == 1 ? 'B' : 'A';
+	probe_config->chip = AHC_AIC7899;
+	probe_config->features = AHC_AIC7899_FE;
+	probe_config->flags |= AHC_NEWEEPROM_FMT;
+	probe_config->bugs |= AHC_SCBCHAN_UPLOAD_BUG;
+	return (0);
+}
+
+static int
+ahc_aha29160C_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	int error;
+
+	error = ahc_aic7899_setup(pci, probe_config);
+	if (error != 0)
+		return (error);
+	probe_config->features |= AHC_REMOVABLE;
+	return (0);
+}
+
+static int
+ahc_raid_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	printf("RAID functionality unsupported\n");
+	return (ENXIO);
+}
+
+static int
+ahc_aha394XX_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	switch (ahc_get_pci_slot(pci)) {
+	case AHC_394X_SLOT_CHANNEL_A:
+		probe_config->channel = 'A';
+		break;
+	case AHC_394X_SLOT_CHANNEL_B:
+		probe_config->channel = 'B';
+		break;
+	default:
+		printf("adapter at unexpected slot %d\n"
+		       "unable to map to a channel\n",
+		       ahc_get_pci_slot(pci));
+		probe_config->channel = 'A';
+	}
+	return (0);
+}
+
+static int
+ahc_aha398XX_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	switch (ahc_get_pci_slot(pci)) {
+	case AHC_398X_SLOT_CHANNEL_A:
+		probe_config->channel = 'A';
+		break;
+	case AHC_398X_SLOT_CHANNEL_B:
+		probe_config->channel = 'B';
+		break;
+	case AHC_398X_SLOT_CHANNEL_C:
+		probe_config->channel = 'C';
+		break;
+	default:
+		printf("adapter at unexpected slot %d\n"
+		       "unable to map to a channel\n",
+		       ahc_get_pci_slot(pci));
+		probe_config->channel = 'A';
+		break;
+	}
+	probe_config->flags |= AHC_LARGE_SEEPROM;
+	return (0);
+}
+
+static int
+ahc_aha494XX_setup(ahc_dev_softc_t pci, struct ahc_probe_config *probe_config)
+{
+	switch (ahc_get_pci_slot(pci)) {
+	case AHC_494X_SLOT_CHANNEL_A:
+		probe_config->channel = 'A';
+		break;
+	case AHC_494X_SLOT_CHANNEL_B:
+		probe_config->channel = 'B';
+		break;
+	case AHC_494X_SLOT_CHANNEL_C:
+		probe_config->channel = 'C';
+		break;
+	case AHC_494X_SLOT_CHANNEL_D:
+		probe_config->channel = 'D';
+		break;
+	default:
+		printf("adapter at unexpected slot %d\n"
+		       "unable to map to a channel\n",
+		       ahc_get_pci_slot(pci));
+		probe_config->channel = 'A';
+	}
+	probe_config->flags |= AHC_LARGE_SEEPROM;
+	return (0);
+}
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_proc.c linux/drivers/scsi/aic7xxx/aic7xxx_proc.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_proc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx_proc.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2000, 2001 Adaptec Inc.
+ * 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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.
+ *
+ * String handling code courtesy of Gerard Roudier's <groudier@club-internet.fr>
+ * sym driver.
+ *
+ * $Id: //depot/src/linux/drivers/scsi/aic7xxx/aic7xxx_proc.c#7 $
+ */
+#include "aic7xxx_osm.h"
+#include "aic7xxx_inline.h"
+
+static void	copy_mem_info(struct info_str *info, char *data, int len);
+static int	copy_info(struct info_str *info, char *fmt, ...);
+static u_int	scsi_calc_syncsrate(u_int period_factor);
+static void	ahc_dump_target_state(struct ahc_softc *ahc,
+				      struct info_str *info,
+				      u_int our_id, char channel,
+				      u_int target_id, u_int target_offset);
+static void	ahc_dump_device_state(struct info_str *info,
+				      struct ahc_linux_device *dev);
+
+static void
+copy_mem_info(struct info_str *info, char *data, int len)
+{
+	if (info->pos + len > info->offset + info->length)
+		len = info->offset + info->length - info->pos;
+
+	if (info->pos + len < info->offset) {
+		info->pos += len;
+		return;
+	}
+
+	if (info->pos < info->offset) {
+		off_t partial;
+
+		partial = info->offset - info->pos;
+		data += partial;
+		info->pos += partial;
+		len  -= partial;
+	}
+
+	if (len > 0) {
+		memcpy(info->buffer, data, len);
+		info->pos += len;
+		info->buffer += len;
+	}
+}
+
+static int
+copy_info(struct info_str *info, char *fmt, ...)
+{
+	va_list args;
+	char buf[256];
+	int len;
+
+	va_start(args, fmt);
+	len = vsprintf(buf, fmt, args);
+	va_end(args);
+
+	copy_mem_info(info, buf, len);
+	return (len);
+}
+
+/*
+ * Table of syncrates that don't follow the "divisible by 4"
+ * rule. This table will be expanded in future SCSI specs.
+ */
+static struct {
+	u_int period_factor;
+	u_int period;	/* in 10ths of ns */
+} scsi_syncrates[] = {
+	{ 0x09, 125 },	/* FAST-80 */
+	{ 0x0a, 250 },	/* FAST-40 40MHz */
+	{ 0x0b, 303 },	/* FAST-40 33MHz */
+	{ 0x0c, 500 }	/* FAST-20 */
+};
+ 
+/*
+ * Return the frequency in kHz corresponding to the given
+ * sync period factor.
+ */
+static u_int
+scsi_calc_syncsrate(u_int period_factor)
+{
+	int i; 
+	int num_syncrates;
+ 
+	num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]);
+	/* See if the period is in the "exception" table */
+	for (i = 0; i < num_syncrates; i++) {
+
+		if (period_factor == scsi_syncrates[i].period_factor) {
+       			/* Period in kHz */
+			return (10000000 / scsi_syncrates[i].period);
+		}
+	}
+
+	/*
+	 * Wasn't in the table, so use the standard
+	 * 4 times conversion.
+	 */
+	return (10000000 / (period_factor * 4 * 10));
+}
+
+void
+ahc_format_transinfo(struct info_str *info, struct ahc_transinfo *tinfo)
+{
+	u_int speed;
+	u_int freq;
+	u_int mb;
+
+        speed = 3300;
+        freq = 0;
+	if (tinfo->offset != 0) {
+		freq = scsi_calc_syncsrate(tinfo->period);
+		speed = freq;
+	}
+	speed *= (0x01 << tinfo->width);
+        mb = speed / 1000;
+        if (mb > 0)
+		copy_info(info, "%d.%03dMB/s transfers", mb, speed % 1000);
+        else
+		copy_info(info, "%dKB/s transfers", speed);
+
+	if (freq != 0) {
+		copy_info(info, " (%d.%03dMHz%s, offset %d",
+			 freq / 1000, freq % 1000,
+			 (tinfo->ppr_options & MSG_EXT_PPR_DT_REQ) != 0
+			 ? " DT" : "", tinfo->offset);
+	}
+
+	if (tinfo->width > 0) {
+		if (freq != 0) {
+			copy_info(info, ", ");
+		} else {
+			copy_info(info, " (");
+		}
+		copy_info(info, "%dbit)", 8 * (0x01 << tinfo->width));
+	} else if (freq != 0) {
+		copy_info(info, ")");
+	}
+	copy_info(info, "\n");
+}
+
+static void
+ahc_dump_target_state(struct ahc_softc *ahc, struct info_str *info,
+		      u_int our_id, char channel, u_int target_id,
+		      u_int target_offset)
+{
+	struct	ahc_linux_target *targ;
+	struct	ahc_initiator_tinfo *tinfo;
+	struct	tmode_tstate *tstate;
+	int	lun;
+
+	tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+				    target_id, &tstate);
+	copy_info(info, "Channel %c Target %d Negotiation Settings\n",
+		  channel, target_id);
+	copy_info(info, "\tUser: ");
+	ahc_format_transinfo(info, &tinfo->user);
+	targ = ahc->platform_data->targets[target_offset];
+	if (targ == NULL)
+		return;
+
+	copy_info(info, "\tGoal: ");
+	ahc_format_transinfo(info, &tinfo->goal);
+	copy_info(info, "\tCurr: ");
+	ahc_format_transinfo(info, &tinfo->current);
+
+	for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+		struct ahc_linux_device *dev;
+
+		dev = targ->devices[lun];
+
+		if (dev == NULL)
+			continue;
+
+		ahc_dump_device_state(info, dev);
+	}
+}
+
+static void
+ahc_dump_device_state(struct info_str *info, struct ahc_linux_device *dev)
+{
+	copy_info(info, "\tChannel %c Target %d Lun %d Settings\n",
+		  dev->target->channel + 'A', dev->target->target, dev->lun);
+
+	copy_info(info, "\t\tCommands Queued %d\n", dev->num_commands);
+	copy_info(info, "\t\tCommands Active %d\n", dev->active);
+	copy_info(info, "\t\tCommand Openings %d\n", dev->openings);
+	copy_info(info, "\t\tMax Tagged Openings %d\n", dev->maxtags);
+	copy_info(info, "\t\tDevice Queue Frozen Count %d\n", dev->qfrozen);
+}
+
+/*
+ * Return information to handle /proc support for the driver.
+ */
+int
+aic7xxx_proc_info(char *buffer, char **start, off_t offset,
+		  int length, int hostno, int inout)
+{
+	struct	ahc_softc *ahc;
+	struct	info_str info;
+	char	ahc_info[256];
+	u_int	max_targ;
+	u_int	i;
+
+	TAILQ_FOREACH(ahc, &ahc_tailq, links) {
+		if (ahc->platform_data->host->host_no == hostno)
+			break;
+	}
+
+	if (ahc == NULL)
+		return (-EINVAL);
+
+	 /* Has data been written to the file? */ 
+	if (inout == TRUE)
+		return (-ENOSYS);
+
+	if (start)
+		*start = buffer;
+
+	info.buffer	= buffer;
+	info.length	= length;
+	info.offset	= offset;
+	info.pos	= 0;
+
+	copy_info(&info, "Adaptec AIC7xxx driver version: %s\n",
+		  AIC7XXX_DRIVER_VERSION);
+	ahc_controller_info(ahc, ahc_info);
+	copy_info(&info, "%s\n", ahc_info);
+
+	max_targ = 15;
+	if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
+		max_targ = 7;
+
+	for (i = 0; i <= max_targ; i++) {
+		u_int our_id;
+		u_int target_id;
+		char channel;
+
+		channel = 'A';
+		our_id = ahc->our_id;
+		target_id = i;
+		if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+			channel = 'B';
+			our_id = ahc->our_id_b;
+			target_id = i % 8;
+		}
+
+		ahc_dump_target_state(ahc, &info, our_id,
+				      channel, target_id, i);
+	}
+	return (info.pos > info.offset ? info.pos - info.offset : 0);
+}
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_reg.h linux/drivers/scsi/aic7xxx/aic7xxx_reg.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_reg.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx_reg.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,706 @@
+/*
+  * DO NOT EDIT - This file is automatically generated.
+  */
+
+#define	SCSISEQ         		0x00
+#define		TEMODE          	0x80
+#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		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		P_DATAIN_DT     	0x60
+#define		P_DATAOUT_DT    	0x20
+#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		ENABLE_CRC      	0x40
+#define		SINGLE_EDGE     	0x10
+#define		SOFS            	0x0f
+#define		SXFR_ULTRA2     	0x0f
+
+#define	SCSIID          		0x05
+#define	SCSIOFFSET      		0x05
+#define		SOFS_ULTRA2     	0x7f
+
+#define	SCSIDATL        		0x06
+
+#define	SCSIDATH        		0x07
+
+#define	STCNT           		0x08
+
+#define	OPTIONMODE      		0x08
+#define		AUTORATEEN      	0x80
+#define		AUTOACKEN       	0x40
+#define		ATNMGMNTEN      	0x20
+#define		BUSFREEREV      	0x10
+#define		EXPPHASEDIS     	0x08
+#define		SCSIDATL_IMGEN  	0x04
+#define		OPTIONMODE_DEFAULTS	0x03
+#define		AUTO_MSGOUT_DE  	0x02
+#define		DIS_MSGIN_DUALEDGE	0x01
+
+#define	TARGCRCCNT      		0x0a
+
+#define	CLRSINT0        		0x0b
+#define		CLRSELDO        	0x40
+#define		CLRSELDI        	0x20
+#define		CLRSELINGO      	0x10
+#define		CLRIOERR        	0x08
+#define		CLRSWRAP        	0x08
+#define		CLRSPIORDY      	0x02
+
+#define	SSTAT0          		0x0b
+#define		TARGET          	0x80
+#define		SELDO           	0x40
+#define		SELDI           	0x20
+#define		SELINGO         	0x10
+#define		IOERR           	0x08
+#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		SHVALID         	0x40
+#define		SFCNT           	0x1f
+#define		EXP_ACTIVE      	0x10
+#define		CRCVALERR       	0x08
+#define		CRCENDERR       	0x04
+#define		CRCREQERR       	0x02
+#define		DUAL_EDGE_ERR   	0x01
+
+#define	SSTAT3          		0x0e
+#define		SCSICNT         	0xf0
+#define		OFFCNT          	0x0f
+
+#define	SCSIID_ULTRA2   		0x0f
+
+#define	SIMODE0         		0x10
+#define		ENSELDO         	0x40
+#define		ENSELDI         	0x20
+#define		ENSELINGO       	0x10
+#define		ENIOERR         	0x08
+#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	TARGIDIN        		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	SCAMCTL         		0x1a
+#define		ENSCAMSELO      	0x80
+#define		CLRSCAMSELID    	0x40
+#define		ALTSTIM         	0x20
+#define		DFLTTID         	0x10
+#define		SCAMLVL         	0x03
+
+#define	TARGID          		0x1b
+
+#define	SPIOCAP         		0x1b
+#define		SOFT1           	0x80
+#define		SOFT0           	0x40
+#define		SOFTCMDEN       	0x20
+#define		HAS_BRDCTL      	0x10
+#define		SEEPROM         	0x08
+#define		EEPROM          	0x04
+#define		ROM             	0x02
+#define		SSPIOCPS        	0x01
+
+#define	BRDCTL          		0x1d
+#define		BRDDAT7         	0x80
+#define		BRDDAT6         	0x40
+#define		BRDDAT5         	0x20
+#define		BRDDAT4         	0x10
+#define		BRDSTB          	0x10
+#define		BRDCS           	0x08
+#define		BRDDAT3         	0x08
+#define		BRDDAT2         	0x04
+#define		BRDRW           	0x04
+#define		BRDRW_ULTRA2    	0x02
+#define		BRDCTL1         	0x02
+#define		BRDCTL0         	0x01
+#define		BRDSTB_ULTRA2   	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		ENAB40          	0x08
+#define		SELBUSB         	0x08
+#define		ENAB20          	0x04
+#define		SELWIDE         	0x02
+#define		XCVR            	0x01
+
+#define	BUSY_TARGETS    		0x20
+#define	TARG_SCSIRATE   		0x20
+
+#define	SRAM_BASE       		0x20
+
+#define	ULTRA_ENB       		0x30
+#define	CMDSIZE_TABLE   		0x30
+
+#define	DISC_DSB        		0x32
+
+#define	CMDSIZE_TABLE_TAIL		0x34
+
+#define	MWI_RESIDUAL    		0x38
+
+#define	NEXT_QUEUED_SCB 		0x39
+
+#define	MSG_OUT         		0x3a
+
+#define	DMAPARAMS       		0x3b
+#define		PRELOADEN       	0x80
+#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	SEQ_FLAGS       		0x3c
+#define		IDENTIFY_SEEN   	0x80
+#define		TARGET_CMD_IS_TAGGED	0x40
+#define		DPHASE          	0x20
+#define		TARG_CMD_PENDING	0x10
+#define		CMDPHASE_PENDING	0x08
+#define		DPHASE_PENDING  	0x04
+#define		SPHASE_PENDING  	0x02
+#define		NO_DISCONNECT   	0x01
+
+#define	SAVED_SCSIID    		0x3d
+
+#define	SAVED_LUN       		0x3e
+
+#define	LASTPHASE       		0x3f
+#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	WAITING_SCBH    		0x40
+
+#define	DISCONNECTED_SCBH		0x41
+
+#define	FREE_SCBH       		0x42
+
+#define	COMPLETE_SCBH   		0x43
+
+#define	HSCB_ADDR       		0x44
+
+#define	SHARED_DATA_ADDR		0x48
+
+#define	KERNEL_QINPOS   		0x4c
+
+#define	QINPOS          		0x4d
+
+#define	QOUTPOS         		0x4e
+
+#define	KERNEL_TQINPOS  		0x4f
+
+#define	TQINPOS         		0x50
+
+#define	ARG_1           		0x51
+#define	RETURN_1        		0x51
+#define		SEND_MSG        	0x80
+#define		SEND_SENSE      	0x40
+#define		SEND_REJ        	0x20
+#define		MSGOUT_PHASEMIS 	0x10
+#define		EXIT_MSG_LOOP   	0x08
+#define		CONT_MSG_LOOP   	0x04
+#define		CONT_TARG_SESSION	0x02
+
+#define	ARG_2           		0x52
+#define	RETURN_2        		0x52
+
+#define	LAST_MSG        		0x53
+
+#define	TARGET_MSG_REQUEST		0x54
+
+#define	SCSISEQ_TEMPLATE		0x56
+#define		ENSELO          	0x40
+#define		ENSELI          	0x20
+#define		ENRSELI         	0x10
+#define		ENAUTOATNO      	0x08
+#define		ENAUTOATNI      	0x04
+#define		ENAUTOATNP      	0x02
+
+#define	DATA_COUNT_ODD  		0x57
+
+#define	INITIATOR_TAG   		0x58
+
+#define	SEQ_FLAGS2      		0x59
+#define		SCB_DMA         	0x01
+
+#define	SCSICONF        		0x5a
+#define		TERM_ENB        	0x80
+#define		RESET_SCSI      	0x40
+#define		ENSPCHK         	0x20
+#define		HWSCSIID        	0x0f
+#define		HSCSIID         	0x07
+
+#define	INTDEF          		0x5c
+#define		EDGE_TRIG       	0x80
+#define		VECTOR          	0x0f
+
+#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	TARG_OFFSET     		0x70
+
+#define	BCTL            		0x84
+#define		ACE             	0x08
+#define		ENABLE          	0x01
+
+#define	DSCOMMAND0      		0x84
+#define		CACHETHEN       	0x80
+#define		DPARCKEN        	0x40
+#define		MPARCKEN        	0x20
+#define		EXTREQLCK       	0x10
+#define		INTSCBRAMSEL    	0x08
+#define		RAMPS           	0x04
+#define		USCBSIZE32      	0x02
+#define		CIOPARCKEN      	0x01
+
+#define	BUSTIME         		0x85
+#define		BOFF            	0xf0
+#define		BON             	0x0f
+
+#define	BUSSPD          		0x86
+#define		DFTHRSH         	0xc0
+#define		DFTHRSH_75      	0x80
+#define		STBOFF          	0x38
+#define		STBON           	0x07
+
+#define	HS_MAILBOX      		0x86
+#define		HOST_MAILBOX    	0xf0
+#define		HOST_TQINPOS    	0x80
+#define		SEQ_MAILBOX     	0x0f
+
+#define	DSPCISTATUS     		0x86
+#define		DFTHRSH_100     	0xc0
+
+#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		OUT_OF_RANGE    	0xe1
+#define		NO_FREE_SCB     	0xd1
+#define		SCB_MISMATCH    	0xc1
+#define		MISSED_BUSFREE  	0xb1
+#define		MKMSG_FAILED    	0xa1
+#define		DATA_OVERRUN    	0x91
+#define		PERR_DETECTED   	0x81
+#define		BAD_STATUS      	0x71
+#define		HOST_MSG_LOOP   	0x61
+#define		RESIDUAL        	0x51
+#define		IGN_WIDE_RES    	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		CLRPARERR       	0x10
+#define		CLRBRKADRINT    	0x08
+#define		CLRSCSIINT      	0x04
+#define		CLRCMDINT       	0x02
+#define		CLRSEQINT       	0x01
+
+#define	ERROR           		0x92
+#define		CIOPARERR       	0x80
+#define		PCIERRSTAT      	0x40
+#define		MPARERR         	0x20
+#define		DPARERR         	0x10
+#define		SQPARERR        	0x08
+#define		ILLOPCODE       	0x04
+#define		ILLSADDR        	0x02
+#define		ILLHADDR        	0x01
+
+#define	DFCNTRL         		0x93
+
+#define	DFSTATUS        		0x94
+#define		PRELOAD_AVAIL   	0x80
+#define		DWORDEMP        	0x20
+#define		MREQPEND        	0x10
+#define		HDONE           	0x08
+#define		DFTHRESH        	0x04
+#define		FIFOFULL        	0x02
+#define		FIFOEMP         	0x01
+
+#define	DFWADDR         		0x95
+
+#define	DFRADDR         		0x97
+
+#define	DFDAT           		0x99
+
+#define	SCBCNT          		0x9a
+#define		SCBAUTO         	0x80
+#define		SCBCNT_MASK     	0x1f
+
+#define	QINFIFO         		0x9b
+
+#define	QINCNT          		0x9c
+
+#define	QOUTFIFO        		0x9d
+
+#define	CRCCONTROL1     		0x9d
+#define		CRCONSEEN       	0x80
+#define		CRCVALCHKEN     	0x40
+#define		CRCENDCHKEN     	0x20
+#define		CRCREQCHKEN     	0x10
+#define		TARGCRCENDEN    	0x08
+#define		TARGCRCCNTEN    	0x04
+
+#define	QOUTCNT         		0x9e
+
+#define	SCSIPHASE       		0x9e
+#define		STATUS_PHASE    	0x20
+#define		COMMAND_PHASE   	0x10
+#define		MSG_IN_PHASE    	0x08
+#define		MSG_OUT_PHASE   	0x04
+#define		DATA_IN_PHASE   	0x02
+#define		DATA_OUT_PHASE  	0x01
+
+#define	SFUNCT          		0x9f
+#define		ALT_MODE        	0x80
+
+#define	SCB_CDB_PTR     		0xa0
+#define	SCB_CDB_STORE   		0xa0
+#define	SCB_TARGET_INFO 		0xa0
+#define	SCB_RESIDUAL_DATACNT		0xa0
+
+#define	SCB_BASE        		0xa0
+
+#define	SCB_RESIDUAL_SGPTR		0xa4
+
+#define	SCB_SCSI_STATUS 		0xa8
+
+#define	SCB_CDB_STORE_PAD		0xa9
+
+#define	SCB_DATAPTR     		0xac
+
+#define	SCB_DATACNT     		0xb0
+#define		SG_LAST_SEG     	0x80
+#define		SG_HIGH_ADDR_BITS	0x7f
+
+#define	SCB_SGPTR       		0xb4
+#define		SG_RESID_VALID  	0x04
+#define		SG_FULL_RESID   	0x02
+#define		SG_LIST_NULL    	0x01
+
+#define	SCB_CONTROL     		0xb8
+#define		TARGET_SCB      	0x80
+#define		DISCENB         	0x40
+#define		TAG_ENB         	0x20
+#define		MK_MESSAGE      	0x10
+#define		ULTRAENB        	0x08
+#define		DISCONNECTED    	0x04
+#define		SCB_TAG_TYPE    	0x03
+
+#define	SCB_SCSIID      		0xb9
+#define		TID             	0xf0
+#define		TWIN_CHNLB      	0x80
+#define		TWIN_TID        	0x70
+#define		OID             	0x0f
+
+#define	SCB_LUN         		0xba
+#define		LID             	0xff
+
+#define	SCB_TAG         		0xbb
+
+#define	SCB_CDB_LEN     		0xbc
+
+#define	SCB_SCSIRATE    		0xbd
+
+#define	SCB_SCSIOFFSET  		0xbe
+
+#define	SCB_NEXT        		0xbf
+
+#define	SEECTL_2840     		0xc0
+#define		CS_2840         	0x04
+#define		CK_2840         	0x02
+#define		DO_2840         	0x01
+
+#define	SCB_64_SPARE    		0xc0
+
+#define	STATUS_2840     		0xc1
+#define		EEPROM_TF       	0x80
+#define		BIOS_SEL        	0x60
+#define		ADSEL           	0x1e
+#define		DI_2840         	0x01
+
+#define	SCB_64_BTT      		0xd0
+
+#define	CCHADDR         		0xe0
+
+#define	CCHCNT          		0xe8
+
+#define	CCSGRAM         		0xe9
+
+#define	CCSGADDR        		0xea
+
+#define	CCSGCTL         		0xeb
+#define		CCSGDONE        	0x80
+#define		CCSGEN          	0x08
+#define		SG_FETCH_NEEDED 	0x02
+#define		CCSGRESET       	0x01
+
+#define	CCSCBRAM        		0xec
+
+#define	CCSCBADDR       		0xed
+
+#define	CCSCBCTL        		0xee
+#define		CCSCBDONE       	0x80
+#define		ARRDONE         	0x40
+#define		CCARREN         	0x10
+#define		CCSCBEN         	0x08
+#define		CCSCBDIR        	0x04
+#define		CCSCBRESET      	0x01
+
+#define	CCSCBCNT        		0xef
+
+#define	SCBBADDR        		0xf0
+
+#define	CCSCBPTR        		0xf1
+
+#define	HNSCB_QOFF      		0xf4
+
+#define	SNSCB_QOFF      		0xf6
+
+#define	SDSCB_QOFF      		0xf8
+
+#define	QOFF_CTLSTA     		0xfa
+#define		SCB_AVAIL       	0x40
+#define		SNSCB_ROLLOVER  	0x20
+#define		SDSCB_ROLLOVER  	0x10
+#define		SCB_QSIZE       	0x07
+#define		SCB_QSIZE_256   	0x06
+
+#define	DFF_THRSH       		0xfb
+#define		WR_DFTHRSH      	0x70
+#define		WR_DFTHRSH_MAX  	0x70
+#define		WR_DFTHRSH_90   	0x60
+#define		WR_DFTHRSH_85   	0x50
+#define		WR_DFTHRSH_75   	0x40
+#define		WR_DFTHRSH_63   	0x30
+#define		WR_DFTHRSH_50   	0x20
+#define		WR_DFTHRSH_25   	0x10
+#define		RD_DFTHRSH_MAX  	0x07
+#define		RD_DFTHRSH      	0x07
+#define		RD_DFTHRSH_90   	0x06
+#define		RD_DFTHRSH_85   	0x05
+#define		RD_DFTHRSH_75   	0x04
+#define		RD_DFTHRSH_63   	0x03
+#define		RD_DFTHRSH_50   	0x02
+#define		RD_DFTHRSH_25   	0x01
+#define		RD_DFTHRSH_MIN  	0x00
+#define		WR_DFTHRSH_MIN  	0x00
+
+#define	SG_CACHE_SHADOW 		0xfc
+#define		SG_ADDR_MASK    	0xf8
+#define		ODD_SEG         	0x04
+#define		LAST_SEG        	0x02
+#define		LAST_SEG_DONE   	0x01
+
+#define	SG_CACHE_PRE    		0xfc
+
+
+#define	CMD_GROUP_CODE_SHIFT	0x05
+#define	BUS_8_BIT	0x00
+#define	CCSGRAM_MAXSEGS	0x10
+#define	TARGET_DATA_IN	0x01
+#define	STATUS_QUEUE_FULL	0x28
+#define	STATUS_BUSY	0x08
+#define	MAX_OFFSET_8BIT	0x0f
+#define	BUS_16_BIT	0x01
+#define	TID_SHIFT	0x04
+#define	SCB_DOWNLOAD_SIZE_64	0x30
+#define	SCB_UPLOAD_SIZE	0x20
+#define	HOST_MAILBOX_SHIFT	0x04
+#define	SCB_INITIATOR_TAG	0x03
+#define	SCB_TARGET_STATUS	0x02
+#define	SCB_TARGET_DATA_DIR	0x01
+#define	SCB_TARGET_PHASES	0x00
+#define	MAX_OFFSET_ULTRA2	0x7f
+#define	MAX_OFFSET_16BIT	0x08
+#define	TARGET_CMD_CMPLT	0xfe
+#define	SCB_LIST_NULL	0xff
+#define	SG_SIZEOF	0x08
+#define	SCB_DOWNLOAD_SIZE	0x20
+#define	SEQ_MAILBOX_SHIFT	0x00
+#define	HOST_MSG	0xff
+#define	BUS_32_BIT	0x02
+#define	CCSGADDR_MAX	0x80
+
+
+/* Downloaded Constant Definitions */
+#define	SG_PREFETCH_ADDR_MASK	0x06
+#define	SG_PREFETCH_ALIGN_MASK	0x05
+#define	QOUTFIFO_OFFSET	0x00
+#define	SG_PREFETCH_CNT	0x04
+#define	INVERTED_CACHESIZE_MASK	0x03
+#define	CACHESIZE_MASK	0x02
+#define	QINFIFO_OFFSET	0x01
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_seq.h linux/drivers/scsi/aic7xxx/aic7xxx_seq.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aic7xxx_seq.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aic7xxx_seq.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,1239 @@
+/*
+  * DO NOT EDIT - This file is automatically generated.
+  */
+static uint8_t seqprog[] = {
+	0xb2, 0x00, 0x00, 0x08,
+	0xf7, 0x11, 0x22, 0x08,
+	0x00, 0x65, 0xde, 0x59,
+	0xf7, 0x01, 0x02, 0x08,
+	0xff, 0x6a, 0x24, 0x08,
+	0x40, 0x00, 0x40, 0x68,
+	0x08, 0x1f, 0x3e, 0x10,
+	0x40, 0x00, 0x40, 0x68,
+	0xff, 0x40, 0x3c, 0x60,
+	0x08, 0x1f, 0x3e, 0x10,
+	0x60, 0x0b, 0x42, 0x68,
+	0x40, 0xfa, 0x12, 0x78,
+	0x01, 0x4d, 0xc8, 0x30,
+	0x00, 0x4c, 0x12, 0x70,
+	0x01, 0x39, 0xa2, 0x30,
+	0x00, 0x6a, 0x7e, 0x5e,
+	0x01, 0x51, 0x20, 0x31,
+	0x01, 0x59, 0xb2, 0x00,
+	0x0d, 0x6a, 0x76, 0x00,
+	0x00, 0x51, 0xda, 0x5d,
+	0x01, 0x51, 0xc8, 0x30,
+	0x00, 0x39, 0xde, 0x60,
+	0x00, 0xbb, 0x30, 0x70,
+	0xc1, 0x6a, 0x96, 0x5e,
+	0x01, 0xbf, 0x72, 0x30,
+	0x01, 0x40, 0x7e, 0x31,
+	0x01, 0x90, 0x80, 0x30,
+	0x01, 0xf6, 0xd4, 0x30,
+	0x01, 0x4d, 0x9a, 0x18,
+	0xfe, 0x59, 0xb2, 0x08,
+	0x01, 0x40, 0x20, 0x31,
+	0x00, 0x65, 0xe2, 0x58,
+	0x60, 0x0b, 0x40, 0x78,
+	0x08, 0x6a, 0x18, 0x00,
+	0x08, 0x11, 0x22, 0x00,
+	0x60, 0x0b, 0x00, 0x78,
+	0x40, 0x0b, 0x12, 0x69,
+	0x80, 0x0b, 0xcc, 0x78,
+	0x20, 0x6a, 0x16, 0x00,
+	0x20, 0x11, 0x54, 0x68,
+	0x20, 0x6a, 0x18, 0x00,
+	0x20, 0x11, 0x22, 0x00,
+	0xa4, 0x6a, 0x06, 0x00,
+	0x08, 0x3c, 0x78, 0x00,
+	0x01, 0x50, 0xc8, 0x30,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x48, 0x6a, 0xc4, 0x5d,
+	0x01, 0x6a, 0xdc, 0x01,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x48, 0x6a, 0xc4, 0x5d,
+	0x01, 0x6a, 0x26, 0x01,
+	0xf0, 0x19, 0x7a, 0x08,
+	0x0f, 0x18, 0xc8, 0x08,
+	0x0f, 0x0f, 0xc8, 0x08,
+	0x0f, 0x05, 0xc8, 0x08,
+	0x00, 0x3d, 0x7a, 0x00,
+	0x08, 0x1f, 0x74, 0x78,
+	0x80, 0x3d, 0x7a, 0x00,
+	0x01, 0x3d, 0xd8, 0x31,
+	0x01, 0x3d, 0x32, 0x31,
+	0x10, 0x03, 0x56, 0x79,
+	0x00, 0x65, 0x0a, 0x59,
+	0x80, 0x66, 0xa8, 0x78,
+	0x01, 0x66, 0xd8, 0x31,
+	0x01, 0x66, 0x32, 0x31,
+	0x40, 0x66, 0x86, 0x68,
+	0x01, 0x3c, 0x78, 0x00,
+	0x10, 0x03, 0xb0, 0x78,
+	0x00, 0x65, 0x0a, 0x59,
+	0xe0, 0x66, 0xc8, 0x18,
+	0x00, 0x65, 0xb0, 0x50,
+	0xdd, 0x66, 0xc8, 0x18,
+	0x00, 0x65, 0xb0, 0x48,
+	0x01, 0x66, 0xd8, 0x31,
+	0x01, 0x66, 0x32, 0x31,
+	0x10, 0x03, 0x56, 0x79,
+	0x00, 0x65, 0x0a, 0x59,
+	0x01, 0x66, 0xd8, 0x31,
+	0x01, 0x66, 0x32, 0x31,
+	0x01, 0x66, 0xb0, 0x30,
+	0x40, 0x3c, 0x78, 0x00,
+	0x10, 0x03, 0xa6, 0x78,
+	0x00, 0x65, 0x0a, 0x59,
+	0x00, 0x65, 0xb0, 0x40,
+	0x61, 0x6a, 0x96, 0x5e,
+	0x08, 0x51, 0x2e, 0x71,
+	0x02, 0x0b, 0xac, 0x78,
+	0x00, 0x65, 0xa8, 0x40,
+	0x80, 0x86, 0xc8, 0x08,
+	0x01, 0x4f, 0xc8, 0x30,
+	0x00, 0x50, 0xc2, 0x60,
+	0xc4, 0x6a, 0x32, 0x5d,
+	0x40, 0x3c, 0xbe, 0x78,
+	0x28, 0x6a, 0x48, 0x5d,
+	0x00, 0x65, 0x54, 0x41,
+	0x08, 0x6a, 0x48, 0x5d,
+	0x00, 0x65, 0x54, 0x41,
+	0xff, 0x6a, 0xd8, 0x01,
+	0xff, 0x6a, 0x32, 0x01,
+	0x90, 0x3c, 0x78, 0x00,
+	0x10, 0x03, 0x4a, 0x69,
+	0x00, 0x65, 0x2e, 0x41,
+	0x1a, 0x01, 0x02, 0x00,
+	0xf0, 0x19, 0x7a, 0x08,
+	0x0f, 0x0f, 0xc8, 0x08,
+	0x0f, 0x05, 0xc8, 0x08,
+	0x00, 0x3d, 0x7a, 0x00,
+	0x08, 0x1f, 0xda, 0x78,
+	0x80, 0x3d, 0x7a, 0x00,
+	0x20, 0x6a, 0x16, 0x00,
+	0x00, 0x65, 0xbe, 0x41,
+	0x00, 0x65, 0x76, 0x5e,
+	0x00, 0x65, 0x12, 0x40,
+	0x20, 0x11, 0xe8, 0x68,
+	0x20, 0x6a, 0x18, 0x00,
+	0x20, 0x11, 0x22, 0x00,
+	0xf7, 0x1f, 0xca, 0x08,
+	0x80, 0xb9, 0xee, 0x78,
+	0x08, 0x65, 0xca, 0x00,
+	0x01, 0x65, 0x3e, 0x30,
+	0x01, 0xb9, 0x1e, 0x30,
+	0x7f, 0xb9, 0x0a, 0x08,
+	0x01, 0xb9, 0x0a, 0x30,
+	0x01, 0x56, 0xca, 0x30,
+	0x80, 0xb8, 0xfc, 0x78,
+	0x80, 0x65, 0xca, 0x00,
+	0x01, 0x65, 0x00, 0x34,
+	0x01, 0x56, 0x00, 0x34,
+	0x1a, 0x01, 0x02, 0x00,
+	0x08, 0xb8, 0x06, 0x79,
+	0x20, 0x01, 0x02, 0x00,
+	0x02, 0xbd, 0x08, 0x34,
+	0x01, 0xbd, 0x08, 0x34,
+	0x08, 0x01, 0x02, 0x00,
+	0x02, 0x0b, 0x0c, 0x79,
+	0xf7, 0x01, 0x02, 0x08,
+	0x01, 0x06, 0xcc, 0x34,
+	0xb2, 0x00, 0x00, 0x08,
+	0x40, 0x6a, 0x16, 0x00,
+	0x01, 0x40, 0x20, 0x31,
+	0x01, 0xbf, 0x80, 0x30,
+	0x01, 0xb9, 0x7a, 0x30,
+	0x01, 0xba, 0x7c, 0x30,
+	0x00, 0x65, 0x00, 0x59,
+	0x80, 0x0b, 0xba, 0x79,
+	0xe4, 0x6a, 0x32, 0x5d,
+	0x80, 0xba, 0x48, 0x5d,
+	0x20, 0xb8, 0x2c, 0x79,
+	0x20, 0x6a, 0x48, 0x5d,
+	0x00, 0xa3, 0x48, 0x5d,
+	0x01, 0xa0, 0x78, 0x30,
+	0x10, 0x03, 0x46, 0x69,
+	0x08, 0x3c, 0x62, 0x69,
+	0x04, 0x3c, 0x88, 0x69,
+	0x02, 0x3c, 0x8e, 0x69,
+	0x01, 0x3c, 0x4c, 0x79,
+	0x00, 0x6a, 0x7e, 0x5e,
+	0x01, 0x6a, 0xa2, 0x30,
+	0x00, 0x65, 0x9a, 0x59,
+	0x04, 0x51, 0x3e, 0x61,
+	0x0d, 0x6a, 0x76, 0x00,
+	0x00, 0xbb, 0xda, 0x5d,
+	0x00, 0x65, 0x2c, 0x41,
+	0xa4, 0x6a, 0x06, 0x00,
+	0x00, 0x65, 0x0a, 0x59,
+	0x00, 0x65, 0xa8, 0x40,
+	0xe4, 0x6a, 0x32, 0x5d,
+	0x20, 0x3c, 0x52, 0x79,
+	0x02, 0x6a, 0x48, 0x5d,
+	0x04, 0x6a, 0x48, 0x5d,
+	0x01, 0x03, 0x54, 0x69,
+	0xf7, 0x11, 0x22, 0x08,
+	0xff, 0x6a, 0x24, 0x08,
+	0xff, 0x6a, 0x06, 0x08,
+	0x01, 0x6a, 0x7e, 0x00,
+	0x00, 0x65, 0x9a, 0x59,
+	0x00, 0x65, 0x04, 0x40,
+	0x84, 0x6a, 0x32, 0x5d,
+	0x00, 0x65, 0x0a, 0x59,
+	0x01, 0x66, 0xc8, 0x30,
+	0x01, 0x64, 0xd8, 0x31,
+	0x01, 0x64, 0x32, 0x31,
+	0x5b, 0x64, 0xc8, 0x28,
+	0x30, 0x64, 0xca, 0x18,
+	0x01, 0x6c, 0xc8, 0x30,
+	0xff, 0x64, 0x84, 0x79,
+	0x08, 0x01, 0x02, 0x00,
+	0x02, 0x0b, 0x76, 0x79,
+	0x01, 0x64, 0x7c, 0x61,
+	0xf7, 0x01, 0x02, 0x08,
+	0x01, 0x06, 0xd8, 0x31,
+	0x01, 0x06, 0x32, 0x31,
+	0xff, 0x64, 0xc8, 0x18,
+	0xff, 0x64, 0x76, 0x69,
+	0xf7, 0x3c, 0x78, 0x08,
+	0x00, 0x65, 0x2e, 0x41,
+	0x40, 0xa1, 0x7e, 0x10,
+	0x04, 0xa1, 0x32, 0x5d,
+	0x00, 0x65, 0x62, 0x42,
+	0xc4, 0x6a, 0x32, 0x5d,
+	0xc0, 0x6a, 0x7e, 0x00,
+	0x00, 0xa2, 0x48, 0x5d,
+	0xe4, 0x6a, 0x06, 0x00,
+	0x00, 0x6a, 0x48, 0x5d,
+	0x00, 0x65, 0x54, 0x41,
+	0x10, 0x3c, 0x9e, 0x69,
+	0x00, 0xbb, 0x64, 0x44,
+	0x18, 0x6a, 0xda, 0x01,
+	0x01, 0x69, 0xd8, 0x31,
+	0x1c, 0x6a, 0xd0, 0x01,
+	0x09, 0xee, 0xdc, 0x01,
+	0x80, 0xee, 0xa6, 0x79,
+	0xff, 0x6a, 0xdc, 0x09,
+	0x01, 0x93, 0x26, 0x01,
+	0x03, 0x6a, 0x2a, 0x01,
+	0x01, 0x69, 0x32, 0x31,
+	0x1c, 0x6a, 0xa8, 0x5d,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x6c, 0x5e,
+	0x01, 0x50, 0xa0, 0x18,
+	0x02, 0x6a, 0x22, 0x05,
+	0x80, 0x6a, 0x74, 0x00,
+	0x80, 0x3c, 0x78, 0x00,
+	0x00, 0x65, 0xa0, 0x5d,
+	0x01, 0x3f, 0xc8, 0x30,
+	0xbf, 0x64, 0x62, 0x7a,
+	0x80, 0x64, 0x82, 0x73,
+	0xa0, 0x64, 0xd8, 0x73,
+	0xc0, 0x64, 0xd0, 0x73,
+	0xe0, 0x64, 0x18, 0x74,
+	0x01, 0x6a, 0x96, 0x5e,
+	0x00, 0x65, 0xbe, 0x41,
+	0xf7, 0x11, 0x22, 0x08,
+	0x01, 0x06, 0xd4, 0x30,
+	0xff, 0x6a, 0x24, 0x08,
+	0xf7, 0x01, 0x02, 0x08,
+	0x09, 0x0c, 0xd8, 0x79,
+	0x08, 0x0c, 0x04, 0x68,
+	0xb1, 0x6a, 0x96, 0x5e,
+	0xff, 0x6a, 0x26, 0x09,
+	0x12, 0x01, 0x02, 0x00,
+	0x02, 0x6a, 0x08, 0x30,
+	0xff, 0x6a, 0x08, 0x08,
+	0xdf, 0x01, 0x02, 0x08,
+	0x01, 0x6a, 0x7e, 0x00,
+	0xff, 0x6a, 0x78, 0x0c,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x08, 0xa4, 0x48, 0x19,
+	0x00, 0xa5, 0x4a, 0x21,
+	0x00, 0xa6, 0x4c, 0x21,
+	0x00, 0xa7, 0x4e, 0x25,
+	0x08, 0xeb, 0x9a, 0x7e,
+	0x80, 0xeb, 0xf8, 0x79,
+	0xff, 0x6a, 0xd6, 0x09,
+	0x08, 0xeb, 0xfc, 0x69,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x88, 0xeb, 0x12, 0x72,
+	0x08, 0xeb, 0x9a, 0x6e,
+	0x80, 0xa3, 0x9a, 0x6e,
+	0x04, 0xea, 0x16, 0xe2,
+	0x08, 0xee, 0x9a, 0x6e,
+	0x04, 0x6a, 0xd0, 0x81,
+	0x05, 0xa4, 0xc0, 0x89,
+	0x03, 0xa5, 0xc2, 0x31,
+	0x09, 0x6a, 0xd6, 0x05,
+	0x00, 0x65, 0xfa, 0x59,
+	0x06, 0xa4, 0xd4, 0x89,
+	0x80, 0x94, 0x9a, 0x7e,
+	0x04, 0xe9, 0x10, 0x31,
+	0x01, 0xe9, 0xca, 0x30,
+	0x01, 0x65, 0x20, 0x7a,
+	0x01, 0x57, 0xae, 0x10,
+	0x01, 0x65, 0x18, 0x31,
+	0x02, 0xe9, 0x1a, 0x31,
+	0x01, 0xe9, 0x46, 0x31,
+	0x00, 0x65, 0xec, 0x59,
+	0x01, 0xa4, 0xca, 0x30,
+	0x01, 0x57, 0x2e, 0x7a,
+	0x04, 0x65, 0xca, 0x00,
+	0x80, 0xa3, 0x32, 0x7a,
+	0x02, 0x65, 0xca, 0x00,
+	0x01, 0x65, 0xf8, 0x31,
+	0x01, 0x3b, 0x26, 0x31,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x01, 0x8c, 0xc8, 0x30,
+	0x00, 0x88, 0xc8, 0x18,
+	0x02, 0x64, 0xc8, 0x88,
+	0xff, 0x64, 0x9a, 0x7e,
+	0xff, 0x8d, 0x48, 0x6a,
+	0xff, 0x8e, 0x48, 0x6a,
+	0x03, 0x8c, 0xd4, 0x98,
+	0x00, 0x65, 0x9a, 0x56,
+	0x01, 0x64, 0x70, 0x30,
+	0xff, 0x64, 0xc8, 0x10,
+	0x01, 0x64, 0xc8, 0x18,
+	0x00, 0x8c, 0x18, 0x19,
+	0xff, 0x8d, 0x1a, 0x21,
+	0xff, 0x8e, 0x1c, 0x25,
+	0x04, 0x14, 0x10, 0x31,
+	0x03, 0xa0, 0x18, 0x31,
+	0x03, 0xa0, 0x10, 0x30,
+	0x08, 0x6a, 0xcc, 0x00,
+	0xa0, 0x6a, 0xbe, 0x5d,
+	0x01, 0xa0, 0xae, 0x08,
+	0x00, 0x65, 0x88, 0x42,
+	0xa8, 0x6a, 0x76, 0x00,
+	0x79, 0x6a, 0x76, 0x00,
+	0x40, 0x3f, 0x6a, 0x6a,
+	0x04, 0x3b, 0x76, 0x00,
+	0x00, 0x65, 0x52, 0x5d,
+	0x04, 0x6a, 0xd4, 0x81,
+	0x20, 0x3c, 0x54, 0x6a,
+	0x20, 0x3c, 0x78, 0x00,
+	0x07, 0xac, 0x10, 0x31,
+	0x05, 0xb3, 0x46, 0x31,
+	0x88, 0x6a, 0xcc, 0x00,
+	0xac, 0x6a, 0xb6, 0x5d,
+	0xa3, 0x6a, 0xcc, 0x00,
+	0xb3, 0x6a, 0xba, 0x5d,
+	0x00, 0x65, 0x38, 0x5a,
+	0xfd, 0xa4, 0x48, 0x09,
+	0x01, 0x8c, 0xae, 0x08,
+	0x03, 0x8c, 0x10, 0x30,
+	0x00, 0x65, 0xae, 0x5d,
+	0x01, 0xa4, 0x98, 0x7a,
+	0x04, 0x3b, 0x76, 0x08,
+	0x01, 0x3b, 0x26, 0x31,
+	0x80, 0x02, 0x04, 0x00,
+	0x10, 0x0c, 0x90, 0x7a,
+	0x7f, 0x02, 0x04, 0x08,
+	0x91, 0x6a, 0x96, 0x5e,
+	0x00, 0x65, 0xbe, 0x41,
+	0x01, 0xa4, 0xca, 0x30,
+	0x80, 0xa3, 0x9e, 0x7a,
+	0x02, 0x65, 0xca, 0x00,
+	0x01, 0x57, 0xa2, 0x7a,
+	0x04, 0x65, 0xca, 0x00,
+	0x01, 0x65, 0xf8, 0x31,
+	0x01, 0x3b, 0x26, 0x31,
+	0x00, 0x65, 0x00, 0x5a,
+	0x01, 0xfc, 0xae, 0x6a,
+	0x80, 0x0b, 0xa6, 0x6a,
+	0x10, 0x0c, 0xa6, 0x7a,
+	0x04, 0x93, 0xc0, 0x6a,
+	0xdf, 0x93, 0x26, 0x09,
+	0x20, 0x93, 0xb2, 0x6a,
+	0x02, 0x93, 0x26, 0x01,
+	0x01, 0x94, 0xb4, 0x7a,
+	0x01, 0x94, 0xb4, 0x7a,
+	0x01, 0x94, 0xb4, 0x7a,
+	0x01, 0x94, 0xb4, 0x7a,
+	0x01, 0x94, 0xb4, 0x7a,
+	0x10, 0x94, 0xc0, 0x6a,
+	0xd7, 0x93, 0x26, 0x09,
+	0x08, 0x93, 0xc4, 0x6a,
+	0x02, 0xfc, 0xce, 0x7a,
+	0x01, 0xfc, 0x4e, 0x7b,
+	0x01, 0xa4, 0x48, 0x01,
+	0x00, 0x65, 0x4e, 0x43,
+	0x40, 0x0d, 0xd4, 0x6a,
+	0x00, 0x65, 0x00, 0x5a,
+	0x00, 0x65, 0xc6, 0x42,
+	0x80, 0xfc, 0xde, 0x7a,
+	0x80, 0xa4, 0xde, 0x6a,
+	0xff, 0xa5, 0x4a, 0x19,
+	0xff, 0xa6, 0x4c, 0x21,
+	0xff, 0xa7, 0x4e, 0x21,
+	0xf8, 0xfc, 0x48, 0x09,
+	0xff, 0x6a, 0xae, 0x08,
+	0x04, 0xfc, 0xe6, 0x7a,
+	0x01, 0x57, 0xae, 0x00,
+	0xff, 0x6a, 0x46, 0x09,
+	0xff, 0x38, 0xf2, 0x6a,
+	0x80, 0xa3, 0xf2, 0x7a,
+	0x80, 0x0b, 0xf0, 0x7a,
+	0x04, 0x3b, 0xf2, 0x7a,
+	0xbf, 0x3b, 0x76, 0x08,
+	0x01, 0x3b, 0x26, 0x31,
+	0x00, 0x65, 0x00, 0x5a,
+	0x01, 0x0b, 0x00, 0x6b,
+	0x10, 0x0c, 0xf4, 0x7a,
+	0x04, 0x93, 0xfe, 0x6a,
+	0x01, 0x94, 0xfc, 0x7a,
+	0x10, 0x94, 0xfe, 0x6a,
+	0xc7, 0x93, 0x26, 0x09,
+	0x01, 0x99, 0xd4, 0x30,
+	0x38, 0x93, 0x02, 0x6b,
+	0xff, 0x08, 0x4e, 0x6b,
+	0xff, 0x09, 0x4e, 0x6b,
+	0xff, 0x0a, 0x4e, 0x6b,
+	0xff, 0x38, 0x1e, 0x7b,
+	0x04, 0x14, 0x10, 0x31,
+	0x01, 0x38, 0x18, 0x31,
+	0x02, 0x6a, 0x1a, 0x31,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x14, 0x6a, 0xbc, 0x5d,
+	0x00, 0x38, 0xa8, 0x5d,
+	0xff, 0x6a, 0x70, 0x08,
+	0x00, 0x65, 0x44, 0x43,
+	0x80, 0xa3, 0x24, 0x7b,
+	0x01, 0xa4, 0x48, 0x01,
+	0x00, 0x65, 0x4e, 0x43,
+	0x08, 0xeb, 0x2a, 0x7b,
+	0x00, 0x65, 0x00, 0x5a,
+	0x08, 0xeb, 0x26, 0x6b,
+	0x07, 0xe9, 0x10, 0x31,
+	0x80, 0xe9, 0x30, 0x7b,
+	0x80, 0xa3, 0x46, 0x01,
+	0x88, 0x6a, 0xcc, 0x00,
+	0xa4, 0x6a, 0xbc, 0x5d,
+	0x08, 0x6a, 0xa8, 0x5d,
+	0x0d, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x6c, 0x5e,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x00, 0x65, 0x4e, 0x5e,
+	0x01, 0x99, 0x46, 0x31,
+	0x00, 0x65, 0x38, 0x5a,
+	0x00, 0x65, 0xec, 0x59,
+	0x03, 0x8c, 0x10, 0x30,
+	0x00, 0x65, 0xae, 0x5d,
+	0x01, 0x8c, 0x4c, 0x7b,
+	0x01, 0x57, 0xae, 0x10,
+	0x80, 0x0b, 0x88, 0x6a,
+	0x80, 0x0b, 0x54, 0x6b,
+	0x01, 0x0c, 0x50, 0x7b,
+	0x10, 0x0c, 0x88, 0x7a,
+	0x00, 0x65, 0xf6, 0x59,
+	0xff, 0x38, 0x66, 0x7b,
+	0x01, 0x38, 0xc8, 0x30,
+	0x00, 0x08, 0x40, 0x19,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x00, 0x09, 0x42, 0x21,
+	0x00, 0x0a, 0x44, 0x21,
+	0xff, 0x6a, 0x70, 0x08,
+	0x00, 0x65, 0x68, 0x43,
+	0x03, 0x08, 0x40, 0x31,
+	0x03, 0x08, 0x40, 0x31,
+	0x01, 0x08, 0x40, 0x31,
+	0x01, 0x09, 0x42, 0x31,
+	0x01, 0x0a, 0x44, 0x31,
+	0xfd, 0xb4, 0x68, 0x09,
+	0x12, 0x01, 0x02, 0x00,
+	0x12, 0x01, 0x02, 0x00,
+	0x04, 0x3c, 0xbe, 0x79,
+	0xfb, 0x3c, 0x78, 0x08,
+	0x04, 0x93, 0x2e, 0x79,
+	0x01, 0x0c, 0x7c, 0x6b,
+	0x00, 0x65, 0x2e, 0x41,
+	0x00, 0x65, 0xbe, 0x41,
+	0x00, 0x65, 0x52, 0x5d,
+	0x01, 0xbc, 0x18, 0x31,
+	0x02, 0x6a, 0x1a, 0x31,
+	0x02, 0x6a, 0xf8, 0x01,
+	0x01, 0xbc, 0x10, 0x30,
+	0x02, 0x6a, 0x12, 0x30,
+	0x01, 0xbc, 0x10, 0x30,
+	0xff, 0x6a, 0x12, 0x08,
+	0xff, 0x6a, 0x14, 0x08,
+	0xf3, 0xbc, 0xd4, 0x18,
+	0xa0, 0x6a, 0xaa, 0x53,
+	0x04, 0xa0, 0x10, 0x31,
+	0xac, 0x6a, 0x26, 0x01,
+	0x04, 0xa0, 0x10, 0x31,
+	0x03, 0x08, 0x18, 0x31,
+	0x88, 0x6a, 0xcc, 0x00,
+	0xa0, 0x6a, 0xbc, 0x5d,
+	0x00, 0xbc, 0xa8, 0x5d,
+	0x3d, 0x6a, 0x26, 0x01,
+	0x00, 0x65, 0xc2, 0x43,
+	0xff, 0x6a, 0x10, 0x09,
+	0xa4, 0x6a, 0x26, 0x01,
+	0x0c, 0xa0, 0x32, 0x31,
+	0x05, 0x6a, 0x26, 0x01,
+	0x35, 0x6a, 0x26, 0x01,
+	0x0c, 0xa0, 0x32, 0x31,
+	0x36, 0x6a, 0x26, 0x01,
+	0x02, 0x93, 0x26, 0x01,
+	0x35, 0x6a, 0x26, 0x01,
+	0x00, 0x65, 0x60, 0x5e,
+	0x00, 0x65, 0x60, 0x5e,
+	0x02, 0x93, 0x26, 0x01,
+	0x04, 0x0b, 0xc6, 0x6b,
+	0x10, 0x0c, 0xc2, 0x7b,
+	0x01, 0x03, 0xc6, 0x6b,
+	0xc7, 0x93, 0x26, 0x09,
+	0x38, 0x93, 0xca, 0x6b,
+	0x10, 0x01, 0x02, 0x00,
+	0x00, 0x65, 0xbe, 0x41,
+	0x00, 0x65, 0x52, 0x5d,
+	0x01, 0x06, 0x50, 0x31,
+	0x00, 0x65, 0xbe, 0x41,
+	0x10, 0x3f, 0x06, 0x00,
+	0x01, 0x3a, 0xca, 0x30,
+	0x80, 0x65, 0x04, 0x64,
+	0x10, 0xb8, 0x28, 0x6c,
+	0x01, 0xb9, 0xdc, 0x30,
+	0x01, 0x6e, 0xc8, 0x30,
+	0x01, 0x54, 0xca, 0x30,
+	0x80, 0xb9, 0xe8, 0x7b,
+	0x01, 0x55, 0xca, 0x30,
+	0x80, 0xb9, 0xec, 0x7b,
+	0x01, 0x55, 0xca, 0x30,
+	0x00, 0x65, 0x28, 0x6c,
+	0xc0, 0xba, 0xca, 0x00,
+	0x40, 0xb8, 0xf4, 0x6b,
+	0xbf, 0x65, 0xca, 0x08,
+	0x20, 0xb8, 0x08, 0x7c,
+	0x01, 0x65, 0x0c, 0x30,
+	0x00, 0x65, 0xa0, 0x5d,
+	0xa0, 0x3f, 0x10, 0x64,
+	0x23, 0xb8, 0x0c, 0x08,
+	0x00, 0x65, 0xa0, 0x5d,
+	0xa0, 0x3f, 0x10, 0x64,
+	0x00, 0xbb, 0x08, 0x44,
+	0xff, 0x65, 0x08, 0x64,
+	0x00, 0x65, 0x28, 0x44,
+	0x40, 0x6a, 0x18, 0x00,
+	0x01, 0x65, 0x0c, 0x30,
+	0x00, 0x65, 0xa0, 0x5d,
+	0xa0, 0x3f, 0xd6, 0x73,
+	0x40, 0x6a, 0x18, 0x00,
+	0x01, 0x3a, 0xa6, 0x30,
+	0x08, 0x6a, 0x74, 0x00,
+	0x00, 0x65, 0xbe, 0x41,
+	0x64, 0x6a, 0x2c, 0x5d,
+	0x80, 0x64, 0x9e, 0x6c,
+	0x04, 0x64, 0x74, 0x74,
+	0x02, 0x64, 0x82, 0x74,
+	0x00, 0x6a, 0x44, 0x74,
+	0x03, 0x64, 0x90, 0x74,
+	0x23, 0x64, 0x30, 0x74,
+	0x08, 0x64, 0x40, 0x74,
+	0x61, 0x6a, 0x96, 0x5e,
+	0x00, 0x65, 0xa0, 0x5d,
+	0x08, 0x51, 0xc0, 0x71,
+	0x00, 0x65, 0x28, 0x44,
+	0x80, 0x04, 0x3e, 0x7c,
+	0x51, 0x6a, 0x22, 0x5d,
+	0x01, 0x51, 0x3e, 0x64,
+	0x01, 0xa4, 0x3a, 0x7c,
+	0x01, 0x57, 0x40, 0x7c,
+	0x41, 0x6a, 0x96, 0x5e,
+	0x00, 0x65, 0x40, 0x44,
+	0x07, 0x6a, 0x1a, 0x5d,
+	0x01, 0x06, 0xd4, 0x30,
+	0x00, 0x65, 0xbe, 0x41,
+	0x10, 0xb8, 0x48, 0x7c,
+	0xa1, 0x6a, 0x96, 0x5e,
+	0x01, 0xb4, 0x4e, 0x6c,
+	0x02, 0xb4, 0x50, 0x6c,
+	0x01, 0xa4, 0x50, 0x7c,
+	0xff, 0xa8, 0x60, 0x7c,
+	0x04, 0xb4, 0x68, 0x01,
+	0x01, 0x6a, 0x76, 0x00,
+	0x00, 0xbb, 0xda, 0x5d,
+	0xff, 0xa8, 0x60, 0x7c,
+	0x71, 0x6a, 0x96, 0x5e,
+	0x40, 0x51, 0x60, 0x64,
+	0x00, 0x65, 0x76, 0x5e,
+	0x00, 0x65, 0xd0, 0x41,
+	0x00, 0xbb, 0x64, 0x5c,
+	0x00, 0x65, 0xd0, 0x41,
+	0x00, 0x65, 0x76, 0x5e,
+	0x01, 0x65, 0xa2, 0x30,
+	0x01, 0xf8, 0xc8, 0x30,
+	0x01, 0x4e, 0xc8, 0x30,
+	0x00, 0x6a, 0x7e, 0xdd,
+	0x00, 0x51, 0x90, 0x5d,
+	0x01, 0x4e, 0x9c, 0x18,
+	0x02, 0x6a, 0x22, 0x05,
+	0x04, 0xb8, 0x70, 0x01,
+	0x00, 0x65, 0x92, 0x5e,
+	0x20, 0xb8, 0xd0, 0x69,
+	0x01, 0xbb, 0xa2, 0x30,
+	0x01, 0xba, 0x7c, 0x30,
+	0x00, 0xb9, 0x94, 0x5c,
+	0x00, 0x65, 0xd0, 0x41,
+	0x20, 0x3c, 0x40, 0x7c,
+	0x04, 0x14, 0x58, 0x31,
+	0x08, 0xa0, 0x60, 0x31,
+	0xac, 0x6a, 0xcc, 0x00,
+	0x14, 0x6a, 0xbc, 0x5d,
+	0xa0, 0x6a, 0xb4, 0x5d,
+	0x00, 0x65, 0x40, 0x44,
+	0xdf, 0x3c, 0x78, 0x08,
+	0x00, 0x65, 0x40, 0x44,
+	0x4c, 0x65, 0xcc, 0x28,
+	0x01, 0x3e, 0x20, 0x31,
+	0xd0, 0x66, 0xcc, 0x18,
+	0x20, 0x66, 0xcc, 0x18,
+	0x01, 0x51, 0xda, 0x34,
+	0x4c, 0x3d, 0xca, 0x28,
+	0x3f, 0x64, 0x7c, 0x08,
+	0xd0, 0x65, 0xca, 0x18,
+	0x01, 0x3e, 0x20, 0x31,
+	0x30, 0x65, 0xd4, 0x18,
+	0x00, 0x65, 0xac, 0x4c,
+	0xe1, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x20, 0x65, 0xd4, 0x18,
+	0x00, 0x65, 0xb4, 0x54,
+	0xe1, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x20, 0x65, 0xca, 0x18,
+	0xe0, 0x65, 0xd4, 0x18,
+	0x00, 0x65, 0xbe, 0x4c,
+	0xe1, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0xd0, 0x65, 0xd4, 0x18,
+	0x00, 0x65, 0xc6, 0x54,
+	0xe1, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x01, 0x6c, 0xa2, 0x30,
+	0xff, 0x51, 0xd8, 0x74,
+	0x00, 0x51, 0x56, 0x5d,
+	0x01, 0x51, 0x20, 0x31,
+	0x00, 0x65, 0xfa, 0x44,
+	0x01, 0xba, 0xc8, 0x30,
+	0x00, 0x3e, 0xfa, 0x74,
+	0x00, 0x65, 0x74, 0x5e,
+	0x80, 0x3c, 0x78, 0x00,
+	0x01, 0x06, 0xd4, 0x30,
+	0x00, 0x65, 0xa0, 0x5d,
+	0x01, 0x3c, 0x78, 0x00,
+	0xe0, 0x3f, 0x16, 0x65,
+	0x02, 0x3c, 0x78, 0x00,
+	0x20, 0x12, 0x16, 0x65,
+	0x51, 0x6a, 0x22, 0x5d,
+	0x00, 0x51, 0x56, 0x5d,
+	0x51, 0x6a, 0x22, 0x5d,
+	0x01, 0x51, 0x20, 0x31,
+	0x04, 0x3c, 0x78, 0x00,
+	0x01, 0xb9, 0xc8, 0x30,
+	0x00, 0x3d, 0x14, 0x65,
+	0x08, 0x3c, 0x78, 0x00,
+	0x01, 0xba, 0xc8, 0x30,
+	0x00, 0x3e, 0x14, 0x65,
+	0x10, 0x3c, 0x78, 0x00,
+	0x04, 0xb8, 0x14, 0x7d,
+	0xfb, 0xb8, 0x70, 0x09,
+	0x20, 0xb8, 0x0a, 0x6d,
+	0x01, 0x90, 0xc8, 0x30,
+	0xff, 0x6a, 0xa2, 0x00,
+	0x00, 0x3d, 0x94, 0x5c,
+	0x01, 0x64, 0x20, 0x31,
+	0x80, 0x6a, 0x78, 0x00,
+	0x00, 0x65, 0x02, 0x59,
+	0x10, 0xb8, 0x40, 0x7c,
+	0xff, 0x6a, 0x1a, 0x5d,
+	0x00, 0x65, 0x40, 0x44,
+	0x00, 0x65, 0x74, 0x5e,
+	0x31, 0x6a, 0x96, 0x5e,
+	0x00, 0x65, 0x40, 0x44,
+	0x10, 0x3f, 0x06, 0x00,
+	0x01, 0x65, 0x74, 0x34,
+	0x81, 0x6a, 0x96, 0x5e,
+	0x00, 0x65, 0x24, 0x45,
+	0x01, 0x06, 0xd4, 0x30,
+	0x01, 0x0c, 0x24, 0x7d,
+	0x04, 0x0c, 0x1e, 0x6d,
+	0xe0, 0x03, 0x7e, 0x08,
+	0xe0, 0x3f, 0xbe, 0x61,
+	0x01, 0x65, 0xcc, 0x30,
+	0x01, 0x12, 0xda, 0x34,
+	0x01, 0x06, 0xd4, 0x34,
+	0x01, 0x03, 0x32, 0x6d,
+	0x40, 0x03, 0xcc, 0x08,
+	0x01, 0x65, 0x06, 0x30,
+	0x40, 0x65, 0xc8, 0x08,
+	0x00, 0x66, 0x40, 0x75,
+	0x40, 0x65, 0x40, 0x7d,
+	0x00, 0x65, 0x40, 0x5d,
+	0xff, 0x6a, 0xd4, 0x08,
+	0xff, 0x6a, 0xd4, 0x08,
+	0xff, 0x6a, 0xd4, 0x08,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x08, 0x01, 0x02, 0x00,
+	0x02, 0x0b, 0x4a, 0x7d,
+	0x01, 0x65, 0x0c, 0x30,
+	0x02, 0x0b, 0x4e, 0x7d,
+	0xf7, 0x01, 0x02, 0x0c,
+	0x80, 0x3c, 0x9a, 0x6e,
+	0x21, 0x6a, 0x96, 0x46,
+	0x01, 0x65, 0xc8, 0x30,
+	0xff, 0x41, 0x76, 0x75,
+	0x01, 0x41, 0x20, 0x31,
+	0xff, 0x6a, 0xa4, 0x00,
+	0x00, 0x65, 0x66, 0x45,
+	0xff, 0xbf, 0x76, 0x75,
+	0x01, 0x90, 0xa4, 0x30,
+	0x01, 0xbf, 0x20, 0x31,
+	0x00, 0xbb, 0x60, 0x65,
+	0xff, 0x52, 0x74, 0x75,
+	0x01, 0xbf, 0xcc, 0x30,
+	0x01, 0x90, 0xca, 0x30,
+	0x01, 0x52, 0x20, 0x31,
+	0x01, 0x66, 0x7e, 0x31,
+	0x01, 0x65, 0x20, 0x35,
+	0x01, 0xbf, 0x82, 0x34,
+	0x01, 0x64, 0xa2, 0x30,
+	0x00, 0x6a, 0x7e, 0x5e,
+	0x0d, 0x6a, 0x76, 0x00,
+	0x00, 0x51, 0xda, 0x45,
+	0x01, 0x65, 0xa4, 0x30,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x48, 0x6a, 0xce, 0x5d,
+	0x01, 0x6a, 0xd0, 0x01,
+	0x01, 0x6a, 0xdc, 0x05,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x48, 0x6a, 0xce, 0x5d,
+	0x01, 0x6a, 0xa8, 0x5d,
+	0x01, 0x6a, 0x26, 0x05,
+	0x01, 0x65, 0xd8, 0x31,
+	0x09, 0xee, 0xdc, 0x01,
+	0x80, 0xee, 0x94, 0x7d,
+	0xff, 0x6a, 0xdc, 0x0d,
+	0x01, 0x65, 0x32, 0x31,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x6c, 0x46,
+	0x81, 0x6a, 0x96, 0x5e,
+	0x01, 0x0c, 0xa0, 0x7d,
+	0x04, 0x0c, 0x9e, 0x6d,
+	0xe0, 0x03, 0x06, 0x08,
+	0xe0, 0x03, 0x7e, 0x0c,
+	0x01, 0x65, 0x18, 0x31,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x0d,
+	0x01, 0x8c, 0x10, 0x30,
+	0x01, 0x8d, 0x12, 0x30,
+	0x01, 0x8e, 0x14, 0x34,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x30,
+	0x01, 0x6c, 0xda, 0x34,
+	0x3d, 0x64, 0xa4, 0x28,
+	0x55, 0x64, 0xc8, 0x28,
+	0x00, 0x65, 0xce, 0x45,
+	0x2e, 0x64, 0xa4, 0x28,
+	0x66, 0x64, 0xc8, 0x28,
+	0x00, 0x6c, 0xda, 0x18,
+	0x01, 0x52, 0xc8, 0x30,
+	0x00, 0x6c, 0xda, 0x20,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x00, 0x6c, 0xda, 0x20,
+	0x00, 0x6c, 0xda, 0x24,
+	0x01, 0x65, 0xc8, 0x30,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x44, 0x6a, 0xca, 0x5d,
+	0x01, 0x90, 0xe2, 0x31,
+	0x04, 0x3b, 0xee, 0x7d,
+	0x30, 0x6a, 0xd0, 0x01,
+	0x20, 0x6a, 0xd0, 0x01,
+	0x1d, 0x6a, 0xdc, 0x01,
+	0xdc, 0xee, 0xea, 0x65,
+	0x00, 0x65, 0x06, 0x46,
+	0x20, 0x6a, 0xd0, 0x01,
+	0x01, 0x6a, 0xdc, 0x01,
+	0x20, 0xa0, 0xd8, 0x31,
+	0x09, 0xee, 0xdc, 0x01,
+	0x80, 0xee, 0xf6, 0x7d,
+	0x11, 0x6a, 0xdc, 0x01,
+	0x50, 0xee, 0xfa, 0x65,
+	0x20, 0x6a, 0xd0, 0x01,
+	0x09, 0x6a, 0xdc, 0x01,
+	0x88, 0xee, 0x00, 0x66,
+	0x19, 0x6a, 0xdc, 0x01,
+	0xd8, 0xee, 0x04, 0x66,
+	0xff, 0x6a, 0xdc, 0x09,
+	0x18, 0xee, 0x08, 0x6e,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x44, 0x6a, 0xca, 0x5d,
+	0x20, 0x6a, 0xa8, 0x5d,
+	0x01, 0x3b, 0x26, 0x31,
+	0x04, 0x3b, 0x22, 0x6e,
+	0xa0, 0x6a, 0xca, 0x00,
+	0x20, 0x65, 0xc8, 0x18,
+	0x00, 0x65, 0x5c, 0x5e,
+	0x00, 0x65, 0x1a, 0x66,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x6c, 0x46,
+	0xa0, 0x6a, 0xcc, 0x00,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x01, 0x94, 0x26, 0x6e,
+	0x10, 0x94, 0x28, 0x6e,
+	0x08, 0x94, 0x3e, 0x6e,
+	0x08, 0x94, 0x3e, 0x6e,
+	0x08, 0x94, 0x3e, 0x6e,
+	0x07, 0x8c, 0xca, 0x18,
+	0x3d, 0x65, 0xca, 0x28,
+	0x00, 0x65, 0xc8, 0x18,
+	0x00, 0x65, 0x42, 0x5e,
+	0xff, 0x65, 0xca, 0x10,
+	0x05, 0x65, 0xc8, 0x18,
+	0x00, 0x65, 0x26, 0x46,
+	0xf7, 0x93, 0x26, 0x09,
+	0x08, 0x93, 0x40, 0x6e,
+	0x00, 0x62, 0xc4, 0x18,
+	0x00, 0x65, 0x6c, 0x5e,
+	0x00, 0x65, 0x4c, 0x5e,
+	0x00, 0x65, 0x4c, 0x5e,
+	0x00, 0x65, 0x4c, 0x5e,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x30,
+	0x01, 0x99, 0xda, 0x34,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x31,
+	0x01, 0x6c, 0x32, 0x35,
+	0x08, 0x94, 0x6c, 0x7e,
+	0xf7, 0x93, 0x26, 0x09,
+	0x08, 0x93, 0x70, 0x6e,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x04, 0xb8, 0x92, 0x6e,
+	0x01, 0x42, 0x7e, 0x31,
+	0xff, 0x6a, 0x76, 0x01,
+	0x01, 0x90, 0x84, 0x34,
+	0xff, 0x6a, 0x76, 0x05,
+	0xff, 0x42, 0x8e, 0x66,
+	0xff, 0x41, 0x86, 0x66,
+	0xd1, 0x6a, 0x96, 0x5e,
+	0xff, 0x6a, 0xca, 0x04,
+	0x01, 0x41, 0x20, 0x31,
+	0x01, 0xbf, 0x82, 0x30,
+	0x01, 0x6a, 0x76, 0x00,
+	0x00, 0xbb, 0xda, 0x45,
+	0x01, 0x42, 0x20, 0x31,
+	0x01, 0xbf, 0x84, 0x34,
+	0x01, 0x41, 0x7e, 0x31,
+	0x01, 0x90, 0x82, 0x34,
+	0x01, 0x65, 0x22, 0x31,
+	0xff, 0x6a, 0xd4, 0x08,
+	0xff, 0x6a, 0xd4, 0x0c
+};
+
+static int ahc_patch22_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch22_func(struct ahc_softc *ahc)
+{
+	return ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0);
+}
+
+static int ahc_patch21_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch21_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_CMD_CHAN) == 0);
+}
+
+static int ahc_patch20_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch20_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_QUEUE_REGS) == 0);
+}
+
+static int ahc_patch19_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch19_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_WIDE) != 0);
+}
+
+static int ahc_patch18_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch18_func(struct ahc_softc *ahc)
+{
+	return ((ahc->flags & AHC_SCB_BTT) != 0);
+}
+
+static int ahc_patch17_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch17_func(struct ahc_softc *ahc)
+{
+	return ((ahc->bugs & AHC_PCI_2_1_RETRY_BUG) != 0);
+}
+
+static int ahc_patch16_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch16_func(struct ahc_softc *ahc)
+{
+	return ((ahc->flags & AHC_TMODE_WIDEODD_BUG) != 0);
+}
+
+static int ahc_patch15_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch15_func(struct ahc_softc *ahc)
+{
+	return ((ahc->bugs & AHC_AUTOFLUSH_BUG) != 0);
+}
+
+static int ahc_patch14_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch14_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_DT) == 0);
+}
+
+static int ahc_patch13_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch13_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_ULTRA2) == 0);
+}
+
+static int ahc_patch12_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch12_func(struct ahc_softc *ahc)
+{
+	return ((ahc->bugs & AHC_PCI_MWI_BUG) != 0 && ahc->pci_cachesize != 0);
+}
+
+static int ahc_patch11_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch11_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_ULTRA) != 0);
+}
+
+static int ahc_patch10_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch10_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_HS_MAILBOX) != 0);
+}
+
+static int ahc_patch9_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch9_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_MULTI_TID) != 0);
+}
+
+static int ahc_patch8_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch8_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_CMD_CHAN) != 0);
+}
+
+static int ahc_patch7_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch7_func(struct ahc_softc *ahc)
+{
+	return ((ahc->flags & AHC_INITIATORROLE) != 0);
+}
+
+static int ahc_patch6_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch6_func(struct ahc_softc *ahc)
+{
+	return ((ahc->flags & AHC_TARGETROLE) != 0);
+}
+
+static int ahc_patch5_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch5_func(struct ahc_softc *ahc)
+{
+	return ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0);
+}
+
+static int ahc_patch4_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch4_func(struct ahc_softc *ahc)
+{
+	return ((ahc->flags & AHC_PAGESCBS) != 0);
+}
+
+static int ahc_patch3_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch3_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_QUEUE_REGS) != 0);
+}
+
+static int ahc_patch2_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch2_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_TWIN) != 0);
+}
+
+static int ahc_patch1_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch1_func(struct ahc_softc *ahc)
+{
+	return ((ahc->features & AHC_ULTRA2) != 0);
+}
+
+static int ahc_patch0_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch0_func(struct ahc_softc *ahc)
+{
+	return (0);
+}
+
+typedef int patch_func_t (struct ahc_softc *);
+struct patch {
+	patch_func_t	*patch_func;
+	uint32_t	begin	   :10,
+			skip_instr :10,
+			skip_patch :12;
+} patches[] = {
+	{ ahc_patch1_func, 4, 1, 1 },
+	{ ahc_patch2_func, 6, 2, 1 },
+	{ ahc_patch2_func, 9, 1, 1 },
+	{ ahc_patch3_func, 11, 1, 2 },
+	{ ahc_patch0_func, 12, 2, 1 },
+	{ ahc_patch4_func, 15, 1, 2 },
+	{ ahc_patch0_func, 16, 1, 1 },
+	{ ahc_patch5_func, 22, 2, 1 },
+	{ ahc_patch3_func, 27, 1, 2 },
+	{ ahc_patch0_func, 28, 1, 1 },
+	{ ahc_patch6_func, 37, 65, 21 },
+	{ ahc_patch7_func, 37, 1, 1 },
+	{ ahc_patch8_func, 45, 3, 2 },
+	{ ahc_patch0_func, 48, 3, 1 },
+	{ ahc_patch9_func, 52, 1, 2 },
+	{ ahc_patch0_func, 53, 2, 3 },
+	{ ahc_patch1_func, 53, 1, 2 },
+	{ ahc_patch0_func, 54, 1, 1 },
+	{ ahc_patch2_func, 56, 2, 1 },
+	{ ahc_patch8_func, 58, 1, 2 },
+	{ ahc_patch0_func, 59, 1, 1 },
+	{ ahc_patch8_func, 63, 1, 2 },
+	{ ahc_patch0_func, 64, 1, 1 },
+	{ ahc_patch8_func, 73, 1, 2 },
+	{ ahc_patch0_func, 74, 1, 1 },
+	{ ahc_patch8_func, 77, 1, 2 },
+	{ ahc_patch0_func, 78, 1, 1 },
+	{ ahc_patch10_func, 88, 1, 2 },
+	{ ahc_patch0_func, 89, 1, 1 },
+	{ ahc_patch8_func, 97, 1, 2 },
+	{ ahc_patch0_func, 98, 1, 1 },
+	{ ahc_patch7_func, 102, 9, 4 },
+	{ ahc_patch1_func, 104, 1, 2 },
+	{ ahc_patch0_func, 105, 1, 1 },
+	{ ahc_patch2_func, 107, 2, 1 },
+	{ ahc_patch2_func, 116, 4, 1 },
+	{ ahc_patch1_func, 120, 1, 2 },
+	{ ahc_patch0_func, 121, 2, 3 },
+	{ ahc_patch2_func, 121, 1, 2 },
+	{ ahc_patch0_func, 122, 1, 1 },
+	{ ahc_patch6_func, 123, 4, 2 },
+	{ ahc_patch0_func, 127, 1, 1 },
+	{ ahc_patch11_func, 129, 2, 1 },
+	{ ahc_patch1_func, 131, 1, 2 },
+	{ ahc_patch0_func, 132, 1, 1 },
+	{ ahc_patch6_func, 133, 4, 1 },
+	{ ahc_patch6_func, 144, 77, 9 },
+	{ ahc_patch4_func, 156, 1, 1 },
+	{ ahc_patch1_func, 172, 1, 1 },
+	{ ahc_patch8_func, 180, 1, 2 },
+	{ ahc_patch0_func, 181, 1, 1 },
+	{ ahc_patch8_func, 190, 1, 2 },
+	{ ahc_patch0_func, 191, 1, 1 },
+	{ ahc_patch8_func, 207, 6, 2 },
+	{ ahc_patch0_func, 213, 6, 1 },
+	{ ahc_patch7_func, 221, 18, 2 },
+	{ ahc_patch1_func, 234, 1, 1 },
+	{ ahc_patch1_func, 241, 1, 2 },
+	{ ahc_patch0_func, 242, 2, 2 },
+	{ ahc_patch11_func, 243, 1, 1 },
+	{ ahc_patch8_func, 251, 33, 2 },
+	{ ahc_patch1_func, 267, 16, 1 },
+	{ ahc_patch12_func, 284, 14, 1 },
+	{ ahc_patch1_func, 298, 2, 2 },
+	{ ahc_patch0_func, 300, 3, 3 },
+	{ ahc_patch8_func, 300, 1, 2 },
+	{ ahc_patch0_func, 301, 2, 1 },
+	{ ahc_patch1_func, 305, 1, 2 },
+	{ ahc_patch0_func, 306, 1, 1 },
+	{ ahc_patch8_func, 310, 1, 1 },
+	{ ahc_patch8_func, 313, 2, 2 },
+	{ ahc_patch0_func, 315, 4, 1 },
+	{ ahc_patch12_func, 319, 1, 1 },
+	{ ahc_patch13_func, 322, 2, 3 },
+	{ ahc_patch8_func, 322, 1, 2 },
+	{ ahc_patch0_func, 323, 1, 1 },
+	{ ahc_patch1_func, 332, 40, 6 },
+	{ ahc_patch6_func, 341, 1, 1 },
+	{ ahc_patch7_func, 342, 1, 1 },
+	{ ahc_patch14_func, 344, 2, 1 },
+	{ ahc_patch15_func, 346, 5, 1 },
+	{ ahc_patch0_func, 372, 51, 15 },
+	{ ahc_patch12_func, 372, 1, 1 },
+	{ ahc_patch6_func, 374, 2, 2 },
+	{ ahc_patch16_func, 375, 1, 1 },
+	{ ahc_patch8_func, 378, 1, 1 },
+	{ ahc_patch17_func, 385, 1, 1 },
+	{ ahc_patch12_func, 390, 9, 3 },
+	{ ahc_patch8_func, 391, 3, 2 },
+	{ ahc_patch0_func, 394, 3, 1 },
+	{ ahc_patch8_func, 402, 6, 2 },
+	{ ahc_patch0_func, 408, 8, 1 },
+	{ ahc_patch12_func, 416, 1, 1 },
+	{ ahc_patch8_func, 418, 1, 2 },
+	{ ahc_patch0_func, 419, 1, 1 },
+	{ ahc_patch6_func, 422, 1, 1 },
+	{ ahc_patch6_func, 423, 1, 1 },
+	{ ahc_patch7_func, 424, 2, 1 },
+	{ ahc_patch8_func, 426, 1, 1 },
+	{ ahc_patch12_func, 427, 9, 4 },
+	{ ahc_patch8_func, 427, 1, 1 },
+	{ ahc_patch8_func, 434, 2, 1 },
+	{ ahc_patch0_func, 436, 4, 3 },
+	{ ahc_patch8_func, 436, 1, 2 },
+	{ ahc_patch0_func, 437, 3, 1 },
+	{ ahc_patch1_func, 441, 2, 1 },
+	{ ahc_patch6_func, 443, 5, 2 },
+	{ ahc_patch0_func, 448, 1, 1 },
+	{ ahc_patch7_func, 449, 113, 22 },
+	{ ahc_patch1_func, 450, 3, 2 },
+	{ ahc_patch0_func, 453, 5, 3 },
+	{ ahc_patch8_func, 453, 2, 2 },
+	{ ahc_patch0_func, 455, 3, 1 },
+	{ ahc_patch1_func, 460, 2, 2 },
+	{ ahc_patch0_func, 462, 6, 3 },
+	{ ahc_patch8_func, 462, 2, 2 },
+	{ ahc_patch0_func, 464, 3, 1 },
+	{ ahc_patch1_func, 470, 2, 2 },
+	{ ahc_patch0_func, 472, 9, 7 },
+	{ ahc_patch8_func, 472, 5, 6 },
+	{ ahc_patch18_func, 472, 1, 2 },
+	{ ahc_patch0_func, 473, 1, 1 },
+	{ ahc_patch18_func, 475, 1, 2 },
+	{ ahc_patch0_func, 476, 1, 1 },
+	{ ahc_patch0_func, 477, 4, 1 },
+	{ ahc_patch1_func, 486, 1, 1 },
+	{ ahc_patch2_func, 498, 2, 2 },
+	{ ahc_patch0_func, 500, 2, 2 },
+	{ ahc_patch19_func, 500, 2, 1 },
+	{ ahc_patch19_func, 536, 7, 1 },
+	{ ahc_patch3_func, 564, 1, 2 },
+	{ ahc_patch0_func, 565, 1, 1 },
+	{ ahc_patch20_func, 568, 1, 1 },
+	{ ahc_patch7_func, 570, 95, 26 },
+	{ ahc_patch4_func, 571, 1, 1 },
+	{ ahc_patch8_func, 578, 2, 2 },
+	{ ahc_patch0_func, 580, 3, 1 },
+	{ ahc_patch18_func, 587, 2, 2 },
+	{ ahc_patch0_func, 589, 1, 1 },
+	{ ahc_patch18_func, 593, 10, 3 },
+	{ ahc_patch5_func, 595, 8, 1 },
+	{ ahc_patch0_func, 603, 9, 2 },
+	{ ahc_patch5_func, 604, 8, 1 },
+	{ ahc_patch4_func, 614, 1, 2 },
+	{ ahc_patch0_func, 615, 1, 1 },
+	{ ahc_patch18_func, 616, 1, 2 },
+	{ ahc_patch0_func, 617, 3, 2 },
+	{ ahc_patch4_func, 619, 1, 1 },
+	{ ahc_patch5_func, 620, 1, 1 },
+	{ ahc_patch5_func, 623, 1, 1 },
+	{ ahc_patch5_func, 625, 1, 1 },
+	{ ahc_patch4_func, 627, 2, 2 },
+	{ ahc_patch0_func, 629, 2, 1 },
+	{ ahc_patch5_func, 631, 1, 1 },
+	{ ahc_patch5_func, 634, 1, 1 },
+	{ ahc_patch5_func, 637, 1, 1 },
+	{ ahc_patch18_func, 641, 1, 1 },
+	{ ahc_patch18_func, 644, 1, 1 },
+	{ ahc_patch4_func, 650, 1, 1 },
+	{ ahc_patch6_func, 665, 16, 1 },
+	{ ahc_patch4_func, 683, 20, 1 },
+	{ ahc_patch8_func, 704, 4, 2 },
+	{ ahc_patch0_func, 708, 4, 1 },
+	{ ahc_patch8_func, 712, 4, 2 },
+	{ ahc_patch0_func, 716, 3, 1 },
+	{ ahc_patch21_func, 724, 14, 1 },
+	{ ahc_patch6_func, 738, 3, 1 },
+	{ ahc_patch8_func, 750, 24, 8 },
+	{ ahc_patch18_func, 754, 1, 2 },
+	{ ahc_patch0_func, 755, 1, 1 },
+	{ ahc_patch13_func, 760, 4, 2 },
+	{ ahc_patch0_func, 764, 7, 3 },
+	{ ahc_patch22_func, 764, 5, 2 },
+	{ ahc_patch0_func, 769, 2, 1 },
+	{ ahc_patch0_func, 774, 40, 3 },
+	{ ahc_patch17_func, 786, 16, 2 },
+	{ ahc_patch0_func, 802, 1, 1 },
+	{ ahc_patch4_func, 826, 1, 1 },
+	{ ahc_patch4_func, 827, 3, 2 },
+	{ ahc_patch0_func, 830, 1, 1 },
+	{ ahc_patch4_func, 831, 12, 1 }
+};
+struct cs {
+	u_int16_t	begin;
+	u_int16_t	end;
+} critical_sections[] = {
+	{ 11, 18 },
+	{ 21, 30 },
+	{ 683, 699 },
+	{ 827, 830 },
+	{ 831, 837 },
+	{ 839, 841 },
+	{ 841, 843 }
+};
+const int num_critical_sections = sizeof(critical_sections)
+				 / sizeof(*critical_sections);
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/Makefile linux/drivers/scsi/aic7xxx/aicasm/Makefile
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/Makefile	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aicasm/Makefile	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,28 @@
+PROG=	aicasm
+
+CSRCS=	aicasm.c aicasm_symbol.c
+GENSRCS= aicasm_gram.c aicasm_scan.c
+
+GENHDRS=	y.tab.h
+
+SRCS=	${GENSRCS} ${CSRCS}
+CLEANFILES= ${GENSRCS} ${GENHDRS} y.output
+# Override default kernel CFLAGS.  This is a userland app.
+AICASM_CFLAGS:= -I/usr/include -ldb1
+YFLAGS= -d
+
+NOMAN=	noman
+
+ifdef DEBUG
+CFLAGS+= -DDEBUG -g
+YFLAGS+= -t -v
+LFLAGS= -d
+endif
+
+.SUFFIXES= .l .y .c
+
+$(PROG): $(SRCS)
+	$(CC) $(AICASM_CFLAGS) $(SRCS) -o $(PROG)
+
+clean:
+	rm -f $(CLEANFILES) $(PROG)
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm.c linux/drivers/scsi/aic7xxx/aicasm/aicasm.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aicasm/aicasm.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,777 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler
+ *
+ * Copyright (c) 1997, 1998, 2000 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm.c#6 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm.c,v 1.29 2000/10/05 04:25:42 gibbs Exp $
+ */
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+typedef struct patch {
+	STAILQ_ENTRY(patch) links;
+	int		patch_func;
+	u_int		begin;
+	u_int		skip_instr;
+	u_int		skip_patch;
+} patch_t;
+
+STAILQ_HEAD(patch_list, patch) patches;
+
+static void usage(void);
+static void back_patch(void);
+static void output_code(void);
+static void output_listing(char *ifilename);
+static void dump_scope(scope_t *scope);
+static void emit_patch(scope_t *scope, int patch);
+static int check_patch(patch_t **start_patch, int start_instr,
+		       int *skip_addr, int *func_vals);
+
+struct path_list search_path;
+int includes_search_curdir;
+char *appname;
+FILE *ofile;
+char *ofilename;
+char *regfilename;
+FILE *regfile;
+char *listfilename;
+FILE *listfile;
+
+static STAILQ_HEAD(,instruction) seq_program;
+struct cs_tailq cs_tailq;
+struct scope_list scope_stack;
+symlist_t patch_functions;
+
+#if DEBUG
+extern int yy_flex_debug;
+extern int yydebug;
+#endif
+extern FILE *yyin;
+extern int yyparse(void);
+
+int main(int argc, char *argv[]);
+
+int
+main(int argc, char *argv[])
+{
+	extern char *optarg;
+	extern int optind;
+	int  ch;
+	int  retval;
+	char *inputfilename;
+	scope_t *sentinal;
+
+	STAILQ_INIT(&patches);
+	SLIST_INIT(&search_path);
+	STAILQ_INIT(&seq_program);
+	TAILQ_INIT(&cs_tailq);
+	SLIST_INIT(&scope_stack);
+
+	/* Set Sentinal scope node */
+	sentinal = scope_alloc();
+	sentinal->type = SCOPE_ROOT;
+	
+	includes_search_curdir = 1;
+	appname = *argv;
+	regfile = NULL;
+	listfile = NULL;
+#if DEBUG
+	yy_flex_debug = 0;
+	yydebug = 0;
+#endif
+	while ((ch = getopt(argc, argv, "d:l:n:o:r:I:O:")) != -1) {
+		switch(ch) {
+		case 'd':
+#if DEBUG
+			if (strcmp(optarg, "s") == 0) {
+				yy_flex_debug = 1;
+			} else if (strcmp(optarg, "p") == 0) {
+				yydebug = 1;
+			} else {
+				fprintf(stderr, "%s: -d Requires either an "
+					"'s' or 'p' argument\n", appname);
+				usage();
+			}
+#else
+			stop("-d: Assembler not built with debugging "
+			     "information", EX_SOFTWARE);
+#endif
+			break;
+		case 'l':
+			/* Create a program listing */
+			if ((listfile = fopen(optarg, "w")) == NULL) {
+				perror(optarg);
+				stop(NULL, EX_CANTCREAT);
+			}
+			listfilename = optarg;
+			break;
+		case 'n':
+			/* Don't complain about the -nostdinc directrive */
+			if (strcmp(optarg, "ostdinc")) {
+				fprintf(stderr, "%s: Unknown option -%c%s\n",
+					appname, ch, optarg);
+				usage();
+				/* NOTREACHED */
+			}
+			break;
+		case 'o':
+			if ((ofile = fopen(optarg, "w")) == NULL) {
+				perror(optarg);
+				stop(NULL, EX_CANTCREAT);
+			}
+			ofilename = optarg;
+			break;
+		case 'r':
+			if ((regfile = fopen(optarg, "w")) == NULL) {
+				perror(optarg);
+				stop(NULL, EX_CANTCREAT);
+			}
+			regfilename = optarg;
+			break;
+		case 'I':
+		{
+			path_entry_t include_dir;
+
+			if (strcmp(optarg, "-") == 0) {
+				if (includes_search_curdir == 0) {
+					fprintf(stderr, "%s: Warning - '-I-' "
+							"specified multiple "
+							"times\n", appname);
+				}
+				includes_search_curdir = 0;
+				for (include_dir = search_path.slh_first;
+				     include_dir != NULL;
+				     include_dir = include_dir->links.sle_next)
+					/*
+					 * All entries before a '-I-' only
+					 * apply to includes specified with
+					 * quotes instead of "<>".
+					 */
+					include_dir->quoted_includes_only = 1;
+			} else {
+				include_dir =
+				    (path_entry_t)malloc(sizeof(*include_dir));
+				if (include_dir == NULL) {
+					perror(optarg);
+					stop(NULL, EX_OSERR);
+				}
+				include_dir->directory = strdup(optarg);
+				if (include_dir->directory == NULL) {
+					perror(optarg);
+					stop(NULL, EX_OSERR);
+				}
+				include_dir->quoted_includes_only = 0;
+				SLIST_INSERT_HEAD(&search_path, include_dir,
+						  links);
+			}
+			break;
+		}
+		case '?':
+		default:
+			usage();
+			/* NOTREACHED */
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	if (argc != 1) {
+		fprintf(stderr, "%s: No input file specifiled\n", appname);
+		usage();
+		/* NOTREACHED */
+	}
+
+	symtable_open();
+	inputfilename = *argv;
+	include_file(*argv, SOURCE_FILE);
+	retval = yyparse();
+	if (retval == 0) {
+		if (SLIST_FIRST(&scope_stack) == NULL
+		 || SLIST_FIRST(&scope_stack)->type != SCOPE_ROOT) {
+			stop("Unterminated conditional expression",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+
+		/* Process outmost scope */
+		process_scope(SLIST_FIRST(&scope_stack));
+		/*
+		 * Decend the tree of scopes and insert/emit
+		 * patches as appropriate.  We perform a depth first
+		 * tranversal, recursively handling each scope.
+		 */
+		/* start at the root scope */
+		dump_scope(SLIST_FIRST(&scope_stack));
+
+		/* Patch up forward jump addresses */
+		back_patch();
+
+		if (ofile != NULL)
+			output_code();
+		if (regfile != NULL) {
+			symtable_dump(regfile);
+		}
+		if (listfile != NULL)
+			output_listing(inputfilename);
+	}
+
+	stop(NULL, 0);
+	/* NOTREACHED */
+	return (0);
+}
+
+static void
+usage()
+{
+
+	(void)fprintf(stderr,
+"usage: %-16s [-nostdinc] [-I-] [-I directory] [-o output_file]
+			[-r register_output_file] [-l program_list_file]
+			input_file\n",
+			appname);
+	exit(EX_USAGE);
+}
+
+static void
+back_patch()
+{
+	struct instruction *cur_instr;
+
+	for(cur_instr = seq_program.stqh_first;
+	    cur_instr != NULL;
+	    cur_instr = cur_instr->links.stqe_next) {
+		if (cur_instr->patch_label != NULL) {
+			struct ins_format3 *f3_instr;
+			u_int address;
+
+			if (cur_instr->patch_label->type != LABEL) {
+				char buf[255];
+
+				snprintf(buf, sizeof(buf),
+					 "Undefined label %s",
+					 cur_instr->patch_label->name);
+				stop(buf, EX_DATAERR);
+				/* NOTREACHED */
+			}
+			f3_instr = &cur_instr->format.format3;
+			address = f3_instr->address;
+			address += cur_instr->patch_label->info.linfo->address;
+			f3_instr->address = address;
+		}
+	}
+}
+
+static void
+output_code()
+{
+	struct instruction *cur_instr;
+	patch_t *cur_patch;
+	critical_section_t *cs;
+	symbol_node_t *cur_node;
+	int instrcount;
+
+	instrcount = 0;
+	fprintf(ofile,
+"/*
+  * DO NOT EDIT - This file is automatically generated.
+  */\n");
+
+	fprintf(ofile, "static uint8_t seqprog[] = {\n");
+	for(cur_instr = seq_program.stqh_first;
+	    cur_instr != NULL;
+	    cur_instr = cur_instr->links.stqe_next) {
+
+		fprintf(ofile, "%s\t0x%02x, 0x%02x, 0x%02x, 0x%02x",
+			cur_instr == seq_program.stqh_first ? "" : ",\n",
+#if BYTE_ORDER == LITTLE_ENDIAN
+			cur_instr->format.bytes[0],
+			cur_instr->format.bytes[1],
+			cur_instr->format.bytes[2],
+			cur_instr->format.bytes[3]);
+#else
+			cur_instr->format.bytes[3],
+			cur_instr->format.bytes[2],
+			cur_instr->format.bytes[1],
+			cur_instr->format.bytes[0]);
+#endif
+		instrcount++;
+	}
+	fprintf(ofile, "\n};\n\n");
+
+	/*
+	 *  Output patch information.  Patch functions first.
+	 */
+	for(cur_node = SLIST_FIRST(&patch_functions);
+	    cur_node != NULL;
+	    cur_node = SLIST_NEXT(cur_node,links)) {
+		fprintf(ofile,
+"static int ahc_patch%d_func(struct ahc_softc *ahc);
+
+static int
+ahc_patch%d_func(struct ahc_softc *ahc)
+{
+	return (%s);
+}\n\n",
+			cur_node->symbol->info.condinfo->func_num,
+			cur_node->symbol->info.condinfo->func_num,
+			cur_node->symbol->name);
+	}
+
+	fprintf(ofile,
+"typedef int patch_func_t (struct ahc_softc *);
+struct patch {
+	patch_func_t	*patch_func;
+	uint32_t	begin	   :10,
+			skip_instr :10,
+			skip_patch :12;
+} patches[] = {\n");
+
+	for(cur_patch = STAILQ_FIRST(&patches);
+	    cur_patch != NULL;
+	    cur_patch = STAILQ_NEXT(cur_patch,links)) {
+		fprintf(ofile, "%s\t{ ahc_patch%d_func, %d, %d, %d }",
+			cur_patch == STAILQ_FIRST(&patches) ? "" : ",\n",
+			cur_patch->patch_func, cur_patch->begin,
+			cur_patch->skip_instr, cur_patch->skip_patch);
+	}
+
+	fprintf(ofile, "\n};\n");
+
+	fprintf(ofile,
+"struct cs {
+	u_int16_t	begin;
+	u_int16_t	end;
+} critical_sections[] = {\n");
+
+	for(cs = TAILQ_FIRST(&cs_tailq);
+	    cs != NULL;
+	    cs = TAILQ_NEXT(cs, links)) {
+		fprintf(ofile, "%s\t{ %d, %d }",
+			cs == TAILQ_FIRST(&cs_tailq) ? "" : ",\n",
+			cs->begin_addr, cs->end_addr);
+	}
+
+	fprintf(ofile, "\n};\n");
+
+	fprintf(ofile,
+"const int num_critical_sections = sizeof(critical_sections)
+				 / sizeof(*critical_sections);\n");
+
+	fprintf(stderr, "%s: %d instructions used\n", appname, instrcount);
+}
+
+static void
+dump_scope(scope_t *scope)
+{
+	scope_t *cur_scope;
+
+	/*
+	 * Emit the first patch for this scope
+	 */
+	emit_patch(scope, 0);
+
+	/*
+	 * Dump each scope within this one.
+	 */
+	cur_scope = TAILQ_FIRST(&scope->inner_scope);
+
+	while (cur_scope != NULL) {
+
+		dump_scope(cur_scope);
+
+		cur_scope = TAILQ_NEXT(cur_scope, scope_links);
+	}
+
+	/*
+	 * Emit the second, closing, patch for this scope
+	 */
+	emit_patch(scope, 1);
+}
+
+void
+emit_patch(scope_t *scope, int patch)
+{
+	patch_info_t *pinfo;
+	patch_t *new_patch;
+
+	pinfo = &scope->patches[patch];
+
+	if (pinfo->skip_instr == 0)
+		/* No-Op patch */
+		return;
+
+	new_patch = (patch_t *)malloc(sizeof(*new_patch));
+
+	if (new_patch == NULL)
+		stop("Could not malloc patch structure", EX_OSERR);
+
+	memset(new_patch, 0, sizeof(*new_patch));
+
+	if (patch == 0) {
+		new_patch->patch_func = scope->func_num;
+		new_patch->begin = scope->begin_addr;
+	} else {
+		new_patch->patch_func = 0;
+		new_patch->begin = scope->end_addr;
+	}
+	new_patch->skip_instr = pinfo->skip_instr;
+	new_patch->skip_patch = pinfo->skip_patch;
+	STAILQ_INSERT_TAIL(&patches, new_patch, links);
+}
+
+void
+output_listing(char *ifilename)
+{
+	char buf[1024];
+	FILE *ifile;
+	struct instruction *cur_instr;
+	patch_t *cur_patch;
+	symbol_node_t *cur_func;
+	int *func_values;
+	int instrcount;
+	int instrptr;
+	int line;
+	int func_count;
+	int skip_addr;
+
+	instrcount = 0;
+	instrptr = 0;
+	line = 1;
+	skip_addr = 0;
+	if ((ifile = fopen(ifilename, "r")) == NULL) {
+		perror(ifilename);
+		stop(NULL, EX_DATAERR);
+	}
+
+	/*
+	 * Determine which options to apply to this listing.
+	 */
+	for (func_count = 0, cur_func = SLIST_FIRST(&patch_functions);
+	    cur_func != NULL;
+	    cur_func = SLIST_NEXT(cur_func, links))
+		func_count++;
+
+	func_values = NULL;
+	if (func_count != 0) {
+		func_values = (int *)malloc(func_count * sizeof(int));
+
+		if (func_values == NULL)
+			stop("Could not malloc", EX_OSERR);
+		
+		func_values[0] = 0; /* FALSE func */
+		func_count--;
+
+		/*
+		 * Ask the user to fill in the return values for
+		 * the rest of the functions.
+		 */
+		
+		
+		for (cur_func = SLIST_FIRST(&patch_functions);
+		     cur_func != NULL && SLIST_NEXT(cur_func, links) != NULL;
+		     cur_func = SLIST_NEXT(cur_func, links), func_count--) {
+			int input;
+			
+			fprintf(stdout, "\n(%s)\n", cur_func->symbol->name);
+			fprintf(stdout,
+				"Enter the return value for "
+				"this expression[T/F]:");
+
+			while (1) {
+
+				input = getchar();
+				input = toupper(input);
+
+				if (input == 'T') {
+					func_values[func_count] = 1;
+					break;
+				} else if (input == 'F') {
+					func_values[func_count] = 0;
+					break;
+				}
+			}
+			if (isatty(fileno(stdin)) == 0)
+				putchar(input);
+		}
+		fprintf(stdout, "\nThanks!\n");
+	}
+
+	/* Now output the listing */
+	cur_patch = STAILQ_FIRST(&patches);
+	for(cur_instr = STAILQ_FIRST(&seq_program);
+	    cur_instr != NULL;
+	    cur_instr = STAILQ_NEXT(cur_instr, links), instrcount++) {
+
+		if (check_patch(&cur_patch, instrcount,
+				&skip_addr, func_values) == 0) {
+			/* Don't count this instruction as it is in a patch
+			 * that was removed.
+			 */
+                        continue;
+		}
+
+		while (line < cur_instr->srcline) {
+			fgets(buf, sizeof(buf), ifile);
+				fprintf(listfile, "\t\t%s", buf);
+				line++;
+		}
+		fprintf(listfile, "%03x %02x%02x%02x%02x", instrptr,
+#if BYTE_ORDER == LITTLE_ENDIAN
+			cur_instr->format.bytes[0],
+			cur_instr->format.bytes[1],
+			cur_instr->format.bytes[2],
+			cur_instr->format.bytes[3]);
+#else
+			cur_instr->format.bytes[3],
+			cur_instr->format.bytes[2],
+			cur_instr->format.bytes[1],
+			cur_instr->format.bytes[0]);
+#endif
+		fgets(buf, sizeof(buf), ifile);
+		fprintf(listfile, "\t%s", buf);
+		line++;
+		instrptr++;
+	}
+	/* Dump the remainder of the file */
+	while(fgets(buf, sizeof(buf), ifile) != NULL)
+		fprintf(listfile, "\t\t%s", buf);
+
+	fclose(ifile);
+}
+
+static int
+check_patch(patch_t **start_patch, int start_instr,
+	    int *skip_addr, int *func_vals)
+{
+	patch_t *cur_patch;
+
+	cur_patch = *start_patch;
+
+	while (cur_patch != NULL && start_instr == cur_patch->begin) {
+		if (func_vals[cur_patch->patch_func] == 0) {
+			int skip;
+
+			/* Start rejecting code */
+			*skip_addr = start_instr + cur_patch->skip_instr;
+			for (skip = cur_patch->skip_patch;
+			     skip > 0 && cur_patch != NULL;
+			     skip--)
+				cur_patch = STAILQ_NEXT(cur_patch, links);
+		} else {
+			/* Accepted this patch.  Advance to the next
+			 * one and wait for our intruction pointer to
+			 * hit this point.
+			 */
+			cur_patch = STAILQ_NEXT(cur_patch, links);
+		}
+	}
+
+	*start_patch = cur_patch;
+	if (start_instr < *skip_addr)
+		/* Still skipping */
+		return (0);
+
+	return (1);
+}
+
+/*
+ * Print out error information if appropriate, and clean up before
+ * terminating the program.
+ */
+void
+stop(const char *string, int err_code)
+{
+	if (string != NULL) {
+		fprintf(stderr, "%s: ", appname);
+		if (yyfilename != NULL) {
+			fprintf(stderr, "Stopped at file %s, line %d - ",
+				yyfilename, yylineno);
+		}
+		fprintf(stderr, "%s\n", string);
+	}
+
+	if (ofile != NULL) {
+		fclose(ofile);
+		if (err_code != 0) {
+			fprintf(stderr, "%s: Removing %s due to error\n",
+				appname, ofilename);
+			unlink(ofilename);
+		}
+	}
+
+	if (regfile != NULL) {
+		fclose(regfile);
+		if (err_code != 0) {
+			fprintf(stderr, "%s: Removing %s due to error\n",
+				appname, regfilename);
+			unlink(regfilename);
+		}
+	}
+
+	if (listfile != NULL) {
+		fclose(listfile);
+		if (err_code != 0) {
+			fprintf(stderr, "%s: Removing %s due to error\n",
+				appname, listfilename);
+			unlink(listfilename);
+		}
+	}
+
+	symlist_free(&patch_functions);
+	symtable_close();
+
+	exit(err_code);
+}
+
+struct instruction *
+seq_alloc()
+{
+	struct instruction *new_instr;
+
+	new_instr = (struct instruction *)malloc(sizeof(struct instruction));
+	if (new_instr == NULL)
+		stop("Unable to malloc instruction object", EX_SOFTWARE);
+	memset(new_instr, 0, sizeof(*new_instr));
+	STAILQ_INSERT_TAIL(&seq_program, new_instr, links);
+	new_instr->srcline = yylineno;
+	return new_instr;
+}
+
+critical_section_t *
+cs_alloc()
+{
+	critical_section_t *new_cs;
+
+	new_cs= (critical_section_t *)malloc(sizeof(critical_section_t));
+	if (new_cs == NULL)
+		stop("Unable to malloc critical_section object", EX_SOFTWARE);
+	memset(new_cs, 0, sizeof(*new_cs));
+	
+	TAILQ_INSERT_TAIL(&cs_tailq, new_cs, links);
+	return new_cs;
+}
+
+scope_t *
+scope_alloc()
+{
+	scope_t *new_scope;
+
+	new_scope = (scope_t *)malloc(sizeof(scope_t));
+	if (new_scope == NULL)
+		stop("Unable to malloc scope object", EX_SOFTWARE);
+	memset(new_scope, 0, sizeof(*new_scope));
+	TAILQ_INIT(&new_scope->inner_scope);
+	
+	if (SLIST_FIRST(&scope_stack) != NULL) {
+		TAILQ_INSERT_TAIL(&SLIST_FIRST(&scope_stack)->inner_scope,
+				  new_scope, scope_links);
+	}
+	/* This patch is now the current scope */
+	SLIST_INSERT_HEAD(&scope_stack, new_scope, scope_stack_links);
+	return new_scope;
+}
+
+void
+process_scope(scope_t *scope)
+{
+	/*
+	 * We are "leaving" this scope.  We should now have
+	 * enough information to process the lists of scopes
+	 * we encapsulate.
+	 */
+	scope_t *cur_scope;
+	u_int skip_patch_count;
+	u_int skip_instr_count;
+
+	cur_scope = TAILQ_LAST(&scope->inner_scope, scope_tailq);
+	skip_patch_count = 0;
+	skip_instr_count = 0;
+	while (cur_scope != NULL) {
+		u_int patch0_patch_skip;
+
+		patch0_patch_skip = 0;
+		switch (cur_scope->type) {
+		case SCOPE_IF:
+		case SCOPE_ELSE_IF:
+			if (skip_instr_count != 0) {
+				/* Create a tail patch */
+				patch0_patch_skip++;
+				cur_scope->patches[1].skip_patch =
+				    skip_patch_count + 1;
+				cur_scope->patches[1].skip_instr =
+				    skip_instr_count;
+			}
+
+			/* Count Head patch */
+			patch0_patch_skip++;
+
+			/* Count any patches contained in our inner scope */
+			patch0_patch_skip += cur_scope->inner_scope_patches;
+
+			cur_scope->patches[0].skip_patch = patch0_patch_skip;
+			cur_scope->patches[0].skip_instr =
+			    cur_scope->end_addr - cur_scope->begin_addr;
+
+			skip_instr_count += cur_scope->patches[0].skip_instr;
+
+			skip_patch_count += patch0_patch_skip;
+			if (cur_scope->type == SCOPE_IF) {
+				scope->inner_scope_patches += skip_patch_count;
+				skip_patch_count = 0;
+			        skip_instr_count = 0;
+			}
+			break;
+		case SCOPE_ELSE:
+			/* Count any patches contained in our innter scope */
+			skip_patch_count += cur_scope->inner_scope_patches;
+
+			skip_instr_count += cur_scope->end_addr
+					  - cur_scope->begin_addr;
+			break;
+		case SCOPE_ROOT:
+			stop("Unexpected scope type encountered", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+
+		cur_scope = TAILQ_PREV(cur_scope, scope_tailq, scope_links);
+	}
+}
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm.h linux/drivers/scsi/aic7xxx/aicasm/aicasm.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aicasm/aicasm.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,78 @@
+/*
+ * Assembler 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm.h#4 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm.h,v 1.11 2000/09/22 22:19:54 gibbs Exp $
+ */
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+typedef struct path_entry {
+	char	*directory;
+	int	quoted_includes_only;
+	SLIST_ENTRY(path_entry) links;
+} *path_entry_t;
+
+typedef enum {  
+	QUOTED_INCLUDE,
+	BRACKETED_INCLUDE,
+	SOURCE_FILE
+} include_type;
+
+SLIST_HEAD(path_list, path_entry);
+
+extern struct path_list search_path;
+extern struct cs_tailq cs_tailq;
+extern struct scope_list scope_stack;
+extern struct symlist patch_functions;
+extern int includes_search_curdir;		/* False if we've seen -I- */
+extern char *appname;
+extern int yylineno;
+extern char *yyfilename;
+
+void stop(const char *errstring, int err_code);
+void include_file(char *file_name, include_type type);
+struct instruction *seq_alloc(void);
+struct critical_section *cs_alloc(void);
+struct scope *scope_alloc(void);
+void process_scope(struct scope *);
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y linux/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aicasm/aicasm_gram.y	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,1455 @@
+%{
+/*
+ * Parser for the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 1997, 1998, 2000 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm_gram.y#6 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_gram.y,v 1.12 2000/10/31 18:44:32 gibbs Exp $
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include <sys/types.h>
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "aicasm_insformat.h"
+
+int yylineno;
+char *yyfilename;
+static symbol_t *cur_symbol;
+static symtype cur_symtype;
+static symbol_t *accumulator;
+static symbol_ref_t allones;
+static symbol_ref_t allzeros;
+static symbol_ref_t none;
+static symbol_ref_t sindex;
+static int instruction_ptr;
+static int sram_or_scb_offset;
+static int download_constant_count;
+static int in_critical_section;
+
+static void process_bitmask(int mask_type, symbol_t *sym, int mask);
+static void initialize_symbol(symbol_t *symbol);
+static void process_register(symbol_t **p_symbol);
+static void format_1_instr(int opcode, symbol_ref_t *dest,
+			   expression_t *immed, symbol_ref_t *src, int ret);
+static void format_2_instr(int opcode, symbol_ref_t *dest,
+			   expression_t *places, symbol_ref_t *src, int ret);
+static void format_3_instr(int opcode, symbol_ref_t *src,
+			   expression_t *immed, symbol_ref_t *address);
+static void test_readable_symbol(symbol_t *symbol);
+static void test_writable_symbol(symbol_t *symbol);
+static void type_check(symbol_t *symbol, expression_t *expression, int and_op);
+static void make_expression(expression_t *immed, int value);
+static void add_conditional(symbol_t *symbol);
+static int  is_download_const(expression_t *immed);
+
+#define YYDEBUG 1
+#define SRAM_SYMNAME "SRAM_BASE"
+#define SCB_SYMNAME "SCB_BASE"
+%}
+
+%union {
+	int		value;
+	char		*str;
+	symbol_t	*sym;
+	symbol_ref_t	sym_ref;
+	expression_t	expression;
+}
+
+%token T_REGISTER
+
+%token <value> T_CONST
+
+%token T_DOWNLOAD
+
+%token T_SCB
+
+%token T_SRAM
+
+%token T_ALIAS
+
+%token T_SIZE
+
+%token <value> T_ADDRESS
+
+%token T_ACCESS_MODE
+
+%token <value> T_MODE
+
+%token T_BEGIN_CS
+
+%token T_END_CS
+
+%token T_BIT
+
+%token T_MASK
+
+%token <value> T_NUMBER
+
+%token <str> T_PATH
+
+%token <sym> T_CEXPR
+
+%token T_EOF T_INCLUDE 
+
+%token <value> T_SHR T_SHL T_ROR T_ROL
+
+%token <value> T_MVI T_MOV T_CLR T_BMOV
+
+%token <value> T_JMP T_JC T_JNC T_JE T_JNE T_JNZ T_JZ T_CALL
+
+%token <value> T_ADD T_ADC
+
+%token <value> T_INC T_DEC
+
+%token <value> T_STC T_CLC
+
+%token <value> T_CMP T_NOT T_XOR
+
+%token <value> T_TEST T_AND
+
+%token <value> T_OR
+
+%token T_RET
+
+%token T_NOP
+
+%token T_ACCUM T_ALLONES T_ALLZEROS T_NONE T_SINDEX
+
+%token T_A
+
+%token <sym> T_SYMBOL
+
+%token T_NL
+
+%token T_IF T_ELSE T_ELSE_IF T_ENDIF
+
+%type <sym_ref> reg_symbol address destination source opt_source
+
+%type <expression> expression immediate immediate_or_a
+
+%type <value> ret f1_opcode f2_opcode jmp_jc_jnc_call jz_jnz je_jne
+
+%type <value> numerical_value
+
+%left '|'
+%left '&'
+%left '+' '-'
+%right '~'
+%nonassoc UMINUS
+%%
+
+program:
+	include
+|	program include
+|	register
+|	program register
+|	constant
+|	program constant
+|	scratch_ram
+|	program scratch_ram
+|	scb
+|	program scb
+|	label
+|	program label
+|	critical_section_start
+|	program critical_section_start
+|	critical_section_end
+|	program critical_section_end
+|	conditional
+|	program conditional
+|	code
+|	program code
+;
+
+include:
+	T_INCLUDE '<' T_PATH '>'
+	{ include_file($3, BRACKETED_INCLUDE); }
+|	T_INCLUDE '"' T_PATH '"'
+	{ include_file($3, QUOTED_INCLUDE); }
+;
+
+register:
+	T_REGISTER { cur_symtype = REGISTER; } reg_definition
+;
+
+reg_definition:
+	T_SYMBOL '{'
+		{
+			if ($1->type != UNINITIALIZED) {
+				stop("Register multiply defined", EX_DATAERR);
+				/* NOTREACHED */
+			}
+			cur_symbol = $1; 
+			cur_symbol->type = cur_symtype;
+			initialize_symbol(cur_symbol);
+		}
+		reg_attribute_list
+	'}'
+		{                    
+			/*
+			 * Default to allowing everything in for registers
+			 * with no bit or mask definitions.
+			 */
+			if (cur_symbol->info.rinfo->valid_bitmask == 0)
+				cur_symbol->info.rinfo->valid_bitmask = 0xFF;
+
+			if (cur_symbol->info.rinfo->size == 0)
+				cur_symbol->info.rinfo->size = 1;
+
+			/*
+			 * This might be useful for registers too.
+			 */
+			if (cur_symbol->type != REGISTER) {
+				if (cur_symbol->info.rinfo->address == 0)
+					cur_symbol->info.rinfo->address =
+					    sram_or_scb_offset;
+				sram_or_scb_offset +=
+				    cur_symbol->info.rinfo->size;
+			}
+			cur_symbol = NULL;
+		}
+;
+
+reg_attribute_list:
+	reg_attribute
+|	reg_attribute_list reg_attribute
+;
+
+reg_attribute:		
+	reg_address
+|	size
+|	access_mode
+|	bit_defn
+|	mask_defn
+|	alias
+|	accumulator
+|	allones
+|	allzeros
+|	none
+|	sindex
+;
+
+reg_address:
+	T_ADDRESS T_NUMBER
+	{
+		cur_symbol->info.rinfo->address = $2;
+	}
+;
+
+size:
+	T_SIZE T_NUMBER
+	{
+		cur_symbol->info.rinfo->size = $2;
+	}
+;
+
+access_mode:
+	T_ACCESS_MODE T_MODE
+	{
+		cur_symbol->info.rinfo->mode = $2;
+	}
+;
+
+bit_defn:
+	T_BIT T_SYMBOL T_NUMBER
+	{
+		process_bitmask(BIT, $2, $3);
+	}
+;
+
+mask_defn:
+	T_MASK T_SYMBOL expression
+	{
+		process_bitmask(MASK, $2, $3.value);
+	}
+;
+
+alias:
+	T_ALIAS	T_SYMBOL
+	{
+		if ($2->type != UNINITIALIZED) {
+			stop("Re-definition of register alias",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$2->type = ALIAS;
+		initialize_symbol($2);
+		$2->info.ainfo->parent = cur_symbol;
+	}
+;
+
+accumulator:
+	T_ACCUM
+	{
+		if (accumulator != NULL) {
+			stop("Only one accumulator definition allowed",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		accumulator = cur_symbol;
+	}
+;
+
+allones:
+	T_ALLONES
+	{
+		if (allones.symbol != NULL) {
+			stop("Only one definition of allones allowed",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		allones.symbol = cur_symbol;
+	}
+;
+
+allzeros:
+	T_ALLZEROS
+	{
+		if (allzeros.symbol != NULL) {
+			stop("Only one definition of allzeros allowed",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		allzeros.symbol = cur_symbol;
+	}
+;
+
+none:
+	T_NONE
+	{
+		if (none.symbol != NULL) {
+			stop("Only one definition of none allowed",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		none.symbol = cur_symbol;
+	}
+;
+
+sindex:
+	T_SINDEX
+	{
+		if (sindex.symbol != NULL) {
+			stop("Only one definition of sindex allowed",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		sindex.symbol = cur_symbol;
+	}
+;
+
+expression:
+	expression '|' expression
+	{
+		 $$.value = $1.value | $3.value;
+		 symlist_merge(&$$.referenced_syms,
+			       &$1.referenced_syms,
+			       &$3.referenced_syms);
+	}
+|	expression '&' expression
+	{
+		$$.value = $1.value & $3.value;
+		symlist_merge(&$$.referenced_syms,
+			       &$1.referenced_syms,
+			       &$3.referenced_syms);
+	}
+|	expression '+' expression
+	{
+		$$.value = $1.value + $3.value;
+		symlist_merge(&$$.referenced_syms,
+			       &$1.referenced_syms,
+			       &$3.referenced_syms);
+	}
+|	expression '-' expression
+	{
+		$$.value = $1.value - $3.value;
+		symlist_merge(&($$.referenced_syms),
+			       &($1.referenced_syms),
+			       &($3.referenced_syms));
+	}
+|	'(' expression ')'
+	{
+		$$ = $2;
+	}
+|	'~' expression
+	{
+		$$ = $2;
+		$$.value = (~$$.value) & 0xFF;
+	}
+|	'-' expression %prec UMINUS
+	{
+		$$ = $2;
+		$$.value = -$$.value;
+	}
+|	T_NUMBER
+	{
+		$$.value = $1;
+		SLIST_INIT(&$$.referenced_syms);
+	}
+|	T_SYMBOL
+	{
+		symbol_t *symbol;
+
+		symbol = $1;
+		switch (symbol->type) {
+		case ALIAS:
+			symbol = $1->info.ainfo->parent;
+		case REGISTER:
+		case SCBLOC:
+		case SRAMLOC:
+			$$.value = symbol->info.rinfo->address;
+			break;
+		case MASK:
+		case BIT:
+			$$.value = symbol->info.minfo->mask;
+			break;
+		case DOWNLOAD_CONST:
+		case CONST:
+			$$.value = symbol->info.cinfo->value;
+			break;
+		case UNINITIALIZED:
+		default:
+		{
+			char buf[255];
+
+			snprintf(buf, sizeof(buf),
+				 "Undefined symbol %s referenced",
+				 symbol->name);
+			stop(buf, EX_DATAERR);
+			/* NOTREACHED */
+			break;
+		}
+		}
+		SLIST_INIT(&$$.referenced_syms);
+		symlist_add(&$$.referenced_syms, symbol, SYMLIST_INSERT_HEAD);
+	}
+;
+
+constant:
+	T_CONST T_SYMBOL numerical_value
+	{
+		if ($2->type != UNINITIALIZED) {
+			stop("Re-definition of symbol as a constant",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$2->type = CONST;
+		initialize_symbol($2);
+		$2->info.cinfo->value = $3;
+		$2->info.cinfo->define = $1;
+	}
+|	T_CONST T_SYMBOL T_DOWNLOAD
+	{
+		if ($1) {
+			stop("Invalid downloaded constant declaration",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		if ($2->type != UNINITIALIZED) {
+			stop("Re-definition of symbol as a downloaded constant",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$2->type = DOWNLOAD_CONST;
+		initialize_symbol($2);
+		$2->info.cinfo->value = download_constant_count++;
+		$2->info.cinfo->define = FALSE;
+	}
+;
+
+numerical_value:
+	T_NUMBER
+	{
+		$$ = $1;
+	}
+|	'-' T_NUMBER
+	{
+		$$ = -$2;
+	}
+;
+
+scratch_ram:
+	T_SRAM '{'
+		{
+			cur_symbol = symtable_get(SRAM_SYMNAME);
+			cur_symtype = SRAMLOC;
+			if (cur_symbol->type != UNINITIALIZED) {
+				stop("Only one SRAM definition allowed",
+				     EX_DATAERR);
+				/* NOTREACHED */
+			}
+			cur_symbol->type = SRAMLOC;
+			initialize_symbol(cur_symbol);
+		}
+		reg_address
+		{
+			sram_or_scb_offset = cur_symbol->info.rinfo->address;
+		}
+		scb_or_sram_reg_list
+	'}'
+		{
+			cur_symbol = NULL;
+		}
+;
+
+scb:
+	T_SCB '{'
+		{
+			cur_symbol = symtable_get(SCB_SYMNAME);
+			cur_symtype = SCBLOC;
+			if (cur_symbol->type != UNINITIALIZED) {
+				stop("Only one SRAM definition allowed",
+				     EX_SOFTWARE);
+				/* NOTREACHED */
+			}
+			cur_symbol->type = SCBLOC;
+			initialize_symbol(cur_symbol);
+			/* 64 bytes of SCB space */
+			cur_symbol->info.rinfo->size = 64;
+		}
+		reg_address
+		{
+			sram_or_scb_offset = cur_symbol->info.rinfo->address;
+		}
+		scb_or_sram_reg_list
+	'}'
+		{
+			cur_symbol = NULL;
+		}
+;
+
+scb_or_sram_reg_list:
+	reg_definition
+|	scb_or_sram_reg_list reg_definition
+;
+
+reg_symbol:
+	T_SYMBOL
+	{
+		process_register(&$1);
+		$$.symbol = $1;
+		$$.offset = 0;
+	}
+|	T_SYMBOL '[' T_SYMBOL ']'
+	{
+		process_register(&$1);
+		if ($3->type != CONST) {
+			stop("register offset must be a constant", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		if (($3->info.cinfo->value + 1) > $1->info.rinfo->size) {
+			stop("Accessing offset beyond range of register",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$$.symbol = $1;
+		$$.offset = $3->info.cinfo->value;
+	}
+|	T_SYMBOL '[' T_NUMBER ']'
+	{
+		process_register(&$1);
+		if (($3 + 1) > $1->info.rinfo->size) {
+			stop("Accessing offset beyond range of register",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$$.symbol = $1;
+		$$.offset = $3;
+	}
+|	T_A
+	{
+		if (accumulator == NULL) {
+			stop("No accumulator has been defined", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$$.symbol = accumulator;
+		$$.offset = 0;
+	}
+;
+
+destination:
+	reg_symbol
+	{
+		test_writable_symbol($1.symbol);
+		$$ = $1;
+	}
+;
+
+immediate:
+	expression
+	{ $$ = $1; }
+;
+
+immediate_or_a:
+	expression
+	{
+		$$ = $1;
+	}
+|	T_A
+	{
+		SLIST_INIT(&$$.referenced_syms);
+		$$.value = 0;
+	}
+;
+
+source:
+	reg_symbol
+	{
+		test_readable_symbol($1.symbol);
+		$$ = $1;
+	}
+;
+
+opt_source:
+	{
+		$$.symbol = NULL;
+		$$.offset = 0;
+	}
+|	',' source
+	{ $$ = $2; }
+;
+
+ret:
+	{ $$ = 0; }
+|	T_RET
+	{ $$ = 1; }
+;
+
+critical_section_start:
+	T_BEGIN_CS
+	{
+		critical_section_t *cs;
+
+		if (in_critical_section != FALSE) {
+			stop("Critical Section within Critical Section",
+			     EX_DATAERR);
+			/* NOTREACHED */
+		}
+		cs = cs_alloc();
+		cs->begin_addr = instruction_ptr;
+		in_critical_section = TRUE;
+	}
+
+critical_section_end:
+	T_END_CS
+	{
+		critical_section_t *cs;
+
+		if (in_critical_section == FALSE) {
+			stop("Unballanced 'end_cs'", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		cs = TAILQ_LAST(&cs_tailq, cs_tailq);
+		cs->end_addr = instruction_ptr;
+		in_critical_section = FALSE;
+	}
+
+label:
+	T_SYMBOL ':'
+	{
+		if ($1->type != UNINITIALIZED) {
+			stop("Program label multiply defined", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		$1->type = LABEL;
+		initialize_symbol($1);
+		$1->info.linfo->address = instruction_ptr;
+	}
+;
+
+address:
+	T_SYMBOL
+	{
+		$$.symbol = $1;
+		$$.offset = 0;
+	}
+|	T_SYMBOL '+' T_NUMBER
+	{
+		$$.symbol = $1;
+		$$.offset = $3;
+	}
+|	T_SYMBOL '-' T_NUMBER
+	{
+		$$.symbol = $1;
+		$$.offset = -$3;
+	}
+|	'.'
+	{
+		$$.symbol = NULL;
+		$$.offset = 0;
+	}
+|	'.' '+' T_NUMBER
+	{
+		$$.symbol = NULL;
+		$$.offset = $3;
+	}
+|	'.' '-' T_NUMBER
+	{
+		$$.symbol = NULL;
+		$$.offset = -$3;
+	}
+;
+
+conditional:
+	T_IF T_CEXPR '{'
+	{
+		scope_t *new_scope;
+
+		add_conditional($2);
+		new_scope = scope_alloc();
+		new_scope->type = SCOPE_IF;
+		new_scope->begin_addr = instruction_ptr;
+		new_scope->func_num = $2->info.condinfo->func_num;
+	}
+|	T_ELSE T_IF T_CEXPR '{'
+	{
+		scope_t *new_scope;
+		scope_t *scope_context;
+		scope_t *last_scope;
+
+		/*
+		 * Ensure that the previous scope is either an
+		 * if or and else if.
+		 */
+		scope_context = SLIST_FIRST(&scope_stack);
+		last_scope = TAILQ_LAST(&scope_context->inner_scope,
+					scope_tailq);
+		if (last_scope == NULL
+		 || last_scope->type == T_ELSE) {
+
+			stop("'else if' without leading 'if'", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		add_conditional($3);
+		new_scope = scope_alloc();
+		new_scope->type = SCOPE_ELSE_IF;
+		new_scope->begin_addr = instruction_ptr;
+		new_scope->func_num = $3->info.condinfo->func_num;
+	}
+|	T_ELSE '{'
+	{
+		scope_t *new_scope;
+		scope_t *scope_context;
+		scope_t *last_scope;
+
+		/*
+		 * Ensure that the previous scope is either an
+		 * if or and else if.
+		 */
+		scope_context = SLIST_FIRST(&scope_stack);
+		last_scope = TAILQ_LAST(&scope_context->inner_scope,
+					scope_tailq);
+		if (last_scope == NULL
+		 || last_scope->type == SCOPE_ELSE) {
+
+			stop("'else' without leading 'if'", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		new_scope = scope_alloc();
+		new_scope->type = SCOPE_ELSE;
+		new_scope->begin_addr = instruction_ptr;
+	}
+;
+
+conditional:
+	'}'
+	{
+		scope_t *scope_context;
+
+		scope_context = SLIST_FIRST(&scope_stack);
+		if (scope_context->type == SCOPE_ROOT) {
+			stop("Unexpected '}' encountered", EX_DATAERR);
+			/* NOTREACHED */
+		}
+
+		scope_context->end_addr = instruction_ptr;
+
+		/* Pop the scope */
+		SLIST_REMOVE_HEAD(&scope_stack, scope_stack_links);
+
+		process_scope(scope_context);
+
+		if (SLIST_FIRST(&scope_stack) == NULL) {
+			stop("Unexpected '}' encountered", EX_DATAERR);
+			/* NOTREACHED */
+		}
+	}
+;
+
+f1_opcode:
+	T_AND { $$ = AIC_OP_AND; }
+|	T_XOR { $$ = AIC_OP_XOR; }
+|	T_ADD { $$ = AIC_OP_ADD; }
+|	T_ADC { $$ = AIC_OP_ADC; }
+;
+
+code:
+	f1_opcode destination ',' immediate_or_a opt_source ret ';'
+	{
+		format_1_instr($1, &$2, &$4, &$5, $6);
+	}
+;
+
+code:
+	T_OR reg_symbol ',' immediate_or_a opt_source ret ';'
+	{
+		format_1_instr(AIC_OP_OR, &$2, &$4, &$5, $6);
+	}
+;
+
+code:
+	T_INC destination opt_source ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 1);
+		format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
+	}
+;
+
+code:
+	T_DEC destination opt_source ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, -1);
+		format_1_instr(AIC_OP_ADD, &$2, &immed, &$3, $4);
+	}
+;
+
+code:
+	T_CLC ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, -1);
+		format_1_instr(AIC_OP_ADD, &none, &immed, &allzeros, $2);
+	}
+|	T_CLC T_MVI destination ',' immediate_or_a ret ';'
+	{
+		format_1_instr(AIC_OP_ADD, &$3, &$5, &allzeros, $6);
+	}
+;
+
+code:
+	T_STC ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 1);
+		format_1_instr(AIC_OP_ADD, &none, &immed, &allones, $2);
+	}
+|	T_STC destination ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 1);
+		format_1_instr(AIC_OP_ADD, &$2, &immed, &allones, $3);
+	}
+;
+
+code:
+	T_BMOV destination ',' source ',' immediate ret ';'
+	{
+		format_1_instr(AIC_OP_BMOV, &$2, &$6, &$4, $7);
+	}
+;
+
+code:
+	T_MOV destination ',' source ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 1);
+		format_1_instr(AIC_OP_BMOV, &$2, &immed, &$4, $5);
+	}
+;
+
+code:
+	T_MVI destination ',' immediate_or_a ret ';'
+	{
+		format_1_instr(AIC_OP_OR, &$2, &$4, &allzeros, $5);
+	}
+;
+
+code:
+	T_NOT destination opt_source ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 0xff);
+		format_1_instr(AIC_OP_XOR, &$2, &immed, &$3, $4);
+	}
+;
+
+code:
+	T_CLR destination ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 0xff);
+		format_1_instr(AIC_OP_AND, &$2, &immed, &allzeros, $3);
+	}
+;
+
+code:
+	T_NOP ret ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 0xff);
+		format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, $2);
+	}
+;
+
+code:
+	T_RET ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 0xff);
+		format_1_instr(AIC_OP_AND, &none, &immed, &allzeros, TRUE);
+	}
+;
+
+	/*
+	 * This grammer differs from the one in the aic7xxx
+	 * reference manual since the grammer listed there is
+	 * ambiguous and causes a shift/reduce conflict.
+	 * It also seems more logical as the "immediate"
+	 * argument is listed as the second arg like the
+	 * other formats.
+	 */
+
+f2_opcode:
+	T_SHL { $$ = AIC_OP_SHL; }
+|	T_SHR { $$ = AIC_OP_SHR; }
+|	T_ROL { $$ = AIC_OP_ROL; }
+|	T_ROR { $$ = AIC_OP_ROR; }
+;
+
+code:
+	f2_opcode destination ',' expression opt_source ret ';'
+	{
+		format_2_instr($1, &$2, &$4, &$5, $6);
+	}
+;
+
+jmp_jc_jnc_call:
+	T_JMP	{ $$ = AIC_OP_JMP; }
+|	T_JC	{ $$ = AIC_OP_JC; }
+|	T_JNC	{ $$ = AIC_OP_JNC; }
+|	T_CALL	{ $$ = AIC_OP_CALL; }
+;
+
+jz_jnz:
+	T_JZ	{ $$ = AIC_OP_JZ; }
+|	T_JNZ	{ $$ = AIC_OP_JNZ; }
+;
+
+je_jne:
+	T_JE	{ $$ = AIC_OP_JE; }
+|	T_JNE	{ $$ = AIC_OP_JNE; }
+;
+
+code:
+	jmp_jc_jnc_call address ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 0);
+		format_3_instr($1, &sindex, &immed, &$2);
+	}
+;
+
+code:
+	T_OR reg_symbol ',' immediate jmp_jc_jnc_call address ';'
+	{
+		format_3_instr($5, &$2, &$4, &$6);
+	}
+;
+
+code:
+	T_TEST source ',' immediate_or_a jz_jnz address ';'
+	{
+		format_3_instr($5, &$2, &$4, &$6);
+	}
+;
+
+code:
+	T_CMP source ',' immediate_or_a je_jne address ';'
+	{
+		format_3_instr($5, &$2, &$4, &$6);
+	}
+;
+
+code:
+	T_MOV source jmp_jc_jnc_call address ';'
+	{
+		expression_t immed;
+
+		make_expression(&immed, 0);
+		format_3_instr($3, &$2, &immed, &$4);
+	}
+;
+
+code:
+	T_MVI immediate jmp_jc_jnc_call address ';'
+	{
+		format_3_instr($3, &allzeros, &$2, &$4);
+	}
+;
+
+%%
+
+static void
+process_bitmask(int mask_type, symbol_t *sym, int mask)
+{
+	/*
+	 * Add the current register to its
+	 * symbol list, if it already exists,
+	 * warn if we are setting it to a
+	 * different value, or in the bit to
+	 * the "allowed bits" of this register.
+	 */
+	if (sym->type == UNINITIALIZED) {
+		sym->type = mask_type;
+		initialize_symbol(sym);
+		if (mask_type == BIT) {
+			if (mask == 0) {
+				stop("Bitmask with no bits set", EX_DATAERR);
+				/* NOTREACHED */
+			}
+			if ((mask & ~(0x01 << (ffs(mask) - 1))) != 0) {
+				stop("Bitmask with more than one bit set",
+				     EX_DATAERR);
+				/* NOTREACHED */
+			}
+		}
+		sym->info.minfo->mask = mask;
+	} else if (sym->type != mask_type) {
+		stop("Bit definition mirrors a definition of the same "
+		     " name, but a different type", EX_DATAERR);
+		/* NOTREACHED */
+	} else if (mask != sym->info.minfo->mask) {
+		stop("Bitmask redefined with a conflicting value", EX_DATAERR);
+		/* NOTREACHED */
+	}
+	/* Fail if this symbol is already listed */
+	if (symlist_search(&(sym->info.minfo->symrefs),
+			   cur_symbol->name) != NULL) {
+		stop("Bitmask defined multiple times for register", EX_DATAERR);
+		/* NOTREACHED */
+	}
+	symlist_add(&(sym->info.minfo->symrefs), cur_symbol,
+		    SYMLIST_INSERT_HEAD);
+	cur_symbol->info.rinfo->valid_bitmask |= mask;
+	cur_symbol->info.rinfo->typecheck_masks = TRUE;
+}
+
+static void
+initialize_symbol(symbol_t *symbol)
+{
+	switch (symbol->type) {
+        case UNINITIALIZED:
+		stop("Call to initialize_symbol with type field unset",
+		     EX_SOFTWARE);
+		/* NOTREACHED */
+		break;
+        case REGISTER:
+        case SRAMLOC:
+        case SCBLOC:
+		symbol->info.rinfo =
+		    (struct reg_info *)malloc(sizeof(struct reg_info));
+		if (symbol->info.rinfo == NULL) {
+			stop("Can't create register info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.rinfo, 0,
+		       sizeof(struct reg_info));
+		break;
+        case ALIAS:
+		symbol->info.ainfo =
+		    (struct alias_info *)malloc(sizeof(struct alias_info));
+		if (symbol->info.ainfo == NULL) {
+			stop("Can't create alias info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.ainfo, 0,
+		       sizeof(struct alias_info));
+		break;
+        case MASK:
+        case BIT:
+		symbol->info.minfo =
+		    (struct mask_info *)malloc(sizeof(struct mask_info));
+		if (symbol->info.minfo == NULL) {
+			stop("Can't create bitmask info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.minfo, 0, sizeof(struct mask_info));
+		SLIST_INIT(&(symbol->info.minfo->symrefs));
+		break;
+        case CONST:
+        case DOWNLOAD_CONST:
+		symbol->info.cinfo =
+		    (struct const_info *)malloc(sizeof(struct const_info));
+		if (symbol->info.cinfo == NULL) {
+			stop("Can't create alias info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.cinfo, 0,
+		       sizeof(struct const_info));
+		break;
+	case LABEL:
+		symbol->info.linfo =
+		    (struct label_info *)malloc(sizeof(struct label_info));
+		if (symbol->info.linfo == NULL) {
+			stop("Can't create label info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.linfo, 0,
+		       sizeof(struct label_info));
+		break;
+	case CONDITIONAL:
+		symbol->info.condinfo =
+		    (struct cond_info *)malloc(sizeof(struct cond_info));
+		if (symbol->info.condinfo == NULL) {
+			stop("Can't create conditional info", EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		memset(symbol->info.condinfo, 0,
+		       sizeof(struct cond_info));
+		break;
+	default:
+		stop("Call to initialize_symbol with invalid symbol type",
+		     EX_SOFTWARE);
+		/* NOTREACHED */
+		break;
+	}
+}
+
+static void
+process_register(symbol_t **p_symbol)
+{
+	char buf[255];
+	symbol_t *symbol = *p_symbol;
+
+	if (symbol->type == UNINITIALIZED) {
+		snprintf(buf, sizeof(buf), "Undefined register %s",
+			 symbol->name);
+		stop(buf, EX_DATAERR);
+		/* NOTREACHED */
+	} else if (symbol->type == ALIAS) {
+		*p_symbol = symbol->info.ainfo->parent;
+	} else if ((symbol->type != REGISTER)
+		&& (symbol->type != SCBLOC)
+		&& (symbol->type != SRAMLOC)) {
+		snprintf(buf, sizeof(buf),
+			 "Specified symbol %s is not a register",
+			 symbol->name);
+		stop(buf, EX_DATAERR);
+	}
+}
+
+static void
+format_1_instr(int opcode, symbol_ref_t *dest, expression_t *immed,
+	       symbol_ref_t *src, int ret)
+{
+	struct instruction *instr;
+	struct ins_format1 *f1_instr;
+
+	if (src->symbol == NULL)
+		src = dest;
+
+	/* Test register permissions */
+	test_writable_symbol(dest->symbol);
+	test_readable_symbol(src->symbol);
+
+	/* Ensure that immediate makes sense for this destination */
+	type_check(dest->symbol, immed, opcode);
+
+	/* Allocate sequencer space for the instruction and fill it out */
+	instr = seq_alloc();
+	f1_instr = &instr->format.format1;
+	f1_instr->ret = ret ? 1 : 0;
+	f1_instr->opcode = opcode;
+	f1_instr->destination = dest->symbol->info.rinfo->address
+			      + dest->offset;
+	f1_instr->source = src->symbol->info.rinfo->address
+			 + src->offset;
+	f1_instr->immediate = immed->value;
+
+	if (is_download_const(immed))
+		f1_instr->parity = 1;
+
+	symlist_free(&immed->referenced_syms);
+	instruction_ptr++;
+}
+
+static void
+format_2_instr(int opcode, symbol_ref_t *dest, expression_t *places,
+	       symbol_ref_t *src, int ret)
+{
+	struct instruction *instr;
+	struct ins_format2 *f2_instr;
+	uint8_t shift_control;
+
+	if (src->symbol == NULL)
+		src = dest;
+
+	/* Test register permissions */
+	test_writable_symbol(dest->symbol);
+	test_readable_symbol(src->symbol);
+
+	/* Allocate sequencer space for the instruction and fill it out */
+	instr = seq_alloc();
+	f2_instr = &instr->format.format2;
+	f2_instr->ret = ret ? 1 : 0;
+	f2_instr->opcode = AIC_OP_ROL;
+	f2_instr->destination = dest->symbol->info.rinfo->address
+			      + dest->offset;
+	f2_instr->source = src->symbol->info.rinfo->address
+			 + src->offset;
+	if (places->value > 8 || places->value <= 0) {
+		stop("illegal shift value", EX_DATAERR);
+		/* NOTREACHED */
+	}
+	switch (opcode) {
+	case AIC_OP_SHL:
+		if (places->value == 8)
+			shift_control = 0xf0;
+		else
+			shift_control = (places->value << 4) | places->value;
+		break;
+	case AIC_OP_SHR:
+		if (places->value == 8) {
+			shift_control = 0xf8;
+		} else {
+			shift_control = (places->value << 4)
+				      | (8 - places->value)
+				      | 0x08;
+		}
+		break;
+	case AIC_OP_ROL:
+		shift_control = places->value & 0x7;
+		break;
+	case AIC_OP_ROR:
+		shift_control = (8 - places->value) | 0x08;
+		break;
+	default:
+		shift_control = 0; /* Quiet Compiler */
+		stop("Invalid shift operation specified", EX_SOFTWARE);
+		/* NOTREACHED */
+		break;
+	};
+	f2_instr->shift_control = shift_control;
+	symlist_free(&places->referenced_syms);
+	instruction_ptr++;
+}
+
+static void
+format_3_instr(int opcode, symbol_ref_t *src,
+	       expression_t *immed, symbol_ref_t *address)
+{
+	struct instruction *instr;
+	struct ins_format3 *f3_instr;
+	int addr;
+
+	/* Test register permissions */
+	test_readable_symbol(src->symbol);
+
+	/* Ensure that immediate makes sense for this source */
+	type_check(src->symbol, immed, opcode);
+
+	/* Allocate sequencer space for the instruction and fill it out */
+	instr = seq_alloc();
+	f3_instr = &instr->format.format3;
+	if (address->symbol == NULL) {
+		/* 'dot' referrence.  Use the current instruction pointer */
+		addr = instruction_ptr + address->offset;
+	} else if (address->symbol->type == UNINITIALIZED) {
+		/* forward reference */
+		addr = address->offset;
+		instr->patch_label = address->symbol;
+	} else
+		addr = address->symbol->info.linfo->address + address->offset;
+	f3_instr->opcode = opcode;
+	f3_instr->address = addr;
+	f3_instr->source = src->symbol->info.rinfo->address
+			 + src->offset;
+	f3_instr->immediate = immed->value;
+
+	if (is_download_const(immed))
+		f3_instr->parity = 1;
+
+	symlist_free(&immed->referenced_syms);
+	instruction_ptr++;
+}
+
+static void
+test_readable_symbol(symbol_t *symbol)
+{
+	if (symbol->info.rinfo->mode == WO) {
+		stop("Write Only register specified as source",
+		     EX_DATAERR);
+		/* NOTREACHED */
+	}
+}
+
+static void
+test_writable_symbol(symbol_t *symbol)
+{
+	if (symbol->info.rinfo->mode == RO) {
+		stop("Read Only register specified as destination",
+		     EX_DATAERR);
+		/* NOTREACHED */
+	}
+}
+
+static void
+type_check(symbol_t *symbol, expression_t *expression, int opcode)
+{
+	symbol_node_t *node;
+	int and_op;
+	char buf[255];
+
+	and_op = FALSE;
+	if (opcode == AIC_OP_AND || opcode == AIC_OP_JNZ || AIC_OP_JZ)
+		and_op = TRUE;
+
+	/*
+	 * Make sure that we aren't attempting to write something
+	 * that hasn't been defined.  If this is an and operation,
+	 * this is a mask, so "undefined" bits are okay.
+	 */
+	if (and_op == FALSE
+	 && (expression->value & ~symbol->info.rinfo->valid_bitmask) != 0) {
+		snprintf(buf, sizeof(buf),
+			 "Invalid bit(s) 0x%x in immediate written to %s",
+			 expression->value & ~symbol->info.rinfo->valid_bitmask,
+			 symbol->name);
+		stop(buf, EX_DATAERR);
+		/* NOTREACHED */
+	}
+
+	/*
+	 * Now make sure that all of the symbols referenced by the
+	 * expression are defined for this register.
+	 */
+	if(symbol->info.rinfo->typecheck_masks != FALSE) {
+		for(node = expression->referenced_syms.slh_first;
+		    node != NULL;
+		    node = node->links.sle_next) {
+			if ((node->symbol->type == MASK
+			  || node->symbol->type == BIT)
+			 && symlist_search(&node->symbol->info.minfo->symrefs,
+					   symbol->name) == NULL) {
+				snprintf(buf, sizeof(buf),
+					 "Invalid bit or mask %s "
+					 "for register %s",
+					 node->symbol->name, symbol->name);
+				stop(buf, EX_DATAERR);
+				/* NOTREACHED */
+			}
+		}
+	}
+}
+
+static void
+make_expression(expression_t *immed, int value)
+{
+	SLIST_INIT(&immed->referenced_syms);
+	immed->value = value & 0xff;
+}
+
+static void
+add_conditional(symbol_t *symbol)
+{
+	static int numfuncs;
+
+	if (numfuncs == 0) {
+		/* add a special conditional, "0" */
+		symbol_t *false_func;
+
+		false_func = symtable_get("0");
+		if (false_func->type != UNINITIALIZED) {
+			stop("Conditional expression '0' "
+			     "conflicts with a symbol", EX_DATAERR);
+			/* NOTREACHED */
+		}
+		false_func->type = CONDITIONAL;
+		initialize_symbol(false_func);
+		false_func->info.condinfo->func_num = numfuncs++;
+		symlist_add(&patch_functions, false_func, SYMLIST_INSERT_HEAD);
+	}
+
+	/* This condition has occurred before */
+	if (symbol->type == CONDITIONAL)
+		return;
+
+	if (symbol->type != UNINITIALIZED) {
+		stop("Conditional expression conflicts with a symbol",
+		     EX_DATAERR);
+		/* NOTREACHED */
+	}
+
+	symbol->type = CONDITIONAL;
+	initialize_symbol(symbol);
+	symbol->info.condinfo->func_num = numfuncs++;
+	symlist_add(&patch_functions, symbol, SYMLIST_INSERT_HEAD);
+}
+
+void
+yyerror(const char *string)
+{
+	stop(string, EX_DATAERR);
+}
+
+static int
+is_download_const(expression_t *immed)
+{
+	if ((immed->referenced_syms.slh_first != NULL)
+	 && (immed->referenced_syms.slh_first->symbol->type == DOWNLOAD_CONST))
+		return (TRUE);
+
+	return (FALSE);
+}
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h linux/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aicasm/aicasm_insformat.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,129 @@
+/*
+ * Instruction formats for the sequencer program downloaded to
+ * Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997, 1998, 2000 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm_insformat.h#4 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_insformat.h,v 1.3 2000/09/22 22:19:54 gibbs Exp $
+ */
+
+#if linux
+#include <endian.h>
+#else
+#include <machine/endian.h>
+#endif
+
+struct ins_format1 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+	uint32_t	immediate	: 8,
+			source		: 9,
+			destination	: 9,
+			ret		: 1,
+			opcode		: 4,
+			parity		: 1;
+#else
+	uint32_t	parity		: 1,
+			opcode		: 4,
+			ret		: 1,
+			destination	: 9,
+			source		: 9,
+			immediate	: 8;
+#endif
+};
+
+struct ins_format2 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+	uint32_t	shift_control	: 8,
+			source		: 9,
+			destination	: 9,
+			ret		: 1,
+			opcode		: 4,
+			parity		: 1;
+#else
+	uint32_t	parity		: 1,
+			opcode		: 4,
+			ret		: 1,
+			destination	: 9,
+			source		: 9,
+			shift_control	: 8;
+#endif
+};
+
+struct ins_format3 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+	uint32_t	immediate	: 8,
+			source		: 9,
+			address		: 10,
+			opcode		: 4,
+			parity		: 1;
+#else
+	uint32_t	parity		: 1,
+			opcode		: 4,
+			address		: 10,
+			source		: 9,
+			immediate	: 8;
+#endif
+};
+
+union ins_formats {
+		struct ins_format1 format1;
+		struct ins_format2 format2;
+		struct ins_format3 format3;
+		uint8_t		   bytes[4];
+		uint32_t	   integer;
+};
+struct instruction {
+	union	ins_formats format;
+	u_int	srcline;
+	struct symbol *patch_label;
+	STAILQ_ENTRY(instruction) 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_BMOV	0x6
+
+#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 -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l linux/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aicasm/aicasm_scan.l	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,302 @@
+%{
+/*
+ * Lexical Analyzer for the Aic7xxx SCSI Host adapter sequencer assembler.
+ *
+ * Copyright (c) 1997, 1998 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm_scan.l#4 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_scan.l,v 1.13 2000/09/22 22:19:54 gibbs Exp $
+ */
+
+#include <sys/types.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+#include "aicasm.h"
+#include "aicasm_symbol.h"
+#include "y.tab.h"
+
+#define MAX_STR_CONST 256
+char string_buf[MAX_STR_CONST];
+char *string_buf_ptr;
+int  parren_count;
+%}
+
+PATH		[-/A-Za-z0-9_.]*[./][-/A-Za-z0-9_.]*
+WORD		[A-Za-z_][-A-Za-z_0-9]*
+SPACE		[ \t]+
+
+%x COMMENT
+%x CEXPR
+%x INCLUDE
+
+%%
+\n			{ ++yylineno; }
+"/*"			{ BEGIN COMMENT;  /* Enter comment eating state */ }
+<COMMENT>"/*"		{ fprintf(stderr, "Warning! Comment within comment."); }
+<COMMENT>\n		{ ++yylineno; }
+<COMMENT>[^*/\n]*	;
+<COMMENT>"*"+[^*/\n]*	;
+<COMMENT>"/"+[^*/\n]*	;
+<COMMENT>"*"+"/"	{ BEGIN INITIAL; }
+if[ \t]*\(		{
+				string_buf_ptr = string_buf;
+				parren_count = 1;
+				BEGIN CEXPR;
+				return T_IF;
+			}
+<CEXPR>\(		{	*string_buf_ptr++ = '('; parren_count++; }
+<CEXPR>\)		{
+				parren_count--;
+				if (parren_count == 0) {
+					/* All done */
+					BEGIN INITIAL;
+					*string_buf_ptr = '\0';
+					yylval.sym = symtable_get(string_buf);
+					return T_CEXPR;
+				} else {
+					*string_buf_ptr++ = ')';
+				}
+			}
+<CEXPR>\n		{ ++yylineno; }
+<CEXPR>[^()\n]+	{
+				char *yptr = yytext;
+
+				while (*yptr != '\0') {
+					/* Remove duplicate spaces */
+					if (*yptr == '\t')
+						*yptr = ' ';
+					if (*yptr == ' '
+					 && string_buf_ptr != string_buf
+					 && string_buf_ptr[-1] == ' ')
+						yptr++;
+					else 
+						*string_buf_ptr++ = *yptr++;
+				}
+			}
+
+{SPACE}			;
+
+	/* Register/SCB/SRAM definition keywords */
+register		{ return T_REGISTER; }
+const			{ yylval.value = FALSE; return T_CONST; }
+download		{ return T_DOWNLOAD; }
+address			{ return T_ADDRESS; }
+access_mode		{ return T_ACCESS_MODE; }
+RW|RO|WO		{
+				 if (strcmp(yytext, "RW") == 0)
+					yylval.value = RW;
+				 else if (strcmp(yytext, "RO") == 0)
+					yylval.value = RO;
+				 else
+					yylval.value = WO;
+				 return T_MODE;
+			}
+BEGIN_CRITICAL		{ return T_BEGIN_CS; }
+END_CRITICAL		{ return T_END_CS; }
+bit			{ return T_BIT; }
+mask			{ return T_MASK; }
+alias			{ return T_ALIAS; }
+size			{ return T_SIZE; }
+scb			{ return T_SCB; }
+scratch_ram		{ return T_SRAM; }
+accumulator		{ return T_ACCUM; }
+allones			{ return T_ALLONES; }
+allzeros		{ return T_ALLZEROS; }
+none			{ return T_NONE; }
+sindex			{ return T_SINDEX; }
+A			{ return T_A; }
+
+	/* Opcodes */
+shl			{ return T_SHL; }
+shr			{ return T_SHR; }
+ror			{ return T_ROR; }
+rol			{ return T_ROL; }
+mvi			{ return T_MVI; }
+mov			{ return T_MOV; }
+clr			{ return T_CLR; }
+jmp			{ return T_JMP; }
+jc			{ return T_JC;	}
+jnc			{ return T_JNC;	}
+je			{ return T_JE;	}
+jne			{ return T_JNE;	}
+jz			{ return T_JZ;	}
+jnz			{ return T_JNZ;	}
+call			{ return T_CALL; }
+add			{ return T_ADD; }
+adc			{ return T_ADC; }
+bmov			{ return T_BMOV; }
+inc			{ return T_INC; }
+dec			{ return T_DEC; }
+stc			{ return T_STC;	}
+clc			{ return T_CLC; }
+cmp			{ return T_CMP;	}
+not			{ return T_NOT;	}
+xor			{ return T_XOR;	}
+test			{ return T_TEST;}
+and			{ return T_AND;	}
+or			{ return T_OR;	}
+ret			{ return T_RET; }
+nop			{ return T_NOP; }
+else			{ return T_ELSE; }
+
+	/* Allowed Symbols */
+[-+,:()~|&."{};<>[\]!]	{ return yytext[0]; }
+
+	/* Number processing */
+0[0-7]*			{
+				yylval.value = strtol(yytext, NULL, 8);
+				return T_NUMBER;
+			}
+
+0[xX][0-9a-fA-F]+	{
+				yylval.value = strtoul(yytext + 2, NULL, 16);
+				return T_NUMBER;
+			}
+
+[1-9][0-9]*		{
+				yylval.value = strtol(yytext, NULL, 10);
+				return T_NUMBER;
+			}
+
+	/* Include Files */
+#include		{ return T_INCLUDE; BEGIN INCLUDE;}
+<INCLUDE>[<>\"]		{ return yytext[0]; }
+<INCLUDE>{PATH}		{ yylval.str = strdup(yytext); return T_PATH; }
+<INCLUDE>;		{ BEGIN INITIAL; return yytext[0]; }
+<INCLUDE>.		{ stop("Invalid include line", EX_DATAERR); }
+
+	/* For parsing C include files with #define foo */
+#define			{ yylval.value = TRUE; return T_CONST; }
+	/* Throw away macros */
+#define[^\n]*[()]+[^\n]* ;
+{PATH}			{ yylval.str = strdup(yytext); return T_PATH; }
+
+{WORD}			{ yylval.sym = symtable_get(yytext);  return T_SYMBOL; }
+
+.			{ 
+				char buf[255];
+
+				snprintf(buf, sizeof(buf), "Invalid character "
+					 "'%c'", yytext[0]);
+				stop(buf, EX_DATAERR);
+			}
+%%
+
+typedef struct include {
+        YY_BUFFER_STATE  buffer;
+        int              lineno;
+        char            *filename;
+	SLIST_ENTRY(include) links;
+}include_t;
+
+SLIST_HEAD(, include) include_stack;
+
+void
+include_file(char *file_name, include_type type)
+{
+	FILE *newfile;
+	include_t *include;
+
+	newfile = NULL;
+	/* Try the current directory first */
+	if (includes_search_curdir != 0 || type == SOURCE_FILE)
+		newfile = fopen(file_name, "r");
+
+	if (newfile == NULL && type != SOURCE_FILE) {
+                path_entry_t include_dir;
+                for (include_dir = search_path.slh_first;
+                     include_dir != NULL;                
+                     include_dir = include_dir->links.sle_next) {
+			char fullname[PATH_MAX];
+
+			if ((include_dir->quoted_includes_only == TRUE)
+			 && (type != QUOTED_INCLUDE))
+				continue;
+
+			snprintf(fullname, sizeof(fullname),
+				 "%s/%s", include_dir->directory, file_name);
+
+			if ((newfile = fopen(fullname, "r")) != NULL)
+				break;
+                }
+        }
+
+	if (newfile == NULL) {
+		perror(file_name);
+		stop("Unable to open input file", EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+
+	if (type != SOURCE_FILE) {
+		include = (include_t *)malloc(sizeof(include_t));
+		if (include == NULL) {
+			stop("Unable to allocate include stack entry",
+			     EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+		include->buffer = YY_CURRENT_BUFFER;
+		include->lineno = yylineno;
+		include->filename = yyfilename;
+		SLIST_INSERT_HEAD(&include_stack, include, links);
+	}
+	yy_switch_to_buffer(yy_create_buffer(newfile, YY_BUF_SIZE));
+	yylineno = 1;
+	yyfilename = strdup(file_name);
+}
+
+int
+yywrap()
+{
+	include_t *include;
+
+	yy_delete_buffer(YY_CURRENT_BUFFER);
+	(void)fclose(yyin);
+	if (yyfilename != NULL)
+		free(yyfilename);
+	yyfilename = NULL;
+	include = include_stack.slh_first;
+	if (include != NULL) {
+		yy_switch_to_buffer(include->buffer);
+		yylineno = include->lineno;
+		yyfilename = include->filename;
+		SLIST_REMOVE_HEAD(&include_stack, links);
+		free(include);
+		return (0);
+	}
+	return (1);
+}
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c linux/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,468 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler symbol table implementation
+ *
+ * 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm_symbol.c#4 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_symbol.c,v 1.11 2000/09/22 22:19:54 gibbs Exp $
+ */
+
+#include <sys/types.h>
+
+#ifdef __linux__
+#include <db1/db.h>
+#else
+#include <db.h>
+#endif
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "aicasm_symbol.h"
+#include "aicasm.h"
+
+static DB *symtable;
+
+symbol_t *
+symbol_create(char *name)
+{
+	symbol_t *new_symbol;
+
+	new_symbol = (symbol_t *)malloc(sizeof(symbol_t));
+	if (new_symbol == NULL) {
+		perror("Unable to create new symbol");
+		exit(EX_SOFTWARE);
+	}
+	memset(new_symbol, 0, sizeof(*new_symbol));
+	new_symbol->name = strdup(name);
+	new_symbol->type = UNINITIALIZED;
+	return (new_symbol);
+}
+
+void
+symbol_delete(symbol_t *symbol)
+{
+	if (symtable != NULL) {
+		DBT	 key;
+
+		key.data = symbol->name;
+		key.size = strlen(symbol->name);
+		symtable->del(symtable, &key, /*flags*/0);
+	}
+	switch(symbol->type) {
+	case SCBLOC:
+	case SRAMLOC:
+	case REGISTER:
+		if (symbol->info.rinfo != NULL)
+			free(symbol->info.rinfo);
+		break;
+	case ALIAS:
+		if (symbol->info.ainfo != NULL)
+			free(symbol->info.ainfo);
+		break;
+	case MASK:
+	case BIT:
+		if (symbol->info.minfo != NULL) {
+			symlist_free(&symbol->info.minfo->symrefs);
+			free(symbol->info.minfo);
+		}
+		break;
+	case DOWNLOAD_CONST:
+	case CONST:
+		if (symbol->info.cinfo != NULL)
+			free(symbol->info.cinfo);
+		break;
+	case LABEL:
+		if (symbol->info.linfo != NULL)
+			free(symbol->info.linfo);
+		break;
+	case UNINITIALIZED:
+	default:
+		break;
+	}
+	free(symbol->name);
+	free(symbol);
+}
+
+void
+symtable_open()
+{
+	symtable = dbopen(/*filename*/NULL,
+			  O_CREAT | O_NONBLOCK | O_RDWR, /*mode*/0, DB_HASH,
+			  /*openinfo*/NULL);
+
+	if (symtable == NULL) {
+		perror("Symbol table creation failed");
+		exit(EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+}
+
+void
+symtable_close()
+{
+	if (symtable != NULL) {
+		DBT	 key;
+		DBT	 data;
+
+		while (symtable->seq(symtable, &key, &data, R_FIRST) == 0) {
+			symbol_t *stored_ptr;
+
+			memcpy(&stored_ptr, data.data, sizeof(stored_ptr));
+			symbol_delete(stored_ptr);
+		}
+		symtable->close(symtable);
+	}
+}
+
+/*
+ * The semantics of get is to return an uninitialized symbol entry
+ * if a lookup fails.
+ */
+symbol_t *
+symtable_get(char *name)
+{
+	symbol_t *stored_ptr;
+	DBT	  key;
+	DBT	  data;
+	int	  retval;
+
+	key.data = (void *)name;
+	key.size = strlen(name);
+
+	if ((retval = symtable->get(symtable, &key, &data, /*flags*/0)) != 0) {
+		if (retval == -1) {
+			perror("Symbol table get operation failed");
+			exit(EX_SOFTWARE);
+			/* NOTREACHED */
+		} else if (retval == 1) {
+			/* Symbol wasn't found, so create a new one */
+			symbol_t *new_symbol;
+
+			new_symbol = symbol_create(name);
+			data.data = &new_symbol;
+			data.size = sizeof(new_symbol);
+			if (symtable->put(symtable, &key, &data,
+					  /*flags*/0) !=0) {
+				perror("Symtable put failed");
+				exit(EX_SOFTWARE);
+			}
+			return (new_symbol);
+		} else {
+			perror("Unexpected return value from db get routine");
+			exit(EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+	}
+	memcpy(&stored_ptr, data.data, sizeof(stored_ptr));
+	return (stored_ptr);
+}
+
+symbol_node_t *
+symlist_search(symlist_t *symlist, char *symname)
+{
+	symbol_node_t *curnode;
+
+	curnode = symlist->slh_first;
+	while(curnode != NULL) {
+		if (strcmp(symname, curnode->symbol->name) == 0)
+			break;
+		curnode = curnode->links.sle_next;
+	}
+	return (curnode);
+}
+
+void
+symlist_add(symlist_t *symlist, symbol_t *symbol, int how)
+{
+	symbol_node_t *newnode;
+
+	newnode = (symbol_node_t *)malloc(sizeof(symbol_node_t));
+	if (newnode == NULL) {
+		stop("symlist_add: Unable to malloc symbol_node", EX_SOFTWARE);
+		/* NOTREACHED */
+	}
+	newnode->symbol = symbol;
+	if (how == SYMLIST_SORT) {
+		symbol_node_t *curnode;
+		int mask;
+
+		mask = FALSE;
+		switch(symbol->type) {
+		case REGISTER:
+		case SCBLOC:
+		case SRAMLOC:
+			break;
+		case BIT:
+		case MASK:
+			mask = TRUE;
+			break;
+		default:
+			stop("symlist_add: Invalid symbol type for sorting",
+			     EX_SOFTWARE);
+			/* NOTREACHED */
+		}
+
+		curnode = symlist->slh_first;
+		if (curnode == NULL
+		 || (mask && (curnode->symbol->info.minfo->mask >
+		              newnode->symbol->info.minfo->mask))
+		 || (!mask && (curnode->symbol->info.rinfo->address >
+		               newnode->symbol->info.rinfo->address))) {
+			SLIST_INSERT_HEAD(symlist, newnode, links);
+			return;
+		}
+
+		while (1) {
+			if (curnode->links.sle_next == NULL) {
+				SLIST_INSERT_AFTER(curnode, newnode,
+						   links);
+				break;
+			} else {
+				symbol_t *cursymbol;
+
+				cursymbol = curnode->links.sle_next->symbol;
+				if ((mask && (cursymbol->info.minfo->mask >
+				              symbol->info.minfo->mask))
+				 || (!mask &&(cursymbol->info.rinfo->address >
+				              symbol->info.rinfo->address))){
+					SLIST_INSERT_AFTER(curnode, newnode,
+							   links);
+					break;
+				}
+			}
+			curnode = curnode->links.sle_next;
+		}
+	} else {
+		SLIST_INSERT_HEAD(symlist, newnode, links);
+	}
+}
+
+void
+symlist_free(symlist_t *symlist)
+{
+	symbol_node_t *node1, *node2;
+
+	node1 = symlist->slh_first;
+	while (node1 != NULL) {
+		node2 = node1->links.sle_next;
+		free(node1);
+		node1 = node2;
+	}
+	SLIST_INIT(symlist);
+}
+
+void
+symlist_merge(symlist_t *symlist_dest, symlist_t *symlist_src1,
+	      symlist_t *symlist_src2)
+{
+	symbol_node_t *node;
+
+	*symlist_dest = *symlist_src1;
+	while((node = symlist_src2->slh_first) != NULL) {
+		SLIST_REMOVE_HEAD(symlist_src2, links);
+		SLIST_INSERT_HEAD(symlist_dest, node, links);
+	}
+
+	/* These are now empty */
+	SLIST_INIT(symlist_src1);
+	SLIST_INIT(symlist_src2);
+}
+
+void
+symtable_dump(FILE *ofile)
+{
+	/*
+	 * Sort the registers by address with a simple insertion sort.
+	 * Put bitmasks next to the first register that defines them.
+	 * Put constants at the end.
+	 */
+	symlist_t registers;
+	symlist_t masks;
+	symlist_t constants;
+	symlist_t download_constants;
+	symlist_t aliases;
+
+	SLIST_INIT(&registers);
+	SLIST_INIT(&masks);
+	SLIST_INIT(&constants);
+	SLIST_INIT(&download_constants);
+	SLIST_INIT(&aliases);
+
+	if (symtable != NULL) {
+		DBT	 key;
+		DBT	 data;
+		int	 flag = R_FIRST;
+
+		while (symtable->seq(symtable, &key, &data, flag) == 0) {
+			symbol_t *cursym;
+
+			memcpy(&cursym, data.data, sizeof(cursym));
+			switch(cursym->type) {
+			case REGISTER:
+			case SCBLOC:
+			case SRAMLOC:
+				symlist_add(&registers, cursym, SYMLIST_SORT);
+				break;
+			case MASK:
+			case BIT:
+				symlist_add(&masks, cursym, SYMLIST_SORT);
+				break;
+			case CONST:
+				if (cursym->info.cinfo->define == FALSE) {
+					symlist_add(&constants, cursym,
+						    SYMLIST_INSERT_HEAD);
+				}
+				break;
+			case DOWNLOAD_CONST:
+				symlist_add(&download_constants, cursym,
+					    SYMLIST_INSERT_HEAD);
+				break;
+			case ALIAS:
+				symlist_add(&aliases, cursym,
+					    SYMLIST_INSERT_HEAD);
+				break;
+			default:
+				break;
+			}
+			flag = R_NEXT;
+		}
+
+		/* Put in the masks and bits */
+		while (masks.slh_first != NULL) {
+			symbol_node_t *curnode;
+			symbol_node_t *regnode;
+			char *regname;
+
+			curnode = masks.slh_first;
+			SLIST_REMOVE_HEAD(&masks, links);
+
+			regnode =
+			    curnode->symbol->info.minfo->symrefs.slh_first;
+			regname = regnode->symbol->name;
+			regnode = symlist_search(&registers, regname);
+			SLIST_INSERT_AFTER(regnode, curnode, links);
+		}
+
+		/* Add the aliases */
+		while (aliases.slh_first != NULL) {
+			symbol_node_t *curnode;
+			symbol_node_t *regnode;
+			char *regname;
+
+			curnode = aliases.slh_first;
+			SLIST_REMOVE_HEAD(&aliases, links);
+
+			regname = curnode->symbol->info.ainfo->parent->name;
+			regnode = symlist_search(&registers, regname);
+			SLIST_INSERT_AFTER(regnode, curnode, links);
+		}
+
+		/* Output what we have */
+		fprintf(ofile,
+"/*
+  * DO NOT EDIT - This file is automatically generated.
+  */\n");
+		while (registers.slh_first != NULL) {
+			symbol_node_t *curnode;
+			u_int8_t value;
+			char *tab_str;
+			char *tab_str2;
+
+			curnode = registers.slh_first;
+			SLIST_REMOVE_HEAD(&registers, links);
+			switch(curnode->symbol->type) {
+			case REGISTER:
+			case SCBLOC:
+			case SRAMLOC:
+				fprintf(ofile, "\n");
+				value = curnode->symbol->info.rinfo->address;
+				tab_str = "\t";
+				tab_str2 = "\t\t";
+				break;
+			case ALIAS:
+			{
+				symbol_t *parent;
+
+				parent = curnode->symbol->info.ainfo->parent;
+				value = parent->info.rinfo->address;
+				tab_str = "\t";
+				tab_str2 = "\t\t";
+				break;
+			}
+			case MASK:
+			case BIT:
+				value = curnode->symbol->info.minfo->mask;
+				tab_str = "\t\t";
+				tab_str2 = "\t";
+				break;
+			default:
+				value = 0; /* Quiet compiler */
+				tab_str = NULL;
+				tab_str2 = NULL;
+				stop("symtable_dump: Invalid symbol type "
+				     "encountered", EX_SOFTWARE);
+				break;
+			}
+			fprintf(ofile, "#define%s%-16s%s0x%02x\n",
+				tab_str, curnode->symbol->name, tab_str2,
+				value);
+			free(curnode);
+		}
+		fprintf(ofile, "\n\n");
+
+		while (constants.slh_first != NULL) {
+			symbol_node_t *curnode;
+
+			curnode = constants.slh_first;
+			SLIST_REMOVE_HEAD(&constants, links);
+			fprintf(ofile, "#define\t%-8s\t0x%02x\n",
+				curnode->symbol->name,
+				curnode->symbol->info.cinfo->value);
+			free(curnode);
+		}
+
+		
+		fprintf(ofile, "\n\n/* Downloaded Constant Definitions */\n");
+
+		while (download_constants.slh_first != NULL) {
+			symbol_node_t *curnode;
+
+			curnode = download_constants.slh_first;
+			SLIST_REMOVE_HEAD(&download_constants, links);
+			fprintf(ofile, "#define\t%-8s\t0x%02x\n",
+				curnode->symbol->name,
+				curnode->symbol->info.cinfo->value);
+			free(curnode);
+		}
+	}
+}
+
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h linux/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/aicasm/aicasm_symbol.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,177 @@
+/*
+ * Aic7xxx SCSI host adapter firmware asssembler symbol table definitions
+ *
+ * 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/aic7xxx/aicasm/aicasm_symbol.h#4 $
+ *
+ * $FreeBSD: src/sys/dev/aic7xxx/aicasm/aicasm_symbol.h,v 1.11 2000/09/22 22:19:55 gibbs Exp $
+ */
+
+#ifdef __linux__
+#include "../queue.h"
+#else
+#include <sys/queue.h>
+#endif
+
+typedef enum {
+	UNINITIALIZED,
+	REGISTER,
+	ALIAS,
+	SCBLOC,
+	SRAMLOC,
+	MASK,
+	BIT,
+	CONST,
+	DOWNLOAD_CONST,
+	LABEL,
+	CONDITIONAL
+}symtype;
+
+typedef enum {
+	RO = 0x01,
+	WO = 0x02,
+	RW = 0x03
+}amode_t;
+
+struct reg_info {
+	u_int8_t address;
+	int	 size;
+	amode_t	 mode;
+	u_int8_t valid_bitmask;
+	int	 typecheck_masks;
+};
+
+typedef SLIST_HEAD(symlist, symbol_node) symlist_t;
+
+struct mask_info {
+	symlist_t symrefs;
+	u_int8_t mask;
+};
+
+struct const_info {
+	u_int8_t value;
+	int	 define;
+};
+
+struct alias_info {
+	struct symbol *parent;
+};
+
+struct label_info {
+	int	address;
+};
+
+struct cond_info {
+	int	func_num;
+};
+
+typedef struct expression_info {
+        symlist_t       referenced_syms;
+        int             value;
+} expression_t;
+
+typedef struct symbol {
+	char	*name;
+	symtype	type;
+	union	{
+		struct reg_info *rinfo;
+		struct mask_info *minfo;
+		struct const_info *cinfo;
+		struct alias_info *ainfo;
+		struct label_info *linfo;
+		struct cond_info *condinfo;
+	}info;
+} symbol_t;
+
+typedef struct symbol_ref {
+	symbol_t *symbol;
+	int	 offset;
+} symbol_ref_t;
+
+typedef struct symbol_node {
+	SLIST_ENTRY(symbol_node) links;
+	symbol_t *symbol;
+} symbol_node_t;
+
+typedef struct critical_section {
+	TAILQ_ENTRY(critical_section) links;
+	int begin_addr;
+	int end_addr;
+} critical_section_t;
+
+typedef enum {
+	SCOPE_ROOT,
+	SCOPE_IF,
+	SCOPE_ELSE_IF,
+	SCOPE_ELSE
+} scope_type;
+
+typedef struct patch_info {
+	int skip_patch;
+	int skip_instr;
+} patch_info_t;
+
+typedef struct scope {
+	SLIST_ENTRY(scope) scope_stack_links;
+	TAILQ_ENTRY(scope) scope_links;
+	TAILQ_HEAD(, scope) inner_scope;
+	scope_type type;
+	int inner_scope_patches;
+	int begin_addr;
+        int end_addr;
+	patch_info_t patches[2];
+	int func_num;
+} scope_t;
+
+TAILQ_HEAD(cs_tailq, critical_section);
+SLIST_HEAD(scope_list, scope);
+TAILQ_HEAD(scope_tailq, scope);
+
+void	symbol_delete __P((symbol_t *symbol));
+
+void	symtable_open __P((void));
+
+void	symtable_close __P((void));
+
+symbol_t *
+	symtable_get __P((char *name));
+
+symbol_node_t *
+	symlist_search __P((symlist_t *symlist, char *symname));
+
+void
+	symlist_add __P((symlist_t *symlist, symbol_t *symbol, int how));
+#define SYMLIST_INSERT_HEAD	0x00
+#define SYMLIST_SORT		0x01
+
+void	symlist_free __P((symlist_t *symlist));
+
+void	symlist_merge __P((symlist_t *symlist_dest, symlist_t *symlist_src1,
+			   symlist_t *symlist_src2));
+void	symtable_dump __P((FILE *ofile));
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/cam.h linux/drivers/scsi/aic7xxx/cam.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/cam.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/cam.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,142 @@
+/*
+ * Data structures and definitions for the CAM system.
+ *
+ * Copyright (c) 1997 Justin T. Gibbs.
+ * Copyright (c) 2000 Adaptec Inc.
+ * 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.
+ * 2. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU Public License ("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: //depot/src/linux/drivers/scsi/aic7xxx/cam.h#9 $
+ */
+
+#ifndef _AIC7XXX_CAM_H
+#define _AIC7XXX_CAM_H 1
+
+/* Provide a mapping from CAM constructs to Linux SCSI constructs */
+
+#define	CAM_BUS_WILDCARD ((u_int)~0)
+#define	CAM_TARGET_WILDCARD ((u_int)~0)
+#define	CAM_LUN_WILDCARD ((u_int)~0)
+
+/* CAM Status field values */
+typedef enum {
+	/* CCB request is in progress */
+	CAM_REQ_INPROG		= 0x3F, /* Some value unused by Linux */
+	/* CCB request completed without error */
+	CAM_REQ_CMP		= DID_OK,
+	/* CCB request aborted by the host */
+	CAM_REQ_ABORTED		= DID_ABORT,
+	/* Unable to abort CCB request */
+	CAM_UA_ABORT		= DID_ERROR,
+	/* CCB request completed with an error */
+	CAM_REQ_CMP_ERR		= DID_ERROR,
+	/* CAM subsytem is busy */
+	CAM_BUSY		= DID_BUS_BUSY,
+	/* CCB request was invalid */
+	CAM_REQ_INVALID		= DID_BAD_TARGET,
+	/* Supplied Path ID is invalid */
+	CAM_PATH_INVALID	= DID_BAD_TARGET,
+	/* Target Selection Timeout */
+	CAM_SEL_TIMEOUT		= DID_NO_CONNECT,
+	/* Command timeout */
+	CAM_CMD_TIMEOUT		= DID_ERROR, /*
+					      * Should never occur in Linux
+					      * as the upper level code
+					      * handles all timeout processing.
+					      */
+	/* SCSI error, look at error code in CCB */
+	CAM_SCSI_STATUS_ERROR	= DID_OK, /* Linux looks at status byte */
+	/* SCSI Bus Reset Sent/Received */
+	CAM_SCSI_BUS_RESET	= DID_RESET,
+	/* Uncorrectable parity error occurred */
+	CAM_UNCOR_PARITY	= DID_PARITY,
+	/* Autosense: request sense cmd fail */
+	CAM_AUTOSENSE_FAIL	= DID_ERROR,
+	/* No HBA Detected Error */
+	CAM_NO_HBA		= DID_ERROR,
+	/* Data Overrun error */
+	CAM_DATA_RUN_ERR	= DID_ERROR,
+	/* Unexpected Bus Free */
+	CAM_UNEXP_BUSFREE	= DID_ERROR,
+	/* CCB length supplied is inadequate */
+	CAM_CCB_LEN_ERR		= DID_ERROR,
+	/* Unable to provide requested capability */
+	CAM_PROVIDE_FAIL	= DID_ERROR,
+	/* A SCSI BDR msg was sent to target */
+	CAM_BDR_SENT		= DID_RESET,
+	/* CCB request terminated by the host */
+	CAM_REQ_TERMIO		= DID_ERROR,
+	/* Unrecoverable Host Bus Adapter Error */
+	CAM_UNREC_HBA_ERROR	= DID_ERROR,
+	/* The request was too large for this host */
+	CAM_REQ_TOO_BIG		= DID_ERROR,
+	/*
+	 * This request should be requeued to preserve
+	 * transaction ordering.  This typically occurs
+	 * when the SIM recognizes an error that should
+	 * freeze the queue and must place additional
+	 * requests for the target at the sim level
+	 * back into the XPT queue.
+	 */
+	CAM_REQUEUE_REQ		= DID_BUS_BUSY,
+	/*
+	 * The transaction has made it through our queue routine.
+	 */
+	CAM_SIM_QUEUED		= 0x200,
+
+	CAM_STATUS_MASK		= 0x3F
+} cam_status;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
+#define	SCSI_DATA_READ	1
+#define	SCSI_DATA_WRITE 2
+#define SCSI_DATA_NONE  3
+#endif
+
+/*
+ * Definitions for the asynchronous callback CCB fields.
+ */
+typedef enum {
+	AC_GETDEV_CHANGED	= 0x800,/* Getdev info might have changed */
+	AC_INQ_CHANGED		= 0x400,/* Inquiry info might have changed */
+	AC_TRANSFER_NEG		= 0x200,/* New transfer settings in effect */
+	AC_LOST_DEVICE		= 0x100,/* A device went away */
+	AC_FOUND_DEVICE		= 0x080,/* A new device was found */
+	AC_PATH_DEREGISTERED	= 0x040,/* A path has de-registered */
+	AC_PATH_REGISTERED	= 0x020,/* A new path has been registered */
+	AC_SENT_BDR		= 0x010,/* A BDR message was sent to target */
+	AC_SCSI_AEN		= 0x008,/* A SCSI AEN has been received */
+	AC_UNSOL_RESEL		= 0x002,/* Unsolicited reselection occurred */
+	AC_BUS_RESET		= 0x001 /* A SCSI bus reset occurred */
+} ac_code;
+
+typedef enum {
+	CAM_DIR_IN		= SCSI_DATA_READ,
+	CAM_DIR_OUT		= SCSI_DATA_WRITE,
+	CAM_DIR_NONE		= SCSI_DATA_NONE
+} ccb_flags;
+
+#endif /* _AIC7XXX_CAM_H */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/queue.h linux/drivers/scsi/aic7xxx/queue.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/queue.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx/queue.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 1991, 1993
+ *	The Regents of the University of California.  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.
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
+ *
+ *	@(#)queue.h	8.5 (Berkeley) 8/20/94
+ * $FreeBSD: src/sys/sys/queue.h,v 1.38 2000/05/26 02:06:56 jake Exp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define	_SYS_QUEUE_H_
+
+/*
+ * This file defines five types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists, tail queues, and circular queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction.  Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A circle queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or after
+ * an existing element, at the head of the list, or at the end of the list.
+ * A circle queue may be traversed in either direction, but has a more
+ * complex end of list detection.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ *			SLIST	LIST	STAILQ	TAILQ	CIRCLEQ
+ * _HEAD		+	+	+	+	+
+ * _HEAD_INITIALIZER	+	+	+	+	+
+ * _ENTRY		+	+	+	+	+
+ * _INIT		+	+	+	+	+
+ * _EMPTY		+	+	+	+	+
+ * _FIRST		+	+	+	+	+
+ * _NEXT		+	+	+	+	+
+ * _PREV		-	-	-	+	+
+ * _LAST		-	-	+	+	+
+ * _FOREACH		+	+	+	+	+
+ * _FOREACH_REVERSE	-	-	-	+	+
+ * _INSERT_HEAD		+	+	+	+	+
+ * _INSERT_BEFORE	-	+	-	+	+
+ * _INSERT_AFTER	+	+	+	+	+
+ * _INSERT_TAIL		-	-	+	+	+
+ * _REMOVE_HEAD		+	-	+	-	-
+ * _REMOVE		+	+	+	+	+
+ *
+ */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define	SLIST_HEAD(name, type)						\
+struct name {								\
+	struct type *slh_first;	/* first element */			\
+}
+
+#define	SLIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+ 
+#define	SLIST_ENTRY(type)						\
+struct {								\
+	struct type *sle_next;	/* next element */			\
+}
+ 
+/*
+ * Singly-linked List functions.
+ */
+#define	SLIST_EMPTY(head)	((head)->slh_first == NULL)
+
+#define	SLIST_FIRST(head)	((head)->slh_first)
+
+#define	SLIST_FOREACH(var, head, field)					\
+	for ((var) = SLIST_FIRST((head));				\
+	    (var);							\
+	    (var) = SLIST_NEXT((var), field))
+
+#define	SLIST_INIT(head) do {						\
+	SLIST_FIRST((head)) = NULL;					\
+} while (0)
+
+#define	SLIST_INSERT_AFTER(slistelm, elm, field) do {			\
+	SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field);	\
+	SLIST_NEXT((slistelm), field) = (elm);				\
+} while (0)
+
+#define	SLIST_INSERT_HEAD(head, elm, field) do {			\
+	SLIST_NEXT((elm), field) = SLIST_FIRST((head));			\
+	SLIST_FIRST((head)) = (elm);					\
+} while (0)
+
+#define	SLIST_NEXT(elm, field)	((elm)->field.sle_next)
+
+#define	SLIST_REMOVE(head, elm, type, field) do {			\
+	if (SLIST_FIRST((head)) == (elm)) {				\
+		SLIST_REMOVE_HEAD((head), field);			\
+	}								\
+	else {								\
+		struct type *curelm = SLIST_FIRST((head));		\
+		while (SLIST_NEXT(curelm, field) != (elm))		\
+			curelm = SLIST_NEXT(curelm, field);		\
+		SLIST_NEXT(curelm, field) =				\
+		    SLIST_NEXT(SLIST_NEXT(curelm, field), field);	\
+	}								\
+} while (0)
+
+#define	SLIST_REMOVE_HEAD(head, field) do {				\
+	SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field);	\
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define	STAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *stqh_first;/* first element */			\
+	struct type **stqh_last;/* addr of last next element */		\
+}
+
+#define	STAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).stqh_first }
+
+#define	STAILQ_ENTRY(type)						\
+struct {								\
+	struct type *stqe_next;	/* next element */			\
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define	STAILQ_EMPTY(head)	((head)->stqh_first == NULL)
+
+#define	STAILQ_FIRST(head)	((head)->stqh_first)
+
+#define	STAILQ_FOREACH(var, head, field)				\
+	for((var) = STAILQ_FIRST((head));				\
+	   (var);							\
+	   (var) = STAILQ_NEXT((var), field))
+
+#define	STAILQ_INIT(head) do {						\
+	STAILQ_FIRST((head)) = NULL;					\
+	(head)->stqh_last = &STAILQ_FIRST((head));			\
+} while (0)
+
+#define	STAILQ_INSERT_AFTER(head, tqelm, elm, field) do {		\
+	if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\
+	STAILQ_NEXT((tqelm), field) = (elm);				\
+} while (0)
+
+#define	STAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL)	\
+		(head)->stqh_last = &STAILQ_NEXT((elm), field);		\
+	STAILQ_FIRST((head)) = (elm);					\
+} while (0)
+
+#define	STAILQ_INSERT_TAIL(head, elm, field) do {			\
+	STAILQ_NEXT((elm), field) = NULL;				\
+	STAILQ_LAST((head)) = (elm);					\
+	(head)->stqh_last = &STAILQ_NEXT((elm), field);			\
+} while (0)
+
+#define	STAILQ_LAST(head)	(*(head)->stqh_last)
+
+#define	STAILQ_NEXT(elm, field)	((elm)->field.stqe_next)
+
+#define	STAILQ_REMOVE(head, elm, type, field) do {			\
+	if (STAILQ_FIRST((head)) == (elm)) {				\
+		STAILQ_REMOVE_HEAD(head, field);			\
+	}								\
+	else {								\
+		struct type *curelm = STAILQ_FIRST((head));		\
+		while (STAILQ_NEXT(curelm, field) != (elm))		\
+			curelm = STAILQ_NEXT(curelm, field);		\
+		if ((STAILQ_NEXT(curelm, field) =			\
+		     STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
+			(head)->stqh_last = &STAILQ_NEXT((curelm), field);\
+	}								\
+} while (0)
+
+#define	STAILQ_REMOVE_HEAD(head, field) do {				\
+	if ((STAILQ_FIRST((head)) =					\
+	     STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL)		\
+		(head)->stqh_last = &STAILQ_FIRST((head));		\
+} while (0)
+
+#define	STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do {			\
+	if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL)	\
+		(head)->stqh_last = &STAILQ_FIRST((head));		\
+} while (0)
+
+/*
+ * List declarations.
+ */
+#define	LIST_HEAD(name, type)						\
+struct name {								\
+	struct type *lh_first;	/* first element */			\
+}
+
+#define	LIST_HEAD_INITIALIZER(head)					\
+	{ NULL }
+
+#define	LIST_ENTRY(type)						\
+struct {								\
+	struct type *le_next;	/* next element */			\
+	struct type **le_prev;	/* address of previous next element */	\
+}
+
+/*
+ * List functions.
+ */
+
+#define	LIST_EMPTY(head)	((head)->lh_first == NULL)
+
+#define	LIST_FIRST(head)	((head)->lh_first)
+
+#define	LIST_FOREACH(var, head, field)					\
+	for ((var) = LIST_FIRST((head));				\
+	    (var);							\
+	    (var) = LIST_NEXT((var), field))
+
+#define	LIST_INIT(head) do {						\
+	LIST_FIRST((head)) = NULL;					\
+} while (0)
+
+#define	LIST_INSERT_AFTER(listelm, elm, field) do {			\
+	if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+		LIST_NEXT((listelm), field)->field.le_prev =		\
+		    &LIST_NEXT((elm), field);				\
+	LIST_NEXT((listelm), field) = (elm);				\
+	(elm)->field.le_prev = &LIST_NEXT((listelm), field);		\
+} while (0)
+
+#define	LIST_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.le_prev = (listelm)->field.le_prev;		\
+	LIST_NEXT((elm), field) = (listelm);				\
+	*(listelm)->field.le_prev = (elm);				\
+	(listelm)->field.le_prev = &LIST_NEXT((elm), field);		\
+} while (0)
+
+#define	LIST_INSERT_HEAD(head, elm, field) do {				\
+	if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL)	\
+		LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+	LIST_FIRST((head)) = (elm);					\
+	(elm)->field.le_prev = &LIST_FIRST((head));			\
+} while (0)
+
+#define	LIST_NEXT(elm, field)	((elm)->field.le_next)
+
+#define	LIST_REMOVE(elm, field) do {					\
+	if (LIST_NEXT((elm), field) != NULL)				\
+		LIST_NEXT((elm), field)->field.le_prev = 		\
+		    (elm)->field.le_prev;				\
+	*(elm)->field.le_prev = LIST_NEXT((elm), field);		\
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define	TAILQ_HEAD(name, type)						\
+struct name {								\
+	struct type *tqh_first;	/* first element */			\
+	struct type **tqh_last;	/* addr of last next element */		\
+}
+
+#define	TAILQ_HEAD_INITIALIZER(head)					\
+	{ NULL, &(head).tqh_first }
+
+#define	TAILQ_ENTRY(type)						\
+struct {								\
+	struct type *tqe_next;	/* next element */			\
+	struct type **tqe_prev;	/* address of previous next element */	\
+}
+
+/*
+ * Tail queue functions.
+ */
+#define	TAILQ_EMPTY(head)	((head)->tqh_first == NULL)
+
+#define	TAILQ_FIRST(head)	((head)->tqh_first)
+
+#define	TAILQ_FOREACH(var, head, field)					\
+	for ((var) = TAILQ_FIRST((head));				\
+	    (var);							\
+	    (var) = TAILQ_NEXT((var), field))
+
+#define	TAILQ_FOREACH_REVERSE(var, head, headname, field)		\
+	for ((var) = TAILQ_LAST((head), headname);			\
+	    (var);							\
+	    (var) = TAILQ_PREV((var), headname, field))
+
+#define	TAILQ_INIT(head) do {						\
+	TAILQ_FIRST((head)) = NULL;					\
+	(head)->tqh_last = &TAILQ_FIRST((head));			\
+} while (0)
+
+#define	TAILQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+		TAILQ_NEXT((elm), field)->field.tqe_prev = 		\
+		    &TAILQ_NEXT((elm), field);				\
+	else								\
+		(head)->tqh_last = &TAILQ_NEXT((elm), field);		\
+	TAILQ_NEXT((listelm), field) = (elm);				\
+	(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field);		\
+} while (0)
+
+#define	TAILQ_INSERT_BEFORE(listelm, elm, field) do {			\
+	(elm)->field.tqe_prev = (listelm)->field.tqe_prev;		\
+	TAILQ_NEXT((elm), field) = (listelm);				\
+	*(listelm)->field.tqe_prev = (elm);				\
+	(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field);		\
+} while (0)
+
+#define	TAILQ_INSERT_HEAD(head, elm, field) do {			\
+	if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL)	\
+		TAILQ_FIRST((head))->field.tqe_prev =			\
+		    &TAILQ_NEXT((elm), field);				\
+	else								\
+		(head)->tqh_last = &TAILQ_NEXT((elm), field);		\
+	TAILQ_FIRST((head)) = (elm);					\
+	(elm)->field.tqe_prev = &TAILQ_FIRST((head));			\
+} while (0)
+
+#define	TAILQ_INSERT_TAIL(head, elm, field) do {			\
+	TAILQ_NEXT((elm), field) = NULL;				\
+	(elm)->field.tqe_prev = (head)->tqh_last;			\
+	*(head)->tqh_last = (elm);					\
+	(head)->tqh_last = &TAILQ_NEXT((elm), field);			\
+} while (0)
+
+#define	TAILQ_LAST(head, headname)					\
+	(*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define	TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define	TAILQ_PREV(elm, headname, field)				\
+	(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define	TAILQ_REMOVE(head, elm, field) do {				\
+	if ((TAILQ_NEXT((elm), field)) != NULL)				\
+		TAILQ_NEXT((elm), field)->field.tqe_prev = 		\
+		    (elm)->field.tqe_prev;				\
+	else								\
+		(head)->tqh_last = (elm)->field.tqe_prev;		\
+	*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field);		\
+} while (0)
+
+/*
+ * Circular queue declarations.
+ */
+#define	CIRCLEQ_HEAD(name, type)					\
+struct name {								\
+	struct type *cqh_first;		/* first element */		\
+	struct type *cqh_last;		/* last element */		\
+}
+
+#define	CIRCLEQ_HEAD_INITIALIZER(head)					\
+	{ (void *)&(head), (void *)&(head) }
+
+#define	CIRCLEQ_ENTRY(type)						\
+struct {								\
+	struct type *cqe_next;		/* next element */		\
+	struct type *cqe_prev;		/* previous element */		\
+}
+
+/*
+ * Circular queue functions.
+ */
+#define	CIRCLEQ_EMPTY(head)	((head)->cqh_first == (void *)(head))
+
+#define	CIRCLEQ_FIRST(head)	((head)->cqh_first)
+
+#define	CIRCLEQ_FOREACH(var, head, field)				\
+	for ((var) = CIRCLEQ_FIRST((head));				\
+	    (var) != (void *)(head);					\
+	    (var) = CIRCLEQ_NEXT((var), field))
+
+#define	CIRCLEQ_FOREACH_REVERSE(var, head, field)			\
+	for ((var) = CIRCLEQ_LAST((head));				\
+	    (var) != (void *)(head);					\
+	    (var) = CIRCLEQ_PREV((var), field))
+
+#define	CIRCLEQ_INIT(head) do {						\
+	CIRCLEQ_FIRST((head)) = (void *)(head);				\
+	CIRCLEQ_LAST((head)) = (void *)(head);				\
+} while (0)
+
+#define	CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do {		\
+	CIRCLEQ_NEXT((elm), field) = CIRCLEQ_NEXT((listelm), field);	\
+	CIRCLEQ_PREV((elm), field) = (listelm);				\
+	if (CIRCLEQ_NEXT((listelm), field) == (void *)(head))		\
+		CIRCLEQ_LAST((head)) = (elm);				\
+	else								\
+		CIRCLEQ_PREV(CIRCLEQ_NEXT((listelm), field), field) = (elm);\
+	CIRCLEQ_NEXT((listelm), field) = (elm);				\
+} while (0)
+
+#define	CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do {		\
+	CIRCLEQ_NEXT((elm), field) = (listelm);				\
+	CIRCLEQ_PREV((elm), field) = CIRCLEQ_PREV((listelm), field);	\
+	if (CIRCLEQ_PREV((listelm), field) == (void *)(head))		\
+		CIRCLEQ_FIRST((head)) = (elm);				\
+	else								\
+		CIRCLEQ_NEXT(CIRCLEQ_PREV((listelm), field), field) = (elm);\
+	CIRCLEQ_PREV((listelm), field) = (elm);				\
+} while (0)
+
+#define	CIRCLEQ_INSERT_HEAD(head, elm, field) do {			\
+	CIRCLEQ_NEXT((elm), field) = CIRCLEQ_FIRST((head));		\
+	CIRCLEQ_PREV((elm), field) = (void *)(head);			\
+	if (CIRCLEQ_LAST((head)) == (void *)(head))			\
+		CIRCLEQ_LAST((head)) = (elm);				\
+	else								\
+		CIRCLEQ_PREV(CIRCLEQ_FIRST((head)), field) = (elm);	\
+	CIRCLEQ_FIRST((head)) = (elm);					\
+} while (0)
+
+#define	CIRCLEQ_INSERT_TAIL(head, elm, field) do {			\
+	CIRCLEQ_NEXT((elm), field) = (void *)(head);			\
+	CIRCLEQ_PREV((elm), field) = CIRCLEQ_LAST((head));		\
+	if (CIRCLEQ_FIRST((head)) == (void *)(head))			\
+		CIRCLEQ_FIRST((head)) = (elm);				\
+	else								\
+		CIRCLEQ_NEXT(CIRCLEQ_LAST((head)), field) = (elm);	\
+	CIRCLEQ_LAST((head)) = (elm);					\
+} while (0)
+
+#define	CIRCLEQ_LAST(head)	((head)->cqh_last)
+
+#define	CIRCLEQ_NEXT(elm,field)	((elm)->field.cqe_next)
+
+#define	CIRCLEQ_PREV(elm,field)	((elm)->field.cqe_prev)
+
+#define	CIRCLEQ_REMOVE(head, elm, field) do {				\
+	if (CIRCLEQ_NEXT((elm), field) == (void *)(head))		\
+		CIRCLEQ_LAST((head)) = CIRCLEQ_PREV((elm), field);	\
+	else								\
+		CIRCLEQ_PREV(CIRCLEQ_NEXT((elm), field), field) =	\
+		    CIRCLEQ_PREV((elm), field);				\
+	if (CIRCLEQ_PREV((elm), field) == (void *)(head))		\
+		CIRCLEQ_FIRST((head)) = CIRCLEQ_NEXT((elm), field);	\
+	else								\
+		CIRCLEQ_NEXT(CIRCLEQ_PREV((elm), field), field) =	\
+		    CIRCLEQ_NEXT((elm), field);				\
+} while (0)
+
+#endif /* !_SYS_QUEUE_H_ */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/scsi_message.h linux/drivers/scsi/aic7xxx/scsi_message.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/scsi_message.h	Wed Jun  9 16:59:15 1999
+++ linux/drivers/scsi/aic7xxx/scsi_message.h	Mon Dec  3 22:29:50 2001
@@ -1,34 +1,52 @@
+/*
+ * This file is in the public domain.
+ * $FreeBSD: src/sys/cam/scsi/scsi_message.h,v 1.2 2000/05/01 20:21:29 peter Exp $
+ */
+
 /* Messages (1 byte) */		     /* I/T (M)andatory or (O)ptional */
 #define MSG_CMDCOMPLETE		0x00 /* M/M */
+#define MSG_TASK_COMPLETE	0x00 /* M/M */ /* SPI3 Terminology */
 #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_ABORT_TASK_SET	0x06 /* O/M */ /* SPI3 Terminology */
 #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_TARGET_RESET	0x0c /* O/M */ /* SPI3 Terminology */
 #define MSG_ABORT_TAG		0x0d /* O/O */
+#define MSG_ABORT_TASK		0x0d /* O/O */ /* SPI3 Terminology */
 #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 */
+#define MSG_CLEAR_TASK_SET	0x0e /* O/O */ /* SPI3 Terminology */
+#define MSG_INIT_RECOVERY	0x0f /* O/O */ /* Deprecated in SPI3 */
+#define MSG_REL_RECOVERY	0x10 /* O/O */ /* Deprecated in SPI3 */
+#define MSG_TERM_IO_PROC	0x11 /* O/O */ /* Deprecated in SPI3 */
+#define MSG_CLEAR_ACA		0x16 /* O/O */ /* SPI3 */
+#define MSG_LOGICAL_UNIT_RESET	0x17 /* O/O */ /* SPI3 */
+#define MSG_QAS_REQUEST		0x55 /* O/O */ /* SPI3 */
 
 /* Messages (2 byte) */
 #define MSG_SIMPLE_Q_TAG	0x20 /* O/O */
+#define MSG_SIMPLE_TASK		0x20 /* O/O */ /* SPI3 Terminology */
 #define MSG_HEAD_OF_Q_TAG	0x21 /* O/O */
+#define MSG_HEAD_OF_QUEUE_TASK	0x21 /* O/O */ /* SPI3 Terminology */
 #define MSG_ORDERED_Q_TAG	0x22 /* O/O */
+#define MSG_ORDERED_TASK	0x22 /* O/O */ /* SPI3 Terminology */
 #define MSG_IGN_WIDE_RESIDUE	0x23 /* O/O */
+#define MSG_ACA_TASK		0x24 /* 0/0 */ /* SPI3 */
 
 /* Identify message */		     /* M/M */	
 #define MSG_IDENTIFYFLAG	0x80 
 #define MSG_IDENTIFY_DISCFLAG	0x40 
 #define MSG_IDENTIFY(lun, disc)	(((disc) ? 0xc0 : MSG_IDENTIFYFLAG) | (lun))
 #define MSG_ISIDENTIFY(m)	((m) & MSG_IDENTIFYFLAG)
+#define MSG_IDENTIFY_LUNMASK	0x03F 
 
 /* Extended messages (opcode and length) */
 #define MSG_EXT_SDTR		0x01
@@ -38,12 +56,10 @@
 #define MSG_EXT_WDTR_LEN	0x02
 #define MSG_EXT_WDTR_BUS_8_BIT	0x00
 #define MSG_EXT_WDTR_BUS_16_BIT	0x01
-#define MSG_EXT_WDTR_BUS_32_BIT	0x02
+#define MSG_EXT_WDTR_BUS_32_BIT	0x02 /* Deprecated in SPI3 */
 
-#define MSG_EXT_PPR     0x04
-#define MSG_EXT_PPR_LEN	0x06
-#define MSG_EXT_PPR_OPTION_ST 0x00
-#define MSG_EXT_PPR_OPTION_DT_CRC 0x02
-#define MSG_EXT_PPR_OPTION_DT_UNITS 0x03
-#define MSG_EXT_PPR_OPTION_DT_CRC_QUICK 0x04
-#define MSG_EXT_PPR_OPTION_DT_UNITS_QUICK 0x05
+#define MSG_EXT_PPR		0x04 /* SPI3 */
+#define MSG_EXT_PPR_LEN		0x06
+#define MSG_EXT_PPR_QAS_REQ	0x04
+#define MSG_EXT_PPR_DT_REQ	0x02
+#define MSG_EXT_PPR_IU_REQ	0x01
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx/sequencer.h linux/drivers/scsi/aic7xxx/sequencer.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx/sequencer.h	Sun Nov  8 13:20:14 1998
+++ linux/drivers/scsi/aic7xxx/sequencer.h	Wed Dec 31 16:00:00 1969
@@ -1,135 +0,0 @@
-/*
- * Instruction formats for the sequencer program downloaded to
- * Aic7xxx SCSI host adapters
- *
- * Copyright (c) 1997, 1998 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. 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.3 1997/09/27 19:37:31 gibbs Exp $
- */
-
-#ifdef __LITTLE_ENDIAN_BITFIELD
-struct ins_format1 {
-	unsigned int
-			immediate	: 8,
-			source		: 9,
-			destination	: 9,
-			ret		: 1,
-			opcode		: 4,
-			parity		: 1;
-};
-
-struct ins_format2 {
-	unsigned int
-			shift_control	: 8,
-			source		: 9,
-			destination	: 9,
-			ret		: 1,
-			opcode		: 4,
-			parity		: 1;
-};
-
-struct ins_format3 {
-	unsigned int
-			immediate	: 8,
-			source		: 9,
-			address		: 10,
-			opcode		: 4,
-			parity		: 1;
-};
-#elif defined(__BIG_ENDIAN_BITFIELD)
-struct ins_format1 {
-	unsigned int
-			parity		: 1,
-			opcode		: 4,
-			ret		: 1,
-			destination	: 9,
-			source		: 9,
-			immediate	: 8;
-};
-
-struct ins_format2 {
-	unsigned int
-			parity		: 1,
-			opcode		: 4,
-			ret		: 1,
-			destination	: 9,
-			source		: 9,
-			shift_control	: 8;
-};
-
-struct ins_format3 {
-	unsigned int
-			parity		: 1,
-			opcode		: 4,
-			address		: 10,
-			source		: 9,
-			immediate	: 8;
-};
-#endif
-
-union ins_formats {
-		struct ins_format1 format1;
-		struct ins_format2 format2;
-		struct ins_format3 format3;
-		unsigned char	   bytes[4];
-		unsigned int	   integer;
-};
-struct instruction {
-	union	ins_formats format;
-	unsigned int	srcline;
-	struct symbol *patch_label;
-  struct {
-    struct instruction *stqe_next;
-  } 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_BMOV	0x6
-
-#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 -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx.c linux/drivers/scsi/aic7xxx.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/aic7xxx.c	Wed Dec 31 16:00:00 1969
@@ -1,12242 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx device driver for Linux.
- *
- * 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.
- *
- * Sources include the Adaptec 1740 driver (aha1740.c), the Ultrastor 24F
- * driver (ultrastor.c), various Linux kernel source, the Adaptec EISA
- * config file (!adp7771.cfg), the Adaptec AHA-2740A Series User's Guide,
- * the Linux Kernel Hacker's Guide, Writing a SCSI Device Driver for Linux,
- * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA overlay file
- * (adp7770.ovl), the Adaptec AHA-2740 Series Technical Reference Manual,
- * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
- * ANSI SCSI-2 specification (draft 10c), ...
- *
- * --------------------------------------------------------------------------
- *
- *  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 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
- *           aic7xxx=no_reset
- *           aic7xxx=ultra
- *           aic7xxx=irq_trigger:[0,1]  # 0 edge, 1 level
- *           aic7xxx=verbose
- *
- *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
- *
- *  $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
- *-M*************************************************************************/
-
-/*+M**************************************************************************
- *
- * Further driver modifications made by Doug Ledford <dledford@redhat.com>
- *
- * Copyright (c) 1997-1999 Doug Ledford
- *
- * These changes are released under the same licensing terms as the FreeBSD
- * driver written by Justin Gibbs.  Please see his Copyright notice above
- * for the exact terms and conditions covering my changes as well as the
- * warranty statement.
- *
- * Modifications made to the aic7xxx.c,v 4.1 driver from Dan Eischen include
- * but are not limited to:
- *
- *  1: Import of the latest FreeBSD sequencer code for this driver
- *  2: Modification of kernel code to accomodate different sequencer semantics
- *  3: Extensive changes throughout kernel portion of driver to improve
- *     abort/reset processing and error hanndling
- *  4: Other work contributed by various people on the Internet
- *  5: Changes to printk information and verbosity selection code
- *  6: General reliability related changes, especially in IRQ management
- *  7: Modifications to the default probe/attach order for supported cards
- *  8: SMP friendliness has been improved
- *
- * Overall, this driver represents a significant departure from the official
- * aic7xxx driver released by Dan Eischen in two ways.  First, in the code
- * itself.  A diff between the two version of the driver is now a several
- * thousand line diff.  Second, in approach to solving the same problem.  The
- * problem is importing the FreeBSD aic7xxx driver code to linux can be a
- * difficult and time consuming process, that also can be error prone.  Dan
- * Eischen's official driver uses the approach that the linux and FreeBSD
- * drivers should be as identical as possible.  To that end, his next version
- * of this driver will be using a mid-layer code library that he is developing
- * to moderate communications between the linux mid-level SCSI code and the
- * low level FreeBSD driver.  He intends to be able to essentially drop the
- * FreeBSD driver into the linux kernel with only a few minor tweaks to some
- * include files and the like and get things working, making for fast easy
- * imports of the FreeBSD code into linux.
- *
- * I disagree with Dan's approach.  Not that I don't think his way of doing
- * things would be nice, easy to maintain, and create a more uniform driver
- * between FreeBSD and Linux.  I have no objection to those issues.  My
- * disagreement is on the needed functionality.  There simply are certain
- * things that are done differently in FreeBSD than linux that will cause
- * problems for this driver regardless of any middle ware Dan implements.
- * The biggest example of this at the moment is interrupt semantics.  Linux
- * doesn't provide the same protection techniques as FreeBSD does, nor can
- * they be easily implemented in any middle ware code since they would truly
- * belong in the kernel proper and would effect all drivers.  For the time
- * being, I see issues such as these as major stumbling blocks to the 
- * reliability of code based upon such middle ware.  Therefore, I choose to
- * use a different approach to importing the FreeBSD code that doesn't
- * involve any middle ware type code.  My approach is to import the sequencer
- * code from FreeBSD wholesale.  Then, to only make changes in the kernel
- * portion of the driver as they are needed for the new sequencer semantics.
- * In this way, the portion of the driver that speaks to the rest of the
- * linux kernel is fairly static and can be changed/modified to solve
- * any problems one might encounter without concern for the FreeBSD driver.
- *
- * Note: If time and experience should prove me wrong that the middle ware
- * code Dan writes is reliable in its operation, then I'll retract my above
- * statements.  But, for those that don't know, I'm from Missouri (in the US)
- * and our state motto is "The Show-Me State".  Well, before I will put
- * faith into it, you'll have to show me that it works :)
- *
- *_M*************************************************************************/
-
-/*
- * The next three defines are user configurable.  These should be the only
- * defines a user might need to get in here and change.  There are other
- * defines buried deeper in the code, but those really shouldn't need touched
- * under normal conditions.
- */
-
-/*
- * AIC7XXX_STRICT_PCI_SETUP
- *   Should we assume the PCI config options on our controllers are set with
- *   sane and proper values, or should we be anal about our PCI config
- *   registers and force them to what we want?  The main advantage to
- *   defining this option is on non-Intel hardware where the BIOS may not
- *   have been run to set things up, or if you have one of the BIOSless
- *   Adaptec controllers, such as a 2910, that don't get set up by the
- *   BIOS.  However, keep in mind that we really do set the most important
- *   items in the driver regardless of this setting, this only controls some
- *   of the more esoteric PCI options on these cards.  In that sense, I
- *   would default to leaving this off.  However, if people wish to try
- *   things both ways, that would also help me to know if there are some
- *   machines where it works one way but not another.
- *
- *   -- July 7, 17:09
- *     OK...I need this on my machine for testing, so the default is to
- *     leave it defined.
- *
- *   -- July 7, 18:49
- *     I needed it for testing, but it didn't make any difference, so back
- *     off she goes.
- *
- *   -- July 16, 23:04
- *     I turned it back on to try and compensate for the 2.1.x PCI code
- *     which no longer relies solely on the BIOS and now tries to set
- *     things itself.
- */
-
-#define AIC7XXX_STRICT_PCI_SETUP
-
-/*
- * AIC7XXX_VERBOSE_DEBUGGING
- *   This option enables a lot of extra printk();s in the code, surrounded
- *   by if (aic7xxx_verbose ...) statements.  Executing all of those if
- *   statements and the extra checks can get to where it actually does have
- *   an impact on CPU usage and such, as well as code size.  Disabling this
- *   define will keep some of those from becoming part of the code.
- *
- *   NOTE:  Currently, this option has no real effect, I will be adding the
- *   various #ifdef's in the code later when I've decided a section is
- *   complete and no longer needs debugging.  OK...a lot of things are now
- *   surrounded by this define, so turning this off does have an impact.
- */
- 
-/*
- * #define AIC7XXX_VERBOSE_DEBUGGING
- */
- 
-#if defined(MODULE) || defined(PCMCIA)
-#include <linux/module.h>
-#endif
-
-#if defined(PCMCIA)
-#  undef MODULE
-#endif
-
-#include <stdarg.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/byteorder.h>
-#include <linux/version.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/ioport.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/pci.h>
-#include <linux/proc_fs.h>
-#include <linux/blk.h>
-#include <linux/tqueue.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/smp.h>
-#include "sd.h"
-#include "scsi.h"
-#include "hosts.h"
-#include "aic7xxx.h"
-
-#include "aic7xxx/sequencer.h"
-#include "aic7xxx/scsi_message.h"
-#include "aic7xxx_reg.h"
-#include <scsi/scsicam.h>
-
-#include <linux/stat.h>
-#include <linux/slab.h>        /* for kmalloc() */
-
-#include <linux/config.h>        /* for CONFIG_PCI */
-
-/*
- * To generate the correct addresses for the controller to issue
- * on the bus.  Originally added for DEC Alpha support.
- */
-#define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a))
-
-#define AIC7XXX_C_VERSION  "5.2.1"
-
-#define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
-#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
-#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
-#define ALL_TARGETS -1
-#define ALL_CHANNELS -1
-#define ALL_LUNS -1
-#define MAX_TARGETS  16
-#define MAX_LUNS     8
-#ifndef TRUE
-#  define TRUE 1
-#endif
-#ifndef FALSE
-#  define FALSE 0
-#endif
-
-#if defined(__powerpc__) || defined(__i386__)
-#  define MMAPIO
-#endif
-
-#  if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-#    define cpuid smp_processor_id()
-#    define DRIVER_LOCK_INIT \
-       spin_lock_init(&p->spin_lock);
-#    define DRIVER_LOCK \
-       if(!p->cpu_lock_count[cpuid]) { \
-         spin_lock_irqsave(&p->spin_lock, cpu_flags); \
-         p->cpu_lock_count[cpuid]++; \
-       } else { \
-         p->cpu_lock_count[cpuid]++; \
-       }
-#    define DRIVER_UNLOCK \
-       if(--p->cpu_lock_count[cpuid] == 0) \
-         spin_unlock_irqrestore(&p->spin_lock, cpu_flags);
-#  else
-#    define DRIVER_LOCK_INIT
-#    define DRIVER_LOCK
-#    define DRIVER_UNLOCK
-#  endif
-
-/*
- * You can try raising me if tagged queueing is enabled, or lowering
- * me if you only have 4 SCBs.
- */
-#ifdef CONFIG_AIC7XXX_CMDS_PER_DEVICE
-#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_CMDS_PER_DEVICE
-#else
-#define AIC7XXX_CMDS_PER_DEVICE 8
-#endif
-
-/*
- * 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.
- */
-#ifdef CONFIG_AIC7XXX_PROC_STATS
-#define AIC7XXX_PROC_STATS
-#endif
-
-/*
- * *** Determining commands per LUN ***
- * 
- * When AIC7XXX_CMDS_PER_DEVICE is not defined, the driver will use its
- * own algorithm to determine the commands/LUN.  If SCB paging is
- * enabled, which is always now, the default is 8 commands per lun
- * that indicates it supports tagged queueing.  All non-tagged devices
- * use an internal queue depth of 3, with no more than one of those
- * three commands active at one time.
- */
-
-typedef struct
-{
-  unsigned char tag_commands[16];   /* Allow for wide/twin adapters. */
-} adapter_tag_info_t;
-
-/*
- * Make a define that will tell the driver not to use tagged queueing
- * by default.
- */
-#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
-#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\
-                              0, 0, 0, 0, 0, 0, 0, 0}
-#else
-#define DEFAULT_TAG_COMMANDS {255, 255, 255, 255, 255, 255, 255, 255,\
-                              255, 255, 255, 255, 255, 255, 255, 255}
-#endif
-
-/*
- * 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 255, the driver will
- * not enable tagged queueing for that particular device.  When positive
- * (> 0) and (< 255) the values in the array are used for the queue_depth.
- * Note that the maximum value for an entry is 254, but you're insane if
- * you try to use that many commands on one device.
- *
- * In this example, the first line will disable tagged queueing for all
- * the devices on the first probed aic7xxx adapter.
- *
- * 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.
- */
-
-/*
- * NOTE: The below structure is for reference only, the actual structure
- *       to modify in order to change things is found after this fake one.
- *
-adapter_tag_info_t aic7xxx_tag_info[] =
-{
-  {DEFAULT_TAG_COMMANDS},
-  {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 255, 4, 4, 4}},
-  {DEFAULT_TAG_COMMANDS},
-  {{255, 16, 4, 255, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
-};
-*/
-
-static adapter_tag_info_t aic7xxx_tag_info[] =
-{
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS},
-  {DEFAULT_TAG_COMMANDS}
-};
-
-
-/*
- * Define an array of board names that can be indexed by aha_type.
- * Don't forget to change this when changing the types!
- */
-static const char *board_names[] = {
-  "AIC-7xxx Unknown",                                   /* AIC_NONE */
-  "Adaptec AIC-7810 Hardware RAID Controller",          /* AIC_7810 */
-  "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 */
-  "Adaptec AHA-2940UW Pro Ultra SCSI host adapter",     /* AIC_7887 */
-  "Adaptec AIC-7895 Ultra SCSI host adapter",           /* AIC_7895 */
-  "Adaptec AIC-7890/1 Ultra2 SCSI host adapter",        /* AIC_7890 */
-  "Adaptec AHA-293X Ultra2 SCSI host adapter",          /* AIC_7890 */
-  "Adaptec AHA-294X Ultra2 SCSI host adapter",          /* AIC_7890 */
-  "Adaptec AIC-7896/7 Ultra2 SCSI host adapter",        /* AIC_7896 */
-  "Adaptec AHA-394X Ultra2 SCSI host adapter",          /* AIC_7897 */
-  "Adaptec AHA-395X Ultra2 SCSI host adapter",          /* AIC_7897 */
-  "Adaptec PCMCIA SCSI controller",                     /* card bus stuff */
-  "Adaptec AIC-7892 Ultra 160/m SCSI host adapter",     /* AIC_7892 */
-  "Adaptec AIC-7899 Ultra 160/m SCSI host adapter",     /* AIC_7899 */
-};
-
-/*
- * There should be a specific return value for this in scsi.h, but
- * it seems that most drivers ignore it.
- */
-#define DID_UNDERFLOW   DID_ERROR
-
-/*
- *  What we want to do is have the higher level scsi driver requeue
- *  the command to us. There is no specific driver status for this
- *  condition, but the higher level scsi driver will requeue the
- *  command on a DID_BUS_BUSY error.
- *
- *  Upon further inspection and testing, it seems that DID_BUS_BUSY
- *  will *always* retry the command.  We can get into an infinite loop
- *  if this happens when we really want some sort of counter that
- *  will automatically abort/reset the command after so many retries.
- *  Using DID_ERROR will do just that.  (Made by a suggestion by
- *  Doug Ledford 8/1/96)
- */
-#define DID_RETRY_COMMAND DID_ERROR
-
-#define HSCSIID        0x07
-#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)
- */
-#define AHC_HID0              0x80   /* 0,1: msb of ID2, 2-7: ID1      */
-#define AHC_HID1              0x81   /* 0-4: ID3, 5-7: LSB ID2         */
-#define AHC_HID2              0x82   /* product                        */
-#define AHC_HID3              0x83   /* firmware revision              */
-
-/*
- * AIC-7770 I/O range to reserve for a card
- */
-#define MINREG                0xC00
-#define MAXREG                0xCFF
-
-#define INTDEF                0x5C      /* Interrupt Definition Register */
-
-/*
- * AIC-78X0 PCI registers
- */
-#define        CLASS_PROGIF_REVID        0x08
-#define                DEVREVID        0x000000FFul
-#define                PROGINFC        0x0000FF00ul
-#define                SUBCLASS        0x00FF0000ul
-#define                BASECLASS        0xFF000000ul
-
-#define        CSIZE_LATTIME                0x0C
-#define                CACHESIZE        0x0000003Ful        /* only 5 bits */
-#define                LATTIME                0x0000FF00ul
-
-#define        DEVCONFIG                0x40
-#define                SCBSIZE32        0x00010000ul        /* aic789X only */
-#define                MPORTMODE        0x00000400ul        /* aic7870 only */
-#define                RAMPSM           0x00000200ul        /* aic7870 only */
-#define                RAMPSM_ULTRA2    0x00000004
-#define                VOLSENSE         0x00000100ul
-#define                SCBRAMSEL        0x00000080ul
-#define                SCBRAMSEL_ULTRA2 0x00000008
-#define                MRDCEN           0x00000040ul
-#define                EXTSCBTIME       0x00000020ul        /* aic7870 only */
-#define                EXTSCBPEN        0x00000010ul        /* aic7870 only */
-#define                BERREN           0x00000008ul
-#define                DACEN            0x00000004ul
-#define                STPWLEVEL        0x00000002ul
-#define                DIFACTNEGEN      0x00000001ul        /* aic7870 only */
-
-#define        SCAMCTL                  0x1a                /* Ultra2 only  */
-#define        CCSCBBADDR               0xf0                /* aic7895/6/7  */
-
-/*
- * Define the different types of SEEPROMs on aic7xxx adapters
- * and make it also represent the address size used in accessing
- * its registers.  The 93C46 chips have 1024 bits organized into
- * 64 16-bit words, while the 93C56 chips have 2048 bits organized
- * into 128 16-bit words.  The C46 chips use 6 bits to address
- * 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;
-
-/*
- *
- * Define the format of the SEEPROM registers (16 bits).
- *
- */
-struct seeprom_config {
-
-/*
- * SCSI ID Configuration Flags
- */
-#define CFXFER                0x0007      /* synchronous transfer rate */
-#define CFSYNCH               0x0008      /* enable synchronous transfer */
-#define CFDISC                0x0010      /* enable disconnection */
-#define CFWIDEB               0x0020      /* wide bus device (wide card) */
-#define CFSYNCHISULTRA        0x0040      /* CFSYNC is an ultra offset */
-#define CFNEWULTRAFORMAT      0x0080      /* Use the Ultra2 SEEPROM format */
-#define CFSTART               0x0100      /* send start unit SCSI command */
-#define CFINCBIOS             0x0200      /* include in BIOS scan */
-#define CFRNFOUND             0x0400      /* report even if not found */
-#define CFMULTILUN            0x0800      /* probe mult luns in BIOS scan */
-#define CFWBCACHEYES          0x4000      /* Enable W-Behind Cache on drive */
-#define CFWBCACHENC           0xc000      /* Don't change W-Behind Cache */
-/* UNUSED                0x3000 */
-  unsigned short device_flags[16];        /* words 0-15 */
-
-/*
- * BIOS Control Bits
- */
-#define CFSUPREM        0x0001  /* support all removable drives */
-#define CFSUPREMB       0x0002  /* support removable drives for boot only */
-#define CFBIOSEN        0x0004  /* BIOS enabled */
-/* UNUSED                0x0008 */
-#define CFSM2DRV        0x0010  /* support more than two drives */
-#define CF284XEXTEND    0x0020  /* extended translation (284x cards) */
-/* UNUSED                0x0040 */
-#define CFEXTEND        0x0080  /* extended translation enabled */
-/* UNUSED                0xFF00 */
-  unsigned short bios_control;  /* word 16 */
-
-/*
- * Host Adapter Control Bits
- */
-#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 */
-#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 boot */
-#define CFBPRIMARY      0x0100  /* Channel B primary on 7895 chipsets */
-#define CFSEAUTOTERM    0x0400  /* aic7890 Perform SE Auto Term */
-#define CFLVDSTERM      0x0800  /* aic7890 LVD Termination */
-/* UNUSED                0xF280 */
-  unsigned short adapter_control;        /* word 17 */
-
-/*
- * Bus Release, Host Adapter ID
- */
-#define CFSCSIID        0x000F                /* host adapter SCSI ID */
-/* UNUSED                0x00F0 */
-#define CFBRTIME        0xFF00                /* bus release time */
-  unsigned short brtime_id;                /* word 18 */
-
-/*
- * Maximum targets
- */
-#define CFMAXTARG        0x00FF        /* maximum targets */
-/* UNUSED                0xFF00 */
-  unsigned short max_targets;                /* word 19 */
-
-  unsigned short res_1[11];                /* words 20-30 */
-  unsigned short checksum;                /* word 31 */
-};
-
-#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
- * to completion - it's easier that way - making a note of the error
- * condition in this location. This then will modify a DID_OK status
- * into an appropriate error for the higher-level SCSI code.
- */
-#define aic7xxx_error(cmd)        ((cmd)->SCp.Status)
-
-/*
- * Keep track of the targets returned status.
- */
-#define aic7xxx_status(cmd)        ((cmd)->SCp.sent_command)
-
-/*
- * The position of the SCSI commands scb within the scb array.
- */
-#define aic7xxx_position(cmd)        ((cmd)->SCp.have_data_in)
-
-/*
- * The stored DMA mapping for single-buffer data transfers.
- */
-#define aic7xxx_mapping(cmd)	     ((cmd)->SCp.phase)
-
-/*
- * So we can keep track of our host structs
- */
-static struct aic7xxx_host *first_aic7xxx = NULL;
-
-/*
- * 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.
- */
-struct hw_scatterlist {
-  unsigned int address;
-  unsigned int length;
-};
-
-/*
- * Maximum number of SG segments these cards can support.
- */
-#define        AIC7XXX_MAX_SG 128
-
-/*
- * 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 int  SG_list_pointer;
-/* 8*/  unsigned char residual_SG_segment_count;
-/* 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*/  unsigned char tag;          /* Index into our kernel SCB array.
-                                     * Also used as the tag for tagged I/O
-                                     */
-#define SCB_PIO_TRANSFER_SIZE  26   /* amount we need to upload/download
-                                     * via PIO to initialize a transaction.
-                                     */
-/*26*/  unsigned char next;         /* Used to thread SCBs awaiting selection
-                                     * or disconnected down in the sequencer.
-                                     */
-/*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_WAITINGQ            = 0x0002,
-        SCB_ACTIVE              = 0x0004,
-        SCB_SENSE               = 0x0008,
-        SCB_ABORT               = 0x0010,
-        SCB_DEVICE_RESET        = 0x0020,
-        SCB_RESET               = 0x0040,
-        SCB_RECOVERY_SCB        = 0x0080,
-        SCB_MSGOUT_PPR          = 0x0100,
-        SCB_MSGOUT_SENT         = 0x0200,
-        SCB_MSGOUT_SDTR         = 0x0400,
-        SCB_MSGOUT_WDTR         = 0x0800,
-        SCB_MSGOUT_BITS         = SCB_MSGOUT_PPR |
-                                  SCB_MSGOUT_SENT | 
-                                  SCB_MSGOUT_SDTR |
-                                  SCB_MSGOUT_WDTR,
-        SCB_QUEUED_ABORT        = 0x1000,
-        SCB_QUEUED_FOR_DONE     = 0x2000,
-        SCB_WAS_BUSY            = 0x4000
-} scb_flag_type;
-
-typedef enum {
-        AHC_FNONE                 = 0x00000000,
-        AHC_PAGESCBS              = 0x00000001,
-        AHC_CHANNEL_B_PRIMARY     = 0x00000002,
-        AHC_USEDEFAULTS           = 0x00000004,
-        AHC_INDIRECT_PAGING       = 0x00000008,
-        AHC_CHNLB                 = 0x00000020,
-        AHC_CHNLC                 = 0x00000040,
-        AHC_EXTEND_TRANS_A        = 0x00000100,
-        AHC_EXTEND_TRANS_B        = 0x00000200,
-        AHC_TERM_ENB_A            = 0x00000400,
-        AHC_TERM_ENB_SE_LOW       = 0x00000400,
-        AHC_TERM_ENB_B            = 0x00000800,
-        AHC_TERM_ENB_SE_HIGH      = 0x00000800,
-        AHC_HANDLING_REQINITS     = 0x00001000,
-        AHC_TARGETMODE            = 0x00002000,
-        AHC_NEWEEPROM_FMT         = 0x00004000,
- /*
-  *  Here ends the FreeBSD defined flags and here begins the linux defined
-  *  flags.  NOTE: I did not preserve the old flag name during this change
-  *  specifically to force me to evaluate what flags were being used properly
-  *  and what flags weren't.  This way, I could clean up the flag usage on
-  *  a use by use basis.  Doug Ledford
-  */
-        AHC_MOTHERBOARD           = 0x00020000,
-        AHC_NO_STPWEN             = 0x00040000,
-        AHC_RESET_DELAY           = 0x00080000,
-        AHC_A_SCANNED             = 0x00100000,
-        AHC_B_SCANNED             = 0x00200000,
-        AHC_MULTI_CHANNEL         = 0x00400000,
-        AHC_BIOS_ENABLED          = 0x00800000,
-        AHC_SEEPROM_FOUND         = 0x01000000,
-        AHC_TERM_ENB_LVD          = 0x02000000,
-        AHC_ABORT_PENDING         = 0x04000000,
-        AHC_RESET_PENDING         = 0x08000000,
-#define AHC_IN_ISR_BIT              28
-        AHC_IN_ISR                = 0x10000000,
-        AHC_IN_ABORT              = 0x20000000,
-        AHC_IN_RESET              = 0x40000000,
-        AHC_EXTERNAL_SRAM         = 0x80000000
-} ahc_flag_type;
-
-typedef enum {
-  AHC_NONE             = 0x0000,
-  AHC_CHIPID_MASK      = 0x00ff,
-  AHC_AIC7770          = 0x0001,
-  AHC_AIC7850          = 0x0002,
-  AHC_AIC7860          = 0x0003,
-  AHC_AIC7870          = 0x0004,
-  AHC_AIC7880          = 0x0005,
-  AHC_AIC7890          = 0x0006,
-  AHC_AIC7895          = 0x0007,
-  AHC_AIC7896          = 0x0008,
-  AHC_AIC7892          = 0x0009,
-  AHC_AIC7899          = 0x000a,
-  AHC_VL               = 0x0100,
-  AHC_EISA             = 0x0200,
-  AHC_PCI              = 0x0400,
-} ahc_chip;
-
-typedef enum {
-  AHC_FENONE           = 0x0000,
-  AHC_ULTRA            = 0x0001,
-  AHC_ULTRA2           = 0x0002,
-  AHC_WIDE             = 0x0004,
-  AHC_TWIN             = 0x0008,
-  AHC_MORE_SRAM        = 0x0010,
-  AHC_CMD_CHAN         = 0x0020,
-  AHC_QUEUE_REGS       = 0x0040,
-  AHC_SG_PRELOAD       = 0x0080,
-  AHC_SPIOCAP          = 0x0100,
-  AHC_ULTRA3           = 0x0200,
-  AHC_NEW_AUTOTERM     = 0x0400,
-  AHC_AIC7770_FE       = AHC_FENONE,
-  AHC_AIC7850_FE       = AHC_SPIOCAP,
-  AHC_AIC7860_FE       = AHC_ULTRA|AHC_SPIOCAP,
-  AHC_AIC7870_FE       = AHC_FENONE,
-  AHC_AIC7880_FE       = AHC_ULTRA,
-  AHC_AIC7890_FE       = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2|
-                         AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_NEW_AUTOTERM,
-  AHC_AIC7895_FE       = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA,
-  AHC_AIC7896_FE       = AHC_AIC7890_FE,
-  AHC_AIC7892_FE       = AHC_AIC7890_FE|AHC_ULTRA3,
-  AHC_AIC7899_FE       = AHC_AIC7890_FE|AHC_ULTRA3,
-} ahc_feature;
-
-#define SCB_DMA_ADDR(scb, addr) ((unsigned long)(addr) + (scb)->scb_dma->dma_offset)
-
-struct aic7xxx_scb_dma {
-	unsigned long	       dma_offset;    /* Correction you have to add
-					       * to virtual address to get
-					       * dma handle in this region */
-	dma_addr_t	       dma_address;   /* DMA handle of the start,
-					       * for unmap */
-	unsigned int	       dma_len;	      /* DMA length */
-};
-
-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 */
-        volatile scb_flag_type flags;         /* current state of scb */
-        struct hw_scatterlist *sg_list;       /* SG list in adapter format */
-        unsigned char          tag_action;
-        unsigned char          sg_count;
-        unsigned char          *sense_cmd;    /*
-                                               * Allocate 6 characters for
-                                               * sense command.
-                                               */
-	unsigned char	       *cmnd;
-        unsigned int           sg_length; /* We init this during buildscb so we
-                                           * don't have to calculate anything
-                                           * during underflow/overflow/stat code
-                                           */
-        void                  *kmalloc_ptr;
-	struct aic7xxx_scb_dma *scb_dma;
-};
-
-/*
- * Define a linked list of SCBs.
- */
-typedef struct {
-  struct aic7xxx_scb *head;
-  struct aic7xxx_scb *tail;
-} scb_queue_type;
-
-static struct {
-  unsigned char errno;
-  const char *errmesg;
-} hard_error[] = {
-  { ILLHADDR,  "Illegal Host Access" },
-  { ILLSADDR,  "Illegal Sequencer Address referenced" },
-  { ILLOPCODE, "Illegal Opcode in sequencer program" },
-  { SQPARERR,  "Sequencer Ram Parity Error" },
-  { DPARERR,   "Data-Path Ram Parity Error" },
-  { MPARERR,   "Scratch Ram/SCB Array Ram Parity Error" },
-  { PCIERRSTAT,"PCI Error detected" },
-  { CIOPARERR, "CIOBUS Parity Error" }
-};
-
-static unsigned char
-generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
-
-typedef struct {
-  scb_queue_type free_scbs;        /*
-                                    * SCBs assigned to free slot on
-                                    * card (no paging required)
-                                    */
-  struct aic7xxx_scb   *scb_array[AIC7XXX_MAXSCB];
-  struct aic7xxx_hwscb *hscbs;
-  unsigned char  numscbs;          /* current number of scbs */
-  unsigned char  maxhscbs;         /* hardware scbs */
-  unsigned char  maxscbs;          /* max scbs including pageable scbs */
-  dma_addr_t	 hscbs_dma;	   /* DMA handle to hscbs */
-  unsigned int   hscbs_dma_len;    /* length of the above DMA area */
-  void          *hscb_kmalloc_ptr;
-} scb_data_type;
-
-struct target_cmd {
-  unsigned char mesg_bytes[4];
-  unsigned char command[28];
-};
-
-#define AHC_TRANS_CUR    0x0001
-#define AHC_TRANS_ACTIVE 0x0002
-#define AHC_TRANS_GOAL   0x0004
-#define AHC_TRANS_USER   0x0008
-#define AHC_TRANS_QUITE  0x0010
-typedef struct {
-  unsigned char cur_width;
-  unsigned char goal_width;
-  unsigned char cur_period;
-  unsigned char goal_period;
-  unsigned char cur_offset;
-  unsigned char goal_offset;
-  unsigned char cur_options;
-  unsigned char goal_options;
-  unsigned char user_width;
-  unsigned char user_period;
-  unsigned char user_offset;
-  unsigned char user_options;
-} transinfo_type;
-
-/*
- * Define a structure used for each host adapter.  Note, in order to avoid
- * problems with architectures I can't test on (because I don't have one,
- * such as the Alpha based systems) which happen to give faults for
- * non-aligned memory accesses, care was taken to align this structure
- * in a way that gauranteed all accesses larger than 8 bits were aligned
- * on the appropriate boundary.  It's also organized to try and be more
- * cache line efficient.  Be careful when changing this lest you might hurt
- * overall performance and bring down the wrath of the masses.
- */
-struct aic7xxx_host {
-  /*
-   *  This is the first 64 bytes in the host struct
-   */
-
-  /*
-   * We are grouping things here....first, items that get either read or
-   * written with nearly every interrupt
-   */
-  volatile ahc_flag_type   flags;
-  ahc_feature              features;         /* chip features */
-  unsigned long            base;             /* card base address */
-  volatile unsigned char  *maddr;            /* memory mapped address */
-  unsigned long            isr_count;        /* Interrupt count */
-  unsigned long            spurious_int;
-  scb_data_type           *scb_data;
-  volatile unsigned short  needdv;
-  volatile unsigned short  needppr;
-  volatile unsigned short  needsdtr;
-  volatile unsigned short  needwdtr;
-  volatile unsigned short  dtr_pending;
-  struct aic7xxx_cmd_queue {
-    Scsi_Cmnd *head;
-    Scsi_Cmnd *tail;
-  } completeq;
-
-  /*
-   * Things read/written on nearly every entry into aic7xxx_queue()
-   */
-  volatile scb_queue_type  waiting_scbs;
-  unsigned short           discenable;       /* Targets allowed to disconnect */
-  unsigned short           tagenable;        /* Targets using tagged I/O */
-  unsigned short           orderedtag;       /* Ordered Q tags allowed */
-  unsigned char            unpause;          /* unpause value for HCNTRL */
-  unsigned char            pause;            /* pause value for HCNTRL */
-  volatile unsigned char   qoutfifonext;
-  volatile unsigned char   activescbs;       /* active scbs */
-  volatile unsigned char   max_activescbs;
-  volatile unsigned char   qinfifonext;
-  volatile unsigned char  *untagged_scbs;
-  volatile unsigned char  *qoutfifo;
-  volatile unsigned char  *qinfifo;
-
-#define  DEVICE_PRESENT                 0x01
-#define  BUS_DEVICE_RESET_PENDING       0x02
-#define  DEVICE_RESET_DELAY             0x04
-#define  DEVICE_PRINT_DTR               0x08
-#define  DEVICE_PARITY_ERROR            0x10
-#define  DEVICE_WAS_BUSY                0x20
-#define  DEVICE_SCSI_3                  0x40
-#define  DEVICE_SCANNED                 0x80
-  volatile unsigned char   dev_flags[MAX_TARGETS];
-  volatile unsigned char   dev_active_cmds[MAX_TARGETS];
-  volatile unsigned char   dev_temp_queue_depth[MAX_TARGETS];
-  unsigned char            dev_commands_sent[MAX_TARGETS];
-
-  unsigned int             dev_timer_active; /* Which devs have a timer set */
-  struct timer_list        dev_timer;
-  unsigned long            dev_expires[MAX_TARGETS];
-
-  spinlock_t               spin_lock;
-  volatile unsigned char   cpu_lock_count[NR_CPUS];
-
-  Scsi_Cmnd               *dev_dtr_cmnd[MAX_TARGETS];
-
-  unsigned int             dev_checksum[MAX_TARGETS];
-  
-  unsigned char            dev_last_queue_full[MAX_TARGETS];
-  unsigned char            dev_last_queue_full_count[MAX_TARGETS];
-  unsigned char            dev_max_queue_depth[MAX_TARGETS];
-
-  volatile scb_queue_type  delayed_scbs[MAX_TARGETS];
-
-
-  unsigned char            msg_buf[13];      /* The message for the target */
-  unsigned char            msg_type;
-#define MSG_TYPE_NONE              0x00
-#define MSG_TYPE_INITIATOR_MSGOUT  0x01
-#define MSG_TYPE_INITIATOR_MSGIN   0x02
-  unsigned char            msg_len;          /* Length of message */
-  unsigned char            msg_index;        /* Index into msg_buf array */
-  transinfo_type           transinfo[MAX_TARGETS];
-
-
-  /*
-   * We put the less frequently used host structure items after the more
-   * frequently used items to try and ease the burden on the cache subsystem.
-   * These entries are not *commonly* accessed, whereas the preceding entries
-   * are accessed very often.
-   */
-
-  unsigned int             irq;              /* IRQ for this adapter */
-  int                      instance;         /* aic7xxx instance number */
-  int                      scsi_id;          /* host adapter SCSI ID */
-  int                      scsi_id_b;        /* channel B for twin adapters */
-  unsigned int             bios_address;
-  int                      board_name_index;
-  unsigned short           needppr_copy;     /* default config */
-  unsigned short           needsdtr_copy;    /* default config */
-  unsigned short           needwdtr_copy;    /* default config */
-  unsigned short           ultraenb;         /* Ultra mode target list */
-  unsigned short           bios_control;     /* bios control - SEEPROM */
-  unsigned short           adapter_control;  /* adapter control - SEEPROM */
-  struct pci_dev	  *pdev;
-  unsigned char            pci_bus;
-  unsigned char            pci_device_fn;
-  struct seeprom_config    sc;
-  unsigned short           sc_type;
-  unsigned short           sc_size;
-  struct aic7xxx_host     *next;             /* allow for multiple IRQs */
-  struct Scsi_Host        *host;             /* pointer to scsi host */
-  int                      host_no;          /* SCSI host number */
-  unsigned long            mbase;            /* I/O memory address */
-  ahc_chip                 chip;             /* chip type */
-  dma_addr_t		   fifo_dma;	     /* DMA handle for fifo arrays */
-
-  /*
-   * Statistics Kept:
-   *
-   * Total Xfers (count for each command that has a data xfer),
-   * broken down further by reads && writes.
-   *
-   * Binned sizes, writes && reads:
-   *    < 512, 512, 1-2K, 2-4K, 4-8K, 8-16K, 16-32K, 32-64K, 64K-128K, > 128K
-   *
-   * Total amounts read/written above 512 bytes (amts under ignored)
-   *
-   * NOTE: Enabling this feature is likely to cause a noticeable performance
-   * decrease as the accesses into the stats structures blows apart multiple
-   * cache lines and is CPU time consuming.
-   *
-   * NOTE: Since it doesn't really buy us much, but consumes *tons* of RAM
-   * and blows apart all sorts of cache lines, I modified this so that we
-   * no longer look at the LUN.  All LUNs now go into the same bin on each
-   * device for stats purposes.
-   */
-  struct aic7xxx_xferstats {
-    long w_total;                          /* total writes */
-    long r_total;                          /* total reads */
-#ifdef AIC7XXX_PROC_STATS
-    long w_bins[8];                       /* binned write */
-    long r_bins[8];                       /* binned reads */
-#endif /* AIC7XXX_PROC_STATS */
-  } stats[MAX_TARGETS];                    /* [(channel << 3)|target] */
-
-#if 0
-  struct target_cmd       *targetcmds;
-  unsigned int             num_targetcmds;
-#endif
-
-};
-
-/*
- * Valid SCSIRATE values. (p. 3-17)
- * Provides a mapping of transfer periods in ns/4 to the proper value to
- * stick in the SCSIRATE reg to use that transfer rate.
- */
-#define AHC_SYNCRATE_ULTRA3 0
-#define AHC_SYNCRATE_ULTRA2 1
-#define AHC_SYNCRATE_ULTRA  3
-#define AHC_SYNCRATE_FAST   6
-#define AHC_SYNCRATE_CRC 0x40
-#define AHC_SYNCRATE_SE  0x10
-static struct aic7xxx_syncrate {
-  /* Rates in Ultra mode have bit 8 of sxfr set */
-#define                ULTRA_SXFR 0x100
-  int sxfr_ultra2;
-  int sxfr;
-  unsigned char period;
-  const char *rate[2];
-} aic7xxx_syncrates[] = {
-  { 0x42,  0x000,   9,  {"80.0", "160.0"} },
-  { 0x13,  0x000,  10,  {"40.0", "80.0"} },
-  { 0x14,  0x000,  11,  {"33.0", "66.6"} },
-  { 0x15,  0x100,  12,  {"20.0", "40.0"} },
-  { 0x16,  0x110,  15,  {"16.0", "32.0"} },
-  { 0x17,  0x120,  18,  {"13.4", "26.8"} },
-  { 0x18,  0x000,  25,  {"10.0", "20.0"} },
-  { 0x19,  0x010,  31,  {"8.0",  "16.0"} },
-  { 0x1a,  0x020,  37,  {"6.67", "13.3"} },
-  { 0x1b,  0x030,  43,  {"5.7",  "11.4"} },
-  { 0x10,  0x040,  50,  {"5.0",  "10.0"} },
-  { 0x00,  0x050,  56,  {"4.4",  "8.8" } },
-  { 0x00,  0x060,  62,  {"4.0",  "8.0" } },
-  { 0x00,  0x070,  68,  {"3.6",  "7.2" } },
-  { 0x00,  0x000,  0,   {NULL, NULL}   },
-};
-
-#define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1),  \
-                        (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
-                        ((scb->hscb)->target_channel_lun & 0x07)
-
-#define CTL_OF_CMD(cmd) ((cmd->channel) & 0x01),  \
-                        ((cmd->target) & 0x0f), \
-                        ((cmd->lun) & 0x07)
-
-#define TARGET_INDEX(cmd)  ((cmd)->target | ((cmd)->channel << 3))
-
-/*
- * A nice little define to make doing our printks a little easier
- */
-
-#define WARN_LEAD KERN_WARNING "(scsi%d:%d:%d:%d) "
-#define INFO_LEAD KERN_INFO "(scsi%d:%d:%d:%d) "
-
-/*
- * XXX - these options apply unilaterally to _all_ 274x/284x/294x
- *       cards in the system.  This should be fixed.  Exceptions to this
- *       rule are noted in the comments.
- */
-
-
-/*
- * Skip the scsi bus reset.  Non 0 make us skip the reset at startup.  This
- * has no effect on any later resets that might occur due to things like
- * SCSI bus timeouts.
- */
-static unsigned int aic7xxx_no_reset = 0;
-/*
- * Certain PCI motherboards will scan PCI devices from highest to lowest,
- * others scan from lowest to highest, and they tend to do all kinds of
- * strange things when they come into contact with PCI bridge chips.  The
- * net result of all this is that the PCI card that is actually used to boot
- * the machine is very hard to detect.  Most motherboards go from lowest
- * PCI slot number to highest, and the first SCSI controller found is the
- * one you boot from.  The only exceptions to this are when a controller
- * has its BIOS disabled.  So, we by default sort all of our SCSI controllers
- * from lowest PCI slot number to highest PCI slot number.  We also force
- * all controllers with their BIOS disabled to the end of the list.  This
- * works on *almost* all computers.  Where it doesn't work, we have this
- * option.  Setting this option to non-0 will reverse the order of the sort
- * to highest first, then lowest, but will still leave cards with their BIOS
- * disabled at the very end.  That should fix everyone up unless there are
- * really strange cirumstances.
- */
-static int aic7xxx_reverse_scan = 0;
-/*
- * Should we force EXTENDED translation on a controller.
- *     0 == Use whatever is in the SEEPROM or default to off
- *     1 == Use whatever is in the SEEPROM or default to on
- */
-static unsigned int aic7xxx_extended = 0;
-/*
- * The IRQ trigger method used on EISA controllers. Does not effect PCI cards.
- *   -1 = Use detected settings.
- *    0 = Force Edge triggered mode.
- *    1 = Force Level triggered mode.
- */
-static int aic7xxx_irq_trigger = -1;
-/*
- * This variable is used to override the termination settings on a controller.
- * This should not be used under normal conditions.  However, in the case
- * that a controller does not have a readable SEEPROM (so that we can't
- * read the SEEPROM settings directly) and that a controller has a buggered
- * version of the cable detection logic, this can be used to force the 
- * correct termination.  It is preferable to use the manual termination
- * settings in the BIOS if possible, but some motherboard controllers store
- * those settings in a format we can't read.  In other cases, auto term
- * should also work, but the chipset was put together with no auto term
- * logic (common on motherboard controllers).  In those cases, we have
- * 32 bits here to work with.  That's good for 8 controllers/channels.  The
- * bits are organized as 4 bits per channel, with scsi0 getting the lowest
- * 4 bits in the int.  A 1 in a bit position indicates the termination setting
- * that corresponds to that bit should be enabled, a 0 is disabled.
- * It looks something like this:
- *
- *    0x0f =  1111-Single Ended Low Byte Termination on/off
- *            ||\-Single Ended High Byte Termination on/off
- *            |\-LVD Low Byte Termination on/off
- *            \-LVD High Byte Termination on/off
- *
- * For non-Ultra2 controllers, the upper 2 bits are not important.  So, to
- * enable both high byte and low byte termination on scsi0, I would need to
- * make sure that the override_term variable was set to 0x03 (bits 0011).
- * To make sure that all termination is enabled on an Ultra2 controller at
- * scsi2 and only high byte termination on scsi1 and high and low byte
- * termination on scsi0, I would set override_term=0xf23 (bits 1111 0010 0011)
- *
- * For the most part, users should never have to use this, that's why I
- * left it fairly cryptic instead of easy to understand.  If you need it,
- * most likely someone will be telling you what your's needs to be set to.
- */
-static int aic7xxx_override_term = -1;
-/*
- * Certain motherboard chipset controllers tend to screw
- * up the polarity of the term enable output pin.  Use this variable
- * to force the correct polarity for your system.  This is a bitfield variable
- * similar to the previous one, but this one has one bit per channel instead
- * of four.
- *    0 = Force the setting to active low.
- *    1 = Force setting to active high.
- * Most Adaptec cards are active high, several motherboards are active low.
- * To force a 2940 card at SCSI 0 to active high and a motherboard 7895
- * controller at scsi1 and scsi2 to active low, and a 2910 card at scsi3
- * to active high, you would need to set stpwlev=0x9 (bits 1001).
- *
- * People shouldn't need to use this, but if you are experiencing lots of
- * SCSI timeout problems, this may help.  There is one sure way to test what
- * this option needs to be.  Using a boot floppy to boot the system, configure
- * your system to enable all SCSI termination (in the Adaptec SCSI BIOS) and
- * if needed then also pass a value to override_term to make sure that the
- * driver is enabling SCSI termination, then set this variable to either 0
- * or 1.  When the driver boots, make sure there are *NO* SCSI cables
- * connected to your controller.  If it finds and inits the controller
- * without problem, then the setting you passed to stpwlev was correct.  If
- * the driver goes into a reset loop and hangs the system, then you need the
- * other setting for this variable.  If neither setting lets the machine
- * boot then you have definite termination problems that may not be fixable.
- */
-static int aic7xxx_stpwlev = -1;
-/*
- * Set this to non-0 in order to force the driver to panic the kernel
- * and print out debugging info on a SCSI abort or reset cycle.
- */
-static int aic7xxx_panic_on_abort = 0;
-/*
- * PCI bus parity checking of the Adaptec controllers.  This is somewhat
- * dubious at best.  To my knowledge, this option has never actually
- * solved a PCI parity problem, but on certain machines with broken PCI
- * chipset configurations, it can generate tons of false error messages.
- * It's included in the driver for completeness.
- *   0 = Shut off PCI parity check
- *  -1 = Normal polarity pci parity checking
- *   1 = reverse polarity pci parity checking
- *
- * NOTE: you can't actually pass -1 on the lilo prompt.  So, to set this
- * variable to -1 you would actually want to simply pass the variable
- * name without a number.  That will invert the 0 which will result in
- * -1.
- */
-static int aic7xxx_pci_parity = 0;
-/*
- * Set this to any non-0 value to cause us to dump the contents of all
- * the card's registers in a hex dump format tailored to each model of
- * controller.
- * 
- * NOTE: THE CONTROLLER IS LEFT IN AN UNUSEABLE STATE BY THIS OPTION.
- *       YOU CANNOT BOOT UP WITH THIS OPTION, IT IS FOR DEBUGGING PURPOSES
- *       ONLY
- */
-static int aic7xxx_dump_card = 0;
-/*
- * Set this to a non-0 value to make us dump out the 32 bit instruction
- * registers on the card after completing the sequencer download.  This
- * allows the actual sequencer download to be verified.  It is possible
- * to use this option and still boot up and run your system.  This is
- * only intended for debugging purposes.
- */
-static int aic7xxx_dump_sequencer = 0;
-/*
- * Certain newer motherboards have put new PCI based devices into the
- * IO spaces that used to typically be occupied by VLB or EISA cards.
- * This overlap can cause these newer motherboards to lock up when scanned
- * for older EISA and VLB devices.  Setting this option to non-0 will
- * cause the driver to skip scanning for any VLB or EISA controllers and
- * only support the PCI controllers.  NOTE: this means that if the kernel
- * os compiled with PCI support disabled, then setting this to non-0
- * would result in never finding any devices :)
- */
-static int aic7xxx_no_probe = 0;
-/*
- * On some machines, enabling the external SCB RAM isn't reliable yet.  I
- * haven't had time to make test patches for things like changing the
- * timing mode on that external RAM either.  Some of those changes may
- * fix the problem.  Until then though, we default to external SCB RAM
- * off and give a command line option to enable it.
- */
-static int aic7xxx_scbram = 0;
-/*
- * So that we can set how long each device is given as a selection timeout.
- * The table of values goes like this:
- *   0 - 256ms
- *   1 - 128ms
- *   2 - 64ms
- *   3 - 32ms
- * We default to 64ms because it's fast.  Some old SCSI-I devices need a
- * longer time.  The final value has to be left shifted by 3, hence 0x10
- * is the final value.
- */
-static int aic7xxx_seltime = 0x10;
-/*
- * So that insmod can find the variable and make it point to something
- */
-#ifdef MODULE
-static char * aic7xxx = NULL;
-MODULE_PARM(aic7xxx, "s");
-
-/*
- * Just in case someone uses commas to separate items on the insmod
- * command line, we define a dummy buffer here to avoid having insmod
- * write wild stuff into our code segment
- */
-static char dummy_buffer[60] = "Please don't trounce on me insmod!!\n";
-
-#endif
-
-#define VERBOSE_NORMAL         0x0000
-#define VERBOSE_NEGOTIATION    0x0001
-#define VERBOSE_SEQINT         0x0002
-#define VERBOSE_SCSIINT        0x0004
-#define VERBOSE_PROBE          0x0008
-#define VERBOSE_PROBE2         0x0010
-#define VERBOSE_NEGOTIATION2   0x0020
-#define VERBOSE_MINOR_ERROR    0x0040
-#define VERBOSE_TRACING        0x0080
-#define VERBOSE_ABORT          0x0f00
-#define VERBOSE_ABORT_MID      0x0100
-#define VERBOSE_ABORT_FIND     0x0200
-#define VERBOSE_ABORT_PROCESS  0x0400
-#define VERBOSE_ABORT_RETURN   0x0800
-#define VERBOSE_RESET          0xf000
-#define VERBOSE_RESET_MID      0x1000
-#define VERBOSE_RESET_FIND     0x2000
-#define VERBOSE_RESET_PROCESS  0x4000
-#define VERBOSE_RESET_RETURN   0x8000
-static int aic7xxx_verbose = VERBOSE_NORMAL | VERBOSE_NEGOTIATION |
-           VERBOSE_PROBE;                     /* verbose messages */
-
-
-/****************************************************************************
- *
- * We're going to start putting in function declarations so that order of
- * functions is no longer important.  As needed, they are added here.
- *
- ***************************************************************************/
-
-static void aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd);
-static void aic7xxx_print_card(struct aic7xxx_host *p);
-static void aic7xxx_print_scratch_ram(struct aic7xxx_host *p);
-static void aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded);
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-static void aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer);
-#endif
-
-/****************************************************************************
- *
- * These functions are now used.  They happen to be wrapped in useless
- * inb/outb port read/writes around the real reads and writes because it
- * seems that certain very fast CPUs have a problem dealing with us when
- * going at full speed.
- *
- ***************************************************************************/
-
-static inline unsigned char
-aic_inb(struct aic7xxx_host *p, long port)
-{
-#ifdef MMAPIO
-  unsigned char x;
-  if(p->maddr)
-  {
-    x = readb(p->maddr + port);
-  }
-  else
-  {
-    x = inb(p->base + port);
-  }
-  return(x);
-#else
-  return(inb(p->base + port));
-#endif
-}
-
-static inline void
-aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
-{
-#ifdef MMAPIO
-  if(p->maddr)
-  {
-    writeb(val, p->maddr + port);
-    mb(); /* locked operation in order to force CPU ordering */
-    readb(p->maddr + HCNTRL); /* dummy read to flush the PCI write */
-  }
-  else
-  {
-    outb(val, p->base + port);
-    mb(); /* locked operation in order to force CPU ordering */
-  }
-#else
-  outb(val, p->base + port);
-  mb(); /* locked operation in order to force CPU ordering */
-#endif
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_setup
- *
- * Description:
- *   Handle Linux boot parameters. This routine allows for assigning a value
- *   to a parameter with a ':' between the parameter and the value.
- *   ie. aic7xxx=unpause:0x0A,extended
- *-F*************************************************************************/
-static int
-aic7xxx_setup(char *s)
-{
-  int   i, n;
-  char *p;
-  char *end;
-
-  static struct {
-    const char *name;
-    unsigned int *flag;
-  } options[] = {
-    { "extended",    &aic7xxx_extended },
-    { "no_reset",    &aic7xxx_no_reset },
-    { "irq_trigger", &aic7xxx_irq_trigger },
-    { "verbose",     &aic7xxx_verbose },
-    { "reverse_scan",&aic7xxx_reverse_scan },
-    { "override_term", &aic7xxx_override_term },
-    { "stpwlev", &aic7xxx_stpwlev },
-    { "no_probe", &aic7xxx_no_probe },
-    { "panic_on_abort", &aic7xxx_panic_on_abort },
-    { "pci_parity", &aic7xxx_pci_parity },
-    { "dump_card", &aic7xxx_dump_card },
-    { "dump_sequencer", &aic7xxx_dump_sequencer },
-    { "scbram", &aic7xxx_scbram },
-    { "seltime", &aic7xxx_seltime },
-    { "tag_info",    NULL }
-  };
-
-  end = strchr(s, '\0');
-
-  for (p = strtok(s, ",."); p; p = strtok(NULL, ",."))
-  {
-    for (i = 0; i < NUMBER(options); i++)
-    {
-      n = strlen(options[i].name);
-      if (!strncmp(options[i].name, p, n))
-      {
-        if (!strncmp(p, "tag_info", n))
-        {
-          if (p[n] == ':')
-          {
-            char *base;
-            char *tok, *tok_end, *tok_end2;
-            char tok_list[] = { '.', ',', '{', '}', '\0' };
-            int i, instance = -1, device = -1;
-            unsigned char done = FALSE;
-
-            base = p;
-            tok = base + n + 1;  /* Forward us just past the ':' */
-            tok_end = strchr(tok, '\0');
-            if (tok_end < end)
-              *tok_end = ',';
-            while(!done)
-            {
-              switch(*tok)
-              {
-                case '{':
-                  if (instance == -1)
-                    instance = 0;
-                  else if (device == -1)
-                    device = 0;
-                  tok++;
-                  break;
-                case '}':
-                  if (device != -1)
-                    device = -1;
-                  else if (instance != -1)
-                    instance = -1;
-                  tok++;
-                  break;
-                case ',':
-                case '.':
-                  if (instance == -1)
-                    done = TRUE;
-                  else if (device >= 0)
-                    device++;
-                  else if (instance >= 0)
-                    instance++;
-                  if ( (device >= MAX_TARGETS) || 
-                       (instance >= NUMBER(aic7xxx_tag_info)) )
-                    done = TRUE;
-                  tok++;
-                  if (!done)
-                  {
-                    base = tok;
-                  }
-                  break;
-                case '\0':
-                  done = TRUE;
-                  break;
-                default:
-                  done = TRUE;
-                  tok_end = strchr(tok, '\0');
-                  for(i=0; tok_list[i]; i++)
-                  {
-                    tok_end2 = strchr(tok, tok_list[i]);
-                    if ( (tok_end2) && (tok_end2 < tok_end) )
-                    {
-                      tok_end = tok_end2;
-                      done = FALSE;
-                    }
-                  }
-                  if ( (instance >= 0) && (device >= 0) &&
-                       (instance < NUMBER(aic7xxx_tag_info)) &&
-                       (device < MAX_TARGETS) )
-                    aic7xxx_tag_info[instance].tag_commands[device] =
-                      simple_strtoul(tok, NULL, 0) & 0xff;
-                  tok = tok_end;
-                  break;
-              }
-            }
-            while((p != base) && (p != NULL))
-              p = strtok(NULL, ",.");
-          }
-        }
-        else if (p[n] == ':')
-        {
-          *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
-          if(!strncmp(p, "seltime", n))
-          {
-            *(options[i].flag) = (*(options[i].flag) % 4) << 3;
-          }
-        }
-        else if (!strncmp(p, "verbose", n))
-        {
-          *(options[i].flag) = 0xff29;
-        }
-        else
-        {
-          *(options[i].flag) = ~(*(options[i].flag));
-          if(!strncmp(p, "seltime", n))
-          {
-            *(options[i].flag) = (*(options[i].flag) % 4) << 3;
-          }
-        }
-      }
-    }
-  }
-  return 1;
-}
-
-__setup("aic7xxx=", aic7xxx_setup);
-
-/*+F*************************************************************************
- * Function:
- *   pause_sequencer
- *
- * Description:
- *   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
-pause_sequencer(struct aic7xxx_host *p)
-{
-  aic_outb(p, p->pause, HCNTRL);
-  while ((aic_inb(p, HCNTRL) & PAUSE) == 0)
-  {
-    ;
-  }
-  if(p->features & AHC_ULTRA2)
-  {
-    aic_inb(p, CCSCBCTL);
-  }
-}
-
-/*+F*************************************************************************
- * Function:
- *   unpause_sequencer
- *
- * Description:
- *   Unpause the sequencer. Unremarkable, yet done often enough to
- *   warrant an easy way to do it.
- *-F*************************************************************************/
-static void
-unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
-{
-  if (unpause_always ||
-      ( !(aic_inb(p, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) &&
-        !(p->flags & AHC_HANDLING_REQINITS) ) )
-  {
-    aic_outb(p, p->unpause, HCNTRL);
-  }
-}
-
-/*+F*************************************************************************
- * Function:
- *   restart_sequencer
- *
- * Description:
- *   Restart the sequencer program from address zero.  This assumes
- *   that the sequencer is already paused.
- *-F*************************************************************************/
-static void
-restart_sequencer(struct aic7xxx_host *p)
-{
-  aic_outb(p, 0, SEQADDR0);
-  aic_outb(p, 0, SEQADDR1);
-  aic_outb(p, FASTMODE, SEQCTL);
-}
-
-/*
- * We include the aic7xxx_seq.c file here so that the other defines have
- * already been made, and so that it comes before the code that actually
- * downloads the instructions (since we don't typically use function
- * prototype, our code has to be ordered that way, it's a left-over from
- * the original driver days.....I should fix it some time DL).
- */
-#include "aic7xxx_seq.c"
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_check_patch
- *
- * Description:
- *   See if the next patch to download should be downloaded.
- *-F*************************************************************************/
-static int
-aic7xxx_check_patch(struct aic7xxx_host *p,
-  struct sequencer_patch **start_patch, int start_instr, int *skip_addr)
-{
-  struct sequencer_patch *cur_patch;
-  struct sequencer_patch *last_patch;
-  int num_patches;
-
-  num_patches = sizeof(sequencer_patches)/sizeof(struct sequencer_patch);
-  last_patch = &sequencer_patches[num_patches];
-  cur_patch = *start_patch;
-
-  while ((cur_patch < last_patch) && (start_instr == cur_patch->begin))
-  {
-    if (cur_patch->patch_func(p) == 0)
-    {
-      /*
-       * Start rejecting code.
-       */
-      *skip_addr = start_instr + cur_patch->skip_instr;
-      cur_patch += cur_patch->skip_patch;
-    }
-    else
-    {
-      /*
-       * Found an OK patch.  Advance the patch pointer to the next patch
-       * and wait for our instruction pointer to get here.
-       */
-      cur_patch++;
-    }
-  }
-
-  *start_patch = cur_patch;
-  if (start_instr < *skip_addr)
-    /*
-     * Still skipping
-     */
-    return (0);
-  return(1);
-}
-
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_download_instr
- *
- * Description:
- *   Find the next patch to download.
- *-F*************************************************************************/
-static void
-aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr,
-  unsigned char *dconsts)
-{
-  union ins_formats instr;
-  struct ins_format1 *fmt1_ins;
-  struct ins_format3 *fmt3_ins;
-  unsigned char opcode;
-
-  instr = *(union ins_formats*) &seqprog[instrptr * 4];
-
-  instr.integer = le32_to_cpu(instr.integer);
-  
-  fmt1_ins = &instr.format1;
-  fmt3_ins = NULL;
-
-  /* Pull the opcode */
-  opcode = instr.format1.opcode;
-  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:
-    {
-      struct sequencer_patch *cur_patch;
-      int address_offset;
-      unsigned int address;
-      int skip_addr;
-      int i;
-
-      fmt3_ins = &instr.format3;
-      address_offset = 0;
-      address = fmt3_ins->address;
-      cur_patch = sequencer_patches;
-      skip_addr = 0;
-
-      for (i = 0; i < address;)
-      {
-        aic7xxx_check_patch(p, &cur_patch, i, &skip_addr);
-        if (skip_addr > i)
-        {
-          int end_addr;
-
-          end_addr = MIN(address, skip_addr);
-          address_offset += end_addr - i;
-          i = skip_addr;
-        }
-        else
-        {
-          i++;
-        }
-      }
-      address -= address_offset;
-      fmt3_ins->address = address;
-      /* Fall Through to the next code section */
-    }
-    case AIC_OP_OR:
-    case AIC_OP_AND:
-    case AIC_OP_XOR:
-    case AIC_OP_ADD:
-    case AIC_OP_ADC:
-    case AIC_OP_BMOV:
-      if (fmt1_ins->parity != 0)
-      {
-        fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
-      }
-      fmt1_ins->parity = 0;
-      /* Fall Through to the next code section */
-    case AIC_OP_ROL:
-      if ((p->features & AHC_ULTRA2) != 0)
-      {
-        int i, count;
-
-        /* Calculate odd parity for the instruction */
-        for ( i=0, count=0; i < 31; i++)
-        {
-          unsigned int mask;
-
-          mask = 0x01 << i;
-          if ((instr.integer & mask) != 0)
-            count++;
-        }
-        if (!(count & 0x01))
-          instr.format1.parity = 1;
-      }
-      else
-      {
-        if (fmt3_ins != NULL)
-        {
-          instr.integer =  fmt3_ins->immediate |
-                          (fmt3_ins->source << 8) |
-                          (fmt3_ins->address << 16) |
-                          (fmt3_ins->opcode << 25);
-        }
-        else
-        {
-          instr.integer =  fmt1_ins->immediate |
-                          (fmt1_ins->source << 8) |
-                          (fmt1_ins->destination << 16) |
-                          (fmt1_ins->ret << 24) |
-                          (fmt1_ins->opcode << 25);
-        }
-      }
-      aic_outb(p, (instr.integer & 0xff), SEQRAM);
-      aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM);
-      aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM);
-      aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM);
-      udelay(10);
-      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)
-{
-  struct sequencer_patch *cur_patch;
-  int i;
-  int downloaded;
-  int skip_addr;
-  unsigned char download_consts[4] = {0, 0, 0, 0};
-
-  if (aic7xxx_verbose & VERBOSE_PROBE)
-  {
-    printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no);
-  }
-#if 0
-  download_consts[TMODE_NUMCMDS] = p->num_targetcmds;
-#endif
-  download_consts[TMODE_NUMCMDS] = 0;
-  cur_patch = &sequencer_patches[0];
-  downloaded = 0;
-  skip_addr = 0;
-
-  aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
-  aic_outb(p, 0, SEQADDR0);
-  aic_outb(p, 0, SEQADDR1);
-
-  for (i = 0; i < sizeof(seqprog) / 4;  i++)
-  {
-    if (aic7xxx_check_patch(p, &cur_patch, i, &skip_addr) == 0)
-    {
-      /* Skip this instruction for this configuration. */
-      continue;
-    }
-    aic7xxx_download_instr(p, i, &download_consts[0]);
-    downloaded++;
-  }
-
-  aic_outb(p, 0, SEQADDR0);
-  aic_outb(p, 0, SEQADDR1);
-  aic_outb(p, FASTMODE | FAILDIS, SEQCTL);
-  unpause_sequencer(p, TRUE);
-  mdelay(1);
-  pause_sequencer(p);
-  aic_outb(p, FASTMODE, SEQCTL);
-  if (aic7xxx_verbose & VERBOSE_PROBE)
-  {
-    printk(" %d instructions downloaded\n", downloaded);
-  }
-  if (aic7xxx_dump_sequencer)
-    aic7xxx_print_sequencer(p, downloaded);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_print_sequencer
- *
- * Description:
- *   Print the contents of the sequencer memory to the screen.
- *-F*************************************************************************/
-static void
-aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded)
-{
-  int i, k, temp;
-  
-  aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
-  aic_outb(p, 0, SEQADDR0);
-  aic_outb(p, 0, SEQADDR1);
-
-  k = 0;
-  for (i=0; i < downloaded; i++)
-  {
-    if ( k == 0 )
-      printk("%03x: ", i);
-    temp = aic_inb(p, SEQRAM);
-    temp |= (aic_inb(p, SEQRAM) << 8);
-    temp |= (aic_inb(p, SEQRAM) << 16);
-    temp |= (aic_inb(p, SEQRAM) << 24);
-    printk("%08x", temp);
-    if ( ++k == 8 )
-    {
-      printk("\n");
-      k = 0;
-    }
-    else
-      printk(" ");
-  }
-  aic_outb(p, 0, SEQADDR0);
-  aic_outb(p, 0, SEQADDR1);
-  aic_outb(p, FASTMODE | FAILDIS, SEQCTL);
-  unpause_sequencer(p, TRUE);
-  mdelay(1);
-  pause_sequencer(p);
-  aic_outb(p, FASTMODE, SEQCTL);
-  printk("\n");
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_info
- *
- * Description:
- *   Return a string describing the driver.
- *-F*************************************************************************/
-const char *
-aic7xxx_info(struct Scsi_Host *dooh)
-{
-  static char buffer[256];
-  char *bp;
-  struct aic7xxx_host *p;
-
-  bp = &buffer[0];
-  p = (struct aic7xxx_host *)dooh->hostdata;
-  memset(bp, 0, sizeof(buffer));
-  strcpy(bp, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
-  strcat(bp, AIC7XXX_C_VERSION);
-  strcat(bp, "/");
-  strcat(bp, AIC7XXX_H_VERSION);
-  strcat(bp, "\n");
-  strcat(bp, "       <");
-  strcat(bp, board_names[p->board_name_index]);
-  strcat(bp, ">");
-
-  return(bp);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_find_syncrate
- *
- * Description:
- *   Look up the valid period to SCSIRATE conversion in our table
- *-F*************************************************************************/
-static struct aic7xxx_syncrate *
-aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period,
-  unsigned int maxsync, unsigned char *options)
-{
-  struct aic7xxx_syncrate *syncrate;
-  int done = FALSE;
-
-  switch(*options)
-  {
-    case MSG_EXT_PPR_OPTION_DT_CRC:
-    case MSG_EXT_PPR_OPTION_DT_UNITS:
-      if(!(p->features & AHC_ULTRA3))
-      {
-        *options = 0;
-        maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
-      }
-      break;
-    case MSG_EXT_PPR_OPTION_DT_CRC_QUICK:
-    case MSG_EXT_PPR_OPTION_DT_UNITS_QUICK:
-      if(!(p->features & AHC_ULTRA3))
-      {
-        *options = 0;
-        maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
-      }
-      else
-      {
-        /*
-         * we don't support the Quick Arbitration variants of dual edge
-         * clocking.  As it turns out, we want to send back the
-         * same basic option, but without the QA attribute.
-         * We know that we are responding because we would never set
-         * these options ourself, we would only respond to them.
-         */
-        switch(*options)
-        {
-          case MSG_EXT_PPR_OPTION_DT_CRC_QUICK:
-            *options = MSG_EXT_PPR_OPTION_DT_CRC;
-            break;
-          case MSG_EXT_PPR_OPTION_DT_UNITS_QUICK:
-            *options = MSG_EXT_PPR_OPTION_DT_UNITS;
-            break;
-        }
-      }
-      break;
-    default:
-      *options = 0;
-      maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
-      break;
-  }
-  syncrate = &aic7xxx_syncrates[maxsync];
-  while ( (syncrate->rate[0] != NULL) &&
-         (!(p->features & AHC_ULTRA2) || syncrate->sxfr_ultra2) )
-  {
-    if (*period <= syncrate->period) 
-    {
-      switch(*options)
-      {
-        case MSG_EXT_PPR_OPTION_DT_CRC:
-        case MSG_EXT_PPR_OPTION_DT_UNITS:
-          if(!(syncrate->sxfr_ultra2 & AHC_SYNCRATE_CRC))
-          {
-            done = TRUE;
-            /*
-             * oops, we went too low for the CRC/DualEdge signalling, so
-             * clear the options byte
-             */
-            *options = 0;
-            /*
-             * We'll be sending a reply to this packet to set the options
-             * properly, so unilaterally set the period as well.
-             */
-            *period = syncrate->period;
-          }
-          else
-          {
-            done = TRUE;
-            if(syncrate == &aic7xxx_syncrates[maxsync])
-            {
-              *period = syncrate->period;
-            }
-          }
-          break;
-        default:
-          if(!(syncrate->sxfr_ultra2 & AHC_SYNCRATE_CRC))
-          {
-            done = TRUE;
-            if(syncrate == &aic7xxx_syncrates[maxsync])
-            {
-              *period = syncrate->period;
-            }
-          }
-          break;
-      }
-      if(done)
-      {
-        break;
-      }
-    }
-    syncrate++;
-  }
-  if ( (*period == 0) || (syncrate->rate[0] == NULL) ||
-       ((p->features & AHC_ULTRA2) && (syncrate->sxfr_ultra2 == 0)) )
-  {
-    /*
-     * Use async transfers for this target
-     */
-    *options = 0;
-    *period = 255;
-    syncrate = NULL;
-  }
-  return (syncrate);
-}
-
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_find_period
- *
- * Description:
- *   Look up the valid SCSIRATE to period conversion in our table
- *-F*************************************************************************/
-static unsigned int
-aic7xxx_find_period(struct aic7xxx_host *p, unsigned int scsirate,
-  unsigned int maxsync)
-{
-  struct aic7xxx_syncrate *syncrate;
-
-  if (p->features & AHC_ULTRA2)
-  {
-    scsirate &= SXFR_ULTRA2;
-  }
-  else
-  {
-    scsirate &= SXFR;
-  }
-
-  syncrate = &aic7xxx_syncrates[maxsync];
-  while (syncrate->rate[0] != NULL)
-  {
-    if (p->features & AHC_ULTRA2)
-    {
-      if (syncrate->sxfr_ultra2 == 0)
-        break;
-      else if (scsirate == syncrate->sxfr_ultra2)
-        return (syncrate->period);
-      else if (scsirate == (syncrate->sxfr_ultra2 & ~AHC_SYNCRATE_CRC))
-        return (syncrate->period);
-    }
-    else if (scsirate == (syncrate->sxfr & ~ULTRA_SXFR))
-    {
-      return (syncrate->period);
-    }
-    syncrate++;
-  }
-  return (0); /* async */
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_validate_offset
- *
- * Description:
- *   Set a valid offset value for a particular card in use and transfer
- *   settings in use.
- *-F*************************************************************************/
-static void
-aic7xxx_validate_offset(struct aic7xxx_host *p,
-  struct aic7xxx_syncrate *syncrate, unsigned int *offset, int wide)
-{
-  unsigned int maxoffset;
-
-  /* Limit offset to what the card (and device) can do */
-  if (syncrate == NULL)
-  {
-    maxoffset = 0;
-  }
-  else if (p->features & AHC_ULTRA2)
-  {
-    maxoffset = MAX_OFFSET_ULTRA2;
-  }
-  else
-  {
-    if (wide)
-      maxoffset = MAX_OFFSET_16BIT;
-    else
-      maxoffset = MAX_OFFSET_8BIT;
-  }
-  *offset = MIN(*offset, maxoffset);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_set_syncrate
- *
- * Description:
- *   Set the actual syncrate down in the card and in our host structs
- *-F*************************************************************************/
-static void
-aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
-    int target, int channel, unsigned int period, unsigned int offset,
-    unsigned char options, unsigned int type)
-{
-  unsigned char tindex;
-  unsigned short target_mask;
-  unsigned char lun, old_options;
-  unsigned int old_period, old_offset;
-
-  tindex = target | (channel << 3);
-  target_mask = 0x01 << tindex;
-  lun = aic_inb(p, SCB_TCL) & 0x07;
-
-  if (syncrate == NULL)
-  {
-    period = 0;
-    offset = 0;
-  }
-
-  old_period = p->transinfo[tindex].cur_period;
-  old_offset = p->transinfo[tindex].cur_offset;
-  old_options = p->transinfo[tindex].cur_options;
-
-  
-  if (type & AHC_TRANS_CUR)
-  {
-    unsigned int scsirate;
-
-    scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
-    if (p->features & AHC_ULTRA2)
-    {
-      scsirate &= ~SXFR_ULTRA2;
-      if (syncrate != NULL)
-      {
-        switch(options)
-        {
-          case MSG_EXT_PPR_OPTION_DT_UNITS:
-            /*
-             * mask off the CRC bit in the xfer settings
-             */
-            scsirate |= (syncrate->sxfr_ultra2 & ~AHC_SYNCRATE_CRC);
-            break;
-          default:
-            scsirate |= syncrate->sxfr_ultra2;
-            break;
-        }
-      }
-      if (type & AHC_TRANS_ACTIVE)
-      {
-        aic_outb(p, offset, SCSIOFFSET);
-      }
-      aic_outb(p, offset, TARG_OFFSET + tindex);
-    }
-    else /* Not an Ultra2 controller */
-    {
-      scsirate &= ~(SXFR|SOFS);
-      p->ultraenb &= ~target_mask;
-      if (syncrate != NULL)
-      {
-        if (syncrate->sxfr & ULTRA_SXFR)
-        {
-          p->ultraenb |= target_mask;
-        }
-        scsirate |= (syncrate->sxfr & SXFR);
-        scsirate |= (offset & SOFS);
-      }
-      if (type & AHC_TRANS_ACTIVE)
-      {
-        unsigned char sxfrctl0;
-
-        sxfrctl0 = aic_inb(p, SXFRCTL0);
-        sxfrctl0 &= ~FAST20;
-        if (p->ultraenb & target_mask)
-          sxfrctl0 |= FAST20;
-        aic_outb(p, sxfrctl0, SXFRCTL0);
-      }
-      aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB);
-      aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1 );
-    }
-    if (type & AHC_TRANS_ACTIVE)
-    {
-      aic_outb(p, scsirate, SCSIRATE);
-    }
-    aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
-    p->transinfo[tindex].cur_period = period;
-    p->transinfo[tindex].cur_offset = offset;
-    p->transinfo[tindex].cur_options = options;
-    if ( !(type & AHC_TRANS_QUITE) &&
-         (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
-         (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
-    {
-      if (offset)
-      {
-        int rate_mod = (scsirate & WIDEXFER) ? 1 : 0;
-      
-        printk(INFO_LEAD "Synchronous at %s Mbyte/sec, "
-               "offset %d.\n", p->host_no, channel, target, lun,
-               syncrate->rate[rate_mod], offset);
-      }
-      else
-      {
-        printk(INFO_LEAD "Using asynchronous transfers.\n",
-               p->host_no, channel, target, lun);
-      }
-      p->dev_flags[tindex] &= ~DEVICE_PRINT_DTR;
-    }
-  }
-
-  if (type & AHC_TRANS_GOAL)
-  {
-    p->transinfo[tindex].goal_period = period;
-    p->transinfo[tindex].goal_offset = offset;
-    p->transinfo[tindex].goal_options = options;
-  }
-
-  if (type & AHC_TRANS_USER)
-  {
-    p->transinfo[tindex].user_period = period;
-    p->transinfo[tindex].user_offset = offset;
-    p->transinfo[tindex].user_options = options;
-  }
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_set_width
- *
- * Description:
- *   Set the actual width down in the card and in our host structs
- *-F*************************************************************************/
-static void
-aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun,
-    unsigned int width, unsigned int type)
-{
-  unsigned char tindex;
-  unsigned short target_mask;
-  unsigned int old_width;
-
-  tindex = target | (channel << 3);
-  target_mask = 1 << tindex;
-  
-  old_width = p->transinfo[tindex].cur_width;
-
-  if (type & AHC_TRANS_CUR) 
-  {
-    unsigned char scsirate;
-
-    scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
-
-    scsirate &= ~WIDEXFER;
-    if (width == MSG_EXT_WDTR_BUS_16_BIT)
-      scsirate |= WIDEXFER;
-
-    aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
-
-    if (type & AHC_TRANS_ACTIVE)
-      aic_outb(p, scsirate, SCSIRATE);
-
-    p->transinfo[tindex].cur_width = width;
-
-    if ( !(type & AHC_TRANS_QUITE) &&
-          (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && 
-          (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
-    {
-      printk(INFO_LEAD "Using %s transfers\n", p->host_no, channel, target,
-        lun, (scsirate & WIDEXFER) ? "Wide(16bit)" : "Narrow(8bit)" );
-    }
-  }
-
-  if (type & AHC_TRANS_GOAL)
-    p->transinfo[tindex].goal_width = width;
-  if (type & AHC_TRANS_USER)
-    p->transinfo[tindex].user_width = width;
-
-  if (p->transinfo[tindex].goal_offset)
-  {
-    if (p->features & AHC_ULTRA2)
-    {
-      p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
-    }
-    else if (width == MSG_EXT_WDTR_BUS_16_BIT)
-    {
-      p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
-    }
-    else
-    {
-      p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
-    }
-  }
-}
-      
-/*+F*************************************************************************
- * Function:
- *   scbq_init
- *
- * Description:
- *   SCB queue initialization.
- *
- *-F*************************************************************************/
-static void
-scbq_init(volatile scb_queue_type *queue)
-{
-  queue->head = NULL;
-  queue->tail = NULL;
-}
-
-/*+F*************************************************************************
- * Function:
- *   scbq_insert_head
- *
- * Description:
- *   Add an SCB to the head of the list.
- *
- *-F*************************************************************************/
-static inline void
-scbq_insert_head(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-  unsigned long cpu_flags;
-#endif
-
-  DRIVER_LOCK
-  scb->q_next = queue->head;
-  queue->head = scb;
-  if (queue->tail == NULL)       /* If list was empty, update tail. */
-    queue->tail = queue->head;
-  DRIVER_UNLOCK
-}
-
-/*+F*************************************************************************
- * Function:
- *   scbq_remove_head
- *
- * Description:
- *   Remove an SCB from the head of the list.
- *
- *-F*************************************************************************/
-static inline struct aic7xxx_scb *
-scbq_remove_head(volatile scb_queue_type *queue)
-{
-  struct aic7xxx_scb * scbp;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-  unsigned long cpu_flags;
-#endif
-
-  DRIVER_LOCK
-  scbp = queue->head;
-  if (queue->head != NULL)
-    queue->head = queue->head->q_next;
-  if (queue->head == NULL)       /* If list is now empty, update tail. */
-    queue->tail = NULL;
-  DRIVER_UNLOCK
-  return(scbp);
-}
-
-/*+F*************************************************************************
- * Function:
- *   scbq_remove
- *
- * Description:
- *   Removes an SCB from the list.
- *
- *-F*************************************************************************/
-static inline void
-scbq_remove(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-  unsigned long cpu_flags;
-#endif
-
-  DRIVER_LOCK
-  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;
-      }
-    }
-  }
-  DRIVER_UNLOCK
-}
-
-/*+F*************************************************************************
- * Function:
- *   scbq_insert_tail
- *
- * Description:
- *   Add an SCB at the tail of the list.
- *
- *-F*************************************************************************/
-static inline void
-scbq_insert_tail(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-  unsigned long cpu_flags;
-#endif
-
-  DRIVER_LOCK
-  scb->q_next = NULL;
-  if (queue->tail != NULL)       /* Add the scb at the end of the list. */
-    queue->tail->q_next = scb;
-  queue->tail = scb;             /* Update the tail. */
-  if (queue->head == NULL)       /* If list was empty, update head. */
-    queue->head = queue->tail;
-  DRIVER_UNLOCK
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_match_scb
- *
- * Description:
- *   Checks to see if an scb matches the target/channel as specified.
- *   If target is ALL_TARGETS (-1), then we're looking for any device
- *   on the specified channel; this happens when a channel is going
- *   to be reset and all devices on that channel must be aborted.
- *-F*************************************************************************/
-static int
-aic7xxx_match_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
-    int target, int channel, int lun, unsigned char tag)
-{
-  int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
-  int chan = (scb->hscb->target_channel_lun >> 3) & 0x01;
-  int slun = scb->hscb->target_channel_lun & 0x07;
-  int match;
-
-  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
-   */
-  aic_outb(p, SCB_LIST_NULL, SCB_TAG);
-  aic_outb(p, 0, SCB_CONTROL);
-
-  aic_outb(p, aic_inb(p, FREE_SCBH), SCB_NEXT);
-  aic_outb(p, aic_inb(p, SCBPTR), 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 prev)
-{
-  unsigned char next;
-
-  aic_outb(p, scbptr, SCBPTR);
-  next = aic_inb(p, SCB_NEXT);
-  aic7xxx_add_curscb_to_free_list(p);
-
-  if (prev != SCB_LIST_NULL)
-  {
-    aic_outb(p, prev, SCBPTR);
-    aic_outb(p, next, SCB_NEXT);
-  }
-  else
-  {
-    aic_outb(p, next, DISCONNECTED_SCBH);
-  }
-
-  return next;
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_busy_target
- *
- * Description:
- *   Set the specified target busy.
- *-F*************************************************************************/
-static inline void
-aic7xxx_busy_target(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  p->untagged_scbs[scb->hscb->target_channel_lun] = scb->hscb->tag;
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_index_busy_target
- *
- * Description:
- *   Returns the index of the busy target, and optionally sets the
- *   target inactive.
- *-F*************************************************************************/
-static inline unsigned char
-aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char tcl,
-    int unbusy)
-{
-  unsigned char busy_scbid;
-
-  busy_scbid = p->untagged_scbs[tcl];
-  if (unbusy)
-  {
-    p->untagged_scbs[tcl] = SCB_LIST_NULL;
-  }
-  return (busy_scbid);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_find_scb
- *
- * Description:
- *   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 unsigned char
-aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  unsigned char saved_scbptr;
-  unsigned char curindex;
-
-  saved_scbptr = aic_inb(p, SCBPTR);
-  curindex = 0;
-  for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
-  {
-    aic_outb(p, curindex, SCBPTR);
-    if (aic_inb(p, SCB_TAG) == scb->hscb->tag)
-    {
-      break;
-    }
-  }
-  aic_outb(p, saved_scbptr, SCBPTR);
-  if (curindex >= p->scb_data->maxhscbs)
-  {
-    curindex = SCB_LIST_NULL;
-  }
-
-  return (curindex);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_allocate_scb
- *
- * Description:
- *   Get an SCB from the free list or by allocating a new one.
- *-F*************************************************************************/
-static int
-aic7xxx_allocate_scb(struct aic7xxx_host *p)
-{
-  struct aic7xxx_scb   *scbp = NULL;
-  int scb_size = (sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG) + 12 + 6;
-  int i;
-  int step = PAGE_SIZE / 1024;
-  unsigned long scb_count = 0;
-  struct hw_scatterlist *hsgp;
-  struct aic7xxx_scb *scb_ap;
-  struct aic7xxx_scb_dma *scb_dma;
-  unsigned char *bufs;
-
-  if (p->scb_data->numscbs < p->scb_data->maxscbs)
-  {
-    /*
-     * Calculate the optimal number of SCBs to allocate.
-     *
-     * NOTE: This formula works because the sizeof(sg_array) is always
-     * 1024.  Therefore, scb_size * i would always be > PAGE_SIZE *
-     * (i/step).  The (i-1) allows the left hand side of the equation
-     * to grow into the right hand side to a point of near perfect
-     * efficiency since scb_size * (i -1) is growing slightly faster
-     * than the right hand side.  If the number of SG array elements
-     * is changed, this function may not be near so efficient any more.
-     *
-     * Since the DMA'able buffers are now allocated in a seperate
-     * chunk this algorithm has been modified to match.  The '12'
-     * and '6' factors in scb_size are for the DMA'able command byte
-     * and sensebuffers respectively.  -DaveM
-     */
-    for ( i=step;; i *= 2 )
-    {
-      if ( (scb_size * (i-1)) >= ( (PAGE_SIZE * (i/step)) - 64 ) )
-      {
-        i /= 2;
-        break;
-      }
-    }
-    scb_count = MIN( (i-1), p->scb_data->maxscbs - p->scb_data->numscbs);
-    scb_ap = (struct aic7xxx_scb *)kmalloc(sizeof (struct aic7xxx_scb) * scb_count
-					   + sizeof(struct aic7xxx_scb_dma), GFP_ATOMIC);
-    if (scb_ap == NULL)
-      return(0);
-    scb_dma = (struct aic7xxx_scb_dma *)&scb_ap[scb_count];
-    hsgp = (struct hw_scatterlist *)
-      pci_alloc_consistent(p->pdev, scb_size * scb_count,
-			   &scb_dma->dma_address);
-    if (hsgp == NULL)
-    {
-      kfree(scb_ap);
-      return(0);
-    }
-    bufs = (unsigned char *)&hsgp[scb_count * AIC7XXX_MAX_SG];
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-    if (aic7xxx_verbose > 0xffff)
-    {
-      if (p->scb_data->numscbs == 0)
-	printk(INFO_LEAD "Allocating initial %ld SCB structures.\n",
-	  p->host_no, -1, -1, -1, scb_count);
-      else
-	printk(INFO_LEAD "Allocating %ld additional SCB structures.\n",
-	  p->host_no, -1, -1, -1, scb_count);
-    }
-#endif
-    memset(scb_ap, 0, sizeof (struct aic7xxx_scb) * scb_count);
-    scb_dma->dma_offset = (unsigned long)scb_dma->dma_address
-			  - (unsigned long)hsgp;
-    scb_dma->dma_len = scb_size * scb_count;
-    for (i=0; i < scb_count; i++)
-    {
-      scbp = &scb_ap[i];
-      scbp->hscb = &p->scb_data->hscbs[p->scb_data->numscbs];
-      scbp->sg_list = &hsgp[i * AIC7XXX_MAX_SG];
-      scbp->sense_cmd = bufs;
-      scbp->cmnd = bufs + 6;
-      bufs += 12 + 6;
-      scbp->scb_dma = scb_dma;
-      memset(scbp->hscb, 0, sizeof(struct aic7xxx_hwscb));
-      scbp->hscb->tag = p->scb_data->numscbs;
-      /*
-       * Place in the scb array; never is removed
-       */
-      p->scb_data->scb_array[p->scb_data->numscbs++] = scbp;
-      scbq_insert_tail(&p->scb_data->free_scbs, scbp);
-    }
-    scbp->kmalloc_ptr = scb_ap;
-  }
-  return(scb_count);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_queue_cmd_complete
- *
- * Description:
- *   Due to race conditions present in the SCSI subsystem, it is easier
- *   to queue completed commands, then call scsi_done() on them when
- *   we're finished.  This function queues the completed commands.
- *-F*************************************************************************/
-static void
-aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
-{
-  cmd->host_scribble = (char *)p->completeq.head;
-  p->completeq.head = cmd;
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_done_cmds_complete
- *
- * Description:
- *   Process the completed command queue.
- *-F*************************************************************************/
-static void
-aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
-{
-  Scsi_Cmnd *cmd;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-  unsigned int cpu_flags = 0;
-#endif
-  
-  DRIVER_LOCK
-  while (p->completeq.head != NULL)
-  {
-    cmd = p->completeq.head;
-    p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
-    cmd->host_scribble = NULL;
-    cmd->scsi_done(cmd);
-  }
-  DRIVER_UNLOCK
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_free_scb
- *
- * Description:
- *   Free the scb and insert into the free scb list.
- *-F*************************************************************************/
-static void
-aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-
-  scb->flags = SCB_FREE;
-  scb->cmd = NULL;
-  scb->sg_count = 0;
-  scb->sg_length = 0;
-  scb->tag_action = 0;
-  scb->hscb->control = 0;
-  scb->hscb->target_status = 0;
-  scb->hscb->target_channel_lun = SCB_LIST_NULL;
-
-  scbq_insert_head(&p->scb_data->free_scbs, scb);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_done
- *
- * Description:
- *   Calls the higher level scsi done function and frees the scb.
- *-F*************************************************************************/
-static void
-aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  Scsi_Cmnd *cmd = scb->cmd;
-  int tindex = TARGET_INDEX(cmd);
-  struct aic7xxx_scb *scbp;
-  unsigned char queue_depth;
-
-  if (cmd->use_sg > 1)
-  {
-    struct scatterlist *sg;
-
-    sg = (struct scatterlist *)cmd->request_buffer;
-    pci_unmap_sg(p->pdev, sg, cmd->use_sg, scsi_to_pci_dma_dir(cmd->sc_data_direction));
-  }
-  else if (cmd->request_bufflen)
-    pci_unmap_single(p->pdev, aic7xxx_mapping(cmd),
-		     cmd->request_bufflen,
-                     scsi_to_pci_dma_dir(cmd->sc_data_direction));
-  if (scb->flags & SCB_SENSE)
-  {
-    pci_unmap_single(p->pdev,
-                     le32_to_cpu(scb->sg_list[0].address),
-                     sizeof(cmd->sense_buffer),
-                     PCI_DMA_FROMDEVICE);
-  }
-  if (scb->flags & SCB_RECOVERY_SCB)
-  {
-    p->flags &= ~AHC_ABORT_PENDING;
-  }
-  if (scb->flags & SCB_RESET)
-  {
-      cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff);
-  }
-  else if (scb->flags & SCB_ABORT)
-  {
-      cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff);
-  }
-  else if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
-  {
-    if ( (cmd->cmnd[0] == INQUIRY) && (cmd->result == DID_OK) )
-    {
-      char *buffer;
-      
-      p->dev_flags[tindex] |= DEVICE_PRESENT;
-      if(cmd->use_sg)
-      {
-        struct scatterlist *sg;
-
-        sg = (struct scatterlist *)cmd->request_buffer;
-        buffer = (char *)sg[0].address;
-      }
-      else
-      {
-        buffer = (char *)cmd->request_buffer;
-      }
-#define WIDE_INQUIRY_BITS 0x60
-#define SYNC_INQUIRY_BITS 0x10
-#define SCSI_VERSION_BITS 0x07
-#define SCSI_DT_BIT       0x04
-      if ( (buffer[7] & WIDE_INQUIRY_BITS) &&
-           (p->features & AHC_WIDE) )
-      {
-        p->needwdtr |= (1<<tindex);
-        p->needwdtr_copy |= (1<<tindex);
-        p->transinfo[tindex].goal_width = p->transinfo[tindex].user_width;
-      }
-      else
-      {
-        p->needwdtr &= ~(1<<tindex);
-        p->needwdtr_copy &= ~(1<<tindex);
-        pause_sequencer(p);
-        aic7xxx_set_width(p, cmd->target, cmd->channel, cmd->lun,
-                          MSG_EXT_WDTR_BUS_8_BIT, (AHC_TRANS_ACTIVE |
-                                                   AHC_TRANS_GOAL |
-                                                   AHC_TRANS_CUR) );
-        unpause_sequencer(p, FALSE);
-      }
-      if ( (buffer[7] & SYNC_INQUIRY_BITS) &&
-            p->transinfo[tindex].user_offset )
-      {
-        p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period;
-        p->transinfo[tindex].goal_options = p->transinfo[tindex].user_options;
-        if (p->features & AHC_ULTRA2)
-          p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
-        else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT)
-          p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
-        else
-          p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
-        if ( (((buffer[2] & SCSI_VERSION_BITS) == 3) ||
-               (buffer[56] & SCSI_DT_BIT) ||
-	       (p->dev_flags[tindex] & DEVICE_SCSI_3) ) &&
-               (p->transinfo[tindex].user_period <= 9) &&
-               (p->transinfo[tindex].user_options) )
-        {
-          p->needppr |= (1<<tindex);
-          p->needppr_copy |= (1<<tindex);
-          p->needsdtr &= ~(1<<tindex);
-          p->needsdtr_copy &= ~(1<<tindex);
-          p->needwdtr &= ~(1<<tindex);
-          p->needwdtr_copy &= ~(1<<tindex);
-          p->dev_flags[tindex] |= DEVICE_SCSI_3;
-        }
-        else
-        {
-          p->needsdtr |= (1<<tindex);
-          p->needsdtr_copy |= (1<<tindex);
-          p->transinfo[tindex].goal_period = 
-            MAX(10, p->transinfo[tindex].goal_period);
-          p->transinfo[tindex].goal_options = 0;
-        }
-      }
-      else
-      {
-        p->needsdtr &= ~(1<<tindex);
-        p->needsdtr_copy &= ~(1<<tindex);
-        p->transinfo[tindex].goal_period = 255;
-        p->transinfo[tindex].goal_offset = 0;
-        p->transinfo[tindex].goal_options = 0;
-      }
-      /*
-       * This is needed to work around a sequencer bug for now.  Regardless
-       * of the controller in use, if we have a Quantum drive, we need to
-       * limit the speed to 80MByte/sec.  As soon as I get a fixed version
-       * of the sequencer, this code will get yanked.
-       */
-      if(!strncmp(buffer + 8, "QUANTUM", 7) &&
-         p->transinfo[tindex].goal_options )
-      {
-        p->transinfo[tindex].goal_period = 
-          MAX(p->transinfo[tindex].goal_period, 10);
-        p->transinfo[tindex].goal_options = 0;
-        p->needppr &= ~(1<<tindex);
-        p->needppr_copy &= ~(1<<tindex);
-        p->needsdtr |= (1<<tindex);
-        p->needsdtr_copy |= (1<<tindex);
-        p->needwdtr |= (1<<tindex);
-        p->needwdtr_copy |= (1<<tindex);
-      }
-      /*
-       * Get the INQUIRY checksum.  We use this on Ultra 160/m
-       * and older devices both.  It allows us to drop speed on any bus type
-       * while at the same time giving us the needed domain validation for
-       * Ultra 160/m
-       *
-       * Note: We only get the checksum and set the SCANNED bit if this is
-       * one of our dtr commands.  If we don't do this, then we end up
-       * getting bad checksum results on the mid-level SCSI code's INQUIRY
-       * commands.
-       */
-      if(p->dev_dtr_cmnd[tindex] == cmd) {
-        unsigned int checksum = 0;
-        int *ibuffer;
-        int i=0;
-
-        ibuffer = (int *)buffer;
-        for( i = 0; i < (cmd->request_bufflen >> 2); i++)
-        {
-          checksum += ibuffer[i];
-        }
-        p->dev_checksum[tindex] = checksum;
-        p->dev_flags[tindex] |= DEVICE_SCANNED;
-        p->dev_flags[tindex] |= DEVICE_PRINT_DTR;
-      }
-#undef WIDE_INQUIRY_BITS
-#undef SYNC_INQUIRY_BITS
-#undef SCSI_VERSION_BITS
-#undef SCSI_DT_BIT
-    }
-  }
-  else if ((scb->flags & SCB_MSGOUT_BITS) != 0)
-  {
-    unsigned short mask;
-    int message_error = FALSE;
-
-    mask = 0x01 << tindex;
- 
-    /*
-     * Check to see if we get an invalid message or a message error
-     * after failing to negotiate a wide or sync transfer message.
-     */
-    if ((scb->flags & SCB_SENSE) && 
-          ((scb->cmd->sense_buffer[12] == 0x43) ||  /* INVALID_MESSAGE */
-          (scb->cmd->sense_buffer[12] == 0x49))) /* MESSAGE_ERROR  */
-    {
-      message_error = TRUE;
-    }
-
-    if (scb->flags & SCB_MSGOUT_WDTR)
-    {
-      p->dtr_pending &= ~mask;
-      if (message_error)
-      {
-        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
-             (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
-        {
-          printk(INFO_LEAD "Device failed to complete Wide Negotiation "
-            "processing and\n", p->host_no, CTL_OF_SCB(scb));
-          printk(INFO_LEAD "returned a sense error code for invalid message, "
-            "disabling future\n", p->host_no, CTL_OF_SCB(scb));
-          printk(INFO_LEAD "Wide negotiation to this device.\n", p->host_no,
-            CTL_OF_SCB(scb));
-        }
-        p->needwdtr &= ~mask;
-        p->needwdtr_copy &= ~mask;
-      }
-    }
-    if (scb->flags & SCB_MSGOUT_SDTR)
-    {
-      p->dtr_pending &= ~mask;
-      if (message_error)
-      {
-        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
-             (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
-        {
-          printk(INFO_LEAD "Device failed to complete Sync Negotiation "
-            "processing and\n", p->host_no, CTL_OF_SCB(scb));
-          printk(INFO_LEAD "returned a sense error code for invalid message, "
-            "disabling future\n", p->host_no, CTL_OF_SCB(scb));
-          printk(INFO_LEAD "Sync negotiation to this device.\n", p->host_no,
-            CTL_OF_SCB(scb));
-          p->dev_flags[tindex] &= ~DEVICE_PRINT_DTR;
-        }
-        p->needsdtr &= ~mask;
-        p->needsdtr_copy &= ~mask;
-      }
-    }
-    if (scb->flags & SCB_MSGOUT_PPR)
-    {
-      p->dtr_pending &= ~mask;
-      if(message_error)
-      {
-        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
-             (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
-        {
-          printk(INFO_LEAD "Device failed to complete Parallel Protocol "
-            "Request processing and\n", p->host_no, CTL_OF_SCB(scb));
-          printk(INFO_LEAD "returned a sense error code for invalid message, "
-            "disabling future\n", p->host_no, CTL_OF_SCB(scb));
-          printk(INFO_LEAD "Parallel Protocol Request negotiation to this "
-            "device.\n", p->host_no, CTL_OF_SCB(scb));
-        }
-        /*
-         * Disable PPR negotiation and revert back to WDTR and SDTR setup
-         */
-        p->needppr &= ~mask;
-        p->needppr_copy &= ~mask;
-        p->needsdtr |= mask;
-        p->needsdtr_copy |= mask;
-        p->needwdtr |= mask;
-        p->needwdtr_copy |= mask;
-      }
-    }
-  }
-  queue_depth = p->dev_temp_queue_depth[tindex];
-  if (queue_depth >= p->dev_active_cmds[tindex])
-  {
-    scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
-    if (scbp)
-    {
-      if (queue_depth == 1)
-      {
-        /*
-         * Give extra preference to untagged devices, such as CD-R devices
-         * This makes it more likely that a drive *won't* stuff up while
-         * waiting on data at a critical time, such as CD-R writing and
-         * audio CD ripping operations.  Should also benefit tape drives.
-         */
-        scbq_insert_head(&p->waiting_scbs, scbp);
-      }
-      else
-      {
-        scbq_insert_tail(&p->waiting_scbs, scbp);
-      }
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-      if (aic7xxx_verbose > 0xffff)
-        printk(INFO_LEAD "Moving SCB from delayed to waiting queue.\n",
-               p->host_no, CTL_OF_SCB(scbp));
-#endif
-      if (queue_depth > p->dev_active_cmds[tindex])
-      {
-        scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
-        if (scbp)
-          scbq_insert_tail(&p->waiting_scbs, scbp);
-      }
-    }
-  }
-  if ( !(scb->tag_action) && (p->tagenable & (1<<tindex)) )
-  {
-    p->dev_temp_queue_depth[tindex] = p->dev_max_queue_depth[tindex];
-  }
-  p->dev_active_cmds[tindex]--;
-  p->activescbs--;
-
-  {
-    int actual;
-
-    /*
-     * XXX: we should actually know how much actually transferred
-     * XXX: for each command, but apparently that's too difficult.
-     * 
-     * We set a lower limit of 512 bytes on the transfer length.  We
-     * ignore anything less than this because we don't have a real
-     * reason to count it.  Read/Writes to tapes are usually about 20K
-     * and disks are a minimum of 512 bytes unless you want to count
-     * non-read/write commands (such as TEST_UNIT_READY) which we don't
-     */
-    actual = scb->sg_length;
-    if ((actual >= 512) && (((cmd->result >> 16) & 0xf) == DID_OK))
-    {
-      struct aic7xxx_xferstats *sp;
-#ifdef AIC7XXX_PROC_STATS
-      long *ptr;
-      int x;
-#endif /* AIC7XXX_PROC_STATS */
-
-      sp = &p->stats[TARGET_INDEX(cmd)];
-
-      /*
-       * For block devices, cmd->request.cmd is always == either READ or
-       * WRITE.  For character devices, this isn't always set properly, so
-       * we check data_cmnd[0].  This catches the conditions for st.c, but
-       * I'm still not sure if request.cmd is valid for sg devices.
-       */
-      if ( (cmd->request.cmd == WRITE) || (cmd->data_cmnd[0] == WRITE_6) ||
-           (cmd->data_cmnd[0] == WRITE_FILEMARKS) )
-      {
-        sp->w_total++;
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-        if ( (sp->w_total > 16) && (aic7xxx_verbose > 0xffff) )
-          aic7xxx_verbose &= 0xffff;
-#endif
-#ifdef AIC7XXX_PROC_STATS
-        ptr = sp->w_bins;
-#endif /* AIC7XXX_PROC_STATS */
-      }
-      else
-      {
-        sp->r_total++;
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-        if ( (sp->r_total > 16) && (aic7xxx_verbose > 0xffff) )
-          aic7xxx_verbose &= 0xffff;
-#endif
-#ifdef AIC7XXX_PROC_STATS
-        ptr = sp->r_bins;
-#endif /* AIC7XXX_PROC_STATS */
-      }
-#ifdef AIC7XXX_PROC_STATS
-      x = -11;
-      while(actual)
-      {
-        actual >>= 1;
-        x++;
-      }
-      if (x < 0)
-      {
-        ptr[0]++;
-      }
-      else if (x > 7)
-      {
-        ptr[7]++;
-      }
-      else
-      {
-        ptr[x]++;
-      }
-#endif /* AIC7XXX_PROC_STATS */
-    }
-  }
-  aic7xxx_free_scb(p, scb);
-  aic7xxx_queue_cmd_complete(p, cmd);
-
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_run_done_queue
- *
- * Description:
- *   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_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
-{
-  struct aic7xxx_scb *scb;
-  int i, found = 0;
-
-  for (i = 0; i < p->scb_data->numscbs; i++)
-  {
-    scb = p->scb_data->scb_array[i];
-    if (scb->flags & SCB_QUEUED_FOR_DONE)
-    {
-      if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
-        printk(INFO_LEAD "Aborting scb %d\n",
-             p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
-      found++;
-      aic7xxx_done(p, scb);
-    }
-  }
-  if (aic7xxx_verbose & (VERBOSE_ABORT_RETURN | VERBOSE_RESET_RETURN))
-  {
-    printk(INFO_LEAD "%d commands found and queued for "
-        "completion.\n", p->host_no, -1, -1, -1, found);
-  }
-  if (complete)
-  {
-    aic7xxx_done_cmds_complete(p);
-  }
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_abort_waiting_scb
- *
- * Description:
- *   Manipulate the waiting for selection list and return the
- *   scb that follows the one that we remove.
- *-F*************************************************************************/
-static unsigned char
-aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
-    unsigned char scbpos, unsigned char prev)
-{
-  unsigned char curscb, next;
-
-  /*
-   * Select the SCB we want to abort and pull the next pointer out of it.
-   */
-  curscb = aic_inb(p, SCBPTR);
-  aic_outb(p, scbpos, SCBPTR);
-  next = aic_inb(p, SCB_NEXT);
-
-  aic7xxx_add_curscb_to_free_list(p);
-
-  /*
-   * Update the waiting list
-   */
-  if (prev == SCB_LIST_NULL)
-  {
-    /*
-     * First in the list
-     */
-    aic_outb(p, next, WAITING_SCBH);
-  }
-  else
-  {
-    /*
-     * Select the scb that pointed to us and update its next pointer.
-     */
-    aic_outb(p, prev, SCBPTR);
-    aic_outb(p, next, SCB_NEXT);
-  }
-  /*
-   * Point us back at the original scb position and inform the SCSI
-   * system that the command has been aborted.
-   */
-  aic_outb(p, curscb, SCBPTR);
-  return (next);
-}
-
-/*+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, int channel,
-    int lun, unsigned char tag, int flags, int requeue,
-    volatile scb_queue_type *queue)
-{
-  int      found;
-  unsigned char qinpos, qintail;
-  struct aic7xxx_scb *scbp;
-
-  found = 0;
-  qinpos = aic_inb(p, QINPOS);
-  qintail = p->qinfifonext;
-
-  p->qinfifonext = qinpos;
-
-  while (qinpos != qintail)
-  {
-    scbp = p->scb_data->scb_array[p->qinfifo[qinpos++]];
-    if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
-    {
-       /*
-        * We found an scb that needs to be removed.
-        */
-       if (requeue && (queue != NULL))
-       {
-         if (scbp->flags & SCB_WAITINGQ)
-         {
-           scbq_remove(queue, scbp);
-           scbq_remove(&p->waiting_scbs, scbp);
-           scbq_remove(&p->delayed_scbs[TARGET_INDEX(scbp->cmd)], scbp);
-           p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
-           p->activescbs++;
-         }
-         scbq_insert_tail(queue, scbp);
-         p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]--;
-         p->activescbs--;
-         scbp->flags |= SCB_WAITINGQ;
-         if ( !(scbp->tag_action & TAG_ENB) )
-         {
-           aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
-             TRUE);
-         }
-       }
-       else if (requeue)
-       {
-         p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
-       }
-       else
-       {
-        /*
-         * Preserve any SCB_RECOVERY_SCB flags on this scb then set the
-         * flags we were called with, presumeably so aic7xxx_run_done_queue
-         * can find this scb
-         */
-         scbp->flags = flags | (scbp->flags & SCB_RECOVERY_SCB);
-         if (aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
-                                       FALSE) == scbp->hscb->tag)
-         {
-           aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
-             TRUE);
-         }
-       }
-       found++;
-    }
-    else
-    {
-      p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
-    }
-  }
-  /*
-   * Now that we've done the work, clear out any left over commands in the
-   * qinfifo and update the KERNEL_QINPOS down on the card.
-   *
-   *  NOTE: This routine expect the sequencer to already be paused when
-   *        it is run....make sure it's that way!
-   */
-  qinpos = p->qinfifonext;
-  while(qinpos != qintail)
-  {
-    p->qinfifo[qinpos++] = SCB_LIST_NULL;
-  }
-  if (p->features & AHC_QUEUE_REGS)
-    aic_outb(p, p->qinfifonext, HNSCB_QOFF);
-  else
-    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
-
-  return (found);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_scb_on_qoutfifo
- *
- * Description:
- *   Is the scb that was passed to us currently on the qoutfifo?
- *-F*************************************************************************/
-static int
-aic7xxx_scb_on_qoutfifo(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int i=0;
-
-  while(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] != SCB_LIST_NULL)
-  {
-    if(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] == scb->hscb->tag)
-      return TRUE;
-    else
-      i++;
-  }
-  return FALSE;
-}
-
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_reset_device
- *
- * Description:
- *   The device at the given target/channel has been reset.  Abort
- *   all active and queued scbs for that target/channel.  This function
- *   need not worry about linked next pointers because if was a MSG_ABORT_TAG
- *   then we had a tagged command (no linked next), if it was MSG_ABORT or
- *   MSG_BUS_DEV_RESET then the device won't know about any commands any more
- *   and no busy commands will exist, and if it was a bus reset, then nothing
- *   knows about any linked next commands any more.  In all cases, we don't
- *   need to worry about the linked next or busy scb, we just need to clear
- *   them.
- *-F*************************************************************************/
-static void
-aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
-                     int lun, unsigned char tag)
-{
-  struct aic7xxx_scb *scbp;
-  unsigned char active_scb, tcl;
-  int i = 0, j, init_lists = FALSE;
-
-  /*
-   * Restore this when we're done
-   */
-  active_scb = aic_inb(p, SCBPTR);
-
-  if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS))
-    printk(INFO_LEAD "Reset device, active_scb %d\n",
-         p->host_no, channel, target, lun, active_scb);
-  /*
-   * Deal with the busy target and linked next issues.
-   */
-  {
-    int min_target, max_target;
-    struct aic7xxx_scb *scbp, *prev_scbp;
-
-    /* Make all targets 'relative' to bus A. */
-    if (target == ALL_TARGETS)
-    {
-      switch (channel)
-      {
-        case 0:
-                 min_target = 0;
-                 max_target = (p->features & AHC_WIDE) ? 15 : 7;
-                 break;
-        case 1:
-                 min_target = 8;
-                 max_target = 15;
-                 break;
-        case ALL_CHANNELS:
-        default:
-                 min_target = 0;
-                 max_target = (p->features & (AHC_TWIN|AHC_WIDE)) ? 15 : 7;
-                 break;
-      }
-    }
-    else
-    { 
-      min_target = target | (channel << 3);
-      max_target = min_target;
-    }
-
-
-    for (i = min_target; i <= max_target; i++)
-    {
-      if ( i == p->scsi_id )
-      {
-        continue;
-      }
-      if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
-        printk(INFO_LEAD "Cleaning up status information "
-          "and delayed_scbs.\n", p->host_no, channel, i, lun);
-      p->dev_flags[i] &= ~(BUS_DEVICE_RESET_PENDING | DEVICE_PARITY_ERROR);
-      if ( tag == SCB_LIST_NULL )
-      {
-        p->dev_flags[i] |= DEVICE_PRINT_DTR | DEVICE_RESET_DELAY;
-        p->dev_expires[i] = jiffies + (4 * HZ);
-        p->dev_timer_active |= (0x01 << i);
-        p->dev_last_queue_full_count[i] = 0;
-        p->dev_last_queue_full[i] = 0;
-        p->dev_temp_queue_depth[i] =
-          p->dev_max_queue_depth[i];
-      }
-      for(j=0; j<MAX_LUNS; j++)
-      {
-        if (channel == 1)
-          tcl = ((i << 4) & 0x70) | (channel << 3) | j;
-        else
-          tcl = (i << 4) | (channel << 3) | j;
-        if ( (aic7xxx_index_busy_target(p, tcl, FALSE) == tag) ||
-             (tag == SCB_LIST_NULL) )
-          aic7xxx_index_busy_target(p, tcl, /* unbusy */ TRUE);
-      }
-      j = 0; 
-      prev_scbp = NULL; 
-      scbp = p->delayed_scbs[i].head;
-      while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) )
-      {
-        prev_scbp = scbp;
-        scbp = scbp->q_next;
-        if ( prev_scbp == scbp )
-        {
-          if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
-            printk(WARN_LEAD "Yikes!! scb->q_next == scb "
-              "in the delayed_scbs queue!\n", p->host_no, channel, i, lun);
-          scbp = NULL;
-          prev_scbp->q_next = NULL;
-          p->delayed_scbs[i].tail = prev_scbp;
-        }
-        if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
-        {
-          scbq_remove(&p->delayed_scbs[i], prev_scbp);
-          if (prev_scbp->flags & SCB_WAITINGQ)
-          {
-            p->dev_active_cmds[i]++;
-            p->activescbs++;
-          }
-          prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
-          prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
-        }
-      }
-      if ( j > (p->scb_data->maxscbs + 1) )
-      {
-        if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
-          printk(WARN_LEAD "Yikes!! There's a loop in the "
-            "delayed_scbs queue!\n", p->host_no, channel, i, lun);
-        scbq_init(&p->delayed_scbs[i]);
-      }
-      if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) ||
-            time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
-      {
-        mod_timer(&p->dev_timer, p->dev_expires[i]);
-        p->dev_timer_active |= (0x01 << MAX_TARGETS);
-      }
-    }
-  }
-
-  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
-    printk(INFO_LEAD "Cleaning QINFIFO.\n", p->host_no, channel, target, lun );
-  aic7xxx_search_qinfifo(p, target, channel, lun, tag,
-      SCB_RESET | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE, NULL);
-
-/*
- *  Search the waiting_scbs queue for matches, this catches any SCB_QUEUED
- *  ABORT/RESET commands.
- */
-  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
-    printk(INFO_LEAD "Cleaning waiting_scbs.\n", p->host_no, channel,
-      target, lun );
-  {
-    struct aic7xxx_scb *scbp, *prev_scbp;
-
-    j = 0; 
-    prev_scbp = NULL; 
-    scbp = p->waiting_scbs.head;
-    while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) )
-    {
-      prev_scbp = scbp;
-      scbp = scbp->q_next;
-      if ( prev_scbp == scbp )
-      {
-        if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
-          printk(WARN_LEAD "Yikes!! scb->q_next == scb "
-            "in the waiting_scbs queue!\n", p->host_no, CTL_OF_SCB(scbp));
-        scbp = NULL;
-        prev_scbp->q_next = NULL;
-        p->waiting_scbs.tail = prev_scbp;
-      }
-      if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
-      {
-        scbq_remove(&p->waiting_scbs, prev_scbp);
-        if (prev_scbp->flags & SCB_WAITINGQ)
-        {
-          p->dev_active_cmds[TARGET_INDEX(prev_scbp->cmd)]++;
-          p->activescbs++;
-        }
-        prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
-        prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
-      }
-    }
-    if ( j > (p->scb_data->maxscbs + 1) )
-    {
-      if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
-        printk(WARN_LEAD "Yikes!! There's a loop in the "
-          "waiting_scbs queue!\n", p->host_no, channel, target, lun);
-      scbq_init(&p->waiting_scbs);
-    }
-  }
-
-
-  /*
-   * Search waiting for selection list.
-   */
-  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
-    printk(INFO_LEAD "Cleaning waiting for selection "
-      "list.\n", p->host_no, channel, target, lun);
-  {
-    unsigned char next, prev, scb_index;
-
-    next = aic_inb(p, WAITING_SCBH);  /* Start at head of list. */
-    prev = SCB_LIST_NULL;
-    j = 0;
-    while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
-    {
-      aic_outb(p, next, SCBPTR);
-      scb_index = aic_inb(p, SCB_TAG);
-      if (scb_index >= p->scb_data->numscbs)
-      {
-       /*
-        * No aic7xxx_verbose check here.....we want to see this since it
-        * means either the kernel driver or the sequencer screwed things up
-        */
-        printk(WARN_LEAD "Waiting List inconsistency; SCB index=%d, "
-          "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
-          p->scb_data->numscbs);
-        next = aic_inb(p, SCB_NEXT);
-        aic7xxx_add_curscb_to_free_list(p);
-      }
-      else
-      {
-        scbp = p->scb_data->scb_array[scb_index];
-        if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
-        {
-          next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
-          if (scbp->flags & SCB_WAITINGQ)
-          {
-            p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
-            p->activescbs++;
-          }
-          scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
-          scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
-          if (prev == SCB_LIST_NULL)
-          {
-            /*
-             * This is either the first scb on the waiting list, or we
-             * have already yanked the first and haven't left any behind.
-             * Either way, we need to turn off the selection hardware if
-             * it isn't already off.
-             */
-            aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
-            aic_outb(p, CLRSELTIMEO, CLRSINT1);
-          }
-        }
-        else
-        {
-          prev = next;
-          next = aic_inb(p, SCB_NEXT);
-        }
-      }
-    }
-    if ( j > (p->scb_data->maxscbs + 1) )
-    {
-      printk(WARN_LEAD "Yikes!!  There is a loop in the waiting for "
-        "selection list!\n", p->host_no, channel, target, lun);
-      init_lists = TRUE;
-    }
-  }
-
-  /*
-   * Go through disconnected list and remove any entries we have queued
-   * for completion, zeroing their control byte too.
-   */
-  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
-    printk(INFO_LEAD "Cleaning disconnected scbs "
-      "list.\n", p->host_no, channel, target, lun);
-  if (p->flags & AHC_PAGESCBS)
-  {
-    unsigned char next, prev, scb_index;
-
-    next = aic_inb(p, DISCONNECTED_SCBH);
-    prev = SCB_LIST_NULL;
-    j = 0;
-    while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
-    {
-      aic_outb(p, next, SCBPTR);
-      scb_index = aic_inb(p, SCB_TAG);
-      if (scb_index > p->scb_data->numscbs)
-      {
-        printk(WARN_LEAD "Disconnected List inconsistency; SCB index=%d, "
-          "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
-          p->scb_data->numscbs);
-        next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
-      }
-      else
-      {
-        scbp = p->scb_data->scb_array[scb_index];
-        if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
-        {
-          next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
-          if (scbp->flags & SCB_WAITINGQ)
-          {
-            p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
-            p->activescbs++;
-          }
-          scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
-          scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
-          scbp->hscb->control = 0;
-        }
-        else
-        {
-          prev = next;
-          next = aic_inb(p, SCB_NEXT);
-        }
-      }
-    }
-    if ( j > (p->scb_data->maxscbs + 1) )
-    {
-      printk(WARN_LEAD "Yikes!!  There is a loop in the disconnected list!\n",
-        p->host_no, channel, target, lun);
-      init_lists = TRUE;
-    }
-  }
-
-  /*
-   * Walk the free list making sure no entries on the free list have
-   * a valid SCB_TAG value or SCB_CONTROL byte.
-   */
-  if (p->flags & AHC_PAGESCBS)
-  {
-    unsigned char next;
-
-    j = 0;
-    next = aic_inb(p, FREE_SCBH);
-    if ( (next >= p->scb_data->maxhscbs) && (next != SCB_LIST_NULL) )
-    {
-      printk(WARN_LEAD "Bogus FREE_SCBH!.\n", p->host_no, channel,
-        target, lun);
-      init_lists = TRUE;
-      next = SCB_LIST_NULL;
-    }
-    while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
-    {
-      aic_outb(p, next, SCBPTR);
-      if (aic_inb(p, SCB_TAG) < p->scb_data->numscbs)
-      {
-        printk(WARN_LEAD "Free list inconsistency!.\n", p->host_no, channel,
-          target, lun);
-        init_lists = TRUE;
-        next = SCB_LIST_NULL;
-      }
-      else
-      {
-        aic_outb(p, SCB_LIST_NULL, SCB_TAG);
-        aic_outb(p, 0, SCB_CONTROL);
-        next = aic_inb(p, SCB_NEXT);
-      }
-    }
-    if ( j > (p->scb_data->maxscbs + 1) )
-    {
-      printk(WARN_LEAD "Yikes!!  There is a loop in the free list!\n",
-        p->host_no, channel, target, lun);
-      init_lists = TRUE;
-    }
-  }
-
-  /*
-   * Go through the hardware SCB array looking for commands that
-   * were active but not on any list.
-   */
-  if (init_lists)
-  {
-    aic_outb(p, SCB_LIST_NULL, FREE_SCBH);
-    aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
-    aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
-  }
-  for (i = p->scb_data->maxhscbs - 1; i >= 0; i--)
-  {
-    unsigned char scbid;
-
-    aic_outb(p, i, SCBPTR);
-    if (init_lists)
-    {
-      aic_outb(p, SCB_LIST_NULL, SCB_TAG);
-      aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
-      aic_outb(p, 0, SCB_CONTROL);
-      aic7xxx_add_curscb_to_free_list(p);
-    }
-    else
-    {
-      scbid = aic_inb(p, SCB_TAG);
-      if (scbid < p->scb_data->numscbs)
-      {
-        scbp = p->scb_data->scb_array[scbid];
-        if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
-        {
-          aic_outb(p, 0, SCB_CONTROL);
-          aic_outb(p, SCB_LIST_NULL, SCB_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.
-   * Any commands we find here we know this about, it wasn't on any queue,
-   * it wasn't in the qinfifo, it wasn't in the disconnected or waiting
-   * lists, so it really must have been a paged out SCB.  In that case,
-   * we shouldn't need to bother with updating any counters, just mark
-   * the correct flags and go on.
-   */
-  for (i = 0; i < p->scb_data->numscbs; i++)
-  {
-    scbp = p->scb_data->scb_array[i];
-    if ((scbp->flags & SCB_ACTIVE) &&
-        aic7xxx_match_scb(p, scbp, target, channel, lun, tag) &&
-        !aic7xxx_scb_on_qoutfifo(p, scbp))
-    {
-      if (scbp->flags & SCB_WAITINGQ)
-      {
-        scbq_remove(&p->waiting_scbs, scbp);
-        scbq_remove(&p->delayed_scbs[TARGET_INDEX(scbp->cmd)], scbp);
-        p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
-        p->activescbs++;
-      }
-      scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
-      scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
-    }
-  }
-
-  aic_outb(p, active_scb, SCBPTR);
-}
-
-
-/*+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. */
-  aic_outb(p, CLRSELDO | CLRSELDI | CLRSELINGO, CLRSINT0);
-  aic_outb(p, CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
-       CLRPHASECHG | CLRREQINIT, CLRSINT1);
-  aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT | CLRPARERR, CLRINT);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_reset_current_bus
- *
- * Description:
- *   Reset the current SCSI bus.
- *-F*************************************************************************/
-static void
-aic7xxx_reset_current_bus(struct aic7xxx_host *p)
-{
-
-  /* Disable reset interrupts. */
-  aic_outb(p, aic_inb(p, SIMODE1) & ~ENSCSIRST, SIMODE1);
-
-  /* Turn off the bus' current operations, after all, we shouldn't have any
-   * valid commands left to cause a RSELI and SELO once we've tossed the
-   * bus away with this reset, so we might as well shut down the sequencer
-   * until the bus is restarted as oppossed to saving the current settings
-   * and restoring them (which makes no sense to me). */
-
-  /* Turn on the bus reset. */
-  aic_outb(p, aic_inb(p, SCSISEQ) | SCSIRSTO, SCSISEQ);
-  while ( (aic_inb(p, SCSISEQ) & SCSIRSTO) == 0)
-    mdelay(5);
-
-  /*
-   * Some of the new Ultra2 chipsets need a longer delay after a chip
-   * reset than just the init setup creates, so we have to delay here
-   * before we go into a reset in order to make the chips happy.
-   */
-  if (p->features & AHC_ULTRA2)
-    mdelay(250);
-  else
-    mdelay(50);
-
-  /* Turn off the bus reset. */
-  aic_outb(p, 0, SCSISEQ);
-  mdelay(10);
-
-  aic7xxx_clear_intstat(p);
-  /* Re-enable reset interrupts. */
-  aic_outb(p, aic_inb(p, SIMODE1) | ENSCSIRST, SIMODE1);
-
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_reset_channel
- *
- * Description:
- *   Reset the channel.
- *-F*************************************************************************/
-static void
-aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset)
-{
-  unsigned long offset_min, offset_max;
-  unsigned char sblkctl;
-  int cur_channel;
-
-  if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-    printk(INFO_LEAD "Reset channel called, %s initiate reset.\n",
-      p->host_no, channel, -1, -1, (initiate_reset==TRUE) ? "will" : "won't" );
-
-
-  if (channel == 1)
-  {
-    p->needsdtr |= (p->needsdtr_copy & 0xFF00);
-    p->dtr_pending &= 0x00FF;
-    offset_min = 8;
-    offset_max = 16;
-  }
-  else
-  {
-    if (p->features & AHC_TWIN)
-    {
-      /* Channel A */
-      p->needsdtr |= (p->needsdtr_copy & 0x00FF);
-      p->dtr_pending &= 0xFF00;
-      offset_min = 0;
-      offset_max = 8;
-    }
-    else
-    {
-      p->needppr = p->needppr_copy;
-      p->needsdtr = p->needsdtr_copy;
-      p->needwdtr = p->needwdtr_copy;
-      p->dtr_pending = 0x0;
-      offset_min = 0;
-      if (p->features & AHC_WIDE)
-      {
-        offset_max = 16;
-      }
-      else
-      {
-        offset_max = 8;
-      }
-    }
-  }
-
-  while (offset_min < offset_max)
-  {
-    /*
-     * Revert to async/narrow transfers until we renegotiate.
-     */
-    aic_outb(p, 0, TARG_SCSIRATE + offset_min);
-    if (p->features & AHC_ULTRA2)
-    {
-      aic_outb(p, 0, TARG_OFFSET + offset_min);
-    }
-    offset_min++;
-  }
-
-  /*
-   * Reset the bus and unpause/restart the controller
-   */
-  sblkctl = aic_inb(p, SBLKCTL);
-  if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
-    cur_channel = (sblkctl & SELBUSB) >> 3;
-  else
-    cur_channel = 0;
-  if ( (cur_channel != channel) && (p->features & AHC_TWIN) )
-  {
-    /*
-     * Case 1: Command for another bus is active
-     */
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-      printk(INFO_LEAD "Stealthily resetting idle channel.\n", p->host_no,
-        channel, -1, -1);
-    /*
-     * Stealthily reset the other bus without upsetting the current bus.
-     */
-    aic_outb(p, sblkctl ^ SELBUSB, SBLKCTL);
-    aic_outb(p, aic_inb(p, SIMODE1) & ~ENBUSFREE, SIMODE1);
-    if (initiate_reset)
-    {
-      aic7xxx_reset_current_bus(p);
-    }
-    aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
-    aic7xxx_clear_intstat(p);
-    aic_outb(p, sblkctl, SBLKCTL);
-  }
-  else
-  {
-    /*
-     * Case 2: A command from this bus is active or we're idle.
-     */
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-      printk(INFO_LEAD "Resetting currently active channel.\n", p->host_no,
-        channel, -1, -1);
-    aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
-      SIMODE1);
-    p->flags &= ~AHC_HANDLING_REQINITS;
-    p->msg_type = MSG_TYPE_NONE;
-    p->msg_len = 0;
-    if (initiate_reset)
-    {
-      aic7xxx_reset_current_bus(p);
-    }
-    aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
-    aic7xxx_clear_intstat(p);
-  }
-  if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
-    printk(INFO_LEAD "Channel reset\n", p->host_no, channel, -1, -1);
-  /*
-   * Clean up all the state information for the pending transactions
-   * on this bus.
-   */
-  aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
-
-  if ( !(p->features & AHC_TWIN) )
-  {
-    restart_sequencer(p);
-  }
-
-  return;
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_run_waiting_queues
- *
- * Description:
- *   Scan the awaiting_scbs queue downloading and starting as many
- *   scbs as we can.
- *-F*************************************************************************/
-static void
-aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
-{
-  struct aic7xxx_scb *scb;
-  int tindex;
-  int sent;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-  unsigned long cpu_flags = 0;
-#endif
-
-
-  if (p->waiting_scbs.head == NULL)
-    return;
-
-  sent = 0;
-
-  /*
-   * First handle SCBs that are waiting but have been assigned a slot.
-   */
-  DRIVER_LOCK
-  while ((scb = scbq_remove_head(&p->waiting_scbs)) != NULL)
-  {
-    tindex = TARGET_INDEX(scb->cmd);
-    if ( !scb->tag_action && (p->tagenable & (1<<tindex)) )
-    {
-      p->dev_temp_queue_depth[tindex] = 1;
-    }
-    if ( (p->dev_active_cmds[tindex] >=
-          p->dev_temp_queue_depth[tindex]) ||
-         (p->dev_flags[tindex] & (DEVICE_RESET_DELAY|DEVICE_WAS_BUSY)) ||
-         (p->flags & AHC_RESET_DELAY) )
-    {
-      scbq_insert_tail(&p->delayed_scbs[tindex], scb);
-    }
-    else
-    {
-        scb->flags &= ~SCB_WAITINGQ;
-        p->dev_active_cmds[tindex]++;
-        p->activescbs++;
-        if ( !(scb->tag_action) )
-        {
-          aic7xxx_busy_target(p, scb);
-        }
-        p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
-        sent++;
-    }
-  }
-  if (sent)
-  {
-    if (p->features & AHC_QUEUE_REGS)
-      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
-    else
-    {
-      pause_sequencer(p);
-      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
-      unpause_sequencer(p, FALSE);
-    }
-    if (p->activescbs > p->max_activescbs)
-      p->max_activescbs = p->activescbs;
-  }
-  DRIVER_UNLOCK
-}
-
-#ifdef CONFIG_PCI
-
-#define  DPE 0x80
-#define  SSE 0x40
-#define  RMA 0x20
-#define  RTA 0x10
-#define  STA 0x08
-#define  DPR 0x01
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_pci_intr
- *
- * Description:
- *   Check the scsi card for PCI errors and clear the interrupt
- *
- *   NOTE: If you don't have this function and a 2940 card encounters
- *         a PCI error condition, the machine will end up locked as the
- *         interrupt handler gets slammed with non-stop PCI error interrupts
- *-F*************************************************************************/
-static void
-aic7xxx_pci_intr(struct aic7xxx_host *p)
-{
-  unsigned char status1;
-
-  pci_read_config_byte(p->pdev, PCI_STATUS + 1, &status1);
-
-  if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
-      "phase.\n", p->host_no, -1, -1, -1);
-  if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
-      -1, -1, -1);
-  if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
-      -1, -1, -1);
-  if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
-      -1, -1, -1);
-  if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
-      -1, -1, -1);
-  if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
-    printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
-      "PERR#\n", p->host_no, -1, -1, -1);
-  
-  pci_write_config_byte(p->pdev, PCI_STATUS + 1, status1);
-  if (status1 & (DPR|RMA|RTA))
-    aic_outb(p,  CLRPARERR, CLRINT);
-
-  if ( (aic7xxx_panic_on_abort) && (p->spurious_int > 500) )
-    aic7xxx_panic_abort(p, NULL);
-
-}
-#endif /* CONFIG_PCI */
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_timer
- *
- * Description:
- *   Take expired extries off of delayed queues and place on waiting queue
- *   then run waiting queue to start commands.
- ***************************************************************************/
-static void
-aic7xxx_timer(struct aic7xxx_host *p)
-{
-  int i, j;
-  unsigned long cpu_flags = 0;
-  struct aic7xxx_scb *scb;
-
-  spin_lock_irqsave(&io_request_lock, cpu_flags);
-  p->dev_timer_active &= ~(0x01 << MAX_TARGETS);
-  if ( (p->dev_timer_active & (0x01 << p->scsi_id)) &&
-       time_after_eq(jiffies, p->dev_expires[p->scsi_id]) )
-  {
-    p->flags &= ~AHC_RESET_DELAY;
-    p->dev_timer_active &= ~(0x01 << p->scsi_id);
-  }
-  for(i=0; i<MAX_TARGETS; i++)
-  {
-    if ( (i != p->scsi_id) &&
-         (p->dev_timer_active & (0x01 << i)) &&
-         time_after_eq(jiffies, p->dev_expires[i]) )
-    {
-      p->dev_timer_active &= ~(0x01 << i);
-      p->dev_flags[i] &= ~(DEVICE_RESET_DELAY|DEVICE_WAS_BUSY);
-      p->dev_temp_queue_depth[i] =  p->dev_max_queue_depth[i];
-      j = 0;
-      while ( ((scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL) &&
-              (j++ < p->scb_data->numscbs) )
-      {
-        scbq_insert_tail(&p->waiting_scbs, scb);
-      }
-      if (j == p->scb_data->numscbs)
-      {
-        printk(INFO_LEAD "timer: Yikes, loop in delayed_scbs list.\n",
-          p->host_no, 0, i, -1);
-        scbq_init(&p->delayed_scbs[i]);
-        scbq_init(&p->waiting_scbs);
-        /*
-         * Well, things are screwed now, wait for a reset to clean the junk
-         * out.
-         */
-      }
-    }
-    else if ( p->dev_timer_active & (0x01 << i) )
-    {
-      if ( p->dev_timer_active & (0x01 << MAX_TARGETS) )
-      {
-        if ( time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
-        {
-          p->dev_timer.expires = p->dev_expires[i];
-        }
-      }
-      else
-      {
-        p->dev_timer.expires = p->dev_expires[i];
-        p->dev_timer_active |= (0x01 << MAX_TARGETS);
-      }
-    }
-  }
-  if ( p->dev_timer_active & (0x01 << MAX_TARGETS) )
-  {
-    add_timer(&p->dev_timer);
-  }
-
-  aic7xxx_run_waiting_queues(p);
-  spin_unlock_irqrestore(&io_request_lock, cpu_flags);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_construct_ppr
- *
- * Description:
- *   Build up a Parallel Protocol Request message for use with SCSI-3
- *   devices.
- *-F*************************************************************************/
-static void
-aic7xxx_construct_ppr(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int tindex = TARGET_INDEX(scb->cmd);
-
-  p->msg_buf[p->msg_index++] = MSG_EXTENDED;
-  p->msg_buf[p->msg_index++] = MSG_EXT_PPR_LEN;
-  p->msg_buf[p->msg_index++] = MSG_EXT_PPR;
-  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_period;
-  p->msg_buf[p->msg_index++] = 0;
-  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_offset;
-  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_width;
-  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_options;
-  p->msg_len += 8;
-}
-
-/*+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, unsigned char period,
-        unsigned char offset)
-{
-  p->msg_buf[p->msg_index++] = MSG_EXTENDED;
-  p->msg_buf[p->msg_index++] = MSG_EXT_SDTR_LEN;
-  p->msg_buf[p->msg_index++] = MSG_EXT_SDTR;
-  p->msg_buf[p->msg_index++] = period;
-  p->msg_buf[p->msg_index++] = offset;
-  p->msg_len += 5;
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_construct_wdtr
- *
- * Description:
- *   Constucts a wide data transfer message in the message buffer
- *   on the sequencer.
- *-F*************************************************************************/
-static void
-aic7xxx_construct_wdtr(struct aic7xxx_host *p, unsigned char bus_width)
-{
-  p->msg_buf[p->msg_index++] = MSG_EXTENDED;
-  p->msg_buf[p->msg_index++] = MSG_EXT_WDTR_LEN;
-  p->msg_buf[p->msg_index++] = MSG_EXT_WDTR;
-  p->msg_buf[p->msg_index++] = bus_width;
-  p->msg_len += 4;
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_calc_residual
- *
- * Description:
- *   Calculate the residual data not yet transferred.
- *-F*************************************************************************/
-static void
-aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  struct aic7xxx_hwscb *hscb;
-  Scsi_Cmnd *cmd;
-  int actual, i;
-
-  cmd = scb->cmd;
-  hscb = scb->hscb;
-
-  /*
-   *  Don't destroy valid residual information with
-   *  residual coming from a check sense operation.
-   */
-  if (((scb->hscb->control & DISCONNECTED) == 0) &&
-      (scb->flags & SCB_SENSE) == 0)
-  {
-    /*
-     *  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 = scb->sg_length;
-    for (i=1; i < hscb->residual_SG_segment_count; i++)
-    {
-      actual -= scb->sg_list[scb->sg_count - i].length;
-    }
-    actual -= (hscb->residual_data_count[2] << 16) |
-              (hscb->residual_data_count[1] <<  8) |
-              hscb->residual_data_count[0];
-
-    if (actual < cmd->underflow)
-    {
-      if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
-        printk(INFO_LEAD "Underflow - Wanted %u, %s %u, residual SG "
-          "count %d.\n", p->host_no, CTL_OF_SCB(scb), cmd->underflow,
-          (cmd->request.cmd == WRITE) ? "wrote" : "read", actual,
-          hscb->residual_SG_segment_count);
-      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
-      aic7xxx_status(cmd) = hscb->target_status;
-    }
-  }
-
-  /*
-   * Clean out the residual information in the SCB for the
-   * next consumer.
-   */
-  hscb->residual_data_count[2] = 0;
-  hscb->residual_data_count[1] = 0;
-  hscb->residual_data_count[0] = 0;
-  hscb->residual_SG_segment_count = 0;
-}
-
-/*+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, int channel)
-{
-  unsigned short targ_mask;
-  unsigned char tindex = target;
-
-  tindex |= ((channel & 0x01) << 3);
-
-  targ_mask = (0x01 << tindex);
-  /*
-   * Go back to async/narrow transfers and renegotiate.
-   */
-  p->needppr |= (p->needppr_copy & targ_mask);
-  p->needsdtr |= (p->needsdtr_copy & targ_mask);
-  p->needwdtr |= (p->needwdtr_copy & targ_mask);
-  p->dtr_pending &= ~targ_mask;
-  aic_outb(p, 0, TARG_SCSIRATE + tindex);
-  if (p->features & AHC_ULTRA2)
-    aic_outb(p, 0, TARG_OFFSET + tindex);
-  aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
-  if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-    printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel,
-      target, -1);
-  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
-}
-
-/*+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, lun, tindex;
-  unsigned char queue_flag = FALSE;
-  char channel;
-
-  target = ((aic_inb(p, SAVED_TCL) >> 4) & 0x0f);
-  if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
-    channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
-  else
-    channel = 0;
-  tindex = target + (channel << 3);
-  lun = aic_inb(p, SAVED_TCL) & 0x07;
-  target_mask = (0x01 << tindex);
-
-  /*
-   * Go ahead and clear the SEQINT now, that avoids any interrupt race
-   * conditions later on in case we enable some other interrupt.
-   */
-  aic_outb(p, CLRSEQINT, CLRINT);
-  switch (intstat & SEQINT_MASK)
-  {
-    case NO_MATCH:
-      {
-        aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
-                 SCSISEQ);
-        printk(WARN_LEAD "No active SCB for reconnecting target - Issuing "
-               "BUS DEVICE RESET.\n", p->host_no, channel, target, lun);
-        printk(WARN_LEAD "      SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
-               p->host_no, channel, target, lun,
-               aic_inb(p, SAVED_TCL), aic_inb(p, ARG_1),
-               (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
-        if (aic7xxx_panic_on_abort)
-          aic7xxx_panic_abort(p, NULL);
-      }
-      break;
-
-    case SEND_REJECT:
-      {
-        if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
-          printk(INFO_LEAD "Rejecting unknown message (0x%x) received from "
-            "target, SEQ_FLAGS=0x%x\n", p->host_no, channel, target, lun,
-            aic_inb(p, ACCUM), aic_inb(p, SEQ_FLAGS));
-      }
-      break;
-
-    case NO_IDENT:
-      {
-        /*
-         * The reconnecting target either did not send an identify
-         * message, or did, but we didn't find an 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.
-         */
-        if (aic7xxx_verbose & (VERBOSE_SEQINT | VERBOSE_RESET_MID))
-          printk(INFO_LEAD "Target did not send an IDENTIFY message; "
-            "LASTPHASE 0x%x, SAVED_TCL 0x%x\n", p->host_no, channel, target,
-            lun, aic_inb(p, LASTPHASE), aic_inb(p, SAVED_TCL));
-
-        aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
-        aic7xxx_run_done_queue(p, TRUE);
-
-      }
-      break;
-
-    case BAD_PHASE:
-      if (aic_inb(p, LASTPHASE) == P_BUSFREE)
-      {
-        if (aic7xxx_verbose & VERBOSE_SEQINT)
-          printk(INFO_LEAD "Missed busfree.\n", p->host_no, channel,
-            target, lun);
-        restart_sequencer(p);
-      }
-      else
-      {
-        if (aic7xxx_verbose & VERBOSE_SEQINT)
-          printk(INFO_LEAD "Unknown scsi bus phase, continuing\n", p->host_no,
-            channel, target, lun);
-      }
-      break;
-
-    case EXTENDED_MSG:
-      {
-        p->msg_type = MSG_TYPE_INITIATOR_MSGIN;
-        p->msg_len = 0;
-        p->msg_index = 0;
-
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-        if (aic7xxx_verbose > 0xffff)
-          printk(INFO_LEAD "Enabling REQINITs for MSG_IN\n", p->host_no,
-                 channel, target, lun);
-#endif
-
-       /*      
-        * To actually receive the message, simply turn on
-        * REQINIT interrupts and let our interrupt handler
-        * do the rest (REQINIT should already be true).
-        */
-        p->flags |= AHC_HANDLING_REQINITS;
-        aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
-
-       /*
-        * We don't want the sequencer unpaused yet so we return early
-        */
-        return;
-      }
-
-    case REJECT_MSG:
-      {
-        /*
-         * 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;
-        unsigned char last_msg;
-
-        scb_index = aic_inb(p, SCB_TAG);
-        scb = p->scb_data->scb_array[scb_index];
-        last_msg = aic_inb(p, LAST_MSG);
-
-        if ( (last_msg == MSG_IDENTIFYFLAG) &&
-             (scb->tag_action) &&
-            !(scb->flags & SCB_MSGOUT_BITS) )
-        {
-          if (scb->tag_action == MSG_ORDERED_Q_TAG)
-          {
-            /*
-             * OK...the device seems able to accept tagged commands, but
-             * not ordered tag commands, only simple tag commands.  So, we
-             * disable ordered tag commands and go on with life just like
-             * normal.
-             */
-            p->orderedtag &= ~target_mask;
-            scb->tag_action = MSG_SIMPLE_Q_TAG;
-            scb->hscb->control &= ~SCB_TAG_TYPE;
-            scb->hscb->control |= MSG_SIMPLE_Q_TAG;
-            aic_outb(p, scb->hscb->control, SCB_CONTROL);
-            /*
-             * OK..we set the tag type to simple tag command, now we re-assert
-             * ATNO and hope this will take us into the identify phase again
-             * so we can resend the tag type and info to the device.
-             */
-            aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
-            aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
-          }
-          else if (scb->tag_action == MSG_SIMPLE_Q_TAG)
-          {
-            unsigned char i, reset = 0;
-            struct aic7xxx_scb *scbp;
-            int old_verbose;
-            /*
-             * Hmmmm....the device is flaking out on tagged commands.  The
-             * bad thing is that we already have tagged commands enabled in
-             * the device struct in the mid level code.  We also have a queue
-             * set according to the tagged queue depth.  Gonna have to live
-             * with it by controlling our queue depth internally and making
-             * sure we don't set the tagged command flag any more.
-             */
-            p->tagenable &= ~target_mask;
-            p->orderedtag &= ~target_mask;
-            p->dev_max_queue_depth[tindex] =
-               p->dev_temp_queue_depth[tindex] = 1;
-            /*
-             * We set this command up as a bus device reset.  However, we have
-             * to clear the tag type as it's causing us problems.  We shouldnt
-             * have to worry about any other commands being active, since if
-             * the device is refusing tagged commands, this should be the
-             * first tagged command sent to the device, however, we do have
-             * to worry about any other tagged commands that may already be
-             * in the qinfifo.  The easiest way to do this, is to issue a BDR,
-             * send all the commands back to the mid level code, then let them
-             * come back and get rebuilt as untagged commands.
-             */
-            scb->tag_action = 0;
-            scb->hscb->control &= ~(TAG_ENB | SCB_TAG_TYPE);
-            aic_outb(p,  scb->hscb->control, SCB_CONTROL);
-
-            old_verbose = aic7xxx_verbose;
-            aic7xxx_verbose &= ~(VERBOSE_RESET|VERBOSE_ABORT);
-            for (i=0; i!=p->scb_data->numscbs; i++)
-            {
-              scbp = p->scb_data->scb_array[i];
-              if ((scbp->flags & SCB_ACTIVE) && (scbp != scb))
-              {
-                if (aic7xxx_match_scb(p, scbp, target, channel, lun, i))
-                {
-                  aic7xxx_reset_device(p, target, channel, lun, i);
-                  reset++;
-                }
-                aic7xxx_run_done_queue(p, TRUE);
-              }
-            }
-            aic7xxx_verbose = old_verbose;
-            /*
-             * Wait until after the for loop to set the busy index since
-             * aic7xxx_reset_device will clear the busy index during its
-             * operation.
-             */
-            aic7xxx_busy_target(p, scb);
-            printk(INFO_LEAD "Device is refusing tagged commands, using "
-              "untagged I/O.\n", p->host_no, channel, target, lun);
-            aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
-            aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
-          }
-        }
-        else if (scb->flags & SCB_MSGOUT_PPR)
-        {
-          /*
-           * As per the draft specs, any device capable of supporting any of
-           * the option values other than 0 are not allowed to reject the
-           * PPR message.  Instead, they must negotiate out what they do
-           * support instead of rejecting our offering or else they cause
-           * a parity error during msg_out phase to signal that they don't
-           * like our settings.
-           */
-          p->needppr &= ~target_mask;
-          p->needppr_copy &= ~target_mask;
-          aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
-            (AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE));
-          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
-                               AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
-          p->transinfo[tindex].goal_options = 0;
-          p->dtr_pending &= ~target_mask;
-          scb->flags &= ~SCB_MSGOUT_BITS;
-          if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-          {
-            printk(INFO_LEAD "Device is rejecting PPR messages, falling "
-              "back.\n", p->host_no, channel, target, lun);
-          }
-          if ( p->transinfo[tindex].goal_width )
-          {
-            p->needwdtr |= target_mask;
-            p->needwdtr_copy |= target_mask;
-            p->dtr_pending |= target_mask;
-            scb->flags |= SCB_MSGOUT_WDTR;
-          }
-          if ( p->transinfo[tindex].goal_offset )
-          {
-            p->needsdtr |= target_mask;
-            p->needsdtr_copy |= target_mask;
-            if( !(p->dtr_pending & target_mask) )
-            {
-              p->dtr_pending |= target_mask;
-              scb->flags |= SCB_MSGOUT_SDTR;
-            }
-          }
-          if ( p->dtr_pending & target_mask )
-          {
-            aic_outb(p, HOST_MSG, MSG_OUT);
-            aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
-          }
-        }
-        else if (scb->flags & SCB_MSGOUT_WDTR)
-        {
-          /*
-           * note 8bit xfers and clear flag
-           */
-          p->needwdtr &= ~target_mask;
-          p->needwdtr_copy &= ~target_mask;
-          p->dtr_pending &= ~target_mask;
-          scb->flags &= ~SCB_MSGOUT_BITS;
-          aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
-            (AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR));
-          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
-                               AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
-          if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-          {
-            printk(INFO_LEAD "Device is rejecting WDTR messages, using "
-              "narrow transfers.\n", p->host_no, channel, target, lun);
-          }
-          p->needsdtr |= (p->needsdtr_copy & target_mask);
-        }
-        else if (scb->flags & SCB_MSGOUT_SDTR)
-        {
-         /*
-          * note asynch xfers and clear flag
-          */
-          p->needsdtr &= ~target_mask;
-          p->needsdtr_copy &= ~target_mask;
-          p->dtr_pending &= ~target_mask;
-          scb->flags &= ~SCB_MSGOUT_SDTR;
-          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
-            (AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL));
-          if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-          {
-            printk(INFO_LEAD "Device is rejecting SDTR messages, using "
-              "async transfers.\n", p->host_no, channel, target, lun);
-          }
-        }
-        else if (aic7xxx_verbose & VERBOSE_SEQINT)
-        {
-          /*
-           * Otherwise, we ignore it.
-           */
-          printk(INFO_LEAD "Received MESSAGE_REJECT for unknown cause.  "
-            "Ignoring.\n", p->host_no, channel, target, lun);
-        }
-      }
-      break;
-
-    case BAD_STATUS:
-      {
-        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.
-         *
-         * 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.
-         */
-        aic_outb(p, 0, RETURN_1);
-        scb_index = aic_inb(p, SCB_TAG);
-        if (scb_index > p->scb_data->numscbs)
-        {
-          printk(WARN_LEAD "Invalid SCB during SEQINT 0x%02x, SCB_TAG %d.\n",
-            p->host_no, channel, target, lun, intstat, scb_index);
-          break;
-        }
-        scb = p->scb_data->scb_array[scb_index];
-        hscb = scb->hscb;
-
-        if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
-        {
-          printk(WARN_LEAD "Invalid SCB during SEQINT 0x%x, scb %d, flags 0x%x,"
-            " cmd 0x%lx.\n", p->host_no, channel, target, lun, intstat,
-            scb_index, scb->flags, (unsigned long) scb->cmd);
-        }
-        else
-        {
-          cmd = scb->cmd;
-          hscb->target_status = aic_inb(p, SCB_TARGET_STATUS);
-          aic7xxx_status(cmd) = hscb->target_status;
-
-          cmd->result = hscb->target_status;
-
-          switch (status_byte(hscb->target_status))
-          {
-            case GOOD:
-              if (aic7xxx_verbose & VERBOSE_SEQINT)
-                printk(INFO_LEAD "Interrupted for status of GOOD???\n",
-                  p->host_no, CTL_OF_SCB(scb));
-              break;
-
-            case COMMAND_TERMINATED:
-            case CHECK_CONDITION:
-              if ( !(scb->flags & SCB_SENSE) )
-              {
-                /*
-                 * Send a sense command to the requesting target.
-                 * XXX - revisit this and get rid of the memcopys.
-                 */
-                memcpy(scb->sense_cmd, &generic_sense[0],
-                       sizeof(generic_sense));
-
-                scb->sense_cmd[1] = (cmd->lun << 5);
-                scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
-
-                scb->sg_list[0].length = 
-                  cpu_to_le32(sizeof(cmd->sense_buffer));
-		scb->sg_list[0].address =
-                        cpu_to_le32(pci_map_single(p->pdev, cmd->sense_buffer,
-                                                   sizeof(cmd->sense_buffer),
-                                                   PCI_DMA_FROMDEVICE));
-
-                /*
-                 * XXX - We should allow disconnection, but can't as it
-                 * might allow overlapped tagged commands.
-                 */
-                /* hscb->control &= DISCENB; */
-                hscb->control = 0;
-                hscb->target_status = 0;
-                hscb->SG_list_pointer = 
-		  cpu_to_le32(SCB_DMA_ADDR(scb, scb->sg_list));
-                hscb->SCSI_cmd_pointer = 
-                  cpu_to_le32(SCB_DMA_ADDR(scb, scb->sense_cmd));
-                hscb->data_count = scb->sg_list[0].length;
-                hscb->data_pointer = scb->sg_list[0].address;
-                hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
-                hscb->residual_SG_segment_count = 0;
-                hscb->residual_data_count[0] = 0;
-                hscb->residual_data_count[1] = 0;
-                hscb->residual_data_count[2] = 0;
-
-                scb->sg_count = hscb->SG_segment_count = 1;
-                scb->sg_length = sizeof(cmd->sense_buffer);
-                scb->tag_action = 0;
-                scb->flags |= SCB_SENSE;
-                /*
-                 * Ensure the target is busy since this will be an
-                 * an untagged request.
-                 */
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-                if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-                {
-                  if (scb->flags & SCB_MSGOUT_BITS)
-                    printk(INFO_LEAD "Requesting SENSE with %s\n", p->host_no,
-                           CTL_OF_SCB(scb), (scb->flags & SCB_MSGOUT_SDTR) ?
-                           "SDTR" : "WDTR");
-                  else
-                    printk(INFO_LEAD "Requesting SENSE, no MSG\n", p->host_no,
-                           CTL_OF_SCB(scb));
-                }
-#endif
-                aic7xxx_busy_target(p, scb);
-                aic_outb(p, SEND_SENSE, RETURN_1);
-                aic7xxx_error(cmd) = DID_OK;
-                break;
-              }  /* first time sense, no errors */
-              aic7xxx_error(cmd) = DID_ERROR;
-              scb->flags &= ~SCB_SENSE;
-              break;
-
-            case QUEUE_FULL:
-              queue_flag = TRUE;    /* Mark that this is a QUEUE_FULL and */
-            case BUSY:              /* drop through to here */
-            {
-              struct aic7xxx_scb *next_scbp, *prev_scbp;
-              unsigned char active_hscb, next_hscb, prev_hscb, scb_index;
-              /*
-               * We have to look three places for queued commands:
-               *  1: QINFIFO
-               *  2: p->waiting_scbs queue
-               *  3: WAITING_SCBS list on card (for commands that are started
-               *     but haven't yet made it to the device)
-               */
-              aic7xxx_search_qinfifo(p, target, channel, lun,
-                SCB_LIST_NULL, 0, TRUE,
-                &p->delayed_scbs[tindex]);
-              next_scbp = p->waiting_scbs.head;
-              while ( next_scbp != NULL )
-              {
-                prev_scbp = next_scbp;
-                next_scbp = next_scbp->q_next;
-                if ( aic7xxx_match_scb(p, prev_scbp, target, channel, lun,
-                     SCB_LIST_NULL) )
-                {
-                  scbq_remove(&p->waiting_scbs, prev_scbp);
-                  scbq_insert_tail(&p->delayed_scbs[tindex],
-                    prev_scbp);
-                }
-              }
-              next_scbp = NULL;
-              active_hscb = aic_inb(p, SCBPTR);
-              prev_hscb = next_hscb = scb_index = SCB_LIST_NULL;
-              next_hscb = aic_inb(p, WAITING_SCBH);
-              while (next_hscb != SCB_LIST_NULL)
-              {
-                aic_outb(p, next_hscb, SCBPTR);
-                scb_index = aic_inb(p, SCB_TAG);
-                if (scb_index < p->scb_data->numscbs)
-                {
-                  next_scbp = p->scb_data->scb_array[scb_index];
-                  if (aic7xxx_match_scb(p, next_scbp, target, channel, lun,
-                      SCB_LIST_NULL) )
-                  {
-                    if (next_scbp->flags & SCB_WAITINGQ)
-                    {
-                      p->dev_active_cmds[tindex]++;
-                      p->activescbs--;
-                      scbq_remove(&p->delayed_scbs[tindex], next_scbp);
-                      scbq_remove(&p->waiting_scbs, next_scbp);
-                    }
-                    scbq_insert_head(&p->delayed_scbs[tindex],
-                      next_scbp);
-                    next_scbp->flags |= SCB_WAITINGQ;
-                    p->dev_active_cmds[tindex]--;
-                    p->activescbs--;
-                    next_hscb = aic_inb(p, SCB_NEXT);
-                    aic_outb(p, 0, SCB_CONTROL);
-                    aic_outb(p, SCB_LIST_NULL, SCB_TAG);
-                    aic7xxx_add_curscb_to_free_list(p);
-                    if (prev_hscb == SCB_LIST_NULL)
-                    {
-                      /* We were first on the list,
-                       * so we kill the selection
-                       * hardware.  Let the sequencer
-                       * re-init the hardware itself
-                       */
-                      aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
-                      aic_outb(p, CLRSELTIMEO, CLRSINT1);
-                      aic_outb(p, next_hscb, WAITING_SCBH);
-                    }
-                    else
-                    {
-                      aic_outb(p, prev_hscb, SCBPTR);
-                      aic_outb(p, next_hscb, SCB_NEXT);
-                    }
-                  }
-                  else
-                  {
-                    prev_hscb = next_hscb;
-                    next_hscb = aic_inb(p, SCB_NEXT);
-                  }
-                } /* scb_index >= p->scb_data->numscbs */
-              }
-              aic_outb(p, active_hscb, SCBPTR);
-              if (scb->flags & SCB_WAITINGQ)
-              {
-                scbq_remove(&p->delayed_scbs[tindex], scb);
-                scbq_remove(&p->waiting_scbs, scb);
-                p->dev_active_cmds[tindex]++;
-                p->activescbs++;
-              }
-              scbq_insert_head(&p->delayed_scbs[tindex], scb);
-              p->dev_active_cmds[tindex]--;
-              p->activescbs--;
-              scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
-                  
-              if ( !(p->dev_timer_active & (0x01 << tindex)) ) 
-              {
-                p->dev_timer_active |= (0x01 << tindex);
-                if ( p->dev_active_cmds[tindex] )
-                {
-                  p->dev_expires[tindex] = jiffies + HZ;
-                }
-                else
-                {
-                  p->dev_expires[tindex] = jiffies + (HZ / 10);
-                }
-                if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) )
-                {
-                  p->dev_timer.expires = p->dev_expires[tindex];
-                  p->dev_timer_active |= (0x01 << MAX_TARGETS);
-                  add_timer(&p->dev_timer);
-                }
-                else if ( time_after_eq(p->dev_timer.expires,
-                                        p->dev_expires[tindex]) )
-                  mod_timer(&p->dev_timer, p->dev_expires[tindex]);
-              }
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-              if( (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ||
-                  (aic7xxx_verbose > 0xffff) )
-              {
-                if (queue_flag)
-                  printk(INFO_LEAD "Queue full received; queue depth %d, "
-                    "active %d\n", p->host_no, CTL_OF_SCB(scb),
-                    p->dev_max_queue_depth[tindex],
-                    p->dev_active_cmds[tindex]);
-                else
-                  printk(INFO_LEAD "Target busy\n", p->host_no, CTL_OF_SCB(scb));
-
-              }
-#endif
-              if (queue_flag)
-              {
-                if ( p->dev_last_queue_full[tindex] !=
-                     p->dev_active_cmds[tindex] )
-                {
-                  p->dev_last_queue_full[tindex] = 
-                      p->dev_active_cmds[tindex];
-                  p->dev_last_queue_full_count[tindex] = 0;
-                }
-                else
-                {
-                  p->dev_last_queue_full_count[tindex]++;
-                }
-                if ( (p->dev_last_queue_full_count[tindex] > 14) &&
-                     (p->dev_active_cmds[tindex] > 4) )
-                {
-                  if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-                    printk(INFO_LEAD "Queue depth reduced to %d\n", p->host_no,
-                      CTL_OF_SCB(scb), p->dev_active_cmds[tindex]);
-                  p->dev_max_queue_depth[tindex] = 
-                      p->dev_active_cmds[tindex];
-                  p->dev_last_queue_full[tindex] = 0;
-                  p->dev_last_queue_full_count[tindex] = 0;
-                  p->dev_temp_queue_depth[tindex] = 
-                    p->dev_active_cmds[tindex];
-                }
-                else if (p->dev_active_cmds[tindex] == 0)
-                {
-                  if (aic7xxx_verbose & VERBOSE_NEGOTIATION)
-                  {
-                    printk(INFO_LEAD "QUEUE_FULL status received with 0 "
-                           "commands active.\n", p->host_no, CTL_OF_SCB(scb));
-                    printk(INFO_LEAD "Tagged Command Queueing disabled\n",
-                           p->host_no, CTL_OF_SCB(scb));
-                  }
-                  p->dev_max_queue_depth[tindex] = 1;
-                  p->dev_temp_queue_depth[tindex] = 1;
-                  scb->tag_action = 0;
-                  scb->hscb->control &= ~(MSG_ORDERED_Q_TAG|MSG_SIMPLE_Q_TAG);
-                }
-                else
-                {
-                  p->dev_flags[tindex] |= DEVICE_WAS_BUSY;
-                  p->dev_temp_queue_depth[tindex] = 
-                    p->dev_active_cmds[tindex];
-                }
-              }
-              break;
-            }
-            
-            default:
-              if (aic7xxx_verbose & VERBOSE_SEQINT)
-                printk(INFO_LEAD "Unexpected target status 0x%x.\n", p->host_no,
-                     CTL_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 AWAITING_MSG:
-      {
-        unsigned char scb_index, msg_out;
-
-        scb_index = aic_inb(p, SCB_TAG);
-        msg_out = aic_inb(p, MSG_OUT);
-        scb = p->scb_data->scb_array[scb_index];
-        p->msg_index = p->msg_len = 0;
-        /*
-         * 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.
-         */
-
-        if ( !(scb->flags & SCB_DEVICE_RESET) &&
-              (msg_out == MSG_IDENTIFYFLAG) &&
-              (scb->hscb->control & TAG_ENB) )
-        {
-          p->msg_buf[p->msg_index++] = scb->tag_action;
-          p->msg_buf[p->msg_index++] = scb->hscb->tag;
-          p->msg_len += 2;
-        }
-
-        if (scb->flags & SCB_DEVICE_RESET)
-        {
-          p->msg_buf[p->msg_index++] = MSG_BUS_DEV_RESET;
-          p->msg_len++;
-          if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-            printk(INFO_LEAD "Bus device reset mailed.\n",
-                 p->host_no, CTL_OF_SCB(scb));
-        }
-        else if (scb->flags & SCB_ABORT)
-        {
-          if (scb->tag_action)
-          {
-            p->msg_buf[p->msg_index++] = MSG_ABORT_TAG;
-          }
-          else
-          {
-            p->msg_buf[p->msg_index++] = MSG_ABORT;
-          }
-          p->msg_len++;
-          if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
-            printk(INFO_LEAD "Abort message mailed.\n", p->host_no,
-              CTL_OF_SCB(scb));
-        }
-        else if (scb->flags & SCB_MSGOUT_PPR)
-        {
-          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-          {
-            printk(INFO_LEAD "Sending PPR (%d/%d/%d/%d) message.\n",
-                   p->host_no, CTL_OF_SCB(scb),
-                   p->transinfo[tindex].goal_period,
-                   p->transinfo[tindex].goal_offset,
-                   p->transinfo[tindex].goal_width,
-                   p->transinfo[tindex].goal_options);
-          }
-          aic7xxx_construct_ppr(p, scb);
-        }
-        else if (scb->flags & SCB_MSGOUT_WDTR)
-        {
-          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-          {
-            printk(INFO_LEAD "Sending WDTR message.\n", p->host_no,
-                   CTL_OF_SCB(scb));
-          }
-          aic7xxx_construct_wdtr(p, p->transinfo[tindex].goal_width);
-        }
-        else if (scb->flags & SCB_MSGOUT_SDTR)
-        {
-          unsigned int max_sync, period;
-          unsigned char options = 0;
-          /*
-           * Now that the device is selected, use the bits in SBLKCTL and
-           * SSTAT2 to determine the max sync rate for this device.
-           */
-          if (p->features & AHC_ULTRA2)
-          {
-            if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
-                !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
-            {
-              max_sync = AHC_SYNCRATE_ULTRA2;
-            }
-            else
-            {
-              max_sync = AHC_SYNCRATE_ULTRA;
-            }
-          }
-          else if (p->features & AHC_ULTRA)
-          {
-            max_sync = AHC_SYNCRATE_ULTRA;
-          }
-          else
-          {
-            max_sync = AHC_SYNCRATE_FAST;
-          }
-          period = p->transinfo[tindex].goal_period;
-          aic7xxx_find_syncrate(p, &period, max_sync, &options);
-          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-          {
-            printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no,
-                   CTL_OF_SCB(scb), period,
-                   p->transinfo[tindex].goal_offset);
-          }
-          aic7xxx_construct_sdtr(p, period,
-            p->transinfo[tindex].goal_offset);
-        }
-        else 
-        {
-          sti();
-          panic("aic7xxx: AWAITING_MSG for an SCB that does "
-                "not have a waiting message.\n");
-        }
-        /*
-         * We've set everything up to send our message, now to actually do
-         * so we need to enable reqinit interrupts and let the interrupt
-         * handler do the rest.  We don't want to unpause the sequencer yet
-         * though so we'll return early.  We also have to make sure that
-         * we clear the SEQINT *BEFORE* we set the REQINIT handler active
-         * or else it's possible on VLB cards to loose the first REQINIT
-         * interrupt.  Edge triggered EISA cards could also loose this
-         * interrupt, although PCI and level triggered cards should not
-         * have this problem since they continually interrupt the kernel
-         * until we take care of the situation.
-         */
-        scb->flags |= SCB_MSGOUT_SENT;
-        p->msg_index = 0;
-        p->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
-        p->flags |= AHC_HANDLING_REQINITS;
-        aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
-        return;
-      }
-      break;
-
-    case DATA_OVERRUN:
-      {
-        unsigned char scb_index = aic_inb(p, SCB_TAG);
-        unsigned char lastphase = aic_inb(p, LASTPHASE);
-        unsigned int i;
-
-        scb = (p->scb_data->scb_array[scb_index]);
-        /*
-         * 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.
-         *    If we retrieved sense info on this target, then the 
-         *    base SENSE info should have been saved prior to the
-         *    overrun error.  In that case, we return DID_OK and let
-         *    the mid level code pick up on the sense info.  Otherwise
-         *    we return DID_ERROR so the command will get retried.
-         */
-        if ( !(scb->flags & SCB_SENSE) )
-        {
-          printk(WARN_LEAD "Data overrun detected in %s phase, tag %d;\n",
-            p->host_no, CTL_OF_SCB(scb), 
-            (lastphase == P_DATAIN) ? "Data-In" : "Data-Out", scb->hscb->tag);
-          printk(KERN_WARNING "  %s seen Data Phase. Length=%d, NumSGs=%d.\n",
-            (aic_inb(p, SEQ_FLAGS) & DPHASE) ? "Have" : "Haven't",
-            scb->sg_length, scb->sg_count);
-          for (i = 0; i < scb->sg_count; i++)
-          {
-            printk(KERN_WARNING "     sg[%d] - Addr 0x%x : Length %d\n",
-                 i, 
-                 le32_to_cpu(scb->sg_list[i].address),
-                 le32_to_cpu(scb->sg_list[i].length) );
-          }
-          aic7xxx_error(scb->cmd) = DID_ERROR;
-        }
-        else
-          printk(INFO_LEAD "Data Overrun during SEND_SENSE operation.\n",
-            p->host_no, CTL_OF_SCB(scb));
-      }
-      break;
-
-    case WIDE_RESIDUE:
-      {
-        unsigned char resid_sgcnt, index;
-        unsigned char scb_index = aic_inb(p, SCB_TAG);
-        unsigned int cur_addr, resid_dcnt;
-        unsigned int native_addr, native_length;
-        int i;
-
-        if(scb_index > p->scb_data->numscbs)
-        {
-          printk(WARN_LEAD "invalid scb_index during WIDE_RESIDUE.\n",
-            p->host_no, -1, -1, -1);
-          /*
-           * XXX: Add error handling here
-           */
-          break;
-        }
-        scb = p->scb_data->scb_array[scb_index];
-        if(!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
-        {
-          printk(WARN_LEAD "invalid scb during WIDE_RESIDUE flags:0x%x "
-                 "scb->cmd:0x%lx\n", p->host_no, CTL_OF_SCB(scb),
-                 scb->flags, (unsigned long)scb->cmd);
-          break;
-        }
-
-        /*
-         * We have a valid scb to use on this WIDE_RESIDUE message, so
-         * we need to walk the sg list looking for this particular sg
-         * segment, then see if we happen to be at the very beginning of
-         * the segment.  If we are, then we have to back things up to
-         * the previous segment.  If not, then we simply need to remove
-         * one byte from this segments address and add one to the byte
-         * count.
-         */
-        cur_addr = aic_inb(p, SHADDR) | (aic_inb(p, SHADDR + 1) << 8) |
-          (aic_inb(p, SHADDR + 2) << 16) | (aic_inb(p, SHADDR + 3) << 24);
-        resid_sgcnt = aic_inb(p, SCB_RESID_SGCNT);
-        resid_dcnt = aic_inb(p, SCB_RESID_DCNT) |
-          (aic_inb(p, SCB_RESID_DCNT + 1) << 8) |
-          (aic_inb(p, SCB_RESID_DCNT + 2) << 16);
-        index = scb->sg_count - (resid_sgcnt + 1);
-        native_addr = le32_to_cpu(scb->sg_list[index].address);
-        native_length = le32_to_cpu(scb->sg_list[index].length);
-        /*
-         * Make sure this is a valid sg_seg for the given pointer
-         */
-        if(cur_addr < native_addr ||
-           cur_addr > (native_addr + native_length + 1))
-        {
-          printk(WARN_LEAD "invalid cur_addr:0x%x during WIDE_RESIDUE\n",
-                 p->host_no, CTL_OF_SCB(scb), cur_addr);
-          if(index > 0)
-            printk(WARN_LEAD "  sg_address[-1]:0x%x sg_length[-1]:%d\n",
-                   p->host_no, CTL_OF_SCB(scb),
-                   le32_to_cpu(scb->sg_list[index - 1].address),
-                   le32_to_cpu(scb->sg_list[index - 1].length));
-          printk(WARN_LEAD "  sg_address:0x%x sg_length:%d\n",
-                 p->host_no, CTL_OF_SCB(scb),
-                 native_addr, native_length);
-          if(resid_sgcnt > 1)
-            printk(WARN_LEAD "  sg_address[1]:0x%x sg_length[1]:%d\n",
-                   p->host_no, CTL_OF_SCB(scb),
-                   le32_to_cpu(scb->sg_list[index + 1].address),
-                   le32_to_cpu(scb->sg_list[index + 1].length));
-          printk(WARN_LEAD "  cur_address:0x%x resid_dcnt:0x%06x\n",
-                 p->host_no, CTL_OF_SCB(scb),
-                 cur_addr, resid_dcnt);
-          break;
-        }
-
-        if( (resid_sgcnt == 0) &&
-            ((resid_dcnt == 0) || (resid_dcnt == 0xffffff)))
-        {
-          /*
-           * We are at the end of the transfer and this is about a byte
-           * we ignored already (because the sequencer knew this was
-           * the last segment and set the adapter to ignore any wide
-           * residue bytes that might come through, which is only done
-           * on the last scatter gather segment of transfers).
-           */
-          break;
-        }
-        else if(cur_addr == native_addr)
-        {
-          /*
-           * If our current address matches the sg_seg->address then we
-           * have to back up the sg array to the previous segment and set
-           * it up to have only one byte of transfer left to go.
-           */
-          if(index == 0)
-          {
-            printk(WARN_LEAD "bogus WIDE_RESIDUE message, no data has been "
-                   "transferred.\n", p->host_no, CTL_OF_SCB(scb));
-            break;
-          }
-          resid_sgcnt++;
-          index--;
-          cur_addr = le32_to_cpu(scb->sg_list[index].address) + 
-            le32_to_cpu(scb->sg_list[index].length) - 1;
-          native_addr = aic_inb(p, SG_NEXT) | (aic_inb(p, SG_NEXT + 1) << 8)
-            | (aic_inb(p, SG_NEXT + 2) << 16) | (aic_inb(p, SG_NEXT + 3) << 24);
-          native_addr -= SG_SIZEOF;
-          aic_outb(p, resid_sgcnt, SG_COUNT);
-          aic_outb(p, resid_sgcnt, SCB_RESID_SGCNT);
-          aic_outb(p, native_addr & 0xff, SG_NEXT);
-          aic_outb(p, (native_addr >> 8) & 0xff, SG_NEXT + 1);
-          aic_outb(p, (native_addr >> 16) & 0xff, SG_NEXT + 2);
-          aic_outb(p, (native_addr >> 24) & 0xff, SG_NEXT + 3);
-          aic_outb(p, 1, SCB_RESID_DCNT); 
-          aic_outb(p, 0, SCB_RESID_DCNT + 1); 
-          aic_outb(p, 0, SCB_RESID_DCNT + 2); 
-          aic_outb(p, 1, HCNT); 
-          aic_outb(p, 0, HCNT + 1); 
-          aic_outb(p, 0, HCNT + 2); 
-          aic_outb(p, cur_addr & 0xff, HADDR);
-          aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1);
-          aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2);
-          aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3);
-        }
-        else
-        {
-          /*
-           * Back the data pointer up by one and add one to the remaining
-           * byte count.  Then store that in the HCNT and HADDR registers.
-           */
-          cur_addr--;
-          resid_dcnt++;
-          aic_outb(p, resid_dcnt & 0xff, SCB_RESID_DCNT); 
-          aic_outb(p, (resid_dcnt >> 8) & 0xff, SCB_RESID_DCNT + 1); 
-          aic_outb(p, (resid_dcnt >> 16) & 0xff, SCB_RESID_DCNT + 2); 
-          aic_outb(p, resid_dcnt & 0xff, HCNT); 
-          aic_outb(p, (resid_dcnt >> 8) & 0xff, HCNT + 1); 
-          aic_outb(p, (resid_dcnt >> 16) & 0xff, HCNT + 2); 
-          aic_outb(p, cur_addr & 0xff, HADDR);
-          aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1);
-          aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2);
-          aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3);
-        }
-        /*
-         * The sequencer actually wants to find the new address and byte
-         * count in the SHCNT and SHADDR register sets.  These registers
-         * are a shadow of the regular HCNT and HADDR registers.  On the
-         * Ultra2 controllers, these registers are read only and the way
-         * we have to set their values is to put the values we want into
-         * the HCNT and HADDR registers and then output PRELOADEN into
-         * the DFCNTRL register which causes the card to latch the current
-         * values in the HADDR and HCNT registers and drop it through to
-         * the shadow registers.  On older cards we copy them directly
-         * across by hand.
-         */
-        if(p->features & AHC_ULTRA2)
-        {
-          aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL);
-          i=0;
-          udelay(1);
-          while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000))
-          {
-            udelay(1);
-          }
-          aic_outb(p, aic_inb(p, DMAPARAMS) & ~(SCSIEN|HDMAEN), DFCNTRL);
-          i=0;
-          udelay(1);
-          while(((aic_inb(p, DFCNTRL) & (SCSIEN|HDMAEN)) != 0) && (i++ < 1000))
-          {
-            udelay(1);
-          }
-        }
-        else
-        {
-          aic_outb(p, resid_dcnt & 0xff, STCNT); 
-          aic_outb(p, (resid_dcnt >> 8) & 0xff, STCNT + 1); 
-          aic_outb(p, (resid_dcnt >> 16) & 0xff, STCNT + 2); 
-          aic_outb(p, cur_addr & 0xff, SHADDR);
-          aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1);
-          aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2);
-          aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3);
-        }
-      }
-      break;
-
-        
-#if AIC7XXX_NOT_YET 
-    case TRACEPOINT:
-      {
-        printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no,
-               channel, target, lun);
-      }
-      break;
-
-    case TRACEPOINT2:
-      {
-        printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no,
-               channel, target, lun);
-      }
-      break;
-
-    /* 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
-
-    default:                   /* unknown */
-      printk(WARN_LEAD "Unknown SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
-             p->host_no, channel, target, lun, intstat,
-             aic_inb(p, SCSISIGI));
-      break;
-  }
-
-  /*
-   * Clear the sequencer interrupt and unpause the sequencer.
-   */
-  unpause_sequencer(p, /* unpause always */ TRUE);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_parse_msg
- *
- * Description:
- *   Parses incoming messages into actions on behalf of
- *   aic7xxx_handle_reqinit
- *_F*************************************************************************/
-static int
-aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int reject, reply, done;
-  unsigned char target_scsirate, tindex;
-  unsigned short target_mask;
-  unsigned char target, channel, lun;
-
-  target = scb->cmd->target;
-  channel = scb->cmd->channel;
-  lun = scb->cmd->lun;
-  reply = reject = done = FALSE;
-  tindex = TARGET_INDEX(scb->cmd);
-  target_scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
-  target_mask = (0x01 << tindex);
-
-  /*
-   * Parse as much of the message as is availible,
-   * rejecting it if we don't support it.  When
-   * the entire message is availible and has been
-   * handled, return TRUE indicating that we have
-   * parsed an entire message.
-   */
-
-  if (p->msg_buf[0] != MSG_EXTENDED)
-  {
-    reject = TRUE;
-  }
-
-  /*
-   * Just accept the length byte outright and perform
-   * more checking once we know the message type.
-   */
-
-  if ( !reject && (p->msg_len > 2) )
-  {
-    switch(p->msg_buf[2])
-    {
-      case MSG_EXT_SDTR:
-      {
-        unsigned int period, offset;
-        unsigned char maxsync, saved_offset, options;
-        struct aic7xxx_syncrate *syncrate;
-        
-        if (p->msg_buf[1] != MSG_EXT_SDTR_LEN)
-        {
-          reject = TRUE;
-          break;
-        }
-
-        if (p->msg_len < (MSG_EXT_SDTR_LEN + 2))
-        {
-          break;
-        }
-
-        period = p->msg_buf[3];
-        saved_offset = offset = p->msg_buf[4];
-        options = 0;
-
-        /*
-         * Even if we are an Ultra3 card, don't allow Ultra3 sync rates when
-         * using the SDTR messages.  We need the PPR messages to enable the
-         * higher speeds that include things like Dual Edge clocking.
-         */
-        if (p->features & AHC_ULTRA2)
-        {
-          if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
-               !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
-          {
-            maxsync = AHC_SYNCRATE_ULTRA2;
-          }
-          else
-          {
-            maxsync = AHC_SYNCRATE_ULTRA;
-          }
-        }
-        else if (p->features & AHC_ULTRA)
-        {
-          maxsync = AHC_SYNCRATE_ULTRA;
-        }
-        else
-        {
-          maxsync = AHC_SYNCRATE_FAST;
-        }
-        /*
-         * We might have a device that is starting negotiation with us
-         * before we can start up negotiation with it....be prepared to
-         * have a device ask for a higher speed then we want to give it
-         * in that case
-         */
-        if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
-             (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) )
-        {
-          if (!(p->dev_flags[tindex] & DEVICE_SCANNED) &&
-              !(p->needsdtr_copy & target_mask) &&
-               (p->transinfo[tindex].user_offset) )
-          {
-            /*
-             * Not only is the device starting this up, but it also hasn't
-             * been scanned yet, so this would likely be our TUR or our
-             * INQUIRY command at scan time, so we need to use the
-             * settings from the SEEPROM if they existed.  Of course, even
-             * if we didn't find a SEEPROM, we stuffed default values into
-             * the user settings anyway, so use those in all cases.
-             */
-            p->transinfo[tindex].goal_period =
-              p->transinfo[tindex].user_period;
-            if(p->features & AHC_ULTRA2)
-            {
-              p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
-            }
-            else if (p->transinfo[tindex].cur_width)
-            {
-              p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
-            }
-            else
-            {
-              p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
-            }
-            p->needsdtr_copy |= target_mask;
-          }
-          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-          {
-            printk(INFO_LEAD "Received pre-emptive SDTR message from "
-                   "target.\n", p->host_no, CTL_OF_SCB(scb));
-          }
-          if ( !p->transinfo[tindex].goal_offset )
-            period = 255;
-          if ( p->transinfo[tindex].goal_period > period )
-            period = p->transinfo[tindex].goal_period;
-        }
-  
-        syncrate = aic7xxx_find_syncrate(p, &period, maxsync, &options);
-        aic7xxx_validate_offset(p, syncrate, &offset,
-                                target_scsirate & WIDEXFER);
-        aic7xxx_set_syncrate(p, syncrate, target, channel, period,
-                             offset, options, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
-
-        /*
-         * Did we drop to async?  Or are we sending a reply?  If we are,
-         * then we have to make sure that the reply value reflects the proper
-         * settings so we need to set the goal values according to what
-         * we need to send.
-         */
-        if ( (offset != saved_offset) ||
-             ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
-              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) ) )
-        {
-          aic7xxx_set_syncrate(p, syncrate, target, channel, period, offset,
-                               options, AHC_TRANS_GOAL|AHC_TRANS_QUITE);
-        }
-        
-        /*
-         * Did we start this, if not, or if we went to low and had to
-         * go async, then send an SDTR back to the target
-         */
-        p->needsdtr &= ~target_mask;
-        p->dtr_pending &= ~target_mask;
-        if ( ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
-              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) ||
-             (offset != saved_offset) )
-        {
-          reply = TRUE;
-          p->dtr_pending |= target_mask;
-          scb->flags &= ~SCB_MSGOUT_BITS;
-          scb->flags |= SCB_MSGOUT_SDTR;
-          aic_outb(p, HOST_MSG, MSG_OUT);
-          aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
-        }
-        done = TRUE;
-        break;
-      }
-      case MSG_EXT_WDTR:
-      {
-        unsigned char bus_width;
-          
-        if (p->msg_buf[1] != MSG_EXT_WDTR_LEN)
-        {
-          reject = TRUE;
-          break;
-        }
-
-        if (p->msg_len < (MSG_EXT_WDTR_LEN + 2))
-        {
-          break;
-        }
-
-        bus_width = p->msg_buf[3];
-        if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR)) ==
-             (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR) )
-        {
-          switch(bus_width)
-          {
-            default:
-            {
-              reject = TRUE;
-              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
-                   ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) ||
-                    (aic7xxx_verbose > 0xffff)) )
-              {
-                printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
-                  p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
-              }
-            } /* We fall through on purpose */
-            case MSG_EXT_WDTR_BUS_8_BIT:
-            {
-              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
-              p->needwdtr_copy &= ~target_mask;
-              break;
-            }
-            case MSG_EXT_WDTR_BUS_16_BIT:
-            {
-              break;
-            }
-          }
-          p->dtr_pending &= ~target_mask;
-          p->needwdtr &= ~target_mask;
-        }
-        else
-        {
-          if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) )
-          {
-            /* 
-             * Well, we now know the WDTR and SYNC caps of this device since
-             * it contacted us first, mark it as such and copy the user stuff
-             * over to the goal stuff.
-             */
-            p->transinfo[tindex].goal_period =
-              p->transinfo[tindex].user_period;
-            if(p->transinfo[tindex].user_offset)
-            {
-              if(p->features & AHC_ULTRA2)
-              {
-                p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
-              }
-              else if( p->transinfo[tindex].user_width &&
-                       (bus_width == MSG_EXT_WDTR_BUS_16_BIT) &&
-                       p->features & AHC_WIDE )
-              {
-                p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
-              }
-              else
-              {
-                p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
-              }
-            }
-            p->transinfo[tindex].goal_width =
-              p->transinfo[tindex].user_width;
-            p->needwdtr_copy |= target_mask;
-            p->needsdtr_copy |= target_mask;
-          }
-          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-          {
-            printk(INFO_LEAD "Received pre-emptive WDTR message from "
-                   "target.\n", p->host_no, CTL_OF_SCB(scb));
-          }
-          switch(bus_width)
-          {
-            default:
-            {
-              if ( (p->features & AHC_WIDE) &&
-                   (p->transinfo[tindex].goal_width ==
-                    MSG_EXT_WDTR_BUS_16_BIT) )
-              {
-                bus_width = MSG_EXT_WDTR_BUS_16_BIT;
-                break;
-              }
-            } /* Fall through if we aren't a wide card */
-            case MSG_EXT_WDTR_BUS_8_BIT:
-            {
-              p->needwdtr_copy &= ~target_mask;
-              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
-              aic7xxx_set_width(p, target, channel, lun, bus_width,
-                                AHC_TRANS_GOAL|AHC_TRANS_QUITE);
-              break;
-            }
-          }
-          reply = TRUE;
-          scb->flags &= ~SCB_MSGOUT_BITS;
-          scb->flags |= SCB_MSGOUT_WDTR;
-          p->needwdtr &= ~target_mask;
-          p->dtr_pending |= target_mask;
-          aic_outb(p, HOST_MSG, MSG_OUT);
-          aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
-        }
-        aic7xxx_set_width(p, target, channel, lun, bus_width,
-                          AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
-        
-        /*
-         * By virtue of the SCSI spec, a WDTR message negates any existing
-         * SDTR negotiations.  So, even if needsdtr isn't marked for this
-         * device, we still have to do a new SDTR message if the device
-         * supports SDTR at all.  Therefore, we check needsdtr_copy instead
-         * of needstr.
-         */
-        aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
-                             AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
-        p->needsdtr |= (p->needsdtr_copy & target_mask);
-        done = TRUE;
-        break;
-      }
-      case MSG_EXT_PPR:
-      {
-        unsigned char bus_width, trans_options, new_trans_options;
-        unsigned int period, offset;
-        unsigned char maxsync, saved_offset;
-        struct aic7xxx_syncrate *syncrate;
-        
-        if (p->msg_buf[1] != MSG_EXT_PPR_LEN)
-        {
-          reject = TRUE;
-          break;
-        }
-
-        if (p->msg_len < (MSG_EXT_PPR_LEN + 2))
-        {
-          break;
-        }
-
-        period = p->msg_buf[3];
-        offset = saved_offset = p->msg_buf[5];
-        bus_width = p->msg_buf[6];
-        trans_options = new_trans_options = p->msg_buf[7] & 0xf;
-
-        if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-        {
-          printk(INFO_LEAD "Parsing PPR message (%d/%d/%d/%d)\n",
-                 p->host_no, CTL_OF_SCB(scb), period, offset, bus_width,
-                 trans_options);
-        }
-
-        if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
-            !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
-        {
-          if(p->features & AHC_ULTRA3)
-          {
-            maxsync = AHC_SYNCRATE_ULTRA3;
-          }
-          else
-          {
-            maxsync = AHC_SYNCRATE_ULTRA2;
-          }
-        }
-        else
-        {
-          maxsync = AHC_SYNCRATE_ULTRA;
-        }
-        /*
-         * We might have a device that is starting negotiation with us
-         * before we can start up negotiation with it....be prepared to
-         * have a device ask for a higher speed then we want to give it
-         * in that case
-         */
-        if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) !=
-             (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR) )
-        {
-          reply = TRUE;
-          scb->flags &= ~SCB_MSGOUT_BITS;
-          scb->flags |= SCB_MSGOUT_PPR;
-	  p->dev_flags[tindex] |= DEVICE_SCSI_3;
-          if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
-          {
-            /*
-             * Not only is the device starting this up, but it also hasn't
-             * been scanned yet, so this would likely be our TUR or our
-             * INQUIRY command at scan time, so we need to use the
-             * settings from the SEEPROM if they existed.  Of course, even
-             * if we didn't find a SEEPROM, we stuffed default values into
-             * the user settings anyway, so use those in all cases.
-             */
-            p->transinfo[tindex].goal_period =
-              p->transinfo[tindex].user_period;
-            if(p->transinfo[tindex].user_offset)
-            {
-              if(p->features & AHC_ULTRA2)
-              {
-                p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
-              }
-              else if( p->transinfo[tindex].user_width &&
-                       (bus_width == MSG_EXT_WDTR_BUS_16_BIT) &&
-                       p->features & AHC_WIDE )
-              {
-                p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
-              }
-              else
-              {
-                p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
-              }
-            }
-            p->transinfo[tindex].goal_width =
-              p->transinfo[tindex].user_width;
-            p->transinfo[tindex].goal_options =
-              p->transinfo[tindex].user_options;
-          }
-          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-          {
-            printk(INFO_LEAD "Received pre-emptive PPR message from "
-                   "target.\n", p->host_no, CTL_OF_SCB(scb));
-          }
-          if ( !p->transinfo[tindex].goal_offset )
-            period = 255;
-          if ( p->transinfo[tindex].goal_period > period )
-            period = p->transinfo[tindex].goal_period;
-          if ( p->transinfo[tindex].goal_options == 0 )
-            new_trans_options = 0;
-          switch(bus_width)
-          {
-            default:
-            {
-              if ( (p->features & AHC_WIDE) &&
-                   (p->transinfo[tindex].goal_width ==
-                    MSG_EXT_WDTR_BUS_16_BIT) )
-              {
-                bus_width = MSG_EXT_WDTR_BUS_16_BIT;
-                break;
-              }
-            } /* Fall through if we aren't a wide card */
-            case MSG_EXT_WDTR_BUS_8_BIT:
-            {
-              p->needwdtr_copy &= ~target_mask;
-              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
-              aic7xxx_set_width(p, target, channel, lun, bus_width,
-                                AHC_TRANS_GOAL|AHC_TRANS_QUITE);
-              break;
-            }
-          }
-          if ( (p->transinfo[tindex].goal_period > 9) ||
-               (p->transinfo[tindex].goal_options == 0) )
-          {
-            scb->flags &= ~SCB_MSGOUT_BITS;
-            reject = TRUE;
-            reply = FALSE;
-            p->needppr &= ~(1 << tindex);
-            p->needppr_copy &= ~(1 << tindex);
-            if ( p->transinfo[tindex].goal_offset )
-            {
-              p->needsdtr |= (1 << tindex);
-              p->needsdtr_copy |= (1 << tindex);
-            }
-            if ( p->transinfo[tindex].goal_width )
-            {
-              p->needwdtr |= (1 << tindex);
-              p->needwdtr_copy |= (1 << tindex);
-            }
-          }
-        }
-        else
-        {
-          switch(bus_width)
-          {
-            default:
-            {
-              reject = TRUE;
-              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
-                   ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) ||
-                    (aic7xxx_verbose > 0xffff)) )
-              {
-                printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
-                  p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
-              }
-            } /* We fall through on purpose */
-            case MSG_EXT_WDTR_BUS_8_BIT:
-            {
-              /*
-               * According to the spec, if we aren't wide, we also can't be
-               * Dual Edge so clear the options byte
-               */
-              new_trans_options = 0;
-              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
-              break;
-            }
-            case MSG_EXT_WDTR_BUS_16_BIT:
-            {
-              break;
-            }
-          }
-        }
-
-        if ( !reject )
-        {
-          aic7xxx_set_width(p, target, channel, lun, bus_width,
-                            AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
-          syncrate = aic7xxx_find_syncrate(p, &period, maxsync,
-                                           &new_trans_options);
-          aic7xxx_validate_offset(p, syncrate, &offset, bus_width);
-          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
-                               offset, new_trans_options,
-                               AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
-        }
-
-        p->dtr_pending &= ~target_mask;
-        p->needppr &= ~target_mask;
-        if(reply)
-        {
-          p->dtr_pending |= target_mask;
-          scb->flags &= ~SCB_MSGOUT_BITS;
-          scb->flags |= SCB_MSGOUT_PPR;
-          aic_outb(p, HOST_MSG, MSG_OUT);
-          aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
-        }
-        done = TRUE;
-        break;
-      }
-      default:
-      {
-        reject = TRUE;
-        break;
-      }
-    } /* end of switch(p->msg_type) */
-  } /* end of if (!reject && (p->msg_len > 2)) */
-
-  if (!reply && reject)
-  {
-    aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT);
-    aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
-    done = TRUE;
-  }
-  return(done);
-}
-
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_handle_reqinit
- *
- * Description:
- *   Interrupt handler for REQINIT interrupts (used to transfer messages to
- *    and from devices).
- *_F*************************************************************************/
-static void
-aic7xxx_handle_reqinit(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  unsigned char lastbyte;
-  unsigned char phasemis;
-  int done = FALSE;
-
-  switch(p->msg_type)
-  {
-    case MSG_TYPE_INITIATOR_MSGOUT:
-      {
-        if (p->msg_len == 0)
-          panic("aic7xxx: REQINIT with no active message!\n");
-
-        lastbyte = (p->msg_index == (p->msg_len - 1));
-        phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK) != P_MESGOUT;
-
-        if (lastbyte || phasemis)
-        {
-          /* Time to end the message */
-          p->msg_len = 0;
-          p->msg_type = MSG_TYPE_NONE;
-          /*
-           * NOTE-TO-MYSELF: If you clear the REQINIT after you
-           * disable REQINITs, then cases of REJECT_MSG stop working
-           * and hang the bus
-           */
-          aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
-          aic_outb(p, CLRSCSIINT, CLRINT);
-          p->flags &= ~AHC_HANDLING_REQINITS;
-
-          if (phasemis == 0)
-          {
-            aic_outb(p, p->msg_buf[p->msg_index], SINDEX);
-            aic_outb(p, 0, RETURN_1);
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-            if (aic7xxx_verbose > 0xffff)
-              printk(INFO_LEAD "Completed sending of REQINIT message.\n",
-                     p->host_no, CTL_OF_SCB(scb));
-#endif
-          }
-          else
-          {
-            aic_outb(p, MSGOUT_PHASEMIS, RETURN_1);
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-            if (aic7xxx_verbose > 0xffff)
-              printk(INFO_LEAD "PHASEMIS while sending REQINIT message.\n",
-                     p->host_no, CTL_OF_SCB(scb));
-#endif
-          }
-          unpause_sequencer(p, TRUE);
-        }
-        else
-        {
-          /*
-           * Present the byte on the bus (clearing REQINIT) but don't
-           * unpause the sequencer.
-           */
-          aic_outb(p, CLRREQINIT, CLRSINT1);
-          aic_outb(p, CLRSCSIINT, CLRINT);
-          aic_outb(p,  p->msg_buf[p->msg_index++], SCSIDATL);
-        }
-        break;
-      }
-    case MSG_TYPE_INITIATOR_MSGIN:
-      {
-        phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK ) != P_MESGIN;
-
-        if (phasemis == 0)
-        {
-          p->msg_len++;
-          /* Pull the byte in without acking it */
-          p->msg_buf[p->msg_index] = aic_inb(p, SCSIBUSL);
-          done = aic7xxx_parse_msg(p, scb);
-          /* Ack the byte */
-          aic_outb(p, CLRREQINIT, CLRSINT1);
-          aic_outb(p, CLRSCSIINT, CLRINT);
-          aic_inb(p, SCSIDATL);
-          p->msg_index++;
-        }
-        if (phasemis || done)
-        {
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-          if (aic7xxx_verbose > 0xffff)
-          {
-            if (phasemis)
-              printk(INFO_LEAD "PHASEMIS while receiving REQINIT message.\n",
-                     p->host_no, CTL_OF_SCB(scb));
-            else
-              printk(INFO_LEAD "Completed receipt of REQINIT message.\n",
-                     p->host_no, CTL_OF_SCB(scb));
-          }
-#endif
-          /* Time to end our message session */
-          p->msg_len = 0;
-          p->msg_type = MSG_TYPE_NONE;
-          aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
-          aic_outb(p, CLRSCSIINT, CLRINT);
-          p->flags &= ~AHC_HANDLING_REQINITS;
-          unpause_sequencer(p, TRUE);
-        }
-        break;
-      }
-    default:
-      {
-        panic("aic7xxx: Unknown REQINIT message type.\n");
-        break;
-      }
-  } /* End of switch(p->msg_type) */
-}
-
-/*+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 = aic_inb(p, SCB_TAG);
-  status = aic_inb(p, SSTAT1);
-
-  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)
-  {
-    int channel;
-
-    if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
-      channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
-    else
-      channel = 0;
-
-    if (aic7xxx_verbose & VERBOSE_RESET)
-      printk(WARN_LEAD "Someone else reset the channel!!\n",
-           p->host_no, channel, -1, -1);
-    if (aic7xxx_panic_on_abort)
-      aic7xxx_panic_abort(p, NULL);
-    /*
-     * Go through and abort all commands for the channel, but do not
-     * reset the channel again.
-     */
-    aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
-    aic7xxx_run_done_queue(p, TRUE);
-    scb = NULL;
-  }
-  else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
-  {
-    /*
-     * 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 = aic_inb(p, LASTPHASE);
-    unsigned char saved_tcl = aic_inb(p, SAVED_TCL);
-    unsigned char target = (saved_tcl >> 4) & 0x0F;
-    int channel;
-    int printerror = TRUE;
-
-    if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
-      channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
-    else
-      channel = 0;
-
-    aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
-             SCSISEQ);
-    if (lastphase == P_MESGOUT)
-    {
-      unsigned char message;
-
-      message = aic_inb(p, SINDEX);
-
-      if ((message == MSG_ABORT) || (message == MSG_ABORT_TAG))
-      {
-        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
-          printk(INFO_LEAD "SCB %d abort delivered.\n", p->host_no,
-            CTL_OF_SCB(scb), scb->hscb->tag);
-        aic7xxx_reset_device(p, target, channel, ALL_LUNS,
-                (message == MSG_ABORT) ? SCB_LIST_NULL : scb->hscb->tag );
-        aic7xxx_run_done_queue(p, TRUE);
-        scb = NULL;
-        printerror = 0;
-      }
-      else if (message == MSG_BUS_DEV_RESET)
-      {
-        aic7xxx_handle_device_reset(p, target, channel);
-        scb = NULL;
-        printerror = 0;
-      }
-    }
-    if ( (scb != NULL) &&
-         (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)]) )
-    {
-      /*
-       * This might be a SCSI-3 device that is dropping the bus due to
-       * errors and signalling that we should reduce the transfer speed.
-       * All we have to do is complete this command (since it's a negotiation
-       * command already) and the checksum routine should flag an error and
-       * reduce the speed setting and renegotiate.  We call the reset routing
-       * just to clean out the hardware from this scb.
-       */
-      printerror = 0;
-      aic7xxx_reset_device(p, target, channel, ALL_LUNS, scb->hscb->tag);
-      aic7xxx_run_done_queue(p, TRUE);
-      scb = NULL;
-    }
-    if (printerror != 0)
-    {
-      if (scb != NULL)
-      {
-        unsigned char tag;
-
-        if ((scb->hscb->control & TAG_ENB) != 0)
-        {
-          tag = scb->hscb->tag;
-        }
-        else
-        {
-          tag = SCB_LIST_NULL;
-        }
-        aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
-        aic7xxx_run_done_queue(p, TRUE);
-      }
-      else
-      {
-        aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
-        aic7xxx_run_done_queue(p, TRUE);
-      }
-      printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, "
-             "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase,
-             (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
-      scb = NULL;
-    }
-    aic_outb(p, MSG_NOOP, MSG_OUT);
-    aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
-      SIMODE1);
-    p->flags &= ~AHC_HANDLING_REQINITS;
-    aic_outb(p, CLRBUSFREE, CLRSINT1);
-    aic_outb(p, CLRSCSIINT, CLRINT);
-    restart_sequencer(p);
-    unpause_sequencer(p, TRUE);
-  }
-  else if ((status & SELTO) != 0)
-  {
-    unsigned char scbptr;
-    unsigned char nextscb;
-    Scsi_Cmnd *cmd;
-
-    scbptr = aic_inb(p, WAITING_SCBH);
-    if (scbptr > p->scb_data->maxhscbs)
-    {
-      /*
-       * I'm still trying to track down exactly how this happens, but until
-       * I find it, this code will make sure we aren't passing bogus values
-       * into the SCBPTR register, even if that register will just wrap
-       * things around, we still don't like having out of range variables.
-       *
-       * NOTE: Don't check the aic7xxx_verbose variable, I want this message
-       * to always be displayed.
-       */
-      printk(INFO_LEAD "Invalid WAITING_SCBH value %d, improvising.\n",
-             p->host_no, -1, -1, -1, scbptr);
-      if (p->scb_data->maxhscbs > 4)
-        scbptr &= (p->scb_data->maxhscbs - 1);
-      else
-        scbptr &= 0x03;
-    }
-    aic_outb(p, scbptr, SCBPTR);
-    scb_index = aic_inb(p, SCB_TAG);
-
-    scb = NULL;
-    if (scb_index < p->scb_data->numscbs)
-    {
-      scb = p->scb_data->scb_array[scb_index];
-      if ((scb->flags & SCB_ACTIVE) == 0)
-      {
-        scb = NULL;
-      }
-    }
-    if (scb == NULL)
-    {
-      printk(WARN_LEAD "Referenced SCB %d not valid during SELTO.\n",
-             p->host_no, -1, -1, -1, scb_index);
-      printk(KERN_WARNING "        SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x "
-             "SSTAT1 = 0x%x\n", aic_inb(p, SCSISEQ),
-             aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
-             aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
-      if (aic7xxx_panic_on_abort)
-        aic7xxx_panic_abort(p, NULL);
-    }
-    else
-    {
-      cmd = scb->cmd;
-      cmd->result = (DID_TIME_OUT << 16);
-
-      /*
-       * Clear out this hardware SCB
-       */
-      aic_outb(p, 0, SCB_CONTROL);
-
-      /*
-       * Clear out a few values in the card that are in an undetermined
-       * state.
-       */
-      aic_outb(p, MSG_NOOP, MSG_OUT);
-
-      /*
-       * Shift the waiting for selection queue forward
-       */
-      nextscb = aic_inb(p, SCB_NEXT);
-      aic_outb(p, nextscb, WAITING_SCBH);
-
-      /*
-       * Put this SCB back on the free list.
-       */
-      aic7xxx_add_curscb_to_free_list(p);
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-      if (aic7xxx_verbose > 0xffff)
-        printk(INFO_LEAD "Selection Timeout.\n", p->host_no, CTL_OF_SCB(scb));
-#endif
-      if (scb->flags & SCB_QUEUED_ABORT)
-      {
-        /*
-         * We know that this particular SCB had to be the queued abort since
-         * the disconnected SCB would have gotten a reconnect instead.
-         * What we need to do then is to let the command timeout again so
-         * we get a reset since this abort just failed.
-         */
-        cmd->result = 0;
-        scb = NULL;
-      }
-      else if (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)])
-      {
-        /*
-         * Turn off the needsdtr, needwdtr, and needppr bits since this device
-         * doesn't seem to exist.
-         */
-        p->needppr &= ~(0x01 << TARGET_INDEX(scb->cmd));
-        p->needppr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd));
-        p->needsdtr &= ~(0x01 << TARGET_INDEX(scb->cmd));
-        p->needsdtr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd));
-        p->needwdtr &= ~(0x01 << TARGET_INDEX(scb->cmd));
-        p->needwdtr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd));
-      }
-    }
-    /*
-     * Keep the sequencer from trying to restart any selections
-     */
-    aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
-    /*
-     * Make sure the data bits on the bus are released
-     * Don't do this on 7770 chipsets, it makes them give us
-     * a BRKADDRINT and kills the card.
-     */
-    if( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI )
-      aic_outb(p, 0, SCSIBUSL);
-
-    /*
-     * Delay for the selection timeout delay period then stop the selection
-     */
-    udelay(301);
-    aic_outb(p, CLRSELINGO, CLRSINT0);
-    /*
-     * Clear out all the interrupt status bits
-     */
-    aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1);
-    p->flags &= ~AHC_HANDLING_REQINITS;
-    aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1);
-    aic_outb(p, CLRSCSIINT, CLRINT);
-    /*
-     * Restarting the sequencer will stop the selection and make sure devices
-     * are allowed to reselect in.
-     */
-    restart_sequencer(p);
-    unpause_sequencer(p, TRUE);
-  }
-  else if (scb == NULL)
-  {
-    printk(WARN_LEAD "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, -1, -1, -1, status, scb_index, aic_inb(p, SIMODE0),
-           aic_inb(p, SIMODE1), aic_inb(p, SSTAT0),
-           (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
-    /*
-     * Turn off the interrupt and set status to zero, so that it
-     * falls through the rest of the SCSIINT code.
-     */
-    aic_outb(p, status, CLRSINT1);
-    aic_outb(p, CLRSCSIINT, 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 = aic_inb(p, LASTPHASE);
-    unsigned char sstat2 = aic_inb(p, SSTAT2);
-    unsigned char tindex = TARGET_INDEX(scb->cmd);
-
-    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.
-     */
-    if( (p->features & AHC_ULTRA3) && 
-        (aic_inb(p, SCSIRATE) & AHC_SYNCRATE_CRC) &&
-        (lastphase == P_DATAIN) )
-    {
-      printk(WARN_LEAD "CRC error during %s phase.\n",
-             p->host_no, CTL_OF_SCB(scb), phase);
-      if(sstat2 & CRCVALERR)
-      {
-        printk(WARN_LEAD "  CRC error in intermediate CRC packet.\n",
-               p->host_no, CTL_OF_SCB(scb));
-      }
-      if(sstat2 & CRCENDERR)
-      {
-        printk(WARN_LEAD "  CRC error in ending CRC packet.\n",
-               p->host_no, CTL_OF_SCB(scb));
-      }
-      if(sstat2 & CRCREQERR)
-      {
-        printk(WARN_LEAD "  Target incorrectly requested a CRC packet.\n",
-               p->host_no, CTL_OF_SCB(scb));
-      }
-      if(sstat2 & DUAL_EDGE_ERROR)
-      {
-        printk(WARN_LEAD "  Dual Edge transmission error.\n",
-               p->host_no, CTL_OF_SCB(scb));
-      }
-    }
-    else if( (lastphase == P_MESGOUT) &&
-             (cmd == p->dev_dtr_cmnd[tindex]) &&
-             (scb->flags & SCB_MSGOUT_PPR) )
-    {
-      /*
-       * As per the draft specs, any device capable of supporting any of
-       * the option values other than 0 are not allowed to reject the
-       * PPR message.  Instead, they must negotiate out what they do
-       * support instead of rejecting our offering or else they cause
-       * a parity error during msg_out phase to signal that they don't
-       * like our settings.
-       */
-      p->needppr &= ~(1 << tindex);
-      p->needppr_copy &= ~(1 << tindex);
-      aic7xxx_set_width(p, scb->cmd->target, scb->cmd->channel, scb->cmd->lun,
-                        MSG_EXT_WDTR_BUS_8_BIT,
-                        (AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE));
-      aic7xxx_set_syncrate(p, NULL, scb->cmd->target, scb->cmd->channel, 0, 0,
-                           0, AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
-      p->transinfo[tindex].goal_options = 0;
-      p->dtr_pending &= ~(1 << tindex);
-      scb->flags &= ~SCB_MSGOUT_BITS;
-      if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-      {
-        printk(INFO_LEAD "parity error during PPR message, reverting "
-               "to WDTR/SDTR\n", p->host_no, CTL_OF_SCB(scb));
-      }
-      if ( p->transinfo[tindex].goal_width )
-      {
-        p->needwdtr |= (1 << tindex);
-        p->needwdtr_copy |= (1 << tindex);
-      }
-      if ( p->transinfo[tindex].goal_offset )
-      {
-        if( p->transinfo[tindex].goal_period <= 9 )
-        {
-          p->transinfo[tindex].goal_period = 10;
-        }
-        p->needsdtr |= (1 << tindex);
-        p->needsdtr_copy |= (1 << tindex);
-      }
-      scb = NULL;
-    }
-    else if(p->dev_flags[tindex] & DEVICE_PARITY_ERROR)
-    {
-      struct aic7xxx_syncrate *syncrate;
-      unsigned int period = p->transinfo[tindex].cur_period;
-      unsigned char options = p->transinfo[tindex].cur_options;
-      /*
-       * oops, we had a failure, lower the transfer rate and try again.  It's
-       * worth noting here that it might be wise to also check for typical
-       * wide setting on narrow cable type problems and try disabling wide
-       * instead of slowing down if those exist.  That's hard to do with simple
-       * checksums though.
-       */
-      printk(WARN_LEAD "Parity error during %s phase.\n",
-             p->host_no, CTL_OF_SCB(scb), phase);
-      if((syncrate = aic7xxx_find_syncrate(p, &period, 0, &options)) != NULL)
-      {
-        syncrate++;
-        if( (syncrate->rate[0] != NULL) &&
-            (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) )
-        {
-          p->transinfo[tindex].goal_period = syncrate->period;
-          if( p->transinfo[tindex].goal_period > 9 )
-          {
-            p->transinfo[tindex].goal_options = 0;
-            p->needppr &= ~(1<<tindex);
-            p->needsdtr |= (1<<tindex);
-            p->needppr_copy &= ~(1<<tindex);
-            p->needsdtr_copy |= (1<<tindex);
-            if (p->transinfo[tindex].goal_width)
-            {
-              p->needwdtr |= (1<<tindex);
-              p->needwdtr_copy |= (1<<tindex);
-            }
-          }
-        }
-        else if (p->transinfo[tindex].goal_width)
-        {
-          p->transinfo[tindex].goal_width = 0;
-          p->needwdtr &= ~(1<<tindex);
-          p->needwdtr_copy &= ~(1<<tindex);
-          p->transinfo[tindex].goal_offset =
-            p->transinfo[tindex].user_offset;
-          p->transinfo[tindex].goal_period =
-            p->transinfo[tindex].user_period;
-          p->transinfo[tindex].goal_options =
-            p->transinfo[tindex].user_options;
-          if( p->transinfo[tindex].goal_period <= 9 )
-          {
-            p->needppr |= (1<<tindex);
-            p->needsdtr &= ~(1<<tindex);
-            p->needppr_copy |= (1<<tindex);
-            p->needsdtr_copy &= ~(1<<tindex);
-          }
-          else
-          {
-            p->needppr &= ~(1<<tindex);
-            p->needsdtr |= (1<<tindex);
-            p->needppr_copy &= ~(1<<tindex);
-            p->needsdtr_copy |= (1<<tindex);
-          }
-        }
-        else
-        {
-          p->transinfo[tindex].goal_offset = 0;
-          p->transinfo[tindex].goal_period = 255;
-          p->transinfo[tindex].goal_options = 0;
-          p->transinfo[tindex].goal_width = 0;
-          p->needppr &= ~(1<<tindex);
-          p->needsdtr &= ~(1<<tindex);
-          p->needwdtr &= ~(1<<tindex);
-          p->needppr_copy &= ~(1<<tindex);
-          p->needsdtr_copy &= ~(1<<tindex);
-          p->needwdtr_copy &= ~(1<<tindex);
-        }
-      }
-      p->dev_flags[tindex] &= ~DEVICE_PARITY_ERROR;
-    }
-    else
-    {
-      p->dev_flags[tindex] |= DEVICE_PARITY_ERROR;
-    }
-
-    /*
-     * 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)
-    {
-      aic_outb(p, mesg_out, MSG_OUT);
-      aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
-      scb = NULL;
-    }
-    aic_outb(p, CLRSCSIPERR, CLRSINT1);
-    aic_outb(p, CLRSCSIINT, CLRINT);
-    unpause_sequencer(p, /* unpause_always */ TRUE);
-  }
-  else if ( (status & REQINIT) &&
-            (p->flags & AHC_HANDLING_REQINITS) )
-  {
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-    if (aic7xxx_verbose > 0xffff)
-      printk(INFO_LEAD "Handling REQINIT, SSTAT1=0x%x.\n", p->host_no,
-             CTL_OF_SCB(scb), aic_inb(p, SSTAT1));
-#endif
-    aic7xxx_handle_reqinit(p, scb);
-    return;
-  }
-  else
-  {
-    /*
-     * We don't know what's going on. Turn off the
-     * interrupt source and try to continue.
-     */
-    if (aic7xxx_verbose & VERBOSE_SCSIINT)
-      printk(INFO_LEAD "Unknown SCSIINT status, SSTAT1(0x%x).\n",
-        p->host_no, -1, -1, -1, status);
-    aic_outb(p, status, CLRSINT1);
-    aic_outb(p, CLRSCSIINT, CLRINT);
-    unpause_sequencer(p, /* unpause always */ TRUE);
-    scb = NULL;
-  }
-  if (scb != NULL)
-  {
-    aic7xxx_done(p, scb);
-  }
-}
-
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-static void
-aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer)
-{
-  unsigned char saved_scbptr, free_scbh, dis_scbh, wait_scbh, temp;
-  int i, bogus, lost;
-  static unsigned char scb_status[AIC7XXX_MAXSCB];
-
-#define SCB_NO_LIST 0
-#define SCB_FREE_LIST 1
-#define SCB_WAITING_LIST 2
-#define SCB_DISCONNECTED_LIST 4
-#define SCB_CURRENTLY_ACTIVE 8
-
-  /*
-   * Note, these checks will fail on a regular basis once the machine moves
-   * beyond the bus scan phase.  The problem is race conditions concerning
-   * the scbs and where they are linked in.  When you have 30 or so commands
-   * outstanding on the bus, and run this twice with every interrupt, the
-   * chances get pretty good that you'll catch the sequencer with an SCB
-   * only partially linked in.  Therefore, once we pass the scan phase
-   * of the bus, we really should disable this function.
-   */
-  bogus = FALSE;
-  memset(&scb_status[0], 0, sizeof(scb_status));
-  pause_sequencer(p);
-  saved_scbptr = aic_inb(p, SCBPTR);
-  if (saved_scbptr >= p->scb_data->maxhscbs)
-  {
-    printk("Bogus SCBPTR %d\n", saved_scbptr);
-    bogus = TRUE;
-  }
-  scb_status[saved_scbptr] = SCB_CURRENTLY_ACTIVE;
-  free_scbh = aic_inb(p, FREE_SCBH);
-  if ( (free_scbh != SCB_LIST_NULL) &&
-       (free_scbh >= p->scb_data->maxhscbs) )
-  {
-    printk("Bogus FREE_SCBH %d\n", free_scbh);
-    bogus = TRUE;
-  }
-  else
-  {
-    temp = free_scbh;
-    while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
-    {
-      if(scb_status[temp] & 0x07)
-      {
-        printk("HSCB %d on multiple lists, status 0x%02x", temp,
-               scb_status[temp] | SCB_FREE_LIST);
-        bogus = TRUE;
-      }
-      scb_status[temp] |= SCB_FREE_LIST;
-      aic_outb(p, temp, SCBPTR);
-      temp = aic_inb(p, SCB_NEXT);
-    }
-  }
-
-  dis_scbh = aic_inb(p, DISCONNECTED_SCBH);
-  if ( (dis_scbh != SCB_LIST_NULL) &&
-       (dis_scbh >= p->scb_data->maxhscbs) )
-  {
-    printk("Bogus DISCONNECTED_SCBH %d\n", dis_scbh);
-    bogus = TRUE;
-  }
-  else
-  {
-    temp = dis_scbh;
-    while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
-    {
-      if(scb_status[temp] & 0x07)
-      {
-        printk("HSCB %d on multiple lists, status 0x%02x", temp,
-               scb_status[temp] | SCB_DISCONNECTED_LIST);
-        bogus = TRUE;
-      }
-      scb_status[temp] |= SCB_DISCONNECTED_LIST;
-      aic_outb(p, temp, SCBPTR);
-      temp = aic_inb(p, SCB_NEXT);
-    }
-  }
-  
-  wait_scbh = aic_inb(p, WAITING_SCBH);
-  if ( (wait_scbh != SCB_LIST_NULL) &&
-       (wait_scbh >= p->scb_data->maxhscbs) )
-  {
-    printk("Bogus WAITING_SCBH %d\n", wait_scbh);
-    bogus = TRUE;
-  }
-  else
-  {
-    temp = wait_scbh;
-    while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
-    {
-      if(scb_status[temp] & 0x07)
-      {
-        printk("HSCB %d on multiple lists, status 0x%02x", temp,
-               scb_status[temp] | SCB_WAITING_LIST);
-        bogus = TRUE;
-      }
-      scb_status[temp] |= SCB_WAITING_LIST;
-      aic_outb(p, temp, SCBPTR);
-      temp = aic_inb(p, SCB_NEXT);
-    }
-  }
-
-  lost=0;
-  for(i=0; i < p->scb_data->maxhscbs; i++)
-  {
-    aic_outb(p, i, SCBPTR);
-    temp = aic_inb(p, SCB_NEXT);
-    if ( ((temp != SCB_LIST_NULL) &&
-          (temp >= p->scb_data->maxhscbs)) )
-    {
-      printk("HSCB %d bad, SCB_NEXT invalid(%d).\n", i, temp);
-      bogus = TRUE;
-    }
-    if ( temp == i )
-    {
-      printk("HSCB %d bad, SCB_NEXT points to self.\n", i);
-      bogus = TRUE;
-    }
-    if (scb_status[i] == 0)
-      lost++;
-    if (lost > 1)
-    {
-      printk("Too many lost scbs.\n");
-      bogus=TRUE;
-    }
-  }
-  aic_outb(p, saved_scbptr, SCBPTR);
-  unpause_sequencer(p, FALSE);
-  if (bogus)
-  {
-    printk("Bogus parameters found in card SCB array structures.\n");
-    printk("%s\n", buffer);
-    aic7xxx_panic_abort(p, NULL);
-  }
-  return;
-}
-#endif
-
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_handle_command_completion_intr
- *
- * Description:
- *   SCSI command completion interrupt handler.
- *-F*************************************************************************/
-static void
-aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p)
-{
-  struct aic7xxx_scb *scb = NULL;
-  Scsi_Cmnd *cmd;
-  unsigned char scb_index, tindex;
-
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-  if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) )
-    printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1);
-#endif
-    
-  /*
-   * Read the INTSTAT location after clearing the CMDINT bit.  This forces
-   * any posted PCI writes to flush to memory.  Gerard Roudier suggested
-   * this fix to the possible race of clearing the CMDINT bit but not
-   * having all command bytes flushed onto the qoutfifo.
-   */
-  aic_outb(p, CLRCMDINT, CLRINT);
-  aic_inb(p, INTSTAT);
-  /*
-   * The sequencer will continue running when it
-   * issues this interrupt. There may be >1 commands
-   * finished, so loop until we've processed them all.
-   */
-
-  while (p->qoutfifo[p->qoutfifonext] != SCB_LIST_NULL)
-  {
-    scb_index = p->qoutfifo[p->qoutfifonext];
-    p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL;
-    if ( scb_index >= p->scb_data->numscbs )
-    {
-      printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no,
-        -1, -1, -1, scb_index);
-      continue;
-    }
-    scb = p->scb_data->scb_array[scb_index];
-    if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
-    {
-      printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags "
-        "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags,
-        (unsigned long) scb->cmd);
-      continue;
-    }
-    tindex = TARGET_INDEX(scb->cmd);
-    if (scb->flags & SCB_QUEUED_ABORT)
-    {
-      pause_sequencer(p);
-      if ( ((aic_inb(p, LASTPHASE) & PHASE_MASK) != P_BUSFREE) &&
-           (aic_inb(p, SCB_TAG) == scb->hscb->tag) )
-      {
-        unpause_sequencer(p, FALSE);
-        continue;
-      }
-      aic7xxx_reset_device(p, scb->cmd->target, scb->cmd->channel,
-        scb->cmd->lun, scb->hscb->tag);
-      scb->flags &= ~(SCB_QUEUED_FOR_DONE | SCB_RESET | SCB_ABORT |
-        SCB_QUEUED_ABORT);
-      unpause_sequencer(p, FALSE);
-    }
-    else if (scb->flags & SCB_ABORT)
-    {
-      /*
-       * We started to abort this, but it completed on us, let it
-       * through as successful
-       */
-      scb->flags &= ~(SCB_ABORT|SCB_RESET);
-    }
-    else if (scb->flags & SCB_SENSE)
-    {
-      char *buffer = &scb->cmd->sense_buffer[0];
-      if (scb->cmd == p->dev_dtr_cmnd[tindex])
-      {
-        struct aic7xxx_scb *old_scb;
-        /*
-         * We have valid sense data, send it back immediately.
-         */
-        old_scb = p->scb_data->scb_array[scb->cmd->next->tag];
-        *old_scb->cmd->sense_buffer = *scb->cmd->sense_buffer;
-        old_scb->hscb->target_status = scb->hscb->target_status;
-        old_scb->cmd->result = scb->hscb->target_status;
-        old_scb->cmd->result |= (DID_ERROR << 16);
-        aic7xxx_status(old_scb->cmd) = scb->hscb->target_status;
-        scbq_remove(&p->waiting_scbs, old_scb);
-        scbq_remove(&p->delayed_scbs[tindex], old_scb);
-        scb->cmd->next = NULL;
-	aic7xxx_done(p, scb);
-        aic7xxx_done(p, old_scb);
-	continue;
-      } 
-      else if (buffer[12] == 0x47 || buffer[12] == 0x54)
-      {
-        /*
-         * SCSI errors, run domain validation and re-run negotiation
-         */
-        p->needdv |= (1<<tindex);
-        /*
-         * Signal that we need to re-negotiate things, this also gets us our
-         * INQUIRY command to re-checksum off of.
-         */
-        p->needppr |= (p->needppr_copy & (1<<tindex));
-        p->needsdtr |= (p->needsdtr_copy & (1<<tindex));
-        p->needwdtr |= (p->needwdtr_copy & (1<<tindex));
-      }
-    }
-    switch (status_byte(scb->hscb->target_status))
-    {
-      case QUEUE_FULL:
-      case BUSY:
-        scb->hscb->target_status = 0;
-        scb->cmd->result = 0;
-        aic7xxx_error(scb->cmd) = DID_OK;
-        break;
-      default:
-        cmd = scb->cmd;
-        if (scb->hscb->residual_SG_segment_count != 0)
-        {
-          aic7xxx_calculate_residual(p, scb);
-        }
-        cmd->result |= (aic7xxx_error(cmd) << 16);
-        aic7xxx_done(p, scb);
-        break;
-    }      
-  }
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_isr
- *
- * Description:
- *   SCSI controller interrupt handler.
- *-F*************************************************************************/
-static void
-aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
-{
-  struct aic7xxx_host *p;
-  unsigned char intstat;
-
-  p = (struct aic7xxx_host *)dev_id;
-
-  /*
-   * Just a few sanity checks.  Make sure that we have an int pending.
-   * Also, if PCI, then we are going to check for a PCI bus error status
-   * should we get too many spurious interrupts.
-   */
-  if (!((intstat = aic_inb(p, INTSTAT)) & INT_PEND))
-  {
-#ifdef CONFIG_PCI
-    if ( (p->chip & AHC_PCI) && (p->spurious_int > 500) &&
-        !(p->flags & AHC_HANDLING_REQINITS) )
-    {
-      if ( aic_inb(p, ERROR) & PCIERRSTAT )
-      {
-        aic7xxx_pci_intr(p);
-      }
-      p->spurious_int = 0;
-    }
-    else if ( !(p->flags & AHC_HANDLING_REQINITS) )
-    {
-      p->spurious_int++;
-    }
-#endif
-    return;
-  }
-
-  p->spurious_int = 0;
-
-  /*
-   * Keep track of interrupts for /proc/scsi
-   */
-  p->isr_count++;
-
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-  if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
-       (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
-    aic7xxx_check_scbs(p, "Bogus settings at start of interrupt.");
-#endif
-
-  /*
-   * Handle all the interrupt sources - especially for SCSI
-   * interrupts, we won't get a second chance at them.
-   */
-  if (intstat & CMDCMPLT)
-  {
-    aic7xxx_handle_command_completion_intr(p);
-  }
-
-  if (intstat & BRKADRINT)
-  {
-    int i;
-    unsigned char errno = aic_inb(p, 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)
-      {
-        printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
-      }
-    }
-    printk(KERN_ERR "(scsi%d)   SEQADDR=0x%x\n", p->host_no,
-      (((aic_inb(p, SEQADDR1) << 8) & 0x100) | aic_inb(p, SEQADDR0)));
-    if (aic7xxx_panic_on_abort)
-      aic7xxx_panic_abort(p, NULL);
-#ifdef CONFIG_PCI
-    if (errno & PCIERRSTAT)
-      aic7xxx_pci_intr(p);
-#endif
-    if (errno & (SQPARERR | ILLOPCODE | ILLSADDR))
-    {
-      sti();
-      panic("aic7xxx: unrecoverable BRKADRINT.\n");
-    }
-    if (errno & ILLHADDR)
-    {
-      printk(KERN_ERR "(scsi%d) BUG! Driver accessed chip without first "
-             "pausing controller!\n", p->host_no);
-    }
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-    if (errno & DPARERR)
-    {
-      if (aic_inb(p, DMAPARAMS) & DIRECTION)
-        printk("(scsi%d) while DMAing SCB from host to card.\n", p->host_no);
-      else
-        printk("(scsi%d) while DMAing SCB from card to host.\n", p->host_no);
-    }
-#endif
-    aic_outb(p, CLRPARERR | CLRBRKADRINT, CLRINT);
-    unpause_sequencer(p, FALSE);
-  }
-
-  if (intstat & SEQINT)
-  {
-    /*
-     * Read the CCSCBCTL register to work around a bug in the Ultra2 cards
-     */
-    if(p->features & AHC_ULTRA2)
-    {
-      aic_inb(p, CCSCBCTL);
-    }
-    aic7xxx_handle_seqint(p, intstat);
-  }
-
-  if (intstat & SCSIINT)
-  {
-    aic7xxx_handle_scsiint(p, intstat);
-  }
-
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-  if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
-       (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
-    aic7xxx_check_scbs(p, "Bogus settings at end of interrupt.");
-#endif
-
-}
-
-/*+F*************************************************************************
- * Function:
- *   do_aic7xxx_isr
- *
- * Description:
- *   This is a gross hack to solve a problem in linux kernels 2.1.85 and
- *   above.  Please, children, do not try this at home, and if you ever see
- *   anything like it, please inform the Gross Hack Police immediately
- *-F*************************************************************************/
-static void
-do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
-{
-  unsigned long cpu_flags;
-  struct aic7xxx_host *p;
-  
-  p = (struct aic7xxx_host *)dev_id;
-  if(!p)
-    return;
-  spin_lock_irqsave(&io_request_lock, cpu_flags);
-  if(test_and_set_bit(AHC_IN_ISR_BIT, (void *)&p->flags))
-  {
-    spin_unlock_irqrestore(&io_request_lock, cpu_flags);
-    return;
-  }
-  do
-  {
-    aic7xxx_isr(irq, dev_id, regs);
-  } while ( (aic_inb(p, INTSTAT) & INT_PEND) );
-  aic7xxx_done_cmds_complete(p);
-  aic7xxx_run_waiting_queues(p);
-  clear_bit(AHC_IN_ISR_BIT, (void *)&p->flags);
-  spin_unlock_irqrestore(&io_request_lock, cpu_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_DEVICE 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 int
-aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
-{
-  int default_depth = 3;
-  unsigned char tindex;
-  unsigned short target_mask;
-
-  tindex = device->id | (device->channel << 3);
-  target_mask = (1 << tindex);
-
-  if (p->dev_max_queue_depth[tindex] > 1)
-  {
-    /*
-     * We've already scanned this device, leave it alone
-     */
-    return(p->dev_max_queue_depth[tindex]);
-  }
-
-  device->queue_depth = default_depth;
-  p->dev_temp_queue_depth[tindex] = 1;
-  p->dev_max_queue_depth[tindex] = 1;
-  p->tagenable &= ~target_mask;
-
-  if (device->tagged_supported)
-  {
-    int tag_enabled = TRUE;
-
-    default_depth = AIC7XXX_CMDS_PER_DEVICE;
- 
-    if (!(p->discenable & target_mask))
-    {
-      if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-        printk(INFO_LEAD "Disconnection disabled, unable to "
-             "enable tagged queueing.\n",
-             p->host_no, device->channel, device->id, device->lun);
-    }
-    else
-    {
-      if (p->instance >= NUMBER(aic7xxx_tag_info))
-      {
-        static int print_warning = TRUE;
-        if(print_warning)
-        {
-          printk(KERN_INFO "aic7xxx: WARNING, insufficient tag_info instances for"
-                           " installed controllers.\n");
-          printk(KERN_INFO "aic7xxx: Please update the aic7xxx_tag_info array in"
-                           " the aic7xxx.c source file.\n");
-          print_warning = FALSE;
-        }
-        device->queue_depth = default_depth;
-      }
-      else
-      {
-
-        if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 255)
-        {
-          tag_enabled = FALSE;
-          device->queue_depth = 3;  /* Tagged queueing is disabled. */
-        }
-        else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
-        {
-          device->queue_depth = default_depth;
-        }
-        else
-        {
-          device->queue_depth =
-            aic7xxx_tag_info[p->instance].tag_commands[tindex];
-        }
-      }
-      if ((device->tagged_queue == 0) && tag_enabled)
-      {
-        if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
-        {
-              printk(INFO_LEAD "Enabled tagged queuing, queue depth %d.\n",
-                p->host_no, device->channel, device->id,
-                device->lun, device->queue_depth);
-        }
-        p->dev_max_queue_depth[tindex] = device->queue_depth;
-        p->dev_temp_queue_depth[tindex] = device->queue_depth;
-        p->tagenable |= target_mask;
-        p->orderedtag |= target_mask;
-        device->tagged_queue = 1;
-        device->current_tag = SCB_LIST_NULL;
-      }
-    }
-  }
-  return(p->dev_max_queue_depth[tindex]);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_select_queue_depth
- *
- * Description:
- *   Sets the queue depth for each SCSI device hanging off the input
- *   host adapter.  We use a queue depth of 2 for devices that do not
- *   support tagged queueing.  If AIC7XXX_CMDS_PER_LUN is defined, we
- *   use that for tagged queueing devices; otherwise we use our own
- *   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,
-    Scsi_Device *scsi_devs)
-{
-  Scsi_Device *device;
-  struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
-  int scbnum;
-
-  scbnum = 0;
-  for (device = scsi_devs; device != NULL; device = device->next)
-  {
-    if (device->host == host)
-    {
-      scbnum += aic7xxx_device_queue_depth(p, device);
-    }
-  }
-  while (scbnum > p->scb_data->numscbs)
-  {
-    /*
-     * Pre-allocate the needed SCBs to get around the possibility of having
-     * to allocate some when memory is more or less exhausted and we need
-     * the SCB in order to perform a swap operation (possible deadlock)
-     */
-    if ( aic7xxx_allocate_scb(p) == 0 )
-      return;
-  }
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_probe
- *
- * Description:
- *   Probing for EISA boards: it looks like the first two bytes
- *   are a manufacturer code - three characters, five bits each:
- *
- *               BYTE 0   BYTE 1   BYTE 2   BYTE 3
- *              ?1111122 22233333 PPPPPPPP RRRRRRRR
- *
- *   The characters are baselined off ASCII '@', so add that value
- *   to each to get the real ASCII code for it. The next two bytes
- *   appear to be a product and revision number, probably vendor-
- *   specific. This is what is being searched for at each port,
- *   and what should probably correspond to the ID= field in the
- *   ECU's .cfg file for the card - if your card is not detected,
- *   make sure your signature is listed in the array.
- *
- *   The fourth byte's lowest bit seems to be an enabled/disabled
- *   flag (rest of the bits are reserved?).
- *
- * NOTE:  This function is only needed on Intel and Alpha platforms,
- *   the other platforms we support don't have EISA/VLB busses.  So,
- *   we #ifdef this entire function to avoid compiler warnings about
- *   an unused function.
- *-F*************************************************************************/
-#if defined(__i386__) || defined(__alpha__)
-static int
-aic7xxx_probe(int slot, int base, ahc_flag_type *flags)
-{
-  int i;
-  unsigned char buf[4];
-
-  static struct {
-    int n;
-    unsigned char signature[sizeof(buf)];
-    ahc_chip type;
-    int bios_disabled;
-  } AIC7xxx[] = {
-    { 4, { 0x04, 0x90, 0x77, 0x70 },
-      AHC_AIC7770|AHC_EISA, FALSE },  /* mb 7770  */
-    { 4, { 0x04, 0x90, 0x77, 0x71 },
-      AHC_AIC7770|AHC_EISA, FALSE }, /* host adapter 274x */
-    { 4, { 0x04, 0x90, 0x77, 0x56 },
-      AHC_AIC7770|AHC_VL, FALSE }, /* 284x BIOS enabled */
-    { 4, { 0x04, 0x90, 0x77, 0x57 },
-      AHC_AIC7770|AHC_VL, TRUE }   /* 284x BIOS disabled */
-  };
-
-  /*
-   * The VL-bus cards need to be primed by
-   * writing before a signature check.
-   */
-  for (i = 0; i < sizeof(buf); i++)
-  {
-    outb(0x80 + i, base);
-    buf[i] = inb(base + i);
-  }
-
-  for (i = 0; i < NUMBER(AIC7xxx); i++)
-  {
-    /*
-     * Signature match on enabled card?
-     */
-    if (!memcmp(buf, AIC7xxx[i].signature, AIC7xxx[i].n))
-    {
-      if (inb(base + 4) & 1)
-      {
-        if (AIC7xxx[i].bios_disabled)
-        {
-          *flags |= AHC_USEDEFAULTS;
-        }
-        else
-        {
-          *flags |= AHC_BIOS_ENABLED;
-        }
-        return (i);
-      }
-
-      printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
-             "disabled at slot %d, ignored.\n", slot);
-    }
-  }
-
-  return (-1);
-}
-#endif /* (__i386__) || (__alpha__) */
-
-
-/*+F*************************************************************************
- * Function:
- *   read_2840_seeprom
- *
- * Description:
- *   Reads the 2840 serial EEPROM and returns 1 if successful and 0 if
- *   not successful.
- *
- *   See read_seeprom (for the 2940) for the instruction set of the 93C46
- *   chip.
- *
- *   The 2840 interface to the 93C46 serial EEPROM is through the
- *   STATUS_2840 and SEECTL_2840 registers.  The CS_2840, CK_2840, and
- *   DO_2840 bits of the SEECTL_2840 register are connected to the chip
- *   select, clock, and data out lines respectively of the serial EEPROM.
- *   The DI_2840 bit of the STATUS_2840 is connected to the data in line
- *   of the serial EEPROM.  The EEPROM_TF bit of STATUS_2840 register is
- *   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_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
-{
-  int i = 0, k = 0;
-  unsigned char temp;
-  unsigned short checksum = 0;
-  unsigned short *seeprom = (unsigned short *) sc;
-  struct seeprom_cmd {
-    unsigned char len;
-    unsigned char bits[3];
-  };
-  struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
-
-#define CLOCK_PULSE(p) \
-  while ((aic_inb(p, STATUS_2840) & EEPROM_TF) == 0)        \
-  {                                                \
-    ;  /* Do nothing */                                \
-  }                                                \
-  (void) aic_inb(p, SEECTL_2840);
-
-  /*
-   * Read the first 32 registers of the seeprom.  For the 2840,
-   * 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.
-   */
-  for (k = 0; k < (sizeof(*sc) / 2); k++)
-  {
-    /*
-     * Send chip select for one clock cycle.
-     */
-    aic_outb(p, CK_2840 | CS_2840, SEECTL_2840);
-    CLOCK_PULSE(p);
-
-    /*
-     * Now we're ready to send the read command followed by the
-     * address of the 16-bit register we want to read.
-     */
-    for (i = 0; i < seeprom_read.len; i++)
-    {
-      temp = CS_2840 | seeprom_read.bits[i];
-      aic_outb(p, temp, SEECTL_2840);
-      CLOCK_PULSE(p);
-      temp = temp ^ CK_2840;
-      aic_outb(p, temp, SEECTL_2840);
-      CLOCK_PULSE(p);
-    }
-    /*
-     * Send the 6 bit address (MSB first, LSB last).
-     */
-    for (i = 5; i >= 0; i--)
-    {
-      temp = k;
-      temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
-      temp = CS_2840 | temp;
-      aic_outb(p, temp, SEECTL_2840);
-      CLOCK_PULSE(p);
-      temp = temp ^ CK_2840;
-      aic_outb(p, temp, SEECTL_2840);
-      CLOCK_PULSE(p);
-    }
-
-    /*
-     * Now read the 16 bit register.  An initial 0 precedes the
-     * register contents which begins with bit 15 (MSB) and ends
-     * with bit 0 (LSB).  The initial 0 will be shifted off the
-     * top of our word as we let the loop run from 0 to 16.
-     */
-    for (i = 0; i <= 16; i++)
-    {
-      temp = CS_2840;
-      aic_outb(p, temp, SEECTL_2840);
-      CLOCK_PULSE(p);
-      temp = temp ^ CK_2840;
-      seeprom[k] = (seeprom[k] << 1) | (aic_inb(p, STATUS_2840) & DI_2840);
-      aic_outb(p, temp, SEECTL_2840);
-      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.
-     */
-    if (k < (sizeof(*sc) / 2) - 1)
-    {
-      checksum = checksum + seeprom[k];
-    }
-
-    /*
-     * Reset the chip select for the next command cycle.
-     */
-    aic_outb(p, 0, SEECTL_2840);
-    CLOCK_PULSE(p);
-    aic_outb(p, CK_2840, SEECTL_2840);
-    CLOCK_PULSE(p);
-    aic_outb(p, 0, SEECTL_2840);
-    CLOCK_PULSE(p);
-  }
-
-#if 0
-  printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
-  printk("Serial EEPROM:");
-  for (k = 0; k < (sizeof(*sc) / 2); k++)
-  {
-    if (((k % 8) == 0) && (k != 0))
-    {
-      printk("\n              ");
-    }
-    printk(" 0x%x", seeprom[k]);
-  }
-  printk("\n");
-#endif
-
-  if (checksum != sc->checksum)
-  {
-    printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
-    return (0);
-  }
-
-  return (1);
-#undef CLOCK_PULSE
-}
-
-#define CLOCK_PULSE(p)                                               \
-  do {                                                               \
-    int limit = 0;                                                   \
-    do {                                                             \
-      mb();                                                          \
-      pause_sequencer(p);  /* This is just to generate some PCI */   \
-                           /* traffic so the PCI read is flushed */  \
-                           /* it shouldn't be needed, but some */    \
-                           /* chipsets do indeed appear to need */   \
-                           /* something to force PCI reads to get */ \
-                           /* flushed */                             \
-      udelay(1);           /* Do nothing */                          \
-    } while (((aic_inb(p, SEECTL) & SEERDY) == 0) && (++limit < 1000)); \
-  } while(0)
-
-/*+F*************************************************************************
- * Function:
- *   acquire_seeprom
- *
- * Description:
- *   Acquires access to the memory port on PCI controllers.
- *-F*************************************************************************/
-static int
-acquire_seeprom(struct aic7xxx_host *p)
-{
-
-  /*
-   * 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.
-   */
-  aic_outb(p, SEEMS, SEECTL);
-  CLOCK_PULSE(p);
-  if ((aic_inb(p, SEECTL) & SEERDY) == 0)
-  {
-    aic_outb(p, 0, SEECTL);
-    return (0);
-  }
-  return (1);
-}
-
-/*+F*************************************************************************
- * Function:
- *   release_seeprom
- *
- * Description:
- *   Releases access to the memory port on PCI controllers.
- *-F*************************************************************************/
-static void
-release_seeprom(struct aic7xxx_host *p)
-{
-  /*
-   * Make sure the SEEPROM is ready before we release it.
-   */
-  CLOCK_PULSE(p);
-  aic_outb(p, 0, SEECTL);
-}
-
-/*+F*************************************************************************
- * Function:
- *   read_seeprom
- *
- * Description:
- *   Reads the serial EEPROM and returns 1 if successful and 0 if
- *   not successful.
- *
- *   The instruction set of the 93C46/56/66 chips is as follows:
- *
- *               Start  OP
- *     Function   Bit  Code  Address    Data     Description
- *     -------------------------------------------------------------------
- *     READ        1    10   A5 - A0             Reads data stored in memory,
- *                                               starting at specified address
- *     EWEN        1    00   11XXXX              Write enable must precede
- *                                               all programming modes
- *     ERASE       1    11   A5 - A0             Erase register A5A4A3A2A1A0
- *     WRITE       1    01   A5 - A0   D15 - D0  Writes register
- *     ERAL        1    00   10XXXX              Erase all registers
- *     WRAL        1    00   01XXXX    D15 - D0  Writes to all registers
- *     EWDS        1    00   00XXXX              Disables all programming
- *                                               instructions
- *     *Note: A value of X for address is a don't care condition.
- *     *Note: The 93C56 and 93C66 have 8 address bits.
- * 
- *
- *   The 93C46 has a four wire interface: clock, chip select, data in, and
- *   data out.  In order to perform one of the above functions, you need
- *   to enable the chip select for a clock period (typically a minimum of
- *   1 usec, with the clock high and low a minimum of 750 and 250 nsec
- *   respectively.  While the chip select remains high, you can clock in
- *   the instructions (above) starting with the start bit, followed by the
- *   OP code, Address, and Data (if needed).  For the READ instruction, the
- *   requested 16-bit register contents is read from the data out line but
- *   is preceded by an initial zero (leading 0, followed by 16-bits, MSB
- *   first).  The clock cycling from low to high initiates the next data
- *   bit to be sent from the chip.
- *
- *   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,
- *   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.
- *-F*************************************************************************/
-static int
-read_seeprom(struct aic7xxx_host *p, int offset, 
-    unsigned short *scarray, unsigned int len, seeprom_chip_type chip)
-{
-  int i = 0, k;
-  unsigned char temp;
-  unsigned short checksum = 0;
-  struct seeprom_cmd {
-    unsigned char len;
-    unsigned char bits[3];
-  };
-  struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
-
-  /*
-   * Request access of the memory port.
-   */
-  if (acquire_seeprom(p) == 0)
-  {
-    return (0);
-  }
-
-  /*
-   * 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 < len; k++)
-  {
-    /*
-     * Send chip select for one clock cycle.
-     */
-    aic_outb(p, SEEMS | SEECK | SEECS, SEECTL);
-    CLOCK_PULSE(p);
-
-    /*
-     * Now we're ready to send the read command followed by the
-     * address of the 16-bit register we want to read.
-     */
-    for (i = 0; i < seeprom_read.len; i++)
-    {
-      temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
-      aic_outb(p, temp, SEECTL);
-      CLOCK_PULSE(p);
-      temp = temp ^ SEECK;
-      aic_outb(p, temp, SEECTL);
-      CLOCK_PULSE(p);
-    }
-    /*
-     * 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);
-      aic_outb(p, temp, SEECTL);
-      CLOCK_PULSE(p);
-      temp = temp ^ SEECK;
-      aic_outb(p, temp, SEECTL);
-      CLOCK_PULSE(p);
-    }
-
-    /*
-     * Now read the 16 bit register.  An initial 0 precedes the
-     * register contents which begins with bit 15 (MSB) and ends
-     * with bit 0 (LSB).  The initial 0 will be shifted off the
-     * top of our word as we let the loop run from 0 to 16.
-     */
-    for (i = 0; i <= 16; i++)
-    {
-      temp = SEEMS | SEECS;
-      aic_outb(p, temp, SEECTL);
-      CLOCK_PULSE(p);
-      temp = temp ^ SEECK;
-      scarray[k] = (scarray[k] << 1) | (aic_inb(p, SEECTL) & SEEDI);
-      aic_outb(p, temp, SEECTL);
-      CLOCK_PULSE(p);
-    }
-
-    /*
-     * 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 < (len - 1))
-    {
-      checksum = checksum + scarray[k];
-    }
-
-    /*
-     * Reset the chip select for the next command cycle.
-     */
-    aic_outb(p, SEEMS, SEECTL);
-    CLOCK_PULSE(p);
-    aic_outb(p, SEEMS | SEECK, SEECTL);
-    CLOCK_PULSE(p);
-    aic_outb(p, SEEMS, SEECTL);
-    CLOCK_PULSE(p);
-  }
-
-  /*
-   * Release access to the memory port and the serial EEPROM.
-   */
-  release_seeprom(p);
-
-#if 0
-  printk("Computed checksum 0x%x, checksum read 0x%x\n",
-         checksum, scarray[len - 1]);
-  printk("Serial EEPROM:");
-  for (k = 0; k < len; k++)
-  {
-    if (((k % 8) == 0) && (k != 0))
-    {
-      printk("\n              ");
-    }
-    printk(" 0x%x", scarray[k]);
-  }
-  printk("\n");
-#endif
-  if ( (checksum != scarray[len - 1]) || (checksum == 0) )
-  {
-    return (0);
-  }
-
-  return (1);
-}
-
-/*+F*************************************************************************
- * Function:
- *   read_brdctl
- *
- * Description:
- *   Reads the BRDCTL register.
- *-F*************************************************************************/
-static unsigned char
-read_brdctl(struct aic7xxx_host *p)
-{
-  unsigned char brdctl, value;
-
-  /*
-   * Make sure the SEEPROM is ready before we access it
-   */
-  CLOCK_PULSE(p);
-  if (p->features & AHC_ULTRA2)
-  {
-    brdctl = BRDRW_ULTRA2;
-    aic_outb(p, brdctl, BRDCTL);
-    CLOCK_PULSE(p);
-    value = aic_inb(p, BRDCTL);
-    CLOCK_PULSE(p);
-    return(value);
-  }
-  brdctl = BRDRW;
-  if ( !((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) ||
-        (p->flags & AHC_CHNLB) )
-  {
-    brdctl |= BRDCS;
-  }
-  aic_outb(p, brdctl, BRDCTL);
-  CLOCK_PULSE(p);
-  value = aic_inb(p, BRDCTL);
-  CLOCK_PULSE(p);
-  aic_outb(p, 0, BRDCTL);
-  CLOCK_PULSE(p);
-  return (value);
-}
-
-/*+F*************************************************************************
- * Function:
- *   write_brdctl
- *
- * Description:
- *   Writes a value to the BRDCTL register.
- *-F*************************************************************************/
-static void
-write_brdctl(struct aic7xxx_host *p, unsigned char value)
-{
-  unsigned char brdctl;
-
-  /*
-   * Make sure the SEEPROM is ready before we access it
-   */
-  CLOCK_PULSE(p);
-  if (p->features & AHC_ULTRA2)
-  {
-    brdctl = value;
-    aic_outb(p, brdctl, BRDCTL);
-    CLOCK_PULSE(p);
-    brdctl |= BRDSTB_ULTRA2;
-    aic_outb(p, brdctl, BRDCTL);
-    CLOCK_PULSE(p);
-    brdctl &= ~BRDSTB_ULTRA2;
-    aic_outb(p, brdctl, BRDCTL);
-    CLOCK_PULSE(p);
-    read_brdctl(p);
-    CLOCK_PULSE(p);
-  }
-  else
-  {
-    brdctl = BRDSTB;
-    if ( !((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) ||
-          (p->flags & AHC_CHNLB) )
-    {
-      brdctl |= BRDCS;
-    }
-    brdctl = BRDSTB | BRDCS;
-    aic_outb(p, brdctl, BRDCTL);
-    CLOCK_PULSE(p);
-    brdctl |= value;
-    aic_outb(p, brdctl, BRDCTL);
-    CLOCK_PULSE(p);
-    brdctl &= ~BRDSTB;
-    aic_outb(p, brdctl, BRDCTL);
-    CLOCK_PULSE(p);
-    brdctl &= ~BRDCS;
-    aic_outb(p, brdctl, BRDCTL);
-    CLOCK_PULSE(p);
-  }
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic785x_cable_detect
- *
- * Description:
- *   Detect the cables that are present on aic785x class controller chips
- *-F*************************************************************************/
-static void
-aic785x_cable_detect(struct aic7xxx_host *p, int *int_50,
-    int *ext_present, int *eeprom)
-{
-  unsigned char brdctl;
-
-  aic_outb(p, BRDRW | BRDCS, BRDCTL);
-  CLOCK_PULSE(p);
-  aic_outb(p, 0, BRDCTL);
-  CLOCK_PULSE(p);
-  brdctl = aic_inb(p, BRDCTL);
-  CLOCK_PULSE(p);
-  *int_50 = !(brdctl & BRDDAT5);
-  *ext_present = !(brdctl & BRDDAT6);
-  *eeprom = (aic_inb(p, SPIOCAP) & EEPROM);
-}
-
-#undef CLOCK_PULSE
-
-/*+F*************************************************************************
- * Function:
- *   aic2940_uwpro_cable_detect
- *
- * Description:
- *   Detect the cables that are present on the 2940-UWPro cards
- *
- * NOTE: This function assumes the SEEPROM will have already been acquired
- *       prior to invocation of this function.
- *-F*************************************************************************/
-static void
-aic2940_uwpro_wide_cable_detect(struct aic7xxx_host *p, int *int_68,
-    int *ext_68, int *eeprom)
-{
-  unsigned char brdctl;
-
-  /*
-   * 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.
-   */
-  write_brdctl(p, 0);
-
-  /*
-   * Now we read the state of the internal 68 connector.  BRDDAT6
-   * is don't care, BRDDAT7 is internal 68.  The cable is
-   * present if the bit is 0
-   */
-  brdctl = read_brdctl(p);
-  *int_68 = !(brdctl & BRDDAT7);
-
-  /*
-   * Set the bank bit in brdctl and then read the external cable state
-   * and the EEPROM status
-   */
-  write_brdctl(p, BRDDAT5);
-  brdctl = read_brdctl(p);
-
-  *ext_68 = !(brdctl & BRDDAT6);
-  *eeprom = !(brdctl & BRDDAT7);
-
-  /*
-   * We're done, the calling function will release the SEEPROM for us
-   */
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic787x_cable_detect
- *
- * Description:
- *   Detect the cables that are present on aic787x class controller chips
- *
- * NOTE: This function assumes the SEEPROM will have already been acquired
- *       prior to invocation of this function.
- *-F*************************************************************************/
-static void
-aic787x_cable_detect(struct aic7xxx_host *p, int *int_50, int *int_68,
-    int *ext_present, int *eeprom)
-{
-  unsigned char brdctl;
-
-  /*
-   * 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.
-   */
-  write_brdctl(p, 0);
-
-  /*
-   * Now we read the state of the two internal connectors.  BRDDAT6
-   * is internal 50, BRDDAT7 is internal 68.  For each, the cable is
-   * present if the bit is 0
-   */
-  brdctl = read_brdctl(p);
-  *int_50 = !(brdctl & BRDDAT6);
-  *int_68 = !(brdctl & BRDDAT7);
-
-  /*
-   * Set the bank bit in brdctl and then read the external cable state
-   * and the EEPROM status
-   */
-  write_brdctl(p, BRDDAT5);
-  brdctl = read_brdctl(p);
-
-  *ext_present = !(brdctl & BRDDAT6);
-  *eeprom = !(brdctl & BRDDAT7);
-
-  /*
-   * We're done, the calling function will release the SEEPROM for us
-   */
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic787x_ultra2_term_detect
- *
- * Description:
- *   Detect the termination settings present on ultra2 class controllers
- *
- * NOTE: This function assumes the SEEPROM will have already been acquired
- *       prior to invocation of this function.
- *-F*************************************************************************/
-static void
-aic7xxx_ultra2_term_detect(struct aic7xxx_host *p, int *enableSE_low,
-                           int *enableSE_high, int *enableLVD_low,
-                           int *enableLVD_high, int *eprom_present)
-{
-  unsigned char brdctl;
-
-  brdctl = read_brdctl(p);
-
-  *eprom_present  = (brdctl & BRDDAT7);
-  *enableSE_high  = (brdctl & BRDDAT6);
-  *enableSE_low   = (brdctl & BRDDAT5);
-  *enableLVD_high = (brdctl & BRDDAT4);
-  *enableLVD_low  = (brdctl & BRDDAT3);
-}
-
-/*+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)
-{
-  int internal50_present = 0;
-  int internal68_present = 0;
-  int external_present = 0;
-  int eprom_present = 0;
-  int enableSE_low = 0;
-  int enableSE_high = 0;
-  int enableLVD_low = 0;
-  int enableLVD_high = 0;
-  unsigned char brddat = 0;
-  unsigned char max_target = 0;
-  unsigned char sxfrctl1 = aic_inb(p, SXFRCTL1);
-
-  if (acquire_seeprom(p))
-  {
-    if (p->features & (AHC_WIDE|AHC_TWIN))
-      max_target = 16;
-    else
-      max_target = 8;
-    aic_outb(p, SEEMS | SEECS, SEECTL);
-    sxfrctl1 &= ~STPWEN;
-    /*
-     * The termination/cable detection logic is split into three distinct
-     * groups.  Ultra2 and later controllers, 2940UW-Pro controllers, and
-     * older 7850, 7860, 7870, 7880, and 7895 controllers.  Each has its
-     * own unique way of detecting their cables and writing the results
-     * back to the card.
-     */
-    if (p->features & AHC_ULTRA2)
-    {
-      /*
-       * As long as user hasn't overridden term settings, always check the
-       * cable detection logic
-       */
-      if (aic7xxx_override_term == -1)
-      {
-        aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high,
-                                   &enableLVD_low, &enableLVD_high,
-                                   &eprom_present);
-      }
-      
-      /*
-       * If the user is overriding settings, then they have been preserved
-       * to here as fake adapter_control entries.  Parse them and allow
-       * them to override the detected settings (if we even did detection).
-       */
-      if (!(p->adapter_control & CFSEAUTOTERM))
-      {
-        enableSE_low = (p->adapter_control & CFSTERM);
-        enableSE_high = (p->adapter_control & CFWSTERM);
-      }
-      if (!(p->adapter_control & CFAUTOTERM))
-      {
-        enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM);
-      }
-
-      /*
-       * Now take those settings that we have and translate them into the
-       * values that must be written into the registers.
-       *
-       * Flash Enable = BRDDAT7
-       * Secondary High Term Enable = BRDDAT6
-       * Secondary Low Term Enable = BRDDAT5
-       * LVD/Primary High Term Enable = BRDDAT4
-       * LVD/Primary Low Term Enable = STPWEN bit in SXFRCTL1
-       */
-      if (enableLVD_low != 0)
-      {
-        sxfrctl1 |= STPWEN;
-        p->flags |= AHC_TERM_ENB_LVD;
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk(KERN_INFO "(scsi%d) LVD/Primary Low byte termination "
-                 "Enabled\n", p->host_no);
-      }
-          
-      if (enableLVD_high != 0)
-      {
-        brddat |= BRDDAT4;
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk(KERN_INFO "(scsi%d) LVD/Primary High byte termination "
-                 "Enabled\n", p->host_no);
-      }
-
-      if (enableSE_low != 0)
-      {
-        brddat |= BRDDAT5;
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk(KERN_INFO "(scsi%d) Secondary Low byte termination "
-                 "Enabled\n", p->host_no);
-      }
-
-      if (enableSE_high != 0)
-      {
-        brddat |= BRDDAT6;
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk(KERN_INFO "(scsi%d) Secondary High byte termination "
-                 "Enabled\n", p->host_no);
-      }
-    }
-    else if (p->features & AHC_NEW_AUTOTERM)
-    {
-      /*
-       * The 50 pin connector termination is controlled by STPWEN in the
-       * SXFRCTL1 register.  Since the Adaptec docs typically say the
-       * controller is not allowed to be in the middle of a cable and
-       * this is the only connection on that stub of the bus, there is
-       * no need to even check for narrow termination, it's simply
-       * always on.
-       */
-      sxfrctl1 |= STPWEN;
-      if (aic7xxx_verbose & VERBOSE_PROBE2)
-        printk(KERN_INFO "(scsi%d) Narrow channel termination Enabled\n",
-               p->host_no);
-
-      if (p->adapter_control & CFAUTOTERM)
-      {
-        aic2940_uwpro_wide_cable_detect(p, &internal68_present,
-                                        &external_present,
-                                        &eprom_present);
-        printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, "
-               "Ext-68 %s)\n", p->host_no,
-               "Don't Care",
-               internal68_present ? "YES" : "NO",
-               external_present ? "YES" : "NO");
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no,
-               eprom_present ? "is" : "is not");
-        if (internal68_present && external_present)
-        {
-          brddat = 0;
-          p->flags &= ~AHC_TERM_ENB_SE_HIGH;
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk(KERN_INFO "(scsi%d) Wide channel termination Disabled\n",
-                   p->host_no);
-        }
-        else
-        {
-          brddat = BRDDAT6;
-          p->flags |= AHC_TERM_ENB_SE_HIGH;
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk(KERN_INFO "(scsi%d) Wide channel termination Enabled\n",
-                   p->host_no);
-        }
-      }
-      else
-      {
-        /*
-         * The termination of the Wide channel is done more like normal
-         * though, and the setting of this termination is done by writing
-         * either a 0 or 1 to BRDDAT6 of the BRDDAT register
-         */
-        if (p->adapter_control & CFWSTERM)
-        {
-          brddat = BRDDAT6;
-          p->flags |= AHC_TERM_ENB_SE_HIGH;
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk(KERN_INFO "(scsi%d) Wide channel termination Enabled\n",
-                   p->host_no);
-        }
-        else
-        {
-          brddat = 0;
-        }
-      }
-    }
-    else
-    {
-      if (p->adapter_control & CFAUTOTERM)
-      {
-        if (p->flags & AHC_MOTHERBOARD)
-        {
-          printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n",
-                 p->host_no);
-          printk(KERN_INFO "(scsi%d) Please verify driver detected settings "
-            "are correct.\n", p->host_no);
-          printk(KERN_INFO "(scsi%d) If not, then please properly set the "
-            "device termination\n", p->host_no);
-          printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting "
-            "CTRL-A when prompted\n", p->host_no);
-          printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no);
-        }
-        /* Configure auto termination. */
-
-        if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 )
-        {
-          aic787x_cable_detect(p, &internal50_present, &internal68_present,
-            &external_present, &eprom_present);
-        }
-        else
-        {
-          aic785x_cable_detect(p, &internal50_present, &external_present,
-            &eprom_present);
-        }
-
-        if (max_target <= 8)
-          internal68_present = 0;
-
-        if (max_target > 8)
-        {
-          printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, "
-                 "Ext-68 %s)\n", p->host_no,
-                 internal50_present ? "YES" : "NO",
-                 internal68_present ? "YES" : "NO",
-                 external_present ? "YES" : "NO");
-        }
-        else
-        {
-          printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Ext-50 %s)\n",
-                 p->host_no,
-                 internal50_present ? "YES" : "NO",
-                 external_present ? "YES" : "NO");
-        }
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no,
-               eprom_present ? "is" : "is not");
-
-        /*
-         * Now set the termination based on what we found.  BRDDAT6
-         * controls wide termination enable.
-         * Flash Enable = BRDDAT7
-         * SE High Term Enable = BRDDAT6
-         */
-        if (internal50_present && internal68_present && external_present)
-        {
-          printk(KERN_INFO "(scsi%d) Illegal cable configuration!!  Only two\n",
-                 p->host_no);
-          printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be "
-                 "in use at a time!\n", p->host_no);
-          /*
-           * Force termination (low and high byte) on.  This is safer than
-           * leaving it completely off, especially since this message comes
-           * most often from motherboard controllers that don't even have 3
-           * connectors, but instead are failing the cable detection.
-           */
-          internal50_present = external_present = 0;
-          enableSE_high = enableSE_low = 1;
-        }
-
-        if ((max_target > 8) &&
-            ((external_present == 0) || (internal68_present == 0)) )
-        {
-          brddat |= BRDDAT6;
-          p->flags |= AHC_TERM_ENB_SE_HIGH;
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
-                   p->host_no);
-        }
-
-        if ( ((internal50_present ? 1 : 0) +
-              (internal68_present ? 1 : 0) +
-              (external_present   ? 1 : 0)) <= 1 )
-        {
-          sxfrctl1 |= STPWEN;
-          p->flags |= AHC_TERM_ENB_SE_LOW;
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
-                   p->host_no);
-        }
-      }
-      else /* p->adapter_control & CFAUTOTERM */
-      {
-        if (p->adapter_control & CFSTERM)
-        {
-          sxfrctl1 |= STPWEN;
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
-                   p->host_no);
-        }
-
-        if (p->adapter_control & CFWSTERM)
-        {
-          brddat |= BRDDAT6;
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
-                   p->host_no);
-        }
-      }
-    }
-
-    aic_outb(p, sxfrctl1, SXFRCTL1);
-    write_brdctl(p, brddat);
-    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;
-
-  /*
-   * 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.
-     */
-    aic_outb(p, 0, FREE_SCBH);
-
-    for (i = 0; i < AIC7XXX_MAXSCB; i++)
-    {
-      aic_outb(p, i, SCBPTR);
-      aic_outb(p, i, SCB_CONTROL);
-      if (aic_inb(p, SCB_CONTROL) != i)
-        break;
-      aic_outb(p, 0, SCBPTR);
-      if (aic_inb(p, SCB_CONTROL) != 0)
-        break;
-
-      aic_outb(p, i, SCBPTR);
-      aic_outb(p, 0, SCB_CONTROL);   /* Clear the control byte. */
-      aic_outb(p, i + 1, SCB_NEXT);  /* Set the next pointer. */
-      aic_outb(p, SCB_LIST_NULL, SCB_TAG);  /* Make the tag invalid. */
-      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS);  /* no busy untagged */
-      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+1);/* targets active yet */
-      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+2);
-      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+3);
-    }
-
-    /* Make sure the last SCB terminates the free list. */
-    aic_outb(p, i - 1, SCBPTR);
-    aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
-
-    /* Ensure we clear the first (0) SCBs control byte. */
-    aic_outb(p, 0, SCBPTR);
-    aic_outb(p, 0, SCB_CONTROL);
-
-    p->scb_data->maxhscbs = i;
-    /*
-     * Use direct indexing instead for speed
-     */
-    if ( i == AIC7XXX_MAXSCB )
-      p->flags &= ~AHC_PAGESCBS;
-  }
-
-}
-
-/*+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 reset_delay)
-{
-  int i, result;
-  int max_targets;
-  int found = 1;
-  unsigned char term, scsi_conf;
-  struct Scsi_Host *host;
-
-  host = p->host;
-
-  p->scb_data->maxscbs = AIC7XXX_MAXSCB;
-  host->can_queue = AIC7XXX_MAXSCB;
-  host->cmd_per_lun = 3;
-  host->sg_tablesize = AIC7XXX_MAX_SG;
-  host->select_queue_depths = aic7xxx_select_queue_depth;
-  host->this_id = p->scsi_id;
-  host->io_port = p->base;
-  host->n_io_port = 0xFF;
-  host->base = p->mbase;
-  host->irq = p->irq;
-  if (p->features & AHC_WIDE)
-  {
-    host->max_id = 16;
-  }
-  if (p->features & AHC_TWIN)
-  {
-    host->max_channel = 1;
-  }
-
-  p->host = host;
-  p->host_no = host->host_no;
-  host->unique_id = p->instance;
-  p->isr_count = 0;
-  p->next = NULL;
-  p->completeq.head = NULL;
-  p->completeq.tail = NULL;
-  scbq_init(&p->scb_data->free_scbs);
-  scbq_init(&p->waiting_scbs);
-  init_timer(&p->dev_timer);
-  p->dev_timer.data = (unsigned long)p;
-  p->dev_timer.function = (void *)aic7xxx_timer;
-  p->dev_timer_active = 0;
-
-  /*
-   * We currently have no commands of any type
-   */
-  p->qinfifonext = 0;
-  p->qoutfifonext = 0;
-
-  for (i = 0; i < MAX_TARGETS; i++)
-  {
-    p->dev_commands_sent[i] = 0;
-    p->dev_flags[i] = 0;
-    p->dev_active_cmds[i] = 0;
-    p->dev_last_queue_full[i] = 0;
-    p->dev_last_queue_full_count[i] = 0;
-    p->dev_max_queue_depth[i] = 1;
-    p->dev_temp_queue_depth[i] = 1;
-    p->dev_expires[i] = 0;
-    scbq_init(&p->delayed_scbs[i]);
-  }
-
-  printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no,
-    board_names[p->board_name_index]);
-  switch(p->chip)
-  {
-    case (AHC_AIC7770|AHC_EISA):
-      printk("EISA slot %d\n", p->pci_device_fn);
-      break;
-    case (AHC_AIC7770|AHC_VL):
-      printk("VLB slot %d\n", p->pci_device_fn);
-      break;
-    default:
-      printk("PCI %d/%d/%d\n", p->pci_bus, PCI_SLOT(p->pci_device_fn),
-        PCI_FUNC(p->pci_device_fn));
-      break;
-  }
-  if (p->features & AHC_TWIN)
-  {
-    printk(KERN_INFO "(scsi%d) Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
-           p->host_no, p->scsi_id, p->scsi_id_b);
-  }
-  else
-  {
-    char *channel;
-
-    channel = "";
-
-    if ((p->flags & AHC_MULTI_CHANNEL) != 0)
-    {
-      channel = " A";
-
-      if ( (p->flags & (AHC_CHNLB|AHC_CHNLC)) != 0 )
-      {
-        channel = (p->flags & AHC_CHNLB) ? " B" : " C";
-      }
-    }
-    if (p->features & AHC_WIDE)
-    {
-      printk(KERN_INFO "(scsi%d) Wide ", p->host_no);
-    }
-    else
-    {
-      printk(KERN_INFO "(scsi%d) Narrow ", p->host_no);
-    }
-    printk("Channel%s, SCSI ID=%d, ", channel, p->scsi_id);
-  }
-  aic_outb(p, 0, SEQ_FLAGS);
-
-  detect_maxscb(p);
-
-  printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs);
-  if (aic7xxx_verbose & VERBOSE_PROBE2)
-  {
-    printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n",
-      p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis",
-      p->base, p->irq);
-    printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n",
-      p->host_no, p->mbase, (unsigned long)p->maddr);
-  }
-
-#ifdef CONFIG_PCI
-  /*
-   * Now that we know our instance number, we can set the flags we need to
-   * force termination if need be.
-   */
-  if (aic7xxx_stpwlev != -1)
-  {
-    /*
-     * This option only applies to PCI controllers.
-     */
-    if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
-    {
-      unsigned char devconfig;
-
-      pci_read_config_byte(p->pdev, DEVCONFIG, &devconfig);
-      if ( (aic7xxx_stpwlev >> p->instance) & 0x01 )
-      {
-        devconfig |= STPWLEVEL;
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk("(scsi%d) Force setting STPWLEVEL bit\n", p->host_no);
-      }
-      else
-      {
-        devconfig &= ~STPWLEVEL;
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk("(scsi%d) Force clearing STPWLEVEL bit\n", p->host_no);
-      }
-      pci_write_config_byte(p->pdev, DEVCONFIG, devconfig);
-    }
-  }
-#endif
-
-  /*
-   * That took care of devconfig and stpwlev, now for the actual termination
-   * settings.
-   */
-  if (aic7xxx_override_term != -1)
-  {
-    /*
-     * Again, this only applies to PCI controllers.  We don't have problems
-     * with the termination on 274x controllers to the best of my knowledge.
-     */
-    if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
-    {
-      unsigned char term_override;
-
-      term_override = ( (aic7xxx_override_term >> (p->instance * 4)) & 0x0f);
-      p->adapter_control &= 
-        ~(CFSTERM|CFWSTERM|CFLVDSTERM|CFAUTOTERM|CFSEAUTOTERM);
-      if ( (p->features & AHC_ULTRA2) && (term_override & 0x0c) )
-      {
-        p->adapter_control |= CFLVDSTERM;
-      }
-      if (term_override & 0x02)
-      {
-        p->adapter_control |= CFWSTERM;
-      }
-      if (term_override & 0x01)
-      {
-        p->adapter_control |= CFSTERM;
-      }
-    }
-  }
-
-  if ( (p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1) )
-  {
-    if (p->features & AHC_SPIOCAP)
-    {
-      if ( aic_inb(p, SPIOCAP) & SSPIOCPS )
-      /*
-       * Update the settings in sxfrctl1 to match the termination
-       * settings.
-       */
-        configure_termination(p);
-    }
-    else if ((p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870)
-    {
-      configure_termination(p);
-    }
-  }
-
-  /*
-   * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
-   */
-  if (p->features & AHC_TWIN)
-  {
-    /* Select channel B */
-    aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
-
-    if ((p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1))
-      term = (aic_inb(p, SXFRCTL1) & STPWEN);
-    else
-      term = ((p->flags & AHC_TERM_ENB_B) ? STPWEN : 0);
-
-    aic_outb(p, p->scsi_id_b, SCSIID);
-    scsi_conf = aic_inb(p, SCSICONF + 1);
-    aic_outb(p, DFON | SPIOEN, SXFRCTL0);
-    aic_outb(p, (scsi_conf & ENSPCHK) | aic7xxx_seltime | term | 
-         ENSTIMER | ACTNEGEN, SXFRCTL1);
-    aic_outb(p, 0, SIMODE0);
-    aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
-    aic_outb(p, 0, SCSIRATE);
-
-    /* Select channel A */
-    aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
-  }
-
-  if (p->features & AHC_ULTRA2)
-  {
-    aic_outb(p, p->scsi_id, SCSIID_ULTRA2);
-  }
-  else
-  {
-    aic_outb(p, p->scsi_id, SCSIID);
-  }
-  if ((p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1))
-    term = (aic_inb(p, SXFRCTL1) & STPWEN);
-  else
-    term = ((p->flags & (AHC_TERM_ENB_A|AHC_TERM_ENB_LVD)) ? STPWEN : 0);
-  scsi_conf = aic_inb(p, SCSICONF);
-  aic_outb(p, DFON | SPIOEN, SXFRCTL0);
-  aic_outb(p, (scsi_conf & ENSPCHK) | aic7xxx_seltime | term | 
-       ENSTIMER | ACTNEGEN, SXFRCTL1);
-  aic_outb(p, 0, SIMODE0);
-  /*
-   * If we are a cardbus adapter then don't enable SCSI reset detection.
-   * We shouldn't likely be sharing SCSI busses with someone else, and
-   * if we don't have a cable currently plugged into the controller then
-   * we won't have a power source for the SCSI termination, which means
-   * we'll see infinite incoming bus resets.
-   */
-  if(p->flags & AHC_NO_STPWEN)
-    aic_outb(p, ENSELTIMO | ENSCSIPERR, SIMODE1);
-  else
-    aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
-  aic_outb(p, 0, SCSIRATE);
-  if ( p->features & AHC_ULTRA2)
-    aic_outb(p, 0, SCSIOFFSET);
-
-  /*
-   * Look at the information that board initialization or the board
-   * BIOS has left us. In the lower four bits of each target's
-   * scratch space any value other than 0 indicates that we should
-   * initiate synchronous transfers. If it's zero, the user or the
-   * BIOS has decided to disable synchronous negotiation to that
-   * target so we don't activate the needsdtr flag.
-   */
-  if ((p->features & (AHC_TWIN|AHC_WIDE)) == 0)
-  {
-    max_targets = 8;
-  }
-  else
-  {
-    max_targets = 16;
-  }
-
-  if (!(aic7xxx_no_reset))
-  {
-    /*
-     * If we reset the bus, then clear the transfer settings, else leave
-     * them be
-     */
-    for (i = 0; i < max_targets; i++)
-    {
-      aic_outb(p, 0, TARG_SCSIRATE + i);
-      if (p->features & AHC_ULTRA2)
-      {
-        aic_outb(p, 0, TARG_OFFSET + i);
-      }
-      p->transinfo[i].cur_offset = 0;
-      p->transinfo[i].cur_period = 0;
-      p->transinfo[i].cur_width = MSG_EXT_WDTR_BUS_8_BIT;
-    }
-
-    /*
-     * If we reset the bus, then clear the transfer settings, else leave
-     * them be.
-     */
-    aic_outb(p, 0, ULTRA_ENB);
-    aic_outb(p, 0, ULTRA_ENB + 1);
-    p->ultraenb = 0;
-  }
-
-  /*
-   * 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.
-   */
-  {
-    size_t array_size;
-    unsigned int hscb_physaddr;
-
-    array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb);
-    if (p->scb_data->hscbs == NULL)
-    {
-      /* pci_alloc_consistent enforces the alignment already and
-       * clears the area as well.
-       */
-      p->scb_data->hscbs = pci_alloc_consistent(p->pdev, array_size,
-						&p->scb_data->hscbs_dma);
-      /* We have to use pci_free_consistent, not kfree */
-      p->scb_data->hscb_kmalloc_ptr = NULL;
-      p->scb_data->hscbs_dma_len = array_size;
-    }
-    if (p->scb_data->hscbs == NULL)
-    {
-      printk("(scsi%d) Unable to allocate hardware SCB array; "
-             "failing detection.\n", p->host_no);
-      aic_outb(p, 0, SIMODE1);
-      p->irq = 0;
-      return(0);
-    }
-
-    hscb_physaddr = p->scb_data->hscbs_dma;
-    aic_outb(p, hscb_physaddr & 0xFF, HSCB_ADDR);
-    aic_outb(p, (hscb_physaddr >> 8) & 0xFF, HSCB_ADDR + 1);
-    aic_outb(p, (hscb_physaddr >> 16) & 0xFF, HSCB_ADDR + 2);
-    aic_outb(p, (hscb_physaddr >> 24) & 0xFF, HSCB_ADDR + 3);
-
-    /* Set up the fifo areas at the same time */
-    p->untagged_scbs = pci_alloc_consistent(p->pdev, 3*256, &p->fifo_dma);
-    if (p->untagged_scbs == NULL)
-    {
-      printk("(scsi%d) Unable to allocate hardware FIFO arrays; "
-             "failing detection.\n", p->host_no);
-      p->irq = 0;
-      return(0);
-    }
-
-    p->qoutfifo = p->untagged_scbs + 256;
-    p->qinfifo = p->qoutfifo + 256;
-    for (i = 0; i < 256; i++)
-    {
-      p->untagged_scbs[i] = SCB_LIST_NULL;
-      p->qinfifo[i] = SCB_LIST_NULL;
-      p->qoutfifo[i] = SCB_LIST_NULL;
-    }
-
-    hscb_physaddr = p->fifo_dma;
-    aic_outb(p, hscb_physaddr & 0xFF, SCBID_ADDR);
-    aic_outb(p, (hscb_physaddr >> 8) & 0xFF, SCBID_ADDR + 1);
-    aic_outb(p, (hscb_physaddr >> 16) & 0xFF, SCBID_ADDR + 2);
-    aic_outb(p, (hscb_physaddr >> 24) & 0xFF, SCBID_ADDR + 3);
-  }
-
-  /* The Q-FIFOs we just set up are all empty */
-  aic_outb(p, 0, QINPOS);
-  aic_outb(p, 0, KERNEL_QINPOS);
-  aic_outb(p, 0, QOUTPOS);
-
-  if(p->features & AHC_QUEUE_REGS)
-  {
-    aic_outb(p, SCB_QSIZE_256, QOFF_CTLSTA);
-    aic_outb(p, 0, SDSCB_QOFF);
-    aic_outb(p, 0, SNSCB_QOFF);
-    aic_outb(p, 0, HNSCB_QOFF);
-  }
-
-  /*
-   * We don't have any waiting selections or disconnected SCBs.
-   */
-  aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
-  aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
-
-  /*
-   * Message out buffer starts empty
-   */
-  aic_outb(p, MSG_NOOP, MSG_OUT);
-  aic_outb(p, MSG_NOOP, LAST_MSG);
-
-  /*
-   * Set all the other asundry items that haven't been set yet.
-   * This includes just dumping init values to a lot of registers simply
-   * to make sure they've been touched and are ready for use parity wise
-   * speaking.
-   */
-  aic_outb(p, 0, TMODE_CMDADDR);
-  aic_outb(p, 0, TMODE_CMDADDR + 1);
-  aic_outb(p, 0, TMODE_CMDADDR + 2);
-  aic_outb(p, 0, TMODE_CMDADDR + 3);
-  aic_outb(p, 0, TMODE_CMDADDR_NEXT);
-
-  /*
-   * Link us into the list of valid hosts
-   */
-  p->next = first_aic7xxx;
-  first_aic7xxx = p;
-
-  /*
-   * Allocate the first set of scbs for this controller.  This is to stream-
-   * line code elsewhere in the driver.  If we have to check for the existence
-   * of scbs in certain code sections, it slows things down.  However, as
-   * soon as we register the IRQ for this card, we could get an interrupt that
-   * includes possibly the SCSI_RSTI interrupt.  If we catch that interrupt
-   * then we are likely to segfault if we don't have at least one chunk of
-   * SCBs allocated or add checks all through the reset code to make sure
-   * that the SCBs have been allocated which is an invalid running condition
-   * and therefore I think it's preferable to simply pre-allocate the first
-   * chunk of SCBs.
-   */
-  aic7xxx_allocate_scb(p);
-
-  /*
-   * Load the sequencer program, then re-enable the board -
-   * resetting the AIC-7770 disables it, leaving the lights
-   * on with nobody home.
-   */
-  aic7xxx_loadseq(p);
-
-  /*
-   * Make sure the AUTOFLUSHDIS bit is *not* set in the SBLKCTL register
-   */
-  aic_outb(p, aic_inb(p, SBLKCTL) & ~AUTOFLUSHDIS, SBLKCTL);
-
-  if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
-  {
-    aic_outb(p, ENABLE, BCTL);  /* Enable the boards BUS drivers. */
-  }
-
-  if ( !(aic7xxx_no_reset) )
-  {
-    if (p->features & AHC_TWIN)
-    {
-      if (aic7xxx_verbose & VERBOSE_PROBE2)
-        printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no);
-      aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
-      aic7xxx_reset_current_bus(p);
-      aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
-    }
-    /* Reset SCSI bus A. */
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-    {  /* In case we are a 3940, 3985, or 7895, print the right channel */
-      char *channel = "";
-      if (p->flags & AHC_MULTI_CHANNEL)
-      {
-        channel = " A";
-        if (p->flags & (AHC_CHNLB|AHC_CHNLC))
-          channel = (p->flags & AHC_CHNLB) ? " B" : " C";
-      }
-      printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel);
-    }
-    
-    aic7xxx_reset_current_bus(p);
-
-    /*
-     * Delay for the reset delay by setting the timer, this will delay
-     * future commands sent to any devices.
-     */
-    p->flags |= AHC_RESET_DELAY;
-    for(i=0; i<MAX_TARGETS; i++)
-    {
-      p->dev_expires[i] = jiffies + (4 * HZ);
-      p->dev_timer_active |= (0x01 << i);
-    }
-    p->dev_timer.expires = p->dev_expires[p->scsi_id];
-    add_timer(&p->dev_timer);
-    p->dev_timer_active |= (0x01 << MAX_TARGETS);
-  }
-  else
-  {
-    if (!reset_delay)
-    {
-      printk(KERN_INFO "(scsi%d) Not resetting SCSI bus.  Note: Don't use "
-             "the no_reset\n", p->host_no);
-      printk(KERN_INFO "(scsi%d) option unless you have a verifiable need "
-             "for it.\n", p->host_no);
-    }
-  }
-  
-  /*
-   * Register IRQ with the kernel.  Only allow sharing IRQs with
-   * PCI devices.
-   */
-  if (!(p->chip & AHC_PCI))
-  {
-    result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p));
-  }
-  else
-  {
-    result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ,
-              "aic7xxx", p));
-    if (result < 0)
-    {
-      result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
-              "aic7xxx", p));
-    }
-  }
-  if (result < 0)
-  {
-    printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring "
-           "controller.\n", p->host_no, p->irq);
-    aic_outb(p, 0, SIMODE1);
-    p->irq = 0;
-    return (0);
-  }
-
-  if(aic_inb(p, INTSTAT) & INT_PEND)
-    printk(INFO_LEAD "spurious interrupt during configuration, cleared.\n",
-      p->host_no, -1, -1 , -1);
-  aic7xxx_clear_intstat(p);
-
-  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*************************************************************************/
-int
-aic7xxx_chip_reset(struct aic7xxx_host *p)
-{
-  unsigned char sblkctl;
-  int wait;
-
-  /*
-   * For some 274x boards, we must clear the CHIPRST bit and pause
-   * the sequencer. For some reason, this makes the driver work.
-   */
-  aic_outb(p, PAUSE | CHIPRST, 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 msec (1000 * 1 msec) */
-  while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK))
-  {
-    udelay(1);  /* 1 usec */
-  }
-
-  pause_sequencer(p);
-
-  sblkctl = aic_inb(p, SBLKCTL) & (SELBUSB|SELWIDE);
-  if (p->chip & AHC_PCI)
-    sblkctl &= ~SELBUSB;
-  switch( sblkctl )
-  {
-    case 0:  /* normal narrow card */
-      break;
-    case 2:  /* Wide card */
-      p->features |= AHC_WIDE;
-      break;
-    case 8:  /* Twin card */
-      p->features |= AHC_TWIN;
-      p->flags |= AHC_MULTI_CHANNEL;
-      break;
-    default: /* hmmm...we don't know what this is */
-      printk(KERN_WARNING "aic7xxx: Unsupported adapter type %d, ignoring.\n",
-        aic_inb(p, SBLKCTL) & 0x0a);
-      return(-1);
-  }
-  return(0);
-}
-
-/*+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, struct aic7xxx_host *temp)
-{
-  struct aic7xxx_host *p = NULL;
-  struct Scsi_Host *host;
-  int i;
-
-  /*
-   * 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 = *temp;
-    p->host = host;
-
-    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.
-       */
-      release_region(p->base, MAXREG - MINREG);
-      scsi_unregister(host);
-      return(NULL);
-    }
-    p->host_no = host->host_no;
-    p->tagenable = 0;
-    p->orderedtag = 0;
-    for (i=0; i<MAX_TARGETS; i++)
-    {
-      p->transinfo[i].goal_period = 255;
-      p->transinfo[i].goal_offset = 0;
-      p->transinfo[i].goal_options = 0;
-      p->transinfo[i].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
-    }
-    DRIVER_LOCK_INIT
-  }
-  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;
-
-  /*
-   * Free the allocated hardware SCB space.
-   */
-  if (p->scb_data != NULL)
-  {
-    struct aic7xxx_scb_dma *scb_dma = NULL;
-    if (p->scb_data->hscbs != NULL)
-    {
-      pci_free_consistent(p->pdev, p->scb_data->hscbs_dma_len,
-			  p->scb_data->hscbs, p->scb_data->hscbs_dma);
-      p->scb_data->hscbs = p->scb_data->hscb_kmalloc_ptr = NULL;
-    }
-    /*
-     * Free the driver SCBs.  These were allocated on an as-need
-     * basis.  We allocated these in groups depending on how many
-     * we could fit into a given amount of RAM.  The tail SCB for
-     * these allocations has a pointer to the alloced area.
-     */
-    for (i = 0; i < p->scb_data->numscbs; i++)
-    {
-      if (p->scb_data->scb_array[i]->scb_dma != scb_dma)
-      {
-	scb_dma = p->scb_data->scb_array[i]->scb_dma;
-	pci_free_consistent(p->pdev, scb_dma->dma_len,
-			    (void *)((unsigned long)scb_dma->dma_address
-                                     - scb_dma->dma_offset),
-			    scb_dma->dma_address);
-      }
-      if (p->scb_data->scb_array[i]->kmalloc_ptr != NULL)
-        kfree(p->scb_data->scb_array[i]->kmalloc_ptr);
-      p->scb_data->scb_array[i] = NULL;
-    }
-  
-    /*
-     * Free the SCB data area.
-     */
-    kfree(p->scb_data);
-  }
-
-  /*
-   * Free any alloced Scsi_Cmnd structures that might be around for
-   * negotiation purposes....
-   */
-  for (i = 0; i < MAX_TARGETS; i++)
-  {
-    if(p->dev_dtr_cmnd[i])
-    {
-      if(p->dev_dtr_cmnd[i]->request_buffer)
-      {
-        kfree(p->dev_dtr_cmnd[i]->request_buffer);
-      }
-      kfree(p->dev_dtr_cmnd[i]);
-    }
-  }
-
-  pci_free_consistent(p->pdev, 3*256, (void *)p->untagged_scbs, p->fifo_dma);
-}
-
-/*+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 void
-aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
-{
-  int have_seeprom = 0;
-  int i, max_targets, mask;
-  unsigned char scsirate, scsi_conf;
-  unsigned short scarray[128];
-  struct seeprom_config *sc = (struct seeprom_config *) scarray;
-
-  if (aic7xxx_verbose & VERBOSE_PROBE2)
-  {
-    printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
-  }
-  switch (p->chip)
-  {
-    case (AHC_AIC7770|AHC_EISA):  /* None of these adapters have seeproms. */
-      if (aic_inb(p, SCSICONF) & TERM_ENB)
-        p->flags |= AHC_TERM_ENB_A;
-      if ( (p->features & AHC_TWIN) && (aic_inb(p, SCSICONF + 1) & TERM_ENB) )
-        p->flags |= AHC_TERM_ENB_B;
-      break;
-
-    case (AHC_AIC7770|AHC_VL):
-      have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
-      break;
-
-    default:
-      have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                  scarray, p->sc_size, p->sc_type);
-      if (!have_seeprom)
-      {
-        if(p->sc_type == C46)
-          have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                      scarray, p->sc_size, C56_66);
-        else
-          have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                      scarray, p->sc_size, C46);
-      }
-      if (!have_seeprom)
-      {
-        p->sc_size = 128;
-        have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                    scarray, p->sc_size, p->sc_type);
-        if (!have_seeprom)
-        {
-          if(p->sc_type == C46)
-            have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                        scarray, p->sc_size, C56_66);
-          else
-            have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)),
-                                        scarray, p->sc_size, C46);
-        }
-      }
-      break;
-  }
-
-  if (!have_seeprom)
-  {
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-    {
-      printk("\naic7xxx: No SEEPROM available.\n");
-    }
-    p->flags |= AHC_NEWEEPROM_FMT;
-    if (aic_inb(p, SCSISEQ) == 0)
-    {
-      p->flags |= AHC_USEDEFAULTS;
-      p->flags &= ~AHC_BIOS_ENABLED;
-      p->scsi_id = p->scsi_id_b = 7;
-      *sxfrctl1 |= STPWEN;
-      if (aic7xxx_verbose & VERBOSE_PROBE2)
-      {
-        printk("aic7xxx: Using default values.\n");
-      }
-    }
-    else if (aic7xxx_verbose & VERBOSE_PROBE2)
-    {
-      printk("aic7xxx: Using leftover BIOS values.\n");
-    }
-    if ( ((p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) && (*sxfrctl1 & STPWEN) )
-    {
-      p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
-      sc->adapter_control &= ~CFAUTOTERM;
-      sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM;
-    }
-    if (aic7xxx_extended)
-      p->flags |= (AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
-    else
-      p->flags &= ~(AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
-  }
-  else
-  {
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-    {
-      printk("done\n");
-    }
-
-    /*
-     * Note things in our flags
-     */
-    p->flags |= AHC_SEEPROM_FOUND;
-
-    /*
-     * Update the settings in sxfrctl1 to match the termination settings.
-     */
-    *sxfrctl1 = 0;
-
-    /*
-     * Get our SCSI ID from the SEEPROM setting...
-     */
-    p->scsi_id = (sc->brtime_id & CFSCSIID);
-
-    /*
-     * First process the settings that are different between the VLB
-     * and PCI adapter seeproms.
-     */
-    if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7770)
-    {
-      /* VLB adapter seeproms */
-      if (sc->bios_control & CF284XEXTEND)
-        p->flags |= AHC_EXTEND_TRANS_A;
-
-      if (sc->adapter_control & CF284XSTERM)
-      {
-        *sxfrctl1 |= STPWEN;
-        p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
-      }
-    }
-    else
-    {
-      /* PCI adapter seeproms */
-      if (sc->bios_control & CFEXTEND)
-        p->flags |= AHC_EXTEND_TRANS_A;
-      if (sc->bios_control & CFBIOSEN)
-        p->flags |= AHC_BIOS_ENABLED;
-      else
-        p->flags &= ~AHC_BIOS_ENABLED;
-
-      if (sc->adapter_control & CFSTERM)
-      {
-        *sxfrctl1 |= STPWEN;
-        p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
-      }
-    }
-    memcpy(&p->sc, sc, sizeof(struct seeprom_config));
-  }
-
-  p->discenable = 0;
-    
-  /*
-   * Limit to 16 targets just in case.  The 2842 for one is known to
-   * blow the max_targets setting, future cards might also.
-   */
-  max_targets = ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8);
-
-  if (have_seeprom)
-  {
-    for (i = 0; i < max_targets; i++)
-    {
-      if( ((p->features & AHC_ULTRA) &&
-          !(sc->adapter_control & CFULTRAEN) &&
-           (sc->device_flags[i] & CFSYNCHISULTRA)) ||
-          (sc->device_flags[i] & CFNEWULTRAFORMAT) )
-      {
-        p->flags |= AHC_NEWEEPROM_FMT;
-        break;
-      }
-    }
-  }
-
-  for (i = 0; i < max_targets; i++)
-  {
-    mask = (0x01 << i);
-    if (!have_seeprom)
-    {
-      if (aic_inb(p, SCSISEQ) != 0)
-      {
-        /*
-         * OK...the BIOS set things up and left behind the settings we need.
-         * Just make our sc->device_flags[i] entry match what the card has
-         * set for this device.
-         */
-        p->discenable = 
-          ~(aic_inb(p, DISC_DSB) | (aic_inb(p, DISC_DSB + 1) << 8) );
-        p->ultraenb =
-          (aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8) );
-        sc->device_flags[i] = (p->discenable & mask) ? CFDISC : 0;
-        if (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER)
-          sc->device_flags[i] |= CFWIDEB;
-        if (p->features & AHC_ULTRA2)
-        {
-          if (aic_inb(p, TARG_OFFSET + i))
-          {
-            sc->device_flags[i] |= CFSYNCH;
-            sc->device_flags[i] |= (aic_inb(p, TARG_SCSIRATE + i) & 0x07);
-            if ( (aic_inb(p, TARG_SCSIRATE + i) & 0x18) == 0x18 )
-              sc->device_flags[i] |= CFSYNCHISULTRA;
-          }
-        }
-        else
-        {
-          if (aic_inb(p, TARG_SCSIRATE + i) & ~WIDEXFER)
-          {
-            sc->device_flags[i] |= CFSYNCH;
-            if (p->features & AHC_ULTRA)
-              sc->device_flags[i] |= ((p->ultraenb & mask) ?
-                                      CFSYNCHISULTRA : 0);
-          }
-        }
-      }
-      else
-      {
-        /*
-         * Assume the BIOS has NOT been run on this card and nothing between
-         * the card and the devices is configured yet.
-         */
-        sc->device_flags[i] = CFDISC;
-        if (p->features & AHC_WIDE)
-          sc->device_flags[i] |= CFWIDEB;
-        if (p->features & AHC_ULTRA3)
-          sc->device_flags[i] |= 2;
-        else if (p->features & AHC_ULTRA2)
-          sc->device_flags[i] |= 3;
-        else if (p->features & AHC_ULTRA)
-          sc->device_flags[i] |= CFSYNCHISULTRA;
-        sc->device_flags[i] |= CFSYNCH;
-        aic_outb(p, 0, TARG_SCSIRATE + i);
-        if (p->features & AHC_ULTRA2)
-          aic_outb(p, 0, TARG_OFFSET + i);
-      }
-    }
-    if (sc->device_flags[i] & CFDISC)
-    {
-      p->discenable |= mask;
-    }
-    if (p->flags & AHC_NEWEEPROM_FMT)
-    {
-      if ( !(p->features & AHC_ULTRA2) )
-      {
-        /*
-         * I know of two different Ultra BIOSes that do this differently.
-         * One on the Gigabyte 6BXU mb that wants flags[i] & CFXFER to
-         * be == to 0x03 and SYNCHISULTRA to be true to mean 40MByte/s
-         * while on the IBM Netfinity 5000 they want the same thing
-         * to be something else, while flags[i] & CFXFER == 0x03 and
-         * SYNCHISULTRA false should be 40MByte/s.  So, we set both to
-         * 40MByte/s and the lower speeds be damned.  People will have
-         * to select around the conversely mapped lower speeds in order
-         * to select lower speeds on these boards.
-         */
-        if ( (sc->device_flags[i] & CFNEWULTRAFORMAT) &&
-            ((sc->device_flags[i] & CFXFER) == 0x03) )
-        {
-          sc->device_flags[i] &= ~CFXFER;
-          sc->device_flags[i] |= CFSYNCHISULTRA;
-        }
-        if (sc->device_flags[i] & CFSYNCHISULTRA)
-        {
-          p->ultraenb |= mask;
-        }
-      }
-      else if ( !(sc->device_flags[i] & CFNEWULTRAFORMAT) &&
-                 (p->features & AHC_ULTRA2) &&
-		 (sc->device_flags[i] & CFSYNCHISULTRA) )
-      {
-        p->ultraenb |= mask;
-      }
-    }
-    else if (sc->adapter_control & CFULTRAEN)
-    {
-      p->ultraenb |= mask;
-    }
-    if ( (sc->device_flags[i] & CFSYNCH) == 0)
-    {
-      sc->device_flags[i] &= ~CFXFER;
-      p->ultraenb &= ~mask;
-      p->transinfo[i].user_offset = 0;
-      p->transinfo[i].user_period = 0;
-      p->transinfo[i].user_options = 0;
-      p->transinfo[i].cur_offset = 0;
-      p->transinfo[i].cur_period = 0;
-      p->transinfo[i].cur_options = 0;
-      p->needsdtr_copy &= ~mask;
-    }
-    else
-    {
-      if (p->features & AHC_ULTRA3)
-      {
-        p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2;
-        p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i);
-        if( (sc->device_flags[i] & CFXFER) < 0x03 )
-        {
-          scsirate = (sc->device_flags[i] & CFXFER);
-          p->transinfo[i].user_options = MSG_EXT_PPR_OPTION_DT_CRC;
-          if( (aic_inb(p, TARG_SCSIRATE + i) & CFXFER) < 0x03 )
-          {
-            p->transinfo[i].cur_options = 
-              ((aic_inb(p, TARG_SCSIRATE + i) & 0x40) ?
-                 MSG_EXT_PPR_OPTION_DT_CRC : MSG_EXT_PPR_OPTION_DT_UNITS);
-          }
-          else
-          {
-            p->transinfo[i].cur_options = 0;
-          }
-        }
-        else
-        {
-          scsirate = (sc->device_flags[i] & CFXFER) |
-                     ((p->ultraenb & mask) ? 0x18 : 0x10);
-          p->transinfo[i].user_options = 0;
-          p->transinfo[i].cur_options = 0;
-        }
-        p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate,
-                                       AHC_SYNCRATE_ULTRA3);
-        p->transinfo[i].cur_period = aic7xxx_find_period(p,
-                                       aic_inb(p, TARG_SCSIRATE + i),
-                                       AHC_SYNCRATE_ULTRA3);
-      }
-      else if (p->features & AHC_ULTRA2)
-      {
-        p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2;
-        p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i);
-        scsirate = (sc->device_flags[i] & CFXFER) |
-                   ((p->ultraenb & mask) ? 0x18 : 0x10);
-        p->transinfo[i].user_options = 0;
-        p->transinfo[i].cur_options = 0;
-        p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate,
-                                       AHC_SYNCRATE_ULTRA2);
-        p->transinfo[i].cur_period = aic7xxx_find_period(p,
-                                       aic_inb(p, TARG_SCSIRATE + i),
-                                       AHC_SYNCRATE_ULTRA2);
-      }
-      else
-      {
-        scsirate = (sc->device_flags[i] & CFXFER) << 4;
-        p->transinfo[i].user_options = 0;
-        p->transinfo[i].cur_options = 0;
-        p->transinfo[i].user_offset = MAX_OFFSET_8BIT;
-        if (p->features & AHC_ULTRA)
-        {
-          short ultraenb;
-          ultraenb = aic_inb(p, ULTRA_ENB) |
-            (aic_inb(p, ULTRA_ENB + 1) << 8);
-          p->transinfo[i].user_period = aic7xxx_find_period(p,
-                                          scsirate,
-                                          (p->ultraenb & mask) ?
-                                          AHC_SYNCRATE_ULTRA :
-                                          AHC_SYNCRATE_FAST);
-          p->transinfo[i].cur_period = aic7xxx_find_period(p,
-                                         aic_inb(p, TARG_SCSIRATE + i),
-                                         (ultraenb & mask) ? 
-                                         AHC_SYNCRATE_ULTRA :
-                                         AHC_SYNCRATE_FAST);
-        }
-        else
-          p->transinfo[i].user_period = aic7xxx_find_period(p,
-                                          scsirate, AHC_SYNCRATE_FAST);
-      }
-      p->needsdtr_copy |= mask;
-    }
-    if ( (sc->device_flags[i] & CFWIDEB) && (p->features & AHC_WIDE) )
-    {
-      p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_16_BIT;
-      p->needwdtr_copy |= mask;
-    }
-    else
-    {
-      p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_8_BIT;
-      p->needwdtr_copy &= ~mask;
-    }
-    p->transinfo[i].cur_width =
-      (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER) ?
-      MSG_EXT_WDTR_BUS_16_BIT : MSG_EXT_WDTR_BUS_8_BIT;
-  }
-  aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
-  aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
-  p->needppr = p->needppr_copy = p->needdv = 0;
-  p->needwdtr = p->needwdtr_copy;
-  p->needsdtr = p->needsdtr_copy;
-  p->dtr_pending = 0;
-
-  /*
-   * We set the p->ultraenb from the SEEPROM to begin with, but now we make
-   * it match what is already down in the card.  If we are doing a reset
-   * on the card then this will get put back to a default state anyway.
-   * This allows us to not have to pre-emptively negotiate when using the
-   * no_reset option.
-   */
-  if (p->features & AHC_ULTRA)
-    p->ultraenb = aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8);
-
-  
-  scsi_conf = (p->scsi_id & HSCSIID);
-
-  if(have_seeprom)
-  {
-    p->adapter_control = sc->adapter_control;
-    p->bios_control = sc->bios_control;
-
-    switch (p->chip & AHC_CHIPID_MASK)
-    {
-      case AHC_AIC7895:
-      case AHC_AIC7896:
-      case AHC_AIC7899:
-        if (p->adapter_control & CFBPRIMARY)
-          p->flags |= AHC_CHANNEL_B_PRIMARY;
-      default:
-        break;
-    }
-
-    if (sc->adapter_control & CFSPARITY)
-      scsi_conf |= ENSPCHK;
-  }
-  else
-  {
-    scsi_conf |= ENSPCHK | RESET_SCSI;
-  }
-
-  /*
-   * Only set the SCSICONF and SCSICONF + 1 registers if we are a PCI card.
-   * The 2842 and 2742 cards already have these registers set and we don't
-   * want to muck with them since we don't set all the bits they do.
-   */
-  if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI )
-  {
-    /* Set the host ID */
-    aic_outb(p, scsi_conf, SCSICONF);
-    /* In case we are a wide card */
-    aic_outb(p, p->scsi_id, SCSICONF + 1);
-  }
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_detect
- *
- * 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)
-{
-  struct aic7xxx_host *temp_p = NULL;
-  struct aic7xxx_host *current_p = NULL;
-  struct aic7xxx_host *list_p = NULL;
-  int found = 0;
-#if defined(__i386__) || defined(__alpha__)
-  ahc_flag_type flags = 0;
-  int type;
-#endif
-  unsigned char sxfrctl1;
-#if defined(__i386__) || defined(__alpha__)
-  unsigned char hcntrl, hostconf;
-  unsigned int slot, base;
-#endif
-
-#ifdef MODULE
-  /*
-   * If we are called as a module, the aic7xxx pointer may not be null
-   * and it would point to our bootup string, just like on the lilo
-   * command line.  IF not NULL, then process this config string with
-   * aic7xxx_setup
-   */
-  if(aic7xxx)
-    aic7xxx_setup(aic7xxx);
-  if(dummy_buffer[0] != 'P')
-    printk(KERN_WARNING "aic7xxx: Please read the file /usr/src/linux/drivers"
-      "/scsi/README.aic7xxx\n"
-      "aic7xxx: to see the proper way to specify options to the aic7xxx "
-      "module\n"
-      "aic7xxx: Specifically, don't use any commas when passing arguments to\n"
-      "aic7xxx: insmod or else it might trash certain memory areas.\n");
-#endif
-
-  template->proc_name = "aic7xxx";
-  template->sg_tablesize = AIC7XXX_MAX_SG;
-
-
-#ifdef CONFIG_PCI
-  /*
-   * PCI-bus probe.
-   */
-  if (pci_present())
-  {
-    struct
-    {
-      unsigned short      vendor_id;
-      unsigned short      device_id;
-      ahc_chip            chip;
-      ahc_flag_type       flags;
-      ahc_feature         features;
-      int                 board_name_index;
-      unsigned short      seeprom_size;
-      unsigned short      seeprom_type;
-    } const aic_pdevs[] = {
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7810, AHC_NONE,
-       AHC_FNONE, AHC_FENONE,                                1,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AHC_AIC7850,
-       AHC_PAGESCBS, AHC_AIC7850_FE,                         5,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
-       AHC_PAGESCBS, AHC_AIC7850_FE,                         6,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7821, AHC_AIC7860,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7860_FE,                                       7,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_3860, AHC_AIC7860,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7860_FE,                                       7,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_38602, AHC_AIC7860,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7860_FE,                                       7,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_38602, AHC_AIC7860,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7860_FE,                                       7,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MOTHERBOARD,
-       AHC_AIC7860_FE,                                       7,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_AIC7860,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7860_FE,                                       8,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MOTHERBOARD,
-       AHC_AIC7870_FE,                                       9,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_AIC7870,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE,     10,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_AIC7870,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7870_FE,                                      11,
-       32, C56_66 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_AIC7870,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7870_FE,                                      12,
-       32, C56_66 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_AIC7870,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE,     13,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MOTHERBOARD,
-       AHC_AIC7880_FE,                                      14,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_AIC7880,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     15,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_AIC7880,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7880_FE,                                      16,
-       32, C56_66 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_AIC7880,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7880_FE,                                      17,
-       32, C56_66 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7885, AHC_AIC7880,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7886, AHC_AIC7880,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE | AHC_NEW_AUTOTERM, 19,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880,
-       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7895_FE,                                      20,
-       32, C56_66 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890, AHC_AIC7890,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7890_FE,                                      21,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890B, AHC_AIC7890,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7890_FE,                                      21,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2930U2, AHC_AIC7890,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7890_FE,                                      22,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7890_FE,                                      23,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      24,
-       32, C56_66 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      25,
-       32, C56_66 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3950U2D, AHC_AIC7896,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7896_FE,                                      26,
-       32, C56_66 },
-      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_NO_STPWEN,
-       AHC_AIC7860_FE,                                      27,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892A, AHC_AIC7892,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7892_FE,                                      28,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892B, AHC_AIC7892,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7892_FE,                                      28,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892D, AHC_AIC7892,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7892_FE,                                      28,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892P, AHC_AIC7892,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
-       AHC_AIC7892_FE,                                      28,
-       32, C46 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899A, AHC_AIC7899,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7899_FE,                                      29,
-       32, C56_66 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899B, AHC_AIC7899,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7899_FE,                                      29,
-       32, C56_66 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899D, AHC_AIC7899,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7899_FE,                                      29,
-       32, C56_66 },
-      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899P, AHC_AIC7899,
-       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
-       AHC_AIC7899_FE,                                      29,
-       32, C56_66 },
-    };
-
-    unsigned short command;
-    unsigned int  devconfig, i, oldverbose;
-    struct pci_dev *pdev = NULL;
-
-    for (i = 0; i < NUMBER(aic_pdevs); i++)
-    {
-      pdev = NULL;
-      while ((pdev = pci_find_device(aic_pdevs[i].vendor_id,
-                                     aic_pdevs[i].device_id,
-                                     pdev))) {
-	if (pci_enable_device(pdev))
-		continue;
-        if ( i == 0 ) /* We found one, but it's the 7810 RAID cont. */
-        {
-          if (aic7xxx_verbose & (VERBOSE_PROBE|VERBOSE_PROBE2))
-          {
-            printk(KERN_INFO "aic7xxx: The 7810 RAID controller is not "
-              "supported by\n");
-            printk(KERN_INFO "         this driver, we are ignoring it.\n");
-          }
-        }
-        else if ( (temp_p = kmalloc(sizeof(struct aic7xxx_host),
-                                    GFP_ATOMIC)) != NULL )
-        {
-          memset(temp_p, 0, sizeof(struct aic7xxx_host));
-          temp_p->chip = aic_pdevs[i].chip | AHC_PCI;
-          temp_p->flags = aic_pdevs[i].flags;
-          temp_p->features = aic_pdevs[i].features;
-          temp_p->board_name_index = aic_pdevs[i].board_name_index;
-          temp_p->sc_size = aic_pdevs[i].seeprom_size;
-          temp_p->sc_type = aic_pdevs[i].seeprom_type;
-
-          /*
-           * Read sundry information from PCI BIOS.
-           */
-          temp_p->irq = pdev->irq;
-          temp_p->pdev = pdev;
-          temp_p->pci_bus = pdev->bus->number;
-          temp_p->pci_device_fn = pdev->devfn;
-          temp_p->base = pci_resource_start(pdev, 0);
-          temp_p->mbase = pci_resource_start(pdev, 1);
-          current_p = list_p;
-	  while(current_p && temp_p)
-	  {
-	    if ( ((current_p->pci_bus == temp_p->pci_bus) &&
-	          (current_p->pci_device_fn == temp_p->pci_device_fn)) ||
-                 (temp_p->base && (current_p->base == temp_p->base)) ||
-                 (temp_p->mbase && (current_p->mbase == temp_p->mbase)) )
-	    {
-              /* duplicate PCI entry, skip it */
-	      kfree(temp_p);
-	      temp_p = NULL;
-	    }
-	    current_p = current_p->next;
-	  }
-	  if ( temp_p == NULL )
-            continue;
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk("aic7xxx: <%s> at PCI %d/%d\n", 
-              board_names[aic_pdevs[i].board_name_index],
-              PCI_SLOT(pdev->devfn),
-              PCI_FUNC(pdev->devfn));
-          pci_read_config_word(pdev, PCI_COMMAND, &command);
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-          {
-            printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n",
-              (int)command);
-          }
-#ifdef AIC7XXX_STRICT_PCI_SETUP
-          command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
-            PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
-#else
-          command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
-#endif
-          command &= ~PCI_COMMAND_INVALIDATE;
-          if (aic7xxx_pci_parity == 0)
-            command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
-          pci_write_config_word(pdev, PCI_COMMAND, command);
-#ifdef AIC7XXX_STRICT_PCI_SETUP
-          pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-          {
-            printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
-          }
-          devconfig |= 0x80000040;
-          pci_write_config_dword(pdev, DEVCONFIG, devconfig);
-#endif /* AIC7XXX_STRICT_PCI_SETUP */
-
-          if(temp_p->base && check_region(temp_p->base, MAXREG - MINREG))
-          {
-            printk("aic7xxx: <%s> at PCI %d/%d/%d\n", 
-              board_names[aic_pdevs[i].board_name_index],
-              temp_p->pci_bus,
-              PCI_SLOT(temp_p->pci_device_fn),
-              PCI_FUNC(temp_p->pci_device_fn));
-            printk("aic7xxx: I/O ports already in use, ignoring.\n");
-            kfree(temp_p);
-            temp_p = NULL;
-            continue;
-          }
-
-          temp_p->unpause = INTEN;
-          temp_p->pause = temp_p->unpause | PAUSE;
-          if ( ((temp_p->base == 0) &&
-                (temp_p->mbase == 0)) ||
-               (temp_p->irq == 0) )
-          {
-            printk("aic7xxx: <%s> at PCI %d/%d/%d\n", 
-              board_names[aic_pdevs[i].board_name_index],
-              temp_p->pci_bus,
-              PCI_SLOT(temp_p->pci_device_fn),
-              PCI_FUNC(temp_p->pci_device_fn));
-            printk("aic7xxx: Controller disabled by BIOS, ignoring.\n");
-            kfree(temp_p);
-            temp_p = NULL;
-            continue;
-          }
-
-#ifdef MMAPIO
-          if ( !(temp_p->base) || !(temp_p->flags & AHC_MULTI_CHANNEL) ||
-               ((temp_p->chip != (AHC_AIC7870 | AHC_PCI)) &&
-                (temp_p->chip != (AHC_AIC7880 | AHC_PCI))) )
-          {
-            unsigned long page_offset, base;
-
-            base = temp_p->mbase & PAGE_MASK;
-            page_offset = temp_p->mbase - base;
-            temp_p->maddr = ioremap_nocache(base, page_offset + 256);
-            if(temp_p->maddr)
-            {
-              temp_p->maddr += page_offset;
-              /*
-               * We need to check the I/O with the MMAPed address.  Some machines
-               * simply fail to work with MMAPed I/O and certain controllers.
-               */
-              if(aic_inb(temp_p, HCNTRL) == 0xff)
-              {
-                /*
-                 * OK.....we failed our test....go back to programmed I/O
-                 */
-                printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n", 
-                  board_names[aic_pdevs[i].board_name_index],
-                  temp_p->pci_bus,
-                  PCI_SLOT(temp_p->pci_device_fn),
-                  PCI_FUNC(temp_p->pci_device_fn));
-                printk(KERN_INFO "aic7xxx: MMAPed I/O failed, reverting to "
-                                 "Programmed I/O.\n");
-                iounmap((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK));
-                temp_p->maddr = 0;
-                if(temp_p->base == 0)
-                {
-                  printk("aic7xxx: <%s> at PCI %d/%d/%d\n", 
-                    board_names[aic_pdevs[i].board_name_index],
-                    temp_p->pci_bus,
-                    PCI_SLOT(temp_p->pci_device_fn),
-                    PCI_FUNC(temp_p->pci_device_fn));
-                  printk("aic7xxx: Controller disabled by BIOS, ignoring.\n");
-                  kfree(temp_p);
-                  temp_p = NULL;
-                  continue;
-                }
-              }
-            }
-          }
-#endif
-
-          /*
-           * Lock out other contenders for our i/o space.
-           */
-          if(temp_p->base)
-            request_region(temp_p->base, MAXREG - MINREG, "aic7xxx");
-
-          /*
-           * We HAVE to make sure the first pause_sequencer() and all other
-           * subsequent I/O that isn't PCI config space I/O takes place
-           * after the MMAPed I/O region is configured and tested.  The
-           * problem is the PowerPC architecture that doesn't support
-           * programmed I/O at all, so we have to have the MMAP I/O set up
-           * for this pause to even work on those machines.
-           */
-          pause_sequencer(temp_p);
-
-          /*
-           * Clear out any pending PCI error status messages.  Also set
-           * verbose to 0 so that we don't emit strange PCI error messages
-           * while cleaning out the current status bits.
-           */
-          oldverbose = aic7xxx_verbose;
-          aic7xxx_verbose = 0;
-          aic7xxx_pci_intr(temp_p);
-          aic7xxx_verbose = oldverbose;
-
-          temp_p->bios_address = 0;
-
-          /*
-           * Remember how the card was setup in case there is no seeprom.
-           */
-          if (temp_p->features & AHC_ULTRA2)
-            temp_p->scsi_id = aic_inb(temp_p, SCSIID_ULTRA2) & OID;
-          else
-            temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID;
-          /*
-           * Get current termination setting
-           */
-          sxfrctl1 = aic_inb(temp_p, SXFRCTL1);
-
-          if (aic7xxx_chip_reset(temp_p) == -1)
-          {
-            release_region(temp_p->base, MAXREG - MINREG);
-            kfree(temp_p);
-            temp_p = NULL;
-            continue;
-          }
-          /*
-           * Very quickly put the term setting back into the register since
-           * the chip reset may cause odd things to happen.  This is to keep
-           * LVD busses with lots of drives from draining the power out of
-           * the diffsense line before we get around to running the
-           * configure_termination() function.  Also restore the STPWLEVEL
-           * bit of DEVCONFIG
-           */
-          aic_outb(temp_p, sxfrctl1, SXFRCTL1);
-          pci_write_config_dword(temp_p->pdev, DEVCONFIG, devconfig);
-          sxfrctl1 &= STPWEN;
-
-          /*
-           * We need to set the CHNL? assignments before loading the SEEPROM
-           * The 3940 and 3985 cards (original stuff, not any of the later
-           * stuff) are 7870 and 7880 class chips.  The Ultra2 stuff falls
-           * under 7896 and 7897.  The 7895 is in a class by itself :)
-           */
-          switch (temp_p->chip & AHC_CHIPID_MASK)
-          {
-            case AHC_AIC7870: /* 3840 / 3985 */
-            case AHC_AIC7880: /* 3840 UW / 3985 UW */
-              if(temp_p->flags & AHC_MULTI_CHANNEL)
-              {
-                switch(PCI_SLOT(temp_p->pci_device_fn))
-                {
-                  case 5:
-                    temp_p->flags |= AHC_CHNLB;
-                    break;
-                  case 8:
-                    temp_p->flags |= AHC_CHNLB;
-                    break;
-                  case 12:
-                    temp_p->flags |= AHC_CHNLC;
-                    break;
-                  default:
-                    break;
-                }
-              }
-              break;
-
-            case AHC_AIC7895: /* 7895 */
-            case AHC_AIC7896: /* 7896/7 */
-            case AHC_AIC7899: /* 7899 */
-              if (PCI_FUNC(pdev->devfn) != 0)
-              {
-                temp_p->flags |= AHC_CHNLB;
-              }
-              /*
-               * The 7895 is the only chipset that sets the SCBSIZE32 param
-               * in the DEVCONFIG register.  The Ultra2 chipsets use
-               * the DSCOMMAND0 register instead.
-               */
-              if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
-              {
-                pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
-                devconfig |= SCBSIZE32;
-                pci_write_config_dword(pdev, DEVCONFIG, devconfig);
-              }
-              break;
-            default:
-              break;
-          }
-
-          /*
-           * Loading of the SEEPROM needs to come after we've set the flags
-           * to indicate possible CHNLB and CHNLC assigments.  Otherwise,
-           * on 394x and 398x cards we'll end up reading the wrong settings
-           * for channels B and C
-           */
-          switch (temp_p->chip & AHC_CHIPID_MASK)
-          {
-            case AHC_AIC7892:
-            case AHC_AIC7899:
-              aic_outb(temp_p, 0, SCAMCTL);
-              /*
-               * Switch to the alt mode of the chip...
-               */
-              aic_outb(temp_p, aic_inb(temp_p, SFUNCT) | ALT_MODE, SFUNCT);
-              /*
-               * Set our options...the last two items set our CRC after x byte
-	       * count in target mode...
-               */
-              aic_outb(temp_p, AUTO_MSGOUT_DE | DIS_MSGIN_DUALEDGE, OPTIONMODE);
-	      aic_outb(temp_p, 0x00, 0x0b);
-	      aic_outb(temp_p, 0x10, 0x0a);
-              /*
-               * switch back to normal mode...
-               */
-              aic_outb(temp_p, aic_inb(temp_p, SFUNCT) & ~ALT_MODE, SFUNCT);
-              aic_outb(temp_p, CRCVALCHKEN | CRCENDCHKEN | CRCREQCHKEN |
-			       TARGCRCENDEN | TARGCRCCNTEN,
-                       CRCCONTROL1);
-              aic_outb(temp_p, ((aic_inb(temp_p, DSCOMMAND0) | USCBSIZE32 |
-                                 MPARCKEN | CIOPARCKEN | CACHETHEN) & 
-                               ~DPARCKEN), DSCOMMAND0);
-              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
-              break;
-            case AHC_AIC7890:
-            case AHC_AIC7896:
-              aic_outb(temp_p, 0, SCAMCTL);
-              aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
-                                CACHETHEN | MPARCKEN | USCBSIZE32 |
-                                CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0);
-              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
-              break;
-            case AHC_AIC7850:
-            case AHC_AIC7860:
-              /*
-               * Set the DSCOMMAND0 register on these cards different from
-               * on the 789x cards.  Also, read the SEEPROM as well.
-               */
-              aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
-                                CACHETHEN | MPARCKEN) & ~DPARCKEN,
-                       DSCOMMAND0);
-              /* FALLTHROUGH */
-            default:
-              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
-              break;
-            case AHC_AIC7880:
-              /*
-               * Check the rev of the chipset before we change DSCOMMAND0
-               */
-              pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
-              if ((devconfig & 0xff) >= 1)
-              {
-                aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
-                                  CACHETHEN | MPARCKEN) & ~DPARCKEN,
-                         DSCOMMAND0);
-              }
-              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
-              break;
-          }
-          
-
-          /*
-           * and then we need another switch based on the type in order to
-           * make sure the channel B primary flag is set properly on 7895
-           * controllers....Arrrgggghhh!!!  We also have to catch the fact
-           * that when you disable the BIOS on the 7895 on the Intel DK440LX
-           * motherboard, and possibly others, it only sets the BIOS disabled
-           * bit on the A channel...I think I'm starting to lean towards
-           * going postal....
-           */
-          switch(temp_p->chip & AHC_CHIPID_MASK)
-          {
-            case AHC_AIC7895:
-            case AHC_AIC7896:
-            case AHC_AIC7899:
-              current_p = list_p;
-              while(current_p != NULL)
-              {
-                if ( (current_p->pci_bus == temp_p->pci_bus) &&
-                     (PCI_SLOT(current_p->pci_device_fn) ==
-                      PCI_SLOT(temp_p->pci_device_fn)) )
-                {
-                  if ( PCI_FUNC(current_p->pci_device_fn) == 0 )
-                  {
-                    temp_p->flags |= 
-                      (current_p->flags & AHC_CHANNEL_B_PRIMARY);
-                    temp_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
-                    temp_p->flags |=
-                      (current_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
-                  }
-                  else
-                  {
-                    current_p->flags |=
-                      (temp_p->flags & AHC_CHANNEL_B_PRIMARY);
-                    current_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
-                    current_p->flags |=
-                      (temp_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
-                  }
-                }
-                current_p = current_p->next;
-              }
-              break;
-            default:
-              break;
-          }
-
-          /*
-           * We only support external SCB RAM on the 7895/6/7 chipsets.
-           * We could support it on the 7890/1 easy enough, but I don't
-           * know of any 7890/1 based cards that have it.  I do know
-           * of 7895/6/7 cards that have it and they work properly.
-           */
-          switch(temp_p->chip & AHC_CHIPID_MASK)
-          {
-            default:
-              break;
-            case AHC_AIC7895:
-            case AHC_AIC7896:
-            case AHC_AIC7899:
-              pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
-              if (temp_p->features & AHC_ULTRA2)
-              {
-                if ( (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) &&
-                     (aic7xxx_scbram) )
-                {
-                  aic_outb(temp_p,
-                           aic_inb(temp_p, DSCOMMAND0) & ~SCBRAMSEL_ULTRA2,
-                           DSCOMMAND0);
-                  temp_p->flags |= AHC_EXTERNAL_SRAM;
-                  devconfig |= EXTSCBPEN;
-                }
-                else if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2)
-                {
-                  printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n", 
-                    board_names[aic_pdevs[i].board_name_index],
-                    temp_p->pci_bus,
-                    PCI_SLOT(temp_p->pci_device_fn),
-                    PCI_FUNC(temp_p->pci_device_fn));
-                  printk("aic7xxx: external SCB RAM detected, "
-                         "but not enabled\n");
-                }
-              }
-              else
-              {
-                if ((devconfig & RAMPSM) && (aic7xxx_scbram))
-                {
-                  devconfig &= ~SCBRAMSEL;
-                  devconfig |= EXTSCBPEN;
-                  temp_p->flags |= AHC_EXTERNAL_SRAM;
-                }
-                else if (devconfig & RAMPSM)
-                {
-                  printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n", 
-                    board_names[aic_pdevs[i].board_name_index],
-                    temp_p->pci_bus,
-                    PCI_SLOT(temp_p->pci_device_fn),
-                    PCI_FUNC(temp_p->pci_device_fn));
-                  printk("aic7xxx: external SCB RAM detected, "
-                         "but not enabled\n");
-                }
-              }
-              pci_write_config_dword(pdev, DEVCONFIG, devconfig);
-              if ( (temp_p->flags & AHC_EXTERNAL_SRAM) &&
-                   (temp_p->flags & AHC_CHNLB) )
-                aic_outb(temp_p, 1, CCSCBBADDR);
-              break;
-          }
-
-          /*
-           * Take the LED out of diagnostic mode
-           */
-          aic_outb(temp_p, 
-            (aic_inb(temp_p, SBLKCTL) & ~(DIAGLEDEN | DIAGLEDON)),
-            SBLKCTL);
-
-          /*
-           * We don't know where this is set in the SEEPROM or by the
-           * BIOS, so we default to 100%.  On Ultra2 controllers, use 75%
-           * instead.
-           */
-          if (temp_p->features & AHC_ULTRA2)
-          {
-            aic_outb(temp_p, RD_DFTHRSH_MAX | WR_DFTHRSH_MAX, DFF_THRSH);
-          }
-          else
-          {
-            aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
-          }
-
-          if ( list_p == NULL )
-          {
-            list_p = current_p = temp_p;
-          }
-          else
-          {
-            current_p = list_p;
-            while(current_p->next != NULL)
-              current_p = current_p->next;
-            current_p->next = temp_p;
-          }
-          temp_p->next = NULL;
-          found++;
-        }  /* Found an Adaptec PCI device. */
-        else /* Well, we found one, but we couldn't get any memory */
-        {
-          printk("aic7xxx: Found <%s>\n", 
-            board_names[aic_pdevs[i].board_name_index]);
-          printk(KERN_INFO "aic7xxx: Unable to allocate device memory, "
-            "skipping.\n");
-        }
-      } /* while(pdev=....) */
-    } /* for PCI_DEVICES */
-  } /* PCI BIOS present */
-#endif CONFIG_PCI
-
-#if defined(__i386__) || defined(__alpha__)
-  /*
-   * EISA/VL-bus card signature probe.
-   */
-  slot = MINSLOT;
-  while ( (slot <= MAXSLOT) && 
-         !(aic7xxx_no_probe) )
-  {
-    base = SLOTBASE(slot) + MINREG;
-
-    if (check_region(base, MAXREG - MINREG))
-    {
-      /*
-       * Some other driver has staked a
-       * claim to this i/o region already.
-       */
-      slot++;
-      continue; /* back to the beginning of the for loop */
-    }
-    flags = 0;
-    type = aic7xxx_probe(slot, base + AHC_HID0, &flags);
-    if (type == -1)
-    {
-      slot++;
-      continue;
-    }
-    temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC);
-    if (temp_p == NULL)
-    {
-      printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
-      slot++;
-      continue; /* back to the beginning of the while loop */
-    }
-    /*
-     * Lock out other contenders for our i/o space.
-     */
-    request_region(base, MAXREG - MINREG, "aic7xxx");
-
-    /*
-     * Pause the card preserving the IRQ type.  Allow the operator
-     * to override the IRQ trigger.
-     */
-    if (aic7xxx_irq_trigger == 1)
-      hcntrl = IRQMS;  /* Level */
-    else if (aic7xxx_irq_trigger == 0)
-      hcntrl = 0;  /* Edge */
-    else
-      hcntrl = inb(base + HCNTRL) & IRQMS;  /* Default */
-    memset(temp_p, 0, sizeof(struct aic7xxx_host));
-    temp_p->unpause = hcntrl | INTEN;
-    temp_p->pause = hcntrl | PAUSE | INTEN;
-    temp_p->base = base;
-    temp_p->mbase = 0;
-    temp_p->maddr = 0;
-    temp_p->pci_bus = 0;
-    temp_p->pci_device_fn = slot;
-    aic_outb(temp_p, hcntrl | PAUSE, HCNTRL);
-    while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ;
-    if (aic7xxx_chip_reset(temp_p) == -1)
-      temp_p->irq = 0;
-    else
-      temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F;
-    temp_p->flags |= AHC_PAGESCBS;
-
-    switch (temp_p->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 %d, ignoring.\n", temp_p->irq);
-        kfree(temp_p);
-        release_region(base, MAXREG - MINREG);
-        slot++;
-        continue; /* back to the beginning of the while loop */
-    }
-
-    /*
-     * We are commited now, everything has been checked and this card
-     * has been found, now we just set it up
-     */
-
-    /*
-     * Insert our new struct into the list at the end
-     */
-    if (list_p == NULL)
-    {
-      list_p = current_p = temp_p;
-    }
-    else
-    {
-      current_p = list_p;
-      while (current_p->next != NULL)
-        current_p = current_p->next;
-      current_p->next = temp_p;
-    }
-
-    switch (type)
-    {
-      case 0:
-        temp_p->board_name_index = 2;
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk("aic7xxx: <%s> at EISA %d\n",
-               board_names[2], slot);
-        /* FALLTHROUGH */
-      case 1:
-      {
-        temp_p->chip = AHC_AIC7770 | AHC_EISA;
-        temp_p->features |= AHC_AIC7770_FE;
-        temp_p->bios_control = aic_inb(temp_p, 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 (temp_p->board_name_index == 0)
-        {
-          temp_p->board_name_index = 3;
-          if (aic7xxx_verbose & VERBOSE_PROBE2)
-            printk("aic7xxx: <%s> at EISA %d\n",
-                 board_names[3], slot);
-        }
-        if (temp_p->bios_control & CHANNEL_B_PRIMARY)
-        {
-          temp_p->flags |= AHC_CHANNEL_B_PRIMARY;
-        }
-
-        if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED)
-        {
-          temp_p->flags &= ~AHC_BIOS_ENABLED;
-        }
-        else
-        {
-          temp_p->flags &= ~AHC_USEDEFAULTS;
-          temp_p->flags |= AHC_BIOS_ENABLED;
-          if ( (temp_p->bios_control & 0x20) == 0 )
-          {
-            temp_p->bios_address = 0xcc000;
-            temp_p->bios_address += (0x4000 * (temp_p->bios_control & 0x07));
-          }
-          else
-          {
-            temp_p->bios_address = 0xd0000;
-            temp_p->bios_address += (0x8000 * (temp_p->bios_control & 0x06));
-          }
-        }
-        temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8;
-        temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1);
-        if (temp_p->features & AHC_WIDE)
-        {
-          temp_p->scsi_id = temp_p->adapter_control & HWSCSIID;
-          temp_p->scsi_id_b = temp_p->scsi_id;
-        }
-        else
-        {
-          temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID;
-          temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID;
-        }
-        aic7xxx_load_seeprom(temp_p, &sxfrctl1);
-        break;
-      }
-
-      case 2:
-      case 3:
-        temp_p->chip = AHC_AIC7770 | AHC_VL;
-        temp_p->features |= AHC_AIC7770_FE;
-        if (type == 2)
-          temp_p->flags |= AHC_BIOS_ENABLED;
-        else
-          temp_p->flags &= ~AHC_BIOS_ENABLED;
-        if (aic_inb(temp_p, SCSICONF) & TERM_ENB)
-          sxfrctl1 = STPWEN;
-        aic7xxx_load_seeprom(temp_p, &sxfrctl1);
-        temp_p->board_name_index = 4;
-        if (aic7xxx_verbose & VERBOSE_PROBE2)
-          printk("aic7xxx: <%s> at VLB %d\n",
-               board_names[2], slot);
-        switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL )
-        {
-          case 0x00:
-            temp_p->bios_address = 0xe0000;
-            break;
-          case 0x20:
-            temp_p->bios_address = 0xc8000;
-            break;
-          case 0x40:
-            temp_p->bios_address = 0xd0000;
-            break;
-          case 0x60:
-            temp_p->bios_address = 0xd8000;
-            break;
-          default:
-            break; /* can't get here */
-        }
-        break;
-
-      default:  /* Won't get here. */
-        break;
-    }
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-    {
-      printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%lx, IRQ %d (%s)\n",
-        (temp_p->flags & AHC_USEDEFAULTS) ? "dis" : "en", temp_p->base,
-        temp_p->irq,
-        (temp_p->pause & IRQMS) ? "level sensitive" : "edge triggered");
-      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
-             (temp_p->flags & AHC_EXTEND_TRANS_A) ? "en" : "dis");
-    }
-
-    /*
-     * Set the FIFO threshold and the bus off time.
-     */
-    hostconf = aic_inb(temp_p, HOSTCONF);
-    aic_outb(temp_p, hostconf & DFTHRSH, BUSSPD);
-    aic_outb(temp_p, (hostconf << 2) & BOFF, BUSTIME);
-    slot++;
-    found++;
-  }
-
-#endif /* defined(__i386__) || defined(__alpha__) */
-
-  /*
-   * Now, we re-order the probed devices by BIOS address and BUS class.
-   * In general, we follow this algorithm to make the adapters show up
-   * in the same order under linux that the computer finds them.
-   *  1: All VLB/EISA cards with BIOS_ENABLED first, according to BIOS
-   *     address, going from lowest to highest.
-   *  2: All PCI controllers with BIOS_ENABLED next, according to BIOS
-   *     address, going from lowest to highest.
-   *  3: Remaining VLB/EISA controllers going in slot order.
-   *  4: Remaining PCI controllers, going in PCI device order (reversable)
-   */
-
-  {
-    struct aic7xxx_host *sort_list[4] = { NULL, NULL, NULL, NULL };
-    struct aic7xxx_host *vlb, *pci;
-    struct aic7xxx_host *prev_p;
-    struct aic7xxx_host *p;
-    unsigned char left;
-
-    prev_p = vlb = pci = NULL;
-
-    temp_p = list_p;
-    while (temp_p != NULL)
-    {
-      switch(temp_p->chip & ~AHC_CHIPID_MASK)
-      {
-        case AHC_EISA:
-        case AHC_VL:
-        {
-          p = temp_p;
-          if (p->flags & AHC_BIOS_ENABLED)
-            vlb = sort_list[0];
-          else
-            vlb = sort_list[2];
-
-          if (vlb == NULL)
-          {
-            vlb = temp_p;
-            temp_p = temp_p->next;
-            vlb->next = NULL;
-          }
-          else
-          {
-            current_p = vlb;
-            prev_p = NULL;
-            while ( (current_p != NULL) &&
-                    (current_p->bios_address < temp_p->bios_address))
-            {
-              prev_p = current_p;
-              current_p = current_p->next;
-            }
-            if (prev_p != NULL)
-            {
-              prev_p->next = temp_p;
-              temp_p = temp_p->next;
-              prev_p->next->next = current_p;
-            }
-            else
-            {
-              vlb = temp_p;
-              temp_p = temp_p->next;
-              vlb->next = current_p;
-            }
-          }
-          
-          if (p->flags & AHC_BIOS_ENABLED)
-            sort_list[0] = vlb;
-          else
-            sort_list[2] = vlb;
-          
-          break;
-        }
-        default:  /* All PCI controllers fall through to default */
-        {
-
-          p = temp_p;
-          if (p->flags & AHC_BIOS_ENABLED) 
-            pci = sort_list[1];
-          else
-            pci = sort_list[3];
-
-          if (pci == NULL)
-          {
-            pci = temp_p;
-            temp_p = temp_p->next;
-            pci->next = NULL;
-          }
-          else
-          {
-            current_p = pci;
-            prev_p = NULL;
-            if (!aic7xxx_reverse_scan)
-            {
-              while ( (current_p != NULL) &&
-                      ( (PCI_SLOT(current_p->pci_device_fn) |
-                        (current_p->pci_bus << 8)) < 
-                        (PCI_SLOT(temp_p->pci_device_fn) |
-                        (temp_p->pci_bus << 8)) ) )
-              {
-                prev_p = current_p;
-                current_p = current_p->next;
-              }
-            }
-            else
-            {
-              while ( (current_p != NULL) &&
-                      ( (PCI_SLOT(current_p->pci_device_fn) |
-                        (current_p->pci_bus << 8)) > 
-                        (PCI_SLOT(temp_p->pci_device_fn) |
-                        (temp_p->pci_bus << 8)) ) )
-              {
-                prev_p = current_p;
-                current_p = current_p->next;
-              }
-            }
-            /*
-             * Are we dealing with a 7895/6/7/9 where we need to sort the
-             * channels as well, if so, the bios_address values should
-             * be the same
-             */
-            if ( (current_p) && (temp_p->flags & AHC_MULTI_CHANNEL) &&
-                 (temp_p->pci_bus == current_p->pci_bus) &&
-                 (PCI_SLOT(temp_p->pci_device_fn) ==
-                  PCI_SLOT(current_p->pci_device_fn)) )
-            {
-              if (temp_p->flags & AHC_CHNLB)
-              {
-                if ( !(temp_p->flags & AHC_CHANNEL_B_PRIMARY) )
-                {
-                  prev_p = current_p;
-                  current_p = current_p->next;
-                }
-              }
-              else
-              {
-                if (temp_p->flags & AHC_CHANNEL_B_PRIMARY)
-                {
-                  prev_p = current_p;
-                  current_p = current_p->next;
-                }
-              }
-            }
-            if (prev_p != NULL)
-            {
-              prev_p->next = temp_p;
-              temp_p = temp_p->next;
-              prev_p->next->next = current_p;
-            }
-            else
-            {
-              pci = temp_p;
-              temp_p = temp_p->next;
-              pci->next = current_p;
-            }
-          }
-
-          if (p->flags & AHC_BIOS_ENABLED)
-            sort_list[1] = pci;
-          else
-            sort_list[3] = pci;
-
-          break;
-        }
-      }  /* End of switch(temp_p->type) */
-    } /* End of while (temp_p != NULL) */
-    /*
-     * At this point, the cards have been broken into 4 sorted lists, now
-     * we run through the lists in order and register each controller
-     */
-    {
-      int i;
-      
-      left = found;
-      for (i=0; i<NUMBER(sort_list); i++)
-      {
-        temp_p = sort_list[i];
-        while(temp_p != NULL)
-        {
-          template->name = board_names[temp_p->board_name_index];
-          p = aic7xxx_alloc(template, temp_p);
-          if (p != NULL)
-          {
-            p->instance = found - left;
-            if (aic7xxx_register(template, p, (--left)) == 0)
-            {
-              found--;
-              aic7xxx_release(p->host);
-              scsi_unregister(p->host);
-            }
-            else if (aic7xxx_dump_card)
-            {
-              pause_sequencer(p);
-              aic7xxx_print_card(p);
-              aic7xxx_print_scratch_ram(p);
-              unpause_sequencer(p, TRUE);
-            }
-          }
-          current_p = temp_p;
-          temp_p = (struct aic7xxx_host *)temp_p->next;
-          kfree(current_p);
-        }
-      }
-    }
-  }
-  return (found);
-}
-
-static void aic7xxx_build_negotiation_cmnd(struct aic7xxx_host *p,
-                                           Scsi_Cmnd *old_cmd, int tindex);
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_allocate_negotiation_command
- *
- * Description:
- *   allocate the actual command struct and fill in the gaps...
- *-F*************************************************************************/
-static Scsi_Cmnd *
-aic7xxx_allocate_negotiation_command(struct aic7xxx_host *p,
-                                     Scsi_Cmnd *old_cmd, int tindex)
-{
-  Scsi_Cmnd *cmd;
-  char *buffer;
-
-  if (!(p->dev_dtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) )
-  {
-    return(NULL);
-  }
-  if (!(buffer = kmalloc(256, GFP_ATOMIC)))
-  {
-    kfree(p->dev_dtr_cmnd[tindex]);
-    p->dev_dtr_cmnd[tindex] = NULL;
-    return(NULL);
-  }
-  cmd = p->dev_dtr_cmnd[tindex];
-  memset(cmd, 0, sizeof(Scsi_Cmnd));
-  memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd));
-  memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd));
-  memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd));
-  cmd->lun = 0;
-  cmd->request_bufflen = 255;
-  cmd->request_buffer = buffer;
-  cmd->sc_data_direction = SCSI_DATA_READ;
-  cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0;
-  cmd->bufflen = 0;
-  cmd->buffer = NULL;
-  cmd->underflow = 0;
-  cmd->cmd_len = 6;
-  cmd->cmnd[0] = cmd->data_cmnd[0] = INQUIRY;
-  cmd->cmnd[1] = cmd->data_cmnd[1] = 0;
-  cmd->cmnd[2] = cmd->data_cmnd[2] = 0;
-  cmd->cmnd[3] = cmd->data_cmnd[3] = 0;
-  cmd->cmnd[4] = cmd->data_cmnd[4] = 255; /* match what scsi.c does here */
-  cmd->cmnd[5] = cmd->data_cmnd[5] = 0;
-  return(cmd);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_negotiation_complete
- *
- * Description:
- *   Handle completion events for our Negotiation commands.  Clear out the
- *   struct and get it ready for its next use.
- *-F*************************************************************************/
-static void
-aic7xxx_negotiation_complete(Scsi_Cmnd *cmd)
-{
-  unsigned int checksum;
-  int i;
-  int *ibuffer;
-  struct aic7xxx_host *p = (struct aic7xxx_host *)cmd->host->hostdata;
-  int tindex = TARGET_INDEX(cmd);
-  struct aic7xxx_syncrate *syncrate;
-
-  /*
-   * perform our minimalistic domain validation
-   */
-  if(p->dev_flags[tindex] & DEVICE_SCANNED)
-  {
-    ibuffer = (int *)cmd->request_buffer;
-    checksum = 0;
-    for(i = 0; i < (cmd->request_bufflen >> 2); i++)
-    {
-      checksum += ibuffer[i];
-    }
-    if( (checksum != p->dev_checksum[tindex]) &&
-        (p->transinfo[tindex].cur_offset != 0) )
-    {
-      unsigned int period = p->transinfo[tindex].cur_period;
-      unsigned char options = p->transinfo[tindex].cur_options;
-
-      if (p->needdv & (1<<tindex))
-      {
-        /*
-         * oops, we had a failure, lower the transfer rate and try again.  It's
-         * worth noting here that it might be wise to also check for typical
-         * wide setting on narrow cable type problems and try disabling wide
-         * instead of slowing down if those exist.  That's hard to do with simple
-         * checksums though.
-         */
-        if(aic7xxx_verbose & VERBOSE_NEGOTIATION2) 
-        {
-          printk(INFO_LEAD "reducing SCSI transfer speed due to Domain "
-                 "validation failure.\n", p->host_no, CTL_OF_CMD(cmd));
-        }
-        if((syncrate = aic7xxx_find_syncrate(p, &period, 0, &options)) != NULL)
-        {
-          syncrate++;
-          if( (syncrate->rate[0] != NULL) &&
-              (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) )
-          {
-            p->transinfo[tindex].goal_period = syncrate->period;
-            if( p->transinfo[tindex].goal_period > 9 )
-            {
-              p->transinfo[tindex].goal_options = 0;
-              p->needppr &= ~(1<<tindex);
-              p->needsdtr |= (1<<tindex);
-              p->needppr_copy &= ~(1<<tindex);
-              p->needsdtr_copy |= (1<<tindex);
-              if (p->transinfo[tindex].goal_width)
-              {
-                p->needwdtr |= (1<<tindex);
-                p->needwdtr_copy |= (1<<tindex);
-              }
-            }
-          }
-          else if (p->transinfo[tindex].goal_width)
-          {
-            p->transinfo[tindex].goal_width = 0;
-            p->needwdtr &= ~(1<<tindex);
-            p->needwdtr_copy &= ~(1<<tindex);
-            p->transinfo[tindex].goal_offset =
-              p->transinfo[tindex].user_offset;
-            p->transinfo[tindex].goal_period =
-              p->transinfo[tindex].user_period;
-            p->transinfo[tindex].goal_options =
-              p->transinfo[tindex].user_options;
-            if( p->transinfo[tindex].goal_period <= 9 )
-            {
-              p->needppr |= (1<<tindex);
-              p->needsdtr &= ~(1<<tindex);
-              p->needppr_copy |= (1<<tindex);
-              p->needsdtr_copy &= ~(1<<tindex);
-            }
-            else
-            {
-              p->needppr &= ~(1<<tindex);
-              p->needsdtr |= (1<<tindex);
-              p->needppr_copy &= ~(1<<tindex);
-              p->needsdtr_copy |= (1<<tindex);
-            }
-          }
-          else
-          {
-            p->transinfo[tindex].goal_offset = 0;
-            p->transinfo[tindex].goal_period = 255;
-            p->transinfo[tindex].goal_options = 0;
-            p->transinfo[tindex].goal_width = 0;
-            p->needppr &= ~(1<<tindex);
-            p->needsdtr &= ~(1<<tindex);
-            p->needwdtr &= ~(1<<tindex);
-            p->needppr_copy &= ~(1<<tindex);
-            p->needsdtr_copy &= ~(1<<tindex);
-            p->needwdtr_copy &= ~(1<<tindex);
-          }
-        }
-        p->needdv &= ~(1<<tindex);
-      }
-      else
-      {
-        if(aic7xxx_verbose & VERBOSE_NEGOTIATION2) 
-        {
-          printk(INFO_LEAD "Performing Domain validation.\n",
-                 p->host_no, CTL_OF_CMD(cmd));
-        }
-        /*
-         * Update the checksum in case the INQUIRY data has changed, maybe
-         * in relation to a change in the mode pages, or whatever.
-         */
-        p->dev_checksum[tindex] = checksum;
-        /*
-         * Signal that we are trying out the domain validation
-         */
-        p->needdv |= (1<<tindex);
-        /*
-         * Signal that we need to re-negotiate things, this also gets us our
-         * INQUIRY command to re-checksum off of.
-         */
-        p->needppr |= (p->needppr_copy & (1<<tindex));
-        p->needsdtr |= (p->needsdtr_copy & (1<<tindex));
-        p->needwdtr |= (p->needwdtr_copy & (1<<tindex));
-      }
-    } 
-    else
-    {
-      if( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
-          (p->needdv & (1<<tindex)) )
-      {
-        printk(INFO_LEAD "Successfully completed Domain validation.\n",
-               p->host_no, CTL_OF_CMD(cmd));
-      }
-      /*
-       * We successfully did our checksum, so don't leave the needdv flag set
-       * in case we might have set it last time through.
-       */
-      p->needdv &= ~(1<<tindex);
-    }
-  }
-
-  p->dtr_pending &= ~(0x01 << tindex);
-  /*
-   * This looks recursive in the extreme, but if this was a WDTR negotiation
-   * and we didn't follow up with SDTR yet, then this will get it started.
-   * For all other cases, this should work out to be a no-op, unless we are
-   * doing domain validation and happen to need a new negotiation command.
-   *
-   * In case we don't want this to go any further, the cmdcmplt interrupt
-   * handler will NULL out the cmd->next entry so that the real SCSI command
-   * can be sent back to the mid layer code with SENSE data intact.  We'll
-   * finish things up when the cmd gets sent back down to us, so no worries.
-   */
-  if(cmd->next)
-  {
-    aic7xxx_build_negotiation_cmnd(p, cmd->next, tindex);
-  }
-  return;
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_build_negotiation_command
- *
- * Description:
- *   Build a Scsi_Cmnd structure to perform negotiation with or else send
- *   a pre-built command specifically for this purpose.
- *-F*************************************************************************/
-static void
-aic7xxx_build_negotiation_cmnd(struct aic7xxx_host *p, Scsi_Cmnd *old_cmd,
-  int tindex)
-{
-
-  if ( !(p->dtr_pending & (1<<tindex)) &&
-       ( (p->needppr & (1<<tindex)) ||
-         (p->needwdtr & (1<<tindex)) ||
-         (p->needsdtr & (1<<tindex)) ) )
-  {
-    if ( (p->dev_dtr_cmnd[tindex] == NULL) &&
-         (aic7xxx_allocate_negotiation_command(p, old_cmd, tindex) == NULL) )
-    {
-      return;
-    }
-    /*
-     * Before sending this thing out, we also make the cmd->next pointer
-     * point to the real command so we can stuff any possible SENSE data
-     * into the real command instead of this fake command.  This has to be
-     * done each time the command is built, not just the first time, hence
-     * it's outside of the above if()...
-     */
-    p->dev_dtr_cmnd[tindex]->next = old_cmd;
-    /*
-     * Clear the buffer so checksums come out right....
-     */
-    memset(p->dev_dtr_cmnd[tindex]->request_buffer, 0,
-           p->dev_dtr_cmnd[tindex]->request_bufflen);
-    /*
-     * Remove any commands for this particular device that might be on the
-     * waiting_scbs queue or qinfifo so that this command goes out first.
-     * This is vital for our implementation of domain validation.
-     */
-    pause_sequencer(p);
-    aic7xxx_search_qinfifo(p, old_cmd->target, old_cmd->channel, ALL_LUNS,
-                SCB_LIST_NULL, 0, TRUE, &p->delayed_scbs[tindex]);
-    unpause_sequencer(p, FALSE);
-    {
-      struct aic7xxx_scb *scb, *next;
-
-      scb = p->waiting_scbs.head;
-      while(scb != NULL)
-      {
-        if( aic7xxx_match_scb(p, scb, old_cmd->target, old_cmd->channel,
-                              ALL_LUNS, SCB_LIST_NULL) )
-        {
-          next = scb->q_next;
-          scbq_remove(&p->waiting_scbs, scb);
-          scbq_insert_tail(&p->delayed_scbs[tindex], scb);
-          scb = next;
-        }
-        else
-        {
-          scb = scb->q_next;
-        }
-      }
-    }
-    aic7xxx_queue(p->dev_dtr_cmnd[tindex], 
-                  aic7xxx_negotiation_complete);
-  }
-}
-
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_print_scb
- *
- * Description:
- *   Dump the byte codes for an about to be sent SCB.
- *-F*************************************************************************/
-static void
-aic7xxx_print_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
-{
-  int i;
-  unsigned char *x;  
-
-  x = (unsigned char *)&scb->hscb->control;
-
-  for(i=0; i<32; i++)
-  {
-    printk("%02x ", x[i]);
-  }
-  printk("\n");
-}
-#endif
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_buildscb
- *
- * Description:
- *   Build a SCB.
- *-F*************************************************************************/
-static void
-aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
-    struct aic7xxx_scb *scb)
-{
-  unsigned short mask;
-  struct aic7xxx_hwscb *hscb;
-  unsigned char tindex = TARGET_INDEX(cmd);
-
-  mask = (0x01 << tindex);
-  hscb = scb->hscb;
-
-  /*
-   * Setup the control byte if we need negotiation and have not
-   * already requested it.
-   */
-  hscb->control = 0;
-  scb->tag_action = 0;
-  cmd->tag = hscb->tag;
-  if (p->discenable & mask)
-  {
-    hscb->control |= DISCENB;
-    if ( (p->tagenable & mask) &&
-         (cmd->cmnd[0] != TEST_UNIT_READY) )
-    {
-      p->dev_commands_sent[tindex]++;
-      if (p->dev_commands_sent[tindex] < 200)
-      {
-        hscb->control |= MSG_SIMPLE_Q_TAG;
-        scb->tag_action = MSG_SIMPLE_Q_TAG;
-      }
-      else
-      {
-        if (p->orderedtag & mask)
-        {
-          hscb->control |= MSG_ORDERED_Q_TAG;
-          scb->tag_action = MSG_ORDERED_Q_TAG;
-        }
-        else
-        {
-          hscb->control |= MSG_SIMPLE_Q_TAG;
-          scb->tag_action = MSG_SIMPLE_Q_TAG;
-        }
-        p->dev_commands_sent[tindex] = 0;
-      }
-    }
-  }
-  if ( cmd == p->dev_dtr_cmnd[tindex] )
-  {
-    p->dtr_pending |= mask;
-    scb->tag_action = 0;
-    if (p->dev_flags[tindex] & DEVICE_SCANNED)
-    {
-      hscb->control &= DISCENB;
-      hscb->control |= MK_MESSAGE;
-      if(p->needppr & mask)
-      {
-        scb->flags |= SCB_MSGOUT_PPR;
-      }
-      else if(p->needwdtr & mask)
-      {
-        scb->flags |= SCB_MSGOUT_WDTR;
-      }
-      else if(p->needsdtr & mask)
-      {
-        scb->flags |= SCB_MSGOUT_SDTR;
-      }
-    }
-  }
-  if ( !(p->dtr_pending & mask) &&
-        ( (p->needppr & mask) ||
-          (p->needwdtr & mask) ||
-          (p->needsdtr & mask) ) )
-  {
-    aic7xxx_build_negotiation_cmnd(p, cmd, tindex);
-  }
-  hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
-        ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
-
-  /*
-   * The interpretation of request_buffer and request_bufflen
-   * changes depending on whether or not use_sg is zero; a
-   * non-zero use_sg indicates the number of elements in the
-   * scatter-gather array.
-   */
-
-  /*
-   * XXX - this relies on the host data being stored in a
-   *       little-endian format.
-   */
-  hscb->SCSI_cmd_length = cmd->cmd_len;
-  memcpy(scb->cmnd, cmd->cmnd, cmd->cmd_len);
-  hscb->SCSI_cmd_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, scb->cmnd));
-
-  if (cmd->use_sg)
-  {
-    struct scatterlist *sg;  /* Must be mid-level SCSI code scatterlist */
-
-    /*
-     * We must build an SG list in adapter format, as the kernel's SG list
-     * cannot be used directly because of data field size (__alpha__)
-     * differences and the kernel SG list uses virtual addresses where
-     * we need physical addresses.
-     */
-    int i, use_sg;
-
-    sg = (struct scatterlist *)cmd->request_buffer;
-    scb->sg_length = 0;
-    use_sg = pci_map_sg(p->pdev, sg, cmd->use_sg, scsi_to_pci_dma_dir(cmd->sc_data_direction));
-    /*
-     * Copy the segments into the SG array.  NOTE!!! - We used to
-     * have the first entry both in the data_pointer area and the first
-     * SG element.  That has changed somewhat.  We still have the first
-     * entry in both places, but now we download the address of
-     * scb->sg_list[1] instead of 0 to the sg pointer in the hscb.
-     */
-    for (i = 0; i < use_sg; i++)
-    {
-      unsigned int len = sg_dma_len(sg+i);
-      scb->sg_list[i].address = cpu_to_le32(sg_dma_address(sg+i));
-      scb->sg_list[i].length = cpu_to_le32(len);
-      scb->sg_length += len;
-    }
-    /* 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->sg_count = i;
-    hscb->SG_segment_count = i;
-    hscb->SG_list_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, &scb->sg_list[1]));
-  }
-  else
-  {
-    if (cmd->request_bufflen)
-    {
-      unsigned int address = pci_map_single(p->pdev, cmd->request_buffer,
-					    cmd->request_bufflen,
-                                            scsi_to_pci_dma_dir(cmd->sc_data_direction));
-      aic7xxx_mapping(cmd) = address;
-      scb->sg_list[0].address = cpu_to_le32(address);
-      scb->sg_list[0].length = cpu_to_le32(cmd->request_bufflen);
-      scb->sg_count = 1;
-      scb->sg_length = cmd->request_bufflen;
-      hscb->SG_segment_count = 1;
-      hscb->SG_list_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, &scb->sg_list[0]));
-      hscb->data_count = scb->sg_list[0].length;
-      hscb->data_pointer = scb->sg_list[0].address;
-    }
-    else
-    {
-      scb->sg_count = 0;
-      scb->sg_length = 0;
-      hscb->SG_segment_count = 0;
-      hscb->SG_list_pointer = 0;
-      hscb->data_count = 0;
-      hscb->data_pointer = 0;
-    }
-  }
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_queue
- *
- * Description:
- *   Queue a SCB to the controller.
- *-F*************************************************************************/
-int
-aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
-{
-  struct aic7xxx_host *p;
-  struct aic7xxx_scb *scb;
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-  int tindex = TARGET_INDEX(cmd);
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-  unsigned long cpu_flags = 0;
-#endif
-
-  p = (struct aic7xxx_host *) cmd->host->hostdata;
-  /*
-   * Check to see if channel was scanned.
-   */
-  
-#ifdef AIC7XXX_VERBOSE_DEBUGGING
-  if (!(p->flags & AHC_A_SCANNED) && (cmd->channel == 0))
-  {
-    if (aic7xxx_verbose & VERBOSE_PROBE2)
-      printk(INFO_LEAD "Scanning channel for devices.\n",
-        p->host_no, 0, -1, -1);
-    p->flags |= AHC_A_SCANNED;
-  }
-  else
-  {
-    if (!(p->flags & AHC_B_SCANNED) && (cmd->channel == 1))
-    {
-      if (aic7xxx_verbose & VERBOSE_PROBE2)
-        printk(INFO_LEAD "Scanning channel for devices.\n",
-          p->host_no, 1, -1, -1);
-      p->flags |= AHC_B_SCANNED;
-    }
-  }
-
-  if (p->dev_active_cmds[tindex] > (cmd->device->queue_depth + 1))
-  {
-    printk(WARN_LEAD "Commands queued exceeds queue "
-           "depth, active=%d\n",
-           p->host_no, CTL_OF_CMD(cmd), 
-           p->dev_active_cmds[tindex]);
-    if ( p->dev_active_cmds[tindex] > 220 )
-      p->dev_active_cmds[tindex] = 0;
-  }
-#endif
-
-  scb = scbq_remove_head(&p->scb_data->free_scbs);
-  if (scb == NULL)
-  {
-    DRIVER_LOCK
-    aic7xxx_allocate_scb(p);
-    DRIVER_UNLOCK
-    scb = scbq_remove_head(&p->scb_data->free_scbs);
-  }
-  if (scb == NULL)
-  {
-    printk(WARN_LEAD "Couldn't get a free SCB.\n", p->host_no,
-           CTL_OF_CMD(cmd));
-    cmd->result = (DID_BUS_BUSY << 16);
-    DRIVER_LOCK
-    aic7xxx_queue_cmd_complete(p, cmd);
-    DRIVER_UNLOCK
-    return 0;
-  }
-  else
-  {
-    scb->cmd = cmd;
-    aic7xxx_position(cmd) = scb->hscb->tag;
-
-    /*
-     * Construct the SCB beforehand, so the sequencer is
-     * paused a minimal amount of time.
-     */
-    aic7xxx_buildscb(p, cmd, scb);
-
-    /*
-     * Make sure the Scsi_Cmnd pointer is saved, the struct it points to
-     * is set up properly, and the parity error flag is reset, then send
-     * the SCB to the sequencer and watch the fun begin.
-     */
-    cmd->scsi_done = fn;
-    cmd->result = DID_OK;
-    memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
-    aic7xxx_error(cmd) = DID_OK;
-    aic7xxx_status(cmd) = 0;
-    cmd->host_scribble = NULL;
-
-    scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
-
-    DRIVER_LOCK
-    scbq_insert_tail(&p->waiting_scbs, scb);
-    if ( (p->flags & (AHC_IN_ISR | AHC_IN_ABORT | AHC_IN_RESET)) == 0)
-    {
-      aic7xxx_run_waiting_queues(p);
-    }
-    DRIVER_UNLOCK
-  }
-  return (0);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_bus_device_reset
- *
- * Description:
- *   Abort or reset the current SCSI command(s).  If the scb has not
- *   previously been aborted, then we attempt to send a BUS_DEVICE_RESET
- *   message to the target.  If the scb has previously been unsuccessfully
- *   aborted, then we will reset the channel and have all devices renegotiate.
- *   Returns an enumerated type that indicates the status of the operation.
- *-F*************************************************************************/
-static int
-aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
-{
-  struct aic7xxx_scb   *scb;
-  struct aic7xxx_hwscb *hscb;
-  int result = -1;
-  int channel;
-  unsigned char saved_scbptr, lastphase;
-  unsigned char hscb_index;
-  int disconnected;
-
-  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
-  hscb = scb->hscb;
-
-  lastphase = aic_inb(p, LASTPHASE);
-  if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-  {
-    printk(INFO_LEAD "Bus Device reset, scb flags 0x%x, ",
-         p->host_no, CTL_OF_SCB(scb), scb->flags);
-    switch (lastphase)
-    {
-      case P_DATAOUT:
-        printk("Data-Out phase\n");
-        break;
-      case P_DATAIN:
-        printk("Data-In phase\n");
-        break;
-      case P_COMMAND:
-        printk("Command phase\n");
-        break;
-      case P_MESGOUT:
-        printk("Message-Out phase\n");
-        break;
-      case P_STATUS:
-        printk("Status phase\n");
-        break;
-      case P_MESGIN:
-        printk("Message-In phase\n");
-        break;
-      default:
-      /*
-       * We're not in a valid phase, so assume we're idle.
-       */
-        printk("while idle, LASTPHASE = 0x%x\n", lastphase);
-        break;
-    }
-    printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
-         "0x%x\n", p->host_no, CTL_OF_SCB(scb),
-         aic_inb(p, SCSISIGI),
-         aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
-         aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
-  }
-
-  channel = cmd->channel;
-
-    /*
-     * Send a Device Reset 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 = aic_inb(p, SCBPTR);
-  disconnected = FALSE;
-
-  if (lastphase != P_BUSFREE)
-  {
-    if (aic_inb(p, SCB_TAG) >= p->scb_data->numscbs)
-    {
-      printk(WARN_LEAD "Invalid SCB ID %d is active, "
-             "SCB flags = 0x%x.\n", p->host_no,
-            CTL_OF_CMD(cmd), scb->hscb->tag, scb->flags);
-      return(SCSI_RESET_ERROR);
-    }
-    if (scb->hscb->tag == aic_inb(p, SCB_TAG))
-    { 
-      if ( (lastphase != P_MESGOUT) && (lastphase != P_MESGIN) )
-      {
-        if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-          printk(INFO_LEAD "Device reset message in "
-                "message buffer\n", p->host_no, CTL_OF_SCB(scb));
-        scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
-        aic7xxx_error(scb->cmd) = DID_RESET;
-        p->dev_flags[TARGET_INDEX(scb->cmd)] |= 
-                BUS_DEVICE_RESET_PENDING;
-        /* Send the abort message to the active SCB. */
-        aic_outb(p, HOST_MSG, MSG_OUT);
-        aic_outb(p, lastphase | ATNO, SCSISIGO);
-        return(SCSI_RESET_PENDING);
-      }
-      else
-      {
-        /* We want to send out the message, but it could screw an already */
-        /* in place and being used message.  Instead, we return an error  */
-        /* to try and start the bus reset phase since this command is     */
-        /* probably hung (aborts failed, and now reset is failing).  We   */
-        /* also make sure to set BUS_DEVICE_RESET_PENDING so we won't try */
-        /* any more on this device, but instead will escalate to a bus or */
-        /* host reset (additionally, we won't try to abort any more).     */
-        printk(WARN_LEAD "Device reset, Message buffer "
-                "in use\n", p->host_no, CTL_OF_SCB(scb));
-        scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
-        aic7xxx_error(scb->cmd) = DID_RESET;
-        p->dev_flags[TARGET_INDEX(scb->cmd)] |= 
-                BUS_DEVICE_RESET_PENDING;
-        return(SCSI_RESET_ERROR);
-      }
-    }
-  } /* if (last_phase != P_BUSFREE).....indicates we are idle and can work */
-  hscb_index = aic7xxx_find_scb(p, scb);
-  if (hscb_index == SCB_LIST_NULL)
-  {
-    disconnected = (aic7xxx_scb_on_qoutfifo(p, scb)) ? FALSE : TRUE;
-  }
-  else
-  {
-    aic_outb(p, hscb_index, SCBPTR);
-    if (aic_inb(p, SCB_CONTROL) & DISCONNECTED)
-    {
-      disconnected = TRUE;
-    }
-  }
-  if (disconnected)
-  {
-        /*
-         * Simply set the MK_MESSAGE flag and the SEQINT handler will do
-         * the rest on a reconnect.
-         */
-    scb->hscb->control |= MK_MESSAGE;
-    scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
-    p->dev_flags[TARGET_INDEX(scb->cmd)] |= 
-        BUS_DEVICE_RESET_PENDING;
-    if (hscb_index != SCB_LIST_NULL)
-    {
-      unsigned char scb_control;
-
-      aic_outb(p, hscb_index, SCBPTR);
-      scb_control = aic_inb(p, SCB_CONTROL);
-      aic_outb(p, scb_control | MK_MESSAGE, 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, then this will be the only
-         * outstanding command and we can simply shove it on the
-         * qoutfifo and be done.  If it is tagged, then it goes right
-         * in with all the others, no problem :)  We need to add it
-         * to the qinfifo and let the sequencer know it is there.
-         * Now, the only problem left to deal with is, *IF* this
-         * command completes, in spite of the MK_MESSAGE bit in the
-         * control byte, then we need to pick that up in the interrupt
-         * routine and clean things up.  This *shouldn't* ever happen.
-         */
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-      printk(INFO_LEAD "Queueing device reset "
-           "command.\n", p->host_no, CTL_OF_SCB(scb));
-    p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
-    if (p->features & AHC_QUEUE_REGS)
-      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
-    else
-      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
-    scb->flags |= SCB_QUEUED_ABORT;
-    result = SCSI_RESET_PENDING;
-  }
-  else if (result == -1)
-  {
-    result = SCSI_RESET_ERROR;
-  }
-  aic_outb(p, saved_scbptr, SCBPTR);
-  return (result);
-}
-
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_panic_abort
- *
- * Description:
- *   Abort the current SCSI command(s).
- *-F*************************************************************************/
-void
-aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
-{
-
-  printk("aic7xxx driver version %s/%s\n", AIC7XXX_C_VERSION,
-         UTS_RELEASE);
-  printk("Controller type:\n    %s\n", board_names[p->board_name_index]);
-  printk("p->flags=0x%x, p->chip=0x%x, p->features=0x%x, "
-         "sequencer %s paused\n",
-     p->flags, p->chip, p->features,
-    (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" );
-  pause_sequencer(p);
-  disable_irq(p->irq);
-  aic7xxx_print_card(p);
-  aic7xxx_print_scratch_ram(p);
-  spin_unlock_irq(&io_request_lock);
-  for(;;) barrier();
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_abort
- *
- * Description:
- *   Abort the current SCSI command(s).
- *-F*************************************************************************/
-int
-aic7xxx_abort(Scsi_Cmnd *cmd)
-{
-  struct aic7xxx_scb  *scb = NULL;
-  struct aic7xxx_host *p;
-  int    result, found=0;
-  unsigned char tmp_char, saved_hscbptr, next_hscbptr, prev_hscbptr;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-  unsigned long cpu_flags = 0;
-#endif
-  Scsi_Cmnd *cmd_next, *cmd_prev;
-
-  p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
-
-  /*
-   * I added a new config option to the driver: "panic_on_abort" that will
-   * cause the driver to panic and the machine to stop on the first abort
-   * or reset call into the driver.  At that point, it prints out a lot of
-   * usefull information for me which I can then use to try and debug the
-   * problem.  Simply enable the boot time prompt in order to activate this
-   * code.
-   */
-  if (aic7xxx_panic_on_abort)
-    aic7xxx_panic_abort(p, cmd);
-
-  DRIVER_LOCK
-
-/*
- *  Run the isr to grab any command in the QOUTFIFO and any other misc.
- *  assundry tasks.  This should also set up the bh handler if there is
- *  anything to be done, but it won't run until we are done here since
- *  we are following a straight code path without entering the scheduler
- *  code.
- */
-  pause_sequencer(p);
-  while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
-  {
-    aic7xxx_isr(p->irq, p, (void *)NULL);
-    pause_sequencer(p);
-    aic7xxx_done_cmds_complete(p);
-  }
-
-  if ((scb == NULL) || (cmd->serial_number != cmd->serial_number_at_timeout))
-                      /*  Totally bogus cmd since it points beyond our  */
-  {                   /*  valid SCB range or doesn't even match it's own*/
-                      /*  timeout serial number.                        */
-    if (aic7xxx_verbose & VERBOSE_ABORT_MID)
-      printk(INFO_LEAD "Abort called with bogus Scsi_Cmnd "
-        "pointer.\n", p->host_no, CTL_OF_CMD(cmd));
-    unpause_sequencer(p, FALSE);
-    DRIVER_UNLOCK
-    return(SCSI_ABORT_NOT_RUNNING);
-  }
-  if (scb->cmd != cmd)  /*  Hmmm...either this SCB is currently free with a */
-  {                     /*  NULL cmd pointer (NULLed out when freed) or it  */
-                        /*  has already been recycled for another command   */
-                        /*  Either way, this SCB has nothing to do with this*/
-                        /*  command and we need to deal with cmd without    */
-                        /*  touching the SCB.                               */
-                        /*  The theory here is to return a value that will  */
-                        /*  make the queued for complete command actually   */
-                        /*  finish successfully, or to indicate that we     */
-                        /*  don't have this cmd any more and the mid level  */
-                        /*  code needs to find it.                          */
-    cmd_next = p->completeq.head;
-    cmd_prev = NULL;
-    while (cmd_next != NULL) 
-    {
-      if (cmd_next == cmd) 
-      {
-        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
-          printk(INFO_LEAD "Abort called for command "
-          "on completeq, completing.\n", p->host_no, CTL_OF_CMD(cmd));
-        if ( cmd_prev == NULL )
-          p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
-        else
-          cmd_prev->host_scribble = cmd_next->host_scribble;
-        cmd_next->scsi_done(cmd_next);
-        unpause_sequencer(p, FALSE);
-        DRIVER_UNLOCK
-        return(SCSI_ABORT_NOT_RUNNING); /* It's already back as a successful
-                                         * completion */
-      }                                  
-      cmd_prev = cmd_next;
-      cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
-    }
-    if (aic7xxx_verbose & VERBOSE_ABORT_MID)
-      printk(INFO_LEAD "Abort called for already completed"
-        " command.\n", p->host_no, CTL_OF_CMD(cmd));
-    unpause_sequencer(p, FALSE);
-    DRIVER_UNLOCK
-    return(SCSI_ABORT_NOT_RUNNING);
-  }
-    
-/*   At this point we know the following:
- *     the SCB pointer is valid
- *     the command pointer passed in to us and the scb->cmd pointer match
- *     this then means that the command we need to abort is the same as the
- *     command held by the scb pointer and is a valid abort request.
- *   Now, we just have to figure out what to do from here.  Current plan is:
- *     if we have already been here on this command, escalate to a reset
- *     if scb is on waiting list or QINFIFO, send it back as aborted, but
- *       we also need to be aware of the possibility that we could be using
- *       a faked negotiation command that is holding this command up,  if
- *       so we need to take care of that command instead, which means we
- *       would then treat this one like it was sitting around disconnected
- *       instead.
- *     if scb is on WAITING_SCB list in sequencer, free scb and send back
- *     if scb is disconnected and not completed, abort with abort message
- *     if scb is currently running, then it may be causing the bus to hang
- *       so we want a return value that indicates a reset would be appropriate
- *       if the command does not finish shortly
- *     if scb is already complete but not on completeq, we're screwed because
- *       this can't happen (except if the command is in the QOUTFIFO, in which
- *       case we would like it to complete successfully instead of having to
- *       to be re-done)
- *   All other scenarios already dealt with by previous code.
- */
-
-  if ( scb->flags & (SCB_ABORT | SCB_RESET | SCB_QUEUED_ABORT) )
-  {
-    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
-      printk(INFO_LEAD "SCB aborted once already, "
-        "escalating.\n", p->host_no, CTL_OF_SCB(scb));
-    unpause_sequencer(p, FALSE);
-    DRIVER_UNLOCK
-    return(SCSI_ABORT_SNOOZE);
-  }
-  if ( (p->flags & (AHC_RESET_PENDING | AHC_ABORT_PENDING)) || 
-          (p->dev_flags[TARGET_INDEX(scb->cmd)] & 
-           BUS_DEVICE_RESET_PENDING) )
-  {
-    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
-      printk(INFO_LEAD "Reset/Abort pending for this "
-        "device, not wasting our time.\n", p->host_no, CTL_OF_SCB(scb));
-    unpause_sequencer(p, FALSE);
-    DRIVER_UNLOCK
-    return(SCSI_ABORT_PENDING);
-  }
-
-  found = 0;
-  p->flags |= AHC_IN_ABORT;
-  if (aic7xxx_verbose & VERBOSE_ABORT)
-    printk(INFO_LEAD "Aborting scb %d, flags 0x%x\n",
-         p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
-
-/*
- *   First, let's check to see if the currently running command is our target
- *    since if it is, the return is fairly easy and quick since we don't want
- *    to touch the command in case it might complete, but we do want a timeout
- *    in case it's actually hung, so we really do nothing, but tell the mid
- *    level code to reset the timeout.
- */
-
-  if ( scb->hscb->tag == aic_inb(p, SCB_TAG) )
-  {
-   /*
-    *  Check to see if the sequencer is just sitting on this command, or
-    *   if it's actively being run.
-    */
-    result = aic_inb(p, LASTPHASE);
-    switch (result)
-    {
-      case P_DATAOUT:    /*    For any of these cases, we can assume we are */
-      case P_DATAIN:     /*    an active command and act according.  For    */
-      case P_COMMAND:    /*    anything else we are going to fall on through*/
-      case P_STATUS:     /*    The SCSI_ABORT_SNOOZE will give us two abort */
-      case P_MESGOUT:    /*    chances to finish and then escalate to a     */
-      case P_MESGIN:     /*    reset call                                   */
-        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
-          printk(INFO_LEAD "SCB is currently active.  "
-                "Waiting on completion.\n", p->host_no, CTL_OF_SCB(scb));
-        unpause_sequencer(p, FALSE);
-        p->flags &= ~AHC_IN_ABORT;
-        scb->flags |= SCB_RECOVERY_SCB; /*  Note the fact that we've been  */
-        p->flags |= AHC_ABORT_PENDING;  /*  here so we will know not to    */
-        DRIVER_UNLOCK                   /*  muck with other SCBs if this   */
-        return(SCSI_ABORT_PENDING);     /*  one doesn't complete and clear */
-        break;                          /*  out.                           */
-      default:
-        break;
-    }
-  }
-
-  if ((found == 0) && (scb->flags & SCB_WAITINGQ))
-  {
-    int tindex = TARGET_INDEX(cmd);
-    unsigned short mask;
-
-    mask = (1 << tindex);
-
-    if (p->dtr_pending & mask)
-    {
-      if (p->dev_dtr_cmnd[tindex]->next != cmd)
-        found = 1;
-      else
-        found = 0;
-    }
-    else
-    {
-      found = 1;
-    }
-    if (found == 0)
-    {
-      /*
-       * OK..this means the command we are currently getting an abort
-       * for has an outstanding negotiation command in front of it.
-       * We don't really have a way to tie back into the negotiation
-       * commands, so we just send this back as pending, then it
-       * will get reset in 2 seconds.
-       */
-      unpause_sequencer(p, TRUE);
-      scb->flags |= SCB_ABORT;
-      DRIVER_UNLOCK
-      return(SCSI_ABORT_PENDING);
-    }
-    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) 
-      printk(INFO_LEAD "SCB found on waiting list and "
-          "aborted.\n", p->host_no, CTL_OF_SCB(scb));
-    scbq_remove(&p->waiting_scbs, scb);
-    scbq_remove(&p->delayed_scbs[tindex], scb);
-    p->dev_active_cmds[tindex]++;
-    p->activescbs++;
-    scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
-    scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
-    found = 1;
-  }
-
-/*
- *  We just checked the waiting_q, now for the QINFIFO
- */
-  if ( found == 0 )
-  {
-    if ( ((found = aic7xxx_search_qinfifo(p, cmd->target, 
-                     cmd->channel,
-                     cmd->lun, scb->hscb->tag, SCB_ABORT | SCB_QUEUED_FOR_DONE,
-                     FALSE, NULL)) != 0) &&
-                    (aic7xxx_verbose & VERBOSE_ABORT_PROCESS))
-      printk(INFO_LEAD "SCB found in QINFIFO and "
-        "aborted.\n", p->host_no, CTL_OF_SCB(scb));
-  }
-
-/*
- *  QINFIFO, waitingq, completeq done.  Next, check WAITING_SCB list in card
- */
-
-  if ( found == 0 )
-  {
-    unsigned char scb_next_ptr;
-    prev_hscbptr = SCB_LIST_NULL;
-    saved_hscbptr = aic_inb(p, SCBPTR);
-    next_hscbptr = aic_inb(p, WAITING_SCBH);
-    while ( next_hscbptr != SCB_LIST_NULL )
-    {
-      aic_outb(p,  next_hscbptr, SCBPTR );
-      if ( scb->hscb->tag == aic_inb(p, SCB_TAG) )
-      {
-        found = 1;
-        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
-          printk(INFO_LEAD "SCB found on hardware waiting"
-            " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
-        if ( prev_hscbptr == SCB_LIST_NULL )
-        {
-            aic_outb(p, aic_inb(p, SCB_NEXT), WAITING_SCBH);
-            /* stop the selection since we just
-             * grabbed the scb out from under the
-             * card
-             */
-            aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
-            aic_outb(p, CLRSELTIMEO, CLRSINT1);
-        }
-        else
-        {
-            scb_next_ptr = aic_inb(p, SCB_NEXT);
-            aic_outb(p, prev_hscbptr, SCBPTR);
-            aic_outb(p, scb_next_ptr, SCB_NEXT);
-            aic_outb(p, next_hscbptr, SCBPTR);
-        }
-        aic_outb(p, SCB_LIST_NULL, SCB_TAG);
-        aic_outb(p, 0, SCB_CONTROL);
-        aic7xxx_add_curscb_to_free_list(p);
-        scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
-        break;
-      }
-      prev_hscbptr = next_hscbptr;
-      next_hscbptr = aic_inb(p, SCB_NEXT);
-    }
-    aic_outb(p,  saved_hscbptr, SCBPTR );
-  }
-        
-/*
- *  Hmmm...completeq, QOUTFIFO, QINFIFO, WAITING_SCBH, waitingq all checked.
- *  OK...the sequencer's paused, interrupts are off, and we haven't found the
- *  command anyplace where it could be easily aborted.  Time for the hard
- *  work.  We also know the command is valid.  This essentially means the
- *  command is disconnected, or connected but not into any phases yet, which
- *  we know due to the tests we ran earlier on the current active scb phase.
- *  At this point we can queue the abort tag and go on with life.
- */
-
-  if ( found == 0 )
-  {
-    p->flags |= AHC_ABORT_PENDING;
-    scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
-    scb->hscb->control |= MK_MESSAGE;
-    result=aic7xxx_find_scb(p, scb);
-    if ( result != SCB_LIST_NULL ) 
-    {
-      saved_hscbptr = aic_inb(p, SCBPTR);
-      aic_outb(p, result, SCBPTR);
-      tmp_char = aic_inb(p, SCB_CONTROL);
-      aic_outb(p,  tmp_char | MK_MESSAGE, SCB_CONTROL);
-      aic_outb(p, saved_hscbptr, SCBPTR);
-    }
-    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
-      printk(INFO_LEAD "SCB disconnected.  Queueing Abort"
-        " SCB.\n", p->host_no, CTL_OF_SCB(scb));
-    p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
-    if (p->features & AHC_QUEUE_REGS)
-      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
-    else
-      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
-  }
-  if (found)
-  {
-    aic7xxx_run_done_queue(p, TRUE);
-    aic7xxx_run_waiting_queues(p);
-  }
-  p->flags &= ~AHC_IN_ABORT;
-  unpause_sequencer(p, FALSE);
-  DRIVER_UNLOCK
-
-/*
- *  On the return value.  If we found the command and aborted it, then we know
- *  it's already sent back and there is no reason for a further timeout, so
- *  we use SCSI_ABORT_SUCCESS.  On the queued abort side, we aren't so certain
- *  there hasn't been a bus hang or something that might keep the abort from
- *  from completing.  Therefore, we use SCSI_ABORT_PENDING.  The first time this
- *  is passed back, the timeout on the command gets extended, the second time
- *  we pass this back, the mid level SCSI code calls our reset function, which
- *  would shake loose a hung bus.
- */
-  if ( found != 0 )
-    return(SCSI_ABORT_SUCCESS);
-  else
-    return(SCSI_ABORT_PENDING); 
-}
-
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_reset
- *
- * Description:
- *   Resetting the bus always succeeds - is has to, otherwise the
- *   kernel will panic! Try a surgical technique - sending a BUS
- *   DEVICE RESET message - on the offending target before pulling
- *   the SCSI bus reset line.
- *-F*************************************************************************/
-int
-aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
-{
-  struct aic7xxx_scb *scb = NULL;
-  struct aic7xxx_host *p;
-  int    tindex;
-  int    result = -1;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
-  unsigned long cpu_flags = 0;
-#endif
-#define DEVICE_RESET 0x01
-#define BUS_RESET    0x02
-#define HOST_RESET   0x04
-#define FAIL         0x08
-#define RESET_DELAY  0x10
-  int        action;
-  Scsi_Cmnd *cmd_prev, *cmd_next;
-
-
-  if ( cmd == NULL )
-  {
-    printk(KERN_WARNING "(scsi?:?:?:?) Reset called with NULL Scsi_Cmnd "
-      "pointer, failing.\n");
-    return(SCSI_RESET_SNOOZE);
-  }
-
-  p = (struct aic7xxx_host *) cmd->host->hostdata;
-  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
-  tindex = TARGET_INDEX(cmd);
-
-  /*
-   * I added a new config option to the driver: "panic_on_abort" that will
-   * cause the driver to panic and the machine to stop on the first abort
-   * or reset call into the driver.  At that point, it prints out a lot of
-   * usefull information for me which I can then use to try and debug the
-   * problem.  Simply enable the boot time prompt in order to activate this
-   * code.
-   */
-  if (aic7xxx_panic_on_abort)
-    aic7xxx_panic_abort(p, cmd);
-
-  DRIVER_LOCK
-
-  pause_sequencer(p);
-  while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
-  {
-    aic7xxx_isr(p->irq, p, (void *)NULL );
-    pause_sequencer(p);
-    aic7xxx_done_cmds_complete(p);
-  }
-
-  if (scb == NULL)
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_MID)
-      printk(INFO_LEAD "Reset called with bogus Scsi_Cmnd"
-           "->SCB mapping, improvising.\n", p->host_no, CTL_OF_CMD(cmd));
-    if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
-    {
-      action = HOST_RESET;
-    }
-    else
-    {
-      action = BUS_RESET;
-    }
-  }
-  else if (scb->cmd != cmd) 
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_MID)
-    printk(INFO_LEAD "Reset called with recycled SCB "
-        "for cmd.\n", p->host_no, CTL_OF_CMD(cmd));
-    cmd_prev = NULL;
-    cmd_next = p->completeq.head;
-    while ( cmd_next != NULL )
-    {
-      if (cmd_next == cmd)
-      {
-        if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
-          printk(INFO_LEAD "Reset, found cmd on completeq"
-          ", completing.\n", p->host_no, CTL_OF_CMD(cmd));
-        unpause_sequencer(p, FALSE);
-        DRIVER_UNLOCK
-        return(SCSI_RESET_NOT_RUNNING);
-      }
-      cmd_prev = cmd_next;
-      cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
-    }
-    if ( !(flags & SCSI_RESET_SYNCHRONOUS) )
-    {
-      if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
-        printk(INFO_LEAD "Reset, cmd not found,"
-          " failing.\n", p->host_no, CTL_OF_CMD(cmd));
-      unpause_sequencer(p, FALSE);
-      DRIVER_UNLOCK
-      return(SCSI_RESET_NOT_RUNNING);
-    }
-    else
-    {
-      if (aic7xxx_verbose & VERBOSE_RESET_MID)
-        printk(INFO_LEAD "Reset called, no scb, "
-          "flags 0x%x\n", p->host_no, CTL_OF_CMD(cmd), flags);
-      scb = NULL;
-      action = HOST_RESET;
-    }
-  }
-  else
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_MID)
-      printk(INFO_LEAD "Reset called, scb %d, flags "
-        "0x%x\n", p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
-    if ( aic7xxx_scb_on_qoutfifo(p, scb) )
-    {
-      if(aic7xxx_verbose & VERBOSE_RESET_RETURN)
-        printk(INFO_LEAD "SCB on qoutfifo, completing.\n", p->host_no,
-          CTL_OF_SCB(scb));
-      if ((aic_inb(p,INTSTAT) & CMDCMPLT) == 0)
-        printk(INFO_LEAD "missed CMDCMPLT interrupt!\n", p->host_no,
-          CTL_OF_SCB(scb));
-      aic7xxx_handle_command_completion_intr(p);
-      aic7xxx_done_cmds_complete(p);
-      aic7xxx_run_waiting_queues(p);
-      unpause_sequencer(p, FALSE);
-      DRIVER_UNLOCK
-      return(SCSI_RESET_SUCCESS);
-    }
-    if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
-    {
-      action = HOST_RESET;
-    }
-    else if ( flags & SCSI_RESET_SUGGEST_BUS_RESET )
-    {
-      action = BUS_RESET;
-    }
-    else 
-    {
-      action = DEVICE_RESET;
-    }
-  }
-  if ( (action & DEVICE_RESET) && 
-        (p->dev_flags[tindex] & BUS_DEVICE_RESET_PENDING) )
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-      printk(INFO_LEAD "Bus device reset already sent to "
-        "device, escalating.\n", p->host_no, CTL_OF_CMD(cmd));
-    action = BUS_RESET;
-  }
-  if ( (action & DEVICE_RESET) &&
-       (scb->flags & SCB_QUEUED_ABORT) )
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-    {
-      printk(INFO_LEAD "Have already attempted to reach "
-        "device with queued\n", p->host_no, CTL_OF_CMD(cmd));
-      printk(INFO_LEAD "message, will escalate to bus "
-        "reset.\n", p->host_no, CTL_OF_CMD(cmd));
-    }
-    action = BUS_RESET;
-  }
-  if ( (action & DEVICE_RESET) && 
-       (p->flags & (AHC_RESET_PENDING | AHC_ABORT_PENDING)) )
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-     printk(INFO_LEAD "Bus device reset stupid when "
-        "other action has failed.\n", p->host_no, CTL_OF_CMD(cmd));
-    action = BUS_RESET;
-  }
-  if ( (action & BUS_RESET) && !(p->features & AHC_TWIN) )
-  {
-    action = HOST_RESET;
-  }
-  if ( (p->dev_flags[tindex] & DEVICE_RESET_DELAY) &&
-       !(action & (HOST_RESET | BUS_RESET)))
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-    {
-      printk(INFO_LEAD "Reset called too soon after last "
-        "reset without requesting\n", p->host_no, CTL_OF_CMD(cmd));
-      printk(INFO_LEAD "bus or host reset, escalating.\n", p->host_no,
-        CTL_OF_CMD(cmd));
-    }
-    action = BUS_RESET;
-  }
-  if ( (p->flags & AHC_RESET_DELAY) &&
-       (action & (HOST_RESET | BUS_RESET)) )
-  {
-    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
-      printk(INFO_LEAD "Reset called too soon after "
-        "last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd));
-    action = RESET_DELAY;
-  }
-/*
- *  By this point, we want to already know what we are going to do and
- *  only have the following code implement our course of action.
- */
-  switch (action)
-  {
-    case RESET_DELAY:
-      unpause_sequencer(p, FALSE);
-      DRIVER_UNLOCK
-      return(SCSI_RESET_PENDING);
-      break;
-    case FAIL:
-      unpause_sequencer(p, FALSE);
-      DRIVER_UNLOCK
-      return(SCSI_RESET_ERROR);
-      break;
-    case DEVICE_RESET:
-      p->flags |= AHC_IN_RESET;
-      result = aic7xxx_bus_device_reset(p, cmd);
-      aic7xxx_run_done_queue(p, TRUE);
-      /*  We can't rely on run_waiting_queues to unpause the sequencer for
-       *  PCI based controllers since we use AAP */
-      aic7xxx_run_waiting_queues(p);
-      unpause_sequencer(p, FALSE);
-      p->flags &= ~AHC_IN_RESET;
-      DRIVER_UNLOCK
-      return(result);
-      break;
-    case BUS_RESET:
-    case HOST_RESET:
-    default:
-      p->flags |= AHC_IN_RESET | AHC_RESET_DELAY;
-      p->dev_expires[p->scsi_id] = jiffies + (3 * HZ);
-      p->dev_timer_active |= (0x01 << p->scsi_id);
-      if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) ||
-            time_after_eq(p->dev_timer.expires, p->dev_expires[p->scsi_id]) )
-      {
-        mod_timer(&p->dev_timer, p->dev_expires[p->scsi_id]);
-        p->dev_timer_active |= (0x01 << MAX_TARGETS);
-      }
-      aic7xxx_reset_channel(p, cmd->channel, TRUE);
-      if ( (p->features & AHC_TWIN) && (action & HOST_RESET) )
-      {
-        aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE);
-        restart_sequencer(p);
-      }
-      if (action != HOST_RESET)
-        result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
-      else
-      {
-        result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
-        aic_outb(p,  aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE),
-          SIMODE1);
-        aic7xxx_clear_intstat(p);
-        p->flags &= ~AHC_HANDLING_REQINITS;
-        p->msg_type = MSG_TYPE_NONE;
-        p->msg_index = 0;
-        p->msg_len = 0;
-      }
-      aic7xxx_run_done_queue(p, TRUE);
-      /*
-       * If this a SCSI_RESET_SYNCHRONOUS then the command we were given is
-       * in need of being re-started, so send it on through to aic7xxx_queue
-       * and let it set until the delay is over.  This keeps it from dying
-       * entirely and avoids getting a bogus dead command back through the
-       * mid-level code due to too many retries.
-       */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,132)
-      if ( flags & SCSI_RESET_SYNCHRONOUS )
-      {
-        cmd->result = DID_BUS_BUSY << 16;
-        cmd->done(cmd);
-      }
-#endif
-      p->flags &= ~AHC_IN_RESET;
-      /*
-       * We can't rely on run_waiting_queues to unpause the sequencer for
-       * PCI based controllers since we use AAP.  NOTE: this also sets
-       * the timer for the one command we might have queued in the case
-       * of a synch reset.
-       */
-      aic7xxx_run_waiting_queues(p);
-      unpause_sequencer(p, FALSE);
-      DRIVER_UNLOCK
-      return(result);
-      break;
-  }
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_biosparam
- *
- * Description:
- *   Return the disk geometry for the given SCSI device.
- *-F*************************************************************************/
-int
-aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[])
-{
-  int heads, sectors, cylinders, ret;
-  struct aic7xxx_host *p;
-  struct buffer_head *bh;
-
-  p = (struct aic7xxx_host *) disk->device->host->hostdata;
-  bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024);
-
-  if ( bh )
-  {
-    ret = scsi_partsize(bh, disk->capacity, &geom[2], &geom[0], &geom[1]);
-    brelse(bh);
-    if ( ret != -1 )
-      return(ret);
-  }
-  
-  heads = 64;
-  sectors = 32;
-  cylinders = disk->capacity / (heads * sectors);
-
-  if ((p->flags & AHC_EXTEND_TRANS_A) && (cylinders > 1024))
-  {
-    heads = 255;
-    sectors = 63;
-    cylinders = disk->capacity / (heads * sectors);
-  }
-
-  geom[0] = heads;
-  geom[1] = sectors;
-  geom[2] = cylinders;
-
-  return (0);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_release
- *
- * Description:
- *   Free the passed in Scsi_Host memory structures prior to unloading the
- *   module.
- *-F*************************************************************************/
-int
-aic7xxx_release(struct Scsi_Host *host)
-{
-  struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
-  struct aic7xxx_host *next, *prev;
-
-  if(p->irq)
-    free_irq(p->irq, p);
-  if(p->base)
-    release_region(p->base, MAXREG - MINREG);
-#ifdef MMAPIO
-  if(p->maddr)
-  {
-    iounmap((void *) (((unsigned long) p->maddr) & PAGE_MASK));
-  }
-#endif /* MMAPIO */
-  prev = NULL;
-  next = first_aic7xxx;
-  while(next != NULL)
-  {
-    if(next == p)
-    {
-      if(prev == NULL)
-        first_aic7xxx = next->next;
-      else
-        prev->next = next->next;
-    }
-    else
-    {
-      prev = next;
-    }
-    next = next->next;
-  }
-  aic7xxx_free(p);
-  return(0);
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_print_card
- *
- * Description:
- *   Print out all of the control registers on the card
- *
- *   NOTE: This function is not yet safe for use on the VLB and EISA
- *   controllers, so it isn't used on those controllers at all.
- *-F*************************************************************************/
-static void
-aic7xxx_print_card(struct aic7xxx_host *p)
-{
-  int i, j, k, chip;
-  static struct register_ranges {
-    int num_ranges;
-    int range_val[32];
-  } cards_ds[] = {
-    { 0, {0,} }, /* none */
-    {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1f, 0x1f, 0x60, 0x60, /*7771*/
-          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9b, 0x9f} },
-    { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7850*/
-          0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
-    { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7860*/
-          0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
-    {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1c, 0x1f, 0x60, 0x60, /*7870*/
-          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
-    {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1a, 0x1c, 0x1f, 0x60, 0x60, /*7880*/
-          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
-    {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7890*/
-          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
-          0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
-          0xfe, 0xff} },
-    {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1b, 0x1f, 0x60, 0x60, /*7895*/
-          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a,
-          0x9f, 0x9f, 0xe0, 0xf1} },
-    {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7896*/
-          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
-          0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
-          0xfe, 0xff} },
-    {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7892*/
-          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f,
-          0xe0, 0xf1, 0xf4, 0xfc} },
-    {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7899*/
-          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f,
-          0xe0, 0xf1, 0xf4, 0xfc} },
-  };
-  chip = p->chip & AHC_CHIPID_MASK;
-  printk("%s at ",
-         board_names[p->board_name_index]);
-  switch(p->chip & ~AHC_CHIPID_MASK)
-  {
-    case AHC_VL:
-      printk("VLB Slot %d.\n", p->pci_device_fn);
-      break;
-    case AHC_EISA:
-      printk("EISA Slot %d.\n", p->pci_device_fn);
-      break;
-    case AHC_PCI:
-    default:
-      printk("PCI %d/%d/%d.\n", p->pci_bus, PCI_SLOT(p->pci_device_fn),
-             PCI_FUNC(p->pci_device_fn));
-      break;
-  }
-
-  /*
-   * the registers on the card....
-   */
-  printk("Card Dump:\n");
-  k = 0;
-  for(i=0; i<cards_ds[chip].num_ranges; i++)
-  {
-    for(j  = cards_ds[chip].range_val[ i * 2 ];
-        j <= cards_ds[chip].range_val[ i * 2 + 1 ] ;
-        j++)
-    {
-      printk("%02x:%02x ", j, aic_inb(p, j));
-      if(++k == 13)
-      {
-        printk("\n");
-        k=0;
-      }
-    }
-  }
-  if(k != 0)
-    printk("\n");
-
-  /*
-   * If this was an Ultra2 controller, then we just hosed the card in terms
-   * of the QUEUE REGS.  This function is only called at init time or by
-   * the panic_abort function, so it's safe to assume a generic init time
-   * setting here
-   */
-
-  if(p->features & AHC_QUEUE_REGS)
-  {
-    aic_outb(p, 0, SDSCB_QOFF);
-    aic_outb(p, 0, SNSCB_QOFF);
-    aic_outb(p, 0, HNSCB_QOFF);
-  }
-
-}
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_print_scratch_ram
- *
- * Description:
- *   Print out the scratch RAM values on the card.
- *-F*************************************************************************/
-static void
-aic7xxx_print_scratch_ram(struct aic7xxx_host *p)
-{
-  int i, k;
-
-  k = 0;
-  printk("Scratch RAM:\n");
-  for(i = SRAM_BASE; i < SEQCTL; i++)
-  {
-    printk("%02x:%02x ", i, aic_inb(p, i));
-    if(++k == 13)
-    {
-      printk("\n");
-      k=0;
-    }
-  }
-  if (p->features & AHC_MORE_SRAM)
-  {
-    for(i = TARG_OFFSET; i < 0x80; i++)
-    {
-      printk("%02x:%02x ", i, aic_inb(p, i));
-      if(++k == 13)
-      {
-        printk("\n");
-        k=0;
-      }
-    }
-  }
-  printk("\n");
-}
-
-
-#include "aic7xxx_proc.c"
-
-/* Eventually this will go into an include file, but this will be later */
-static Scsi_Host_Template driver_template = AIC7XXX;
-
-#include "scsi_module.c"
-
-/*
- * Overrides for Emacs so that we almost follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 2
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -2
- * c-argdecl-indent: 2
- * c-label-offset: -2
- * c-continued-statement-offset: 2
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx.h linux/drivers/scsi/aic7xxx.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx.h	Wed Jun 21 17:25:03 2000
+++ linux/drivers/scsi/aic7xxx.h	Wed Dec 31 16:00:00 1969
@@ -1,73 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx device driver for Linux.
- *
- * 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.
- * 
- * $Id: aic7xxx.h,v 3.2 1996/07/23 03:37:26 deang Exp $
- *-M*************************************************************************/
-#ifndef _aic7xxx_h
-#define _aic7xxx_h
-
-#define AIC7XXX_H_VERSION  "5.2.0"
-
-/*
- * Scsi_Host_Template (see hosts.h) for AIC-7xxx - some fields
- * to do with card config are filled in after the card is detected.
- */
-#define AIC7XXX	{						\
-	next: NULL,						\
-	module: NULL,						\
-	proc_info: aic7xxx_proc_info,				\
-	name: NULL,						\
-	detect: aic7xxx_detect,					\
-	release: aic7xxx_release,				\
-	info: aic7xxx_info,					\
-	command: NULL,						\
-	queuecommand: aic7xxx_queue,				\
-	eh_strategy_handler: NULL,				\
-	eh_abort_handler: NULL,					\
-	eh_device_reset_handler: NULL,				\
-	eh_bus_reset_handler: NULL,				\
-	eh_host_reset_handler: NULL,				\
-	abort: aic7xxx_abort,					\
-	reset: aic7xxx_reset,					\
-	slave_attach: NULL,					\
-	bios_param: aic7xxx_biosparam,				\
-	can_queue: 255,		/* max simultaneous cmds      */\
-	this_id: -1,		/* scsi id of host adapter    */\
-	sg_tablesize: 0,	/* max scatter-gather cmds    */\
-	cmd_per_lun: 3,		/* cmds per lun (linked cmds) */\
-	present: 0,		/* number of 7xxx's present   */\
-	unchecked_isa_dma: 0,	/* no memory DMA restrictions */\
-	use_clustering: ENABLE_CLUSTERING,			\
-	use_new_eh_code: 0					\
-}
-
-extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
-extern int aic7xxx_biosparam(Disk *, kdev_t, int[]);
-extern int aic7xxx_detect(Scsi_Host_Template *);
-extern int aic7xxx_command(Scsi_Cmnd *);
-extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int);
-extern int aic7xxx_abort(Scsi_Cmnd *);
-extern int aic7xxx_release(struct Scsi_Host *);
-
-extern const char *aic7xxx_info(struct Scsi_Host *);
-
-extern int aic7xxx_proc_info(char *, char **, off_t, int, int, int);
-
-#endif /* _aic7xxx_h */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/README.aic7xxx linux/drivers/scsi/aic7xxx_old/README.aic7xxx
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/README.aic7xxx	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_old/README.aic7xxx	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,511 @@
+			    AIC7xxx Driver for Linux
+
+Introduction
+----------------------------
+The AIC7xxx SCSI driver adds support for Adaptec (http://www.adaptec.com)
+SCSI controllers and chipsets. Major portions of the driver and driver
+development are shared between both Linux and FreeBSD. Support for the
+AIC-7xxx chipsets have been in the default Linux kernel since approximately
+linux-1.1.x and fairly stable since linux-1.2.x, and are also in FreeBSD
+2.1.0 or later.
+
+  Supported cards/chipsets
+  ----------------------------
+    Adaptec Cards
+    ----------------------------
+    AHA-274x
+    AHA-274xT               
+    AHA-2842
+    AHA-2910B               
+    AHA-2920C
+    AHA-2930
+    AHA-2930U
+    AHA-2930CU
+    AHA-2930U2
+    AHA-2940               
+    AHA-2940W              
+    AHA-2940U              
+    AHA-2940UW
+    AHA-2940UW-PRO
+    AHA-2940AU 
+    AHA-2940U2W
+    AHA-2940U2
+    AHA-2940U2B
+    AHA-2940U2BOEM
+    AHA-2944D              
+    AHA-2944WD
+    AHA-2944UD
+    AHA-2944UWD
+    AHA-2950U2
+    AHA-2950U2W
+    AHA-2950U2B
+    AHA-29160M
+    AHA-3940
+    AHA-3940U
+    AHA-3940W
+    AHA-3940UW
+    AHA-3940AUW
+    AHA-3940U2W
+    AHA-3950U2B
+    AHA-3950U2D
+    AHA-3960D
+    AHA-39160M
+    AHA-3985
+    AHA-3985U
+    AHA-3985W
+    AHA-3985UW
+
+    Motherboard Chipsets
+    ----------------------------
+    AIC-777x   
+    AIC-785x
+    AIC-786x
+    AIC-787x
+    AIC-788x
+    AIC-789x
+    AIC-3860
+
+    Bus Types
+    ----------------------------
+    W - Wide SCSI, SCSI-3, 16bit bus, 68pin connector, will also support
+        SCSI-1/SCSI-2 50pin devices, transfer rates up to 20MB/s.
+    U - Ultra SCSI, transfer rates up to 40MB/s.
+    U2- Ultra 2 SCSI, transfer rates up to 80MB/s.
+    D - Differential SCSI.
+    T - Twin Channel SCSI. Up to 14 SCSI devices.
+
+    AHA-274x - EISA SCSI controller
+    AHA-284x - VLB SCSI controller
+    AHA-29xx - PCI SCSI controller
+    AHA-394x - PCI controllers with two separate SCSI controllers on-board.
+    AHA-398x - PCI RAID controllers with three separate SCSI controllers
+               on-board.
+
+  Not Supported Devices
+  ------------------------------
+    Adaptec Cards
+    ----------------------------
+    AHA-2920 (Only the cards that use the Future Domain chipset are not
+              supported, any 2920 cards based on Adaptec AIC chipsets,
+	      such as the 2920C, are supported)
+    AAA-13x Raid Adapters
+    AAA-113x Raid Port Card
+
+    Motherboard Chipsets
+    ----------------------------
+    AIC-7810
+
+    Bus Types
+    ----------------------------
+    R - Raid Port busses are not supported.
+
+    The hardware RAID devices sold by Adaptec are *NOT* supported by this
+    driver (and will people please stop emailing me about them, they are
+    a totally separate beast from the bare SCSI controllers and this driver
+    can not be retrofitted in any sane manner to support the hardware RAID
+    features on those cards - Doug Ledford).
+    
+
+  People
+  ------------------------------
+    Justin T Gibbs  gibbs@plutotech.com
+      (BSD Driver Author)
+    Dan Eischen     deischen@iworks.InterWorks.org
+      (Original Linux Driver Co-maintainer)
+    Dean Gehnert    deang@teleport.com
+      (Original Linux FTP/patch maintainer)
+    Jess Johnson    jester@frenzy.com
+      (AIC7xxx FAQ author)
+    Doug Ledford    dledford@redhat.com
+      (Current Linux aic7xxx-5.x.x Driver/Patch/FTP maintainer)
+    
+    Special thanks go to John Aycock (aycock@cpsc.ucalgary.ca), the original
+    author of the driver. John has since retired from the project. Thanks
+    again for all his work!
+    
+  Mailing list
+  ------------------------------
+    There is a mailing list available for users who want to track development
+    and converse with other users and developers. This list is for both
+    FreeBSD and Linux support of the AIC7xxx chipsets.
+
+    To subscribe to the AIC7xxx mailing list send mail to the list server,
+    with "subscribe AIC7xxx" in the body (no Subject: required):
+        To: majordomo@FreeBSD.ORG
+        ---
+        subscribe AIC7xxx
+
+    To unsubscribe from the list, send mail to the list server with:
+        To: majordomo@FreeBSD.ORG
+        ---
+        unsubscribe AIC7xxx
+
+    Send regular messages and replies to: AIC7xxx@FreeBSD.ORG
+    
+  Boot Command line options
+  ------------------------------
+    "aic7xxx=no_reset" -  Eliminate the SCSI bus reset during startup.
+        Some SCSI devices need the initial reset that this option disables
+	in order to work.  If you have problems at bootup, please make sure
+	you aren't using this option.
+	
+    "aic7xxx=reverse_scan" - Certain PCI motherboards scan for devices at
+        bootup by scanning from the highest numbered PCI device to the
+	lowest numbered PCI device, others do just the opposite and scan
+	from lowest to highest numbered PCI device.  There is no reliable
+	way to autodetect this ordering.  So, we default to the most common
+	order, which is lowest to highest.  Then, in case your motherboard
+	scans from highest to lowest, we have this option.  If your BIOS
+	finds the drives on controller A before controller B but the linux
+	kernel finds your drives on controller B before A, then you should
+	use this option.
+	
+    "aic7xxx=extended" - Force the driver to detect extended drive translation
+        on your controller.  This helps those people who have cards without
+        a SEEPROM make sure that linux and all other operating systems think
+        the same way about your hard drives.
+
+    "aic7xxx=scbram" - Some cards have external SCB RAM that can be used to
+        give the card more hardware SCB slots.  This allows the driver to use
+	that SCB RAM.  Without this option, the driver won't touch the SCB
+	RAM because it is known to cause problems on a few cards out there
+	(such as 3985 class cards).
+	
+    "aic7xxx=irq_trigger:x" - Replace x with either 0 or 1 to force the kernel
+        to use the correct IRQ type for your card.  This only applies to EISA
+        based controllers.  On these controllers, 0 is for Edge triggered
+        interrupts, and 1 is for Level triggered interrupts.  If you aren't
+        sure or don't know which IRQ trigger type your EISA card uses, then
+        let the kernel autodetect the trigger type.
+	
+    "aic7xxx=verbose" - This option can be used in one of two ways.  If you
+        simply specify aic7xxx=verbose, then the kernel will automatically
+	pick the default set of verbose messages for you to see.
+	Alternatively, you can specify the command as 
+	"aic7xxx=verbose:0xXXXX" where the X entries are replaced with
+	hexadecimal digits.  This option is a bit field type option.  For
+	a full listing of the available options, search for the 
+	#define VERBOSE_xxxxxx lines in the aic7xxx.c file.  If you want
+	verbose messages, then it is recommended that you simply use the
+	aic7xxx=verbose variant of this command.
+	
+    "aic7xxx=pci_parity:x" - This option controls whether or not the driver
+        enables PCI parity error checking on the PCI bus.  By default, this
+        checking is disabled.  To enable the checks, simply specify pci_parity
+        with no value afterwords.  To reverse the parity from even to odd,
+        supply any number other than 0 or 255.  In short:
+          pci_parity     - Even parity checking (even is the normal PCI parity)
+          pci_parity:x   - Where x > 0, Odd parity checking
+          pci_parity:0   - No check (default)
+        NOTE: In order to get Even PCI parity checking, you must use the
+        version of the option that does not include the : and a number at
+        the end (unless you want to enter exactly 2^32 - 1 as the number).
+	
+    "aic7xxx=no_probe" - This option will disable the probing for any VLB
+        based 2842 controllers and any EISA based controllers.  This is
+	needed on certain newer motherboards where the normal EISA I/O ranges
+	have been claimed by other PCI devices.  Probing on those machines
+	will often result in the machine crashing or spontaneously rebooting
+	during startup.  Examples of machines that need this are the
+	Dell PowerEdge 6300 machines.
+
+    "aic7xxx=seltime:2" - This option controls how long the card waits
+        during a device selection sequence for the device to respond.
+	The original SCSI spec says that this "should be" 256ms.  This
+	is generally not required with modern devices.  However, some
+	very old SCSI I devices need the full 256ms.  Most modern devices
+	can run fine with only 64ms.  The default for this option is
+	64ms.  If you need to change this option, then use the following
+	table to set the proper value in the example above:
+	  0  -  256ms
+	  1  -  128ms
+	  2  -   64ms
+	  3  -   32ms
+	
+    "aic7xxx=panic_on_abort" - This option is for debugging and will cause
+        the driver to panic the linux kernel and freeze the system the first
+	time the drivers abort or reset routines are called.  This is most
+	helpful when some problem causes infinite reset loops that scroll too
+	fast to see.  By using this option, you can write down what the errors
+	actually are and send that information to me so it can be fixed.
+	
+    "aic7xxx=dump_card" - This option will print out the *entire* set of
+        configuration registers on the card during the init sequence.  This
+	is a debugging aid used to see exactly what state the card is in
+	when we finally finish our initialization routines.  If you don't
+	have documentation on the chipsets, this will do you absolutely
+	no good unless you are simply trying to write all the information
+	down in order to send it to me.
+	
+    "aic7xxx=dump_sequencer" - This is the same as the above options except
+        that instead of dumping the register contents on the card, this
+	option dumps the contents of the sequencer program RAM.  This gives
+	the ability to verify that the instructions downloaded to the
+	card's sequencer are indeed what they are suppossed to be.  Again,
+	unless you have documentation to tell you how to interpret these
+	numbers, then it is totally useless.
+	
+    "aic7xxx=override_term:0xffffffff" - This option is used to force the
+    	termination on your SCSI controllers to a particular setting.  This
+	is a bit mask variable that applies for up to 8 aic7xxx SCSI channels.
+	Each channel gets 4 bits, divided as follows:
+	bit   3   2   1   0
+	      |   |   |   Enable/Disable Single Ended Low Byte Termination
+	      |   |   En/Disable Single Ended High Byte Termination
+	      |   En/Disable Low Byte LVD Termination
+	      En/Disable High Byte LVD Termination
+
+	The upper 2 bits that deal with LVD termination only apply to Ultra2
+	controllers.  Futhermore, due to the current Ultra2 controller
+	designs, these bits are tied together such that setting either bit
+	enables both low and high byte LVD termination.  It is not possible
+	to only set high or low byte LVD termination in this manner.  This is
+	an artifact of the BIOS definition on Ultra2 controllers.  For other
+	controllers, the only important bits are the two lowest bits.  Setting
+	the higher bits on non-Ultra2 controllers has no effect.  A few
+	examples of how to use this option:
+
+	Enable low and high byte termination on a non-ultra2 controller that
+	is the first aic7xxx controller (the correct bits are 0011), 
+	aic7xxx=override_term:0x3
+
+	Enable all termination on the third aic7xxx controller, high byte
+	termination on the second aic7xxx controller, and low and high byte
+	SE termination on the first aic7xxx controller 
+	(bits are 1111 0010 0011), 
+	aic7xxx=override_term:0xf23
+	
+	No attempt has been made to make this option non-cryptic.  It really
+	shouldn't be used except in dire circumstances, and if that happens,
+	I'm probably going to be telling you what to set this to anyway :)
+
+    "aic7xxx=stpwlev:0xffffffff" - This option is used to control the STPWLEV
+        bit in the DEVCONFIG PCI register.  Currently, this is one of the
+	very few registers that we have absolutely *no* way of detecting
+	what the variable should be.  It depends entirely on how the chipset
+	and external terminators were coupled by the card/motherboard maker.
+	Further, a chip reset (at power up) always sets this bit to 0.  If
+	there is no BIOS to run on the chipset/card (such as with a 2910C
+	or a motherboard controller with the BIOS totally disabled) then
+	the variable may not get set properly.  Of course, if the proper
+	setting was 0, then that's what it would be after the reset, but if
+	the proper setting is actually 1.....you get the picture.  Now, since
+	we can't detect this at all, I've added this option to force the
+	setting.  If you have a BIOS on your controller then you should never
+	need to use this option.  However, if you are having lots of SCSI
+	reset problems and can't seem to get them knocked out, this may help.
+
+	Here's a test to know for certain if you need this option.  Make
+	a boot floppy that you can use to boot your computer up and that
+	will detect the aic7xxx controller.  Next, power down your computer.
+	While it's down, unplug all SCSI cables from your Adaptec SCSI
+	controller.  Boot the system back up to the Adaptec EZ-SCSI BIOS
+	and then make sure that termination is enabled on your adapter (if
+	you have an Adaptec BIOS of course).  Next, boot up the floppy you
+	made and wait for it to detect the aic7xxx controller.  If the kernel
+	finds the controller fine, says scsi : x hosts and then tries to
+	detect your devices like normal, up to the point where it fails to
+	mount your root file system and panics, then you're fine.  If, on
+	the other hand, the system goes into an infinite reset loop, then
+	you need to use this option and/or the previous option to force the
+	proper termination settings on your controller.   If this happens,
+	then you next need to figure out what your settings should be.
+
+	To find the correct settings, power your machine back down, connect
+	back up the SCSI cables, and boot back into your machine like normal.
+	However, boot with the aic7xxx=verbose:0x39 option.  Record the
+	initial DEVCONFIG values for each of your aic7xxx controllers as
+	they are listed, and also record what the machine is detecting as
+	the proper termination on your controllers.  NOTE: the order in
+	which the initial DEVCONFIG values are printed out is not gauranteed
+	to be the same order as the SCSI controllers are registered.  The
+	above option and this option both work on the order of the SCSI
+	controllers as they are registered, so make sure you match the right
+	DEVCONFIG values with the right controllers if you have more than
+	one aic7xxx controller.
+
+	Once you have the detected termination settings and the initial
+	DEVCONFIG values for each controller, then figure out what the
+	termination on each of the controllers *should* be.  Hopefully, that
+	part is correct, but it could possibly be wrong if there is
+	bogus cable detection logic on your controller or something similar.
+	If all the controllers have the correct termination settings, then
+	don't set the aic7xxx=override_term variable at all, leave it alone.
+	Next, on any controllers that go into an infinite reset loop when
+	you unplug all the SCSI cables, get the starting DEVCONFIG value.
+	If the initial DEVCONFIG value is divisible by 2, then the correct
+	setting for that controller is 0.  If it's an odd number, then
+	the correct setting for that controller is 1.  For any other
+	controllers that didn't have an infinite reset problem, then reverse
+	the above options.  If DEVCONFIG was even, then the correct setting
+	is 1, if not then the correct setting is 0.
+
+	Now that you know what the correct setting was for each controller,
+	we need to encode that into the aic7xxx=stpwlev:0x... variable.
+	This variable is a bit field encoded variable.  Bit 0 is for the first
+	aic7xxx controller, bit 1 for the next, etc.  Put all these bits
+	together and you get a number.  For example, if the third aic7xxx
+	needed a 1, but the second and first both needed a 0, then the bits
+	would be 100 in binary.  This then translates to 0x04.  You would
+	therefore set aic7xxx=stpwlev:0x04.  This is fairly standard binary
+	to hexadecimal conversions here.  If you aren't up to speed on the
+	binary->hex conversion then send an email to the aic7xxx mailing
+	list and someone can help you out.
+
+    "aic7xxx=tag_info:{{8,8..},{8,8..},..}" - This option is used to disable
+        or enable Tagged Command Queueing (TCQ) on specific devices.  As of
+	driver version 5.1.11, TCQ is now either on or off by default
+	according to the setting you choose during the make config process.
+	In order to en/disable TCQ for certian devices at boot time, a user
+	may use this boot param.  The driver will then parse this message out
+        and en/disable the specific device entries that are present based upon
+        the value given.  The param line is parsed in the following manner:
+
+          { - first instance indicates the start of this parameter values
+              second instance is the start of entries for a particular
+              device entry
+          } - end the entries for a particular host adapter, or end the entire
+              set of parameter entries
+          , - move to next entry.  Inside of a set of device entries, this
+              moves us to the next device on the list.  Outside of device
+              entries, this moves us to the next host adapter
+          . - Same effect as , but is safe to use with insmod.
+          x - the number to enter into the array at this position.  
+              0 = Enable tagged queueing on this device and use the default
+                  queue depth
+              1-254 = Enable tagged queueing on this device and use this
+                      number as the queue depth
+              255 = Disable tagged queueing on this device.
+              Note: anything above 32 for an actual queue depth is wasteful
+                    and not recommended.
+
+        A few examples of how this can be used:
+
+        tag_info:{{8,12,,0,,255,4}}
+          This line will only effect the first aic7xxx card registered.  It
+          will set scsi id 0 to a queue depth of 8, id 1 to 12, leave id 2
+          at the default, set id 3 to tagged queueing enabled and use the
+          default queue depth, id 4 default, id 5 disabled, and id 6 to 4.
+          Any not specified entries stay at the default value, repeated
+          commas with no value specified will simply increment to the next id
+          without changing anything for the missing values.
+
+        tag_info:{,,,{,,,255}}
+          First, second, and third adapters at default values.  Fourth
+          adapter, id 3 is disabled.  Notice that leading commas simply
+	  increment what the first number effects, and there are no need
+	  for trailing commas.  When you close out an adapter, or the
+	  entire entry, anything not explicitly set stays at the default
+	  value.
+
+        A final note on this option.  The scanner I used for this isn't
+        perfect or highly robust.  If you mess the line up, the worst that
+        should happen is that the line will get ignored.  If you don't
+        close out the entire entry with the final bracket, then any other
+        aic7xxx options after this will get ignored.  So, in general, be
+        sure of what you are entering, and after you have it right, just
+        add it to the lilo.conf file so there won't be any mistakes.  As
+        a means of checking this parser, the entire tag_info array for
+        each card is now printed out in the /proc/scsi/aic7xxx/x file.  You
+        can use that to verify that your options were parsed correctly. 
+        
+    Boot command line options may be combined to form the proper set of options
+    a user might need.  For example, the following is valid:
+    
+    aic7xxx=verbose,extended,irq_trigger:1
+    
+    The only requirement is that individual options be separated by a comma or
+    a period on the command line.
+        
+  Module Loading command options
+  ------------------------------
+    When loading the aic7xxx driver as a module, the exact same options are
+    available to the user.  However, the syntax to specify the options changes
+    slightly.  For insmod, you need to wrap the aic7xxx= argument in quotes
+    and replace all ',' with '.'.  So, for example, a valid insmod line
+    would be:
+
+    insmod aic7xxx aic7xxx='verbose.irq_trigger:1.extended'
+
+    This line should result in the *exact* same behaviour as if you typed
+    it in at the lilo prompt and the driver was compiled into the kernel
+    instead of being a module.  The reason for the single quote is so that
+    the shell won't try to interpret anything in the line, such as {. 
+    Insmod assumes any options starting with a letter instead of a number
+    is a character string (which is what we want) and by switching all of
+    the commas to periods, insmod won't interpret this as more than one
+    string and write junk into our binary image.  I consider it a bug in
+    the insmod program that even if you wrap your string in quotes (quotes
+    that pass the shell mind you and that insmod sees) it still treates
+    a comma inside of those quotes as starting a new variable, resulting
+    in memory scribbles if you don't switch the commas to periods.
+
+
+  Kernel Compile options
+  ------------------------------
+    The various kernel compile time options for this driver are now fairly
+    well documented in the file Documentation/Configure.help.  In order to
+    see this documentation, you need to use one of the advanced configuration
+    programs (menuconfig and xconfig).  If you are using the "make menuconfig"
+    method of configuring your kernel, then you would simply highlight the
+    option in question and hit the ? key.  If you are using the "make xconfig"
+    method of configuring your kernel, then simply click on the help button
+    next to the option you have questions about.  The help information from
+    the Configure.help file will then get automatically displayed.
+
+  /proc support
+  ------------------------------
+    The /proc support for the AIC7xxx can be found in the /proc/scsi/aic7xxx/
+    directory. That directory contains a file for each SCSI controller in
+    the system. Each file presents the current configuration and transfer
+    statistics (enabled with #define in aic7xxx.c) for each controller.
+
+    Thanks to Michael Neuffer for his upper-level SCSI help, and
+    Matthew Jacob for statistics support.
+
+  Debugging the driver
+  ------------------------------
+    Should you have problems with this driver, and would like some help in
+    getting them solved, there are a couple debugging items built into
+    the driver to facilitate getting the needed information from the system.
+    In general, I need a complete description of the problem, with as many
+    logs as possible concerning what happens.  To help with this, there is
+    a command option aic7xxx=panic_on_abort.  This option, when set, forces
+    the driver to panic the kernel on the first SCSI abort issued by the
+    mid level SCSI code.  If your system is going to reset loops and you
+    can't read the screen, then this is what you need.  Not only will it
+    stop the system, but it also prints out a large amount of state
+    information in the process.  Second, if you specify the option
+    "aic7xxx=verbose:0x1ffff", the system will print out *SOOOO* much
+    information as it runs that you won't be able to see anything.
+    However, this can actually be very usefull if your machine simply
+    locks up when trying to boot, since it will pin-point what was last
+    happening (in regards to the aic7xxx driver) immediately prior to
+    the lockup.  This is really only usefull if your machine simply can
+    not boot up successfully.  If you can get your machine to run, then
+    this will produce far too much information.
+
+  FTP sites
+  ------------------------------
+    ftp://ftp.redhat.com/pub/aic/
+      - Out of date.  I used to keep stuff here, but too many people
+        complained about having a hard time getting into Red Hat's ftp
+	server.  So use the web site below instead.
+    ftp://ftp.pcnet.com/users/eischen/Linux/
+      - Dan Eischen's driver distribution area
+    ftp://ekf2.vsb.cz/pub/linux/kernel/aic7xxx/ftp.teleport.com/
+      - European Linux mirror of Teleport site
+
+  Web sites
+  ------------------------------
+    http://people.redhat.com/dledford/
+      - My web site, also the primary aic7xxx site with several related
+        pages.
+
+Dean W. Gehnert
+deang@teleport.com
+
+$Revision: 3.0 $
+
+Modified by Doug Ledford 1998-2000
+
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/aic7xxx.h linux/drivers/scsi/aic7xxx_old/aic7xxx.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/aic7xxx.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_old/aic7xxx.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,73 @@
+/*+M*************************************************************************
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * 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.
+ * 
+ * $Id: aic7xxx.h,v 3.2 1996/07/23 03:37:26 deang Exp $
+ *-M*************************************************************************/
+#ifndef _aic7xxx_h
+#define _aic7xxx_h
+
+#define AIC7XXX_H_VERSION  "5.2.0"
+
+/*
+ * Scsi_Host_Template (see hosts.h) for AIC-7xxx - some fields
+ * to do with card config are filled in after the card is detected.
+ */
+#define AIC7XXX	{						\
+	next: NULL,						\
+	module: NULL,						\
+	proc_info: aic7xxx_proc_info,				\
+	name: NULL,						\
+	detect: aic7xxx_detect,					\
+	release: aic7xxx_release,				\
+	info: aic7xxx_info,					\
+	command: NULL,						\
+	queuecommand: aic7xxx_queue,				\
+	eh_strategy_handler: NULL,				\
+	eh_abort_handler: NULL,					\
+	eh_device_reset_handler: NULL,				\
+	eh_bus_reset_handler: NULL,				\
+	eh_host_reset_handler: NULL,				\
+	abort: aic7xxx_abort,					\
+	reset: aic7xxx_reset,					\
+	slave_attach: NULL,					\
+	bios_param: aic7xxx_biosparam,				\
+	can_queue: 255,		/* max simultaneous cmds      */\
+	this_id: -1,		/* scsi id of host adapter    */\
+	sg_tablesize: 0,	/* max scatter-gather cmds    */\
+	cmd_per_lun: 3,		/* cmds per lun (linked cmds) */\
+	present: 0,		/* number of 7xxx's present   */\
+	unchecked_isa_dma: 0,	/* no memory DMA restrictions */\
+	use_clustering: ENABLE_CLUSTERING,			\
+	use_new_eh_code: 0					\
+}
+
+extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+extern int aic7xxx_biosparam(Disk *, kdev_t, int[]);
+extern int aic7xxx_detect(Scsi_Host_Template *);
+extern int aic7xxx_command(Scsi_Cmnd *);
+extern int aic7xxx_reset(Scsi_Cmnd *, unsigned int);
+extern int aic7xxx_abort(Scsi_Cmnd *);
+extern int aic7xxx_release(struct Scsi_Host *);
+
+extern const char *aic7xxx_info(struct Scsi_Host *);
+
+extern int aic7xxx_proc_info(char *, char **, off_t, int, int, int);
+
+#endif /* _aic7xxx_h */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/aic7xxx.reg linux/drivers/scsi/aic7xxx_old/aic7xxx.reg
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/aic7xxx.reg	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_old/aic7xxx.reg	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,1396 @@
+/*
+ * Aic7xxx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994-1998 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. 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	SXFR_ULTRA2	0x7f		/* 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 Maximum Offset (p. 4-61 aic7890/91 Data Book)
+	 * The aic7890/91 allow an offset of up to 127 transfers in both wide
+	 * and narrow mode.
+	 */
+	alias	SCSIOFFSET
+	mask	SOFS_ULTRA2	0x7f		/* Sync offset U2 chips */
+}
+
+/*
+ * 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
+}
+
+/*
+ * Option Mode Register (Alternate Mode) (p. 5-198)
+ * This register is used to set certain options on Ultra3 based chips.
+ * The chip must be in alternate mode (bit ALT_MODE in SFUNCT must be set)
+ */
+register OPTIONMODE {
+	address			0x008
+	access_mode RW
+	bit	AUTORATEEN	0x80
+	bit	AUTOACKEN	0x40
+	bit	ATNMGMNTEN	0x20
+	bit	BUSFREEREV	0x10
+	bit	EXPPHASEDIS	0x08
+	bit	SCSIDATL_IMGEN	0x04
+	bit	AUTO_MSGOUT_DE	0x02
+	bit	DIS_MSGIN_DUALEDGE	0x01
+}
+
+
+/*
+ * 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	IOERR		0x08	/* LVD Tranceiver mode changed */
+	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
+	bit	SHVALID		0x40
+	bit	WIDE_RES	0x20
+	bit	EXP_ACTIVE	0x10	/* SCSI Expander Active */
+	bit	CRCVALERR	0x08	/* CRC Value Error */
+	bit	CRCENDERR	0x04	/* CRC End Error */
+	bit	CRCREQERR	0x02	/* CRC REQ Error */
+	bit	DUAL_EDGE_ERROR	0x01	/* Invalid pins for Dual Edge phase */
+	mask	SFCNT		0x1f
+}
+
+/*
+ * SCSI Status 3 (p. 3-26)
+ */
+register SSTAT3 {
+	address			0x00e
+	access_mode RO
+	mask	SCSICNT		0xf0
+	mask	OFFCNT		0x0f
+}
+
+/*
+ * SCSI ID for the aic7890/91 chips
+ */
+register SCSIID_ULTRA2 {
+	address			0x00f
+	access_mode RW
+	mask	TID		0xf0		/* Target ID mask */
+	mask	OID		0x0f		/* Our ID mask */
+}
+
+/*
+ * 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	ENIOERR		0x08	/* LVD Tranceiver mode changes */
+	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
+}
+
+/*
+ * Serial Port I/O Cabability register (p. 4-95 aic7860 Data Book)
+ * Indicates if external logic has been attached to the chip to
+ * perform the tasks of accessing a serial eeprom, testing termination
+ * strength, and performing cable detection.  On the aic7860, most of
+ * these features are handled on chip, but on the aic7855 an attached
+ * aic3800 does the grunt work.
+ */
+register SPIOCAP {
+	address			0x01b
+	access_mode RW
+	bit	SOFT1		0x80
+	bit	SOFT0		0x40
+	bit	SOFTCMDEN	0x20	
+	bit	HAS_BRDCTL	0x10	/* External Board control */
+	bit	SEEPROM		0x08	/* External serial eeprom logic */
+	bit	EEPROM		0x04	/* Writable external BIOS ROM */
+	bit	ROM		0x02	/* Logic for accessing external ROM */
+	bit	SSPIOCPS	0x01	/* Termination and cable detection */
+}
+
+/*
+ * 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	ENAB40		0x08	/* LVD transceiver active */
+	bit	ENAB20		0x04	/* SE/HVD transceiver active */
+	bit	SELWIDE		0x02
+	bit	XCVR		0x01	/* External transceiver active */
+}
+
+/*
+ * 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 succession.  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
+}
+
+register DSCOMMAND0 {
+	address			0x084
+	access_mode RW
+	bit	CACHETHEN	0x80
+	bit	DPARCKEN	0x40
+	bit	MPARCKEN	0x20
+	bit	EXTREQLCK	0x10
+	bit	INTSCBRAMSEL	0x08
+	bit	RAMPS		0x04
+	bit	USCBSIZE32	0x02
+	bit	CIOPARCKEN	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	WIDE_RESIDUE	0x50|SEQINT	/* need kernel to back up */
+						/* the SG array for us */
+	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	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	TRACEPOINT	0xb0|SEQINT
+	mask	TRACEPOINT2	0xc0|SEQINT
+	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	CIOPARERR	0x80	/* Ultra2 only */
+	bit	PCIERRSTAT	0x40	/* PCI only */
+	bit	MPARERR		0x20	/* PCI only */
+	bit	DPARERR		0x10	/* PCI only */
+	bit	SQPARERR	0x08
+	bit	ILLOPCODE	0x04
+	bit	ILLSADDR	0x02
+	bit	DSCTMOUT	0x02	/* Ultra3 only */
+	bit	ILLHADDR	0x01
+}
+
+/*
+ * Clear Interrupt Status (p. 3-52)
+ */
+register CLRINT {
+	address			0x092
+	access_mode WO
+	bit	CLRPARERR	0x10	/* PCI only */
+	bit	CLRBRKADRINT	0x08
+	bit	CLRSCSIINT      0x04
+	bit	CLRCMDINT 	0x02
+	bit	CLRSEQINT 	0x01
+}
+
+register DFCNTRL {
+	address			0x093
+	access_mode RW
+	bit	PRELOADEN	0x80	/* aic7890 only */
+	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	PRELOAD_AVAIL	0x80
+	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
+}
+
+/*
+ * SCSIDATL IMAGE Register (p. 5-104)
+ * Write to this register also go to SCSIDATL but this register will preserve
+ * the data for later reading as long as the SCSIDATL_IMGEN bit in the
+ * OPTIONMODE register is set.
+ */
+register SCSIDATL_IMG {
+	address			0x09c
+	access_mode RW
+}
+
+/*
+ * Queue Out FIFO (p. 3-61)
+ * Queue of SCBs that have completed and await the host
+ */
+register QOUTFIFO {
+	address			0x09d
+	access_mode WO
+}
+
+/*
+ * CRC Control 1 Register (p. 5-105)
+ * Control bits for the Ultra 160/m CRC facilities
+ */
+register CRCCONTROL1 {
+	address			0x09d
+	access_mode RW
+	bit	CRCONSEEN	0x80 /* CRC ON Single Edge ENable */
+	bit	CRCVALCHKEN	0x40 /* CRC Value Check Enable */
+	bit	CRCENDCHKEN	0x20 /* CRC End Check Enable */
+	bit	CRCREQCHKEN	0x10
+	bit	TARGCRCENDEN	0x08 /* Enable End CRC transfer when target */
+	bit	TARGCRCCNTEN	0x04 /* Enable CRC transfer when target */
+}
+
+/*
+ * Queue Out Count (p. 3-61)
+ * Number of queued SCBs in the Out FIFO
+ */
+register QOUTCNT {
+	address			0x09e
+	access_mode RO
+}
+
+/*
+ * SCSI Phase Register (p. 5-106)
+ * Current bus phase
+ */
+register SCSIPHASE {
+	address			0x09e
+	access_mode RO
+	bit	SP_STATUS		0x20
+	bit	SP_COMMAND		0x10
+	bit	SP_MSG_IN		0x08
+	bit	SP_MSG_OUT		0x04
+	bit	SP_DATA_IN		0x02
+	bit	SP_DATA_OUT	0x01
+}
+
+/*
+ * Special Function
+ */
+register SFUNCT {
+	address			0x09f
+	access_mode RW
+	bit	ALT_MODE	0x80
+}
+
+/*
+ * SCB Definition (p. 5-4)
+ */
+scb {
+	address			0x0a0
+	SCB_CONTROL {
+		size	1
+		bit	MK_MESSAGE      0x80
+		bit	DISCENB         0x40
+		bit	TAG_ENB		0x20
+		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 {
+		/*
+		 * Really only 3 bytes, but padded to make
+		 * the kernel's job easier.
+		 */
+		size	4
+	}
+	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
+	mask	DFTHRSH_100	0xc0
+}
+
+register CCHADDR {
+	address			0x0E0
+	size 8
+}
+
+register CCHCNT {
+	address			0x0E8
+}
+
+register CCSGRAM {
+	address			0x0E9
+}
+
+register CCSGADDR {
+	address			0x0EA
+}
+
+register CCSGCTL {
+	address			0x0EB
+	bit	CCSGDONE	0x80
+	bit	CCSGEN		0x08
+	bit	FLAG		0x02
+	bit	CCSGRESET	0x01
+}
+
+register CCSCBCNT {
+	address			0xEF
+}
+
+register CCSCBCTL {
+	address			0x0EE
+	bit	CCSCBDONE	0x80
+	bit	ARRDONE		0x40	/* SCB Array prefetch done */
+	bit	CCARREN		0x10
+	bit	CCSCBEN		0x08
+	bit	CCSCBDIR	0x04
+	bit	CCSCBRESET	0x01
+}
+
+register CCSCBADDR {
+	address			0x0ED
+}
+
+register CCSCBRAM {
+	address			0xEC
+}
+
+register CCSCBPTR {
+	address			0x0F1
+}
+
+register HNSCB_QOFF {
+	address			0x0F4
+}
+
+register HESCB_QOFF {
+	address			0x0F5
+}
+
+register SNSCB_QOFF {
+	address			0x0F6
+}
+
+register SESCB_QOFF {
+	address			0x0F7
+}
+
+register SDSCB_QOFF {
+	address			0x0F8
+}
+
+register QOFF_CTLSTA {
+	address			0x0FA
+	bit	ESTABLISH_SCB_AVAIL	0x80
+	bit	SCB_AVAIL	0x40
+	bit	SNSCB_ROLLOVER	0x20
+	bit	SDSCB_ROLLOVER	0x10
+	bit	SESCB_ROLLOVER	0x08
+	mask	SCB_QSIZE	0x07
+	mask	SCB_QSIZE_256	0x06
+}
+
+register DFF_THRSH {
+	address			0x0FB
+	mask	WR_DFTHRSH	0x70
+	mask	RD_DFTHRSH	0x07
+	mask	RD_DFTHRSH_MIN	0x00
+	mask	RD_DFTHRSH_25	0x01
+	mask	RD_DFTHRSH_50	0x02
+	mask	RD_DFTHRSH_63	0x03
+	mask	RD_DFTHRSH_75	0x04
+	mask	RD_DFTHRSH_85	0x05
+	mask	RD_DFTHRSH_90	0x06
+	mask	RD_DFTHRSH_MAX	0x07
+	mask	WR_DFTHRSH_MIN	0x00
+	mask	WR_DFTHRSH_25	0x10
+	mask	WR_DFTHRSH_50	0x20
+	mask	WR_DFTHRSH_63	0x30
+	mask	WR_DFTHRSH_75	0x40
+	mask	WR_DFTHRSH_85	0x50
+	mask	WR_DFTHRSH_90	0x60
+	mask	WR_DFTHRSH_MAX	0x70
+}
+
+register SG_CACHEPTR {
+	access_mode RW
+	address			0x0fc
+	mask	SG_USER_DATA	0xfc
+	bit	LAST_SEG	0x02
+	bit	LAST_SEG_DONE	0x01
+}
+
+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
+	/* 7890 Definitions */
+	bit	BRDDAT4		0x10
+	bit	BRDDAT3		0x08
+	bit	BRDDAT2		0x04
+	bit	BRDRW_ULTRA2	0x02
+	bit	BRDSTB_ULTRA2	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_SCSIRATE {
+		size		16
+	}
+	/*
+	 * Bit vector of targets that have ULTRA enabled.
+	 */
+	ULTRA_ENB {
+		size		2
+	}
+	/*
+	 * Bit vector of targets that have disconnection disabled.
+	 */
+	DISC_DSB {
+		size		2
+	}
+	/*
+	 * Single byte buffer used to designate the type or message
+	 * to send to a target.
+	 */
+	MSG_OUT {
+		size		1
+	}
+	/* Parameters for DMA Logic */
+	DMAPARAMS {
+		size		1
+		bit	PRELOADEN	0x80
+		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
+	}
+	SEQ_FLAGS {
+		size		1
+		bit	IDENTIFY_SEEN	0x80
+		bit	SCBPTR_VALID	0x20
+		bit	DPHASE		0x10
+		bit	AMTARGET	0x08
+		bit	WIDE_BUS	0x02
+		bit	TWIN_BUS	0x01
+	}
+	/*
+	 * Temporary storage for the
+	 * target/channel/lun of a
+	 * reconnecting target
+	 */
+	SAVED_TCL {
+		size		1
+	}
+	/* Working value of the number of SG segments left */
+	SG_COUNT {
+		size		1
+	}
+	/* Working value of SG pointer */
+	SG_NEXT	{
+		size		4
+	}
+	/*
+	 * 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
+	}
+	/*
+	 * head of list of SCBs awaiting
+	 * selection
+	 */
+	WAITING_SCBH {
+		size		1
+	}
+	/*
+	 * 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
+	}
+	/*
+	 * Address of the hardware scb array in the host.
+	 */
+	HSCB_ADDR {
+		size		4
+	}
+	/*
+	 * Address of the 256 byte array storing the SCBID of outstanding
+	 * untagged SCBs indexed by TCL.
+	 */
+	SCBID_ADDR {
+		size		4
+	}
+	/*
+	 * Address of the array of command descriptors used to store
+	 * information about incoming selections.
+	 */
+	TMODE_CMDADDR {
+		size		4
+	}
+	KERNEL_QINPOS {
+		size		1
+	}
+	QINPOS {
+		size		1
+	}
+	QOUTPOS {
+		size		1
+	}
+	/*
+	 * Offset into the command descriptor array for the next
+	 * available desciptor to use.
+	 */
+	TMODE_CMDADDR_NEXT {
+		size		1
+	}
+	ARG_1 {
+		size		1
+		mask	SEND_MSG	0x80
+		mask	SEND_SENSE	0x40
+		mask	SEND_REJ	0x20
+		mask	MSGOUT_PHASEMIS	0x10
+		alias	RETURN_1
+	}
+	ARG_2 {
+		size		1
+		alias	RETURN_2
+	}
+
+	/*
+	 * Snapshot of MSG_OUT taken after each message is sent.
+	 */
+	LAST_MSG {
+		size		1
+	}
+
+	/*
+	 * Number of times we have filled the CCSGRAM with prefetched
+	 * SG elements.
+	 */
+	PREFETCH_CNT {
+		size		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	TERM_ENB	0x80
+		bit	RESET_SCSI	0x40
+		mask	HSCSIID		0x07	/* our SCSI ID */
+		mask	HWSCSIID	0x0f	/* our SCSI ID if Wide Bus */
+	}
+	HOSTCONF {
+		address		0x05d
+		size		1
+	}
+	HA_274_BIOSCTRL	{
+		address		0x05f
+		size		1
+		mask	BIOSMODE		0x30
+		mask	BIOSDISABLED		0x30	
+		bit	CHANNEL_B_PRIMARY	0x08
+	}
+	/*
+	 * Per target SCSI offset values for Ultra2 controllers.
+	 */
+	TARG_OFFSET {
+		address		0x070
+		size		16
+	}
+}
+
+const SCB_LIST_NULL	0xff
+
+const CCSGADDR_MAX	0x80
+const CCSGRAM_MAXSEGS	16
+
+/* Offsets into the SCBID array where different data is stored */
+const UNTAGGEDSCB_OFFSET	0
+const QOUTFIFO_OFFSET		1
+const QINFIFO_OFFSET		2
+
+/* WDTR Message values */
+const BUS_8_BIT			0x00
+const BUS_16_BIT		0x01
+const BUS_32_BIT		0x02
+
+/* Offset maximums */
+const MAX_OFFSET_8BIT		0x0f
+const MAX_OFFSET_16BIT		0x08
+const MAX_OFFSET_ULTRA2		0x7f
+const HOST_MSG			0xff
+
+/* Target mode command processing constants */
+const CMD_GROUP_CODE_SHIFT	0x05
+const CMD_GROUP0_BYTE_DELTA	-4
+const CMD_GROUP2_BYTE_DELTA	-6
+const CMD_GROUP4_BYTE_DELTA	4
+const CMD_GROUP5_BYTE_DELTA	11
+
+/*
+ * Downloaded (kernel inserted) constants
+ */
+
+/*
+ * Number of command descriptors in the command descriptor array.
+ */
+const TMODE_NUMCMDS	download
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/aic7xxx.seq linux/drivers/scsi/aic7xxx_old/aic7xxx.seq
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/aic7xxx.seq	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_old/aic7xxx.seq	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,1411 @@
+/*
+ * Adaptec 274x/284x/294x device driver firmware for Linux and FreeBSD.
+ *
+ * Copyright (c) 1994-1999 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. 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.77 1998/06/28 02:58:57 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 pseudo-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.
+ */
+
+reset:
+	clr	SCSISIGO;		/* De-assert BSY */
+	and	SXFRCTL1, ~BITBUCKET;
+	/* Always allow reselection */
+	mvi	SCSISEQ, ENRSELI|ENAUTOATNP;
+
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		/* Ensure that no DMA operations are in progress */
+		clr	CCSGCTL;
+		clr	CCSCBCTL;
+	}
+
+	call	clear_target_state;
+poll_for_work:
+	and	SXFRCTL0, ~SPIOEN;
+	if ((p->features & AHC_QUEUE_REGS) == 0) {
+		mov	A, QINPOS;
+	}
+poll_for_work_loop:
+	if ((p->features & AHC_QUEUE_REGS) == 0) {
+		and	SEQCTL, ~PAUSEDIS;
+	}
+	test	SSTAT0, SELDO|SELDI	jnz selection;
+	test	SCSISEQ, ENSELO	jnz poll_for_work;
+	if ((p->features & AHC_TWIN) != 0) {
+		/*
+		 * 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|SELDI	jnz selection;
+		test	SCSISEQ, ENSELO	jnz poll_for_work;
+		xor	SBLKCTL,SELBUSB;	/* Toggle back */
+	}
+	cmp	WAITING_SCBH,SCB_LIST_NULL jne start_waiting;
+test_queue:
+	/* Has the driver posted any work for us? */
+	if ((p->features & AHC_QUEUE_REGS) != 0) {
+		test	QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
+		mov	NONE, SNSCB_QOFF;
+		inc	QINPOS;
+	} else {
+		or	SEQCTL, PAUSEDIS;
+		cmp	KERNEL_QINPOS, A je poll_for_work_loop;
+		inc	QINPOS;
+		and	SEQCTL, ~PAUSEDIS;
+	}
+
+/*
+ * 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 ((p->flags & AHC_PAGESCBS) != 0) {
+		mov	ALLZEROS	call	get_free_or_disc_scb;
+	}
+
+dequeue_scb:
+	add	A, -1, QINPOS;
+	mvi	QINFIFO_OFFSET call fetch_byte;
+
+	if ((p->flags & AHC_PAGESCBS) == 0) {
+		/* In the non-paging case, the SCBID == hardware SCB index */
+		mov	SCBPTR, RETURN_2;
+	}
+dma_queued_scb:
+/*
+ * DMA the SCB from host ram into the current SCB location.
+ */
+	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+	mov	RETURN_2	 call dma_scb;
+
+/*
+ * Preset the residual fields in case we never go through a data phase.
+ * This isn't done by the host so we can avoid a DMA to clear these
+ * fields for the normal case of I/O that completes without underrun
+ * or overrun conditions.
+ */
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov    SCB_RESID_DCNT, SCB_DATACNT, 3;
+	} else {
+		mov     SCB_RESID_DCNT[0],SCB_DATACNT[0];
+		mov     SCB_RESID_DCNT[1],SCB_DATACNT[1];
+		mov     SCB_RESID_DCNT[2],SCB_DATACNT[2];
+	}
+	mov     SCB_RESID_SGCNT, SCB_SGCOUNT;
+
+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.
+	 */
+	mov	SCBPTR, WAITING_SCBH;
+	call	start_selection;
+	jmp	poll_for_work;
+
+start_selection:
+	if ((p->features & AHC_TWIN) != 0) {
+		and	SINDEX,~SELBUSB,SBLKCTL;/* Clear channel select bit */
+		and	A,SELBUSB,SCB_TCL;	/* Get new channel bit */
+		or	SINDEX,A;
+		mov	SBLKCTL,SINDEX;		/* select channel */
+	}
+initialize_scsiid:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		and	A, TID, SCB_TCL;	/* Get target ID */
+		and	SCSIID_ULTRA2, OID;	/* Clear old target */
+		or	SCSIID_ULTRA2, A;
+	} else {
+		and	A, TID, SCB_TCL;	/* Get target ID */
+		and	SCSIID, OID;		/* Clear old target */
+		or	SCSIID, A;
+	}
+	mov	SCSIDATL, ALLZEROS;		/* clear out the latched */
+						/* data register, this */
+						/* fixes a bug on some */
+						/* controllers where the */
+						/* last byte written to */
+						/* this register can leak */
+						/* onto the data bus at */
+						/* bad times, such as during */
+						/* selection timeouts */
+	mvi	SCSISEQ, ENSELO|ENAUTOATNO|ENRSELI|ENAUTOATNP ret;
+
+/*
+ * Initialize Ultra mode setting and clear the SCSI channel.
+ * SINDEX should contain any additional bit's the client wants
+ * set in SXFRCTL0.
+ */
+initialize_channel:
+	or	SXFRCTL0, CLRSTCNT|CLRCHN, SINDEX;
+	if ((p->features & AHC_ULTRA) != 0) {
+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;
+	} 
+/*
+ * Initialize SCSIRATE with the appropriate value for this target.
+ * The SCSIRATE settings for each target are stored in an array
+ * based at TARG_SCSIRATE.
+ */
+ndx_dtr:
+	shr	A,4,SAVED_TCL;
+	if ((p->features & AHC_TWIN) != 0) {
+		test	SBLKCTL,SELBUSB	jz ndx_dtr_2;
+		or	SAVED_TCL, SELBUSB; 
+		or	A,0x08;			/* Channel B entries add 8 */
+ndx_dtr_2:
+	}
+
+	if ((p->features & AHC_ULTRA2) != 0) {
+		add	SINDEX, TARG_OFFSET, A;
+		mov	SCSIOFFSET, SINDIR;
+	}
+
+	add	SINDEX,TARG_SCSIRATE,A;
+	mov	SCSIRATE,SINDIR ret;
+
+
+selection:
+	test	SSTAT0,SELDO	jnz select_out;
+/*
+ * 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.
+ */
+initiator_reselect:
+	mvi	CLRSINT0, CLRSELDI;
+	/* XXX test for and handle ONE BIT condition */
+	and	SAVED_TCL, SELID_MASK, SELID;
+	mvi	CLRSINT1,CLRBUSFREE;
+	or	SIMODE1, ENBUSFREE;		/*
+						 * We aren't expecting a
+						 * bus free, so interrupt
+						 * the kernel driver if it
+						 * happens.
+						 */
+	mvi	SPIOEN call	initialize_channel;
+	mvi	MSG_OUT, MSG_NOOP;		/* No message to send */
+	jmp	ITloop;
+
+/*
+ * 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_out:
+	/* 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;
+	mvi	CLRSINT1,CLRBUSFREE;
+	or	SIMODE1, ENBUSFREE;		/*
+						 * We aren't expecting a
+						 * bus free, so interrupt
+						 * the kernel driver if it
+						 * happens.
+						 */
+	mvi	SPIOEN call	initialize_channel;
+/*
+ * As soon as we get a successful selection, the target should go
+ * into the message out phase since we have ATN asserted.
+ */
+	mvi	MSG_OUT, MSG_IDENTIFYFLAG;
+	or	SEQ_FLAGS, IDENTIFY_SEEN;
+
+/*
+ * Main loop for information transfer phases.  Wait for the target
+ * to assert REQ before checking MSG, C/D and I/O for the bus phase.
+ */
+ITloop:
+	call	phase_lock;
+
+	mov	A, LASTPHASE;
+
+	test	A, ~P_DATAIN	jz p_data;
+	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 */
+	and	SXFRCTL0, ~SPIOEN;
+	test	SSTAT1,REQINIT|BUSFREE	jz .;
+	test	SSTAT1, BUSFREE jnz poll_for_work;
+	mvi	INTSTAT, BAD_PHASE;
+	
+clear_target_state:
+	/*
+	 * We assume that the kernel driver may reset us
+	 * at any time, even in the middle of a DMA, so
+	 * clear DFCNTRL too.
+	 */
+	clr	DFCNTRL;
+
+	/*
+	 * We don't know the target we will connect to,
+	 * so default to narrow transfers to avoid
+	 * parity problems.
+	 */
+	if ((p->features & AHC_ULTRA2) != 0) {
+		bmov    SCSIRATE, ALLZEROS, 2;
+	} else {
+		clr     SCSIRATE;
+		and     SXFRCTL0, ~(FAST20);
+	}
+	mvi	LASTPHASE, P_BUSFREE;
+	/* clear target specific flags */
+	clr	SEQ_FLAGS ret;
+
+/*
+ * 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:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		bmov	HADDR, SHADDR, 4;
+		bmov    HCNT, SCB_RESID_DCNT, 3;
+	}
+	if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+		bmov    STCNT, SCB_RESID_DCNT, 3;
+	}
+	if ((p->features & AHC_CMD_CHAN) == 0) {
+		mvi	DINDEX, STCNT;
+		mvi	SCB_RESID_DCNT	call bcopy_3;
+	}
+	jmp	data_phase_loop;
+
+p_data:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		mvi	DMAPARAMS, PRELOADEN|SCSIEN|HDMAEN;
+	} else {
+		mvi	DMAPARAMS, WIDEODD|SCSIEN|SDMAEN|HDMAEN|FIFORESET;
+	}
+	test	LASTPHASE, IOI jnz . + 2;
+	or	DMAPARAMS, DIRECTION;
+	call	assert;			/*
+					 * Ensure entering a data
+					 * phase is okay - seen identify, etc.
+					 */
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	CCSGADDR, CCSGADDR_MAX;
+	}
+	test	SEQ_FLAGS, DPHASE	jnz data_phase_reinit;
+
+	/* We have seen a data phase */
+	or	SEQ_FLAGS, DPHASE;
+
+	/*
+	 * 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.
+	 */
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov	HADDR, SCB_DATAPTR, 7;
+		bmov    STCNT, HCNT, 3;
+		bmov    SG_COUNT, SCB_SGCOUNT, 5;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCB_DATAPTR	call bcopy_7;
+		call	set_stcnt_from_hcnt;
+		mvi	DINDEX, SG_COUNT;
+		mvi	SCB_SGCOUNT	call bcopy_5;
+	}
+
+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;
+	and	DMAPARAMS, ~(HDMAEN|SDMAEN);
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		if ((p->features & AHC_ULTRA2) != 0) {
+			bmov	HCNT, ALLONES, 3;
+		}
+		bmov	STCNT, ALLONES, 3;
+	} else {
+		mvi	STCNT[0], 0xFF;
+		mvi	STCNT[1], 0xFF;
+		mvi	STCNT[2], 0xFF;
+	}
+data_phase_inbounds:
+/* If we are the last SG block, tell the hardware. */
+	cmp	SG_COUNT,0x01 jne data_phase_wideodd;
+	if ((p->features & AHC_ULTRA2) == 0) {
+		and	DMAPARAMS, ~WIDEODD;
+	} else {
+		mvi	SG_CACHEPTR, LAST_SEG;
+	}
+data_phase_wideodd:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		mov	SINDEX, ALLONES;
+		mov	DFCNTRL, DMAPARAMS;
+		test	SSTAT0, SDONE jnz .;
+data_phase_dma_loop:
+		test	SSTAT0, SDONE jnz data_phase_dma_done;
+		test	SSTAT1,PHASEMIS	jz data_phase_dma_loop;	/* ie. underrun */
+data_phase_dma_phasemis:
+		test	SSTAT0,SDONE	jnz data_phase_dma_done;
+		clr	SINDEX;			/* Remember the phasemiss */
+	} else {
+		mov	DMAPARAMS  call dma;
+	}
+
+data_phase_dma_done:
+/* 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? */
+/*
+ * 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:
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		/*
+		 * Do we have any prefetch left???
+		 */
+		cmp	CCSGADDR, CCSGADDR_MAX jne prefetched_segs_avail;
+
+		/*
+		 * Fetch MIN(CCSGADDR_MAX, (SG_COUNT * 8)) bytes.
+		 */
+		add	A, -(CCSGRAM_MAXSEGS + 1), SG_COUNT;
+		mvi	A, CCSGADDR_MAX;
+		jc	. + 2;
+		shl	A, 3, SG_COUNT;
+		mov	CCHCNT, A;
+		bmov	CCHADDR, SG_NEXT, 4;
+		mvi	CCSGCTL, CCSGEN|CCSGRESET;
+		test	CCSGCTL, CCSGDONE jz .;
+		and	CCSGCTL, ~CCSGEN;
+		test	CCSGCTL, CCSGEN jnz .;
+		mvi	CCSGCTL, CCSGRESET;
+prefetched_segs_avail:
+		bmov 	HADDR, CCSGRAM, 8;
+		if ((p->features & AHC_ULTRA2) == 0) {
+			bmov    STCNT, HCNT, 3;
+		}
+	} else {
+		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;
+		call	set_stcnt_from_hcnt;
+	}
+
+/* Advance the SG pointer */
+	clr	A;			/* add sizeof(struct scatter) */
+	add	SG_NEXT[0],SG_SIZEOF;
+	adc	SG_NEXT[1],A;
+
+	test    SSTAT1, REQINIT jz .;
+	test	SSTAT1,PHASEMIS	jz data_phase_loop;
+
+/* This drops the last SG segment down to the shadow layer for us */
+	if ((p->features & AHC_ULTRA2) != 0) {
+		mov	DFCNTRL, DMAPARAMS;
+		test	SSTAT0, SDONE	jnz .;
+	}
+
+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.
+ */
+	if ((p->features & AHC_ULTRA2) != 0) {
+		call	ultra2_dmafinish;
+	}
+	if ((p->features & AHC_ULTRA2) == 0) {
+		if ((p->features & AHC_CMD_CHAN) != 0) {
+			bmov    SCB_RESID_DCNT, STCNT, 3;
+			mov	SCB_RESID_SGCNT, SG_COUNT;
+		} else {
+			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;
+		}
+	}
+
+	jmp	ITloop;
+
+data_phase_overrun:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		call	ultra2_dmafinish;
+	}
+/*
+ * Turn off BITBUCKET mode and notify the host
+ */
+	and	SXFRCTL1, ~BITBUCKET;
+	mvi	INTSTAT,DATA_OVERRUN;
+	jmp	ITloop;
+
+ultra2_dmafinish:
+	if ((p->features & AHC_ULTRA2) != 0) {
+		test	DFCNTRL, DIRECTION jnz ultra2_dmahalt;
+		and	DFCNTRL, ~SCSIEN;
+		test	DFCNTRL, SCSIEN jnz .;
+ultra2_dmafifoflush:
+		or	DFCNTRL, FIFOFLUSH;
+		test	DFSTATUS, FIFOEMP jz . - 1;
+		/*
+		 * hardware bug alert!  This needless set of jumps is to
+		 * protect against a FIFOEMP status bit glitch in the
+		 * silicon.
+		 */
+		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+		test	DFSTATUS, FIFOEMP jz ultra2_dmafifoflush;
+		test	DFSTATUS, MREQPEND	jnz .;
+ultra2_dmahalt:
+		test	SCSIOFFSET, 0x7f	jnz ultra2_shutdown;
+ultra2_await_nreq:
+		test	SCSISIGI, REQI	jz ultra2_shutdown;
+		test	SSTAT1, (PHASEMIS|REQINIT)	jz ultra2_await_nreq;
+ultra2_shutdown:
+		and     DFCNTRL, ~(HDMAEN|SCSIEN);
+		test	DFCNTRL, (HDMAEN|SCSIEN) jnz .;
+		bmov	SCB_RESID_DCNT, STCNT, 3;
+		mov	SCB_RESID_SGCNT, SG_COUNT;
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+		ret;
+	}
+
+/*
+ * Command phase.  Set up the DMA registers and let 'er rip.
+ */
+p_command:
+	call	assert;
+
+/*
+ * Load HADDR and HCNT.
+ */
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov	HADDR, SCB_CMDPTR, 5;
+		bmov	HCNT[1], ALLZEROS, 2;
+		if ((p->features & AHC_ULTRA2) == 0) {
+			bmov	STCNT, HCNT, 3;
+		}
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCB_CMDPTR	call bcopy_5;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		call	set_stcnt_from_hcnt;
+	}
+
+	if ((p->features & AHC_ULTRA2) == 0) {
+		mvi	(SCSIEN|SDMAEN|HDMAEN|DIRECTION|FIFORESET) call dma;
+	} else {
+		mvi	DFCNTRL, (PRELOADEN|SCSIEN|HDMAEN|DIRECTION);
+		test	SSTAT0, SDONE jnz .;
+p_command_dma_loop:
+		test	SSTAT0, SDONE jnz p_command_ultra2_dma_done;
+		test	SSTAT1,PHASEMIS	jz p_command_dma_loop;	/* ie. underrun */
+p_command_ultra2_dma_done:
+		test	SCSISIGI, REQI	jz p_command_ultra2_shutdown;
+		test	SSTAT1, (PHASEMIS|REQINIT)	jz p_command_ultra2_dma_done;
+p_command_ultra2_shutdown:
+		and     DFCNTRL, ~(HDMAEN|SCSIEN);
+		test	DFCNTRL, (HDMAEN|SCSIEN) jnz .;
+		or	SXFRCTL0, CLRSTCNT|CLRCHN;
+	}
+	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 MSG_OUT is 0x80, build I full indentify message
+ * sequence and send it to the target.  In addition, if the MK_MESSAGE bit
+ * is set in the SCB_CONTROL byte, interrupt the host and allow it to send
+ * it's own message.
+ * 
+ * If MSG_OUT is == HOST_MSG, also interrupt the host and take a message.
+ * This is done to allow the hsot to send messages outside of an identify
+ * sequence while protecting the seqencer from testing the MK_MESSAGE bit
+ * on an SCB that might not be for the current nexus. (For example, a
+ * BDR message in responce to a bad reselection would leave us pointed to
+ * an SCB that doesn't have anything to do with the current target).
+ * Otherwise, treat MSG_OUT as a 1 byte message to send (abort, abort tag,
+ * bus device reset).
+ *
+ * When there are no messages to send, MSG_OUT should be set to MSG_NOOP,
+ * in case the target decides to put us in this phase for some strange
+ * reason.
+ */
+p_mesgout_retry:
+	or      SCSISIGO,ATNO,LASTPHASE;/* turn on ATN for the retry */
+p_mesgout:
+	mov	SINDEX, MSG_OUT;
+	cmp	SINDEX, MSG_IDENTIFYFLAG jne p_mesgout_from_host;
+p_mesgout_identify:
+	if ((p->features & AHC_WIDE) != 0) {
+		and	SINDEX,0xf,SCB_TCL;	/* lun */
+	} else {
+		and	SINDEX,0x7,SCB_TCL;	/* lun */
+	}
+	and	A,DISCENB,SCB_CONTROL;	/* mask off disconnect privledge */
+	or	SINDEX,A;		/* or in disconnect privledge */
+	or	SINDEX,MSG_IDENTIFYFLAG;
+p_mesgout_mk_message:
+	test	SCB_CONTROL,MK_MESSAGE  jz p_mesgout_tag;
+	mov	SCSIDATL, SINDEX;	/* Send the last byte */
+	jmp	p_mesgout_from_host + 1;/* Skip HOST_MSG test */
+/*
+ * 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.
+ */
+p_mesgout_tag:
+	test	SCB_CONTROL,TAG_ENB jz  p_mesgout_onebyte;
+	mov	SCSIDATL, SINDEX;	/* Send the identify message */
+	call	phase_lock;
+	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
+	and	SCSIDATL,TAG_ENB|SCB_TAG_TYPE,SCB_CONTROL;
+	call	phase_lock;
+	cmp	LASTPHASE, P_MESGOUT	jne p_mesgout_done;
+	mov	SCB_TAG	jmp p_mesgout_onebyte;
+/*
+ * Interrupt the driver, and allow it to send a message
+ * if it asks.
+ */
+p_mesgout_from_host:
+	cmp	SINDEX, HOST_MSG	jne p_mesgout_onebyte;
+	mvi     INTSTAT,AWAITING_MSG;
+	nop;
+	/*
+	 * Did the host detect a phase change?
+	 */
+	cmp	RETURN_1, MSGOUT_PHASEMIS je p_mesgout_done;
+
+p_mesgout_onebyte:
+	mvi	CLRSINT1, CLRATNO;
+	mov	SCSIDATL, SINDEX;
+
+/*
+ * 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.
+ */
+	call	phase_lock;
+	cmp     LASTPHASE, P_MESGOUT    je p_mesgout_retry;
+
+p_mesgout_done:
+	mvi	CLRSINT1,CLRATNO;	/* Be sure to turn ATNO off */
+	mov	LAST_MSG, MSG_OUT;
+	cmp	MSG_OUT, MSG_IDENTIFYFLAG jne . + 2;
+	and	SCB_CONTROL, ~MK_MESSAGE;
+	mvi	MSG_OUT, MSG_NOOP;	/* No message left */
+	jmp	ITloop;
+
+/*
+ * Message in phase.  Bytes are read using Automatic PIO mode.
+ */
+p_mesgin:
+	mvi	ACCUM		call inb_first;	/* read the 1st message byte */
+
+	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;
+	cmp	A,MSG_IGN_WIDE_RESIDUE	je mesgin_wide_residue;
+
+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 STATUS_GOOD (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 complete;	/* Good Status? */
+upload_scb:
+	mvi	DMAPARAMS, FIFORESET;
+	mov	SCB_TAG		call dma_scb;
+check_status:
+	test	SCB_TARGET_STATUS,0xff	jz complete;	/* Just a residual? */
+	mvi	INTSTAT,BAD_STATUS;			/* let driver know */
+	nop;
+	cmp	RETURN_1, SEND_SENSE	jne complete;
+	/* This SCB becomes the next to execute as it will retrieve sense */
+	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+	mov	SCB_TAG		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;
+
+complete:
+	/* If we are untagged, clear our address up in host ram */
+	test	SCB_CONTROL, TAG_ENB jnz complete_post;
+	mov	A, SAVED_TCL;
+	mvi	UNTAGGEDSCB_OFFSET call post_byte_setup;
+	mvi	SCB_LIST_NULL call post_byte;
+
+complete_post:
+	/* Post the SCB and issue an interrupt */
+	if ((p->features & AHC_QUEUE_REGS) != 0) {
+		mov	A, SDSCB_QOFF;
+	} else {
+		mov	A, QOUTPOS;
+	}
+	mvi	QOUTFIFO_OFFSET call post_byte_setup;
+	mov	SCB_TAG call post_byte;
+	if ((p->features & AHC_QUEUE_REGS) == 0) {
+		inc 	QOUTPOS;
+	}
+	mvi	INTSTAT,CMDCMPLT;
+
+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	INTSTAT,EXTENDED_MSG;		/* let driver know */
+	jmp	ITloop;
+
+/*
+ * 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;
+	call	add_scb_to_disc_list;
+	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;
+	/*
+	 * The SCB SGPTR becomes the next one we'll download,
+	 * and the SCB DATAPTR becomes the current SHADDR.
+	 * Use the residual number since STCNT is corrupted by
+	 * any message transfer.
+	 */
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov    SCB_SGCOUNT, SG_COUNT, 5;
+		bmov    SCB_DATAPTR, SHADDR, 4;
+		bmov    SCB_DATACNT, SCB_RESID_DCNT, 3;
+	} else {
+		mvi	DINDEX, SCB_SGCOUNT;
+		mvi	SG_COUNT	call bcopy_5;
+		mvi	DINDEX, SCB_DATAPTR;
+		mvi	SHADDR		call bcopy_4;
+		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:
+	
+	if ((p->features & AHC_WIDE) != 0) {
+		and	A,0x0f;		/* lun in lower four bits */
+	} else {
+		and	A,0x07;		/* lun in lower three bits */
+	}
+	or      SAVED_TCL,A;		/* SAVED_TCL should be complete now */
+
+	mvi     ARG_2, SCB_LIST_NULL;   /* SCBID of prev SCB in disc List */
+	call	get_untagged_SCBID;
+	cmp	ARG_1, SCB_LIST_NULL	je snoop_tag;
+	if ((p->flags & AHC_PAGESCBS) != 0) {
+		test	SEQ_FLAGS, SCBPTR_VALID	jz use_retrieveSCB;
+	}
+	/*
+	 * If the SCB was found in the disconnected list (as is
+	 * always the case in non-paging scenarios), SCBPTR is already
+	 * set to the correct SCB.  So, simply setup the SCB and get
+	 * on with things.
+	 */
+	mov	SCBPTR	call rem_scb_from_disc_list;
+	jmp	setup_SCB;
+/*
+ * 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 search 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:
+	call	phase_lock;
+	cmp	LASTPHASE, P_MESGIN	jne not_found;
+	cmp	SCSIBUSL,MSG_SIMPLE_Q_TAG jne not_found;
+get_tag:
+	mvi	ARG_1	call inb_next;	/* tag value */
+
+use_retrieveSCB:
+	call	retrieveSCB;
+setup_SCB:
+	mov	A, SAVED_TCL;
+	cmp	SCB_TCL, A	jne not_found_cleanup_scb;
+	test	SCB_CONTROL,DISCONNECTED jz not_found_cleanup_scb;
+	and	SCB_CONTROL,~DISCONNECTED;
+	or	SEQ_FLAGS,IDENTIFY_SEEN;	  /* make note of IDENTIFY */
+	/* See if the host wants to send a message upon reconnection */
+	test	SCB_CONTROL, MK_MESSAGE jz mesgin_done;
+	and	SCB_CONTROL, ~MK_MESSAGE;
+	mvi	HOST_MSG	call mk_mesg;
+	jmp	mesgin_done;
+
+not_found_cleanup_scb:
+	test	SCB_CONTROL, DISCONNECTED jz . + 3;
+	call	add_scb_to_disc_list;
+	jmp	not_found;
+	call	add_scb_to_free_list;
+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;
+
+/*
+ * Wide Residue.  We handle the simple cases, but pass of the one hard case
+ * to the kernel (when the residue byte happened to cause us to advance our
+ * sg element array, so we know have to back that advance out).
+ */
+mesgin_wide_residue:
+	mvi	ARG_1	call inb_next; /* ACK the wide_residue and get */
+				       /* the size byte */
+/*
+ * In order for this to be reliable, we have to do all sorts of horrible
+ * magic in terms of resetting the datafifo and reloading the shadow layer
+ * with the correct new values (so that a subsequent save data pointers
+ * message will do the right thing).  We let the kernel do that work.
+ */
+ 	mvi	INTSTAT, WIDE_RESIDUE;
+	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:
+	or	SCSISIGO,ATNO,LASTPHASE;/* turn on ATNO */
+	mov	MSG_OUT,SINDEX 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 .;
+	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.
+ */
+if ((p->features & AHC_ULTRA2) == 0) {
+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:
+	cmp	LASTPHASE, P_COMMAND	je dma_await_nreq;
+	test	SCSIRATE, 0x0f	jnz dma_shutdown;
+dma_await_nreq:
+	test	SCSISIGI, REQI	jz dma_shutdown;
+	test	SSTAT1, (PHASEMIS|REQINIT)	jz dma_await_nreq;
+dma_shutdown:
+	and	DFCNTRL, ~(SCSIEN|SDMAEN|HDMAEN);
+dma_halt:
+	/*
+	 * Some revisions of the aic7880 have a problem where, if the
+	 * data fifo is full, but the PCI input latch is not empty, 
+	 * HDMAEN cannot be cleared.  The fix used here is to attempt
+	 * to drain the data fifo until there is space for the input
+	 * latch to drain and HDMAEN de-asserts.
+	 */
+	mov	NONE, DFDAT;
+	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,IDENTIFY_SEEN	jnz return;	/* seen IDENTIFY? */
+
+	mvi	INTSTAT,NO_IDENT 	ret;	/* no - tell the kernel */
+
+/*
+ * Locate a disconnected SCB either by SAVED_TCL (ARG_1 is SCB_LIST_NULL)
+ * or by the SCBID ARG_1.  The search begins at the SCB index passed in
+ * via SINDEX which is an SCB that must be on the disconnected list.  If
+ * the SCB cannot be found, SINDEX will be SCB_LIST_NULL, otherwise, SCBPTR
+ * is set to the proper SCB.
+ */
+findSCB:
+	mov	SCBPTR,SINDEX;			/* Initialize SCBPTR */
+	cmp	ARG_1, SCB_LIST_NULL	jne findSCB_by_SCBID;
+	mov	A, SAVED_TCL;
+	mvi	SCB_TCL	jmp findSCB_loop;	/* &SCB_TCL -> SINDEX */
+findSCB_by_SCBID:
+	mov	A, ARG_1;			/* Tag passed in ARG_1 */
+	mvi	SCB_TAG	jmp findSCB_loop;	/* &SCB_TAG -> SINDEX */
+findSCB_next:
+	mov     ARG_2, SCBPTR;
+	cmp	SCB_NEXT, SCB_LIST_NULL je notFound;
+	mov	SCBPTR,SCB_NEXT;
+	dec	SINDEX;		/* Last comparison moved us too far */
+findSCB_loop:
+	cmp	SINDIR, A	jne findSCB_next;
+	mov	SINDEX, SCBPTR 	ret;
+notFound:
+	mvi	SINDEX, SCB_LIST_NULL	ret;
+
+/*
+ * Retrieve an SCB by SCBID first searching the disconnected list falling
+ * back to DMA'ing the SCB down from the host.  This routine assumes that
+ * ARG_1 is the SCBID of interrest and that SINDEX is the position in the
+ * disconnected list to start the search from.  If SINDEX is SCB_LIST_NULL,
+ * we go directly to the host for the SCB.
+ */
+retrieveSCB:
+	test	SEQ_FLAGS, SCBPTR_VALID	jz retrieve_from_host;
+	mov	SCBPTR	call findSCB;	/* Continue the search */
+	cmp	SINDEX, SCB_LIST_NULL	je retrieve_from_host;
+
+/*
+ * This routine expects SINDEX to contain the index of the SCB to be
+ * removed, SCBPTR to be pointing to that SCB, and ARG_2 to be the
+ * SCBID of the SCB just previous to this one in the list or SCB_LIST_NULL
+ * if it is at the head.
+ */
+rem_scb_from_disc_list:
+/* Remove this SCB from the disconnection list */
+	cmp     ARG_2, SCB_LIST_NULL    je rHead;
+	mov	DINDEX, SCB_NEXT;
+	mov	SCBPTR, ARG_2;
+	mov	SCB_NEXT, DINDEX;
+	mov	SCBPTR, SINDEX ret;
+rHead:
+	mov	DISCONNECTED_SCBH,SCB_NEXT ret;
+
+retrieve_from_host:
+/*
+ * We didn't find it.  Pull an SCB and DMA down the one we want.
+ * We should never get here in the non-paging case.
+ */
+	mov	ALLZEROS	call	get_free_or_disc_scb;
+	mvi	DMAPARAMS, HDMAEN|DIRECTION|FIFORESET;
+	/* Jump instead of call as we want to return anyway */
+	mov	ARG_1	jmp dma_scb;
+
+/*
+ * Determine whether a target is using tagged or non-tagged transactions
+ * by first looking for a matching transaction based on the TCL and if
+ * that fails, looking up this device in the host's untagged SCB array.
+ * The TCL to search for is assumed to be in SAVED_TCL.  The value is
+ * returned in ARG_1 (SCB_LIST_NULL for tagged, SCBID for non-tagged).
+ * The SCBPTR_VALID bit is set in SEQ_FLAGS if we found the information
+ * in an SCB instead of having to go to the host.
+ */
+get_untagged_SCBID:
+	cmp	DISCONNECTED_SCBH, SCB_LIST_NULL je get_SCBID_from_host;
+	mvi	ARG_1, SCB_LIST_NULL;
+	mov	DISCONNECTED_SCBH call findSCB;
+	cmp	SINDEX, SCB_LIST_NULL	je get_SCBID_from_host;
+	or	SEQ_FLAGS, SCBPTR_VALID;/* Was in disconnected list */
+	test	SCB_CONTROL, TAG_ENB	jnz . + 2;
+	mov	ARG_1, SCB_TAG	ret;
+	mvi	ARG_1, SCB_LIST_NULL ret;
+
+/*
+ * Fetch a byte from host memory given an index of (A + (256 * SINDEX))
+ * and a base address of SCBID_ADDR.  The byte is returned in RETURN_2.
+ */
+fetch_byte:
+	mov	ARG_2, SINDEX;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	SCBID_ADDR call set_1byte_addr;
+		mvi	CCHCNT, 1;
+		mvi	CCSGCTL, CCSGEN|CCSGRESET;
+		test	CCSGCTL, CCSGDONE jz .;
+		mvi	CCSGCTL, CCSGRESET;
+		bmov	RETURN_2, CCSGRAM, 1 ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCBID_ADDR call set_1byte_addr;
+		mvi	HCNT[0], 1;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mvi	DFCNTRL, HDMAEN|DIRECTION|FIFORESET;
+		call	dma_finish;
+		mov	RETURN_2, DFDAT ret;
+	}
+
+/*
+ * Prepare the hardware to post a byte to host memory given an
+ * index of (A + (256 * SINDEX)) and a base address of SCBID_ADDR.
+ */
+post_byte_setup:
+	mov	ARG_2, SINDEX;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	SCBID_ADDR call	set_1byte_addr;
+		mvi	CCHCNT, 1;
+		mvi	CCSCBCTL, CCSCBRESET ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	SCBID_ADDR call	set_1byte_addr;
+		mvi	HCNT[0], 1;
+		clr	HCNT[1];
+		clr	HCNT[2];
+		mvi	DFCNTRL, FIFORESET ret;
+	}
+
+post_byte:
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		bmov	CCSCBRAM, SINDEX, 1;
+		or	CCSCBCTL, CCSCBEN|CCSCBRESET;
+		test	CCSCBCTL, CCSCBDONE jz .;
+		clr	CCSCBCTL ret;
+	} else {
+		mov	DFDAT, SINDEX;
+		or	DFCNTRL, HDMAEN|FIFOFLUSH;
+		jmp	dma_finish;
+	}
+
+get_SCBID_from_host:
+	mov	A, SAVED_TCL;
+	mvi	UNTAGGEDSCB_OFFSET call fetch_byte;
+	mov	RETURN_1,  RETURN_2 ret;
+
+phase_lock:     
+	test	SSTAT1, REQINIT jz phase_lock;
+	test	SSTAT1, SCSIPERR jnz phase_lock;
+	and	SCSISIGO, PHASE_MASK, SCSISIGI;
+	and	LASTPHASE, PHASE_MASK, SCSISIGI ret;
+
+if ((p->features & AHC_CMD_CHAN) == 0) {
+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;
+}
+
+/*
+ * Setup addr assuming that A is an index into
+ * an array of 32byte objects, SINDEX contains
+ * the base address of that array, and DINDEX
+ * contains the base address of the location
+ * to store the indexed address.
+ */
+set_32byte_addr:
+	shr	ARG_2, 3, A;
+	shl	A, 5;
+/*
+ * Setup addr assuming that A + (ARG_1 * 256) is an
+ * index into an array of 1byte objects, SINDEX contains
+ * the base address of that array, and DINDEX contains
+ * the base address of the location to store the computed
+ * address.
+ */
+set_1byte_addr:
+	add	DINDIR, A, SINDIR;
+	mov	A, ARG_2;
+	adc	DINDIR, A, SINDIR;
+	clr	A;
+	adc	DINDIR, A, SINDIR;
+	adc	DINDIR, A, SINDIR ret;
+
+/*
+ * Either post or fetch and SCB from host memory based on the
+ * DIRECTION bit in DMAPARAMS. The host SCB index is in SINDEX.
+ */
+dma_scb:
+	mov	A, SINDEX;
+	if ((p->features & AHC_CMD_CHAN) != 0) {
+		mvi	DINDEX, CCHADDR;
+		mvi	HSCB_ADDR call set_32byte_addr;
+		mov	CCSCBPTR, SCBPTR;
+		mvi	CCHCNT, 32;
+		test	DMAPARAMS, DIRECTION jz dma_scb_tohost;
+		mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBDIR|CCSCBRESET;
+		cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN|CCSCBDIR jne .;
+		jmp	dma_scb_finish;
+dma_scb_tohost:
+		if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
+			mvi	CCSCBCTL, CCSCBRESET;
+			bmov	CCSCBRAM, SCB_CONTROL, 32;
+			or	CCSCBCTL, CCSCBEN|CCSCBRESET;
+			test	CCSCBCTL, CCSCBDONE jz .;
+		} else {
+			mvi	CCSCBCTL, CCARREN|CCSCBEN|CCSCBRESET;
+			cmp	CCSCBCTL, CCSCBDONE|ARRDONE|CCARREN|CCSCBEN jne .;
+		}
+dma_scb_finish:
+		clr	CCSCBCTL;
+		test	CCSCBCTL, CCARREN|CCSCBEN jnz .;
+		ret;
+	} else {
+		mvi	DINDEX, HADDR;
+		mvi	HSCB_ADDR call set_32byte_addr;
+		mvi	HCNT[0], 32;
+		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, 32, 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;
+		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.
+ */
+if ((p->features & AHC_CMD_CHAN) == 0) {
+dma_finish:
+	test	DFSTATUS,HDONE	jz dma_finish;
+	/* Turn off DMA */
+	and	DFCNTRL, ~HDMAEN;
+	test	DFCNTRL, HDMAEN jnz .;
+	ret;
+}
+
+add_scb_to_free_list:
+	if ((p->flags & AHC_PAGESCBS) != 0) {
+		mov	SCB_NEXT, FREE_SCBH;
+		mov	FREE_SCBH, SCBPTR;
+	}
+	mvi	SCB_TAG, SCB_LIST_NULL ret;
+
+if ((p->flags & AHC_PAGESCBS) != 0) {
+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;
+dma_up_scb:
+	mvi	DMAPARAMS, FIFORESET;
+	mov	SCB_TAG		call dma_scb;
+unlink_disc_scb:
+	mov	DISCONNECTED_SCBH, SCB_NEXT ret;
+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.
+ */
+	mov	SCB_NEXT, DISCONNECTED_SCBH;
+	mov	DISCONNECTED_SCBH, SCBPTR ret;
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_old/aic7xxx_proc.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/aic7xxx_proc.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_old/aic7xxx_proc.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,413 @@
+/*+M*************************************************************************
+ * Adaptec AIC7xxx device driver proc support for Linux.
+ *
+ * Copyright (c) 1995, 1996 Dean W. Gehnert
+ *
+ * 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.
+ *
+ * ----------------------------------------------------------------
+ *  o Modified from the EATA-DMA /proc support.
+ *  o Additional support for device block statistics provided by
+ *    Matthew Jacob.
+ *  o Correction of overflow by Heinz Mauelshagen
+ *  o Adittional corrections by Doug Ledford
+ *
+ *  Dean W. Gehnert, deang@teleport.com, 05/01/96
+ *
+ *  $Id: aic7xxx_proc.c,v 4.1 1997/06/97 08:23:42 deang Exp $
+ *-M*************************************************************************/
+
+#include <linux/config.h>
+
+#define	BLS	(&aic7xxx_buffer[size])
+#define HDRB \
+"             < 2K      2K+     4K+     8K+    16K+    32K+    64K+   128K+"
+
+#ifdef PROC_DEBUG
+extern int vsprintf(char *, const char *, va_list);
+
+static void
+proc_debug(const char *fmt, ...)
+{
+  va_list ap;
+  char buf[256];
+
+  va_start(ap, fmt);
+  vsprintf(buf, fmt, ap);
+  printk(buf);
+  va_end(ap);
+}
+#else /* PROC_DEBUG */
+#  define proc_debug(fmt, args...)
+#endif /* PROC_DEBUG */
+
+static int aic7xxx_buffer_size = 0;
+static char *aic7xxx_buffer = NULL;
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_set_info
+ *
+ * Description:
+ *   Set parameters for the driver from the /proc filesystem.
+ *-F*************************************************************************/
+int
+aic7xxx_set_info(char *buffer, int length, struct Scsi_Host *HBAptr)
+{
+  proc_debug("aic7xxx_set_info(): %s\n", buffer);
+  return (-ENOSYS);  /* Currently this is a no-op */
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_proc_info
+ *
+ * Description:
+ *   Return information to handle /proc support for the driver.
+ *-F*************************************************************************/
+int
+aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length, 
+                    int hostno, int inout)
+{
+  struct Scsi_Host *HBAptr;
+  struct aic7xxx_host *p;
+  int    size = 0;
+  unsigned char i;
+  struct aic7xxx_xferstats *sp;
+  unsigned char target;
+
+  HBAptr = NULL;
+
+  for(p=first_aic7xxx; p->host->host_no != hostno; p=p->next)
+    ;
+
+  if (!p)
+  {
+    size += sprintf(buffer, "Can't find adapter for host number %d\n", hostno);
+    if (size > length)
+    {
+      return (size);
+    }
+    else
+    {
+      return (length);
+    }
+  }
+
+  HBAptr = p->host;
+
+  if (inout == TRUE) /* Has data been written to the file? */ 
+  {
+    return (aic7xxx_set_info(buffer, length, HBAptr));
+  }
+
+  p = (struct aic7xxx_host *) HBAptr->hostdata;
+
+  /*
+   * It takes roughly 1K of space to hold all relevant card info, not
+   * counting any proc stats, so we start out with a 1.5k buffer size and
+   * if proc_stats is defined, then we sweep the stats structure to see
+   * how many drives we will be printing out for and add 384 bytes per
+   * device with active stats.
+   *
+   * Hmmmm...that 1.5k seems to keep growing as items get added so they
+   * can be easily viewed for debugging purposes.  So, we bumped that
+   * 1.5k to 4k so we can quit having to bump it all the time.
+   */
+
+  size = 4096;
+  for (target = 0; target < MAX_TARGETS; target++)
+  {
+    if (p->dev_flags[target] & DEVICE_PRESENT)
+#ifdef AIC7XXX_PROC_STATS
+      size += 512;
+#else
+      size += 256;
+#endif
+  }
+  if (aic7xxx_buffer_size != size)
+  {
+    if (aic7xxx_buffer != NULL) 
+    {
+      kfree(aic7xxx_buffer);
+      aic7xxx_buffer_size = 0;
+    }
+    aic7xxx_buffer = kmalloc(size, GFP_KERNEL);
+  }
+  if (aic7xxx_buffer == NULL)
+  {
+    size = sprintf(buffer, "AIC7xxx - kmalloc error at line %d\n",
+        __LINE__);
+    return size;
+  }
+  aic7xxx_buffer_size = size;
+
+  size = 0;
+  size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
+  size += sprintf(BLS, "%s/", AIC7XXX_C_VERSION);
+  size += sprintf(BLS, "%s", AIC7XXX_H_VERSION);
+  size += sprintf(BLS, "\n");
+  size += sprintf(BLS, "Compile Options:\n");
+#ifdef CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT
+  size += sprintf(BLS, "  TCQ Enabled By Default : Enabled\n");
+#else
+  size += sprintf(BLS, "  TCQ Enabled By Default : Disabled\n");
+#endif
+#ifdef AIC7XXX_PROC_STATS
+  size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Enabled\n");
+#else
+  size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Disabled\n");
+#endif
+  size += sprintf(BLS, "\n");
+  size += sprintf(BLS, "Adapter Configuration:\n");
+  size += sprintf(BLS, "           SCSI Adapter: %s\n",
+      board_names[p->board_name_index]);
+  if (p->flags & AHC_TWIN)
+    size += sprintf(BLS, "                         Twin Channel Controller ");
+  else
+  {
+    char *channel = "";
+    char *ultra = "";
+    char *wide = "Narrow ";
+    if (p->flags & AHC_MULTI_CHANNEL)
+    {
+      channel = " Channel A";
+      if (p->flags & (AHC_CHNLB|AHC_CHNLC))
+        channel = (p->flags & AHC_CHNLB) ? " Channel B" : " Channel C";
+    }
+    if (p->features & AHC_WIDE)
+      wide = "Wide ";
+    if (p->features & AHC_ULTRA3)
+    {
+      switch(p->chip & AHC_CHIPID_MASK)
+      {
+        case AHC_AIC7892:
+        case AHC_AIC7899:
+          ultra = "Ultra-160/m LVD/SE ";
+          break;
+        default:
+          ultra = "Ultra-3 LVD/SE ";
+          break;
+      }
+    }
+    else if (p->features & AHC_ULTRA2)
+      ultra = "Ultra-2 LVD/SE ";
+    else if (p->features & AHC_ULTRA)
+      ultra = "Ultra ";
+    size += sprintf(BLS, "                           %s%sController%s ",
+      ultra, wide, channel);
+  }
+  switch(p->chip & ~AHC_CHIPID_MASK)
+  {
+    case AHC_VL:
+      size += sprintf(BLS, "at VLB slot %d\n", p->pci_device_fn);
+      break;
+    case AHC_EISA:
+      size += sprintf(BLS, "at EISA slot %d\n", p->pci_device_fn);
+      break;
+    default:
+      size += sprintf(BLS, "at PCI %d/%d/%d\n", p->pci_bus,
+        PCI_SLOT(p->pci_device_fn), PCI_FUNC(p->pci_device_fn));
+      break;
+  }
+  if( !(p->maddr) )
+  {
+    size += sprintf(BLS, "    Programmed I/O Base: %lx\n", p->base);
+  }
+  else
+  {
+    size += sprintf(BLS, "    PCI MMAPed I/O Base: 0x%lx\n", p->mbase);
+  }
+  if( (p->chip & (AHC_VL | AHC_EISA)) )
+  {
+    size += sprintf(BLS, "    BIOS Memory Address: 0x%08x\n", p->bios_address);
+  }
+  size += sprintf(BLS, " Adapter SEEPROM Config: %s\n",
+          (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." :
+         ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." :
+           "SEEPROM not found, using leftover BIOS values.") );
+  size += sprintf(BLS, "      Adaptec SCSI BIOS: %s\n",
+          (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
+  size += sprintf(BLS, "                    IRQ: %d\n", HBAptr->irq);
+  size += sprintf(BLS, "                   SCBs: Active %d, Max Active %d,\n",
+            p->activescbs, p->max_activescbs);
+  size += sprintf(BLS, "                         Allocated %d, HW %d, "
+            "Page %d\n", p->scb_data->numscbs, p->scb_data->maxhscbs,
+            p->scb_data->maxscbs);
+  if (p->flags & AHC_EXTERNAL_SRAM)
+    size += sprintf(BLS, "                         Using External SCB SRAM\n");
+  size += sprintf(BLS, "             Interrupts: %ld", p->isr_count);
+  if (p->chip & AHC_EISA)
+  {
+    size += sprintf(BLS, " %s\n",
+        (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
+  }
+  else
+  {
+    size += sprintf(BLS, "\n");
+  }
+  size += sprintf(BLS, "      BIOS Control Word: 0x%04x\n",
+            p->bios_control);
+  size += sprintf(BLS, "   Adapter Control Word: 0x%04x\n",
+            p->adapter_control);
+  size += sprintf(BLS, "   Extended Translation: %sabled\n",
+      (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis");
+  size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable);
+  if (p->features & (AHC_ULTRA | AHC_ULTRA2))
+  {
+    size += sprintf(BLS, "     Ultra Enable Flags: 0x%04x\n", p->ultraenb);
+  }
+  size += sprintf(BLS, " Tag Queue Enable Flags: 0x%04x\n", p->tagenable);
+  size += sprintf(BLS, "Ordered Queue Tag Flags: 0x%04x\n", p->orderedtag);
+  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_DEVICE);
+  size += sprintf(BLS, "    Tagged Queue By Device array for aic7xxx host "
+                       "instance %d:\n", p->instance);
+  size += sprintf(BLS, "      {");
+  for(i=0; i < (MAX_TARGETS - 1); i++)
+    size += sprintf(BLS, "%d,",aic7xxx_tag_info[p->instance].tag_commands[i]);
+  size += sprintf(BLS, "%d}\n",aic7xxx_tag_info[p->instance].tag_commands[i]);
+  size += sprintf(BLS, "    Actual queue depth per device for aic7xxx host "
+                       "instance %d:\n", p->instance);
+  size += sprintf(BLS, "      {");
+  for(i=0; i < (MAX_TARGETS - 1); i++)
+    size += sprintf(BLS, "%d,", p->dev_max_queue_depth[i]);
+  size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]);
+
+  size += sprintf(BLS, "\n");
+  size += sprintf(BLS, "Statistics:\n\n");
+  for (target = 0; target < MAX_TARGETS; target++)
+  {
+    sp = &p->stats[target];
+    if ((p->dev_flags[target] & DEVICE_PRESENT) == 0)
+    {
+      continue;
+    }
+    if (p->features & AHC_TWIN)
+    {
+      size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+          p->host_no, (target >> 3), (target & 0x7), 0);
+    }
+    else
+    {
+      size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
+          p->host_no, 0, target, 0);
+    }
+    size += sprintf(BLS, "  Device using %s/%s",
+          (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ?
+          "Wide" : "Narrow",
+          (p->transinfo[target].cur_offset != 0) ?
+          "Sync transfers at " : "Async transfers.\n" );
+    if (p->transinfo[target].cur_offset != 0)
+    {
+      struct aic7xxx_syncrate *sync_rate;
+      unsigned char options = p->transinfo[target].cur_options;
+      int period = p->transinfo[target].cur_period;
+      int rate = (p->transinfo[target].cur_width ==
+                  MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
+
+      sync_rate = aic7xxx_find_syncrate(p, &period, 0, &options);
+      if (sync_rate != NULL)
+      {
+        size += sprintf(BLS, "%s MByte/sec, offset %d\n",
+                        sync_rate->rate[rate],
+                        p->transinfo[target].cur_offset );
+      }
+      else
+      {
+        size += sprintf(BLS, "3.3 MByte/sec, offset %d\n",
+                        p->transinfo[target].cur_offset );
+      }
+    }
+    size += sprintf(BLS, "  Transinfo settings: ");
+    size += sprintf(BLS, "current(%d/%d/%d/%d), ",
+                    p->transinfo[target].cur_period,
+                    p->transinfo[target].cur_offset,
+                    p->transinfo[target].cur_width,
+                    p->transinfo[target].cur_options);
+    size += sprintf(BLS, "goal(%d/%d/%d/%d), ",
+                    p->transinfo[target].goal_period,
+                    p->transinfo[target].goal_offset,
+                    p->transinfo[target].goal_width,
+                    p->transinfo[target].goal_options);
+    size += sprintf(BLS, "user(%d/%d/%d/%d)\n",
+                    p->transinfo[target].user_period,
+                    p->transinfo[target].user_offset,
+                    p->transinfo[target].user_width,
+                    p->transinfo[target].user_options);
+#ifdef AIC7XXX_PROC_STATS
+    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
+        sp->r_total + sp->w_total, sp->r_total, sp->w_total);
+    size += sprintf(BLS, "%s\n", HDRB);
+    size += sprintf(BLS, "   Reads:");
+    for (i = 0; i < NUMBER(sp->r_bins); i++)
+    {
+      size += sprintf(BLS, " %7ld", sp->r_bins[i]);
+    }
+    size += sprintf(BLS, "\n");
+    size += sprintf(BLS, "  Writes:");
+    for (i = 0; i < NUMBER(sp->w_bins); i++)
+    {
+      size += sprintf(BLS, " %7ld", sp->w_bins[i]);
+    }
+    size += sprintf(BLS, "\n");
+#else
+    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
+        sp->r_total + sp->w_total, sp->r_total, sp->w_total);
+#endif /* AIC7XXX_PROC_STATS */
+    size += sprintf(BLS, "\n\n");
+  }
+
+  if (size >= aic7xxx_buffer_size)
+  {
+    printk(KERN_WARNING "aic7xxx: Overflow in aic7xxx_proc.c\n");
+  }
+
+  if (offset > size - 1)
+  {
+    kfree(aic7xxx_buffer);
+    aic7xxx_buffer = NULL;
+    aic7xxx_buffer_size = length = 0;
+    *start = NULL;
+  }
+  else
+  {
+    *start = buffer;
+    length = MIN(length, size - offset);
+    memcpy(buffer, &aic7xxx_buffer[offset], length);
+  }
+
+  return (length);
+}
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 2
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -2
+ * c-argdecl-indent: 2
+ * c-label-offset: -2
+ * c-continued-statement-offset: 2
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/aic7xxx_reg.h linux/drivers/scsi/aic7xxx_old/aic7xxx_reg.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/aic7xxx_reg.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_old/aic7xxx_reg.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,629 @@
+/*
+  * DO NOT EDIT - This file is automatically generated.
+  */
+
+#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_ULTRA2     	0x7f
+#define		SXFR            	0x70
+#define		SOFS            	0x0f
+
+#define	SCSIID          		0x05
+#define	SCSIOFFSET      		0x05
+#define		SOFS_ULTRA2     	0x7f
+
+#define	SCSIDATL        		0x06
+
+#define	SCSIDATH        		0x07
+
+#define	STCNT           		0x08
+
+#define	OPTIONMODE      		0x08
+#define		AUTORATEEN      	0x80
+#define		AUTOACKEN       	0x40
+#define		ATNMGMNTEN      	0x20
+#define		BUSFREEREV      	0x10
+#define		EXPPHASEDIS     	0x08
+#define		SCSIDATL_IMGEN  	0x04
+#define		AUTO_MSGOUT_DE  	0x02
+#define		DIS_MSGIN_DUALEDGE	0x01
+
+#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		IOERR           	0x08
+#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		SHVALID         	0x40
+#define		WIDE_RES        	0x20
+#define		SFCNT           	0x1f
+#define		EXP_ACTIVE      	0x10
+#define		CRCVALERR       	0x08
+#define		CRCENDERR       	0x04
+#define		CRCREQERR       	0x02
+#define		DUAL_EDGE_ERROR 	0x01
+
+#define	SSTAT3          		0x0e
+#define		SCSICNT         	0xf0
+#define		OFFCNT          	0x0f
+
+#define	SCSIID_ULTRA2   		0x0f
+#define		OID             	0x0f
+
+#define	SIMODE0         		0x10
+#define		ENSELDO         	0x40
+#define		ENSELDI         	0x20
+#define		ENSELINGO       	0x10
+#define		ENIOERR         	0x08
+#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	SPIOCAP         		0x1b
+#define		SOFT1           	0x80
+#define		SOFT0           	0x40
+#define		SOFTCMDEN       	0x20
+#define		HAS_BRDCTL      	0x10
+#define		SEEPROM         	0x08
+#define		EEPROM          	0x04
+#define		ROM             	0x02
+#define		SSPIOCPS        	0x01
+
+#define	BRDCTL          		0x1d
+#define		BRDDAT7         	0x80
+#define		BRDDAT6         	0x40
+#define		BRDDAT5         	0x20
+#define		BRDDAT4         	0x10
+#define		BRDSTB          	0x10
+#define		BRDCS           	0x08
+#define		BRDDAT3         	0x08
+#define		BRDDAT2         	0x04
+#define		BRDRW           	0x04
+#define		BRDRW_ULTRA2    	0x02
+#define		BRDCTL1         	0x02
+#define		BRDCTL0         	0x01
+#define		BRDSTB_ULTRA2   	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		ENAB40          	0x08
+#define		ENAB20          	0x04
+#define		SELWIDE         	0x02
+#define		XCVR            	0x01
+
+#define	SRAM_BASE       		0x20
+
+#define	TARG_SCSIRATE   		0x20
+
+#define	ULTRA_ENB       		0x30
+
+#define	DISC_DSB        		0x32
+
+#define	MSG_OUT         		0x34
+
+#define	DMAPARAMS       		0x35
+#define		PRELOADEN       	0x80
+#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	SEQ_FLAGS       		0x36
+#define		IDENTIFY_SEEN   	0x80
+#define		SCBPTR_VALID    	0x20
+#define		DPHASE          	0x10
+#define		AMTARGET        	0x08
+#define		WIDE_BUS        	0x02
+#define		TWIN_BUS        	0x01
+
+#define	SAVED_TCL       		0x37
+
+#define	SG_COUNT        		0x38
+
+#define	SG_NEXT         		0x39
+
+#define	LASTPHASE       		0x3d
+#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	WAITING_SCBH    		0x3e
+
+#define	DISCONNECTED_SCBH		0x3f
+
+#define	FREE_SCBH       		0x40
+
+#define	HSCB_ADDR       		0x41
+
+#define	SCBID_ADDR      		0x45
+
+#define	TMODE_CMDADDR   		0x49
+
+#define	KERNEL_QINPOS   		0x4d
+
+#define	QINPOS          		0x4e
+
+#define	QOUTPOS         		0x4f
+
+#define	TMODE_CMDADDR_NEXT		0x50
+
+#define	ARG_1           		0x51
+#define	RETURN_1        		0x51
+#define		SEND_MSG        	0x80
+#define		SEND_SENSE      	0x40
+#define		SEND_REJ        	0x20
+#define		MSGOUT_PHASEMIS 	0x10
+
+#define	ARG_2           		0x52
+#define	RETURN_2        		0x52
+
+#define	LAST_MSG        		0x53
+
+#define	PREFETCH_CNT    		0x54
+
+#define	SCSICONF        		0x5a
+#define		TERM_ENB        	0x80
+#define		RESET_SCSI      	0x40
+#define		HWSCSIID        	0x0f
+#define		HSCSIID         	0x07
+
+#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	TARG_OFFSET     		0x70
+
+#define	BCTL            		0x84
+#define		ACE             	0x08
+#define		ENABLE          	0x01
+
+#define	DSCOMMAND0      		0x84
+#define		INTSCBRAMSEL    	0x08
+#define		RAMPS           	0x04
+#define		USCBSIZE32      	0x02
+#define		CIOPARCKEN      	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         	0xc0
+#define		STBOFF          	0x38
+#define		STBON           	0x07
+
+#define	DSPCISTATUS     		0x86
+#define		DFTHRSH_100     	0xc0
+
+#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		TRACEPOINT2     	0xc1
+#define		TRACEPOINT      	0xb1
+#define		AWAITING_MSG    	0xa1
+#define		RESIDUAL        	0x81
+#define		BAD_STATUS      	0x71
+#define		REJECT_MSG      	0x61
+#define		WIDE_RESIDUE    	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		CLRPARERR       	0x10
+#define		CLRBRKADRINT    	0x08
+#define		CLRSCSIINT      	0x04
+#define		CLRCMDINT       	0x02
+#define		CLRSEQINT       	0x01
+
+#define	ERROR           		0x92
+#define		CIOPARERR       	0x80
+#define		PCIERRSTAT      	0x40
+#define		MPARERR         	0x20
+#define		DPARERR         	0x10
+#define		SQPARERR        	0x08
+#define		ILLOPCODE       	0x04
+#define		DSCTMOUT        	0x02
+#define		ILLSADDR        	0x02
+#define		ILLHADDR        	0x01
+
+#define	DFCNTRL         		0x93
+
+#define	DFSTATUS        		0x94
+#define		PRELOAD_AVAIL   	0x80
+#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	SCSIDATL_IMG    		0x9c
+
+#define	QOUTFIFO        		0x9d
+
+#define	CRCCONTROL1     		0x9d
+#define		CRCONSEEN       	0x80
+#define		CRCVALCHKEN     	0x40
+#define		CRCENDCHKEN     	0x20
+#define		CRCREQCHKEN     	0x10
+#define		TARGCRCENDEN    	0x08
+#define		TARGCRCCNTEN    	0x04
+
+#define	QOUTCNT         		0x9e
+
+#define	SCSIPHASE       		0x9e
+#define		SP_STATUS       	0x20
+#define		SP_COMMAND      	0x10
+#define		SP_MSG_IN       	0x08
+#define		SP_MSG_OUT      	0x04
+#define		SP_DATA_IN      	0x02
+#define		SP_DATA_OUT     	0x01
+
+#define	SFUNCT          		0x9f
+#define		ALT_MODE        	0x80
+
+#define	SCB_CONTROL     		0xa0
+#define		MK_MESSAGE      	0x80
+#define		DISCENB         	0x40
+#define		TAG_ENB         	0x20
+#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_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	CCHADDR         		0xe0
+
+#define	CCHCNT          		0xe8
+
+#define	CCSGRAM         		0xe9
+
+#define	CCSGADDR        		0xea
+
+#define	CCSGCTL         		0xeb
+#define		CCSGDONE        	0x80
+#define		CCSGEN          	0x08
+#define		FLAG            	0x02
+#define		CCSGRESET       	0x01
+
+#define	CCSCBRAM        		0xec
+
+#define	CCSCBADDR       		0xed
+
+#define	CCSCBCTL        		0xee
+#define		CCSCBDONE       	0x80
+#define		ARRDONE         	0x40
+#define		CCARREN         	0x10
+#define		CCSCBEN         	0x08
+#define		CCSCBDIR        	0x04
+#define		CCSCBRESET      	0x01
+
+#define	CCSCBCNT        		0xef
+
+#define	CCSCBPTR        		0xf1
+
+#define	HNSCB_QOFF      		0xf4
+
+#define	HESCB_QOFF      		0xf5
+
+#define	SNSCB_QOFF      		0xf6
+
+#define	SESCB_QOFF      		0xf7
+
+#define	SDSCB_QOFF      		0xf8
+
+#define	QOFF_CTLSTA     		0xfa
+#define		ESTABLISH_SCB_AVAIL	0x80
+#define		SCB_AVAIL       	0x40
+#define		SNSCB_ROLLOVER  	0x20
+#define		SDSCB_ROLLOVER  	0x10
+#define		SESCB_ROLLOVER  	0x08
+#define		SCB_QSIZE       	0x07
+#define		SCB_QSIZE_256   	0x06
+
+#define	DFF_THRSH       		0xfb
+#define		WR_DFTHRSH      	0x70
+#define		WR_DFTHRSH_MAX  	0x70
+#define		WR_DFTHRSH_90   	0x60
+#define		WR_DFTHRSH_85   	0x50
+#define		WR_DFTHRSH_75   	0x40
+#define		WR_DFTHRSH_63   	0x30
+#define		WR_DFTHRSH_50   	0x20
+#define		WR_DFTHRSH_25   	0x10
+#define		RD_DFTHRSH_MAX  	0x07
+#define		RD_DFTHRSH      	0x07
+#define		RD_DFTHRSH_90   	0x06
+#define		RD_DFTHRSH_85   	0x05
+#define		RD_DFTHRSH_75   	0x04
+#define		RD_DFTHRSH_63   	0x03
+#define		RD_DFTHRSH_50   	0x02
+#define		RD_DFTHRSH_25   	0x01
+#define		RD_DFTHRSH_MIN  	0x00
+#define		WR_DFTHRSH_MIN  	0x00
+
+#define	SG_CACHEPTR     		0xfc
+#define		SG_USER_DATA    	0xfc
+#define		LAST_SEG        	0x02
+#define		LAST_SEG_DONE   	0x01
+
+
+#define	CMD_GROUP_CODE_SHIFT	0x05
+#define	BUS_8_BIT	0x00
+#define	QOUTFIFO_OFFSET	0x01
+#define	CCSGRAM_MAXSEGS	0x10
+#define	CMD_GROUP2_BYTE_DELTA	0xfa
+#define	MAX_OFFSET_8BIT	0x0f
+#define	BUS_16_BIT	0x01
+#define	QINFIFO_OFFSET	0x02
+#define	CMD_GROUP5_BYTE_DELTA	0x0b
+#define	MAX_OFFSET_ULTRA2	0x7f
+#define	MAX_OFFSET_16BIT	0x08
+#define	UNTAGGEDSCB_OFFSET	0x00
+#define	SCB_LIST_NULL	0xff
+#define	SG_SIZEOF	0x08
+#define	CMD_GROUP4_BYTE_DELTA	0x04
+#define	CMD_GROUP0_BYTE_DELTA	0xfc
+#define	HOST_MSG	0xff
+#define	BUS_32_BIT	0x02
+#define	CCSGADDR_MAX	0x80
+
+
+/* Downloaded Constant Definitions */
+#define	TMODE_NUMCMDS	0x00
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/aic7xxx_seq.c linux/drivers/scsi/aic7xxx_old/aic7xxx_seq.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/aic7xxx_seq.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_old/aic7xxx_seq.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,745 @@
+/*
+  * DO NOT EDIT - This file is automatically generated.
+  */
+static unsigned char seqprog[] = {
+	0xff, 0x6a, 0x06, 0x08,
+	0x7f, 0x02, 0x04, 0x08,
+	0x12, 0x6a, 0x00, 0x00,
+	0xff, 0x6a, 0xd6, 0x09,
+	0xff, 0x6a, 0xdc, 0x09,
+	0x00, 0x65, 0xca, 0x58,
+	0xf7, 0x01, 0x02, 0x08,
+	0xff, 0x4e, 0xc8, 0x08,
+	0xbf, 0x60, 0xc0, 0x08,
+	0x60, 0x0b, 0x86, 0x68,
+	0x40, 0x00, 0x0c, 0x68,
+	0x08, 0x1f, 0x3e, 0x10,
+	0x60, 0x0b, 0x86, 0x68,
+	0x40, 0x00, 0x0c, 0x68,
+	0x08, 0x1f, 0x3e, 0x10,
+	0xff, 0x3e, 0x48, 0x60,
+	0x40, 0xfa, 0x10, 0x78,
+	0xff, 0xf6, 0xd4, 0x08,
+	0x01, 0x4e, 0x9c, 0x18,
+	0x40, 0x60, 0xc0, 0x00,
+	0x00, 0x4d, 0x10, 0x70,
+	0x01, 0x4e, 0x9c, 0x18,
+	0xbf, 0x60, 0xc0, 0x08,
+	0x00, 0x6a, 0x3e, 0x5c,
+	0xff, 0x4e, 0xc8, 0x18,
+	0x02, 0x6a, 0x54, 0x5b,
+	0xff, 0x52, 0x20, 0x09,
+	0x0d, 0x6a, 0x6a, 0x00,
+	0x00, 0x52, 0xca, 0x5b,
+	0x03, 0xb0, 0x52, 0x31,
+	0xff, 0xb0, 0x52, 0x09,
+	0xff, 0xb1, 0x54, 0x09,
+	0xff, 0xb2, 0x56, 0x09,
+	0xff, 0xa3, 0x50, 0x09,
+	0xff, 0x3e, 0x74, 0x09,
+	0xff, 0x90, 0x7c, 0x08,
+	0xff, 0x3e, 0x20, 0x09,
+	0x00, 0x65, 0x4e, 0x58,
+	0x00, 0x65, 0x0c, 0x40,
+	0xf7, 0x1f, 0xca, 0x08,
+	0x08, 0xa1, 0xc8, 0x08,
+	0x00, 0x65, 0xca, 0x00,
+	0xff, 0x65, 0x3e, 0x08,
+	0xf0, 0xa1, 0xc8, 0x08,
+	0x0f, 0x0f, 0x1e, 0x08,
+	0x00, 0x0f, 0x1e, 0x00,
+	0xf0, 0xa1, 0xc8, 0x08,
+	0x0f, 0x05, 0x0a, 0x08,
+	0x00, 0x05, 0x0a, 0x00,
+	0xff, 0x6a, 0x0c, 0x08,
+	0x5a, 0x6a, 0x00, 0x04,
+	0x12, 0x65, 0x02, 0x00,
+	0x31, 0x6a, 0xca, 0x00,
+	0x80, 0x37, 0x6e, 0x68,
+	0xff, 0x65, 0xca, 0x18,
+	0xff, 0x37, 0xdc, 0x08,
+	0xff, 0x6e, 0xc8, 0x08,
+	0x00, 0x6c, 0x76, 0x78,
+	0x20, 0x01, 0x02, 0x00,
+	0x4c, 0x37, 0xc8, 0x28,
+	0x08, 0x1f, 0x7e, 0x78,
+	0x08, 0x37, 0x6e, 0x00,
+	0x08, 0x64, 0xc8, 0x00,
+	0x70, 0x64, 0xca, 0x18,
+	0xff, 0x6c, 0x0a, 0x08,
+	0x20, 0x64, 0xca, 0x18,
+	0xff, 0x6c, 0x08, 0x0c,
+	0x40, 0x0b, 0x96, 0x68,
+	0x20, 0x6a, 0x16, 0x00,
+	0xf0, 0x19, 0x6e, 0x08,
+	0x08, 0x6a, 0x18, 0x00,
+	0x08, 0x11, 0x22, 0x00,
+	0x08, 0x6a, 0x66, 0x58,
+	0x08, 0x6a, 0x68, 0x00,
+	0x00, 0x65, 0xaa, 0x40,
+	0x12, 0x6a, 0x00, 0x00,
+	0x40, 0x6a, 0x16, 0x00,
+	0xff, 0x3e, 0x20, 0x09,
+	0xff, 0xba, 0x7c, 0x08,
+	0xff, 0xa1, 0x6e, 0x08,
+	0x08, 0x6a, 0x18, 0x00,
+	0x08, 0x11, 0x22, 0x00,
+	0x08, 0x6a, 0x66, 0x58,
+	0x80, 0x6a, 0x68, 0x00,
+	0x80, 0x36, 0x6c, 0x00,
+	0x00, 0x65, 0x9e, 0x5b,
+	0xff, 0x3d, 0xc8, 0x08,
+	0xbf, 0x64, 0xe2, 0x78,
+	0x80, 0x64, 0xac, 0x71,
+	0xa0, 0x64, 0xdc, 0x71,
+	0xc0, 0x64, 0xd4, 0x71,
+	0xe0, 0x64, 0x1c, 0x72,
+	0x01, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0xaa, 0x40,
+	0xf7, 0x11, 0x22, 0x08,
+	0x00, 0x65, 0xca, 0x58,
+	0xff, 0x06, 0xd4, 0x08,
+	0xf7, 0x01, 0x02, 0x08,
+	0x09, 0x0c, 0xc4, 0x78,
+	0x08, 0x0c, 0x0c, 0x68,
+	0x01, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0x26, 0x09,
+	0x02, 0x6a, 0x08, 0x30,
+	0xff, 0x6a, 0x08, 0x08,
+	0xdf, 0x01, 0x02, 0x08,
+	0x01, 0x6a, 0x7a, 0x00,
+	0xff, 0x6a, 0x6c, 0x0c,
+	0x04, 0x14, 0x10, 0x31,
+	0x03, 0xa9, 0x18, 0x31,
+	0x03, 0xa9, 0x10, 0x30,
+	0x08, 0x6a, 0xcc, 0x00,
+	0xa9, 0x6a, 0xb4, 0x5b,
+	0x00, 0x65, 0x02, 0x41,
+	0xa8, 0x6a, 0x6a, 0x00,
+	0x79, 0x6a, 0x6a, 0x00,
+	0x40, 0x3d, 0xea, 0x68,
+	0x04, 0x35, 0x6a, 0x00,
+	0x00, 0x65, 0x0e, 0x5b,
+	0x80, 0x6a, 0xd4, 0x01,
+	0x10, 0x36, 0xd6, 0x68,
+	0x10, 0x36, 0x6c, 0x00,
+	0x07, 0xac, 0x10, 0x31,
+	0x03, 0x8c, 0x10, 0x30,
+	0x05, 0xa3, 0x70, 0x30,
+	0x88, 0x6a, 0xcc, 0x00,
+	0xac, 0x6a, 0xac, 0x5b,
+	0x00, 0x65, 0xa6, 0x5b,
+	0x38, 0x6a, 0xcc, 0x00,
+	0xa3, 0x6a, 0xb0, 0x5b,
+	0xff, 0x38, 0x12, 0x69,
+	0x80, 0x02, 0x04, 0x00,
+	0xe7, 0x35, 0x6a, 0x08,
+	0x03, 0x69, 0x18, 0x31,
+	0x03, 0x69, 0x10, 0x30,
+	0xff, 0x6a, 0x10, 0x00,
+	0xff, 0x6a, 0x12, 0x00,
+	0xff, 0x6a, 0x14, 0x00,
+	0x01, 0x38, 0x18, 0x61,
+	0xbf, 0x35, 0x6a, 0x08,
+	0x02, 0x6a, 0xf8, 0x01,
+	0xff, 0x69, 0xca, 0x08,
+	0xff, 0x35, 0x26, 0x09,
+	0x04, 0x0b, 0x1c, 0x69,
+	0x04, 0x0b, 0x28, 0x69,
+	0x10, 0x0c, 0x1e, 0x79,
+	0x04, 0x0b, 0x28, 0x69,
+	0xff, 0x6a, 0xca, 0x08,
+	0x00, 0x35, 0xee, 0x5a,
+	0x80, 0x02, 0x7c, 0x69,
+	0xff, 0x65, 0x6c, 0x79,
+	0xff, 0x38, 0x70, 0x18,
+	0xff, 0x38, 0x6c, 0x79,
+	0x80, 0xea, 0x48, 0x61,
+	0xef, 0x38, 0xc8, 0x18,
+	0x80, 0x6a, 0xc8, 0x00,
+	0x00, 0x65, 0x3a, 0x49,
+	0x33, 0x38, 0xc8, 0x28,
+	0xff, 0x64, 0xd0, 0x09,
+	0x04, 0x39, 0xc0, 0x31,
+	0x09, 0x6a, 0xd6, 0x01,
+	0x80, 0xeb, 0x40, 0x79,
+	0xf7, 0xeb, 0xd6, 0x09,
+	0x08, 0xeb, 0x44, 0x69,
+	0x01, 0x6a, 0xd6, 0x01,
+	0x08, 0xe9, 0x10, 0x31,
+	0x03, 0x8c, 0x10, 0x30,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x39, 0x6a, 0xb2, 0x5b,
+	0x08, 0x6a, 0x18, 0x01,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x0d, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x30, 0x5c,
+	0x88, 0x6a, 0x20, 0x5c,
+	0x00, 0x65, 0xa6, 0x5b,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x08, 0x39, 0x72, 0x18,
+	0x00, 0x3a, 0x74, 0x20,
+	0x01, 0x0c, 0x64, 0x79,
+	0x10, 0x0c, 0x02, 0x79,
+	0xff, 0x35, 0x26, 0x09,
+	0x04, 0x0b, 0x6a, 0x69,
+	0x00, 0x65, 0x84, 0x59,
+	0x03, 0x08, 0x52, 0x31,
+	0xff, 0x38, 0x50, 0x09,
+	0xff, 0x08, 0x52, 0x09,
+	0xff, 0x09, 0x54, 0x09,
+	0xff, 0x0a, 0x56, 0x09,
+	0xff, 0x38, 0x50, 0x09,
+	0x00, 0x65, 0xaa, 0x40,
+	0x00, 0x65, 0x84, 0x59,
+	0x7f, 0x02, 0x04, 0x08,
+	0xe1, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0xaa, 0x40,
+	0x04, 0x93, 0x9a, 0x69,
+	0xdf, 0x93, 0x26, 0x09,
+	0x20, 0x93, 0x88, 0x69,
+	0x02, 0x93, 0x26, 0x01,
+	0x01, 0x94, 0x8a, 0x79,
+	0x01, 0x94, 0x8a, 0x79,
+	0x01, 0x94, 0x8a, 0x79,
+	0x01, 0x94, 0x8a, 0x79,
+	0x01, 0x94, 0x8a, 0x79,
+	0x01, 0x94, 0x8a, 0x79,
+	0x10, 0x94, 0x98, 0x69,
+	0x7f, 0x05, 0xa0, 0x69,
+	0x02, 0x03, 0xa0, 0x79,
+	0x11, 0x0c, 0x9c, 0x79,
+	0xd7, 0x93, 0x26, 0x09,
+	0x28, 0x93, 0xa2, 0x69,
+	0x03, 0x08, 0x52, 0x31,
+	0xff, 0x38, 0x50, 0x09,
+	0x12, 0x01, 0x02, 0x00,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x00, 0x65, 0x0e, 0x5b,
+	0x05, 0xb4, 0x10, 0x31,
+	0x02, 0x6a, 0x1a, 0x31,
+	0x03, 0x8c, 0x10, 0x30,
+	0x88, 0x6a, 0xcc, 0x00,
+	0xb4, 0x6a, 0xb0, 0x5b,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x00, 0x65, 0xa6, 0x5b,
+	0x3d, 0x6a, 0xee, 0x5a,
+	0xac, 0x6a, 0x26, 0x01,
+	0x04, 0x0b, 0xc2, 0x69,
+	0x04, 0x0b, 0xc8, 0x69,
+	0x10, 0x0c, 0xc4, 0x79,
+	0x02, 0x03, 0xcc, 0x79,
+	0x11, 0x0c, 0xc8, 0x79,
+	0xd7, 0x93, 0x26, 0x09,
+	0x28, 0x93, 0xce, 0x69,
+	0x12, 0x01, 0x02, 0x00,
+	0x00, 0x65, 0xaa, 0x40,
+	0x00, 0x65, 0x0e, 0x5b,
+	0xff, 0x06, 0x44, 0x09,
+	0x00, 0x65, 0xaa, 0x40,
+	0x10, 0x3d, 0x06, 0x00,
+	0xff, 0x34, 0xca, 0x08,
+	0x80, 0x65, 0x00, 0x62,
+	0x0f, 0xa1, 0xca, 0x08,
+	0x07, 0xa1, 0xca, 0x08,
+	0x40, 0xa0, 0xc8, 0x08,
+	0x00, 0x65, 0xca, 0x00,
+	0x80, 0x65, 0xca, 0x00,
+	0x80, 0xa0, 0xf0, 0x79,
+	0xff, 0x65, 0x0c, 0x08,
+	0x00, 0x65, 0x02, 0x42,
+	0x20, 0xa0, 0x08, 0x7a,
+	0xff, 0x65, 0x0c, 0x08,
+	0x00, 0x65, 0x9e, 0x5b,
+	0xa0, 0x3d, 0x10, 0x62,
+	0x23, 0xa0, 0x0c, 0x08,
+	0x00, 0x65, 0x9e, 0x5b,
+	0xa0, 0x3d, 0x10, 0x62,
+	0x00, 0xb9, 0x08, 0x42,
+	0xff, 0x65, 0x08, 0x62,
+	0xa1, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x10, 0x51, 0x10, 0x72,
+	0x40, 0x6a, 0x18, 0x00,
+	0xff, 0x65, 0x0c, 0x08,
+	0x00, 0x65, 0x9e, 0x5b,
+	0xa0, 0x3d, 0xda, 0x71,
+	0x40, 0x6a, 0x18, 0x00,
+	0xff, 0x34, 0xa6, 0x08,
+	0x80, 0x34, 0x18, 0x62,
+	0x7f, 0xa0, 0x40, 0x09,
+	0x08, 0x6a, 0x68, 0x00,
+	0x00, 0x65, 0xaa, 0x40,
+	0x64, 0x6a, 0xe4, 0x5a,
+	0x80, 0x64, 0x8e, 0x6a,
+	0x04, 0x64, 0x70, 0x72,
+	0x02, 0x64, 0x76, 0x72,
+	0x00, 0x6a, 0x38, 0x72,
+	0x03, 0x64, 0x8a, 0x72,
+	0x01, 0x64, 0x6c, 0x72,
+	0x07, 0x64, 0xcc, 0x72,
+	0x08, 0x64, 0x34, 0x72,
+	0x23, 0x64, 0xd0, 0x72,
+	0x11, 0x6a, 0x22, 0x01,
+	0x07, 0x6a, 0xd6, 0x5a,
+	0xff, 0x06, 0xd4, 0x08,
+	0x00, 0x65, 0xaa, 0x40,
+	0xff, 0xa8, 0x3c, 0x6a,
+	0xff, 0xa2, 0x54, 0x7a,
+	0x01, 0x6a, 0x6a, 0x00,
+	0x00, 0xb9, 0xca, 0x5b,
+	0xff, 0xa2, 0x54, 0x7a,
+	0x71, 0x6a, 0x22, 0x01,
+	0xff, 0x6a, 0xd4, 0x08,
+	0x40, 0x51, 0x54, 0x62,
+	0x0d, 0x6a, 0x6a, 0x00,
+	0x00, 0xb9, 0xca, 0x5b,
+	0xff, 0x3e, 0x74, 0x09,
+	0xff, 0x90, 0x7c, 0x08,
+	0x00, 0x65, 0x4e, 0x58,
+	0x00, 0x65, 0xbc, 0x40,
+	0x20, 0xa0, 0x5c, 0x6a,
+	0xff, 0x37, 0xc8, 0x08,
+	0x00, 0x6a, 0x74, 0x5b,
+	0xff, 0x6a, 0x8a, 0x5b,
+	0xff, 0xf8, 0xc8, 0x08,
+	0xff, 0x4f, 0xc8, 0x08,
+	0x01, 0x6a, 0x74, 0x5b,
+	0x00, 0xb9, 0x8a, 0x5b,
+	0x01, 0x4f, 0x9e, 0x18,
+	0x02, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x38, 0x5c,
+	0x00, 0x65, 0xbc, 0x40,
+	0x41, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0xaa, 0x40,
+	0x04, 0xa0, 0x40, 0x01,
+	0x00, 0x65, 0x50, 0x5c,
+	0x00, 0x65, 0xbc, 0x40,
+	0x10, 0x36, 0x34, 0x7a,
+	0x05, 0x38, 0x46, 0x31,
+	0x04, 0x14, 0x58, 0x31,
+	0x03, 0xa9, 0x60, 0x31,
+	0xa3, 0x6a, 0xcc, 0x00,
+	0x38, 0x6a, 0xb0, 0x5b,
+	0xac, 0x6a, 0xcc, 0x00,
+	0x14, 0x6a, 0xb2, 0x5b,
+	0xa9, 0x6a, 0xb4, 0x5b,
+	0x00, 0x65, 0x34, 0x42,
+	0xef, 0x36, 0x6c, 0x08,
+	0x00, 0x65, 0x34, 0x42,
+	0x0f, 0x64, 0xc8, 0x08,
+	0x07, 0x64, 0xc8, 0x08,
+	0x00, 0x37, 0x6e, 0x00,
+	0xff, 0x6a, 0xa4, 0x00,
+	0x00, 0x65, 0x44, 0x5b,
+	0xff, 0x51, 0xa0, 0x72,
+	0x20, 0x36, 0xaa, 0x7a,
+	0x00, 0x90, 0x32, 0x5b,
+	0x00, 0x65, 0xac, 0x42,
+	0xff, 0x06, 0xd4, 0x08,
+	0x00, 0x65, 0x9e, 0x5b,
+	0xe0, 0x3d, 0xc6, 0x62,
+	0x20, 0x12, 0xc6, 0x62,
+	0x51, 0x6a, 0xda, 0x5a,
+	0x00, 0x65, 0x2c, 0x5b,
+	0xff, 0x37, 0xc8, 0x08,
+	0x00, 0xa1, 0xbe, 0x62,
+	0x04, 0xa0, 0xbe, 0x7a,
+	0xfb, 0xa0, 0x40, 0x09,
+	0x80, 0x36, 0x6c, 0x00,
+	0x80, 0xa0, 0x34, 0x7a,
+	0x7f, 0xa0, 0x40, 0x09,
+	0xff, 0x6a, 0xd6, 0x5a,
+	0x00, 0x65, 0x34, 0x42,
+	0x04, 0xa0, 0xc4, 0x7a,
+	0x00, 0x65, 0x50, 0x5c,
+	0x00, 0x65, 0xc6, 0x42,
+	0x00, 0x65, 0x38, 0x5c,
+	0x31, 0x6a, 0x22, 0x01,
+	0x0c, 0x6a, 0xd6, 0x5a,
+	0x00, 0x65, 0x34, 0x42,
+	0x61, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x34, 0x42,
+	0x51, 0x6a, 0xda, 0x5a,
+	0x51, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0x34, 0x42,
+	0x10, 0x3d, 0x06, 0x00,
+	0xff, 0x65, 0x68, 0x0c,
+	0xff, 0x06, 0xd4, 0x08,
+	0x01, 0x0c, 0xdc, 0x7a,
+	0x04, 0x0c, 0xde, 0x6a,
+	0xe0, 0x03, 0x7a, 0x08,
+	0xe0, 0x3d, 0xea, 0x62,
+	0xff, 0x65, 0xcc, 0x08,
+	0xff, 0x12, 0xda, 0x0c,
+	0xff, 0x06, 0xd4, 0x0c,
+	0xd1, 0x6a, 0x22, 0x01,
+	0x00, 0x65, 0xaa, 0x40,
+	0xff, 0x65, 0x26, 0x09,
+	0x01, 0x0b, 0xfe, 0x6a,
+	0x10, 0x0c, 0xf0, 0x7a,
+	0x04, 0x0b, 0xf8, 0x6a,
+	0xff, 0x6a, 0xca, 0x08,
+	0x04, 0x93, 0xfc, 0x6a,
+	0x01, 0x94, 0xfa, 0x7a,
+	0x10, 0x94, 0xfc, 0x6a,
+	0x80, 0x3d, 0x02, 0x73,
+	0x0f, 0x04, 0x06, 0x6b,
+	0x02, 0x03, 0x06, 0x7b,
+	0x11, 0x0c, 0x02, 0x7b,
+	0xc7, 0x93, 0x26, 0x09,
+	0xff, 0x99, 0xd4, 0x08,
+	0x38, 0x93, 0x08, 0x6b,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x80, 0x36, 0x0c, 0x6b,
+	0x21, 0x6a, 0x22, 0x05,
+	0xff, 0x65, 0x20, 0x09,
+	0xff, 0x51, 0x1a, 0x63,
+	0xff, 0x37, 0xc8, 0x08,
+	0xa1, 0x6a, 0x26, 0x43,
+	0xff, 0x51, 0xc8, 0x08,
+	0xb9, 0x6a, 0x26, 0x43,
+	0xff, 0x90, 0xa4, 0x08,
+	0xff, 0xba, 0x2a, 0x73,
+	0xff, 0xba, 0x20, 0x09,
+	0xff, 0x65, 0xca, 0x18,
+	0x00, 0x6c, 0x1e, 0x63,
+	0xff, 0x90, 0xca, 0x0c,
+	0xff, 0x6a, 0xca, 0x04,
+	0x20, 0x36, 0x3e, 0x7b,
+	0x00, 0x90, 0x12, 0x5b,
+	0xff, 0x65, 0x3e, 0x73,
+	0xff, 0x52, 0x3c, 0x73,
+	0xff, 0xba, 0xcc, 0x08,
+	0xff, 0x52, 0x20, 0x09,
+	0xff, 0x66, 0x74, 0x09,
+	0xff, 0x65, 0x20, 0x0d,
+	0xff, 0xba, 0x7e, 0x0c,
+	0x00, 0x6a, 0x3e, 0x5c,
+	0x0d, 0x6a, 0x6a, 0x00,
+	0x00, 0x51, 0xca, 0x43,
+	0xff, 0x3f, 0x98, 0x73,
+	0xff, 0x6a, 0xa2, 0x00,
+	0x00, 0x3f, 0x12, 0x5b,
+	0xff, 0x65, 0x98, 0x73,
+	0x20, 0x36, 0x6c, 0x00,
+	0x20, 0xa0, 0x52, 0x6b,
+	0xff, 0xb9, 0xa2, 0x0c,
+	0xff, 0x6a, 0xa2, 0x04,
+	0xff, 0x65, 0xa4, 0x08,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x45, 0x6a, 0xbe, 0x5b,
+	0x01, 0x6a, 0xd0, 0x01,
+	0x09, 0x6a, 0xd6, 0x01,
+	0x80, 0xeb, 0x5e, 0x7b,
+	0x01, 0x6a, 0xd6, 0x01,
+	0x01, 0xe9, 0xa4, 0x34,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x45, 0x6a, 0xbe, 0x5b,
+	0x01, 0x6a, 0x18, 0x01,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x0d, 0x6a, 0x26, 0x01,
+	0x00, 0x65, 0x30, 0x5c,
+	0xff, 0x99, 0xa4, 0x0c,
+	0xff, 0x65, 0xa4, 0x08,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x45, 0x6a, 0xbe, 0x5b,
+	0x01, 0x6a, 0xd0, 0x01,
+	0x01, 0x6a, 0xdc, 0x05,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x45, 0x6a, 0xbe, 0x5b,
+	0x01, 0x6a, 0x18, 0x01,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0x01, 0x6a, 0x26, 0x05,
+	0x01, 0x65, 0xd8, 0x31,
+	0x09, 0xee, 0xdc, 0x01,
+	0x80, 0xee, 0x8e, 0x7b,
+	0xff, 0x6a, 0xdc, 0x0d,
+	0xff, 0x65, 0x32, 0x09,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x30, 0x44,
+	0xff, 0x37, 0xc8, 0x08,
+	0x00, 0x6a, 0x54, 0x5b,
+	0xff, 0x52, 0xa2, 0x0c,
+	0x01, 0x0c, 0x9e, 0x7b,
+	0x04, 0x0c, 0x9e, 0x6b,
+	0xe0, 0x03, 0x06, 0x08,
+	0xe0, 0x03, 0x7a, 0x0c,
+	0xff, 0x8c, 0x10, 0x08,
+	0xff, 0x8d, 0x12, 0x08,
+	0xff, 0x8e, 0x14, 0x0c,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x08,
+	0xff, 0x6c, 0xda, 0x0c,
+	0x3d, 0x64, 0xa4, 0x28,
+	0x55, 0x64, 0xc8, 0x28,
+	0x00, 0x6c, 0xda, 0x18,
+	0xff, 0x52, 0xc8, 0x08,
+	0x00, 0x6c, 0xda, 0x20,
+	0xff, 0x6a, 0xc8, 0x08,
+	0x00, 0x6c, 0xda, 0x20,
+	0x00, 0x6c, 0xda, 0x24,
+	0xff, 0x65, 0xc8, 0x08,
+	0xe0, 0x6a, 0xcc, 0x00,
+	0x41, 0x6a, 0xba, 0x5b,
+	0xff, 0x90, 0xe2, 0x09,
+	0x20, 0x6a, 0xd0, 0x01,
+	0x04, 0x35, 0xdc, 0x7b,
+	0x1d, 0x6a, 0xdc, 0x01,
+	0xdc, 0xee, 0xd8, 0x63,
+	0x00, 0x65, 0xe8, 0x43,
+	0x01, 0x6a, 0xdc, 0x01,
+	0x20, 0xa0, 0xd8, 0x31,
+	0x09, 0xee, 0xdc, 0x01,
+	0x80, 0xee, 0xe2, 0x7b,
+	0x19, 0x6a, 0xdc, 0x01,
+	0xd8, 0xee, 0xe6, 0x63,
+	0xff, 0x6a, 0xdc, 0x09,
+	0x18, 0xee, 0xea, 0x6b,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0x88, 0x6a, 0xcc, 0x00,
+	0x41, 0x6a, 0xba, 0x5b,
+	0x20, 0x6a, 0x18, 0x01,
+	0xff, 0x6a, 0x1a, 0x09,
+	0xff, 0x6a, 0x1c, 0x09,
+	0xff, 0x35, 0x26, 0x09,
+	0x04, 0x35, 0x14, 0x6c,
+	0xa0, 0x6a, 0xca, 0x00,
+	0x20, 0x65, 0xc8, 0x18,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0xff, 0x6c, 0x32, 0x09,
+	0x00, 0x65, 0x00, 0x64,
+	0x0a, 0x93, 0x26, 0x01,
+	0x00, 0x65, 0x30, 0x5c,
+	0x04, 0x35, 0x0c, 0x7b,
+	0xa0, 0x6a, 0x20, 0x5c,
+	0x00, 0x65, 0x22, 0x5c,
+	0x00, 0x65, 0x22, 0x5c,
+	0x00, 0x65, 0x22, 0x44,
+	0xff, 0x65, 0xcc, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x08,
+	0xff, 0x99, 0xda, 0x0c,
+	0x08, 0x94, 0x30, 0x7c,
+	0xf7, 0x93, 0x26, 0x09,
+	0x08, 0x93, 0x34, 0x6c,
+	0xff, 0x6a, 0xd4, 0x0c,
+	0xff, 0x40, 0x74, 0x09,
+	0xff, 0x90, 0x80, 0x08,
+	0xff, 0x6a, 0x72, 0x05,
+	0xff, 0x40, 0x4c, 0x64,
+	0xff, 0x3f, 0x44, 0x64,
+	0xff, 0x6a, 0xca, 0x04,
+	0xff, 0x3f, 0x20, 0x09,
+	0x01, 0x6a, 0x6a, 0x00,
+	0x00, 0xb9, 0xca, 0x5b,
+	0xff, 0xba, 0x7e, 0x0c,
+	0xff, 0x40, 0x20, 0x09,
+	0xff, 0xba, 0x80, 0x0c,
+	0xff, 0x3f, 0x74, 0x09,
+	0xff, 0x90, 0x7e, 0x0c,
+};
+
+static int aic7xxx_patch12_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch12_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_WIDE) != 0);
+}
+
+static int aic7xxx_patch11_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch11_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_ULTRA2) == 0);
+}
+
+static int aic7xxx_patch10_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch10_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_CMD_CHAN) == 0);
+}
+
+static int aic7xxx_patch9_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch9_func(struct aic7xxx_host *p)
+{
+	return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895);
+}
+
+static int aic7xxx_patch8_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch8_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_ULTRA) != 0);
+}
+
+static int aic7xxx_patch7_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch7_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_ULTRA2) != 0);
+}
+
+static int aic7xxx_patch6_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch6_func(struct aic7xxx_host *p)
+{
+	return ((p->flags & AHC_PAGESCBS) == 0);
+}
+
+static int aic7xxx_patch5_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch5_func(struct aic7xxx_host *p)
+{
+	return ((p->flags & AHC_PAGESCBS) != 0);
+}
+
+static int aic7xxx_patch4_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch4_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_QUEUE_REGS) != 0);
+}
+
+static int aic7xxx_patch3_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch3_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_TWIN) != 0);
+}
+
+static int aic7xxx_patch2_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch2_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_QUEUE_REGS) == 0);
+}
+
+static int aic7xxx_patch1_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch1_func(struct aic7xxx_host *p)
+{
+	return ((p->features & AHC_CMD_CHAN) != 0);
+}
+
+static int aic7xxx_patch0_func(struct aic7xxx_host *p);
+
+static int
+aic7xxx_patch0_func(struct aic7xxx_host *p)
+{
+	return (0);
+}
+
+struct sequencer_patch {
+	int		(*patch_func)(struct aic7xxx_host *);
+	unsigned int	begin	   :10,
+			skip_instr :10,
+			skip_patch :12;
+} sequencer_patches[] = {
+	{ aic7xxx_patch1_func, 3, 2, 1 },
+	{ aic7xxx_patch2_func, 7, 1, 1 },
+	{ aic7xxx_patch2_func, 8, 1, 1 },
+	{ aic7xxx_patch3_func, 11, 4, 1 },
+	{ aic7xxx_patch4_func, 16, 3, 2 },
+	{ aic7xxx_patch0_func, 19, 4, 1 },
+	{ aic7xxx_patch5_func, 23, 1, 1 },
+	{ aic7xxx_patch6_func, 26, 1, 1 },
+	{ aic7xxx_patch1_func, 29, 1, 2 },
+	{ aic7xxx_patch0_func, 30, 3, 1 },
+	{ aic7xxx_patch3_func, 39, 4, 1 },
+	{ aic7xxx_patch7_func, 43, 3, 2 },
+	{ aic7xxx_patch0_func, 46, 3, 1 },
+	{ aic7xxx_patch8_func, 52, 7, 1 },
+	{ aic7xxx_patch3_func, 60, 3, 1 },
+	{ aic7xxx_patch7_func, 63, 2, 1 },
+	{ aic7xxx_patch7_func, 102, 1, 2 },
+	{ aic7xxx_patch0_func, 103, 2, 1 },
+	{ aic7xxx_patch7_func, 107, 2, 1 },
+	{ aic7xxx_patch9_func, 109, 1, 1 },
+	{ aic7xxx_patch10_func, 110, 2, 1 },
+	{ aic7xxx_patch7_func, 113, 1, 2 },
+	{ aic7xxx_patch0_func, 114, 1, 1 },
+	{ aic7xxx_patch1_func, 118, 1, 1 },
+	{ aic7xxx_patch1_func, 121, 3, 2 },
+	{ aic7xxx_patch0_func, 124, 5, 1 },
+	{ aic7xxx_patch1_func, 132, 2, 3 },
+	{ aic7xxx_patch7_func, 132, 1, 1 },
+	{ aic7xxx_patch0_func, 134, 3, 1 },
+	{ aic7xxx_patch11_func, 138, 1, 2 },
+	{ aic7xxx_patch0_func, 139, 1, 1 },
+	{ aic7xxx_patch7_func, 140, 7, 2 },
+	{ aic7xxx_patch0_func, 147, 1, 1 },
+	{ aic7xxx_patch1_func, 152, 14, 3 },
+	{ aic7xxx_patch11_func, 165, 1, 1 },
+	{ aic7xxx_patch0_func, 166, 9, 1 },
+	{ aic7xxx_patch7_func, 180, 2, 1 },
+	{ aic7xxx_patch7_func, 182, 1, 1 },
+	{ aic7xxx_patch11_func, 183, 6, 3 },
+	{ aic7xxx_patch1_func, 183, 2, 2 },
+	{ aic7xxx_patch0_func, 185, 4, 1 },
+	{ aic7xxx_patch7_func, 190, 1, 1 },
+	{ aic7xxx_patch7_func, 194, 20, 1 },
+	{ aic7xxx_patch1_func, 215, 3, 3 },
+	{ aic7xxx_patch11_func, 217, 1, 1 },
+	{ aic7xxx_patch0_func, 218, 5, 1 },
+	{ aic7xxx_patch11_func, 223, 1, 2 },
+	{ aic7xxx_patch0_func, 224, 9, 1 },
+	{ aic7xxx_patch12_func, 240, 1, 2 },
+	{ aic7xxx_patch0_func, 241, 1, 1 },
+	{ aic7xxx_patch4_func, 302, 1, 2 },
+	{ aic7xxx_patch0_func, 303, 1, 1 },
+	{ aic7xxx_patch2_func, 306, 1, 1 },
+	{ aic7xxx_patch1_func, 316, 3, 2 },
+	{ aic7xxx_patch0_func, 319, 5, 1 },
+	{ aic7xxx_patch12_func, 327, 1, 2 },
+	{ aic7xxx_patch0_func, 328, 1, 1 },
+	{ aic7xxx_patch5_func, 333, 1, 1 },
+	{ aic7xxx_patch11_func, 375, 15, 1 },
+	{ aic7xxx_patch1_func, 427, 7, 2 },
+	{ aic7xxx_patch0_func, 434, 8, 1 },
+	{ aic7xxx_patch1_func, 443, 4, 2 },
+	{ aic7xxx_patch0_func, 447, 6, 1 },
+	{ aic7xxx_patch1_func, 453, 4, 2 },
+	{ aic7xxx_patch0_func, 457, 3, 1 },
+	{ aic7xxx_patch10_func, 467, 10, 1 },
+	{ aic7xxx_patch1_func, 486, 17, 4 },
+	{ aic7xxx_patch9_func, 494, 4, 2 },
+	{ aic7xxx_patch0_func, 498, 2, 1 },
+	{ aic7xxx_patch0_func, 503, 33, 1 },
+	{ aic7xxx_patch10_func, 536, 4, 1 },
+	{ aic7xxx_patch5_func, 540, 2, 1 },
+	{ aic7xxx_patch5_func, 543, 9, 1 },
+
+};
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/scsi_message.h linux/drivers/scsi/aic7xxx_old/scsi_message.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/scsi_message.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_old/scsi_message.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,49 @@
+/* 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_DISCFLAG	0x40 
+#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
+#define MSG_EXT_WDTR_BUS_8_BIT	0x00
+#define MSG_EXT_WDTR_BUS_16_BIT	0x01
+#define MSG_EXT_WDTR_BUS_32_BIT	0x02
+
+#define MSG_EXT_PPR     0x04
+#define MSG_EXT_PPR_LEN	0x06
+#define MSG_EXT_PPR_OPTION_ST 0x00
+#define MSG_EXT_PPR_OPTION_DT_CRC 0x02
+#define MSG_EXT_PPR_OPTION_DT_UNITS 0x03
+#define MSG_EXT_PPR_OPTION_DT_CRC_QUICK 0x04
+#define MSG_EXT_PPR_OPTION_DT_UNITS_QUICK 0x05
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/sequencer.h linux/drivers/scsi/aic7xxx_old/sequencer.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_old/sequencer.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_old/sequencer.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,135 @@
+/*
+ * Instruction formats for the sequencer program downloaded to
+ * Aic7xxx SCSI host adapters
+ *
+ * Copyright (c) 1997, 1998 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. 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.3 1997/09/27 19:37:31 gibbs Exp $
+ */
+
+#ifdef __LITTLE_ENDIAN_BITFIELD
+struct ins_format1 {
+	unsigned int
+			immediate	: 8,
+			source		: 9,
+			destination	: 9,
+			ret		: 1,
+			opcode		: 4,
+			parity		: 1;
+};
+
+struct ins_format2 {
+	unsigned int
+			shift_control	: 8,
+			source		: 9,
+			destination	: 9,
+			ret		: 1,
+			opcode		: 4,
+			parity		: 1;
+};
+
+struct ins_format3 {
+	unsigned int
+			immediate	: 8,
+			source		: 9,
+			address		: 10,
+			opcode		: 4,
+			parity		: 1;
+};
+#elif defined(__BIG_ENDIAN_BITFIELD)
+struct ins_format1 {
+	unsigned int
+			parity		: 1,
+			opcode		: 4,
+			ret		: 1,
+			destination	: 9,
+			source		: 9,
+			immediate	: 8;
+};
+
+struct ins_format2 {
+	unsigned int
+			parity		: 1,
+			opcode		: 4,
+			ret		: 1,
+			destination	: 9,
+			source		: 9,
+			shift_control	: 8;
+};
+
+struct ins_format3 {
+	unsigned int
+			parity		: 1,
+			opcode		: 4,
+			address		: 10,
+			source		: 9,
+			immediate	: 8;
+};
+#endif
+
+union ins_formats {
+		struct ins_format1 format1;
+		struct ins_format2 format2;
+		struct ins_format3 format3;
+		unsigned char	   bytes[4];
+		unsigned int	   integer;
+};
+struct instruction {
+	union	ins_formats format;
+	unsigned int	srcline;
+	struct symbol *patch_label;
+  struct {
+    struct instruction *stqe_next;
+  } 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_BMOV	0x6
+
+#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 -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_old.c linux/drivers/scsi/aic7xxx_old.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_old.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/aic7xxx_old.c	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,12242 @@
+/*+M*************************************************************************
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * 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.
+ *
+ * Sources include the Adaptec 1740 driver (aha1740.c), the Ultrastor 24F
+ * driver (ultrastor.c), various Linux kernel source, the Adaptec EISA
+ * config file (!adp7771.cfg), the Adaptec AHA-2740A Series User's Guide,
+ * the Linux Kernel Hacker's Guide, Writing a SCSI Device Driver for Linux,
+ * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA overlay file
+ * (adp7770.ovl), the Adaptec AHA-2740 Series Technical Reference Manual,
+ * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
+ * ANSI SCSI-2 specification (draft 10c), ...
+ *
+ * --------------------------------------------------------------------------
+ *
+ *  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 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
+ *           aic7xxx=no_reset
+ *           aic7xxx=ultra
+ *           aic7xxx=irq_trigger:[0,1]  # 0 edge, 1 level
+ *           aic7xxx=verbose
+ *
+ *  Daniel M. Eischen, deischen@iworks.InterWorks.org, 1/23/97
+ *
+ *  $Id: aic7xxx.c,v 4.1 1997/06/12 08:23:42 deang Exp $
+ *-M*************************************************************************/
+
+/*+M**************************************************************************
+ *
+ * Further driver modifications made by Doug Ledford <dledford@redhat.com>
+ *
+ * Copyright (c) 1997-1999 Doug Ledford
+ *
+ * These changes are released under the same licensing terms as the FreeBSD
+ * driver written by Justin Gibbs.  Please see his Copyright notice above
+ * for the exact terms and conditions covering my changes as well as the
+ * warranty statement.
+ *
+ * Modifications made to the aic7xxx.c,v 4.1 driver from Dan Eischen include
+ * but are not limited to:
+ *
+ *  1: Import of the latest FreeBSD sequencer code for this driver
+ *  2: Modification of kernel code to accomodate different sequencer semantics
+ *  3: Extensive changes throughout kernel portion of driver to improve
+ *     abort/reset processing and error hanndling
+ *  4: Other work contributed by various people on the Internet
+ *  5: Changes to printk information and verbosity selection code
+ *  6: General reliability related changes, especially in IRQ management
+ *  7: Modifications to the default probe/attach order for supported cards
+ *  8: SMP friendliness has been improved
+ *
+ * Overall, this driver represents a significant departure from the official
+ * aic7xxx driver released by Dan Eischen in two ways.  First, in the code
+ * itself.  A diff between the two version of the driver is now a several
+ * thousand line diff.  Second, in approach to solving the same problem.  The
+ * problem is importing the FreeBSD aic7xxx driver code to linux can be a
+ * difficult and time consuming process, that also can be error prone.  Dan
+ * Eischen's official driver uses the approach that the linux and FreeBSD
+ * drivers should be as identical as possible.  To that end, his next version
+ * of this driver will be using a mid-layer code library that he is developing
+ * to moderate communications between the linux mid-level SCSI code and the
+ * low level FreeBSD driver.  He intends to be able to essentially drop the
+ * FreeBSD driver into the linux kernel with only a few minor tweaks to some
+ * include files and the like and get things working, making for fast easy
+ * imports of the FreeBSD code into linux.
+ *
+ * I disagree with Dan's approach.  Not that I don't think his way of doing
+ * things would be nice, easy to maintain, and create a more uniform driver
+ * between FreeBSD and Linux.  I have no objection to those issues.  My
+ * disagreement is on the needed functionality.  There simply are certain
+ * things that are done differently in FreeBSD than linux that will cause
+ * problems for this driver regardless of any middle ware Dan implements.
+ * The biggest example of this at the moment is interrupt semantics.  Linux
+ * doesn't provide the same protection techniques as FreeBSD does, nor can
+ * they be easily implemented in any middle ware code since they would truly
+ * belong in the kernel proper and would effect all drivers.  For the time
+ * being, I see issues such as these as major stumbling blocks to the 
+ * reliability of code based upon such middle ware.  Therefore, I choose to
+ * use a different approach to importing the FreeBSD code that doesn't
+ * involve any middle ware type code.  My approach is to import the sequencer
+ * code from FreeBSD wholesale.  Then, to only make changes in the kernel
+ * portion of the driver as they are needed for the new sequencer semantics.
+ * In this way, the portion of the driver that speaks to the rest of the
+ * linux kernel is fairly static and can be changed/modified to solve
+ * any problems one might encounter without concern for the FreeBSD driver.
+ *
+ * Note: If time and experience should prove me wrong that the middle ware
+ * code Dan writes is reliable in its operation, then I'll retract my above
+ * statements.  But, for those that don't know, I'm from Missouri (in the US)
+ * and our state motto is "The Show-Me State".  Well, before I will put
+ * faith into it, you'll have to show me that it works :)
+ *
+ *_M*************************************************************************/
+
+/*
+ * The next three defines are user configurable.  These should be the only
+ * defines a user might need to get in here and change.  There are other
+ * defines buried deeper in the code, but those really shouldn't need touched
+ * under normal conditions.
+ */
+
+/*
+ * AIC7XXX_STRICT_PCI_SETUP
+ *   Should we assume the PCI config options on our controllers are set with
+ *   sane and proper values, or should we be anal about our PCI config
+ *   registers and force them to what we want?  The main advantage to
+ *   defining this option is on non-Intel hardware where the BIOS may not
+ *   have been run to set things up, or if you have one of the BIOSless
+ *   Adaptec controllers, such as a 2910, that don't get set up by the
+ *   BIOS.  However, keep in mind that we really do set the most important
+ *   items in the driver regardless of this setting, this only controls some
+ *   of the more esoteric PCI options on these cards.  In that sense, I
+ *   would default to leaving this off.  However, if people wish to try
+ *   things both ways, that would also help me to know if there are some
+ *   machines where it works one way but not another.
+ *
+ *   -- July 7, 17:09
+ *     OK...I need this on my machine for testing, so the default is to
+ *     leave it defined.
+ *
+ *   -- July 7, 18:49
+ *     I needed it for testing, but it didn't make any difference, so back
+ *     off she goes.
+ *
+ *   -- July 16, 23:04
+ *     I turned it back on to try and compensate for the 2.1.x PCI code
+ *     which no longer relies solely on the BIOS and now tries to set
+ *     things itself.
+ */
+
+#define AIC7XXX_STRICT_PCI_SETUP
+
+/*
+ * AIC7XXX_VERBOSE_DEBUGGING
+ *   This option enables a lot of extra printk();s in the code, surrounded
+ *   by if (aic7xxx_verbose ...) statements.  Executing all of those if
+ *   statements and the extra checks can get to where it actually does have
+ *   an impact on CPU usage and such, as well as code size.  Disabling this
+ *   define will keep some of those from becoming part of the code.
+ *
+ *   NOTE:  Currently, this option has no real effect, I will be adding the
+ *   various #ifdef's in the code later when I've decided a section is
+ *   complete and no longer needs debugging.  OK...a lot of things are now
+ *   surrounded by this define, so turning this off does have an impact.
+ */
+ 
+/*
+ * #define AIC7XXX_VERBOSE_DEBUGGING
+ */
+ 
+#if defined(MODULE) || defined(PCMCIA)
+#include <linux/module.h>
+#endif
+
+#if defined(PCMCIA)
+#  undef MODULE
+#endif
+
+#include <stdarg.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <linux/version.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/blk.h>
+#include <linux/tqueue.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/smp.h>
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "aic7xxx_old/aic7xxx.h"
+
+#include "aic7xxx_old/sequencer.h"
+#include "aic7xxx_old/scsi_message.h"
+#include "aic7xxx_old/aic7xxx_reg.h"
+#include <scsi/scsicam.h>
+
+#include <linux/stat.h>
+#include <linux/malloc.h>        /* for kmalloc() */
+
+#include <linux/config.h>        /* for CONFIG_PCI */
+
+/*
+ * To generate the correct addresses for the controller to issue
+ * on the bus.  Originally added for DEC Alpha support.
+ */
+#define VIRT_TO_BUS(a) (unsigned int)virt_to_bus((void *)(a))
+
+#define AIC7XXX_C_VERSION  "5.2.1"
+
+#define NUMBER(arr)     (sizeof(arr) / sizeof(arr[0]))
+#define MIN(a,b)        (((a) < (b)) ? (a) : (b))
+#define MAX(a,b)        (((a) > (b)) ? (a) : (b))
+#define ALL_TARGETS -1
+#define ALL_CHANNELS -1
+#define ALL_LUNS -1
+#define MAX_TARGETS  16
+#define MAX_LUNS     8
+#ifndef TRUE
+#  define TRUE 1
+#endif
+#ifndef FALSE
+#  define FALSE 0
+#endif
+
+#if defined(__powerpc__) || defined(__i386__)
+#  define MMAPIO
+#endif
+
+#  if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+#    define cpuid smp_processor_id()
+#    define DRIVER_LOCK_INIT \
+       spin_lock_init(&p->spin_lock);
+#    define DRIVER_LOCK \
+       if(!p->cpu_lock_count[cpuid]) { \
+         spin_lock_irqsave(&p->spin_lock, cpu_flags); \
+         p->cpu_lock_count[cpuid]++; \
+       } else { \
+         p->cpu_lock_count[cpuid]++; \
+       }
+#    define DRIVER_UNLOCK \
+       if(--p->cpu_lock_count[cpuid] == 0) \
+         spin_unlock_irqrestore(&p->spin_lock, cpu_flags);
+#  else
+#    define DRIVER_LOCK_INIT
+#    define DRIVER_LOCK
+#    define DRIVER_UNLOCK
+#  endif
+
+/*
+ * You can try raising me if tagged queueing is enabled, or lowering
+ * me if you only have 4 SCBs.
+ */
+#ifdef CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE
+#define AIC7XXX_CMDS_PER_DEVICE CONFIG_AIC7XXX_OLD_CMDS_PER_DEVICE
+#else
+#define AIC7XXX_CMDS_PER_DEVICE 8
+#endif
+
+/*
+ * 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.
+ */
+#ifdef CONFIG_AIC7XXX_OLD_PROC_STATS
+#define AIC7XXX_PROC_STATS
+#endif
+
+/*
+ * *** Determining commands per LUN ***
+ * 
+ * When AIC7XXX_CMDS_PER_DEVICE is not defined, the driver will use its
+ * own algorithm to determine the commands/LUN.  If SCB paging is
+ * enabled, which is always now, the default is 8 commands per lun
+ * that indicates it supports tagged queueing.  All non-tagged devices
+ * use an internal queue depth of 3, with no more than one of those
+ * three commands active at one time.
+ */
+
+typedef struct
+{
+  unsigned char tag_commands[16];   /* Allow for wide/twin adapters. */
+} adapter_tag_info_t;
+
+/*
+ * Make a define that will tell the driver not to use tagged queueing
+ * by default.
+ */
+#ifdef CONFIG_AIC7XXX_OLD_TCQ_ON_BY_DEFAULT
+#define DEFAULT_TAG_COMMANDS {0, 0, 0, 0, 0, 0, 0, 0,\
+                              0, 0, 0, 0, 0, 0, 0, 0}
+#else
+#define DEFAULT_TAG_COMMANDS {255, 255, 255, 255, 255, 255, 255, 255,\
+                              255, 255, 255, 255, 255, 255, 255, 255}
+#endif
+
+/*
+ * 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 255, the driver will
+ * not enable tagged queueing for that particular device.  When positive
+ * (> 0) and (< 255) the values in the array are used for the queue_depth.
+ * Note that the maximum value for an entry is 254, but you're insane if
+ * you try to use that many commands on one device.
+ *
+ * In this example, the first line will disable tagged queueing for all
+ * the devices on the first probed aic7xxx adapter.
+ *
+ * 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.
+ */
+
+/*
+ * NOTE: The below structure is for reference only, the actual structure
+ *       to modify in order to change things is found after this fake one.
+ *
+adapter_tag_info_t aic7xxx_tag_info[] =
+{
+  {DEFAULT_TAG_COMMANDS},
+  {{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 255, 4, 4, 4}},
+  {DEFAULT_TAG_COMMANDS},
+  {{255, 16, 4, 255, 16, 4, 4, 4, 127, 4, 4, 4, 4, 4, 4, 4}}
+};
+*/
+
+static adapter_tag_info_t aic7xxx_tag_info[] =
+{
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS},
+  {DEFAULT_TAG_COMMANDS}
+};
+
+
+/*
+ * Define an array of board names that can be indexed by aha_type.
+ * Don't forget to change this when changing the types!
+ */
+static const char *board_names[] = {
+  "AIC-7xxx Unknown",                                   /* AIC_NONE */
+  "Adaptec AIC-7810 Hardware RAID Controller",          /* AIC_7810 */
+  "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 */
+  "Adaptec AHA-2940UW Pro Ultra SCSI host adapter",     /* AIC_7887 */
+  "Adaptec AIC-7895 Ultra SCSI host adapter",           /* AIC_7895 */
+  "Adaptec AIC-7890/1 Ultra2 SCSI host adapter",        /* AIC_7890 */
+  "Adaptec AHA-293X Ultra2 SCSI host adapter",          /* AIC_7890 */
+  "Adaptec AHA-294X Ultra2 SCSI host adapter",          /* AIC_7890 */
+  "Adaptec AIC-7896/7 Ultra2 SCSI host adapter",        /* AIC_7896 */
+  "Adaptec AHA-394X Ultra2 SCSI host adapter",          /* AIC_7897 */
+  "Adaptec AHA-395X Ultra2 SCSI host adapter",          /* AIC_7897 */
+  "Adaptec PCMCIA SCSI controller",                     /* card bus stuff */
+  "Adaptec AIC-7892 Ultra 160/m SCSI host adapter",     /* AIC_7892 */
+  "Adaptec AIC-7899 Ultra 160/m SCSI host adapter",     /* AIC_7899 */
+};
+
+/*
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW   DID_ERROR
+
+/*
+ *  What we want to do is have the higher level scsi driver requeue
+ *  the command to us. There is no specific driver status for this
+ *  condition, but the higher level scsi driver will requeue the
+ *  command on a DID_BUS_BUSY error.
+ *
+ *  Upon further inspection and testing, it seems that DID_BUS_BUSY
+ *  will *always* retry the command.  We can get into an infinite loop
+ *  if this happens when we really want some sort of counter that
+ *  will automatically abort/reset the command after so many retries.
+ *  Using DID_ERROR will do just that.  (Made by a suggestion by
+ *  Doug Ledford 8/1/96)
+ */
+#define DID_RETRY_COMMAND DID_ERROR
+
+#define HSCSIID        0x07
+#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)
+ */
+#define AHC_HID0              0x80   /* 0,1: msb of ID2, 2-7: ID1      */
+#define AHC_HID1              0x81   /* 0-4: ID3, 5-7: LSB ID2         */
+#define AHC_HID2              0x82   /* product                        */
+#define AHC_HID3              0x83   /* firmware revision              */
+
+/*
+ * AIC-7770 I/O range to reserve for a card
+ */
+#define MINREG                0xC00
+#define MAXREG                0xCFF
+
+#define INTDEF                0x5C      /* Interrupt Definition Register */
+
+/*
+ * AIC-78X0 PCI registers
+ */
+#define        CLASS_PROGIF_REVID        0x08
+#define                DEVREVID        0x000000FFul
+#define                PROGINFC        0x0000FF00ul
+#define                SUBCLASS        0x00FF0000ul
+#define                BASECLASS        0xFF000000ul
+
+#define        CSIZE_LATTIME                0x0C
+#define                CACHESIZE        0x0000003Ful        /* only 5 bits */
+#define                LATTIME                0x0000FF00ul
+
+#define        DEVCONFIG                0x40
+#define                SCBSIZE32        0x00010000ul        /* aic789X only */
+#define                MPORTMODE        0x00000400ul        /* aic7870 only */
+#define                RAMPSM           0x00000200ul        /* aic7870 only */
+#define                RAMPSM_ULTRA2    0x00000004
+#define                VOLSENSE         0x00000100ul
+#define                SCBRAMSEL        0x00000080ul
+#define                SCBRAMSEL_ULTRA2 0x00000008
+#define                MRDCEN           0x00000040ul
+#define                EXTSCBTIME       0x00000020ul        /* aic7870 only */
+#define                EXTSCBPEN        0x00000010ul        /* aic7870 only */
+#define                BERREN           0x00000008ul
+#define                DACEN            0x00000004ul
+#define                STPWLEVEL        0x00000002ul
+#define                DIFACTNEGEN      0x00000001ul        /* aic7870 only */
+
+#define        SCAMCTL                  0x1a                /* Ultra2 only  */
+#define        CCSCBBADDR               0xf0                /* aic7895/6/7  */
+
+/*
+ * Define the different types of SEEPROMs on aic7xxx adapters
+ * and make it also represent the address size used in accessing
+ * its registers.  The 93C46 chips have 1024 bits organized into
+ * 64 16-bit words, while the 93C56 chips have 2048 bits organized
+ * into 128 16-bit words.  The C46 chips use 6 bits to address
+ * 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;
+
+/*
+ *
+ * Define the format of the SEEPROM registers (16 bits).
+ *
+ */
+struct seeprom_config {
+
+/*
+ * SCSI ID Configuration Flags
+ */
+#define CFXFER                0x0007      /* synchronous transfer rate */
+#define CFSYNCH               0x0008      /* enable synchronous transfer */
+#define CFDISC                0x0010      /* enable disconnection */
+#define CFWIDEB               0x0020      /* wide bus device (wide card) */
+#define CFSYNCHISULTRA        0x0040      /* CFSYNC is an ultra offset */
+#define CFNEWULTRAFORMAT      0x0080      /* Use the Ultra2 SEEPROM format */
+#define CFSTART               0x0100      /* send start unit SCSI command */
+#define CFINCBIOS             0x0200      /* include in BIOS scan */
+#define CFRNFOUND             0x0400      /* report even if not found */
+#define CFMULTILUN            0x0800      /* probe mult luns in BIOS scan */
+#define CFWBCACHEYES          0x4000      /* Enable W-Behind Cache on drive */
+#define CFWBCACHENC           0xc000      /* Don't change W-Behind Cache */
+/* UNUSED                0x3000 */
+  unsigned short device_flags[16];        /* words 0-15 */
+
+/*
+ * BIOS Control Bits
+ */
+#define CFSUPREM        0x0001  /* support all removable drives */
+#define CFSUPREMB       0x0002  /* support removable drives for boot only */
+#define CFBIOSEN        0x0004  /* BIOS enabled */
+/* UNUSED                0x0008 */
+#define CFSM2DRV        0x0010  /* support more than two drives */
+#define CF284XEXTEND    0x0020  /* extended translation (284x cards) */
+/* UNUSED                0x0040 */
+#define CFEXTEND        0x0080  /* extended translation enabled */
+/* UNUSED                0xFF00 */
+  unsigned short bios_control;  /* word 16 */
+
+/*
+ * Host Adapter Control Bits
+ */
+#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 */
+#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 boot */
+#define CFBPRIMARY      0x0100  /* Channel B primary on 7895 chipsets */
+#define CFSEAUTOTERM    0x0400  /* aic7890 Perform SE Auto Term */
+#define CFLVDSTERM      0x0800  /* aic7890 LVD Termination */
+/* UNUSED                0xF280 */
+  unsigned short adapter_control;        /* word 17 */
+
+/*
+ * Bus Release, Host Adapter ID
+ */
+#define CFSCSIID        0x000F                /* host adapter SCSI ID */
+/* UNUSED                0x00F0 */
+#define CFBRTIME        0xFF00                /* bus release time */
+  unsigned short brtime_id;                /* word 18 */
+
+/*
+ * Maximum targets
+ */
+#define CFMAXTARG        0x00FF        /* maximum targets */
+/* UNUSED                0xFF00 */
+  unsigned short max_targets;                /* word 19 */
+
+  unsigned short res_1[11];                /* words 20-30 */
+  unsigned short checksum;                /* word 31 */
+};
+
+#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
+ * to completion - it's easier that way - making a note of the error
+ * condition in this location. This then will modify a DID_OK status
+ * into an appropriate error for the higher-level SCSI code.
+ */
+#define aic7xxx_error(cmd)        ((cmd)->SCp.Status)
+
+/*
+ * Keep track of the targets returned status.
+ */
+#define aic7xxx_status(cmd)        ((cmd)->SCp.sent_command)
+
+/*
+ * The position of the SCSI commands scb within the scb array.
+ */
+#define aic7xxx_position(cmd)        ((cmd)->SCp.have_data_in)
+
+/*
+ * The stored DMA mapping for single-buffer data transfers.
+ */
+#define aic7xxx_mapping(cmd)	     ((cmd)->SCp.phase)
+
+/*
+ * So we can keep track of our host structs
+ */
+static struct aic7xxx_host *first_aic7xxx = NULL;
+
+/*
+ * 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.
+ */
+struct hw_scatterlist {
+  unsigned int address;
+  unsigned int length;
+};
+
+/*
+ * Maximum number of SG segments these cards can support.
+ */
+#define        AIC7XXX_MAX_SG 128
+
+/*
+ * 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 int  SG_list_pointer;
+/* 8*/  unsigned char residual_SG_segment_count;
+/* 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*/  unsigned char tag;          /* Index into our kernel SCB array.
+                                     * Also used as the tag for tagged I/O
+                                     */
+#define SCB_PIO_TRANSFER_SIZE  26   /* amount we need to upload/download
+                                     * via PIO to initialize a transaction.
+                                     */
+/*26*/  unsigned char next;         /* Used to thread SCBs awaiting selection
+                                     * or disconnected down in the sequencer.
+                                     */
+/*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_WAITINGQ            = 0x0002,
+        SCB_ACTIVE              = 0x0004,
+        SCB_SENSE               = 0x0008,
+        SCB_ABORT               = 0x0010,
+        SCB_DEVICE_RESET        = 0x0020,
+        SCB_RESET               = 0x0040,
+        SCB_RECOVERY_SCB        = 0x0080,
+        SCB_MSGOUT_PPR          = 0x0100,
+        SCB_MSGOUT_SENT         = 0x0200,
+        SCB_MSGOUT_SDTR         = 0x0400,
+        SCB_MSGOUT_WDTR         = 0x0800,
+        SCB_MSGOUT_BITS         = SCB_MSGOUT_PPR |
+                                  SCB_MSGOUT_SENT | 
+                                  SCB_MSGOUT_SDTR |
+                                  SCB_MSGOUT_WDTR,
+        SCB_QUEUED_ABORT        = 0x1000,
+        SCB_QUEUED_FOR_DONE     = 0x2000,
+        SCB_WAS_BUSY            = 0x4000
+} scb_flag_type;
+
+typedef enum {
+        AHC_FNONE                 = 0x00000000,
+        AHC_PAGESCBS              = 0x00000001,
+        AHC_CHANNEL_B_PRIMARY     = 0x00000002,
+        AHC_USEDEFAULTS           = 0x00000004,
+        AHC_INDIRECT_PAGING       = 0x00000008,
+        AHC_CHNLB                 = 0x00000020,
+        AHC_CHNLC                 = 0x00000040,
+        AHC_EXTEND_TRANS_A        = 0x00000100,
+        AHC_EXTEND_TRANS_B        = 0x00000200,
+        AHC_TERM_ENB_A            = 0x00000400,
+        AHC_TERM_ENB_SE_LOW       = 0x00000400,
+        AHC_TERM_ENB_B            = 0x00000800,
+        AHC_TERM_ENB_SE_HIGH      = 0x00000800,
+        AHC_HANDLING_REQINITS     = 0x00001000,
+        AHC_TARGETMODE            = 0x00002000,
+        AHC_NEWEEPROM_FMT         = 0x00004000,
+ /*
+  *  Here ends the FreeBSD defined flags and here begins the linux defined
+  *  flags.  NOTE: I did not preserve the old flag name during this change
+  *  specifically to force me to evaluate what flags were being used properly
+  *  and what flags weren't.  This way, I could clean up the flag usage on
+  *  a use by use basis.  Doug Ledford
+  */
+        AHC_MOTHERBOARD           = 0x00020000,
+        AHC_NO_STPWEN             = 0x00040000,
+        AHC_RESET_DELAY           = 0x00080000,
+        AHC_A_SCANNED             = 0x00100000,
+        AHC_B_SCANNED             = 0x00200000,
+        AHC_MULTI_CHANNEL         = 0x00400000,
+        AHC_BIOS_ENABLED          = 0x00800000,
+        AHC_SEEPROM_FOUND         = 0x01000000,
+        AHC_TERM_ENB_LVD          = 0x02000000,
+        AHC_ABORT_PENDING         = 0x04000000,
+        AHC_RESET_PENDING         = 0x08000000,
+#define AHC_IN_ISR_BIT              28
+        AHC_IN_ISR                = 0x10000000,
+        AHC_IN_ABORT              = 0x20000000,
+        AHC_IN_RESET              = 0x40000000,
+        AHC_EXTERNAL_SRAM         = 0x80000000
+} ahc_flag_type;
+
+typedef enum {
+  AHC_NONE             = 0x0000,
+  AHC_CHIPID_MASK      = 0x00ff,
+  AHC_AIC7770          = 0x0001,
+  AHC_AIC7850          = 0x0002,
+  AHC_AIC7860          = 0x0003,
+  AHC_AIC7870          = 0x0004,
+  AHC_AIC7880          = 0x0005,
+  AHC_AIC7890          = 0x0006,
+  AHC_AIC7895          = 0x0007,
+  AHC_AIC7896          = 0x0008,
+  AHC_AIC7892          = 0x0009,
+  AHC_AIC7899          = 0x000a,
+  AHC_VL               = 0x0100,
+  AHC_EISA             = 0x0200,
+  AHC_PCI              = 0x0400,
+} ahc_chip;
+
+typedef enum {
+  AHC_FENONE           = 0x0000,
+  AHC_ULTRA            = 0x0001,
+  AHC_ULTRA2           = 0x0002,
+  AHC_WIDE             = 0x0004,
+  AHC_TWIN             = 0x0008,
+  AHC_MORE_SRAM        = 0x0010,
+  AHC_CMD_CHAN         = 0x0020,
+  AHC_QUEUE_REGS       = 0x0040,
+  AHC_SG_PRELOAD       = 0x0080,
+  AHC_SPIOCAP          = 0x0100,
+  AHC_ULTRA3           = 0x0200,
+  AHC_NEW_AUTOTERM     = 0x0400,
+  AHC_AIC7770_FE       = AHC_FENONE,
+  AHC_AIC7850_FE       = AHC_SPIOCAP,
+  AHC_AIC7860_FE       = AHC_ULTRA|AHC_SPIOCAP,
+  AHC_AIC7870_FE       = AHC_FENONE,
+  AHC_AIC7880_FE       = AHC_ULTRA,
+  AHC_AIC7890_FE       = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2|
+                         AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_NEW_AUTOTERM,
+  AHC_AIC7895_FE       = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA,
+  AHC_AIC7896_FE       = AHC_AIC7890_FE,
+  AHC_AIC7892_FE       = AHC_AIC7890_FE|AHC_ULTRA3,
+  AHC_AIC7899_FE       = AHC_AIC7890_FE|AHC_ULTRA3,
+} ahc_feature;
+
+#define SCB_DMA_ADDR(scb, addr) ((unsigned long)(addr) + (scb)->scb_dma->dma_offset)
+
+struct aic7xxx_scb_dma {
+	unsigned long	       dma_offset;    /* Correction you have to add
+					       * to virtual address to get
+					       * dma handle in this region */
+	dma_addr_t	       dma_address;   /* DMA handle of the start,
+					       * for unmap */
+	unsigned int	       dma_len;	      /* DMA length */
+};
+
+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 */
+        volatile scb_flag_type flags;         /* current state of scb */
+        struct hw_scatterlist *sg_list;       /* SG list in adapter format */
+        unsigned char          tag_action;
+        unsigned char          sg_count;
+        unsigned char          *sense_cmd;    /*
+                                               * Allocate 6 characters for
+                                               * sense command.
+                                               */
+	unsigned char	       *cmnd;
+        unsigned int           sg_length; /* We init this during buildscb so we
+                                           * don't have to calculate anything
+                                           * during underflow/overflow/stat code
+                                           */
+        void                  *kmalloc_ptr;
+	struct aic7xxx_scb_dma *scb_dma;
+};
+
+/*
+ * Define a linked list of SCBs.
+ */
+typedef struct {
+  struct aic7xxx_scb *head;
+  struct aic7xxx_scb *tail;
+} scb_queue_type;
+
+static struct {
+  unsigned char errno;
+  const char *errmesg;
+} hard_error[] = {
+  { ILLHADDR,  "Illegal Host Access" },
+  { ILLSADDR,  "Illegal Sequencer Address referenced" },
+  { ILLOPCODE, "Illegal Opcode in sequencer program" },
+  { SQPARERR,  "Sequencer Ram Parity Error" },
+  { DPARERR,   "Data-Path Ram Parity Error" },
+  { MPARERR,   "Scratch Ram/SCB Array Ram Parity Error" },
+  { PCIERRSTAT,"PCI Error detected" },
+  { CIOPARERR, "CIOBUS Parity Error" }
+};
+
+static unsigned char
+generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
+
+typedef struct {
+  scb_queue_type free_scbs;        /*
+                                    * SCBs assigned to free slot on
+                                    * card (no paging required)
+                                    */
+  struct aic7xxx_scb   *scb_array[AIC7XXX_MAXSCB];
+  struct aic7xxx_hwscb *hscbs;
+  unsigned char  numscbs;          /* current number of scbs */
+  unsigned char  maxhscbs;         /* hardware scbs */
+  unsigned char  maxscbs;          /* max scbs including pageable scbs */
+  dma_addr_t	 hscbs_dma;	   /* DMA handle to hscbs */
+  unsigned int   hscbs_dma_len;    /* length of the above DMA area */
+  void          *hscb_kmalloc_ptr;
+} scb_data_type;
+
+struct target_cmd {
+  unsigned char mesg_bytes[4];
+  unsigned char command[28];
+};
+
+#define AHC_TRANS_CUR    0x0001
+#define AHC_TRANS_ACTIVE 0x0002
+#define AHC_TRANS_GOAL   0x0004
+#define AHC_TRANS_USER   0x0008
+#define AHC_TRANS_QUITE  0x0010
+typedef struct {
+  unsigned char cur_width;
+  unsigned char goal_width;
+  unsigned char cur_period;
+  unsigned char goal_period;
+  unsigned char cur_offset;
+  unsigned char goal_offset;
+  unsigned char cur_options;
+  unsigned char goal_options;
+  unsigned char user_width;
+  unsigned char user_period;
+  unsigned char user_offset;
+  unsigned char user_options;
+} transinfo_type;
+
+/*
+ * Define a structure used for each host adapter.  Note, in order to avoid
+ * problems with architectures I can't test on (because I don't have one,
+ * such as the Alpha based systems) which happen to give faults for
+ * non-aligned memory accesses, care was taken to align this structure
+ * in a way that gauranteed all accesses larger than 8 bits were aligned
+ * on the appropriate boundary.  It's also organized to try and be more
+ * cache line efficient.  Be careful when changing this lest you might hurt
+ * overall performance and bring down the wrath of the masses.
+ */
+struct aic7xxx_host {
+  /*
+   *  This is the first 64 bytes in the host struct
+   */
+
+  /*
+   * We are grouping things here....first, items that get either read or
+   * written with nearly every interrupt
+   */
+  volatile long            flags;
+  ahc_feature              features;         /* chip features */
+  unsigned long            base;             /* card base address */
+  volatile unsigned char  *maddr;            /* memory mapped address */
+  unsigned long            isr_count;        /* Interrupt count */
+  unsigned long            spurious_int;
+  scb_data_type           *scb_data;
+  volatile unsigned short  needdv;
+  volatile unsigned short  needppr;
+  volatile unsigned short  needsdtr;
+  volatile unsigned short  needwdtr;
+  volatile unsigned short  dtr_pending;
+  struct aic7xxx_cmd_queue {
+    Scsi_Cmnd *head;
+    Scsi_Cmnd *tail;
+  } completeq;
+
+  /*
+   * Things read/written on nearly every entry into aic7xxx_queue()
+   */
+  volatile scb_queue_type  waiting_scbs;
+  unsigned short           discenable;       /* Targets allowed to disconnect */
+  unsigned short           tagenable;        /* Targets using tagged I/O */
+  unsigned short           orderedtag;       /* Ordered Q tags allowed */
+  unsigned char            unpause;          /* unpause value for HCNTRL */
+  unsigned char            pause;            /* pause value for HCNTRL */
+  volatile unsigned char   qoutfifonext;
+  volatile unsigned char   activescbs;       /* active scbs */
+  volatile unsigned char   max_activescbs;
+  volatile unsigned char   qinfifonext;
+  volatile unsigned char  *untagged_scbs;
+  volatile unsigned char  *qoutfifo;
+  volatile unsigned char  *qinfifo;
+
+#define  DEVICE_PRESENT                 0x01
+#define  BUS_DEVICE_RESET_PENDING       0x02
+#define  DEVICE_RESET_DELAY             0x04
+#define  DEVICE_PRINT_DTR               0x08
+#define  DEVICE_PARITY_ERROR            0x10
+#define  DEVICE_WAS_BUSY                0x20
+#define  DEVICE_SCSI_3                  0x40
+#define  DEVICE_SCANNED                 0x80
+  volatile unsigned char   dev_flags[MAX_TARGETS];
+  volatile unsigned char   dev_active_cmds[MAX_TARGETS];
+  volatile unsigned char   dev_temp_queue_depth[MAX_TARGETS];
+  unsigned char            dev_commands_sent[MAX_TARGETS];
+
+  unsigned int             dev_timer_active; /* Which devs have a timer set */
+  struct timer_list        dev_timer;
+  unsigned long            dev_expires[MAX_TARGETS];
+
+  spinlock_t               spin_lock;
+  volatile unsigned char   cpu_lock_count[NR_CPUS];
+
+  Scsi_Cmnd               *dev_dtr_cmnd[MAX_TARGETS];
+
+  unsigned int             dev_checksum[MAX_TARGETS];
+  
+  unsigned char            dev_last_queue_full[MAX_TARGETS];
+  unsigned char            dev_last_queue_full_count[MAX_TARGETS];
+  unsigned char            dev_max_queue_depth[MAX_TARGETS];
+
+  volatile scb_queue_type  delayed_scbs[MAX_TARGETS];
+
+
+  unsigned char            msg_buf[13];      /* The message for the target */
+  unsigned char            msg_type;
+#define MSG_TYPE_NONE              0x00
+#define MSG_TYPE_INITIATOR_MSGOUT  0x01
+#define MSG_TYPE_INITIATOR_MSGIN   0x02
+  unsigned char            msg_len;          /* Length of message */
+  unsigned char            msg_index;        /* Index into msg_buf array */
+  transinfo_type           transinfo[MAX_TARGETS];
+
+
+  /*
+   * We put the less frequently used host structure items after the more
+   * frequently used items to try and ease the burden on the cache subsystem.
+   * These entries are not *commonly* accessed, whereas the preceding entries
+   * are accessed very often.
+   */
+
+  unsigned int             irq;              /* IRQ for this adapter */
+  int                      instance;         /* aic7xxx instance number */
+  int                      scsi_id;          /* host adapter SCSI ID */
+  int                      scsi_id_b;        /* channel B for twin adapters */
+  unsigned int             bios_address;
+  int                      board_name_index;
+  unsigned short           needppr_copy;     /* default config */
+  unsigned short           needsdtr_copy;    /* default config */
+  unsigned short           needwdtr_copy;    /* default config */
+  unsigned short           ultraenb;         /* Ultra mode target list */
+  unsigned short           bios_control;     /* bios control - SEEPROM */
+  unsigned short           adapter_control;  /* adapter control - SEEPROM */
+  struct pci_dev	  *pdev;
+  unsigned char            pci_bus;
+  unsigned char            pci_device_fn;
+  struct seeprom_config    sc;
+  unsigned short           sc_type;
+  unsigned short           sc_size;
+  struct aic7xxx_host     *next;             /* allow for multiple IRQs */
+  struct Scsi_Host        *host;             /* pointer to scsi host */
+  int                      host_no;          /* SCSI host number */
+  unsigned long            mbase;            /* I/O memory address */
+  ahc_chip                 chip;             /* chip type */
+  dma_addr_t		   fifo_dma;	     /* DMA handle for fifo arrays */
+
+  /*
+   * Statistics Kept:
+   *
+   * Total Xfers (count for each command that has a data xfer),
+   * broken down further by reads && writes.
+   *
+   * Binned sizes, writes && reads:
+   *    < 512, 512, 1-2K, 2-4K, 4-8K, 8-16K, 16-32K, 32-64K, 64K-128K, > 128K
+   *
+   * Total amounts read/written above 512 bytes (amts under ignored)
+   *
+   * NOTE: Enabling this feature is likely to cause a noticeable performance
+   * decrease as the accesses into the stats structures blows apart multiple
+   * cache lines and is CPU time consuming.
+   *
+   * NOTE: Since it doesn't really buy us much, but consumes *tons* of RAM
+   * and blows apart all sorts of cache lines, I modified this so that we
+   * no longer look at the LUN.  All LUNs now go into the same bin on each
+   * device for stats purposes.
+   */
+  struct aic7xxx_xferstats {
+    long w_total;                          /* total writes */
+    long r_total;                          /* total reads */
+#ifdef AIC7XXX_PROC_STATS
+    long w_bins[8];                       /* binned write */
+    long r_bins[8];                       /* binned reads */
+#endif /* AIC7XXX_PROC_STATS */
+  } stats[MAX_TARGETS];                    /* [(channel << 3)|target] */
+
+#if 0
+  struct target_cmd       *targetcmds;
+  unsigned int             num_targetcmds;
+#endif
+
+};
+
+/*
+ * Valid SCSIRATE values. (p. 3-17)
+ * Provides a mapping of transfer periods in ns/4 to the proper value to
+ * stick in the SCSIRATE reg to use that transfer rate.
+ */
+#define AHC_SYNCRATE_ULTRA3 0
+#define AHC_SYNCRATE_ULTRA2 1
+#define AHC_SYNCRATE_ULTRA  3
+#define AHC_SYNCRATE_FAST   6
+#define AHC_SYNCRATE_CRC 0x40
+#define AHC_SYNCRATE_SE  0x10
+static struct aic7xxx_syncrate {
+  /* Rates in Ultra mode have bit 8 of sxfr set */
+#define                ULTRA_SXFR 0x100
+  int sxfr_ultra2;
+  int sxfr;
+  unsigned char period;
+  const char *rate[2];
+} aic7xxx_syncrates[] = {
+  { 0x42,  0x000,   9,  {"80.0", "160.0"} },
+  { 0x13,  0x000,  10,  {"40.0", "80.0"} },
+  { 0x14,  0x000,  11,  {"33.0", "66.6"} },
+  { 0x15,  0x100,  12,  {"20.0", "40.0"} },
+  { 0x16,  0x110,  15,  {"16.0", "32.0"} },
+  { 0x17,  0x120,  18,  {"13.4", "26.8"} },
+  { 0x18,  0x000,  25,  {"10.0", "20.0"} },
+  { 0x19,  0x010,  31,  {"8.0",  "16.0"} },
+  { 0x1a,  0x020,  37,  {"6.67", "13.3"} },
+  { 0x1b,  0x030,  43,  {"5.7",  "11.4"} },
+  { 0x10,  0x040,  50,  {"5.0",  "10.0"} },
+  { 0x00,  0x050,  56,  {"4.4",  "8.8" } },
+  { 0x00,  0x060,  62,  {"4.0",  "8.0" } },
+  { 0x00,  0x070,  68,  {"3.6",  "7.2" } },
+  { 0x00,  0x000,  0,   {NULL, NULL}   },
+};
+
+#define CTL_OF_SCB(scb) (((scb->hscb)->target_channel_lun >> 3) & 0x1),  \
+                        (((scb->hscb)->target_channel_lun >> 4) & 0xf), \
+                        ((scb->hscb)->target_channel_lun & 0x07)
+
+#define CTL_OF_CMD(cmd) ((cmd->channel) & 0x01),  \
+                        ((cmd->target) & 0x0f), \
+                        ((cmd->lun) & 0x07)
+
+#define TARGET_INDEX(cmd)  ((cmd)->target | ((cmd)->channel << 3))
+
+/*
+ * A nice little define to make doing our printks a little easier
+ */
+
+#define WARN_LEAD KERN_WARNING "(scsi%d:%d:%d:%d) "
+#define INFO_LEAD KERN_INFO "(scsi%d:%d:%d:%d) "
+
+/*
+ * XXX - these options apply unilaterally to _all_ 274x/284x/294x
+ *       cards in the system.  This should be fixed.  Exceptions to this
+ *       rule are noted in the comments.
+ */
+
+
+/*
+ * Skip the scsi bus reset.  Non 0 make us skip the reset at startup.  This
+ * has no effect on any later resets that might occur due to things like
+ * SCSI bus timeouts.
+ */
+static unsigned int aic7xxx_no_reset = 0;
+/*
+ * Certain PCI motherboards will scan PCI devices from highest to lowest,
+ * others scan from lowest to highest, and they tend to do all kinds of
+ * strange things when they come into contact with PCI bridge chips.  The
+ * net result of all this is that the PCI card that is actually used to boot
+ * the machine is very hard to detect.  Most motherboards go from lowest
+ * PCI slot number to highest, and the first SCSI controller found is the
+ * one you boot from.  The only exceptions to this are when a controller
+ * has its BIOS disabled.  So, we by default sort all of our SCSI controllers
+ * from lowest PCI slot number to highest PCI slot number.  We also force
+ * all controllers with their BIOS disabled to the end of the list.  This
+ * works on *almost* all computers.  Where it doesn't work, we have this
+ * option.  Setting this option to non-0 will reverse the order of the sort
+ * to highest first, then lowest, but will still leave cards with their BIOS
+ * disabled at the very end.  That should fix everyone up unless there are
+ * really strange cirumstances.
+ */
+static int aic7xxx_reverse_scan = 0;
+/*
+ * Should we force EXTENDED translation on a controller.
+ *     0 == Use whatever is in the SEEPROM or default to off
+ *     1 == Use whatever is in the SEEPROM or default to on
+ */
+static unsigned int aic7xxx_extended = 0;
+/*
+ * The IRQ trigger method used on EISA controllers. Does not effect PCI cards.
+ *   -1 = Use detected settings.
+ *    0 = Force Edge triggered mode.
+ *    1 = Force Level triggered mode.
+ */
+static int aic7xxx_irq_trigger = -1;
+/*
+ * This variable is used to override the termination settings on a controller.
+ * This should not be used under normal conditions.  However, in the case
+ * that a controller does not have a readable SEEPROM (so that we can't
+ * read the SEEPROM settings directly) and that a controller has a buggered
+ * version of the cable detection logic, this can be used to force the 
+ * correct termination.  It is preferable to use the manual termination
+ * settings in the BIOS if possible, but some motherboard controllers store
+ * those settings in a format we can't read.  In other cases, auto term
+ * should also work, but the chipset was put together with no auto term
+ * logic (common on motherboard controllers).  In those cases, we have
+ * 32 bits here to work with.  That's good for 8 controllers/channels.  The
+ * bits are organized as 4 bits per channel, with scsi0 getting the lowest
+ * 4 bits in the int.  A 1 in a bit position indicates the termination setting
+ * that corresponds to that bit should be enabled, a 0 is disabled.
+ * It looks something like this:
+ *
+ *    0x0f =  1111-Single Ended Low Byte Termination on/off
+ *            ||\-Single Ended High Byte Termination on/off
+ *            |\-LVD Low Byte Termination on/off
+ *            \-LVD High Byte Termination on/off
+ *
+ * For non-Ultra2 controllers, the upper 2 bits are not important.  So, to
+ * enable both high byte and low byte termination on scsi0, I would need to
+ * make sure that the override_term variable was set to 0x03 (bits 0011).
+ * To make sure that all termination is enabled on an Ultra2 controller at
+ * scsi2 and only high byte termination on scsi1 and high and low byte
+ * termination on scsi0, I would set override_term=0xf23 (bits 1111 0010 0011)
+ *
+ * For the most part, users should never have to use this, that's why I
+ * left it fairly cryptic instead of easy to understand.  If you need it,
+ * most likely someone will be telling you what your's needs to be set to.
+ */
+static int aic7xxx_override_term = -1;
+/*
+ * Certain motherboard chipset controllers tend to screw
+ * up the polarity of the term enable output pin.  Use this variable
+ * to force the correct polarity for your system.  This is a bitfield variable
+ * similar to the previous one, but this one has one bit per channel instead
+ * of four.
+ *    0 = Force the setting to active low.
+ *    1 = Force setting to active high.
+ * Most Adaptec cards are active high, several motherboards are active low.
+ * To force a 2940 card at SCSI 0 to active high and a motherboard 7895
+ * controller at scsi1 and scsi2 to active low, and a 2910 card at scsi3
+ * to active high, you would need to set stpwlev=0x9 (bits 1001).
+ *
+ * People shouldn't need to use this, but if you are experiencing lots of
+ * SCSI timeout problems, this may help.  There is one sure way to test what
+ * this option needs to be.  Using a boot floppy to boot the system, configure
+ * your system to enable all SCSI termination (in the Adaptec SCSI BIOS) and
+ * if needed then also pass a value to override_term to make sure that the
+ * driver is enabling SCSI termination, then set this variable to either 0
+ * or 1.  When the driver boots, make sure there are *NO* SCSI cables
+ * connected to your controller.  If it finds and inits the controller
+ * without problem, then the setting you passed to stpwlev was correct.  If
+ * the driver goes into a reset loop and hangs the system, then you need the
+ * other setting for this variable.  If neither setting lets the machine
+ * boot then you have definite termination problems that may not be fixable.
+ */
+static int aic7xxx_stpwlev = -1;
+/*
+ * Set this to non-0 in order to force the driver to panic the kernel
+ * and print out debugging info on a SCSI abort or reset cycle.
+ */
+static int aic7xxx_panic_on_abort = 0;
+/*
+ * PCI bus parity checking of the Adaptec controllers.  This is somewhat
+ * dubious at best.  To my knowledge, this option has never actually
+ * solved a PCI parity problem, but on certain machines with broken PCI
+ * chipset configurations, it can generate tons of false error messages.
+ * It's included in the driver for completeness.
+ *   0 = Shut off PCI parity check
+ *  -1 = Normal polarity pci parity checking
+ *   1 = reverse polarity pci parity checking
+ *
+ * NOTE: you can't actually pass -1 on the lilo prompt.  So, to set this
+ * variable to -1 you would actually want to simply pass the variable
+ * name without a number.  That will invert the 0 which will result in
+ * -1.
+ */
+static int aic7xxx_pci_parity = 0;
+/*
+ * Set this to any non-0 value to cause us to dump the contents of all
+ * the card's registers in a hex dump format tailored to each model of
+ * controller.
+ * 
+ * NOTE: THE CONTROLLER IS LEFT IN AN UNUSEABLE STATE BY THIS OPTION.
+ *       YOU CANNOT BOOT UP WITH THIS OPTION, IT IS FOR DEBUGGING PURPOSES
+ *       ONLY
+ */
+static int aic7xxx_dump_card = 0;
+/*
+ * Set this to a non-0 value to make us dump out the 32 bit instruction
+ * registers on the card after completing the sequencer download.  This
+ * allows the actual sequencer download to be verified.  It is possible
+ * to use this option and still boot up and run your system.  This is
+ * only intended for debugging purposes.
+ */
+static int aic7xxx_dump_sequencer = 0;
+/*
+ * Certain newer motherboards have put new PCI based devices into the
+ * IO spaces that used to typically be occupied by VLB or EISA cards.
+ * This overlap can cause these newer motherboards to lock up when scanned
+ * for older EISA and VLB devices.  Setting this option to non-0 will
+ * cause the driver to skip scanning for any VLB or EISA controllers and
+ * only support the PCI controllers.  NOTE: this means that if the kernel
+ * os compiled with PCI support disabled, then setting this to non-0
+ * would result in never finding any devices :)
+ */
+static int aic7xxx_no_probe = 0;
+/*
+ * On some machines, enabling the external SCB RAM isn't reliable yet.  I
+ * haven't had time to make test patches for things like changing the
+ * timing mode on that external RAM either.  Some of those changes may
+ * fix the problem.  Until then though, we default to external SCB RAM
+ * off and give a command line option to enable it.
+ */
+static int aic7xxx_scbram = 0;
+/*
+ * So that we can set how long each device is given as a selection timeout.
+ * The table of values goes like this:
+ *   0 - 256ms
+ *   1 - 128ms
+ *   2 - 64ms
+ *   3 - 32ms
+ * We default to 64ms because it's fast.  Some old SCSI-I devices need a
+ * longer time.  The final value has to be left shifted by 3, hence 0x10
+ * is the final value.
+ */
+static int aic7xxx_seltime = 0x10;
+/*
+ * So that insmod can find the variable and make it point to something
+ */
+#ifdef MODULE
+static char * aic7xxx = NULL;
+MODULE_PARM(aic7xxx, "s");
+
+/*
+ * Just in case someone uses commas to separate items on the insmod
+ * command line, we define a dummy buffer here to avoid having insmod
+ * write wild stuff into our code segment
+ */
+static char dummy_buffer[60] = "Please don't trounce on me insmod!!\n";
+
+#endif
+
+#define VERBOSE_NORMAL         0x0000
+#define VERBOSE_NEGOTIATION    0x0001
+#define VERBOSE_SEQINT         0x0002
+#define VERBOSE_SCSIINT        0x0004
+#define VERBOSE_PROBE          0x0008
+#define VERBOSE_PROBE2         0x0010
+#define VERBOSE_NEGOTIATION2   0x0020
+#define VERBOSE_MINOR_ERROR    0x0040
+#define VERBOSE_TRACING        0x0080
+#define VERBOSE_ABORT          0x0f00
+#define VERBOSE_ABORT_MID      0x0100
+#define VERBOSE_ABORT_FIND     0x0200
+#define VERBOSE_ABORT_PROCESS  0x0400
+#define VERBOSE_ABORT_RETURN   0x0800
+#define VERBOSE_RESET          0xf000
+#define VERBOSE_RESET_MID      0x1000
+#define VERBOSE_RESET_FIND     0x2000
+#define VERBOSE_RESET_PROCESS  0x4000
+#define VERBOSE_RESET_RETURN   0x8000
+static int aic7xxx_verbose = VERBOSE_NORMAL | VERBOSE_NEGOTIATION |
+           VERBOSE_PROBE;                     /* verbose messages */
+
+
+/****************************************************************************
+ *
+ * We're going to start putting in function declarations so that order of
+ * functions is no longer important.  As needed, they are added here.
+ *
+ ***************************************************************************/
+
+static void aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd);
+static void aic7xxx_print_card(struct aic7xxx_host *p);
+static void aic7xxx_print_scratch_ram(struct aic7xxx_host *p);
+static void aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+static void aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer);
+#endif
+
+/****************************************************************************
+ *
+ * These functions are now used.  They happen to be wrapped in useless
+ * inb/outb port read/writes around the real reads and writes because it
+ * seems that certain very fast CPUs have a problem dealing with us when
+ * going at full speed.
+ *
+ ***************************************************************************/
+
+static inline unsigned char
+aic_inb(struct aic7xxx_host *p, long port)
+{
+#ifdef MMAPIO
+  unsigned char x;
+  if(p->maddr)
+  {
+    x = readb(p->maddr + port);
+  }
+  else
+  {
+    x = inb(p->base + port);
+  }
+  return(x);
+#else
+  return(inb(p->base + port));
+#endif
+}
+
+static inline void
+aic_outb(struct aic7xxx_host *p, unsigned char val, long port)
+{
+#ifdef MMAPIO
+  if(p->maddr)
+  {
+    writeb(val, p->maddr + port);
+    mb(); /* locked operation in order to force CPU ordering */
+    readb(p->maddr + HCNTRL); /* dummy read to flush the PCI write */
+  }
+  else
+  {
+    outb(val, p->base + port);
+    mb(); /* locked operation in order to force CPU ordering */
+  }
+#else
+  outb(val, p->base + port);
+  mb(); /* locked operation in order to force CPU ordering */
+#endif
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_setup
+ *
+ * Description:
+ *   Handle Linux boot parameters. This routine allows for assigning a value
+ *   to a parameter with a ':' between the parameter and the value.
+ *   ie. aic7xxx=unpause:0x0A,extended
+ *-F*************************************************************************/
+static int
+aic7xxx_setup(char *s)
+{
+  int   i, n;
+  char *p;
+  char *end;
+
+  static struct {
+    const char *name;
+    unsigned int *flag;
+  } options[] = {
+    { "extended",    &aic7xxx_extended },
+    { "no_reset",    &aic7xxx_no_reset },
+    { "irq_trigger", &aic7xxx_irq_trigger },
+    { "verbose",     &aic7xxx_verbose },
+    { "reverse_scan",&aic7xxx_reverse_scan },
+    { "override_term", &aic7xxx_override_term },
+    { "stpwlev", &aic7xxx_stpwlev },
+    { "no_probe", &aic7xxx_no_probe },
+    { "panic_on_abort", &aic7xxx_panic_on_abort },
+    { "pci_parity", &aic7xxx_pci_parity },
+    { "dump_card", &aic7xxx_dump_card },
+    { "dump_sequencer", &aic7xxx_dump_sequencer },
+    { "scbram", &aic7xxx_scbram },
+    { "seltime", &aic7xxx_seltime },
+    { "tag_info",    NULL }
+  };
+
+  end = strchr(s, '\0');
+
+  for (p = strtok(s, ",."); p; p = strtok(NULL, ",."))
+  {
+    for (i = 0; i < NUMBER(options); i++)
+    {
+      n = strlen(options[i].name);
+      if (!strncmp(options[i].name, p, n))
+      {
+        if (!strncmp(p, "tag_info", n))
+        {
+          if (p[n] == ':')
+          {
+            char *base;
+            char *tok, *tok_end, *tok_end2;
+            char tok_list[] = { '.', ',', '{', '}', '\0' };
+            int i, instance = -1, device = -1;
+            unsigned char done = FALSE;
+
+            base = p;
+            tok = base + n + 1;  /* Forward us just past the ':' */
+            tok_end = strchr(tok, '\0');
+            if (tok_end < end)
+              *tok_end = ',';
+            while(!done)
+            {
+              switch(*tok)
+              {
+                case '{':
+                  if (instance == -1)
+                    instance = 0;
+                  else if (device == -1)
+                    device = 0;
+                  tok++;
+                  break;
+                case '}':
+                  if (device != -1)
+                    device = -1;
+                  else if (instance != -1)
+                    instance = -1;
+                  tok++;
+                  break;
+                case ',':
+                case '.':
+                  if (instance == -1)
+                    done = TRUE;
+                  else if (device >= 0)
+                    device++;
+                  else if (instance >= 0)
+                    instance++;
+                  if ( (device >= MAX_TARGETS) || 
+                       (instance >= NUMBER(aic7xxx_tag_info)) )
+                    done = TRUE;
+                  tok++;
+                  if (!done)
+                  {
+                    base = tok;
+                  }
+                  break;
+                case '\0':
+                  done = TRUE;
+                  break;
+                default:
+                  done = TRUE;
+                  tok_end = strchr(tok, '\0');
+                  for(i=0; tok_list[i]; i++)
+                  {
+                    tok_end2 = strchr(tok, tok_list[i]);
+                    if ( (tok_end2) && (tok_end2 < tok_end) )
+                    {
+                      tok_end = tok_end2;
+                      done = FALSE;
+                    }
+                  }
+                  if ( (instance >= 0) && (device >= 0) &&
+                       (instance < NUMBER(aic7xxx_tag_info)) &&
+                       (device < MAX_TARGETS) )
+                    aic7xxx_tag_info[instance].tag_commands[device] =
+                      simple_strtoul(tok, NULL, 0) & 0xff;
+                  tok = tok_end;
+                  break;
+              }
+            }
+            while((p != base) && (p != NULL))
+              p = strtok(NULL, ",.");
+          }
+        }
+        else if (p[n] == ':')
+        {
+          *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
+          if(!strncmp(p, "seltime", n))
+          {
+            *(options[i].flag) = (*(options[i].flag) % 4) << 3;
+          }
+        }
+        else if (!strncmp(p, "verbose", n))
+        {
+          *(options[i].flag) = 0xff29;
+        }
+        else
+        {
+          *(options[i].flag) = ~(*(options[i].flag));
+          if(!strncmp(p, "seltime", n))
+          {
+            *(options[i].flag) = (*(options[i].flag) % 4) << 3;
+          }
+        }
+      }
+    }
+  }
+  return 1;
+}
+
+__setup("aic7xxx=", aic7xxx_setup);
+
+/*+F*************************************************************************
+ * Function:
+ *   pause_sequencer
+ *
+ * Description:
+ *   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
+pause_sequencer(struct aic7xxx_host *p)
+{
+  aic_outb(p, p->pause, HCNTRL);
+  while ((aic_inb(p, HCNTRL) & PAUSE) == 0)
+  {
+    ;
+  }
+  if(p->features & AHC_ULTRA2)
+  {
+    aic_inb(p, CCSCBCTL);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   unpause_sequencer
+ *
+ * Description:
+ *   Unpause the sequencer. Unremarkable, yet done often enough to
+ *   warrant an easy way to do it.
+ *-F*************************************************************************/
+static void
+unpause_sequencer(struct aic7xxx_host *p, int unpause_always)
+{
+  if (unpause_always ||
+      ( !(aic_inb(p, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) &&
+        !(p->flags & AHC_HANDLING_REQINITS) ) )
+  {
+    aic_outb(p, p->unpause, HCNTRL);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   restart_sequencer
+ *
+ * Description:
+ *   Restart the sequencer program from address zero.  This assumes
+ *   that the sequencer is already paused.
+ *-F*************************************************************************/
+static void
+restart_sequencer(struct aic7xxx_host *p)
+{
+  aic_outb(p, 0, SEQADDR0);
+  aic_outb(p, 0, SEQADDR1);
+  aic_outb(p, FASTMODE, SEQCTL);
+}
+
+/*
+ * We include the aic7xxx_seq.c file here so that the other defines have
+ * already been made, and so that it comes before the code that actually
+ * downloads the instructions (since we don't typically use function
+ * prototype, our code has to be ordered that way, it's a left-over from
+ * the original driver days.....I should fix it some time DL).
+ */
+#include "aic7xxx_old/aic7xxx_seq.c"
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_check_patch
+ *
+ * Description:
+ *   See if the next patch to download should be downloaded.
+ *-F*************************************************************************/
+static int
+aic7xxx_check_patch(struct aic7xxx_host *p,
+  struct sequencer_patch **start_patch, int start_instr, int *skip_addr)
+{
+  struct sequencer_patch *cur_patch;
+  struct sequencer_patch *last_patch;
+  int num_patches;
+
+  num_patches = sizeof(sequencer_patches)/sizeof(struct sequencer_patch);
+  last_patch = &sequencer_patches[num_patches];
+  cur_patch = *start_patch;
+
+  while ((cur_patch < last_patch) && (start_instr == cur_patch->begin))
+  {
+    if (cur_patch->patch_func(p) == 0)
+    {
+      /*
+       * Start rejecting code.
+       */
+      *skip_addr = start_instr + cur_patch->skip_instr;
+      cur_patch += cur_patch->skip_patch;
+    }
+    else
+    {
+      /*
+       * Found an OK patch.  Advance the patch pointer to the next patch
+       * and wait for our instruction pointer to get here.
+       */
+      cur_patch++;
+    }
+  }
+
+  *start_patch = cur_patch;
+  if (start_instr < *skip_addr)
+    /*
+     * Still skipping
+     */
+    return (0);
+  return(1);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_download_instr
+ *
+ * Description:
+ *   Find the next patch to download.
+ *-F*************************************************************************/
+static void
+aic7xxx_download_instr(struct aic7xxx_host *p, int instrptr,
+  unsigned char *dconsts)
+{
+  union ins_formats instr;
+  struct ins_format1 *fmt1_ins;
+  struct ins_format3 *fmt3_ins;
+  unsigned char opcode;
+
+  instr = *(union ins_formats*) &seqprog[instrptr * 4];
+
+  instr.integer = le32_to_cpu(instr.integer);
+  
+  fmt1_ins = &instr.format1;
+  fmt3_ins = NULL;
+
+  /* Pull the opcode */
+  opcode = instr.format1.opcode;
+  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:
+    {
+      struct sequencer_patch *cur_patch;
+      int address_offset;
+      unsigned int address;
+      int skip_addr;
+      int i;
+
+      fmt3_ins = &instr.format3;
+      address_offset = 0;
+      address = fmt3_ins->address;
+      cur_patch = sequencer_patches;
+      skip_addr = 0;
+
+      for (i = 0; i < address;)
+      {
+        aic7xxx_check_patch(p, &cur_patch, i, &skip_addr);
+        if (skip_addr > i)
+        {
+          int end_addr;
+
+          end_addr = MIN(address, skip_addr);
+          address_offset += end_addr - i;
+          i = skip_addr;
+        }
+        else
+        {
+          i++;
+        }
+      }
+      address -= address_offset;
+      fmt3_ins->address = address;
+      /* Fall Through to the next code section */
+    }
+    case AIC_OP_OR:
+    case AIC_OP_AND:
+    case AIC_OP_XOR:
+    case AIC_OP_ADD:
+    case AIC_OP_ADC:
+    case AIC_OP_BMOV:
+      if (fmt1_ins->parity != 0)
+      {
+        fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+      }
+      fmt1_ins->parity = 0;
+      /* Fall Through to the next code section */
+    case AIC_OP_ROL:
+      if ((p->features & AHC_ULTRA2) != 0)
+      {
+        int i, count;
+
+        /* Calculate odd parity for the instruction */
+        for ( i=0, count=0; i < 31; i++)
+        {
+          unsigned int mask;
+
+          mask = 0x01 << i;
+          if ((instr.integer & mask) != 0)
+            count++;
+        }
+        if (!(count & 0x01))
+          instr.format1.parity = 1;
+      }
+      else
+      {
+        if (fmt3_ins != NULL)
+        {
+          instr.integer =  fmt3_ins->immediate |
+                          (fmt3_ins->source << 8) |
+                          (fmt3_ins->address << 16) |
+                          (fmt3_ins->opcode << 25);
+        }
+        else
+        {
+          instr.integer =  fmt1_ins->immediate |
+                          (fmt1_ins->source << 8) |
+                          (fmt1_ins->destination << 16) |
+                          (fmt1_ins->ret << 24) |
+                          (fmt1_ins->opcode << 25);
+        }
+      }
+      aic_outb(p, (instr.integer & 0xff), SEQRAM);
+      aic_outb(p, ((instr.integer >> 8) & 0xff), SEQRAM);
+      aic_outb(p, ((instr.integer >> 16) & 0xff), SEQRAM);
+      aic_outb(p, ((instr.integer >> 24) & 0xff), SEQRAM);
+      udelay(10);
+      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)
+{
+  struct sequencer_patch *cur_patch;
+  int i;
+  int downloaded;
+  int skip_addr;
+  unsigned char download_consts[4] = {0, 0, 0, 0};
+
+  if (aic7xxx_verbose & VERBOSE_PROBE)
+  {
+    printk(KERN_INFO "(scsi%d) Downloading sequencer code...", p->host_no);
+  }
+#if 0
+  download_consts[TMODE_NUMCMDS] = p->num_targetcmds;
+#endif
+  download_consts[TMODE_NUMCMDS] = 0;
+  cur_patch = &sequencer_patches[0];
+  downloaded = 0;
+  skip_addr = 0;
+
+  aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
+  aic_outb(p, 0, SEQADDR0);
+  aic_outb(p, 0, SEQADDR1);
+
+  for (i = 0; i < sizeof(seqprog) / 4;  i++)
+  {
+    if (aic7xxx_check_patch(p, &cur_patch, i, &skip_addr) == 0)
+    {
+      /* Skip this instruction for this configuration. */
+      continue;
+    }
+    aic7xxx_download_instr(p, i, &download_consts[0]);
+    downloaded++;
+  }
+
+  aic_outb(p, 0, SEQADDR0);
+  aic_outb(p, 0, SEQADDR1);
+  aic_outb(p, FASTMODE | FAILDIS, SEQCTL);
+  unpause_sequencer(p, TRUE);
+  mdelay(1);
+  pause_sequencer(p);
+  aic_outb(p, FASTMODE, SEQCTL);
+  if (aic7xxx_verbose & VERBOSE_PROBE)
+  {
+    printk(" %d instructions downloaded\n", downloaded);
+  }
+  if (aic7xxx_dump_sequencer)
+    aic7xxx_print_sequencer(p, downloaded);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_print_sequencer
+ *
+ * Description:
+ *   Print the contents of the sequencer memory to the screen.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_sequencer(struct aic7xxx_host *p, int downloaded)
+{
+  int i, k, temp;
+  
+  aic_outb(p, PERRORDIS|LOADRAM|FAILDIS|FASTMODE, SEQCTL);
+  aic_outb(p, 0, SEQADDR0);
+  aic_outb(p, 0, SEQADDR1);
+
+  k = 0;
+  for (i=0; i < downloaded; i++)
+  {
+    if ( k == 0 )
+      printk("%03x: ", i);
+    temp = aic_inb(p, SEQRAM);
+    temp |= (aic_inb(p, SEQRAM) << 8);
+    temp |= (aic_inb(p, SEQRAM) << 16);
+    temp |= (aic_inb(p, SEQRAM) << 24);
+    printk("%08x", temp);
+    if ( ++k == 8 )
+    {
+      printk("\n");
+      k = 0;
+    }
+    else
+      printk(" ");
+  }
+  aic_outb(p, 0, SEQADDR0);
+  aic_outb(p, 0, SEQADDR1);
+  aic_outb(p, FASTMODE | FAILDIS, SEQCTL);
+  unpause_sequencer(p, TRUE);
+  mdelay(1);
+  pause_sequencer(p);
+  aic_outb(p, FASTMODE, SEQCTL);
+  printk("\n");
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_info
+ *
+ * Description:
+ *   Return a string describing the driver.
+ *-F*************************************************************************/
+const char *
+aic7xxx_info(struct Scsi_Host *dooh)
+{
+  static char buffer[256];
+  char *bp;
+  struct aic7xxx_host *p;
+
+  bp = &buffer[0];
+  p = (struct aic7xxx_host *)dooh->hostdata;
+  memset(bp, 0, sizeof(buffer));
+  strcpy(bp, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
+  strcat(bp, AIC7XXX_C_VERSION);
+  strcat(bp, "/");
+  strcat(bp, AIC7XXX_H_VERSION);
+  strcat(bp, "\n");
+  strcat(bp, "       <");
+  strcat(bp, board_names[p->board_name_index]);
+  strcat(bp, ">");
+
+  return(bp);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_find_syncrate
+ *
+ * Description:
+ *   Look up the valid period to SCSIRATE conversion in our table
+ *-F*************************************************************************/
+static struct aic7xxx_syncrate *
+aic7xxx_find_syncrate(struct aic7xxx_host *p, unsigned int *period,
+  unsigned int maxsync, unsigned char *options)
+{
+  struct aic7xxx_syncrate *syncrate;
+  int done = FALSE;
+
+  switch(*options)
+  {
+    case MSG_EXT_PPR_OPTION_DT_CRC:
+    case MSG_EXT_PPR_OPTION_DT_UNITS:
+      if(!(p->features & AHC_ULTRA3))
+      {
+        *options = 0;
+        maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
+      }
+      break;
+    case MSG_EXT_PPR_OPTION_DT_CRC_QUICK:
+    case MSG_EXT_PPR_OPTION_DT_UNITS_QUICK:
+      if(!(p->features & AHC_ULTRA3))
+      {
+        *options = 0;
+        maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
+      }
+      else
+      {
+        /*
+         * we don't support the Quick Arbitration variants of dual edge
+         * clocking.  As it turns out, we want to send back the
+         * same basic option, but without the QA attribute.
+         * We know that we are responding because we would never set
+         * these options ourself, we would only respond to them.
+         */
+        switch(*options)
+        {
+          case MSG_EXT_PPR_OPTION_DT_CRC_QUICK:
+            *options = MSG_EXT_PPR_OPTION_DT_CRC;
+            break;
+          case MSG_EXT_PPR_OPTION_DT_UNITS_QUICK:
+            *options = MSG_EXT_PPR_OPTION_DT_UNITS;
+            break;
+        }
+      }
+      break;
+    default:
+      *options = 0;
+      maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
+      break;
+  }
+  syncrate = &aic7xxx_syncrates[maxsync];
+  while ( (syncrate->rate[0] != NULL) &&
+         (!(p->features & AHC_ULTRA2) || syncrate->sxfr_ultra2) )
+  {
+    if (*period <= syncrate->period) 
+    {
+      switch(*options)
+      {
+        case MSG_EXT_PPR_OPTION_DT_CRC:
+        case MSG_EXT_PPR_OPTION_DT_UNITS:
+          if(!(syncrate->sxfr_ultra2 & AHC_SYNCRATE_CRC))
+          {
+            done = TRUE;
+            /*
+             * oops, we went too low for the CRC/DualEdge signalling, so
+             * clear the options byte
+             */
+            *options = 0;
+            /*
+             * We'll be sending a reply to this packet to set the options
+             * properly, so unilaterally set the period as well.
+             */
+            *period = syncrate->period;
+          }
+          else
+          {
+            done = TRUE;
+            if(syncrate == &aic7xxx_syncrates[maxsync])
+            {
+              *period = syncrate->period;
+            }
+          }
+          break;
+        default:
+          if(!(syncrate->sxfr_ultra2 & AHC_SYNCRATE_CRC))
+          {
+            done = TRUE;
+            if(syncrate == &aic7xxx_syncrates[maxsync])
+            {
+              *period = syncrate->period;
+            }
+          }
+          break;
+      }
+      if(done)
+      {
+        break;
+      }
+    }
+    syncrate++;
+  }
+  if ( (*period == 0) || (syncrate->rate[0] == NULL) ||
+       ((p->features & AHC_ULTRA2) && (syncrate->sxfr_ultra2 == 0)) )
+  {
+    /*
+     * Use async transfers for this target
+     */
+    *options = 0;
+    *period = 255;
+    syncrate = NULL;
+  }
+  return (syncrate);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_find_period
+ *
+ * Description:
+ *   Look up the valid SCSIRATE to period conversion in our table
+ *-F*************************************************************************/
+static unsigned int
+aic7xxx_find_period(struct aic7xxx_host *p, unsigned int scsirate,
+  unsigned int maxsync)
+{
+  struct aic7xxx_syncrate *syncrate;
+
+  if (p->features & AHC_ULTRA2)
+  {
+    scsirate &= SXFR_ULTRA2;
+  }
+  else
+  {
+    scsirate &= SXFR;
+  }
+
+  syncrate = &aic7xxx_syncrates[maxsync];
+  while (syncrate->rate[0] != NULL)
+  {
+    if (p->features & AHC_ULTRA2)
+    {
+      if (syncrate->sxfr_ultra2 == 0)
+        break;
+      else if (scsirate == syncrate->sxfr_ultra2)
+        return (syncrate->period);
+      else if (scsirate == (syncrate->sxfr_ultra2 & ~AHC_SYNCRATE_CRC))
+        return (syncrate->period);
+    }
+    else if (scsirate == (syncrate->sxfr & ~ULTRA_SXFR))
+    {
+      return (syncrate->period);
+    }
+    syncrate++;
+  }
+  return (0); /* async */
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_validate_offset
+ *
+ * Description:
+ *   Set a valid offset value for a particular card in use and transfer
+ *   settings in use.
+ *-F*************************************************************************/
+static void
+aic7xxx_validate_offset(struct aic7xxx_host *p,
+  struct aic7xxx_syncrate *syncrate, unsigned int *offset, int wide)
+{
+  unsigned int maxoffset;
+
+  /* Limit offset to what the card (and device) can do */
+  if (syncrate == NULL)
+  {
+    maxoffset = 0;
+  }
+  else if (p->features & AHC_ULTRA2)
+  {
+    maxoffset = MAX_OFFSET_ULTRA2;
+  }
+  else
+  {
+    if (wide)
+      maxoffset = MAX_OFFSET_16BIT;
+    else
+      maxoffset = MAX_OFFSET_8BIT;
+  }
+  *offset = MIN(*offset, maxoffset);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_set_syncrate
+ *
+ * Description:
+ *   Set the actual syncrate down in the card and in our host structs
+ *-F*************************************************************************/
+static void
+aic7xxx_set_syncrate(struct aic7xxx_host *p, struct aic7xxx_syncrate *syncrate,
+    int target, int channel, unsigned int period, unsigned int offset,
+    unsigned char options, unsigned int type)
+{
+  unsigned char tindex;
+  unsigned short target_mask;
+  unsigned char lun, old_options;
+  unsigned int old_period, old_offset;
+
+  tindex = target | (channel << 3);
+  target_mask = 0x01 << tindex;
+  lun = aic_inb(p, SCB_TCL) & 0x07;
+
+  if (syncrate == NULL)
+  {
+    period = 0;
+    offset = 0;
+  }
+
+  old_period = p->transinfo[tindex].cur_period;
+  old_offset = p->transinfo[tindex].cur_offset;
+  old_options = p->transinfo[tindex].cur_options;
+
+  
+  if (type & AHC_TRANS_CUR)
+  {
+    unsigned int scsirate;
+
+    scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+    if (p->features & AHC_ULTRA2)
+    {
+      scsirate &= ~SXFR_ULTRA2;
+      if (syncrate != NULL)
+      {
+        switch(options)
+        {
+          case MSG_EXT_PPR_OPTION_DT_UNITS:
+            /*
+             * mask off the CRC bit in the xfer settings
+             */
+            scsirate |= (syncrate->sxfr_ultra2 & ~AHC_SYNCRATE_CRC);
+            break;
+          default:
+            scsirate |= syncrate->sxfr_ultra2;
+            break;
+        }
+      }
+      if (type & AHC_TRANS_ACTIVE)
+      {
+        aic_outb(p, offset, SCSIOFFSET);
+      }
+      aic_outb(p, offset, TARG_OFFSET + tindex);
+    }
+    else /* Not an Ultra2 controller */
+    {
+      scsirate &= ~(SXFR|SOFS);
+      p->ultraenb &= ~target_mask;
+      if (syncrate != NULL)
+      {
+        if (syncrate->sxfr & ULTRA_SXFR)
+        {
+          p->ultraenb |= target_mask;
+        }
+        scsirate |= (syncrate->sxfr & SXFR);
+        scsirate |= (offset & SOFS);
+      }
+      if (type & AHC_TRANS_ACTIVE)
+      {
+        unsigned char sxfrctl0;
+
+        sxfrctl0 = aic_inb(p, SXFRCTL0);
+        sxfrctl0 &= ~FAST20;
+        if (p->ultraenb & target_mask)
+          sxfrctl0 |= FAST20;
+        aic_outb(p, sxfrctl0, SXFRCTL0);
+      }
+      aic_outb(p, p->ultraenb & 0xff, ULTRA_ENB);
+      aic_outb(p, (p->ultraenb >> 8) & 0xff, ULTRA_ENB + 1 );
+    }
+    if (type & AHC_TRANS_ACTIVE)
+    {
+      aic_outb(p, scsirate, SCSIRATE);
+    }
+    aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
+    p->transinfo[tindex].cur_period = period;
+    p->transinfo[tindex].cur_offset = offset;
+    p->transinfo[tindex].cur_options = options;
+    if ( !(type & AHC_TRANS_QUITE) &&
+         (aic7xxx_verbose & VERBOSE_NEGOTIATION) &&
+         (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
+    {
+      if (offset)
+      {
+        int rate_mod = (scsirate & WIDEXFER) ? 1 : 0;
+      
+        printk(INFO_LEAD "Synchronous at %s Mbyte/sec, "
+               "offset %d.\n", p->host_no, channel, target, lun,
+               syncrate->rate[rate_mod], offset);
+      }
+      else
+      {
+        printk(INFO_LEAD "Using asynchronous transfers.\n",
+               p->host_no, channel, target, lun);
+      }
+      p->dev_flags[tindex] &= ~DEVICE_PRINT_DTR;
+    }
+  }
+
+  if (type & AHC_TRANS_GOAL)
+  {
+    p->transinfo[tindex].goal_period = period;
+    p->transinfo[tindex].goal_offset = offset;
+    p->transinfo[tindex].goal_options = options;
+  }
+
+  if (type & AHC_TRANS_USER)
+  {
+    p->transinfo[tindex].user_period = period;
+    p->transinfo[tindex].user_offset = offset;
+    p->transinfo[tindex].user_options = options;
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_set_width
+ *
+ * Description:
+ *   Set the actual width down in the card and in our host structs
+ *-F*************************************************************************/
+static void
+aic7xxx_set_width(struct aic7xxx_host *p, int target, int channel, int lun,
+    unsigned int width, unsigned int type)
+{
+  unsigned char tindex;
+  unsigned short target_mask;
+  unsigned int old_width;
+
+  tindex = target | (channel << 3);
+  target_mask = 1 << tindex;
+  
+  old_width = p->transinfo[tindex].cur_width;
+
+  if (type & AHC_TRANS_CUR) 
+  {
+    unsigned char scsirate;
+
+    scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+
+    scsirate &= ~WIDEXFER;
+    if (width == MSG_EXT_WDTR_BUS_16_BIT)
+      scsirate |= WIDEXFER;
+
+    aic_outb(p, scsirate, TARG_SCSIRATE + tindex);
+
+    if (type & AHC_TRANS_ACTIVE)
+      aic_outb(p, scsirate, SCSIRATE);
+
+    p->transinfo[tindex].cur_width = width;
+
+    if ( !(type & AHC_TRANS_QUITE) &&
+          (aic7xxx_verbose & VERBOSE_NEGOTIATION2) && 
+          (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
+    {
+      printk(INFO_LEAD "Using %s transfers\n", p->host_no, channel, target,
+        lun, (scsirate & WIDEXFER) ? "Wide(16bit)" : "Narrow(8bit)" );
+    }
+  }
+
+  if (type & AHC_TRANS_GOAL)
+    p->transinfo[tindex].goal_width = width;
+  if (type & AHC_TRANS_USER)
+    p->transinfo[tindex].user_width = width;
+
+  if (p->transinfo[tindex].goal_offset)
+  {
+    if (p->features & AHC_ULTRA2)
+    {
+      p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+    }
+    else if (width == MSG_EXT_WDTR_BUS_16_BIT)
+    {
+      p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+    }
+    else
+    {
+      p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+    }
+  }
+}
+      
+/*+F*************************************************************************
+ * Function:
+ *   scbq_init
+ *
+ * Description:
+ *   SCB queue initialization.
+ *
+ *-F*************************************************************************/
+static void
+scbq_init(volatile scb_queue_type *queue)
+{
+  queue->head = NULL;
+  queue->tail = NULL;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   scbq_insert_head
+ *
+ * Description:
+ *   Add an SCB to the head of the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_insert_head(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags;
+#endif
+
+  DRIVER_LOCK
+  scb->q_next = queue->head;
+  queue->head = scb;
+  if (queue->tail == NULL)       /* If list was empty, update tail. */
+    queue->tail = queue->head;
+  DRIVER_UNLOCK
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   scbq_remove_head
+ *
+ * Description:
+ *   Remove an SCB from the head of the list.
+ *
+ *-F*************************************************************************/
+static inline struct aic7xxx_scb *
+scbq_remove_head(volatile scb_queue_type *queue)
+{
+  struct aic7xxx_scb * scbp;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags;
+#endif
+
+  DRIVER_LOCK
+  scbp = queue->head;
+  if (queue->head != NULL)
+    queue->head = queue->head->q_next;
+  if (queue->head == NULL)       /* If list is now empty, update tail. */
+    queue->tail = NULL;
+  DRIVER_UNLOCK
+  return(scbp);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   scbq_remove
+ *
+ * Description:
+ *   Removes an SCB from the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_remove(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags;
+#endif
+
+  DRIVER_LOCK
+  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;
+      }
+    }
+  }
+  DRIVER_UNLOCK
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   scbq_insert_tail
+ *
+ * Description:
+ *   Add an SCB at the tail of the list.
+ *
+ *-F*************************************************************************/
+static inline void
+scbq_insert_tail(volatile scb_queue_type *queue, struct aic7xxx_scb *scb)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags;
+#endif
+
+  DRIVER_LOCK
+  scb->q_next = NULL;
+  if (queue->tail != NULL)       /* Add the scb at the end of the list. */
+    queue->tail->q_next = scb;
+  queue->tail = scb;             /* Update the tail. */
+  if (queue->head == NULL)       /* If list was empty, update head. */
+    queue->head = queue->tail;
+  DRIVER_UNLOCK
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_match_scb
+ *
+ * Description:
+ *   Checks to see if an scb matches the target/channel as specified.
+ *   If target is ALL_TARGETS (-1), then we're looking for any device
+ *   on the specified channel; this happens when a channel is going
+ *   to be reset and all devices on that channel must be aborted.
+ *-F*************************************************************************/
+static int
+aic7xxx_match_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
+    int target, int channel, int lun, unsigned char tag)
+{
+  int targ = (scb->hscb->target_channel_lun >> 4) & 0x0F;
+  int chan = (scb->hscb->target_channel_lun >> 3) & 0x01;
+  int slun = scb->hscb->target_channel_lun & 0x07;
+  int match;
+
+  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
+   */
+  aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+  aic_outb(p, 0, SCB_CONTROL);
+
+  aic_outb(p, aic_inb(p, FREE_SCBH), SCB_NEXT);
+  aic_outb(p, aic_inb(p, SCBPTR), 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 prev)
+{
+  unsigned char next;
+
+  aic_outb(p, scbptr, SCBPTR);
+  next = aic_inb(p, SCB_NEXT);
+  aic7xxx_add_curscb_to_free_list(p);
+
+  if (prev != SCB_LIST_NULL)
+  {
+    aic_outb(p, prev, SCBPTR);
+    aic_outb(p, next, SCB_NEXT);
+  }
+  else
+  {
+    aic_outb(p, next, DISCONNECTED_SCBH);
+  }
+
+  return next;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_busy_target
+ *
+ * Description:
+ *   Set the specified target busy.
+ *-F*************************************************************************/
+static inline void
+aic7xxx_busy_target(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  p->untagged_scbs[scb->hscb->target_channel_lun] = scb->hscb->tag;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_index_busy_target
+ *
+ * Description:
+ *   Returns the index of the busy target, and optionally sets the
+ *   target inactive.
+ *-F*************************************************************************/
+static inline unsigned char
+aic7xxx_index_busy_target(struct aic7xxx_host *p, unsigned char tcl,
+    int unbusy)
+{
+  unsigned char busy_scbid;
+
+  busy_scbid = p->untagged_scbs[tcl];
+  if (unbusy)
+  {
+    p->untagged_scbs[tcl] = SCB_LIST_NULL;
+  }
+  return (busy_scbid);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_find_scb
+ *
+ * Description:
+ *   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 unsigned char
+aic7xxx_find_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  unsigned char saved_scbptr;
+  unsigned char curindex;
+
+  saved_scbptr = aic_inb(p, SCBPTR);
+  curindex = 0;
+  for (curindex = 0; curindex < p->scb_data->maxhscbs; curindex++)
+  {
+    aic_outb(p, curindex, SCBPTR);
+    if (aic_inb(p, SCB_TAG) == scb->hscb->tag)
+    {
+      break;
+    }
+  }
+  aic_outb(p, saved_scbptr, SCBPTR);
+  if (curindex >= p->scb_data->maxhscbs)
+  {
+    curindex = SCB_LIST_NULL;
+  }
+
+  return (curindex);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_allocate_scb
+ *
+ * Description:
+ *   Get an SCB from the free list or by allocating a new one.
+ *-F*************************************************************************/
+static int
+aic7xxx_allocate_scb(struct aic7xxx_host *p)
+{
+  struct aic7xxx_scb   *scbp = NULL;
+  int scb_size = (sizeof (struct hw_scatterlist) * AIC7XXX_MAX_SG) + 12 + 6;
+  int i;
+  int step = PAGE_SIZE / 1024;
+  unsigned long scb_count = 0;
+  struct hw_scatterlist *hsgp;
+  struct aic7xxx_scb *scb_ap;
+  struct aic7xxx_scb_dma *scb_dma;
+  unsigned char *bufs;
+
+  if (p->scb_data->numscbs < p->scb_data->maxscbs)
+  {
+    /*
+     * Calculate the optimal number of SCBs to allocate.
+     *
+     * NOTE: This formula works because the sizeof(sg_array) is always
+     * 1024.  Therefore, scb_size * i would always be > PAGE_SIZE *
+     * (i/step).  The (i-1) allows the left hand side of the equation
+     * to grow into the right hand side to a point of near perfect
+     * efficiency since scb_size * (i -1) is growing slightly faster
+     * than the right hand side.  If the number of SG array elements
+     * is changed, this function may not be near so efficient any more.
+     *
+     * Since the DMA'able buffers are now allocated in a seperate
+     * chunk this algorithm has been modified to match.  The '12'
+     * and '6' factors in scb_size are for the DMA'able command byte
+     * and sensebuffers respectively.  -DaveM
+     */
+    for ( i=step;; i *= 2 )
+    {
+      if ( (scb_size * (i-1)) >= ( (PAGE_SIZE * (i/step)) - 64 ) )
+      {
+        i /= 2;
+        break;
+      }
+    }
+    scb_count = MIN( (i-1), p->scb_data->maxscbs - p->scb_data->numscbs);
+    scb_ap = (struct aic7xxx_scb *)kmalloc(sizeof (struct aic7xxx_scb) * scb_count
+					   + sizeof(struct aic7xxx_scb_dma), GFP_ATOMIC);
+    if (scb_ap == NULL)
+      return(0);
+    scb_dma = (struct aic7xxx_scb_dma *)&scb_ap[scb_count];
+    hsgp = (struct hw_scatterlist *)
+      pci_alloc_consistent(p->pdev, scb_size * scb_count,
+			   &scb_dma->dma_address);
+    if (hsgp == NULL)
+    {
+      kfree(scb_ap);
+      return(0);
+    }
+    bufs = (unsigned char *)&hsgp[scb_count * AIC7XXX_MAX_SG];
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+    if (aic7xxx_verbose > 0xffff)
+    {
+      if (p->scb_data->numscbs == 0)
+	printk(INFO_LEAD "Allocating initial %ld SCB structures.\n",
+	  p->host_no, -1, -1, -1, scb_count);
+      else
+	printk(INFO_LEAD "Allocating %ld additional SCB structures.\n",
+	  p->host_no, -1, -1, -1, scb_count);
+    }
+#endif
+    memset(scb_ap, 0, sizeof (struct aic7xxx_scb) * scb_count);
+    scb_dma->dma_offset = (unsigned long)scb_dma->dma_address
+			  - (unsigned long)hsgp;
+    scb_dma->dma_len = scb_size * scb_count;
+    for (i=0; i < scb_count; i++)
+    {
+      scbp = &scb_ap[i];
+      scbp->hscb = &p->scb_data->hscbs[p->scb_data->numscbs];
+      scbp->sg_list = &hsgp[i * AIC7XXX_MAX_SG];
+      scbp->sense_cmd = bufs;
+      scbp->cmnd = bufs + 6;
+      bufs += 12 + 6;
+      scbp->scb_dma = scb_dma;
+      memset(scbp->hscb, 0, sizeof(struct aic7xxx_hwscb));
+      scbp->hscb->tag = p->scb_data->numscbs;
+      /*
+       * Place in the scb array; never is removed
+       */
+      p->scb_data->scb_array[p->scb_data->numscbs++] = scbp;
+      scbq_insert_tail(&p->scb_data->free_scbs, scbp);
+    }
+    scbp->kmalloc_ptr = scb_ap;
+  }
+  return(scb_count);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_queue_cmd_complete
+ *
+ * Description:
+ *   Due to race conditions present in the SCSI subsystem, it is easier
+ *   to queue completed commands, then call scsi_done() on them when
+ *   we're finished.  This function queues the completed commands.
+ *-F*************************************************************************/
+static void
+aic7xxx_queue_cmd_complete(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
+{
+  cmd->host_scribble = (char *)p->completeq.head;
+  p->completeq.head = cmd;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_done_cmds_complete
+ *
+ * Description:
+ *   Process the completed command queue.
+ *-F*************************************************************************/
+static void
+aic7xxx_done_cmds_complete(struct aic7xxx_host *p)
+{
+  Scsi_Cmnd *cmd;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned int cpu_flags = 0;
+#endif
+  
+  DRIVER_LOCK
+  while (p->completeq.head != NULL)
+  {
+    cmd = p->completeq.head;
+    p->completeq.head = (Scsi_Cmnd *)cmd->host_scribble;
+    cmd->host_scribble = NULL;
+    cmd->scsi_done(cmd);
+  }
+  DRIVER_UNLOCK
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_free_scb
+ *
+ * Description:
+ *   Free the scb and insert into the free scb list.
+ *-F*************************************************************************/
+static void
+aic7xxx_free_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+
+  scb->flags = SCB_FREE;
+  scb->cmd = NULL;
+  scb->sg_count = 0;
+  scb->sg_length = 0;
+  scb->tag_action = 0;
+  scb->hscb->control = 0;
+  scb->hscb->target_status = 0;
+  scb->hscb->target_channel_lun = SCB_LIST_NULL;
+
+  scbq_insert_head(&p->scb_data->free_scbs, scb);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_done
+ *
+ * Description:
+ *   Calls the higher level scsi done function and frees the scb.
+ *-F*************************************************************************/
+static void
+aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  Scsi_Cmnd *cmd = scb->cmd;
+  int tindex = TARGET_INDEX(cmd);
+  struct aic7xxx_scb *scbp;
+  unsigned char queue_depth;
+
+  if (cmd->use_sg > 1)
+  {
+    struct scatterlist *sg;
+
+    sg = (struct scatterlist *)cmd->request_buffer;
+    pci_unmap_sg(p->pdev, sg, cmd->use_sg, scsi_to_pci_dma_dir(cmd->sc_data_direction));
+  }
+  else if (cmd->request_bufflen)
+    pci_unmap_single(p->pdev, aic7xxx_mapping(cmd),
+		     cmd->request_bufflen,
+                     scsi_to_pci_dma_dir(cmd->sc_data_direction));
+  if (scb->flags & SCB_SENSE)
+  {
+    pci_unmap_single(p->pdev,
+                     le32_to_cpu(scb->sg_list[0].address),
+                     sizeof(cmd->sense_buffer),
+                     PCI_DMA_FROMDEVICE);
+  }
+  if (scb->flags & SCB_RECOVERY_SCB)
+  {
+    p->flags &= ~AHC_ABORT_PENDING;
+  }
+  if (scb->flags & SCB_RESET)
+  {
+      cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff);
+  }
+  else if (scb->flags & SCB_ABORT)
+  {
+      cmd->result = (DID_RESET << 16) | (cmd->result & 0xffff);
+  }
+  else if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
+  {
+    if ( (cmd->cmnd[0] == INQUIRY) && (cmd->result == DID_OK) )
+    {
+      char *buffer;
+      
+      p->dev_flags[tindex] |= DEVICE_PRESENT;
+      if(cmd->use_sg)
+      {
+        struct scatterlist *sg;
+
+        sg = (struct scatterlist *)cmd->request_buffer;
+        buffer = (char *)sg[0].address;
+      }
+      else
+      {
+        buffer = (char *)cmd->request_buffer;
+      }
+#define WIDE_INQUIRY_BITS 0x60
+#define SYNC_INQUIRY_BITS 0x10
+#define SCSI_VERSION_BITS 0x07
+#define SCSI_DT_BIT       0x04
+      if ( (buffer[7] & WIDE_INQUIRY_BITS) &&
+           (p->features & AHC_WIDE) )
+      {
+        p->needwdtr |= (1<<tindex);
+        p->needwdtr_copy |= (1<<tindex);
+        p->transinfo[tindex].goal_width = p->transinfo[tindex].user_width;
+      }
+      else
+      {
+        p->needwdtr &= ~(1<<tindex);
+        p->needwdtr_copy &= ~(1<<tindex);
+        pause_sequencer(p);
+        aic7xxx_set_width(p, cmd->target, cmd->channel, cmd->lun,
+                          MSG_EXT_WDTR_BUS_8_BIT, (AHC_TRANS_ACTIVE |
+                                                   AHC_TRANS_GOAL |
+                                                   AHC_TRANS_CUR) );
+        unpause_sequencer(p, FALSE);
+      }
+      if ( (buffer[7] & SYNC_INQUIRY_BITS) &&
+            p->transinfo[tindex].user_offset )
+      {
+        p->transinfo[tindex].goal_period = p->transinfo[tindex].user_period;
+        p->transinfo[tindex].goal_options = p->transinfo[tindex].user_options;
+        if (p->features & AHC_ULTRA2)
+          p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+        else if (p->transinfo[tindex].goal_width == MSG_EXT_WDTR_BUS_16_BIT)
+          p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+        else
+          p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+        if ( (((buffer[2] & SCSI_VERSION_BITS) == 3) ||
+               (buffer[56] & SCSI_DT_BIT) ||
+	       (p->dev_flags[tindex] & DEVICE_SCSI_3) ) &&
+               (p->transinfo[tindex].user_period <= 9) &&
+               (p->transinfo[tindex].user_options) )
+        {
+          p->needppr |= (1<<tindex);
+          p->needppr_copy |= (1<<tindex);
+          p->needsdtr &= ~(1<<tindex);
+          p->needsdtr_copy &= ~(1<<tindex);
+          p->needwdtr &= ~(1<<tindex);
+          p->needwdtr_copy &= ~(1<<tindex);
+          p->dev_flags[tindex] |= DEVICE_SCSI_3;
+        }
+        else
+        {
+          p->needsdtr |= (1<<tindex);
+          p->needsdtr_copy |= (1<<tindex);
+          p->transinfo[tindex].goal_period = 
+            MAX(10, p->transinfo[tindex].goal_period);
+          p->transinfo[tindex].goal_options = 0;
+        }
+      }
+      else
+      {
+        p->needsdtr &= ~(1<<tindex);
+        p->needsdtr_copy &= ~(1<<tindex);
+        p->transinfo[tindex].goal_period = 255;
+        p->transinfo[tindex].goal_offset = 0;
+        p->transinfo[tindex].goal_options = 0;
+      }
+      /*
+       * This is needed to work around a sequencer bug for now.  Regardless
+       * of the controller in use, if we have a Quantum drive, we need to
+       * limit the speed to 80MByte/sec.  As soon as I get a fixed version
+       * of the sequencer, this code will get yanked.
+       */
+      if(!strncmp(buffer + 8, "QUANTUM", 7) &&
+         p->transinfo[tindex].goal_options )
+      {
+        p->transinfo[tindex].goal_period = 
+          MAX(p->transinfo[tindex].goal_period, 10);
+        p->transinfo[tindex].goal_options = 0;
+        p->needppr &= ~(1<<tindex);
+        p->needppr_copy &= ~(1<<tindex);
+        p->needsdtr |= (1<<tindex);
+        p->needsdtr_copy |= (1<<tindex);
+        p->needwdtr |= (1<<tindex);
+        p->needwdtr_copy |= (1<<tindex);
+      }
+      /*
+       * Get the INQUIRY checksum.  We use this on Ultra 160/m
+       * and older devices both.  It allows us to drop speed on any bus type
+       * while at the same time giving us the needed domain validation for
+       * Ultra 160/m
+       *
+       * Note: We only get the checksum and set the SCANNED bit if this is
+       * one of our dtr commands.  If we don't do this, then we end up
+       * getting bad checksum results on the mid-level SCSI code's INQUIRY
+       * commands.
+       */
+      if(p->dev_dtr_cmnd[tindex] == cmd) {
+        unsigned int checksum = 0;
+        int *ibuffer;
+        int i=0;
+
+        ibuffer = (int *)buffer;
+        for( i = 0; i < (cmd->request_bufflen >> 2); i++)
+        {
+          checksum += ibuffer[i];
+        }
+        p->dev_checksum[tindex] = checksum;
+        p->dev_flags[tindex] |= DEVICE_SCANNED;
+        p->dev_flags[tindex] |= DEVICE_PRINT_DTR;
+      }
+#undef WIDE_INQUIRY_BITS
+#undef SYNC_INQUIRY_BITS
+#undef SCSI_VERSION_BITS
+#undef SCSI_DT_BIT
+    }
+  }
+  else if ((scb->flags & SCB_MSGOUT_BITS) != 0)
+  {
+    unsigned short mask;
+    int message_error = FALSE;
+
+    mask = 0x01 << tindex;
+ 
+    /*
+     * Check to see if we get an invalid message or a message error
+     * after failing to negotiate a wide or sync transfer message.
+     */
+    if ((scb->flags & SCB_SENSE) && 
+          ((scb->cmd->sense_buffer[12] == 0x43) ||  /* INVALID_MESSAGE */
+          (scb->cmd->sense_buffer[12] == 0x49))) /* MESSAGE_ERROR  */
+    {
+      message_error = TRUE;
+    }
+
+    if (scb->flags & SCB_MSGOUT_WDTR)
+    {
+      p->dtr_pending &= ~mask;
+      if (message_error)
+      {
+        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+             (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
+        {
+          printk(INFO_LEAD "Device failed to complete Wide Negotiation "
+            "processing and\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "returned a sense error code for invalid message, "
+            "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "Wide negotiation to this device.\n", p->host_no,
+            CTL_OF_SCB(scb));
+        }
+        p->needwdtr &= ~mask;
+        p->needwdtr_copy &= ~mask;
+      }
+    }
+    if (scb->flags & SCB_MSGOUT_SDTR)
+    {
+      p->dtr_pending &= ~mask;
+      if (message_error)
+      {
+        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+             (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
+        {
+          printk(INFO_LEAD "Device failed to complete Sync Negotiation "
+            "processing and\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "returned a sense error code for invalid message, "
+            "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "Sync negotiation to this device.\n", p->host_no,
+            CTL_OF_SCB(scb));
+          p->dev_flags[tindex] &= ~DEVICE_PRINT_DTR;
+        }
+        p->needsdtr &= ~mask;
+        p->needsdtr_copy &= ~mask;
+      }
+    }
+    if (scb->flags & SCB_MSGOUT_PPR)
+    {
+      p->dtr_pending &= ~mask;
+      if(message_error)
+      {
+        if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+             (p->dev_flags[tindex] & DEVICE_PRINT_DTR) )
+        {
+          printk(INFO_LEAD "Device failed to complete Parallel Protocol "
+            "Request processing and\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "returned a sense error code for invalid message, "
+            "disabling future\n", p->host_no, CTL_OF_SCB(scb));
+          printk(INFO_LEAD "Parallel Protocol Request negotiation to this "
+            "device.\n", p->host_no, CTL_OF_SCB(scb));
+        }
+        /*
+         * Disable PPR negotiation and revert back to WDTR and SDTR setup
+         */
+        p->needppr &= ~mask;
+        p->needppr_copy &= ~mask;
+        p->needsdtr |= mask;
+        p->needsdtr_copy |= mask;
+        p->needwdtr |= mask;
+        p->needwdtr_copy |= mask;
+      }
+    }
+  }
+  queue_depth = p->dev_temp_queue_depth[tindex];
+  if (queue_depth >= p->dev_active_cmds[tindex])
+  {
+    scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
+    if (scbp)
+    {
+      if (queue_depth == 1)
+      {
+        /*
+         * Give extra preference to untagged devices, such as CD-R devices
+         * This makes it more likely that a drive *won't* stuff up while
+         * waiting on data at a critical time, such as CD-R writing and
+         * audio CD ripping operations.  Should also benefit tape drives.
+         */
+        scbq_insert_head(&p->waiting_scbs, scbp);
+      }
+      else
+      {
+        scbq_insert_tail(&p->waiting_scbs, scbp);
+      }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Moving SCB from delayed to waiting queue.\n",
+               p->host_no, CTL_OF_SCB(scbp));
+#endif
+      if (queue_depth > p->dev_active_cmds[tindex])
+      {
+        scbp = scbq_remove_head(&p->delayed_scbs[tindex]);
+        if (scbp)
+          scbq_insert_tail(&p->waiting_scbs, scbp);
+      }
+    }
+  }
+  if ( !(scb->tag_action) && (p->tagenable & (1<<tindex)) )
+  {
+    p->dev_temp_queue_depth[tindex] = p->dev_max_queue_depth[tindex];
+  }
+  p->dev_active_cmds[tindex]--;
+  p->activescbs--;
+
+  {
+    int actual;
+
+    /*
+     * XXX: we should actually know how much actually transferred
+     * XXX: for each command, but apparently that's too difficult.
+     * 
+     * We set a lower limit of 512 bytes on the transfer length.  We
+     * ignore anything less than this because we don't have a real
+     * reason to count it.  Read/Writes to tapes are usually about 20K
+     * and disks are a minimum of 512 bytes unless you want to count
+     * non-read/write commands (such as TEST_UNIT_READY) which we don't
+     */
+    actual = scb->sg_length;
+    if ((actual >= 512) && (((cmd->result >> 16) & 0xf) == DID_OK))
+    {
+      struct aic7xxx_xferstats *sp;
+#ifdef AIC7XXX_PROC_STATS
+      long *ptr;
+      int x;
+#endif /* AIC7XXX_PROC_STATS */
+
+      sp = &p->stats[TARGET_INDEX(cmd)];
+
+      /*
+       * For block devices, cmd->request.cmd is always == either READ or
+       * WRITE.  For character devices, this isn't always set properly, so
+       * we check data_cmnd[0].  This catches the conditions for st.c, but
+       * I'm still not sure if request.cmd is valid for sg devices.
+       */
+      if ( (cmd->request.cmd == WRITE) || (cmd->data_cmnd[0] == WRITE_6) ||
+           (cmd->data_cmnd[0] == WRITE_FILEMARKS) )
+      {
+        sp->w_total++;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+        if ( (sp->w_total > 16) && (aic7xxx_verbose > 0xffff) )
+          aic7xxx_verbose &= 0xffff;
+#endif
+#ifdef AIC7XXX_PROC_STATS
+        ptr = sp->w_bins;
+#endif /* AIC7XXX_PROC_STATS */
+      }
+      else
+      {
+        sp->r_total++;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+        if ( (sp->r_total > 16) && (aic7xxx_verbose > 0xffff) )
+          aic7xxx_verbose &= 0xffff;
+#endif
+#ifdef AIC7XXX_PROC_STATS
+        ptr = sp->r_bins;
+#endif /* AIC7XXX_PROC_STATS */
+      }
+#ifdef AIC7XXX_PROC_STATS
+      x = -11;
+      while(actual)
+      {
+        actual >>= 1;
+        x++;
+      }
+      if (x < 0)
+      {
+        ptr[0]++;
+      }
+      else if (x > 7)
+      {
+        ptr[7]++;
+      }
+      else
+      {
+        ptr[x]++;
+      }
+#endif /* AIC7XXX_PROC_STATS */
+    }
+  }
+  aic7xxx_free_scb(p, scb);
+  aic7xxx_queue_cmd_complete(p, cmd);
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_run_done_queue
+ *
+ * Description:
+ *   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_run_done_queue(struct aic7xxx_host *p, /*complete*/ int complete)
+{
+  struct aic7xxx_scb *scb;
+  int i, found = 0;
+
+  for (i = 0; i < p->scb_data->numscbs; i++)
+  {
+    scb = p->scb_data->scb_array[i];
+    if (scb->flags & SCB_QUEUED_FOR_DONE)
+    {
+      if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+        printk(INFO_LEAD "Aborting scb %d\n",
+             p->host_no, CTL_OF_SCB(scb), scb->hscb->tag);
+      found++;
+      aic7xxx_done(p, scb);
+    }
+  }
+  if (aic7xxx_verbose & (VERBOSE_ABORT_RETURN | VERBOSE_RESET_RETURN))
+  {
+    printk(INFO_LEAD "%d commands found and queued for "
+        "completion.\n", p->host_no, -1, -1, -1, found);
+  }
+  if (complete)
+  {
+    aic7xxx_done_cmds_complete(p);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_abort_waiting_scb
+ *
+ * Description:
+ *   Manipulate the waiting for selection list and return the
+ *   scb that follows the one that we remove.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
+    unsigned char scbpos, unsigned char prev)
+{
+  unsigned char curscb, next;
+
+  /*
+   * Select the SCB we want to abort and pull the next pointer out of it.
+   */
+  curscb = aic_inb(p, SCBPTR);
+  aic_outb(p, scbpos, SCBPTR);
+  next = aic_inb(p, SCB_NEXT);
+
+  aic7xxx_add_curscb_to_free_list(p);
+
+  /*
+   * Update the waiting list
+   */
+  if (prev == SCB_LIST_NULL)
+  {
+    /*
+     * First in the list
+     */
+    aic_outb(p, next, WAITING_SCBH);
+  }
+  else
+  {
+    /*
+     * Select the scb that pointed to us and update its next pointer.
+     */
+    aic_outb(p, prev, SCBPTR);
+    aic_outb(p, next, SCB_NEXT);
+  }
+  /*
+   * Point us back at the original scb position and inform the SCSI
+   * system that the command has been aborted.
+   */
+  aic_outb(p, curscb, SCBPTR);
+  return (next);
+}
+
+/*+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, int channel,
+    int lun, unsigned char tag, int flags, int requeue,
+    volatile scb_queue_type *queue)
+{
+  int      found;
+  unsigned char qinpos, qintail;
+  struct aic7xxx_scb *scbp;
+
+  found = 0;
+  qinpos = aic_inb(p, QINPOS);
+  qintail = p->qinfifonext;
+
+  p->qinfifonext = qinpos;
+
+  while (qinpos != qintail)
+  {
+    scbp = p->scb_data->scb_array[p->qinfifo[qinpos++]];
+    if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+    {
+       /*
+        * We found an scb that needs to be removed.
+        */
+       if (requeue && (queue != NULL))
+       {
+         if (scbp->flags & SCB_WAITINGQ)
+         {
+           scbq_remove(queue, scbp);
+           scbq_remove(&p->waiting_scbs, scbp);
+           scbq_remove(&p->delayed_scbs[TARGET_INDEX(scbp->cmd)], scbp);
+           p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
+           p->activescbs++;
+         }
+         scbq_insert_tail(queue, scbp);
+         p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]--;
+         p->activescbs--;
+         scbp->flags |= SCB_WAITINGQ;
+         if ( !(scbp->tag_action & TAG_ENB) )
+         {
+           aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+             TRUE);
+         }
+       }
+       else if (requeue)
+       {
+         p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
+       }
+       else
+       {
+        /*
+         * Preserve any SCB_RECOVERY_SCB flags on this scb then set the
+         * flags we were called with, presumeably so aic7xxx_run_done_queue
+         * can find this scb
+         */
+         scbp->flags = flags | (scbp->flags & SCB_RECOVERY_SCB);
+         if (aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+                                       FALSE) == scbp->hscb->tag)
+         {
+           aic7xxx_index_busy_target(p, scbp->hscb->target_channel_lun,
+             TRUE);
+         }
+       }
+       found++;
+    }
+    else
+    {
+      p->qinfifo[p->qinfifonext++] = scbp->hscb->tag;
+    }
+  }
+  /*
+   * Now that we've done the work, clear out any left over commands in the
+   * qinfifo and update the KERNEL_QINPOS down on the card.
+   *
+   *  NOTE: This routine expect the sequencer to already be paused when
+   *        it is run....make sure it's that way!
+   */
+  qinpos = p->qinfifonext;
+  while(qinpos != qintail)
+  {
+    p->qinfifo[qinpos++] = SCB_LIST_NULL;
+  }
+  if (p->features & AHC_QUEUE_REGS)
+    aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+  else
+    aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+
+  return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_scb_on_qoutfifo
+ *
+ * Description:
+ *   Is the scb that was passed to us currently on the qoutfifo?
+ *-F*************************************************************************/
+static int
+aic7xxx_scb_on_qoutfifo(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  int i=0;
+
+  while(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] != SCB_LIST_NULL)
+  {
+    if(p->qoutfifo[(p->qoutfifonext + i) & 0xff ] == scb->hscb->tag)
+      return TRUE;
+    else
+      i++;
+  }
+  return FALSE;
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_reset_device
+ *
+ * Description:
+ *   The device at the given target/channel has been reset.  Abort
+ *   all active and queued scbs for that target/channel.  This function
+ *   need not worry about linked next pointers because if was a MSG_ABORT_TAG
+ *   then we had a tagged command (no linked next), if it was MSG_ABORT or
+ *   MSG_BUS_DEV_RESET then the device won't know about any commands any more
+ *   and no busy commands will exist, and if it was a bus reset, then nothing
+ *   knows about any linked next commands any more.  In all cases, we don't
+ *   need to worry about the linked next or busy scb, we just need to clear
+ *   them.
+ *-F*************************************************************************/
+static void
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, int channel,
+                     int lun, unsigned char tag)
+{
+  struct aic7xxx_scb *scbp;
+  unsigned char active_scb, tcl;
+  int i = 0, j, init_lists = FALSE;
+
+  /*
+   * Restore this when we're done
+   */
+  active_scb = aic_inb(p, SCBPTR);
+
+  if (aic7xxx_verbose & (VERBOSE_RESET_PROCESS | VERBOSE_ABORT_PROCESS))
+    printk(INFO_LEAD "Reset device, active_scb %d\n",
+         p->host_no, channel, target, lun, active_scb);
+  /*
+   * Deal with the busy target and linked next issues.
+   */
+  {
+    int min_target, max_target;
+    struct aic7xxx_scb *scbp, *prev_scbp;
+
+    /* Make all targets 'relative' to bus A. */
+    if (target == ALL_TARGETS)
+    {
+      switch (channel)
+      {
+        case 0:
+                 min_target = 0;
+                 max_target = (p->features & AHC_WIDE) ? 15 : 7;
+                 break;
+        case 1:
+                 min_target = 8;
+                 max_target = 15;
+                 break;
+        case ALL_CHANNELS:
+        default:
+                 min_target = 0;
+                 max_target = (p->features & (AHC_TWIN|AHC_WIDE)) ? 15 : 7;
+                 break;
+      }
+    }
+    else
+    { 
+      min_target = target | (channel << 3);
+      max_target = min_target;
+    }
+
+
+    for (i = min_target; i <= max_target; i++)
+    {
+      if ( i == p->scsi_id )
+      {
+        continue;
+      }
+      if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+        printk(INFO_LEAD "Cleaning up status information "
+          "and delayed_scbs.\n", p->host_no, channel, i, lun);
+      p->dev_flags[i] &= ~(BUS_DEVICE_RESET_PENDING | DEVICE_PARITY_ERROR);
+      if ( tag == SCB_LIST_NULL )
+      {
+        p->dev_flags[i] |= DEVICE_PRINT_DTR | DEVICE_RESET_DELAY;
+        p->dev_expires[i] = jiffies + (4 * HZ);
+        p->dev_timer_active |= (0x01 << i);
+        p->dev_last_queue_full_count[i] = 0;
+        p->dev_last_queue_full[i] = 0;
+        p->dev_temp_queue_depth[i] =
+          p->dev_max_queue_depth[i];
+      }
+      for(j=0; j<MAX_LUNS; j++)
+      {
+        if (channel == 1)
+          tcl = ((i << 4) & 0x70) | (channel << 3) | j;
+        else
+          tcl = (i << 4) | (channel << 3) | j;
+        if ( (aic7xxx_index_busy_target(p, tcl, FALSE) == tag) ||
+             (tag == SCB_LIST_NULL) )
+          aic7xxx_index_busy_target(p, tcl, /* unbusy */ TRUE);
+      }
+      j = 0; 
+      prev_scbp = NULL; 
+      scbp = p->delayed_scbs[i].head;
+      while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) )
+      {
+        prev_scbp = scbp;
+        scbp = scbp->q_next;
+        if ( prev_scbp == scbp )
+        {
+          if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+            printk(WARN_LEAD "Yikes!! scb->q_next == scb "
+              "in the delayed_scbs queue!\n", p->host_no, channel, i, lun);
+          scbp = NULL;
+          prev_scbp->q_next = NULL;
+          p->delayed_scbs[i].tail = prev_scbp;
+        }
+        if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
+        {
+          scbq_remove(&p->delayed_scbs[i], prev_scbp);
+          if (prev_scbp->flags & SCB_WAITINGQ)
+          {
+            p->dev_active_cmds[i]++;
+            p->activescbs++;
+          }
+          prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+          prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+        }
+      }
+      if ( j > (p->scb_data->maxscbs + 1) )
+      {
+        if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+          printk(WARN_LEAD "Yikes!! There's a loop in the "
+            "delayed_scbs queue!\n", p->host_no, channel, i, lun);
+        scbq_init(&p->delayed_scbs[i]);
+      }
+      if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) ||
+            time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
+      {
+        mod_timer(&p->dev_timer, p->dev_expires[i]);
+        p->dev_timer_active |= (0x01 << MAX_TARGETS);
+      }
+    }
+  }
+
+  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+    printk(INFO_LEAD "Cleaning QINFIFO.\n", p->host_no, channel, target, lun );
+  aic7xxx_search_qinfifo(p, target, channel, lun, tag,
+      SCB_RESET | SCB_QUEUED_FOR_DONE, /* requeue */ FALSE, NULL);
+
+/*
+ *  Search the waiting_scbs queue for matches, this catches any SCB_QUEUED
+ *  ABORT/RESET commands.
+ */
+  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+    printk(INFO_LEAD "Cleaning waiting_scbs.\n", p->host_no, channel,
+      target, lun );
+  {
+    struct aic7xxx_scb *scbp, *prev_scbp;
+
+    j = 0; 
+    prev_scbp = NULL; 
+    scbp = p->waiting_scbs.head;
+    while ( (scbp != NULL) && (j++ <= (p->scb_data->numscbs + 1)) )
+    {
+      prev_scbp = scbp;
+      scbp = scbp->q_next;
+      if ( prev_scbp == scbp )
+      {
+        if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+          printk(WARN_LEAD "Yikes!! scb->q_next == scb "
+            "in the waiting_scbs queue!\n", p->host_no, CTL_OF_SCB(scbp));
+        scbp = NULL;
+        prev_scbp->q_next = NULL;
+        p->waiting_scbs.tail = prev_scbp;
+      }
+      if (aic7xxx_match_scb(p, prev_scbp, target, channel, lun, tag))
+      {
+        scbq_remove(&p->waiting_scbs, prev_scbp);
+        if (prev_scbp->flags & SCB_WAITINGQ)
+        {
+          p->dev_active_cmds[TARGET_INDEX(prev_scbp->cmd)]++;
+          p->activescbs++;
+        }
+        prev_scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+        prev_scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+      }
+    }
+    if ( j > (p->scb_data->maxscbs + 1) )
+    {
+      if (aic7xxx_verbose & (VERBOSE_ABORT | VERBOSE_RESET))
+        printk(WARN_LEAD "Yikes!! There's a loop in the "
+          "waiting_scbs queue!\n", p->host_no, channel, target, lun);
+      scbq_init(&p->waiting_scbs);
+    }
+  }
+
+
+  /*
+   * Search waiting for selection list.
+   */
+  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+    printk(INFO_LEAD "Cleaning waiting for selection "
+      "list.\n", p->host_no, channel, target, lun);
+  {
+    unsigned char next, prev, scb_index;
+
+    next = aic_inb(p, WAITING_SCBH);  /* Start at head of list. */
+    prev = SCB_LIST_NULL;
+    j = 0;
+    while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
+    {
+      aic_outb(p, next, SCBPTR);
+      scb_index = aic_inb(p, SCB_TAG);
+      if (scb_index >= p->scb_data->numscbs)
+      {
+       /*
+        * No aic7xxx_verbose check here.....we want to see this since it
+        * means either the kernel driver or the sequencer screwed things up
+        */
+        printk(WARN_LEAD "Waiting List inconsistency; SCB index=%d, "
+          "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
+          p->scb_data->numscbs);
+        next = aic_inb(p, SCB_NEXT);
+        aic7xxx_add_curscb_to_free_list(p);
+      }
+      else
+      {
+        scbp = p->scb_data->scb_array[scb_index];
+        if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+        {
+          next = aic7xxx_abort_waiting_scb(p, scbp, next, prev);
+          if (scbp->flags & SCB_WAITINGQ)
+          {
+            p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
+            p->activescbs++;
+          }
+          scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+          scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+          if (prev == SCB_LIST_NULL)
+          {
+            /*
+             * This is either the first scb on the waiting list, or we
+             * have already yanked the first and haven't left any behind.
+             * Either way, we need to turn off the selection hardware if
+             * it isn't already off.
+             */
+            aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+            aic_outb(p, CLRSELTIMEO, CLRSINT1);
+          }
+        }
+        else
+        {
+          prev = next;
+          next = aic_inb(p, SCB_NEXT);
+        }
+      }
+    }
+    if ( j > (p->scb_data->maxscbs + 1) )
+    {
+      printk(WARN_LEAD "Yikes!!  There is a loop in the waiting for "
+        "selection list!\n", p->host_no, channel, target, lun);
+      init_lists = TRUE;
+    }
+  }
+
+  /*
+   * Go through disconnected list and remove any entries we have queued
+   * for completion, zeroing their control byte too.
+   */
+  if (aic7xxx_verbose & (VERBOSE_ABORT_PROCESS | VERBOSE_RESET_PROCESS))
+    printk(INFO_LEAD "Cleaning disconnected scbs "
+      "list.\n", p->host_no, channel, target, lun);
+  if (p->flags & AHC_PAGESCBS)
+  {
+    unsigned char next, prev, scb_index;
+
+    next = aic_inb(p, DISCONNECTED_SCBH);
+    prev = SCB_LIST_NULL;
+    j = 0;
+    while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
+    {
+      aic_outb(p, next, SCBPTR);
+      scb_index = aic_inb(p, SCB_TAG);
+      if (scb_index > p->scb_data->numscbs)
+      {
+        printk(WARN_LEAD "Disconnected List inconsistency; SCB index=%d, "
+          "numscbs=%d\n", p->host_no, channel, target, lun, scb_index,
+          p->scb_data->numscbs);
+        next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
+      }
+      else
+      {
+        scbp = p->scb_data->scb_array[scb_index];
+        if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+        {
+          next = aic7xxx_rem_scb_from_disc_list(p, next, prev);
+          if (scbp->flags & SCB_WAITINGQ)
+          {
+            p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
+            p->activescbs++;
+          }
+          scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+          scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+          scbp->hscb->control = 0;
+        }
+        else
+        {
+          prev = next;
+          next = aic_inb(p, SCB_NEXT);
+        }
+      }
+    }
+    if ( j > (p->scb_data->maxscbs + 1) )
+    {
+      printk(WARN_LEAD "Yikes!!  There is a loop in the disconnected list!\n",
+        p->host_no, channel, target, lun);
+      init_lists = TRUE;
+    }
+  }
+
+  /*
+   * Walk the free list making sure no entries on the free list have
+   * a valid SCB_TAG value or SCB_CONTROL byte.
+   */
+  if (p->flags & AHC_PAGESCBS)
+  {
+    unsigned char next;
+
+    j = 0;
+    next = aic_inb(p, FREE_SCBH);
+    if ( (next >= p->scb_data->maxhscbs) && (next != SCB_LIST_NULL) )
+    {
+      printk(WARN_LEAD "Bogus FREE_SCBH!.\n", p->host_no, channel,
+        target, lun);
+      init_lists = TRUE;
+      next = SCB_LIST_NULL;
+    }
+    while ( (next != SCB_LIST_NULL) && (j++ <= (p->scb_data->maxscbs + 1)) )
+    {
+      aic_outb(p, next, SCBPTR);
+      if (aic_inb(p, SCB_TAG) < p->scb_data->numscbs)
+      {
+        printk(WARN_LEAD "Free list inconsistency!.\n", p->host_no, channel,
+          target, lun);
+        init_lists = TRUE;
+        next = SCB_LIST_NULL;
+      }
+      else
+      {
+        aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+        aic_outb(p, 0, SCB_CONTROL);
+        next = aic_inb(p, SCB_NEXT);
+      }
+    }
+    if ( j > (p->scb_data->maxscbs + 1) )
+    {
+      printk(WARN_LEAD "Yikes!!  There is a loop in the free list!\n",
+        p->host_no, channel, target, lun);
+      init_lists = TRUE;
+    }
+  }
+
+  /*
+   * Go through the hardware SCB array looking for commands that
+   * were active but not on any list.
+   */
+  if (init_lists)
+  {
+    aic_outb(p, SCB_LIST_NULL, FREE_SCBH);
+    aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
+    aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
+  }
+  for (i = p->scb_data->maxhscbs - 1; i >= 0; i--)
+  {
+    unsigned char scbid;
+
+    aic_outb(p, i, SCBPTR);
+    if (init_lists)
+    {
+      aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+      aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
+      aic_outb(p, 0, SCB_CONTROL);
+      aic7xxx_add_curscb_to_free_list(p);
+    }
+    else
+    {
+      scbid = aic_inb(p, SCB_TAG);
+      if (scbid < p->scb_data->numscbs)
+      {
+        scbp = p->scb_data->scb_array[scbid];
+        if (aic7xxx_match_scb(p, scbp, target, channel, lun, tag))
+        {
+          aic_outb(p, 0, SCB_CONTROL);
+          aic_outb(p, SCB_LIST_NULL, SCB_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.
+   * Any commands we find here we know this about, it wasn't on any queue,
+   * it wasn't in the qinfifo, it wasn't in the disconnected or waiting
+   * lists, so it really must have been a paged out SCB.  In that case,
+   * we shouldn't need to bother with updating any counters, just mark
+   * the correct flags and go on.
+   */
+  for (i = 0; i < p->scb_data->numscbs; i++)
+  {
+    scbp = p->scb_data->scb_array[i];
+    if ((scbp->flags & SCB_ACTIVE) &&
+        aic7xxx_match_scb(p, scbp, target, channel, lun, tag) &&
+        !aic7xxx_scb_on_qoutfifo(p, scbp))
+    {
+      if (scbp->flags & SCB_WAITINGQ)
+      {
+        scbq_remove(&p->waiting_scbs, scbp);
+        scbq_remove(&p->delayed_scbs[TARGET_INDEX(scbp->cmd)], scbp);
+        p->dev_active_cmds[TARGET_INDEX(scbp->cmd)]++;
+        p->activescbs++;
+      }
+      scbp->flags |= SCB_RESET | SCB_QUEUED_FOR_DONE;
+      scbp->flags &= ~(SCB_ACTIVE | SCB_WAITINGQ);
+    }
+  }
+
+  aic_outb(p, active_scb, SCBPTR);
+}
+
+
+/*+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. */
+  aic_outb(p, CLRSELDO | CLRSELDI | CLRSELINGO, CLRSINT0);
+  aic_outb(p, CLRSELTIMEO | CLRATNO | CLRSCSIRSTI | CLRBUSFREE | CLRSCSIPERR |
+       CLRPHASECHG | CLRREQINIT, CLRSINT1);
+  aic_outb(p, CLRSCSIINT | CLRSEQINT | CLRBRKADRINT | CLRPARERR, CLRINT);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_reset_current_bus
+ *
+ * Description:
+ *   Reset the current SCSI bus.
+ *-F*************************************************************************/
+static void
+aic7xxx_reset_current_bus(struct aic7xxx_host *p)
+{
+
+  /* Disable reset interrupts. */
+  aic_outb(p, aic_inb(p, SIMODE1) & ~ENSCSIRST, SIMODE1);
+
+  /* Turn off the bus' current operations, after all, we shouldn't have any
+   * valid commands left to cause a RSELI and SELO once we've tossed the
+   * bus away with this reset, so we might as well shut down the sequencer
+   * until the bus is restarted as oppossed to saving the current settings
+   * and restoring them (which makes no sense to me). */
+
+  /* Turn on the bus reset. */
+  aic_outb(p, aic_inb(p, SCSISEQ) | SCSIRSTO, SCSISEQ);
+  while ( (aic_inb(p, SCSISEQ) & SCSIRSTO) == 0)
+    mdelay(5);
+
+  /*
+   * Some of the new Ultra2 chipsets need a longer delay after a chip
+   * reset than just the init setup creates, so we have to delay here
+   * before we go into a reset in order to make the chips happy.
+   */
+  if (p->features & AHC_ULTRA2)
+    mdelay(250);
+  else
+    mdelay(50);
+
+  /* Turn off the bus reset. */
+  aic_outb(p, 0, SCSISEQ);
+  mdelay(10);
+
+  aic7xxx_clear_intstat(p);
+  /* Re-enable reset interrupts. */
+  aic_outb(p, aic_inb(p, SIMODE1) | ENSCSIRST, SIMODE1);
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_reset_channel
+ *
+ * Description:
+ *   Reset the channel.
+ *-F*************************************************************************/
+static void
+aic7xxx_reset_channel(struct aic7xxx_host *p, int channel, int initiate_reset)
+{
+  unsigned long offset_min, offset_max;
+  unsigned char sblkctl;
+  int cur_channel;
+
+  if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+    printk(INFO_LEAD "Reset channel called, %s initiate reset.\n",
+      p->host_no, channel, -1, -1, (initiate_reset==TRUE) ? "will" : "won't" );
+
+
+  if (channel == 1)
+  {
+    p->needsdtr |= (p->needsdtr_copy & 0xFF00);
+    p->dtr_pending &= 0x00FF;
+    offset_min = 8;
+    offset_max = 16;
+  }
+  else
+  {
+    if (p->features & AHC_TWIN)
+    {
+      /* Channel A */
+      p->needsdtr |= (p->needsdtr_copy & 0x00FF);
+      p->dtr_pending &= 0xFF00;
+      offset_min = 0;
+      offset_max = 8;
+    }
+    else
+    {
+      p->needppr = p->needppr_copy;
+      p->needsdtr = p->needsdtr_copy;
+      p->needwdtr = p->needwdtr_copy;
+      p->dtr_pending = 0x0;
+      offset_min = 0;
+      if (p->features & AHC_WIDE)
+      {
+        offset_max = 16;
+      }
+      else
+      {
+        offset_max = 8;
+      }
+    }
+  }
+
+  while (offset_min < offset_max)
+  {
+    /*
+     * Revert to async/narrow transfers until we renegotiate.
+     */
+    aic_outb(p, 0, TARG_SCSIRATE + offset_min);
+    if (p->features & AHC_ULTRA2)
+    {
+      aic_outb(p, 0, TARG_OFFSET + offset_min);
+    }
+    offset_min++;
+  }
+
+  /*
+   * Reset the bus and unpause/restart the controller
+   */
+  sblkctl = aic_inb(p, SBLKCTL);
+  if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+    cur_channel = (sblkctl & SELBUSB) >> 3;
+  else
+    cur_channel = 0;
+  if ( (cur_channel != channel) && (p->features & AHC_TWIN) )
+  {
+    /*
+     * Case 1: Command for another bus is active
+     */
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+      printk(INFO_LEAD "Stealthily resetting idle channel.\n", p->host_no,
+        channel, -1, -1);
+    /*
+     * Stealthily reset the other bus without upsetting the current bus.
+     */
+    aic_outb(p, sblkctl ^ SELBUSB, SBLKCTL);
+    aic_outb(p, aic_inb(p, SIMODE1) & ~ENBUSFREE, SIMODE1);
+    if (initiate_reset)
+    {
+      aic7xxx_reset_current_bus(p);
+    }
+    aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
+    aic7xxx_clear_intstat(p);
+    aic_outb(p, sblkctl, SBLKCTL);
+  }
+  else
+  {
+    /*
+     * Case 2: A command from this bus is active or we're idle.
+     */
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+      printk(INFO_LEAD "Resetting currently active channel.\n", p->host_no,
+        channel, -1, -1);
+    aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
+      SIMODE1);
+    p->flags &= ~AHC_HANDLING_REQINITS;
+    p->msg_type = MSG_TYPE_NONE;
+    p->msg_len = 0;
+    if (initiate_reset)
+    {
+      aic7xxx_reset_current_bus(p);
+    }
+    aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP), SCSISEQ);
+    aic7xxx_clear_intstat(p);
+  }
+  if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+    printk(INFO_LEAD "Channel reset\n", p->host_no, channel, -1, -1);
+  /*
+   * Clean up all the state information for the pending transactions
+   * on this bus.
+   */
+  aic7xxx_reset_device(p, ALL_TARGETS, channel, ALL_LUNS, SCB_LIST_NULL);
+
+  if ( !(p->features & AHC_TWIN) )
+  {
+    restart_sequencer(p);
+  }
+
+  return;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_run_waiting_queues
+ *
+ * Description:
+ *   Scan the awaiting_scbs queue downloading and starting as many
+ *   scbs as we can.
+ *-F*************************************************************************/
+static void
+aic7xxx_run_waiting_queues(struct aic7xxx_host *p)
+{
+  struct aic7xxx_scb *scb;
+  int tindex;
+  int sent;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags = 0;
+#endif
+
+
+  if (p->waiting_scbs.head == NULL)
+    return;
+
+  sent = 0;
+
+  /*
+   * First handle SCBs that are waiting but have been assigned a slot.
+   */
+  DRIVER_LOCK
+  while ((scb = scbq_remove_head(&p->waiting_scbs)) != NULL)
+  {
+    tindex = TARGET_INDEX(scb->cmd);
+    if ( !scb->tag_action && (p->tagenable & (1<<tindex)) )
+    {
+      p->dev_temp_queue_depth[tindex] = 1;
+    }
+    if ( (p->dev_active_cmds[tindex] >=
+          p->dev_temp_queue_depth[tindex]) ||
+         (p->dev_flags[tindex] & (DEVICE_RESET_DELAY|DEVICE_WAS_BUSY)) ||
+         (p->flags & AHC_RESET_DELAY) )
+    {
+      scbq_insert_tail(&p->delayed_scbs[tindex], scb);
+    }
+    else
+    {
+        scb->flags &= ~SCB_WAITINGQ;
+        p->dev_active_cmds[tindex]++;
+        p->activescbs++;
+        if ( !(scb->tag_action) )
+        {
+          aic7xxx_busy_target(p, scb);
+        }
+        p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+        sent++;
+    }
+  }
+  if (sent)
+  {
+    if (p->features & AHC_QUEUE_REGS)
+      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+    else
+    {
+      pause_sequencer(p);
+      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+      unpause_sequencer(p, FALSE);
+    }
+    if (p->activescbs > p->max_activescbs)
+      p->max_activescbs = p->activescbs;
+  }
+  DRIVER_UNLOCK
+}
+
+#ifdef CONFIG_PCI
+
+#define  DPE 0x80
+#define  SSE 0x40
+#define  RMA 0x20
+#define  RTA 0x10
+#define  STA 0x08
+#define  DPR 0x01
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_pci_intr
+ *
+ * Description:
+ *   Check the scsi card for PCI errors and clear the interrupt
+ *
+ *   NOTE: If you don't have this function and a 2940 card encounters
+ *         a PCI error condition, the machine will end up locked as the
+ *         interrupt handler gets slammed with non-stop PCI error interrupts
+ *-F*************************************************************************/
+static void
+aic7xxx_pci_intr(struct aic7xxx_host *p)
+{
+  unsigned char status1;
+
+  pci_read_config_byte(p->pdev, PCI_STATUS + 1, &status1);
+
+  if ( (status1 & DPE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Data Parity Error during PCI address or PCI write"
+      "phase.\n", p->host_no, -1, -1, -1);
+  if ( (status1 & SSE) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Signal System Error Detected\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & RMA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Received a PCI Master Abort\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & RTA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Received a PCI Target Abort\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & STA) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Signaled a PCI Target Abort\n", p->host_no,
+      -1, -1, -1);
+  if ( (status1 & DPR) && (aic7xxx_verbose & VERBOSE_MINOR_ERROR) )
+    printk(WARN_LEAD "Data Parity Error has been reported via PCI pin "
+      "PERR#\n", p->host_no, -1, -1, -1);
+  
+  pci_write_config_byte(p->pdev, PCI_STATUS + 1, status1);
+  if (status1 & (DPR|RMA|RTA))
+    aic_outb(p,  CLRPARERR, CLRINT);
+
+  if ( (aic7xxx_panic_on_abort) && (p->spurious_int > 500) )
+    aic7xxx_panic_abort(p, NULL);
+
+}
+#endif /* CONFIG_PCI */
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_timer
+ *
+ * Description:
+ *   Take expired extries off of delayed queues and place on waiting queue
+ *   then run waiting queue to start commands.
+ ***************************************************************************/
+static void
+aic7xxx_timer(struct aic7xxx_host *p)
+{
+  int i, j;
+  unsigned long cpu_flags = 0;
+  struct aic7xxx_scb *scb;
+
+  spin_lock_irqsave(&io_request_lock, cpu_flags);
+  p->dev_timer_active &= ~(0x01 << MAX_TARGETS);
+  if ( (p->dev_timer_active & (0x01 << p->scsi_id)) &&
+       time_after_eq(jiffies, p->dev_expires[p->scsi_id]) )
+  {
+    p->flags &= ~AHC_RESET_DELAY;
+    p->dev_timer_active &= ~(0x01 << p->scsi_id);
+  }
+  for(i=0; i<MAX_TARGETS; i++)
+  {
+    if ( (i != p->scsi_id) &&
+         (p->dev_timer_active & (0x01 << i)) &&
+         time_after_eq(jiffies, p->dev_expires[i]) )
+    {
+      p->dev_timer_active &= ~(0x01 << i);
+      p->dev_flags[i] &= ~(DEVICE_RESET_DELAY|DEVICE_WAS_BUSY);
+      p->dev_temp_queue_depth[i] =  p->dev_max_queue_depth[i];
+      j = 0;
+      while ( ((scb = scbq_remove_head(&p->delayed_scbs[i])) != NULL) &&
+              (j++ < p->scb_data->numscbs) )
+      {
+        scbq_insert_tail(&p->waiting_scbs, scb);
+      }
+      if (j == p->scb_data->numscbs)
+      {
+        printk(INFO_LEAD "timer: Yikes, loop in delayed_scbs list.\n",
+          p->host_no, 0, i, -1);
+        scbq_init(&p->delayed_scbs[i]);
+        scbq_init(&p->waiting_scbs);
+        /*
+         * Well, things are screwed now, wait for a reset to clean the junk
+         * out.
+         */
+      }
+    }
+    else if ( p->dev_timer_active & (0x01 << i) )
+    {
+      if ( p->dev_timer_active & (0x01 << MAX_TARGETS) )
+      {
+        if ( time_after_eq(p->dev_timer.expires, p->dev_expires[i]) )
+        {
+          p->dev_timer.expires = p->dev_expires[i];
+        }
+      }
+      else
+      {
+        p->dev_timer.expires = p->dev_expires[i];
+        p->dev_timer_active |= (0x01 << MAX_TARGETS);
+      }
+    }
+  }
+  if ( p->dev_timer_active & (0x01 << MAX_TARGETS) )
+  {
+    add_timer(&p->dev_timer);
+  }
+
+  aic7xxx_run_waiting_queues(p);
+  spin_unlock_irqrestore(&io_request_lock, cpu_flags);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_construct_ppr
+ *
+ * Description:
+ *   Build up a Parallel Protocol Request message for use with SCSI-3
+ *   devices.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_ppr(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  int tindex = TARGET_INDEX(scb->cmd);
+
+  p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+  p->msg_buf[p->msg_index++] = MSG_EXT_PPR_LEN;
+  p->msg_buf[p->msg_index++] = MSG_EXT_PPR;
+  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_period;
+  p->msg_buf[p->msg_index++] = 0;
+  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_offset;
+  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_width;
+  p->msg_buf[p->msg_index++] = p->transinfo[tindex].goal_options;
+  p->msg_len += 8;
+}
+
+/*+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, unsigned char period,
+        unsigned char offset)
+{
+  p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+  p->msg_buf[p->msg_index++] = MSG_EXT_SDTR_LEN;
+  p->msg_buf[p->msg_index++] = MSG_EXT_SDTR;
+  p->msg_buf[p->msg_index++] = period;
+  p->msg_buf[p->msg_index++] = offset;
+  p->msg_len += 5;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_construct_wdtr
+ *
+ * Description:
+ *   Constucts a wide data transfer message in the message buffer
+ *   on the sequencer.
+ *-F*************************************************************************/
+static void
+aic7xxx_construct_wdtr(struct aic7xxx_host *p, unsigned char bus_width)
+{
+  p->msg_buf[p->msg_index++] = MSG_EXTENDED;
+  p->msg_buf[p->msg_index++] = MSG_EXT_WDTR_LEN;
+  p->msg_buf[p->msg_index++] = MSG_EXT_WDTR;
+  p->msg_buf[p->msg_index++] = bus_width;
+  p->msg_len += 4;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_calc_residual
+ *
+ * Description:
+ *   Calculate the residual data not yet transferred.
+ *-F*************************************************************************/
+static void
+aic7xxx_calculate_residual (struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  struct aic7xxx_hwscb *hscb;
+  Scsi_Cmnd *cmd;
+  int actual, i;
+
+  cmd = scb->cmd;
+  hscb = scb->hscb;
+
+  /*
+   *  Don't destroy valid residual information with
+   *  residual coming from a check sense operation.
+   */
+  if (((scb->hscb->control & DISCONNECTED) == 0) &&
+      (scb->flags & SCB_SENSE) == 0)
+  {
+    /*
+     *  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 = scb->sg_length;
+    for (i=1; i < hscb->residual_SG_segment_count; i++)
+    {
+      actual -= scb->sg_list[scb->sg_count - i].length;
+    }
+    actual -= (hscb->residual_data_count[2] << 16) |
+              (hscb->residual_data_count[1] <<  8) |
+              hscb->residual_data_count[0];
+
+    if (actual < cmd->underflow)
+    {
+      if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+        printk(INFO_LEAD "Underflow - Wanted %u, %s %u, residual SG "
+          "count %d.\n", p->host_no, CTL_OF_SCB(scb), cmd->underflow,
+          (cmd->request.cmd == WRITE) ? "wrote" : "read", actual,
+          hscb->residual_SG_segment_count);
+      aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+      aic7xxx_status(cmd) = hscb->target_status;
+    }
+  }
+
+  /*
+   * Clean out the residual information in the SCB for the
+   * next consumer.
+   */
+  hscb->residual_data_count[2] = 0;
+  hscb->residual_data_count[1] = 0;
+  hscb->residual_data_count[0] = 0;
+  hscb->residual_SG_segment_count = 0;
+}
+
+/*+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, int channel)
+{
+  unsigned short targ_mask;
+  unsigned char tindex = target;
+
+  tindex |= ((channel & 0x01) << 3);
+
+  targ_mask = (0x01 << tindex);
+  /*
+   * Go back to async/narrow transfers and renegotiate.
+   */
+  p->needppr |= (p->needppr_copy & targ_mask);
+  p->needsdtr |= (p->needsdtr_copy & targ_mask);
+  p->needwdtr |= (p->needwdtr_copy & targ_mask);
+  p->dtr_pending &= ~targ_mask;
+  aic_outb(p, 0, TARG_SCSIRATE + tindex);
+  if (p->features & AHC_ULTRA2)
+    aic_outb(p, 0, TARG_OFFSET + tindex);
+  aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+  if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+    printk(INFO_LEAD "Bus Device Reset delivered.\n", p->host_no, channel,
+      target, -1);
+  aic7xxx_run_done_queue(p, /*complete*/ TRUE);
+}
+
+/*+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, lun, tindex;
+  unsigned char queue_flag = FALSE;
+  char channel;
+
+  target = ((aic_inb(p, SAVED_TCL) >> 4) & 0x0f);
+  if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+    channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+  else
+    channel = 0;
+  tindex = target + (channel << 3);
+  lun = aic_inb(p, SAVED_TCL) & 0x07;
+  target_mask = (0x01 << tindex);
+
+  /*
+   * Go ahead and clear the SEQINT now, that avoids any interrupt race
+   * conditions later on in case we enable some other interrupt.
+   */
+  aic_outb(p, CLRSEQINT, CLRINT);
+  switch (intstat & SEQINT_MASK)
+  {
+    case NO_MATCH:
+      {
+        aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
+                 SCSISEQ);
+        printk(WARN_LEAD "No active SCB for reconnecting target - Issuing "
+               "BUS DEVICE RESET.\n", p->host_no, channel, target, lun);
+        printk(WARN_LEAD "      SAVED_TCL=0x%x, ARG_1=0x%x, SEQADDR=0x%x\n",
+               p->host_no, channel, target, lun,
+               aic_inb(p, SAVED_TCL), aic_inb(p, ARG_1),
+               (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
+        if (aic7xxx_panic_on_abort)
+          aic7xxx_panic_abort(p, NULL);
+      }
+      break;
+
+    case SEND_REJECT:
+      {
+        if (aic7xxx_verbose & VERBOSE_MINOR_ERROR)
+          printk(INFO_LEAD "Rejecting unknown message (0x%x) received from "
+            "target, SEQ_FLAGS=0x%x\n", p->host_no, channel, target, lun,
+            aic_inb(p, ACCUM), aic_inb(p, SEQ_FLAGS));
+      }
+      break;
+
+    case NO_IDENT:
+      {
+        /*
+         * The reconnecting target either did not send an identify
+         * message, or did, but we didn't find an 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.
+         */
+        if (aic7xxx_verbose & (VERBOSE_SEQINT | VERBOSE_RESET_MID))
+          printk(INFO_LEAD "Target did not send an IDENTIFY message; "
+            "LASTPHASE 0x%x, SAVED_TCL 0x%x\n", p->host_no, channel, target,
+            lun, aic_inb(p, LASTPHASE), aic_inb(p, SAVED_TCL));
+
+        aic7xxx_reset_channel(p, channel, /*initiate reset*/ TRUE);
+        aic7xxx_run_done_queue(p, TRUE);
+
+      }
+      break;
+
+    case BAD_PHASE:
+      if (aic_inb(p, LASTPHASE) == P_BUSFREE)
+      {
+        if (aic7xxx_verbose & VERBOSE_SEQINT)
+          printk(INFO_LEAD "Missed busfree.\n", p->host_no, channel,
+            target, lun);
+        restart_sequencer(p);
+      }
+      else
+      {
+        if (aic7xxx_verbose & VERBOSE_SEQINT)
+          printk(INFO_LEAD "Unknown scsi bus phase, continuing\n", p->host_no,
+            channel, target, lun);
+      }
+      break;
+
+    case EXTENDED_MSG:
+      {
+        p->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+        p->msg_len = 0;
+        p->msg_index = 0;
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+        if (aic7xxx_verbose > 0xffff)
+          printk(INFO_LEAD "Enabling REQINITs for MSG_IN\n", p->host_no,
+                 channel, target, lun);
+#endif
+
+       /*      
+        * To actually receive the message, simply turn on
+        * REQINIT interrupts and let our interrupt handler
+        * do the rest (REQINIT should already be true).
+        */
+        p->flags |= AHC_HANDLING_REQINITS;
+        aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
+
+       /*
+        * We don't want the sequencer unpaused yet so we return early
+        */
+        return;
+      }
+
+    case REJECT_MSG:
+      {
+        /*
+         * 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;
+        unsigned char last_msg;
+
+        scb_index = aic_inb(p, SCB_TAG);
+        scb = p->scb_data->scb_array[scb_index];
+        last_msg = aic_inb(p, LAST_MSG);
+
+        if ( (last_msg == MSG_IDENTIFYFLAG) &&
+             (scb->tag_action) &&
+            !(scb->flags & SCB_MSGOUT_BITS) )
+        {
+          if (scb->tag_action == MSG_ORDERED_Q_TAG)
+          {
+            /*
+             * OK...the device seems able to accept tagged commands, but
+             * not ordered tag commands, only simple tag commands.  So, we
+             * disable ordered tag commands and go on with life just like
+             * normal.
+             */
+            p->orderedtag &= ~target_mask;
+            scb->tag_action = MSG_SIMPLE_Q_TAG;
+            scb->hscb->control &= ~SCB_TAG_TYPE;
+            scb->hscb->control |= MSG_SIMPLE_Q_TAG;
+            aic_outb(p, scb->hscb->control, SCB_CONTROL);
+            /*
+             * OK..we set the tag type to simple tag command, now we re-assert
+             * ATNO and hope this will take us into the identify phase again
+             * so we can resend the tag type and info to the device.
+             */
+            aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
+            aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+          }
+          else if (scb->tag_action == MSG_SIMPLE_Q_TAG)
+          {
+            unsigned char i, reset = 0;
+            struct aic7xxx_scb *scbp;
+            int old_verbose;
+            /*
+             * Hmmmm....the device is flaking out on tagged commands.  The
+             * bad thing is that we already have tagged commands enabled in
+             * the device struct in the mid level code.  We also have a queue
+             * set according to the tagged queue depth.  Gonna have to live
+             * with it by controlling our queue depth internally and making
+             * sure we don't set the tagged command flag any more.
+             */
+            p->tagenable &= ~target_mask;
+            p->orderedtag &= ~target_mask;
+            p->dev_max_queue_depth[tindex] =
+               p->dev_temp_queue_depth[tindex] = 1;
+            /*
+             * We set this command up as a bus device reset.  However, we have
+             * to clear the tag type as it's causing us problems.  We shouldnt
+             * have to worry about any other commands being active, since if
+             * the device is refusing tagged commands, this should be the
+             * first tagged command sent to the device, however, we do have
+             * to worry about any other tagged commands that may already be
+             * in the qinfifo.  The easiest way to do this, is to issue a BDR,
+             * send all the commands back to the mid level code, then let them
+             * come back and get rebuilt as untagged commands.
+             */
+            scb->tag_action = 0;
+            scb->hscb->control &= ~(TAG_ENB | SCB_TAG_TYPE);
+            aic_outb(p,  scb->hscb->control, SCB_CONTROL);
+
+            old_verbose = aic7xxx_verbose;
+            aic7xxx_verbose &= ~(VERBOSE_RESET|VERBOSE_ABORT);
+            for (i=0; i!=p->scb_data->numscbs; i++)
+            {
+              scbp = p->scb_data->scb_array[i];
+              if ((scbp->flags & SCB_ACTIVE) && (scbp != scb))
+              {
+                if (aic7xxx_match_scb(p, scbp, target, channel, lun, i))
+                {
+                  aic7xxx_reset_device(p, target, channel, lun, i);
+                  reset++;
+                }
+                aic7xxx_run_done_queue(p, TRUE);
+              }
+            }
+            aic7xxx_verbose = old_verbose;
+            /*
+             * Wait until after the for loop to set the busy index since
+             * aic7xxx_reset_device will clear the busy index during its
+             * operation.
+             */
+            aic7xxx_busy_target(p, scb);
+            printk(INFO_LEAD "Device is refusing tagged commands, using "
+              "untagged I/O.\n", p->host_no, channel, target, lun);
+            aic_outb(p, MSG_IDENTIFYFLAG, MSG_OUT);
+            aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+          }
+        }
+        else if (scb->flags & SCB_MSGOUT_PPR)
+        {
+          /*
+           * As per the draft specs, any device capable of supporting any of
+           * the option values other than 0 are not allowed to reject the
+           * PPR message.  Instead, they must negotiate out what they do
+           * support instead of rejecting our offering or else they cause
+           * a parity error during msg_out phase to signal that they don't
+           * like our settings.
+           */
+          p->needppr &= ~target_mask;
+          p->needppr_copy &= ~target_mask;
+          aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
+            (AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE));
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
+                               AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
+          p->transinfo[tindex].goal_options = 0;
+          p->dtr_pending &= ~target_mask;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Device is rejecting PPR messages, falling "
+              "back.\n", p->host_no, channel, target, lun);
+          }
+          if ( p->transinfo[tindex].goal_width )
+          {
+            p->needwdtr |= target_mask;
+            p->needwdtr_copy |= target_mask;
+            p->dtr_pending |= target_mask;
+            scb->flags |= SCB_MSGOUT_WDTR;
+          }
+          if ( p->transinfo[tindex].goal_offset )
+          {
+            p->needsdtr |= target_mask;
+            p->needsdtr_copy |= target_mask;
+            if( !(p->dtr_pending & target_mask) )
+            {
+              p->dtr_pending |= target_mask;
+              scb->flags |= SCB_MSGOUT_SDTR;
+            }
+          }
+          if ( p->dtr_pending & target_mask )
+          {
+            aic_outb(p, HOST_MSG, MSG_OUT);
+            aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+          }
+        }
+        else if (scb->flags & SCB_MSGOUT_WDTR)
+        {
+          /*
+           * note 8bit xfers and clear flag
+           */
+          p->needwdtr &= ~target_mask;
+          p->needwdtr_copy &= ~target_mask;
+          p->dtr_pending &= ~target_mask;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          aic7xxx_set_width(p, target, channel, lun, MSG_EXT_WDTR_BUS_8_BIT,
+            (AHC_TRANS_ACTIVE|AHC_TRANS_GOAL|AHC_TRANS_CUR));
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
+                               AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
+          if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Device is rejecting WDTR messages, using "
+              "narrow transfers.\n", p->host_no, channel, target, lun);
+          }
+          p->needsdtr |= (p->needsdtr_copy & target_mask);
+        }
+        else if (scb->flags & SCB_MSGOUT_SDTR)
+        {
+         /*
+          * note asynch xfers and clear flag
+          */
+          p->needsdtr &= ~target_mask;
+          p->needsdtr_copy &= ~target_mask;
+          p->dtr_pending &= ~target_mask;
+          scb->flags &= ~SCB_MSGOUT_SDTR;
+          aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
+            (AHC_TRANS_CUR|AHC_TRANS_ACTIVE|AHC_TRANS_GOAL));
+          if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Device is rejecting SDTR messages, using "
+              "async transfers.\n", p->host_no, channel, target, lun);
+          }
+        }
+        else if (aic7xxx_verbose & VERBOSE_SEQINT)
+        {
+          /*
+           * Otherwise, we ignore it.
+           */
+          printk(INFO_LEAD "Received MESSAGE_REJECT for unknown cause.  "
+            "Ignoring.\n", p->host_no, channel, target, lun);
+        }
+      }
+      break;
+
+    case BAD_STATUS:
+      {
+        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.
+         *
+         * 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.
+         */
+        aic_outb(p, 0, RETURN_1);
+        scb_index = aic_inb(p, SCB_TAG);
+        if (scb_index > p->scb_data->numscbs)
+        {
+          printk(WARN_LEAD "Invalid SCB during SEQINT 0x%02x, SCB_TAG %d.\n",
+            p->host_no, channel, target, lun, intstat, scb_index);
+          break;
+        }
+        scb = p->scb_data->scb_array[scb_index];
+        hscb = scb->hscb;
+
+        if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+        {
+          printk(WARN_LEAD "Invalid SCB during SEQINT 0x%x, scb %d, flags 0x%x,"
+            " cmd 0x%lx.\n", p->host_no, channel, target, lun, intstat,
+            scb_index, scb->flags, (unsigned long) scb->cmd);
+        }
+        else
+        {
+          cmd = scb->cmd;
+          hscb->target_status = aic_inb(p, SCB_TARGET_STATUS);
+          aic7xxx_status(cmd) = hscb->target_status;
+
+          cmd->result = hscb->target_status;
+
+          switch (status_byte(hscb->target_status))
+          {
+            case GOOD:
+              if (aic7xxx_verbose & VERBOSE_SEQINT)
+                printk(INFO_LEAD "Interrupted for status of GOOD???\n",
+                  p->host_no, CTL_OF_SCB(scb));
+              break;
+
+            case COMMAND_TERMINATED:
+            case CHECK_CONDITION:
+              if ( !(scb->flags & SCB_SENSE) )
+              {
+                /*
+                 * Send a sense command to the requesting target.
+                 * XXX - revisit this and get rid of the memcopys.
+                 */
+                memcpy(scb->sense_cmd, &generic_sense[0],
+                       sizeof(generic_sense));
+
+                scb->sense_cmd[1] = (cmd->lun << 5);
+                scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
+
+                scb->sg_list[0].length = 
+                  cpu_to_le32(sizeof(cmd->sense_buffer));
+		scb->sg_list[0].address =
+                        cpu_to_le32(pci_map_single(p->pdev, cmd->sense_buffer,
+                                                   sizeof(cmd->sense_buffer),
+                                                   PCI_DMA_FROMDEVICE));
+
+                /*
+                 * XXX - We should allow disconnection, but can't as it
+                 * might allow overlapped tagged commands.
+                 */
+                /* hscb->control &= DISCENB; */
+                hscb->control = 0;
+                hscb->target_status = 0;
+                hscb->SG_list_pointer = 
+		  cpu_to_le32(SCB_DMA_ADDR(scb, scb->sg_list));
+                hscb->SCSI_cmd_pointer = 
+                  cpu_to_le32(SCB_DMA_ADDR(scb, scb->sense_cmd));
+                hscb->data_count = scb->sg_list[0].length;
+                hscb->data_pointer = scb->sg_list[0].address;
+                hscb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
+                hscb->residual_SG_segment_count = 0;
+                hscb->residual_data_count[0] = 0;
+                hscb->residual_data_count[1] = 0;
+                hscb->residual_data_count[2] = 0;
+
+                scb->sg_count = hscb->SG_segment_count = 1;
+                scb->sg_length = sizeof(cmd->sense_buffer);
+                scb->tag_action = 0;
+                scb->flags |= SCB_SENSE;
+                /*
+                 * Ensure the target is busy since this will be an
+                 * an untagged request.
+                 */
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+                if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+                {
+                  if (scb->flags & SCB_MSGOUT_BITS)
+                    printk(INFO_LEAD "Requesting SENSE with %s\n", p->host_no,
+                           CTL_OF_SCB(scb), (scb->flags & SCB_MSGOUT_SDTR) ?
+                           "SDTR" : "WDTR");
+                  else
+                    printk(INFO_LEAD "Requesting SENSE, no MSG\n", p->host_no,
+                           CTL_OF_SCB(scb));
+                }
+#endif
+                aic7xxx_busy_target(p, scb);
+                aic_outb(p, SEND_SENSE, RETURN_1);
+                aic7xxx_error(cmd) = DID_OK;
+                break;
+              }  /* first time sense, no errors */
+              aic7xxx_error(cmd) = DID_ERROR;
+              scb->flags &= ~SCB_SENSE;
+              break;
+
+            case QUEUE_FULL:
+              queue_flag = TRUE;    /* Mark that this is a QUEUE_FULL and */
+            case BUSY:              /* drop through to here */
+            {
+              struct aic7xxx_scb *next_scbp, *prev_scbp;
+              unsigned char active_hscb, next_hscb, prev_hscb, scb_index;
+              /*
+               * We have to look three places for queued commands:
+               *  1: QINFIFO
+               *  2: p->waiting_scbs queue
+               *  3: WAITING_SCBS list on card (for commands that are started
+               *     but haven't yet made it to the device)
+               */
+              aic7xxx_search_qinfifo(p, target, channel, lun,
+                SCB_LIST_NULL, 0, TRUE,
+                &p->delayed_scbs[tindex]);
+              next_scbp = p->waiting_scbs.head;
+              while ( next_scbp != NULL )
+              {
+                prev_scbp = next_scbp;
+                next_scbp = next_scbp->q_next;
+                if ( aic7xxx_match_scb(p, prev_scbp, target, channel, lun,
+                     SCB_LIST_NULL) )
+                {
+                  scbq_remove(&p->waiting_scbs, prev_scbp);
+                  scbq_insert_tail(&p->delayed_scbs[tindex],
+                    prev_scbp);
+                }
+              }
+              next_scbp = NULL;
+              active_hscb = aic_inb(p, SCBPTR);
+              prev_hscb = next_hscb = scb_index = SCB_LIST_NULL;
+              next_hscb = aic_inb(p, WAITING_SCBH);
+              while (next_hscb != SCB_LIST_NULL)
+              {
+                aic_outb(p, next_hscb, SCBPTR);
+                scb_index = aic_inb(p, SCB_TAG);
+                if (scb_index < p->scb_data->numscbs)
+                {
+                  next_scbp = p->scb_data->scb_array[scb_index];
+                  if (aic7xxx_match_scb(p, next_scbp, target, channel, lun,
+                      SCB_LIST_NULL) )
+                  {
+                    if (next_scbp->flags & SCB_WAITINGQ)
+                    {
+                      p->dev_active_cmds[tindex]++;
+                      p->activescbs--;
+                      scbq_remove(&p->delayed_scbs[tindex], next_scbp);
+                      scbq_remove(&p->waiting_scbs, next_scbp);
+                    }
+                    scbq_insert_head(&p->delayed_scbs[tindex],
+                      next_scbp);
+                    next_scbp->flags |= SCB_WAITINGQ;
+                    p->dev_active_cmds[tindex]--;
+                    p->activescbs--;
+                    next_hscb = aic_inb(p, SCB_NEXT);
+                    aic_outb(p, 0, SCB_CONTROL);
+                    aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+                    aic7xxx_add_curscb_to_free_list(p);
+                    if (prev_hscb == SCB_LIST_NULL)
+                    {
+                      /* We were first on the list,
+                       * so we kill the selection
+                       * hardware.  Let the sequencer
+                       * re-init the hardware itself
+                       */
+                      aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+                      aic_outb(p, CLRSELTIMEO, CLRSINT1);
+                      aic_outb(p, next_hscb, WAITING_SCBH);
+                    }
+                    else
+                    {
+                      aic_outb(p, prev_hscb, SCBPTR);
+                      aic_outb(p, next_hscb, SCB_NEXT);
+                    }
+                  }
+                  else
+                  {
+                    prev_hscb = next_hscb;
+                    next_hscb = aic_inb(p, SCB_NEXT);
+                  }
+                } /* scb_index >= p->scb_data->numscbs */
+              }
+              aic_outb(p, active_hscb, SCBPTR);
+              if (scb->flags & SCB_WAITINGQ)
+              {
+                scbq_remove(&p->delayed_scbs[tindex], scb);
+                scbq_remove(&p->waiting_scbs, scb);
+                p->dev_active_cmds[tindex]++;
+                p->activescbs++;
+              }
+              scbq_insert_head(&p->delayed_scbs[tindex], scb);
+              p->dev_active_cmds[tindex]--;
+              p->activescbs--;
+              scb->flags |= SCB_WAITINGQ | SCB_WAS_BUSY;
+                  
+              if ( !(p->dev_timer_active & (0x01 << tindex)) ) 
+              {
+                p->dev_timer_active |= (0x01 << tindex);
+                if ( p->dev_active_cmds[tindex] )
+                {
+                  p->dev_expires[tindex] = jiffies + HZ;
+                }
+                else
+                {
+                  p->dev_expires[tindex] = jiffies + (HZ / 10);
+                }
+                if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) )
+                {
+                  p->dev_timer.expires = p->dev_expires[tindex];
+                  p->dev_timer_active |= (0x01 << MAX_TARGETS);
+                  add_timer(&p->dev_timer);
+                }
+                else if ( time_after_eq(p->dev_timer.expires,
+                                        p->dev_expires[tindex]) )
+                  mod_timer(&p->dev_timer, p->dev_expires[tindex]);
+              }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+              if( (aic7xxx_verbose & VERBOSE_MINOR_ERROR) ||
+                  (aic7xxx_verbose > 0xffff) )
+              {
+                if (queue_flag)
+                  printk(INFO_LEAD "Queue full received; queue depth %d, "
+                    "active %d\n", p->host_no, CTL_OF_SCB(scb),
+                    p->dev_max_queue_depth[tindex],
+                    p->dev_active_cmds[tindex]);
+                else
+                  printk(INFO_LEAD "Target busy\n", p->host_no, CTL_OF_SCB(scb));
+
+              }
+#endif
+              if (queue_flag)
+              {
+                if ( p->dev_last_queue_full[tindex] !=
+                     p->dev_active_cmds[tindex] )
+                {
+                  p->dev_last_queue_full[tindex] = 
+                      p->dev_active_cmds[tindex];
+                  p->dev_last_queue_full_count[tindex] = 0;
+                }
+                else
+                {
+                  p->dev_last_queue_full_count[tindex]++;
+                }
+                if ( (p->dev_last_queue_full_count[tindex] > 14) &&
+                     (p->dev_active_cmds[tindex] > 4) )
+                {
+                  if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+                    printk(INFO_LEAD "Queue depth reduced to %d\n", p->host_no,
+                      CTL_OF_SCB(scb), p->dev_active_cmds[tindex]);
+                  p->dev_max_queue_depth[tindex] = 
+                      p->dev_active_cmds[tindex];
+                  p->dev_last_queue_full[tindex] = 0;
+                  p->dev_last_queue_full_count[tindex] = 0;
+                  p->dev_temp_queue_depth[tindex] = 
+                    p->dev_active_cmds[tindex];
+                }
+                else if (p->dev_active_cmds[tindex] == 0)
+                {
+                  if (aic7xxx_verbose & VERBOSE_NEGOTIATION)
+                  {
+                    printk(INFO_LEAD "QUEUE_FULL status received with 0 "
+                           "commands active.\n", p->host_no, CTL_OF_SCB(scb));
+                    printk(INFO_LEAD "Tagged Command Queueing disabled\n",
+                           p->host_no, CTL_OF_SCB(scb));
+                  }
+                  p->dev_max_queue_depth[tindex] = 1;
+                  p->dev_temp_queue_depth[tindex] = 1;
+                  scb->tag_action = 0;
+                  scb->hscb->control &= ~(MSG_ORDERED_Q_TAG|MSG_SIMPLE_Q_TAG);
+                }
+                else
+                {
+                  p->dev_flags[tindex] |= DEVICE_WAS_BUSY;
+                  p->dev_temp_queue_depth[tindex] = 
+                    p->dev_active_cmds[tindex];
+                }
+              }
+              break;
+            }
+            
+            default:
+              if (aic7xxx_verbose & VERBOSE_SEQINT)
+                printk(INFO_LEAD "Unexpected target status 0x%x.\n", p->host_no,
+                     CTL_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 AWAITING_MSG:
+      {
+        unsigned char scb_index, msg_out;
+
+        scb_index = aic_inb(p, SCB_TAG);
+        msg_out = aic_inb(p, MSG_OUT);
+        scb = p->scb_data->scb_array[scb_index];
+        p->msg_index = p->msg_len = 0;
+        /*
+         * 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.
+         */
+
+        if ( !(scb->flags & SCB_DEVICE_RESET) &&
+              (msg_out == MSG_IDENTIFYFLAG) &&
+              (scb->hscb->control & TAG_ENB) )
+        {
+          p->msg_buf[p->msg_index++] = scb->tag_action;
+          p->msg_buf[p->msg_index++] = scb->hscb->tag;
+          p->msg_len += 2;
+        }
+
+        if (scb->flags & SCB_DEVICE_RESET)
+        {
+          p->msg_buf[p->msg_index++] = MSG_BUS_DEV_RESET;
+          p->msg_len++;
+          if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+            printk(INFO_LEAD "Bus device reset mailed.\n",
+                 p->host_no, CTL_OF_SCB(scb));
+        }
+        else if (scb->flags & SCB_ABORT)
+        {
+          if (scb->tag_action)
+          {
+            p->msg_buf[p->msg_index++] = MSG_ABORT_TAG;
+          }
+          else
+          {
+            p->msg_buf[p->msg_index++] = MSG_ABORT;
+          }
+          p->msg_len++;
+          if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+            printk(INFO_LEAD "Abort message mailed.\n", p->host_no,
+              CTL_OF_SCB(scb));
+        }
+        else if (scb->flags & SCB_MSGOUT_PPR)
+        {
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Sending PPR (%d/%d/%d/%d) message.\n",
+                   p->host_no, CTL_OF_SCB(scb),
+                   p->transinfo[tindex].goal_period,
+                   p->transinfo[tindex].goal_offset,
+                   p->transinfo[tindex].goal_width,
+                   p->transinfo[tindex].goal_options);
+          }
+          aic7xxx_construct_ppr(p, scb);
+        }
+        else if (scb->flags & SCB_MSGOUT_WDTR)
+        {
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Sending WDTR message.\n", p->host_no,
+                   CTL_OF_SCB(scb));
+          }
+          aic7xxx_construct_wdtr(p, p->transinfo[tindex].goal_width);
+        }
+        else if (scb->flags & SCB_MSGOUT_SDTR)
+        {
+          unsigned int max_sync, period;
+          unsigned char options = 0;
+          /*
+           * Now that the device is selected, use the bits in SBLKCTL and
+           * SSTAT2 to determine the max sync rate for this device.
+           */
+          if (p->features & AHC_ULTRA2)
+          {
+            if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+                !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+            {
+              max_sync = AHC_SYNCRATE_ULTRA2;
+            }
+            else
+            {
+              max_sync = AHC_SYNCRATE_ULTRA;
+            }
+          }
+          else if (p->features & AHC_ULTRA)
+          {
+            max_sync = AHC_SYNCRATE_ULTRA;
+          }
+          else
+          {
+            max_sync = AHC_SYNCRATE_FAST;
+          }
+          period = p->transinfo[tindex].goal_period;
+          aic7xxx_find_syncrate(p, &period, max_sync, &options);
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Sending SDTR %d/%d message.\n", p->host_no,
+                   CTL_OF_SCB(scb), period,
+                   p->transinfo[tindex].goal_offset);
+          }
+          aic7xxx_construct_sdtr(p, period,
+            p->transinfo[tindex].goal_offset);
+        }
+        else 
+        {
+          sti();
+          panic("aic7xxx: AWAITING_MSG for an SCB that does "
+                "not have a waiting message.\n");
+        }
+        /*
+         * We've set everything up to send our message, now to actually do
+         * so we need to enable reqinit interrupts and let the interrupt
+         * handler do the rest.  We don't want to unpause the sequencer yet
+         * though so we'll return early.  We also have to make sure that
+         * we clear the SEQINT *BEFORE* we set the REQINIT handler active
+         * or else it's possible on VLB cards to loose the first REQINIT
+         * interrupt.  Edge triggered EISA cards could also loose this
+         * interrupt, although PCI and level triggered cards should not
+         * have this problem since they continually interrupt the kernel
+         * until we take care of the situation.
+         */
+        scb->flags |= SCB_MSGOUT_SENT;
+        p->msg_index = 0;
+        p->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+        p->flags |= AHC_HANDLING_REQINITS;
+        aic_outb(p, aic_inb(p, SIMODE1) | ENREQINIT, SIMODE1);
+        return;
+      }
+      break;
+
+    case DATA_OVERRUN:
+      {
+        unsigned char scb_index = aic_inb(p, SCB_TAG);
+        unsigned char lastphase = aic_inb(p, LASTPHASE);
+        unsigned int i;
+
+        scb = (p->scb_data->scb_array[scb_index]);
+        /*
+         * 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.
+         *    If we retrieved sense info on this target, then the 
+         *    base SENSE info should have been saved prior to the
+         *    overrun error.  In that case, we return DID_OK and let
+         *    the mid level code pick up on the sense info.  Otherwise
+         *    we return DID_ERROR so the command will get retried.
+         */
+        if ( !(scb->flags & SCB_SENSE) )
+        {
+          printk(WARN_LEAD "Data overrun detected in %s phase, tag %d;\n",
+            p->host_no, CTL_OF_SCB(scb), 
+            (lastphase == P_DATAIN) ? "Data-In" : "Data-Out", scb->hscb->tag);
+          printk(KERN_WARNING "  %s seen Data Phase. Length=%d, NumSGs=%d.\n",
+            (aic_inb(p, SEQ_FLAGS) & DPHASE) ? "Have" : "Haven't",
+            scb->sg_length, scb->sg_count);
+          for (i = 0; i < scb->sg_count; i++)
+          {
+            printk(KERN_WARNING "     sg[%d] - Addr 0x%x : Length %d\n",
+                 i, 
+                 le32_to_cpu(scb->sg_list[i].address),
+                 le32_to_cpu(scb->sg_list[i].length) );
+          }
+          aic7xxx_error(scb->cmd) = DID_ERROR;
+        }
+        else
+          printk(INFO_LEAD "Data Overrun during SEND_SENSE operation.\n",
+            p->host_no, CTL_OF_SCB(scb));
+      }
+      break;
+
+    case WIDE_RESIDUE:
+      {
+        unsigned char resid_sgcnt, index;
+        unsigned char scb_index = aic_inb(p, SCB_TAG);
+        unsigned int cur_addr, resid_dcnt;
+        unsigned int native_addr, native_length;
+        int i;
+
+        if(scb_index > p->scb_data->numscbs)
+        {
+          printk(WARN_LEAD "invalid scb_index during WIDE_RESIDUE.\n",
+            p->host_no, -1, -1, -1);
+          /*
+           * XXX: Add error handling here
+           */
+          break;
+        }
+        scb = p->scb_data->scb_array[scb_index];
+        if(!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+        {
+          printk(WARN_LEAD "invalid scb during WIDE_RESIDUE flags:0x%x "
+                 "scb->cmd:0x%lx\n", p->host_no, CTL_OF_SCB(scb),
+                 scb->flags, (unsigned long)scb->cmd);
+          break;
+        }
+
+        /*
+         * We have a valid scb to use on this WIDE_RESIDUE message, so
+         * we need to walk the sg list looking for this particular sg
+         * segment, then see if we happen to be at the very beginning of
+         * the segment.  If we are, then we have to back things up to
+         * the previous segment.  If not, then we simply need to remove
+         * one byte from this segments address and add one to the byte
+         * count.
+         */
+        cur_addr = aic_inb(p, SHADDR) | (aic_inb(p, SHADDR + 1) << 8) |
+          (aic_inb(p, SHADDR + 2) << 16) | (aic_inb(p, SHADDR + 3) << 24);
+        resid_sgcnt = aic_inb(p, SCB_RESID_SGCNT);
+        resid_dcnt = aic_inb(p, SCB_RESID_DCNT) |
+          (aic_inb(p, SCB_RESID_DCNT + 1) << 8) |
+          (aic_inb(p, SCB_RESID_DCNT + 2) << 16);
+        index = scb->sg_count - (resid_sgcnt + 1);
+        native_addr = le32_to_cpu(scb->sg_list[index].address);
+        native_length = le32_to_cpu(scb->sg_list[index].length);
+        /*
+         * Make sure this is a valid sg_seg for the given pointer
+         */
+        if(cur_addr < native_addr ||
+           cur_addr > (native_addr + native_length + 1))
+        {
+          printk(WARN_LEAD "invalid cur_addr:0x%x during WIDE_RESIDUE\n",
+                 p->host_no, CTL_OF_SCB(scb), cur_addr);
+          if(index > 0)
+            printk(WARN_LEAD "  sg_address[-1]:0x%x sg_length[-1]:%d\n",
+                   p->host_no, CTL_OF_SCB(scb),
+                   le32_to_cpu(scb->sg_list[index - 1].address),
+                   le32_to_cpu(scb->sg_list[index - 1].length));
+          printk(WARN_LEAD "  sg_address:0x%x sg_length:%d\n",
+                 p->host_no, CTL_OF_SCB(scb),
+                 native_addr, native_length);
+          if(resid_sgcnt > 1)
+            printk(WARN_LEAD "  sg_address[1]:0x%x sg_length[1]:%d\n",
+                   p->host_no, CTL_OF_SCB(scb),
+                   le32_to_cpu(scb->sg_list[index + 1].address),
+                   le32_to_cpu(scb->sg_list[index + 1].length));
+          printk(WARN_LEAD "  cur_address:0x%x resid_dcnt:0x%06x\n",
+                 p->host_no, CTL_OF_SCB(scb),
+                 cur_addr, resid_dcnt);
+          break;
+        }
+
+        if( (resid_sgcnt == 0) &&
+            ((resid_dcnt == 0) || (resid_dcnt == 0xffffff)))
+        {
+          /*
+           * We are at the end of the transfer and this is about a byte
+           * we ignored already (because the sequencer knew this was
+           * the last segment and set the adapter to ignore any wide
+           * residue bytes that might come through, which is only done
+           * on the last scatter gather segment of transfers).
+           */
+          break;
+        }
+        else if(cur_addr == native_addr)
+        {
+          /*
+           * If our current address matches the sg_seg->address then we
+           * have to back up the sg array to the previous segment and set
+           * it up to have only one byte of transfer left to go.
+           */
+          if(index == 0)
+          {
+            printk(WARN_LEAD "bogus WIDE_RESIDUE message, no data has been "
+                   "transferred.\n", p->host_no, CTL_OF_SCB(scb));
+            break;
+          }
+          resid_sgcnt++;
+          index--;
+          cur_addr = le32_to_cpu(scb->sg_list[index].address) + 
+            le32_to_cpu(scb->sg_list[index].length) - 1;
+          native_addr = aic_inb(p, SG_NEXT) | (aic_inb(p, SG_NEXT + 1) << 8)
+            | (aic_inb(p, SG_NEXT + 2) << 16) | (aic_inb(p, SG_NEXT + 3) << 24);
+          native_addr -= SG_SIZEOF;
+          aic_outb(p, resid_sgcnt, SG_COUNT);
+          aic_outb(p, resid_sgcnt, SCB_RESID_SGCNT);
+          aic_outb(p, native_addr & 0xff, SG_NEXT);
+          aic_outb(p, (native_addr >> 8) & 0xff, SG_NEXT + 1);
+          aic_outb(p, (native_addr >> 16) & 0xff, SG_NEXT + 2);
+          aic_outb(p, (native_addr >> 24) & 0xff, SG_NEXT + 3);
+          aic_outb(p, 1, SCB_RESID_DCNT); 
+          aic_outb(p, 0, SCB_RESID_DCNT + 1); 
+          aic_outb(p, 0, SCB_RESID_DCNT + 2); 
+          aic_outb(p, 1, HCNT); 
+          aic_outb(p, 0, HCNT + 1); 
+          aic_outb(p, 0, HCNT + 2); 
+          aic_outb(p, cur_addr & 0xff, HADDR);
+          aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1);
+          aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2);
+          aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3);
+        }
+        else
+        {
+          /*
+           * Back the data pointer up by one and add one to the remaining
+           * byte count.  Then store that in the HCNT and HADDR registers.
+           */
+          cur_addr--;
+          resid_dcnt++;
+          aic_outb(p, resid_dcnt & 0xff, SCB_RESID_DCNT); 
+          aic_outb(p, (resid_dcnt >> 8) & 0xff, SCB_RESID_DCNT + 1); 
+          aic_outb(p, (resid_dcnt >> 16) & 0xff, SCB_RESID_DCNT + 2); 
+          aic_outb(p, resid_dcnt & 0xff, HCNT); 
+          aic_outb(p, (resid_dcnt >> 8) & 0xff, HCNT + 1); 
+          aic_outb(p, (resid_dcnt >> 16) & 0xff, HCNT + 2); 
+          aic_outb(p, cur_addr & 0xff, HADDR);
+          aic_outb(p, (cur_addr >> 8) & 0xff, HADDR + 1);
+          aic_outb(p, (cur_addr >> 16) & 0xff, HADDR + 2);
+          aic_outb(p, (cur_addr >> 24) & 0xff, HADDR + 3);
+        }
+        /*
+         * The sequencer actually wants to find the new address and byte
+         * count in the SHCNT and SHADDR register sets.  These registers
+         * are a shadow of the regular HCNT and HADDR registers.  On the
+         * Ultra2 controllers, these registers are read only and the way
+         * we have to set their values is to put the values we want into
+         * the HCNT and HADDR registers and then output PRELOADEN into
+         * the DFCNTRL register which causes the card to latch the current
+         * values in the HADDR and HCNT registers and drop it through to
+         * the shadow registers.  On older cards we copy them directly
+         * across by hand.
+         */
+        if(p->features & AHC_ULTRA2)
+        {
+          aic_outb(p, aic_inb(p, DMAPARAMS), DFCNTRL);
+          i=0;
+          udelay(1);
+          while(((aic_inb(p, SSTAT0) & SDONE) != 0) && (i++ < 1000))
+          {
+            udelay(1);
+          }
+          aic_outb(p, aic_inb(p, DMAPARAMS) & ~(SCSIEN|HDMAEN), DFCNTRL);
+          i=0;
+          udelay(1);
+          while(((aic_inb(p, DFCNTRL) & (SCSIEN|HDMAEN)) != 0) && (i++ < 1000))
+          {
+            udelay(1);
+          }
+        }
+        else
+        {
+          aic_outb(p, resid_dcnt & 0xff, STCNT); 
+          aic_outb(p, (resid_dcnt >> 8) & 0xff, STCNT + 1); 
+          aic_outb(p, (resid_dcnt >> 16) & 0xff, STCNT + 2); 
+          aic_outb(p, cur_addr & 0xff, SHADDR);
+          aic_outb(p, (cur_addr >> 8) & 0xff, SHADDR + 1);
+          aic_outb(p, (cur_addr >> 16) & 0xff, SHADDR + 2);
+          aic_outb(p, (cur_addr >> 24) & 0xff, SHADDR + 3);
+        }
+      }
+      break;
+
+        
+#if AIC7XXX_NOT_YET 
+    case TRACEPOINT:
+      {
+        printk(INFO_LEAD "Tracepoint #1 reached.\n", p->host_no,
+               channel, target, lun);
+      }
+      break;
+
+    case TRACEPOINT2:
+      {
+        printk(INFO_LEAD "Tracepoint #2 reached.\n", p->host_no,
+               channel, target, lun);
+      }
+      break;
+
+    /* 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
+
+    default:                   /* unknown */
+      printk(WARN_LEAD "Unknown SEQINT, INTSTAT 0x%x, SCSISIGI 0x%x.\n",
+             p->host_no, channel, target, lun, intstat,
+             aic_inb(p, SCSISIGI));
+      break;
+  }
+
+  /*
+   * Clear the sequencer interrupt and unpause the sequencer.
+   */
+  unpause_sequencer(p, /* unpause always */ TRUE);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_parse_msg
+ *
+ * Description:
+ *   Parses incoming messages into actions on behalf of
+ *   aic7xxx_handle_reqinit
+ *_F*************************************************************************/
+static int
+aic7xxx_parse_msg(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  int reject, reply, done;
+  unsigned char target_scsirate, tindex;
+  unsigned short target_mask;
+  unsigned char target, channel, lun;
+
+  target = scb->cmd->target;
+  channel = scb->cmd->channel;
+  lun = scb->cmd->lun;
+  reply = reject = done = FALSE;
+  tindex = TARGET_INDEX(scb->cmd);
+  target_scsirate = aic_inb(p, TARG_SCSIRATE + tindex);
+  target_mask = (0x01 << tindex);
+
+  /*
+   * Parse as much of the message as is availible,
+   * rejecting it if we don't support it.  When
+   * the entire message is availible and has been
+   * handled, return TRUE indicating that we have
+   * parsed an entire message.
+   */
+
+  if (p->msg_buf[0] != MSG_EXTENDED)
+  {
+    reject = TRUE;
+  }
+
+  /*
+   * Just accept the length byte outright and perform
+   * more checking once we know the message type.
+   */
+
+  if ( !reject && (p->msg_len > 2) )
+  {
+    switch(p->msg_buf[2])
+    {
+      case MSG_EXT_SDTR:
+      {
+        unsigned int period, offset;
+        unsigned char maxsync, saved_offset, options;
+        struct aic7xxx_syncrate *syncrate;
+        
+        if (p->msg_buf[1] != MSG_EXT_SDTR_LEN)
+        {
+          reject = TRUE;
+          break;
+        }
+
+        if (p->msg_len < (MSG_EXT_SDTR_LEN + 2))
+        {
+          break;
+        }
+
+        period = p->msg_buf[3];
+        saved_offset = offset = p->msg_buf[4];
+        options = 0;
+
+        /*
+         * Even if we are an Ultra3 card, don't allow Ultra3 sync rates when
+         * using the SDTR messages.  We need the PPR messages to enable the
+         * higher speeds that include things like Dual Edge clocking.
+         */
+        if (p->features & AHC_ULTRA2)
+        {
+          if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+               !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+          {
+            maxsync = AHC_SYNCRATE_ULTRA2;
+          }
+          else
+          {
+            maxsync = AHC_SYNCRATE_ULTRA;
+          }
+        }
+        else if (p->features & AHC_ULTRA)
+        {
+          maxsync = AHC_SYNCRATE_ULTRA;
+        }
+        else
+        {
+          maxsync = AHC_SYNCRATE_FAST;
+        }
+        /*
+         * We might have a device that is starting negotiation with us
+         * before we can start up negotiation with it....be prepared to
+         * have a device ask for a higher speed then we want to give it
+         * in that case
+         */
+        if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
+             (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) )
+        {
+          if (!(p->dev_flags[tindex] & DEVICE_SCANNED) &&
+              !(p->needsdtr_copy & target_mask) &&
+               (p->transinfo[tindex].user_offset) )
+          {
+            /*
+             * Not only is the device starting this up, but it also hasn't
+             * been scanned yet, so this would likely be our TUR or our
+             * INQUIRY command at scan time, so we need to use the
+             * settings from the SEEPROM if they existed.  Of course, even
+             * if we didn't find a SEEPROM, we stuffed default values into
+             * the user settings anyway, so use those in all cases.
+             */
+            p->transinfo[tindex].goal_period =
+              p->transinfo[tindex].user_period;
+            if(p->features & AHC_ULTRA2)
+            {
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+            }
+            else if (p->transinfo[tindex].cur_width)
+            {
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+            }
+            else
+            {
+              p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+            }
+            p->needsdtr_copy |= target_mask;
+          }
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Received pre-emptive SDTR message from "
+                   "target.\n", p->host_no, CTL_OF_SCB(scb));
+          }
+          if ( !p->transinfo[tindex].goal_offset )
+            period = 255;
+          if ( p->transinfo[tindex].goal_period > period )
+            period = p->transinfo[tindex].goal_period;
+        }
+  
+        syncrate = aic7xxx_find_syncrate(p, &period, maxsync, &options);
+        aic7xxx_validate_offset(p, syncrate, &offset,
+                                target_scsirate & WIDEXFER);
+        aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                             offset, options, AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+
+        /*
+         * Did we drop to async?  Or are we sending a reply?  If we are,
+         * then we have to make sure that the reply value reflects the proper
+         * settings so we need to set the goal values according to what
+         * we need to send.
+         */
+        if ( (offset != saved_offset) ||
+             ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
+              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR) ) )
+        {
+          aic7xxx_set_syncrate(p, syncrate, target, channel, period, offset,
+                               options, AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+        }
+        
+        /*
+         * Did we start this, if not, or if we went to low and had to
+         * go async, then send an SDTR back to the target
+         */
+        p->needsdtr &= ~target_mask;
+        p->dtr_pending &= ~target_mask;
+        if ( ((scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) !=
+              (SCB_MSGOUT_SENT|SCB_MSGOUT_SDTR)) ||
+             (offset != saved_offset) )
+        {
+          reply = TRUE;
+          p->dtr_pending |= target_mask;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_SDTR;
+          aic_outb(p, HOST_MSG, MSG_OUT);
+          aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+        }
+        done = TRUE;
+        break;
+      }
+      case MSG_EXT_WDTR:
+      {
+        unsigned char bus_width;
+          
+        if (p->msg_buf[1] != MSG_EXT_WDTR_LEN)
+        {
+          reject = TRUE;
+          break;
+        }
+
+        if (p->msg_len < (MSG_EXT_WDTR_LEN + 2))
+        {
+          break;
+        }
+
+        bus_width = p->msg_buf[3];
+        if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR)) ==
+             (SCB_MSGOUT_SENT|SCB_MSGOUT_WDTR) )
+        {
+          switch(bus_width)
+          {
+            default:
+            {
+              reject = TRUE;
+              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+                   ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) ||
+                    (aic7xxx_verbose > 0xffff)) )
+              {
+                printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
+                  p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
+              }
+            } /* We fall through on purpose */
+            case MSG_EXT_WDTR_BUS_8_BIT:
+            {
+              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+              p->needwdtr_copy &= ~target_mask;
+              break;
+            }
+            case MSG_EXT_WDTR_BUS_16_BIT:
+            {
+              break;
+            }
+          }
+          p->dtr_pending &= ~target_mask;
+          p->needwdtr &= ~target_mask;
+        }
+        else
+        {
+          if ( !(p->dev_flags[tindex] & DEVICE_SCANNED) )
+          {
+            /* 
+             * Well, we now know the WDTR and SYNC caps of this device since
+             * it contacted us first, mark it as such and copy the user stuff
+             * over to the goal stuff.
+             */
+            p->transinfo[tindex].goal_period =
+              p->transinfo[tindex].user_period;
+            if(p->transinfo[tindex].user_offset)
+            {
+              if(p->features & AHC_ULTRA2)
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+              }
+              else if( p->transinfo[tindex].user_width &&
+                       (bus_width == MSG_EXT_WDTR_BUS_16_BIT) &&
+                       p->features & AHC_WIDE )
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+              }
+              else
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+              }
+            }
+            p->transinfo[tindex].goal_width =
+              p->transinfo[tindex].user_width;
+            p->needwdtr_copy |= target_mask;
+            p->needsdtr_copy |= target_mask;
+          }
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Received pre-emptive WDTR message from "
+                   "target.\n", p->host_no, CTL_OF_SCB(scb));
+          }
+          switch(bus_width)
+          {
+            default:
+            {
+              if ( (p->features & AHC_WIDE) &&
+                   (p->transinfo[tindex].goal_width ==
+                    MSG_EXT_WDTR_BUS_16_BIT) )
+              {
+                bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+                break;
+              }
+            } /* Fall through if we aren't a wide card */
+            case MSG_EXT_WDTR_BUS_8_BIT:
+            {
+              p->needwdtr_copy &= ~target_mask;
+              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+              aic7xxx_set_width(p, target, channel, lun, bus_width,
+                                AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+              break;
+            }
+          }
+          reply = TRUE;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_WDTR;
+          p->needwdtr &= ~target_mask;
+          p->dtr_pending |= target_mask;
+          aic_outb(p, HOST_MSG, MSG_OUT);
+          aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+        }
+        aic7xxx_set_width(p, target, channel, lun, bus_width,
+                          AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+        
+        /*
+         * By virtue of the SCSI spec, a WDTR message negates any existing
+         * SDTR negotiations.  So, even if needsdtr isn't marked for this
+         * device, we still have to do a new SDTR message if the device
+         * supports SDTR at all.  Therefore, we check needsdtr_copy instead
+         * of needstr.
+         */
+        aic7xxx_set_syncrate(p, NULL, target, channel, 0, 0, 0,
+                             AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
+        p->needsdtr |= (p->needsdtr_copy & target_mask);
+        done = TRUE;
+        break;
+      }
+      case MSG_EXT_PPR:
+      {
+        unsigned char bus_width, trans_options, new_trans_options;
+        unsigned int period, offset;
+        unsigned char maxsync, saved_offset;
+        struct aic7xxx_syncrate *syncrate;
+        
+        if (p->msg_buf[1] != MSG_EXT_PPR_LEN)
+        {
+          reject = TRUE;
+          break;
+        }
+
+        if (p->msg_len < (MSG_EXT_PPR_LEN + 2))
+        {
+          break;
+        }
+
+        period = p->msg_buf[3];
+        offset = saved_offset = p->msg_buf[5];
+        bus_width = p->msg_buf[6];
+        trans_options = new_trans_options = p->msg_buf[7] & 0xf;
+
+        if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+        {
+          printk(INFO_LEAD "Parsing PPR message (%d/%d/%d/%d)\n",
+                 p->host_no, CTL_OF_SCB(scb), period, offset, bus_width,
+                 trans_options);
+        }
+
+        if ( (aic_inb(p, SBLKCTL) & ENAB40) &&
+            !(aic_inb(p, SSTAT2) & EXP_ACTIVE) )
+        {
+          if(p->features & AHC_ULTRA3)
+          {
+            maxsync = AHC_SYNCRATE_ULTRA3;
+          }
+          else
+          {
+            maxsync = AHC_SYNCRATE_ULTRA2;
+          }
+        }
+        else
+        {
+          maxsync = AHC_SYNCRATE_ULTRA;
+        }
+        /*
+         * We might have a device that is starting negotiation with us
+         * before we can start up negotiation with it....be prepared to
+         * have a device ask for a higher speed then we want to give it
+         * in that case
+         */
+        if ( (scb->flags & (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR)) !=
+             (SCB_MSGOUT_SENT|SCB_MSGOUT_PPR) )
+        {
+          reply = TRUE;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_PPR;
+	  p->dev_flags[tindex] |= DEVICE_SCSI_3;
+          if (!(p->dev_flags[tindex] & DEVICE_SCANNED))
+          {
+            /*
+             * Not only is the device starting this up, but it also hasn't
+             * been scanned yet, so this would likely be our TUR or our
+             * INQUIRY command at scan time, so we need to use the
+             * settings from the SEEPROM if they existed.  Of course, even
+             * if we didn't find a SEEPROM, we stuffed default values into
+             * the user settings anyway, so use those in all cases.
+             */
+            p->transinfo[tindex].goal_period =
+              p->transinfo[tindex].user_period;
+            if(p->transinfo[tindex].user_offset)
+            {
+              if(p->features & AHC_ULTRA2)
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_ULTRA2;
+              }
+              else if( p->transinfo[tindex].user_width &&
+                       (bus_width == MSG_EXT_WDTR_BUS_16_BIT) &&
+                       p->features & AHC_WIDE )
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_16BIT;
+              }
+              else
+              {
+                p->transinfo[tindex].goal_offset = MAX_OFFSET_8BIT;
+              }
+            }
+            p->transinfo[tindex].goal_width =
+              p->transinfo[tindex].user_width;
+            p->transinfo[tindex].goal_options =
+              p->transinfo[tindex].user_options;
+          }
+          if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+          {
+            printk(INFO_LEAD "Received pre-emptive PPR message from "
+                   "target.\n", p->host_no, CTL_OF_SCB(scb));
+          }
+          if ( !p->transinfo[tindex].goal_offset )
+            period = 255;
+          if ( p->transinfo[tindex].goal_period > period )
+            period = p->transinfo[tindex].goal_period;
+          if ( p->transinfo[tindex].goal_options == 0 )
+            new_trans_options = 0;
+          switch(bus_width)
+          {
+            default:
+            {
+              if ( (p->features & AHC_WIDE) &&
+                   (p->transinfo[tindex].goal_width ==
+                    MSG_EXT_WDTR_BUS_16_BIT) )
+              {
+                bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+                break;
+              }
+            } /* Fall through if we aren't a wide card */
+            case MSG_EXT_WDTR_BUS_8_BIT:
+            {
+              p->needwdtr_copy &= ~target_mask;
+              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+              aic7xxx_set_width(p, target, channel, lun, bus_width,
+                                AHC_TRANS_GOAL|AHC_TRANS_QUITE);
+              break;
+            }
+          }
+          if ( (p->transinfo[tindex].goal_period > 9) ||
+               (p->transinfo[tindex].goal_options == 0) )
+          {
+            scb->flags &= ~SCB_MSGOUT_BITS;
+            reject = TRUE;
+            reply = FALSE;
+            p->needppr &= ~(1 << tindex);
+            p->needppr_copy &= ~(1 << tindex);
+            if ( p->transinfo[tindex].goal_offset )
+            {
+              p->needsdtr |= (1 << tindex);
+              p->needsdtr_copy |= (1 << tindex);
+            }
+            if ( p->transinfo[tindex].goal_width )
+            {
+              p->needwdtr |= (1 << tindex);
+              p->needwdtr_copy |= (1 << tindex);
+            }
+          }
+        }
+        else
+        {
+          switch(bus_width)
+          {
+            default:
+            {
+              reject = TRUE;
+              if ( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+                   ((p->dev_flags[tindex] & DEVICE_PRINT_DTR) ||
+                    (aic7xxx_verbose > 0xffff)) )
+              {
+                printk(INFO_LEAD "Requesting %d bit transfers, rejecting.\n",
+                  p->host_no, CTL_OF_SCB(scb), 8 * (0x01 << bus_width));
+              }
+            } /* We fall through on purpose */
+            case MSG_EXT_WDTR_BUS_8_BIT:
+            {
+              /*
+               * According to the spec, if we aren't wide, we also can't be
+               * Dual Edge so clear the options byte
+               */
+              new_trans_options = 0;
+              bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+              break;
+            }
+            case MSG_EXT_WDTR_BUS_16_BIT:
+            {
+              break;
+            }
+          }
+        }
+
+        if ( !reject )
+        {
+          aic7xxx_set_width(p, target, channel, lun, bus_width,
+                            AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+          syncrate = aic7xxx_find_syncrate(p, &period, maxsync,
+                                           &new_trans_options);
+          aic7xxx_validate_offset(p, syncrate, &offset, bus_width);
+          aic7xxx_set_syncrate(p, syncrate, target, channel, period,
+                               offset, new_trans_options,
+                               AHC_TRANS_ACTIVE|AHC_TRANS_CUR);
+        }
+
+        p->dtr_pending &= ~target_mask;
+        p->needppr &= ~target_mask;
+        if(reply)
+        {
+          p->dtr_pending |= target_mask;
+          scb->flags &= ~SCB_MSGOUT_BITS;
+          scb->flags |= SCB_MSGOUT_PPR;
+          aic_outb(p, HOST_MSG, MSG_OUT);
+          aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+        }
+        done = TRUE;
+        break;
+      }
+      default:
+      {
+        reject = TRUE;
+        break;
+      }
+    } /* end of switch(p->msg_type) */
+  } /* end of if (!reject && (p->msg_len > 2)) */
+
+  if (!reply && reject)
+  {
+    aic_outb(p, MSG_MESSAGE_REJECT, MSG_OUT);
+    aic_outb(p, aic_inb(p, SCSISIGO) | ATNO, SCSISIGO);
+    done = TRUE;
+  }
+  return(done);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_reqinit
+ *
+ * Description:
+ *   Interrupt handler for REQINIT interrupts (used to transfer messages to
+ *    and from devices).
+ *_F*************************************************************************/
+static void
+aic7xxx_handle_reqinit(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  unsigned char lastbyte;
+  unsigned char phasemis;
+  int done = FALSE;
+
+  switch(p->msg_type)
+  {
+    case MSG_TYPE_INITIATOR_MSGOUT:
+      {
+        if (p->msg_len == 0)
+          panic("aic7xxx: REQINIT with no active message!\n");
+
+        lastbyte = (p->msg_index == (p->msg_len - 1));
+        phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK) != P_MESGOUT;
+
+        if (lastbyte || phasemis)
+        {
+          /* Time to end the message */
+          p->msg_len = 0;
+          p->msg_type = MSG_TYPE_NONE;
+          /*
+           * NOTE-TO-MYSELF: If you clear the REQINIT after you
+           * disable REQINITs, then cases of REJECT_MSG stop working
+           * and hang the bus
+           */
+          aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+          aic_outb(p, CLRSCSIINT, CLRINT);
+          p->flags &= ~AHC_HANDLING_REQINITS;
+
+          if (phasemis == 0)
+          {
+            aic_outb(p, p->msg_buf[p->msg_index], SINDEX);
+            aic_outb(p, 0, RETURN_1);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+            if (aic7xxx_verbose > 0xffff)
+              printk(INFO_LEAD "Completed sending of REQINIT message.\n",
+                     p->host_no, CTL_OF_SCB(scb));
+#endif
+          }
+          else
+          {
+            aic_outb(p, MSGOUT_PHASEMIS, RETURN_1);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+            if (aic7xxx_verbose > 0xffff)
+              printk(INFO_LEAD "PHASEMIS while sending REQINIT message.\n",
+                     p->host_no, CTL_OF_SCB(scb));
+#endif
+          }
+          unpause_sequencer(p, TRUE);
+        }
+        else
+        {
+          /*
+           * Present the byte on the bus (clearing REQINIT) but don't
+           * unpause the sequencer.
+           */
+          aic_outb(p, CLRREQINIT, CLRSINT1);
+          aic_outb(p, CLRSCSIINT, CLRINT);
+          aic_outb(p,  p->msg_buf[p->msg_index++], SCSIDATL);
+        }
+        break;
+      }
+    case MSG_TYPE_INITIATOR_MSGIN:
+      {
+        phasemis = ( aic_inb(p, SCSISIGI) & PHASE_MASK ) != P_MESGIN;
+
+        if (phasemis == 0)
+        {
+          p->msg_len++;
+          /* Pull the byte in without acking it */
+          p->msg_buf[p->msg_index] = aic_inb(p, SCSIBUSL);
+          done = aic7xxx_parse_msg(p, scb);
+          /* Ack the byte */
+          aic_outb(p, CLRREQINIT, CLRSINT1);
+          aic_outb(p, CLRSCSIINT, CLRINT);
+          aic_inb(p, SCSIDATL);
+          p->msg_index++;
+        }
+        if (phasemis || done)
+        {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+          if (aic7xxx_verbose > 0xffff)
+          {
+            if (phasemis)
+              printk(INFO_LEAD "PHASEMIS while receiving REQINIT message.\n",
+                     p->host_no, CTL_OF_SCB(scb));
+            else
+              printk(INFO_LEAD "Completed receipt of REQINIT message.\n",
+                     p->host_no, CTL_OF_SCB(scb));
+          }
+#endif
+          /* Time to end our message session */
+          p->msg_len = 0;
+          p->msg_type = MSG_TYPE_NONE;
+          aic_outb(p, aic_inb(p, SIMODE1) & ~ENREQINIT, SIMODE1);
+          aic_outb(p, CLRSCSIINT, CLRINT);
+          p->flags &= ~AHC_HANDLING_REQINITS;
+          unpause_sequencer(p, TRUE);
+        }
+        break;
+      }
+    default:
+      {
+        panic("aic7xxx: Unknown REQINIT message type.\n");
+        break;
+      }
+  } /* End of switch(p->msg_type) */
+}
+
+/*+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 = aic_inb(p, SCB_TAG);
+  status = aic_inb(p, SSTAT1);
+
+  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)
+  {
+    int channel;
+
+    if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+      channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+    else
+      channel = 0;
+
+    if (aic7xxx_verbose & VERBOSE_RESET)
+      printk(WARN_LEAD "Someone else reset the channel!!\n",
+           p->host_no, channel, -1, -1);
+    if (aic7xxx_panic_on_abort)
+      aic7xxx_panic_abort(p, NULL);
+    /*
+     * Go through and abort all commands for the channel, but do not
+     * reset the channel again.
+     */
+    aic7xxx_reset_channel(p, channel, /* Initiate Reset */ FALSE);
+    aic7xxx_run_done_queue(p, TRUE);
+    scb = NULL;
+  }
+  else if ( ((status & BUSFREE) != 0) && ((status & SELTO) == 0) )
+  {
+    /*
+     * 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 = aic_inb(p, LASTPHASE);
+    unsigned char saved_tcl = aic_inb(p, SAVED_TCL);
+    unsigned char target = (saved_tcl >> 4) & 0x0F;
+    int channel;
+    int printerror = TRUE;
+
+    if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+      channel = (aic_inb(p, SBLKCTL) & SELBUSB) >> 3;
+    else
+      channel = 0;
+
+    aic_outb(p, aic_inb(p, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP),
+             SCSISEQ);
+    if (lastphase == P_MESGOUT)
+    {
+      unsigned char message;
+
+      message = aic_inb(p, SINDEX);
+
+      if ((message == MSG_ABORT) || (message == MSG_ABORT_TAG))
+      {
+        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+          printk(INFO_LEAD "SCB %d abort delivered.\n", p->host_no,
+            CTL_OF_SCB(scb), scb->hscb->tag);
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS,
+                (message == MSG_ABORT) ? SCB_LIST_NULL : scb->hscb->tag );
+        aic7xxx_run_done_queue(p, TRUE);
+        scb = NULL;
+        printerror = 0;
+      }
+      else if (message == MSG_BUS_DEV_RESET)
+      {
+        aic7xxx_handle_device_reset(p, target, channel);
+        scb = NULL;
+        printerror = 0;
+      }
+    }
+    if ( (scb != NULL) &&
+         (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)]) )
+    {
+      /*
+       * This might be a SCSI-3 device that is dropping the bus due to
+       * errors and signalling that we should reduce the transfer speed.
+       * All we have to do is complete this command (since it's a negotiation
+       * command already) and the checksum routine should flag an error and
+       * reduce the speed setting and renegotiate.  We call the reset routing
+       * just to clean out the hardware from this scb.
+       */
+      printerror = 0;
+      aic7xxx_reset_device(p, target, channel, ALL_LUNS, scb->hscb->tag);
+      aic7xxx_run_done_queue(p, TRUE);
+      scb = NULL;
+    }
+    if (printerror != 0)
+    {
+      if (scb != NULL)
+      {
+        unsigned char tag;
+
+        if ((scb->hscb->control & TAG_ENB) != 0)
+        {
+          tag = scb->hscb->tag;
+        }
+        else
+        {
+          tag = SCB_LIST_NULL;
+        }
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS, tag);
+        aic7xxx_run_done_queue(p, TRUE);
+      }
+      else
+      {
+        aic7xxx_reset_device(p, target, channel, ALL_LUNS, SCB_LIST_NULL);
+        aic7xxx_run_done_queue(p, TRUE);
+      }
+      printk(INFO_LEAD "Unexpected busfree, LASTPHASE = 0x%x, "
+             "SEQADDR = 0x%x\n", p->host_no, channel, target, -1, lastphase,
+             (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
+      scb = NULL;
+    }
+    aic_outb(p, MSG_NOOP, MSG_OUT);
+    aic_outb(p, aic_inb(p, SIMODE1) & ~(ENBUSFREE|ENREQINIT),
+      SIMODE1);
+    p->flags &= ~AHC_HANDLING_REQINITS;
+    aic_outb(p, CLRBUSFREE, CLRSINT1);
+    aic_outb(p, CLRSCSIINT, CLRINT);
+    restart_sequencer(p);
+    unpause_sequencer(p, TRUE);
+  }
+  else if ((status & SELTO) != 0)
+  {
+    unsigned char scbptr;
+    unsigned char nextscb;
+    Scsi_Cmnd *cmd;
+
+    scbptr = aic_inb(p, WAITING_SCBH);
+    if (scbptr > p->scb_data->maxhscbs)
+    {
+      /*
+       * I'm still trying to track down exactly how this happens, but until
+       * I find it, this code will make sure we aren't passing bogus values
+       * into the SCBPTR register, even if that register will just wrap
+       * things around, we still don't like having out of range variables.
+       *
+       * NOTE: Don't check the aic7xxx_verbose variable, I want this message
+       * to always be displayed.
+       */
+      printk(INFO_LEAD "Invalid WAITING_SCBH value %d, improvising.\n",
+             p->host_no, -1, -1, -1, scbptr);
+      if (p->scb_data->maxhscbs > 4)
+        scbptr &= (p->scb_data->maxhscbs - 1);
+      else
+        scbptr &= 0x03;
+    }
+    aic_outb(p, scbptr, SCBPTR);
+    scb_index = aic_inb(p, SCB_TAG);
+
+    scb = NULL;
+    if (scb_index < p->scb_data->numscbs)
+    {
+      scb = p->scb_data->scb_array[scb_index];
+      if ((scb->flags & SCB_ACTIVE) == 0)
+      {
+        scb = NULL;
+      }
+    }
+    if (scb == NULL)
+    {
+      printk(WARN_LEAD "Referenced SCB %d not valid during SELTO.\n",
+             p->host_no, -1, -1, -1, scb_index);
+      printk(KERN_WARNING "        SCSISEQ = 0x%x SEQADDR = 0x%x SSTAT0 = 0x%x "
+             "SSTAT1 = 0x%x\n", aic_inb(p, SCSISEQ),
+             aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+             aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
+      if (aic7xxx_panic_on_abort)
+        aic7xxx_panic_abort(p, NULL);
+    }
+    else
+    {
+      cmd = scb->cmd;
+      cmd->result = (DID_TIME_OUT << 16);
+
+      /*
+       * Clear out this hardware SCB
+       */
+      aic_outb(p, 0, SCB_CONTROL);
+
+      /*
+       * Clear out a few values in the card that are in an undetermined
+       * state.
+       */
+      aic_outb(p, MSG_NOOP, MSG_OUT);
+
+      /*
+       * Shift the waiting for selection queue forward
+       */
+      nextscb = aic_inb(p, SCB_NEXT);
+      aic_outb(p, nextscb, WAITING_SCBH);
+
+      /*
+       * Put this SCB back on the free list.
+       */
+      aic7xxx_add_curscb_to_free_list(p);
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+      if (aic7xxx_verbose > 0xffff)
+        printk(INFO_LEAD "Selection Timeout.\n", p->host_no, CTL_OF_SCB(scb));
+#endif
+      if (scb->flags & SCB_QUEUED_ABORT)
+      {
+        /*
+         * We know that this particular SCB had to be the queued abort since
+         * the disconnected SCB would have gotten a reconnect instead.
+         * What we need to do then is to let the command timeout again so
+         * we get a reset since this abort just failed.
+         */
+        cmd->result = 0;
+        scb = NULL;
+      }
+      else if (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)])
+      {
+        /*
+         * Turn off the needsdtr, needwdtr, and needppr bits since this device
+         * doesn't seem to exist.
+         */
+        p->needppr &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needppr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needsdtr &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needsdtr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needwdtr &= ~(0x01 << TARGET_INDEX(scb->cmd));
+        p->needwdtr_copy &= ~(0x01 << TARGET_INDEX(scb->cmd));
+      }
+    }
+    /*
+     * Keep the sequencer from trying to restart any selections
+     */
+    aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+    /*
+     * Make sure the data bits on the bus are released
+     * Don't do this on 7770 chipsets, it makes them give us
+     * a BRKADDRINT and kills the card.
+     */
+    if( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI )
+      aic_outb(p, 0, SCSIBUSL);
+
+    /*
+     * Delay for the selection timeout delay period then stop the selection
+     */
+    udelay(301);
+    aic_outb(p, CLRSELINGO, CLRSINT0);
+    /*
+     * Clear out all the interrupt status bits
+     */
+    aic_outb(p, aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE), SIMODE1);
+    p->flags &= ~AHC_HANDLING_REQINITS;
+    aic_outb(p, CLRSELTIMEO | CLRBUSFREE, CLRSINT1);
+    aic_outb(p, CLRSCSIINT, CLRINT);
+    /*
+     * Restarting the sequencer will stop the selection and make sure devices
+     * are allowed to reselect in.
+     */
+    restart_sequencer(p);
+    unpause_sequencer(p, TRUE);
+  }
+  else if (scb == NULL)
+  {
+    printk(WARN_LEAD "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, -1, -1, -1, status, scb_index, aic_inb(p, SIMODE0),
+           aic_inb(p, SIMODE1), aic_inb(p, SSTAT0),
+           (aic_inb(p, SEQADDR1) << 8) | aic_inb(p, SEQADDR0));
+    /*
+     * Turn off the interrupt and set status to zero, so that it
+     * falls through the rest of the SCSIINT code.
+     */
+    aic_outb(p, status, CLRSINT1);
+    aic_outb(p, CLRSCSIINT, 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 = aic_inb(p, LASTPHASE);
+    unsigned char sstat2 = aic_inb(p, SSTAT2);
+    unsigned char tindex = TARGET_INDEX(scb->cmd);
+
+    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.
+     */
+    if( (p->features & AHC_ULTRA3) && 
+        (aic_inb(p, SCSIRATE) & AHC_SYNCRATE_CRC) &&
+        (lastphase == P_DATAIN) )
+    {
+      printk(WARN_LEAD "CRC error during %s phase.\n",
+             p->host_no, CTL_OF_SCB(scb), phase);
+      if(sstat2 & CRCVALERR)
+      {
+        printk(WARN_LEAD "  CRC error in intermediate CRC packet.\n",
+               p->host_no, CTL_OF_SCB(scb));
+      }
+      if(sstat2 & CRCENDERR)
+      {
+        printk(WARN_LEAD "  CRC error in ending CRC packet.\n",
+               p->host_no, CTL_OF_SCB(scb));
+      }
+      if(sstat2 & CRCREQERR)
+      {
+        printk(WARN_LEAD "  Target incorrectly requested a CRC packet.\n",
+               p->host_no, CTL_OF_SCB(scb));
+      }
+      if(sstat2 & DUAL_EDGE_ERROR)
+      {
+        printk(WARN_LEAD "  Dual Edge transmission error.\n",
+               p->host_no, CTL_OF_SCB(scb));
+      }
+    }
+    else if( (lastphase == P_MESGOUT) &&
+             (cmd == p->dev_dtr_cmnd[tindex]) &&
+             (scb->flags & SCB_MSGOUT_PPR) )
+    {
+      /*
+       * As per the draft specs, any device capable of supporting any of
+       * the option values other than 0 are not allowed to reject the
+       * PPR message.  Instead, they must negotiate out what they do
+       * support instead of rejecting our offering or else they cause
+       * a parity error during msg_out phase to signal that they don't
+       * like our settings.
+       */
+      p->needppr &= ~(1 << tindex);
+      p->needppr_copy &= ~(1 << tindex);
+      aic7xxx_set_width(p, scb->cmd->target, scb->cmd->channel, scb->cmd->lun,
+                        MSG_EXT_WDTR_BUS_8_BIT,
+                        (AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE));
+      aic7xxx_set_syncrate(p, NULL, scb->cmd->target, scb->cmd->channel, 0, 0,
+                           0, AHC_TRANS_ACTIVE|AHC_TRANS_CUR|AHC_TRANS_QUITE);
+      p->transinfo[tindex].goal_options = 0;
+      p->dtr_pending &= ~(1 << tindex);
+      scb->flags &= ~SCB_MSGOUT_BITS;
+      if(aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+      {
+        printk(INFO_LEAD "parity error during PPR message, reverting "
+               "to WDTR/SDTR\n", p->host_no, CTL_OF_SCB(scb));
+      }
+      if ( p->transinfo[tindex].goal_width )
+      {
+        p->needwdtr |= (1 << tindex);
+        p->needwdtr_copy |= (1 << tindex);
+      }
+      if ( p->transinfo[tindex].goal_offset )
+      {
+        if( p->transinfo[tindex].goal_period <= 9 )
+        {
+          p->transinfo[tindex].goal_period = 10;
+        }
+        p->needsdtr |= (1 << tindex);
+        p->needsdtr_copy |= (1 << tindex);
+      }
+      scb = NULL;
+    }
+    else if(p->dev_flags[tindex] & DEVICE_PARITY_ERROR)
+    {
+      struct aic7xxx_syncrate *syncrate;
+      unsigned int period = p->transinfo[tindex].cur_period;
+      unsigned char options = p->transinfo[tindex].cur_options;
+      /*
+       * oops, we had a failure, lower the transfer rate and try again.  It's
+       * worth noting here that it might be wise to also check for typical
+       * wide setting on narrow cable type problems and try disabling wide
+       * instead of slowing down if those exist.  That's hard to do with simple
+       * checksums though.
+       */
+      printk(WARN_LEAD "Parity error during %s phase.\n",
+             p->host_no, CTL_OF_SCB(scb), phase);
+      if((syncrate = aic7xxx_find_syncrate(p, &period, 0, &options)) != NULL)
+      {
+        syncrate++;
+        if( (syncrate->rate[0] != NULL) &&
+            (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) )
+        {
+          p->transinfo[tindex].goal_period = syncrate->period;
+          if( p->transinfo[tindex].goal_period > 9 )
+          {
+            p->transinfo[tindex].goal_options = 0;
+            p->needppr &= ~(1<<tindex);
+            p->needsdtr |= (1<<tindex);
+            p->needppr_copy &= ~(1<<tindex);
+            p->needsdtr_copy |= (1<<tindex);
+            if (p->transinfo[tindex].goal_width)
+            {
+              p->needwdtr |= (1<<tindex);
+              p->needwdtr_copy |= (1<<tindex);
+            }
+          }
+        }
+        else if (p->transinfo[tindex].goal_width)
+        {
+          p->transinfo[tindex].goal_width = 0;
+          p->needwdtr &= ~(1<<tindex);
+          p->needwdtr_copy &= ~(1<<tindex);
+          p->transinfo[tindex].goal_offset =
+            p->transinfo[tindex].user_offset;
+          p->transinfo[tindex].goal_period =
+            p->transinfo[tindex].user_period;
+          p->transinfo[tindex].goal_options =
+            p->transinfo[tindex].user_options;
+          if( p->transinfo[tindex].goal_period <= 9 )
+          {
+            p->needppr |= (1<<tindex);
+            p->needsdtr &= ~(1<<tindex);
+            p->needppr_copy |= (1<<tindex);
+            p->needsdtr_copy &= ~(1<<tindex);
+          }
+          else
+          {
+            p->needppr &= ~(1<<tindex);
+            p->needsdtr |= (1<<tindex);
+            p->needppr_copy &= ~(1<<tindex);
+            p->needsdtr_copy |= (1<<tindex);
+          }
+        }
+        else
+        {
+          p->transinfo[tindex].goal_offset = 0;
+          p->transinfo[tindex].goal_period = 255;
+          p->transinfo[tindex].goal_options = 0;
+          p->transinfo[tindex].goal_width = 0;
+          p->needppr &= ~(1<<tindex);
+          p->needsdtr &= ~(1<<tindex);
+          p->needwdtr &= ~(1<<tindex);
+          p->needppr_copy &= ~(1<<tindex);
+          p->needsdtr_copy &= ~(1<<tindex);
+          p->needwdtr_copy &= ~(1<<tindex);
+        }
+      }
+      p->dev_flags[tindex] &= ~DEVICE_PARITY_ERROR;
+    }
+    else
+    {
+      p->dev_flags[tindex] |= DEVICE_PARITY_ERROR;
+    }
+
+    /*
+     * 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)
+    {
+      aic_outb(p, mesg_out, MSG_OUT);
+      aic_outb(p, aic_inb(p, SCSISIGI) | ATNO, SCSISIGO);
+      scb = NULL;
+    }
+    aic_outb(p, CLRSCSIPERR, CLRSINT1);
+    aic_outb(p, CLRSCSIINT, CLRINT);
+    unpause_sequencer(p, /* unpause_always */ TRUE);
+  }
+  else if ( (status & REQINIT) &&
+            (p->flags & AHC_HANDLING_REQINITS) )
+  {
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+    if (aic7xxx_verbose > 0xffff)
+      printk(INFO_LEAD "Handling REQINIT, SSTAT1=0x%x.\n", p->host_no,
+             CTL_OF_SCB(scb), aic_inb(p, SSTAT1));
+#endif
+    aic7xxx_handle_reqinit(p, scb);
+    return;
+  }
+  else
+  {
+    /*
+     * We don't know what's going on. Turn off the
+     * interrupt source and try to continue.
+     */
+    if (aic7xxx_verbose & VERBOSE_SCSIINT)
+      printk(INFO_LEAD "Unknown SCSIINT status, SSTAT1(0x%x).\n",
+        p->host_no, -1, -1, -1, status);
+    aic_outb(p, status, CLRSINT1);
+    aic_outb(p, CLRSCSIINT, CLRINT);
+    unpause_sequencer(p, /* unpause always */ TRUE);
+    scb = NULL;
+  }
+  if (scb != NULL)
+  {
+    aic7xxx_done(p, scb);
+  }
+}
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+static void
+aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer)
+{
+  unsigned char saved_scbptr, free_scbh, dis_scbh, wait_scbh, temp;
+  int i, bogus, lost;
+  static unsigned char scb_status[AIC7XXX_MAXSCB];
+
+#define SCB_NO_LIST 0
+#define SCB_FREE_LIST 1
+#define SCB_WAITING_LIST 2
+#define SCB_DISCONNECTED_LIST 4
+#define SCB_CURRENTLY_ACTIVE 8
+
+  /*
+   * Note, these checks will fail on a regular basis once the machine moves
+   * beyond the bus scan phase.  The problem is race conditions concerning
+   * the scbs and where they are linked in.  When you have 30 or so commands
+   * outstanding on the bus, and run this twice with every interrupt, the
+   * chances get pretty good that you'll catch the sequencer with an SCB
+   * only partially linked in.  Therefore, once we pass the scan phase
+   * of the bus, we really should disable this function.
+   */
+  bogus = FALSE;
+  memset(&scb_status[0], 0, sizeof(scb_status));
+  pause_sequencer(p);
+  saved_scbptr = aic_inb(p, SCBPTR);
+  if (saved_scbptr >= p->scb_data->maxhscbs)
+  {
+    printk("Bogus SCBPTR %d\n", saved_scbptr);
+    bogus = TRUE;
+  }
+  scb_status[saved_scbptr] = SCB_CURRENTLY_ACTIVE;
+  free_scbh = aic_inb(p, FREE_SCBH);
+  if ( (free_scbh != SCB_LIST_NULL) &&
+       (free_scbh >= p->scb_data->maxhscbs) )
+  {
+    printk("Bogus FREE_SCBH %d\n", free_scbh);
+    bogus = TRUE;
+  }
+  else
+  {
+    temp = free_scbh;
+    while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+    {
+      if(scb_status[temp] & 0x07)
+      {
+        printk("HSCB %d on multiple lists, status 0x%02x", temp,
+               scb_status[temp] | SCB_FREE_LIST);
+        bogus = TRUE;
+      }
+      scb_status[temp] |= SCB_FREE_LIST;
+      aic_outb(p, temp, SCBPTR);
+      temp = aic_inb(p, SCB_NEXT);
+    }
+  }
+
+  dis_scbh = aic_inb(p, DISCONNECTED_SCBH);
+  if ( (dis_scbh != SCB_LIST_NULL) &&
+       (dis_scbh >= p->scb_data->maxhscbs) )
+  {
+    printk("Bogus DISCONNECTED_SCBH %d\n", dis_scbh);
+    bogus = TRUE;
+  }
+  else
+  {
+    temp = dis_scbh;
+    while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+    {
+      if(scb_status[temp] & 0x07)
+      {
+        printk("HSCB %d on multiple lists, status 0x%02x", temp,
+               scb_status[temp] | SCB_DISCONNECTED_LIST);
+        bogus = TRUE;
+      }
+      scb_status[temp] |= SCB_DISCONNECTED_LIST;
+      aic_outb(p, temp, SCBPTR);
+      temp = aic_inb(p, SCB_NEXT);
+    }
+  }
+  
+  wait_scbh = aic_inb(p, WAITING_SCBH);
+  if ( (wait_scbh != SCB_LIST_NULL) &&
+       (wait_scbh >= p->scb_data->maxhscbs) )
+  {
+    printk("Bogus WAITING_SCBH %d\n", wait_scbh);
+    bogus = TRUE;
+  }
+  else
+  {
+    temp = wait_scbh;
+    while( (temp != SCB_LIST_NULL) && (temp < p->scb_data->maxhscbs) )
+    {
+      if(scb_status[temp] & 0x07)
+      {
+        printk("HSCB %d on multiple lists, status 0x%02x", temp,
+               scb_status[temp] | SCB_WAITING_LIST);
+        bogus = TRUE;
+      }
+      scb_status[temp] |= SCB_WAITING_LIST;
+      aic_outb(p, temp, SCBPTR);
+      temp = aic_inb(p, SCB_NEXT);
+    }
+  }
+
+  lost=0;
+  for(i=0; i < p->scb_data->maxhscbs; i++)
+  {
+    aic_outb(p, i, SCBPTR);
+    temp = aic_inb(p, SCB_NEXT);
+    if ( ((temp != SCB_LIST_NULL) &&
+          (temp >= p->scb_data->maxhscbs)) )
+    {
+      printk("HSCB %d bad, SCB_NEXT invalid(%d).\n", i, temp);
+      bogus = TRUE;
+    }
+    if ( temp == i )
+    {
+      printk("HSCB %d bad, SCB_NEXT points to self.\n", i);
+      bogus = TRUE;
+    }
+    if (scb_status[i] == 0)
+      lost++;
+    if (lost > 1)
+    {
+      printk("Too many lost scbs.\n");
+      bogus=TRUE;
+    }
+  }
+  aic_outb(p, saved_scbptr, SCBPTR);
+  unpause_sequencer(p, FALSE);
+  if (bogus)
+  {
+    printk("Bogus parameters found in card SCB array structures.\n");
+    printk("%s\n", buffer);
+    aic7xxx_panic_abort(p, NULL);
+  }
+  return;
+}
+#endif
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_handle_command_completion_intr
+ *
+ * Description:
+ *   SCSI command completion interrupt handler.
+ *-F*************************************************************************/
+static void
+aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p)
+{
+  struct aic7xxx_scb *scb = NULL;
+  Scsi_Cmnd *cmd;
+  unsigned char scb_index, tindex;
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+  if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) )
+    printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1);
+#endif
+    
+  /*
+   * Read the INTSTAT location after clearing the CMDINT bit.  This forces
+   * any posted PCI writes to flush to memory.  Gerard Roudier suggested
+   * this fix to the possible race of clearing the CMDINT bit but not
+   * having all command bytes flushed onto the qoutfifo.
+   */
+  aic_outb(p, CLRCMDINT, CLRINT);
+  aic_inb(p, INTSTAT);
+  /*
+   * The sequencer will continue running when it
+   * issues this interrupt. There may be >1 commands
+   * finished, so loop until we've processed them all.
+   */
+
+  while (p->qoutfifo[p->qoutfifonext] != SCB_LIST_NULL)
+  {
+    scb_index = p->qoutfifo[p->qoutfifonext];
+    p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL;
+    if ( scb_index >= p->scb_data->numscbs )
+    {
+      printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no,
+        -1, -1, -1, scb_index);
+      continue;
+    }
+    scb = p->scb_data->scb_array[scb_index];
+    if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL))
+    {
+      printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags "
+        "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags,
+        (unsigned long) scb->cmd);
+      continue;
+    }
+    tindex = TARGET_INDEX(scb->cmd);
+    if (scb->flags & SCB_QUEUED_ABORT)
+    {
+      pause_sequencer(p);
+      if ( ((aic_inb(p, LASTPHASE) & PHASE_MASK) != P_BUSFREE) &&
+           (aic_inb(p, SCB_TAG) == scb->hscb->tag) )
+      {
+        unpause_sequencer(p, FALSE);
+        continue;
+      }
+      aic7xxx_reset_device(p, scb->cmd->target, scb->cmd->channel,
+        scb->cmd->lun, scb->hscb->tag);
+      scb->flags &= ~(SCB_QUEUED_FOR_DONE | SCB_RESET | SCB_ABORT |
+        SCB_QUEUED_ABORT);
+      unpause_sequencer(p, FALSE);
+    }
+    else if (scb->flags & SCB_ABORT)
+    {
+      /*
+       * We started to abort this, but it completed on us, let it
+       * through as successful
+       */
+      scb->flags &= ~(SCB_ABORT|SCB_RESET);
+    }
+    else if (scb->flags & SCB_SENSE)
+    {
+      char *buffer = &scb->cmd->sense_buffer[0];
+      if (scb->cmd == p->dev_dtr_cmnd[tindex])
+      {
+        struct aic7xxx_scb *old_scb;
+        /*
+         * We have valid sense data, send it back immediately.
+         */
+        old_scb = p->scb_data->scb_array[scb->cmd->next->tag];
+        *old_scb->cmd->sense_buffer = *scb->cmd->sense_buffer;
+        old_scb->hscb->target_status = scb->hscb->target_status;
+        old_scb->cmd->result = scb->hscb->target_status;
+        old_scb->cmd->result |= (DID_ERROR << 16);
+        aic7xxx_status(old_scb->cmd) = scb->hscb->target_status;
+        scbq_remove(&p->waiting_scbs, old_scb);
+        scbq_remove(&p->delayed_scbs[tindex], old_scb);
+        scb->cmd->next = NULL;
+	aic7xxx_done(p, scb);
+        aic7xxx_done(p, old_scb);
+	continue;
+      } 
+      else if (buffer[12] == 0x47 || buffer[12] == 0x54)
+      {
+        /*
+         * SCSI errors, run domain validation and re-run negotiation
+         */
+        p->needdv |= (1<<tindex);
+        /*
+         * Signal that we need to re-negotiate things, this also gets us our
+         * INQUIRY command to re-checksum off of.
+         */
+        p->needppr |= (p->needppr_copy & (1<<tindex));
+        p->needsdtr |= (p->needsdtr_copy & (1<<tindex));
+        p->needwdtr |= (p->needwdtr_copy & (1<<tindex));
+      }
+    }
+    switch (status_byte(scb->hscb->target_status))
+    {
+      case QUEUE_FULL:
+      case BUSY:
+        scb->hscb->target_status = 0;
+        scb->cmd->result = 0;
+        aic7xxx_error(scb->cmd) = DID_OK;
+        break;
+      default:
+        cmd = scb->cmd;
+        if (scb->hscb->residual_SG_segment_count != 0)
+        {
+          aic7xxx_calculate_residual(p, scb);
+        }
+        cmd->result |= (aic7xxx_error(cmd) << 16);
+        aic7xxx_done(p, scb);
+        break;
+    }      
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_isr
+ *
+ * Description:
+ *   SCSI controller interrupt handler.
+ *-F*************************************************************************/
+static void
+aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct aic7xxx_host *p;
+  unsigned char intstat;
+
+  p = (struct aic7xxx_host *)dev_id;
+
+  /*
+   * Just a few sanity checks.  Make sure that we have an int pending.
+   * Also, if PCI, then we are going to check for a PCI bus error status
+   * should we get too many spurious interrupts.
+   */
+  if (!((intstat = aic_inb(p, INTSTAT)) & INT_PEND))
+  {
+#ifdef CONFIG_PCI
+    if ( (p->chip & AHC_PCI) && (p->spurious_int > 500) &&
+        !(p->flags & AHC_HANDLING_REQINITS) )
+    {
+      if ( aic_inb(p, ERROR) & PCIERRSTAT )
+      {
+        aic7xxx_pci_intr(p);
+      }
+      p->spurious_int = 0;
+    }
+    else if ( !(p->flags & AHC_HANDLING_REQINITS) )
+    {
+      p->spurious_int++;
+    }
+#endif
+    return;
+  }
+
+  p->spurious_int = 0;
+
+  /*
+   * Keep track of interrupts for /proc/scsi
+   */
+  p->isr_count++;
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+  if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
+       (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
+    aic7xxx_check_scbs(p, "Bogus settings at start of interrupt.");
+#endif
+
+  /*
+   * Handle all the interrupt sources - especially for SCSI
+   * interrupts, we won't get a second chance at them.
+   */
+  if (intstat & CMDCMPLT)
+  {
+    aic7xxx_handle_command_completion_intr(p);
+  }
+
+  if (intstat & BRKADRINT)
+  {
+    int i;
+    unsigned char errno = aic_inb(p, 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)
+      {
+        printk(KERN_ERR "  %s\n", hard_error[i].errmesg);
+      }
+    }
+    printk(KERN_ERR "(scsi%d)   SEQADDR=0x%x\n", p->host_no,
+      (((aic_inb(p, SEQADDR1) << 8) & 0x100) | aic_inb(p, SEQADDR0)));
+    if (aic7xxx_panic_on_abort)
+      aic7xxx_panic_abort(p, NULL);
+#ifdef CONFIG_PCI
+    if (errno & PCIERRSTAT)
+      aic7xxx_pci_intr(p);
+#endif
+    if (errno & (SQPARERR | ILLOPCODE | ILLSADDR))
+    {
+      sti();
+      panic("aic7xxx: unrecoverable BRKADRINT.\n");
+    }
+    if (errno & ILLHADDR)
+    {
+      printk(KERN_ERR "(scsi%d) BUG! Driver accessed chip without first "
+             "pausing controller!\n", p->host_no);
+    }
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+    if (errno & DPARERR)
+    {
+      if (aic_inb(p, DMAPARAMS) & DIRECTION)
+        printk("(scsi%d) while DMAing SCB from host to card.\n", p->host_no);
+      else
+        printk("(scsi%d) while DMAing SCB from card to host.\n", p->host_no);
+    }
+#endif
+    aic_outb(p, CLRPARERR | CLRBRKADRINT, CLRINT);
+    unpause_sequencer(p, FALSE);
+  }
+
+  if (intstat & SEQINT)
+  {
+    /*
+     * Read the CCSCBCTL register to work around a bug in the Ultra2 cards
+     */
+    if(p->features & AHC_ULTRA2)
+    {
+      aic_inb(p, CCSCBCTL);
+    }
+    aic7xxx_handle_seqint(p, intstat);
+  }
+
+  if (intstat & SCSIINT)
+  {
+    aic7xxx_handle_scsiint(p, intstat);
+  }
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+  if ( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) &&
+       (aic7xxx_panic_on_abort) && (p->flags & AHC_PAGESCBS) )
+    aic7xxx_check_scbs(p, "Bogus settings at end of interrupt.");
+#endif
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   do_aic7xxx_isr
+ *
+ * Description:
+ *   This is a gross hack to solve a problem in linux kernels 2.1.85 and
+ *   above.  Please, children, do not try this at home, and if you ever see
+ *   anything like it, please inform the Gross Hack Police immediately
+ *-F*************************************************************************/
+static void
+do_aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+  unsigned long cpu_flags;
+  struct aic7xxx_host *p;
+  
+  p = (struct aic7xxx_host *)dev_id;
+  if(!p)
+    return;
+  spin_lock_irqsave(&io_request_lock, cpu_flags);
+  if(test_and_set_bit(AHC_IN_ISR_BIT, (void *)&p->flags))
+  {
+    spin_unlock_irqrestore(&io_request_lock, cpu_flags);
+    return;
+  }
+  do
+  {
+    aic7xxx_isr(irq, dev_id, regs);
+  } while ( (aic_inb(p, INTSTAT) & INT_PEND) );
+  aic7xxx_done_cmds_complete(p);
+  aic7xxx_run_waiting_queues(p);
+  clear_bit(AHC_IN_ISR_BIT, (void *)&p->flags);
+  spin_unlock_irqrestore(&io_request_lock, cpu_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_DEVICE 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 int
+aic7xxx_device_queue_depth(struct aic7xxx_host *p, Scsi_Device *device)
+{
+  int default_depth = 3;
+  unsigned char tindex;
+  unsigned short target_mask;
+
+  tindex = device->id | (device->channel << 3);
+  target_mask = (1 << tindex);
+
+  if (p->dev_max_queue_depth[tindex] > 1)
+  {
+    /*
+     * We've already scanned this device, leave it alone
+     */
+    return(p->dev_max_queue_depth[tindex]);
+  }
+
+  device->queue_depth = default_depth;
+  p->dev_temp_queue_depth[tindex] = 1;
+  p->dev_max_queue_depth[tindex] = 1;
+  p->tagenable &= ~target_mask;
+
+  if (device->tagged_supported)
+  {
+    int tag_enabled = TRUE;
+
+    default_depth = AIC7XXX_CMDS_PER_DEVICE;
+ 
+    if (!(p->discenable & target_mask))
+    {
+      if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+        printk(INFO_LEAD "Disconnection disabled, unable to "
+             "enable tagged queueing.\n",
+             p->host_no, device->channel, device->id, device->lun);
+    }
+    else
+    {
+      if (p->instance >= NUMBER(aic7xxx_tag_info))
+      {
+        static int print_warning = TRUE;
+        if(print_warning)
+        {
+          printk(KERN_INFO "aic7xxx: WARNING, insufficient tag_info instances for"
+                           " installed controllers.\n");
+          printk(KERN_INFO "aic7xxx: Please update the aic7xxx_tag_info array in"
+                           " the aic7xxx.c source file.\n");
+          print_warning = FALSE;
+        }
+        device->queue_depth = default_depth;
+      }
+      else
+      {
+
+        if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 255)
+        {
+          tag_enabled = FALSE;
+          device->queue_depth = 3;  /* Tagged queueing is disabled. */
+        }
+        else if (aic7xxx_tag_info[p->instance].tag_commands[tindex] == 0)
+        {
+          device->queue_depth = default_depth;
+        }
+        else
+        {
+          device->queue_depth =
+            aic7xxx_tag_info[p->instance].tag_commands[tindex];
+        }
+      }
+      if ((device->tagged_queue == 0) && tag_enabled)
+      {
+        if (aic7xxx_verbose & VERBOSE_NEGOTIATION2)
+        {
+              printk(INFO_LEAD "Enabled tagged queuing, queue depth %d.\n",
+                p->host_no, device->channel, device->id,
+                device->lun, device->queue_depth);
+        }
+        p->dev_max_queue_depth[tindex] = device->queue_depth;
+        p->dev_temp_queue_depth[tindex] = device->queue_depth;
+        p->tagenable |= target_mask;
+        p->orderedtag |= target_mask;
+        device->tagged_queue = 1;
+        device->current_tag = SCB_LIST_NULL;
+      }
+    }
+  }
+  return(p->dev_max_queue_depth[tindex]);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_select_queue_depth
+ *
+ * Description:
+ *   Sets the queue depth for each SCSI device hanging off the input
+ *   host adapter.  We use a queue depth of 2 for devices that do not
+ *   support tagged queueing.  If AIC7XXX_CMDS_PER_LUN is defined, we
+ *   use that for tagged queueing devices; otherwise we use our own
+ *   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,
+    Scsi_Device *scsi_devs)
+{
+  Scsi_Device *device;
+  struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
+  int scbnum;
+
+  scbnum = 0;
+  for (device = scsi_devs; device != NULL; device = device->next)
+  {
+    if (device->host == host)
+    {
+      scbnum += aic7xxx_device_queue_depth(p, device);
+    }
+  }
+  while (scbnum > p->scb_data->numscbs)
+  {
+    /*
+     * Pre-allocate the needed SCBs to get around the possibility of having
+     * to allocate some when memory is more or less exhausted and we need
+     * the SCB in order to perform a swap operation (possible deadlock)
+     */
+    if ( aic7xxx_allocate_scb(p) == 0 )
+      return;
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_probe
+ *
+ * Description:
+ *   Probing for EISA boards: it looks like the first two bytes
+ *   are a manufacturer code - three characters, five bits each:
+ *
+ *               BYTE 0   BYTE 1   BYTE 2   BYTE 3
+ *              ?1111122 22233333 PPPPPPPP RRRRRRRR
+ *
+ *   The characters are baselined off ASCII '@', so add that value
+ *   to each to get the real ASCII code for it. The next two bytes
+ *   appear to be a product and revision number, probably vendor-
+ *   specific. This is what is being searched for at each port,
+ *   and what should probably correspond to the ID= field in the
+ *   ECU's .cfg file for the card - if your card is not detected,
+ *   make sure your signature is listed in the array.
+ *
+ *   The fourth byte's lowest bit seems to be an enabled/disabled
+ *   flag (rest of the bits are reserved?).
+ *
+ * NOTE:  This function is only needed on Intel and Alpha platforms,
+ *   the other platforms we support don't have EISA/VLB busses.  So,
+ *   we #ifdef this entire function to avoid compiler warnings about
+ *   an unused function.
+ *-F*************************************************************************/
+#if defined(__i386__) || defined(__alpha__)
+static int
+aic7xxx_probe(int slot, int base, ahc_flag_type *flags)
+{
+  int i;
+  unsigned char buf[4];
+
+  static struct {
+    int n;
+    unsigned char signature[sizeof(buf)];
+    ahc_chip type;
+    int bios_disabled;
+  } AIC7xxx[] = {
+    { 4, { 0x04, 0x90, 0x77, 0x70 },
+      AHC_AIC7770|AHC_EISA, FALSE },  /* mb 7770  */
+    { 4, { 0x04, 0x90, 0x77, 0x71 },
+      AHC_AIC7770|AHC_EISA, FALSE }, /* host adapter 274x */
+    { 4, { 0x04, 0x90, 0x77, 0x56 },
+      AHC_AIC7770|AHC_VL, FALSE }, /* 284x BIOS enabled */
+    { 4, { 0x04, 0x90, 0x77, 0x57 },
+      AHC_AIC7770|AHC_VL, TRUE }   /* 284x BIOS disabled */
+  };
+
+  /*
+   * The VL-bus cards need to be primed by
+   * writing before a signature check.
+   */
+  for (i = 0; i < sizeof(buf); i++)
+  {
+    outb(0x80 + i, base);
+    buf[i] = inb(base + i);
+  }
+
+  for (i = 0; i < NUMBER(AIC7xxx); i++)
+  {
+    /*
+     * Signature match on enabled card?
+     */
+    if (!memcmp(buf, AIC7xxx[i].signature, AIC7xxx[i].n))
+    {
+      if (inb(base + 4) & 1)
+      {
+        if (AIC7xxx[i].bios_disabled)
+        {
+          *flags |= AHC_USEDEFAULTS;
+        }
+        else
+        {
+          *flags |= AHC_BIOS_ENABLED;
+        }
+        return (i);
+      }
+
+      printk("aic7xxx: <Adaptec 7770 SCSI Host Adapter> "
+             "disabled at slot %d, ignored.\n", slot);
+    }
+  }
+
+  return (-1);
+}
+#endif /* (__i386__) || (__alpha__) */
+
+
+/*+F*************************************************************************
+ * Function:
+ *   read_2840_seeprom
+ *
+ * Description:
+ *   Reads the 2840 serial EEPROM and returns 1 if successful and 0 if
+ *   not successful.
+ *
+ *   See read_seeprom (for the 2940) for the instruction set of the 93C46
+ *   chip.
+ *
+ *   The 2840 interface to the 93C46 serial EEPROM is through the
+ *   STATUS_2840 and SEECTL_2840 registers.  The CS_2840, CK_2840, and
+ *   DO_2840 bits of the SEECTL_2840 register are connected to the chip
+ *   select, clock, and data out lines respectively of the serial EEPROM.
+ *   The DI_2840 bit of the STATUS_2840 is connected to the data in line
+ *   of the serial EEPROM.  The EEPROM_TF bit of STATUS_2840 register is
+ *   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_284x_seeprom(struct aic7xxx_host *p, struct seeprom_config *sc)
+{
+  int i = 0, k = 0;
+  unsigned char temp;
+  unsigned short checksum = 0;
+  unsigned short *seeprom = (unsigned short *) sc;
+  struct seeprom_cmd {
+    unsigned char len;
+    unsigned char bits[3];
+  };
+  struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
+
+#define CLOCK_PULSE(p) \
+  while ((aic_inb(p, STATUS_2840) & EEPROM_TF) == 0)        \
+  {                                                \
+    ;  /* Do nothing */                                \
+  }                                                \
+  (void) aic_inb(p, SEECTL_2840);
+
+  /*
+   * Read the first 32 registers of the seeprom.  For the 2840,
+   * 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.
+   */
+  for (k = 0; k < (sizeof(*sc) / 2); k++)
+  {
+    /*
+     * Send chip select for one clock cycle.
+     */
+    aic_outb(p, CK_2840 | CS_2840, SEECTL_2840);
+    CLOCK_PULSE(p);
+
+    /*
+     * Now we're ready to send the read command followed by the
+     * address of the 16-bit register we want to read.
+     */
+    for (i = 0; i < seeprom_read.len; i++)
+    {
+      temp = CS_2840 | seeprom_read.bits[i];
+      aic_outb(p, temp, SEECTL_2840);
+      CLOCK_PULSE(p);
+      temp = temp ^ CK_2840;
+      aic_outb(p, temp, SEECTL_2840);
+      CLOCK_PULSE(p);
+    }
+    /*
+     * Send the 6 bit address (MSB first, LSB last).
+     */
+    for (i = 5; i >= 0; i--)
+    {
+      temp = k;
+      temp = (temp >> i) & 1;  /* Mask out all but lower bit. */
+      temp = CS_2840 | temp;
+      aic_outb(p, temp, SEECTL_2840);
+      CLOCK_PULSE(p);
+      temp = temp ^ CK_2840;
+      aic_outb(p, temp, SEECTL_2840);
+      CLOCK_PULSE(p);
+    }
+
+    /*
+     * Now read the 16 bit register.  An initial 0 precedes the
+     * register contents which begins with bit 15 (MSB) and ends
+     * with bit 0 (LSB).  The initial 0 will be shifted off the
+     * top of our word as we let the loop run from 0 to 16.
+     */
+    for (i = 0; i <= 16; i++)
+    {
+      temp = CS_2840;
+      aic_outb(p, temp, SEECTL_2840);
+      CLOCK_PULSE(p);
+      temp = temp ^ CK_2840;
+      seeprom[k] = (seeprom[k] << 1) | (aic_inb(p, STATUS_2840) & DI_2840);
+      aic_outb(p, temp, SEECTL_2840);
+      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.
+     */
+    if (k < (sizeof(*sc) / 2) - 1)
+    {
+      checksum = checksum + seeprom[k];
+    }
+
+    /*
+     * Reset the chip select for the next command cycle.
+     */
+    aic_outb(p, 0, SEECTL_2840);
+    CLOCK_PULSE(p);
+    aic_outb(p, CK_2840, SEECTL_2840);
+    CLOCK_PULSE(p);
+    aic_outb(p, 0, SEECTL_2840);
+    CLOCK_PULSE(p);
+  }
+
+#if 0
+  printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
+  printk("Serial EEPROM:");
+  for (k = 0; k < (sizeof(*sc) / 2); k++)
+  {
+    if (((k % 8) == 0) && (k != 0))
+    {
+      printk("\n              ");
+    }
+    printk(" 0x%x", seeprom[k]);
+  }
+  printk("\n");
+#endif
+
+  if (checksum != sc->checksum)
+  {
+    printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
+    return (0);
+  }
+
+  return (1);
+#undef CLOCK_PULSE
+}
+
+#define CLOCK_PULSE(p)                                               \
+  do {                                                               \
+    int limit = 0;                                                   \
+    do {                                                             \
+      mb();                                                          \
+      pause_sequencer(p);  /* This is just to generate some PCI */   \
+                           /* traffic so the PCI read is flushed */  \
+                           /* it shouldn't be needed, but some */    \
+                           /* chipsets do indeed appear to need */   \
+                           /* something to force PCI reads to get */ \
+                           /* flushed */                             \
+      udelay(1);           /* Do nothing */                          \
+    } while (((aic_inb(p, SEECTL) & SEERDY) == 0) && (++limit < 1000)); \
+  } while(0)
+
+/*+F*************************************************************************
+ * Function:
+ *   acquire_seeprom
+ *
+ * Description:
+ *   Acquires access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static int
+acquire_seeprom(struct aic7xxx_host *p)
+{
+
+  /*
+   * 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.
+   */
+  aic_outb(p, SEEMS, SEECTL);
+  CLOCK_PULSE(p);
+  if ((aic_inb(p, SEECTL) & SEERDY) == 0)
+  {
+    aic_outb(p, 0, SEECTL);
+    return (0);
+  }
+  return (1);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   release_seeprom
+ *
+ * Description:
+ *   Releases access to the memory port on PCI controllers.
+ *-F*************************************************************************/
+static void
+release_seeprom(struct aic7xxx_host *p)
+{
+  /*
+   * Make sure the SEEPROM is ready before we release it.
+   */
+  CLOCK_PULSE(p);
+  aic_outb(p, 0, SEECTL);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   read_seeprom
+ *
+ * Description:
+ *   Reads the serial EEPROM and returns 1 if successful and 0 if
+ *   not successful.
+ *
+ *   The instruction set of the 93C46/56/66 chips is as follows:
+ *
+ *               Start  OP
+ *     Function   Bit  Code  Address    Data     Description
+ *     -------------------------------------------------------------------
+ *     READ        1    10   A5 - A0             Reads data stored in memory,
+ *                                               starting at specified address
+ *     EWEN        1    00   11XXXX              Write enable must precede
+ *                                               all programming modes
+ *     ERASE       1    11   A5 - A0             Erase register A5A4A3A2A1A0
+ *     WRITE       1    01   A5 - A0   D15 - D0  Writes register
+ *     ERAL        1    00   10XXXX              Erase all registers
+ *     WRAL        1    00   01XXXX    D15 - D0  Writes to all registers
+ *     EWDS        1    00   00XXXX              Disables all programming
+ *                                               instructions
+ *     *Note: A value of X for address is a don't care condition.
+ *     *Note: The 93C56 and 93C66 have 8 address bits.
+ * 
+ *
+ *   The 93C46 has a four wire interface: clock, chip select, data in, and
+ *   data out.  In order to perform one of the above functions, you need
+ *   to enable the chip select for a clock period (typically a minimum of
+ *   1 usec, with the clock high and low a minimum of 750 and 250 nsec
+ *   respectively.  While the chip select remains high, you can clock in
+ *   the instructions (above) starting with the start bit, followed by the
+ *   OP code, Address, and Data (if needed).  For the READ instruction, the
+ *   requested 16-bit register contents is read from the data out line but
+ *   is preceded by an initial zero (leading 0, followed by 16-bits, MSB
+ *   first).  The clock cycling from low to high initiates the next data
+ *   bit to be sent from the chip.
+ *
+ *   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,
+ *   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.
+ *-F*************************************************************************/
+static int
+read_seeprom(struct aic7xxx_host *p, int offset, 
+    unsigned short *scarray, unsigned int len, seeprom_chip_type chip)
+{
+  int i = 0, k;
+  unsigned char temp;
+  unsigned short checksum = 0;
+  struct seeprom_cmd {
+    unsigned char len;
+    unsigned char bits[3];
+  };
+  struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
+
+  /*
+   * Request access of the memory port.
+   */
+  if (acquire_seeprom(p) == 0)
+  {
+    return (0);
+  }
+
+  /*
+   * 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 < len; k++)
+  {
+    /*
+     * Send chip select for one clock cycle.
+     */
+    aic_outb(p, SEEMS | SEECK | SEECS, SEECTL);
+    CLOCK_PULSE(p);
+
+    /*
+     * Now we're ready to send the read command followed by the
+     * address of the 16-bit register we want to read.
+     */
+    for (i = 0; i < seeprom_read.len; i++)
+    {
+      temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
+      aic_outb(p, temp, SEECTL);
+      CLOCK_PULSE(p);
+      temp = temp ^ SEECK;
+      aic_outb(p, temp, SEECTL);
+      CLOCK_PULSE(p);
+    }
+    /*
+     * 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);
+      aic_outb(p, temp, SEECTL);
+      CLOCK_PULSE(p);
+      temp = temp ^ SEECK;
+      aic_outb(p, temp, SEECTL);
+      CLOCK_PULSE(p);
+    }
+
+    /*
+     * Now read the 16 bit register.  An initial 0 precedes the
+     * register contents which begins with bit 15 (MSB) and ends
+     * with bit 0 (LSB).  The initial 0 will be shifted off the
+     * top of our word as we let the loop run from 0 to 16.
+     */
+    for (i = 0; i <= 16; i++)
+    {
+      temp = SEEMS | SEECS;
+      aic_outb(p, temp, SEECTL);
+      CLOCK_PULSE(p);
+      temp = temp ^ SEECK;
+      scarray[k] = (scarray[k] << 1) | (aic_inb(p, SEECTL) & SEEDI);
+      aic_outb(p, temp, SEECTL);
+      CLOCK_PULSE(p);
+    }
+
+    /*
+     * 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 < (len - 1))
+    {
+      checksum = checksum + scarray[k];
+    }
+
+    /*
+     * Reset the chip select for the next command cycle.
+     */
+    aic_outb(p, SEEMS, SEECTL);
+    CLOCK_PULSE(p);
+    aic_outb(p, SEEMS | SEECK, SEECTL);
+    CLOCK_PULSE(p);
+    aic_outb(p, SEEMS, SEECTL);
+    CLOCK_PULSE(p);
+  }
+
+  /*
+   * Release access to the memory port and the serial EEPROM.
+   */
+  release_seeprom(p);
+
+#if 0
+  printk("Computed checksum 0x%x, checksum read 0x%x\n",
+         checksum, scarray[len - 1]);
+  printk("Serial EEPROM:");
+  for (k = 0; k < len; k++)
+  {
+    if (((k % 8) == 0) && (k != 0))
+    {
+      printk("\n              ");
+    }
+    printk(" 0x%x", scarray[k]);
+  }
+  printk("\n");
+#endif
+  if ( (checksum != scarray[len - 1]) || (checksum == 0) )
+  {
+    return (0);
+  }
+
+  return (1);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   read_brdctl
+ *
+ * Description:
+ *   Reads the BRDCTL register.
+ *-F*************************************************************************/
+static unsigned char
+read_brdctl(struct aic7xxx_host *p)
+{
+  unsigned char brdctl, value;
+
+  /*
+   * Make sure the SEEPROM is ready before we access it
+   */
+  CLOCK_PULSE(p);
+  if (p->features & AHC_ULTRA2)
+  {
+    brdctl = BRDRW_ULTRA2;
+    aic_outb(p, brdctl, BRDCTL);
+    CLOCK_PULSE(p);
+    value = aic_inb(p, BRDCTL);
+    CLOCK_PULSE(p);
+    return(value);
+  }
+  brdctl = BRDRW;
+  if ( !((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) ||
+        (p->flags & AHC_CHNLB) )
+  {
+    brdctl |= BRDCS;
+  }
+  aic_outb(p, brdctl, BRDCTL);
+  CLOCK_PULSE(p);
+  value = aic_inb(p, BRDCTL);
+  CLOCK_PULSE(p);
+  aic_outb(p, 0, BRDCTL);
+  CLOCK_PULSE(p);
+  return (value);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   write_brdctl
+ *
+ * Description:
+ *   Writes a value to the BRDCTL register.
+ *-F*************************************************************************/
+static void
+write_brdctl(struct aic7xxx_host *p, unsigned char value)
+{
+  unsigned char brdctl;
+
+  /*
+   * Make sure the SEEPROM is ready before we access it
+   */
+  CLOCK_PULSE(p);
+  if (p->features & AHC_ULTRA2)
+  {
+    brdctl = value;
+    aic_outb(p, brdctl, BRDCTL);
+    CLOCK_PULSE(p);
+    brdctl |= BRDSTB_ULTRA2;
+    aic_outb(p, brdctl, BRDCTL);
+    CLOCK_PULSE(p);
+    brdctl &= ~BRDSTB_ULTRA2;
+    aic_outb(p, brdctl, BRDCTL);
+    CLOCK_PULSE(p);
+    read_brdctl(p);
+    CLOCK_PULSE(p);
+  }
+  else
+  {
+    brdctl = BRDSTB;
+    if ( !((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895) ||
+          (p->flags & AHC_CHNLB) )
+    {
+      brdctl |= BRDCS;
+    }
+    brdctl = BRDSTB | BRDCS;
+    aic_outb(p, brdctl, BRDCTL);
+    CLOCK_PULSE(p);
+    brdctl |= value;
+    aic_outb(p, brdctl, BRDCTL);
+    CLOCK_PULSE(p);
+    brdctl &= ~BRDSTB;
+    aic_outb(p, brdctl, BRDCTL);
+    CLOCK_PULSE(p);
+    brdctl &= ~BRDCS;
+    aic_outb(p, brdctl, BRDCTL);
+    CLOCK_PULSE(p);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic785x_cable_detect
+ *
+ * Description:
+ *   Detect the cables that are present on aic785x class controller chips
+ *-F*************************************************************************/
+static void
+aic785x_cable_detect(struct aic7xxx_host *p, int *int_50,
+    int *ext_present, int *eeprom)
+{
+  unsigned char brdctl;
+
+  aic_outb(p, BRDRW | BRDCS, BRDCTL);
+  CLOCK_PULSE(p);
+  aic_outb(p, 0, BRDCTL);
+  CLOCK_PULSE(p);
+  brdctl = aic_inb(p, BRDCTL);
+  CLOCK_PULSE(p);
+  *int_50 = !(brdctl & BRDDAT5);
+  *ext_present = !(brdctl & BRDDAT6);
+  *eeprom = (aic_inb(p, SPIOCAP) & EEPROM);
+}
+
+#undef CLOCK_PULSE
+
+/*+F*************************************************************************
+ * Function:
+ *   aic2940_uwpro_cable_detect
+ *
+ * Description:
+ *   Detect the cables that are present on the 2940-UWPro cards
+ *
+ * NOTE: This function assumes the SEEPROM will have already been acquired
+ *       prior to invocation of this function.
+ *-F*************************************************************************/
+static void
+aic2940_uwpro_wide_cable_detect(struct aic7xxx_host *p, int *int_68,
+    int *ext_68, int *eeprom)
+{
+  unsigned char brdctl;
+
+  /*
+   * 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.
+   */
+  write_brdctl(p, 0);
+
+  /*
+   * Now we read the state of the internal 68 connector.  BRDDAT6
+   * is don't care, BRDDAT7 is internal 68.  The cable is
+   * present if the bit is 0
+   */
+  brdctl = read_brdctl(p);
+  *int_68 = !(brdctl & BRDDAT7);
+
+  /*
+   * Set the bank bit in brdctl and then read the external cable state
+   * and the EEPROM status
+   */
+  write_brdctl(p, BRDDAT5);
+  brdctl = read_brdctl(p);
+
+  *ext_68 = !(brdctl & BRDDAT6);
+  *eeprom = !(brdctl & BRDDAT7);
+
+  /*
+   * We're done, the calling function will release the SEEPROM for us
+   */
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic787x_cable_detect
+ *
+ * Description:
+ *   Detect the cables that are present on aic787x class controller chips
+ *
+ * NOTE: This function assumes the SEEPROM will have already been acquired
+ *       prior to invocation of this function.
+ *-F*************************************************************************/
+static void
+aic787x_cable_detect(struct aic7xxx_host *p, int *int_50, int *int_68,
+    int *ext_present, int *eeprom)
+{
+  unsigned char brdctl;
+
+  /*
+   * 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.
+   */
+  write_brdctl(p, 0);
+
+  /*
+   * Now we read the state of the two internal connectors.  BRDDAT6
+   * is internal 50, BRDDAT7 is internal 68.  For each, the cable is
+   * present if the bit is 0
+   */
+  brdctl = read_brdctl(p);
+  *int_50 = !(brdctl & BRDDAT6);
+  *int_68 = !(brdctl & BRDDAT7);
+
+  /*
+   * Set the bank bit in brdctl and then read the external cable state
+   * and the EEPROM status
+   */
+  write_brdctl(p, BRDDAT5);
+  brdctl = read_brdctl(p);
+
+  *ext_present = !(brdctl & BRDDAT6);
+  *eeprom = !(brdctl & BRDDAT7);
+
+  /*
+   * We're done, the calling function will release the SEEPROM for us
+   */
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic787x_ultra2_term_detect
+ *
+ * Description:
+ *   Detect the termination settings present on ultra2 class controllers
+ *
+ * NOTE: This function assumes the SEEPROM will have already been acquired
+ *       prior to invocation of this function.
+ *-F*************************************************************************/
+static void
+aic7xxx_ultra2_term_detect(struct aic7xxx_host *p, int *enableSE_low,
+                           int *enableSE_high, int *enableLVD_low,
+                           int *enableLVD_high, int *eprom_present)
+{
+  unsigned char brdctl;
+
+  brdctl = read_brdctl(p);
+
+  *eprom_present  = (brdctl & BRDDAT7);
+  *enableSE_high  = (brdctl & BRDDAT6);
+  *enableSE_low   = (brdctl & BRDDAT5);
+  *enableLVD_high = (brdctl & BRDDAT4);
+  *enableLVD_low  = (brdctl & BRDDAT3);
+}
+
+/*+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)
+{
+  int internal50_present = 0;
+  int internal68_present = 0;
+  int external_present = 0;
+  int eprom_present = 0;
+  int enableSE_low = 0;
+  int enableSE_high = 0;
+  int enableLVD_low = 0;
+  int enableLVD_high = 0;
+  unsigned char brddat = 0;
+  unsigned char max_target = 0;
+  unsigned char sxfrctl1 = aic_inb(p, SXFRCTL1);
+
+  if (acquire_seeprom(p))
+  {
+    if (p->features & (AHC_WIDE|AHC_TWIN))
+      max_target = 16;
+    else
+      max_target = 8;
+    aic_outb(p, SEEMS | SEECS, SEECTL);
+    sxfrctl1 &= ~STPWEN;
+    /*
+     * The termination/cable detection logic is split into three distinct
+     * groups.  Ultra2 and later controllers, 2940UW-Pro controllers, and
+     * older 7850, 7860, 7870, 7880, and 7895 controllers.  Each has its
+     * own unique way of detecting their cables and writing the results
+     * back to the card.
+     */
+    if (p->features & AHC_ULTRA2)
+    {
+      /*
+       * As long as user hasn't overridden term settings, always check the
+       * cable detection logic
+       */
+      if (aic7xxx_override_term == -1)
+      {
+        aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high,
+                                   &enableLVD_low, &enableLVD_high,
+                                   &eprom_present);
+      }
+      
+      /*
+       * If the user is overriding settings, then they have been preserved
+       * to here as fake adapter_control entries.  Parse them and allow
+       * them to override the detected settings (if we even did detection).
+       */
+      if (!(p->adapter_control & CFSEAUTOTERM))
+      {
+        enableSE_low = (p->adapter_control & CFSTERM);
+        enableSE_high = (p->adapter_control & CFWSTERM);
+      }
+      if (!(p->adapter_control & CFAUTOTERM))
+      {
+        enableLVD_low = enableLVD_high = (p->adapter_control & CFLVDSTERM);
+      }
+
+      /*
+       * Now take those settings that we have and translate them into the
+       * values that must be written into the registers.
+       *
+       * Flash Enable = BRDDAT7
+       * Secondary High Term Enable = BRDDAT6
+       * Secondary Low Term Enable = BRDDAT5
+       * LVD/Primary High Term Enable = BRDDAT4
+       * LVD/Primary Low Term Enable = STPWEN bit in SXFRCTL1
+       */
+      if (enableLVD_low != 0)
+      {
+        sxfrctl1 |= STPWEN;
+        p->flags |= AHC_TERM_ENB_LVD;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) LVD/Primary Low byte termination "
+                 "Enabled\n", p->host_no);
+      }
+          
+      if (enableLVD_high != 0)
+      {
+        brddat |= BRDDAT4;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) LVD/Primary High byte termination "
+                 "Enabled\n", p->host_no);
+      }
+
+      if (enableSE_low != 0)
+      {
+        brddat |= BRDDAT5;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) Secondary Low byte termination "
+                 "Enabled\n", p->host_no);
+      }
+
+      if (enableSE_high != 0)
+      {
+        brddat |= BRDDAT6;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) Secondary High byte termination "
+                 "Enabled\n", p->host_no);
+      }
+    }
+    else if (p->features & AHC_NEW_AUTOTERM)
+    {
+      /*
+       * The 50 pin connector termination is controlled by STPWEN in the
+       * SXFRCTL1 register.  Since the Adaptec docs typically say the
+       * controller is not allowed to be in the middle of a cable and
+       * this is the only connection on that stub of the bus, there is
+       * no need to even check for narrow termination, it's simply
+       * always on.
+       */
+      sxfrctl1 |= STPWEN;
+      if (aic7xxx_verbose & VERBOSE_PROBE2)
+        printk(KERN_INFO "(scsi%d) Narrow channel termination Enabled\n",
+               p->host_no);
+
+      if (p->adapter_control & CFAUTOTERM)
+      {
+        aic2940_uwpro_wide_cable_detect(p, &internal68_present,
+                                        &external_present,
+                                        &eprom_present);
+        printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, "
+               "Ext-68 %s)\n", p->host_no,
+               "Don't Care",
+               internal68_present ? "YES" : "NO",
+               external_present ? "YES" : "NO");
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no,
+               eprom_present ? "is" : "is not");
+        if (internal68_present && external_present)
+        {
+          brddat = 0;
+          p->flags &= ~AHC_TERM_ENB_SE_HIGH;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk(KERN_INFO "(scsi%d) Wide channel termination Disabled\n",
+                   p->host_no);
+        }
+        else
+        {
+          brddat = BRDDAT6;
+          p->flags |= AHC_TERM_ENB_SE_HIGH;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk(KERN_INFO "(scsi%d) Wide channel termination Enabled\n",
+                   p->host_no);
+        }
+      }
+      else
+      {
+        /*
+         * The termination of the Wide channel is done more like normal
+         * though, and the setting of this termination is done by writing
+         * either a 0 or 1 to BRDDAT6 of the BRDDAT register
+         */
+        if (p->adapter_control & CFWSTERM)
+        {
+          brddat = BRDDAT6;
+          p->flags |= AHC_TERM_ENB_SE_HIGH;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk(KERN_INFO "(scsi%d) Wide channel termination Enabled\n",
+                   p->host_no);
+        }
+        else
+        {
+          brddat = 0;
+        }
+      }
+    }
+    else
+    {
+      if (p->adapter_control & CFAUTOTERM)
+      {
+        if (p->flags & AHC_MOTHERBOARD)
+        {
+          printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n",
+                 p->host_no);
+          printk(KERN_INFO "(scsi%d) Please verify driver detected settings "
+            "are correct.\n", p->host_no);
+          printk(KERN_INFO "(scsi%d) If not, then please properly set the "
+            "device termination\n", p->host_no);
+          printk(KERN_INFO "(scsi%d) in the Adaptec SCSI BIOS by hitting "
+            "CTRL-A when prompted\n", p->host_no);
+          printk(KERN_INFO "(scsi%d) during machine bootup.\n", p->host_no);
+        }
+        /* Configure auto termination. */
+
+        if ( (p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870 )
+        {
+          aic787x_cable_detect(p, &internal50_present, &internal68_present,
+            &external_present, &eprom_present);
+        }
+        else
+        {
+          aic785x_cable_detect(p, &internal50_present, &external_present,
+            &eprom_present);
+        }
+
+        if (max_target <= 8)
+          internal68_present = 0;
+
+        if (max_target > 8)
+        {
+          printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Int-68 %s, "
+                 "Ext-68 %s)\n", p->host_no,
+                 internal50_present ? "YES" : "NO",
+                 internal68_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
+        }
+        else
+        {
+          printk(KERN_INFO "(scsi%d) Cables present (Int-50 %s, Ext-50 %s)\n",
+                 p->host_no,
+                 internal50_present ? "YES" : "NO",
+                 external_present ? "YES" : "NO");
+        }
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk(KERN_INFO "(scsi%d) EEPROM %s present.\n", p->host_no,
+               eprom_present ? "is" : "is not");
+
+        /*
+         * Now set the termination based on what we found.  BRDDAT6
+         * controls wide termination enable.
+         * Flash Enable = BRDDAT7
+         * SE High Term Enable = BRDDAT6
+         */
+        if (internal50_present && internal68_present && external_present)
+        {
+          printk(KERN_INFO "(scsi%d) Illegal cable configuration!!  Only two\n",
+                 p->host_no);
+          printk(KERN_INFO "(scsi%d) connectors on the SCSI controller may be "
+                 "in use at a time!\n", p->host_no);
+          /*
+           * Force termination (low and high byte) on.  This is safer than
+           * leaving it completely off, especially since this message comes
+           * most often from motherboard controllers that don't even have 3
+           * connectors, but instead are failing the cable detection.
+           */
+          internal50_present = external_present = 0;
+          enableSE_high = enableSE_low = 1;
+        }
+
+        if ((max_target > 8) &&
+            ((external_present == 0) || (internal68_present == 0)) )
+        {
+          brddat |= BRDDAT6;
+          p->flags |= AHC_TERM_ENB_SE_HIGH;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
+                   p->host_no);
+        }
+
+        if ( ((internal50_present ? 1 : 0) +
+              (internal68_present ? 1 : 0) +
+              (external_present   ? 1 : 0)) <= 1 )
+        {
+          sxfrctl1 |= STPWEN;
+          p->flags |= AHC_TERM_ENB_SE_LOW;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
+                   p->host_no);
+        }
+      }
+      else /* p->adapter_control & CFAUTOTERM */
+      {
+        if (p->adapter_control & CFSTERM)
+        {
+          sxfrctl1 |= STPWEN;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk(KERN_INFO "(scsi%d) SE Low byte termination Enabled\n",
+                   p->host_no);
+        }
+
+        if (p->adapter_control & CFWSTERM)
+        {
+          brddat |= BRDDAT6;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk(KERN_INFO "(scsi%d) SE High byte termination Enabled\n",
+                   p->host_no);
+        }
+      }
+    }
+
+    aic_outb(p, sxfrctl1, SXFRCTL1);
+    write_brdctl(p, brddat);
+    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;
+
+  /*
+   * 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.
+     */
+    aic_outb(p, 0, FREE_SCBH);
+
+    for (i = 0; i < AIC7XXX_MAXSCB; i++)
+    {
+      aic_outb(p, i, SCBPTR);
+      aic_outb(p, i, SCB_CONTROL);
+      if (aic_inb(p, SCB_CONTROL) != i)
+        break;
+      aic_outb(p, 0, SCBPTR);
+      if (aic_inb(p, SCB_CONTROL) != 0)
+        break;
+
+      aic_outb(p, i, SCBPTR);
+      aic_outb(p, 0, SCB_CONTROL);   /* Clear the control byte. */
+      aic_outb(p, i + 1, SCB_NEXT);  /* Set the next pointer. */
+      aic_outb(p, SCB_LIST_NULL, SCB_TAG);  /* Make the tag invalid. */
+      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS);  /* no busy untagged */
+      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+1);/* targets active yet */
+      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+2);
+      aic_outb(p, SCB_LIST_NULL, SCB_BUSYTARGETS+3);
+    }
+
+    /* Make sure the last SCB terminates the free list. */
+    aic_outb(p, i - 1, SCBPTR);
+    aic_outb(p, SCB_LIST_NULL, SCB_NEXT);
+
+    /* Ensure we clear the first (0) SCBs control byte. */
+    aic_outb(p, 0, SCBPTR);
+    aic_outb(p, 0, SCB_CONTROL);
+
+    p->scb_data->maxhscbs = i;
+    /*
+     * Use direct indexing instead for speed
+     */
+    if ( i == AIC7XXX_MAXSCB )
+      p->flags &= ~AHC_PAGESCBS;
+  }
+
+}
+
+/*+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 reset_delay)
+{
+  int i, result;
+  int max_targets;
+  int found = 1;
+  unsigned char term, scsi_conf;
+  struct Scsi_Host *host;
+
+  host = p->host;
+
+  p->scb_data->maxscbs = AIC7XXX_MAXSCB;
+  host->can_queue = AIC7XXX_MAXSCB;
+  host->cmd_per_lun = 3;
+  host->sg_tablesize = AIC7XXX_MAX_SG;
+  host->select_queue_depths = aic7xxx_select_queue_depth;
+  host->this_id = p->scsi_id;
+  host->io_port = p->base;
+  host->n_io_port = 0xFF;
+  host->base = p->mbase;
+  host->irq = p->irq;
+  if (p->features & AHC_WIDE)
+  {
+    host->max_id = 16;
+  }
+  if (p->features & AHC_TWIN)
+  {
+    host->max_channel = 1;
+  }
+
+  p->host = host;
+  p->host_no = host->host_no;
+  host->unique_id = p->instance;
+  p->isr_count = 0;
+  p->next = NULL;
+  p->completeq.head = NULL;
+  p->completeq.tail = NULL;
+  scbq_init(&p->scb_data->free_scbs);
+  scbq_init(&p->waiting_scbs);
+  init_timer(&p->dev_timer);
+  p->dev_timer.data = (unsigned long)p;
+  p->dev_timer.function = (void *)aic7xxx_timer;
+  p->dev_timer_active = 0;
+
+  /*
+   * We currently have no commands of any type
+   */
+  p->qinfifonext = 0;
+  p->qoutfifonext = 0;
+
+  for (i = 0; i < MAX_TARGETS; i++)
+  {
+    p->dev_commands_sent[i] = 0;
+    p->dev_flags[i] = 0;
+    p->dev_active_cmds[i] = 0;
+    p->dev_last_queue_full[i] = 0;
+    p->dev_last_queue_full_count[i] = 0;
+    p->dev_max_queue_depth[i] = 1;
+    p->dev_temp_queue_depth[i] = 1;
+    p->dev_expires[i] = 0;
+    scbq_init(&p->delayed_scbs[i]);
+  }
+
+  printk(KERN_INFO "(scsi%d) <%s> found at ", p->host_no,
+    board_names[p->board_name_index]);
+  switch(p->chip)
+  {
+    case (AHC_AIC7770|AHC_EISA):
+      printk("EISA slot %d\n", p->pci_device_fn);
+      break;
+    case (AHC_AIC7770|AHC_VL):
+      printk("VLB slot %d\n", p->pci_device_fn);
+      break;
+    default:
+      printk("PCI %d/%d/%d\n", p->pci_bus, PCI_SLOT(p->pci_device_fn),
+        PCI_FUNC(p->pci_device_fn));
+      break;
+  }
+  if (p->features & AHC_TWIN)
+  {
+    printk(KERN_INFO "(scsi%d) Twin Channel, A SCSI ID %d, B SCSI ID %d, ",
+           p->host_no, p->scsi_id, p->scsi_id_b);
+  }
+  else
+  {
+    char *channel;
+
+    channel = "";
+
+    if ((p->flags & AHC_MULTI_CHANNEL) != 0)
+    {
+      channel = " A";
+
+      if ( (p->flags & (AHC_CHNLB|AHC_CHNLC)) != 0 )
+      {
+        channel = (p->flags & AHC_CHNLB) ? " B" : " C";
+      }
+    }
+    if (p->features & AHC_WIDE)
+    {
+      printk(KERN_INFO "(scsi%d) Wide ", p->host_no);
+    }
+    else
+    {
+      printk(KERN_INFO "(scsi%d) Narrow ", p->host_no);
+    }
+    printk("Channel%s, SCSI ID=%d, ", channel, p->scsi_id);
+  }
+  aic_outb(p, 0, SEQ_FLAGS);
+
+  detect_maxscb(p);
+
+  printk("%d/%d SCBs\n", p->scb_data->maxhscbs, p->scb_data->maxscbs);
+  if (aic7xxx_verbose & VERBOSE_PROBE2)
+  {
+    printk(KERN_INFO "(scsi%d) BIOS %sabled, IO Port 0x%lx, IRQ %d\n",
+      p->host_no, (p->flags & AHC_BIOS_ENABLED) ? "en" : "dis",
+      p->base, p->irq);
+    printk(KERN_INFO "(scsi%d) IO Memory at 0x%lx, MMAP Memory at 0x%lx\n",
+      p->host_no, p->mbase, (unsigned long)p->maddr);
+  }
+
+#ifdef CONFIG_PCI
+  /*
+   * Now that we know our instance number, we can set the flags we need to
+   * force termination if need be.
+   */
+  if (aic7xxx_stpwlev != -1)
+  {
+    /*
+     * This option only applies to PCI controllers.
+     */
+    if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
+    {
+      unsigned char devconfig;
+
+      pci_read_config_byte(p->pdev, DEVCONFIG, &devconfig);
+      if ( (aic7xxx_stpwlev >> p->instance) & 0x01 )
+      {
+        devconfig |= STPWLEVEL;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("(scsi%d) Force setting STPWLEVEL bit\n", p->host_no);
+      }
+      else
+      {
+        devconfig &= ~STPWLEVEL;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("(scsi%d) Force clearing STPWLEVEL bit\n", p->host_no);
+      }
+      pci_write_config_byte(p->pdev, DEVCONFIG, devconfig);
+    }
+  }
+#endif
+
+  /*
+   * That took care of devconfig and stpwlev, now for the actual termination
+   * settings.
+   */
+  if (aic7xxx_override_term != -1)
+  {
+    /*
+     * Again, this only applies to PCI controllers.  We don't have problems
+     * with the termination on 274x controllers to the best of my knowledge.
+     */
+    if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI)
+    {
+      unsigned char term_override;
+
+      term_override = ( (aic7xxx_override_term >> (p->instance * 4)) & 0x0f);
+      p->adapter_control &= 
+        ~(CFSTERM|CFWSTERM|CFLVDSTERM|CFAUTOTERM|CFSEAUTOTERM);
+      if ( (p->features & AHC_ULTRA2) && (term_override & 0x0c) )
+      {
+        p->adapter_control |= CFLVDSTERM;
+      }
+      if (term_override & 0x02)
+      {
+        p->adapter_control |= CFWSTERM;
+      }
+      if (term_override & 0x01)
+      {
+        p->adapter_control |= CFSTERM;
+      }
+    }
+  }
+
+  if ( (p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1) )
+  {
+    if (p->features & AHC_SPIOCAP)
+    {
+      if ( aic_inb(p, SPIOCAP) & SSPIOCPS )
+      /*
+       * Update the settings in sxfrctl1 to match the termination
+       * settings.
+       */
+        configure_termination(p);
+    }
+    else if ((p->chip & AHC_CHIPID_MASK) >= AHC_AIC7870)
+    {
+      configure_termination(p);
+    }
+  }
+
+  /*
+   * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
+   */
+  if (p->features & AHC_TWIN)
+  {
+    /* Select channel B */
+    aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
+
+    if ((p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1))
+      term = (aic_inb(p, SXFRCTL1) & STPWEN);
+    else
+      term = ((p->flags & AHC_TERM_ENB_B) ? STPWEN : 0);
+
+    aic_outb(p, p->scsi_id_b, SCSIID);
+    scsi_conf = aic_inb(p, SCSICONF + 1);
+    aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+    aic_outb(p, (scsi_conf & ENSPCHK) | aic7xxx_seltime | term | 
+         ENSTIMER | ACTNEGEN, SXFRCTL1);
+    aic_outb(p, 0, SIMODE0);
+    aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
+    aic_outb(p, 0, SCSIRATE);
+
+    /* Select channel A */
+    aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
+  }
+
+  if (p->features & AHC_ULTRA2)
+  {
+    aic_outb(p, p->scsi_id, SCSIID_ULTRA2);
+  }
+  else
+  {
+    aic_outb(p, p->scsi_id, SCSIID);
+  }
+  if ((p->flags & AHC_SEEPROM_FOUND) || (aic7xxx_override_term != -1))
+    term = (aic_inb(p, SXFRCTL1) & STPWEN);
+  else
+    term = ((p->flags & (AHC_TERM_ENB_A|AHC_TERM_ENB_LVD)) ? STPWEN : 0);
+  scsi_conf = aic_inb(p, SCSICONF);
+  aic_outb(p, DFON | SPIOEN, SXFRCTL0);
+  aic_outb(p, (scsi_conf & ENSPCHK) | aic7xxx_seltime | term | 
+       ENSTIMER | ACTNEGEN, SXFRCTL1);
+  aic_outb(p, 0, SIMODE0);
+  /*
+   * If we are a cardbus adapter then don't enable SCSI reset detection.
+   * We shouldn't likely be sharing SCSI busses with someone else, and
+   * if we don't have a cable currently plugged into the controller then
+   * we won't have a power source for the SCSI termination, which means
+   * we'll see infinite incoming bus resets.
+   */
+  if(p->flags & AHC_NO_STPWEN)
+    aic_outb(p, ENSELTIMO | ENSCSIPERR, SIMODE1);
+  else
+    aic_outb(p, ENSELTIMO | ENSCSIRST | ENSCSIPERR, SIMODE1);
+  aic_outb(p, 0, SCSIRATE);
+  if ( p->features & AHC_ULTRA2)
+    aic_outb(p, 0, SCSIOFFSET);
+
+  /*
+   * Look at the information that board initialization or the board
+   * BIOS has left us. In the lower four bits of each target's
+   * scratch space any value other than 0 indicates that we should
+   * initiate synchronous transfers. If it's zero, the user or the
+   * BIOS has decided to disable synchronous negotiation to that
+   * target so we don't activate the needsdtr flag.
+   */
+  if ((p->features & (AHC_TWIN|AHC_WIDE)) == 0)
+  {
+    max_targets = 8;
+  }
+  else
+  {
+    max_targets = 16;
+  }
+
+  if (!(aic7xxx_no_reset))
+  {
+    /*
+     * If we reset the bus, then clear the transfer settings, else leave
+     * them be
+     */
+    for (i = 0; i < max_targets; i++)
+    {
+      aic_outb(p, 0, TARG_SCSIRATE + i);
+      if (p->features & AHC_ULTRA2)
+      {
+        aic_outb(p, 0, TARG_OFFSET + i);
+      }
+      p->transinfo[i].cur_offset = 0;
+      p->transinfo[i].cur_period = 0;
+      p->transinfo[i].cur_width = MSG_EXT_WDTR_BUS_8_BIT;
+    }
+
+    /*
+     * If we reset the bus, then clear the transfer settings, else leave
+     * them be.
+     */
+    aic_outb(p, 0, ULTRA_ENB);
+    aic_outb(p, 0, ULTRA_ENB + 1);
+    p->ultraenb = 0;
+  }
+
+  /*
+   * 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.
+   */
+  {
+    size_t array_size;
+    unsigned int hscb_physaddr;
+
+    array_size = p->scb_data->maxscbs * sizeof(struct aic7xxx_hwscb);
+    if (p->scb_data->hscbs == NULL)
+    {
+      /* pci_alloc_consistent enforces the alignment already and
+       * clears the area as well.
+       */
+      p->scb_data->hscbs = pci_alloc_consistent(p->pdev, array_size,
+						&p->scb_data->hscbs_dma);
+      /* We have to use pci_free_consistent, not kfree */
+      p->scb_data->hscb_kmalloc_ptr = NULL;
+      p->scb_data->hscbs_dma_len = array_size;
+    }
+    if (p->scb_data->hscbs == NULL)
+    {
+      printk("(scsi%d) Unable to allocate hardware SCB array; "
+             "failing detection.\n", p->host_no);
+      aic_outb(p, 0, SIMODE1);
+      p->irq = 0;
+      return(0);
+    }
+
+    hscb_physaddr = p->scb_data->hscbs_dma;
+    aic_outb(p, hscb_physaddr & 0xFF, HSCB_ADDR);
+    aic_outb(p, (hscb_physaddr >> 8) & 0xFF, HSCB_ADDR + 1);
+    aic_outb(p, (hscb_physaddr >> 16) & 0xFF, HSCB_ADDR + 2);
+    aic_outb(p, (hscb_physaddr >> 24) & 0xFF, HSCB_ADDR + 3);
+
+    /* Set up the fifo areas at the same time */
+    p->untagged_scbs = pci_alloc_consistent(p->pdev, 3*256, &p->fifo_dma);
+    if (p->untagged_scbs == NULL)
+    {
+      printk("(scsi%d) Unable to allocate hardware FIFO arrays; "
+             "failing detection.\n", p->host_no);
+      p->irq = 0;
+      return(0);
+    }
+
+    p->qoutfifo = p->untagged_scbs + 256;
+    p->qinfifo = p->qoutfifo + 256;
+    for (i = 0; i < 256; i++)
+    {
+      p->untagged_scbs[i] = SCB_LIST_NULL;
+      p->qinfifo[i] = SCB_LIST_NULL;
+      p->qoutfifo[i] = SCB_LIST_NULL;
+    }
+
+    hscb_physaddr = p->fifo_dma;
+    aic_outb(p, hscb_physaddr & 0xFF, SCBID_ADDR);
+    aic_outb(p, (hscb_physaddr >> 8) & 0xFF, SCBID_ADDR + 1);
+    aic_outb(p, (hscb_physaddr >> 16) & 0xFF, SCBID_ADDR + 2);
+    aic_outb(p, (hscb_physaddr >> 24) & 0xFF, SCBID_ADDR + 3);
+  }
+
+  /* The Q-FIFOs we just set up are all empty */
+  aic_outb(p, 0, QINPOS);
+  aic_outb(p, 0, KERNEL_QINPOS);
+  aic_outb(p, 0, QOUTPOS);
+
+  if(p->features & AHC_QUEUE_REGS)
+  {
+    aic_outb(p, SCB_QSIZE_256, QOFF_CTLSTA);
+    aic_outb(p, 0, SDSCB_QOFF);
+    aic_outb(p, 0, SNSCB_QOFF);
+    aic_outb(p, 0, HNSCB_QOFF);
+  }
+
+  /*
+   * We don't have any waiting selections or disconnected SCBs.
+   */
+  aic_outb(p, SCB_LIST_NULL, WAITING_SCBH);
+  aic_outb(p, SCB_LIST_NULL, DISCONNECTED_SCBH);
+
+  /*
+   * Message out buffer starts empty
+   */
+  aic_outb(p, MSG_NOOP, MSG_OUT);
+  aic_outb(p, MSG_NOOP, LAST_MSG);
+
+  /*
+   * Set all the other asundry items that haven't been set yet.
+   * This includes just dumping init values to a lot of registers simply
+   * to make sure they've been touched and are ready for use parity wise
+   * speaking.
+   */
+  aic_outb(p, 0, TMODE_CMDADDR);
+  aic_outb(p, 0, TMODE_CMDADDR + 1);
+  aic_outb(p, 0, TMODE_CMDADDR + 2);
+  aic_outb(p, 0, TMODE_CMDADDR + 3);
+  aic_outb(p, 0, TMODE_CMDADDR_NEXT);
+
+  /*
+   * Link us into the list of valid hosts
+   */
+  p->next = first_aic7xxx;
+  first_aic7xxx = p;
+
+  /*
+   * Allocate the first set of scbs for this controller.  This is to stream-
+   * line code elsewhere in the driver.  If we have to check for the existence
+   * of scbs in certain code sections, it slows things down.  However, as
+   * soon as we register the IRQ for this card, we could get an interrupt that
+   * includes possibly the SCSI_RSTI interrupt.  If we catch that interrupt
+   * then we are likely to segfault if we don't have at least one chunk of
+   * SCBs allocated or add checks all through the reset code to make sure
+   * that the SCBs have been allocated which is an invalid running condition
+   * and therefore I think it's preferable to simply pre-allocate the first
+   * chunk of SCBs.
+   */
+  aic7xxx_allocate_scb(p);
+
+  /*
+   * Load the sequencer program, then re-enable the board -
+   * resetting the AIC-7770 disables it, leaving the lights
+   * on with nobody home.
+   */
+  aic7xxx_loadseq(p);
+
+  /*
+   * Make sure the AUTOFLUSHDIS bit is *not* set in the SBLKCTL register
+   */
+  aic_outb(p, aic_inb(p, SBLKCTL) & ~AUTOFLUSHDIS, SBLKCTL);
+
+  if ( (p->chip & AHC_CHIPID_MASK) == AHC_AIC7770 )
+  {
+    aic_outb(p, ENABLE, BCTL);  /* Enable the boards BUS drivers. */
+  }
+
+  if ( !(aic7xxx_no_reset) )
+  {
+    if (p->features & AHC_TWIN)
+    {
+      if (aic7xxx_verbose & VERBOSE_PROBE2)
+        printk(KERN_INFO "(scsi%d) Resetting channel B\n", p->host_no);
+      aic_outb(p, aic_inb(p, SBLKCTL) | SELBUSB, SBLKCTL);
+      aic7xxx_reset_current_bus(p);
+      aic_outb(p, aic_inb(p, SBLKCTL) & ~SELBUSB, SBLKCTL);
+    }
+    /* Reset SCSI bus A. */
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+    {  /* In case we are a 3940, 3985, or 7895, print the right channel */
+      char *channel = "";
+      if (p->flags & AHC_MULTI_CHANNEL)
+      {
+        channel = " A";
+        if (p->flags & (AHC_CHNLB|AHC_CHNLC))
+          channel = (p->flags & AHC_CHNLB) ? " B" : " C";
+      }
+      printk(KERN_INFO "(scsi%d) Resetting channel%s\n", p->host_no, channel);
+    }
+    
+    aic7xxx_reset_current_bus(p);
+
+    /*
+     * Delay for the reset delay by setting the timer, this will delay
+     * future commands sent to any devices.
+     */
+    p->flags |= AHC_RESET_DELAY;
+    for(i=0; i<MAX_TARGETS; i++)
+    {
+      p->dev_expires[i] = jiffies + (4 * HZ);
+      p->dev_timer_active |= (0x01 << i);
+    }
+    p->dev_timer.expires = p->dev_expires[p->scsi_id];
+    add_timer(&p->dev_timer);
+    p->dev_timer_active |= (0x01 << MAX_TARGETS);
+  }
+  else
+  {
+    if (!reset_delay)
+    {
+      printk(KERN_INFO "(scsi%d) Not resetting SCSI bus.  Note: Don't use "
+             "the no_reset\n", p->host_no);
+      printk(KERN_INFO "(scsi%d) option unless you have a verifiable need "
+             "for it.\n", p->host_no);
+    }
+  }
+  
+  /*
+   * Register IRQ with the kernel.  Only allow sharing IRQs with
+   * PCI devices.
+   */
+  if (!(p->chip & AHC_PCI))
+  {
+    result = (request_irq(p->irq, do_aic7xxx_isr, 0, "aic7xxx", p));
+  }
+  else
+  {
+    result = (request_irq(p->irq, do_aic7xxx_isr, SA_SHIRQ,
+              "aic7xxx", p));
+    if (result < 0)
+    {
+      result = (request_irq(p->irq, do_aic7xxx_isr, SA_INTERRUPT | SA_SHIRQ,
+              "aic7xxx", p));
+    }
+  }
+  if (result < 0)
+  {
+    printk(KERN_WARNING "(scsi%d) Couldn't register IRQ %d, ignoring "
+           "controller.\n", p->host_no, p->irq);
+    aic_outb(p, 0, SIMODE1);
+    p->irq = 0;
+    return (0);
+  }
+
+  if(aic_inb(p, INTSTAT) & INT_PEND)
+    printk(INFO_LEAD "spurious interrupt during configuration, cleared.\n",
+      p->host_no, -1, -1 , -1);
+  aic7xxx_clear_intstat(p);
+
+  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*************************************************************************/
+int
+aic7xxx_chip_reset(struct aic7xxx_host *p)
+{
+  unsigned char sblkctl;
+  int wait;
+
+  /*
+   * For some 274x boards, we must clear the CHIPRST bit and pause
+   * the sequencer. For some reason, this makes the driver work.
+   */
+  aic_outb(p, PAUSE | CHIPRST, 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 msec (1000 * 1 msec) */
+  while (--wait && !(aic_inb(p, HCNTRL) & CHIPRSTACK))
+  {
+    udelay(1);  /* 1 usec */
+  }
+
+  pause_sequencer(p);
+
+  sblkctl = aic_inb(p, SBLKCTL) & (SELBUSB|SELWIDE);
+  if (p->chip & AHC_PCI)
+    sblkctl &= ~SELBUSB;
+  switch( sblkctl )
+  {
+    case 0:  /* normal narrow card */
+      break;
+    case 2:  /* Wide card */
+      p->features |= AHC_WIDE;
+      break;
+    case 8:  /* Twin card */
+      p->features |= AHC_TWIN;
+      p->flags |= AHC_MULTI_CHANNEL;
+      break;
+    default: /* hmmm...we don't know what this is */
+      printk(KERN_WARNING "aic7xxx: Unsupported adapter type %d, ignoring.\n",
+        aic_inb(p, SBLKCTL) & 0x0a);
+      return(-1);
+  }
+  return(0);
+}
+
+/*+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, struct aic7xxx_host *temp)
+{
+  struct aic7xxx_host *p = NULL;
+  struct Scsi_Host *host;
+  int i;
+
+  /*
+   * 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 = *temp;
+    p->host = host;
+
+    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.
+       */
+      release_region(p->base, MAXREG - MINREG);
+      scsi_unregister(host);
+      return(NULL);
+    }
+    p->host_no = host->host_no;
+    p->tagenable = 0;
+    p->orderedtag = 0;
+    for (i=0; i<MAX_TARGETS; i++)
+    {
+      p->transinfo[i].goal_period = 255;
+      p->transinfo[i].goal_offset = 0;
+      p->transinfo[i].goal_options = 0;
+      p->transinfo[i].goal_width = MSG_EXT_WDTR_BUS_8_BIT;
+    }
+    DRIVER_LOCK_INIT
+  }
+  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;
+
+  /*
+   * Free the allocated hardware SCB space.
+   */
+  if (p->scb_data != NULL)
+  {
+    struct aic7xxx_scb_dma *scb_dma = NULL;
+    if (p->scb_data->hscbs != NULL)
+    {
+      pci_free_consistent(p->pdev, p->scb_data->hscbs_dma_len,
+			  p->scb_data->hscbs, p->scb_data->hscbs_dma);
+      p->scb_data->hscbs = p->scb_data->hscb_kmalloc_ptr = NULL;
+    }
+    /*
+     * Free the driver SCBs.  These were allocated on an as-need
+     * basis.  We allocated these in groups depending on how many
+     * we could fit into a given amount of RAM.  The tail SCB for
+     * these allocations has a pointer to the alloced area.
+     */
+    for (i = 0; i < p->scb_data->numscbs; i++)
+    {
+      if (p->scb_data->scb_array[i]->scb_dma != scb_dma)
+      {
+	scb_dma = p->scb_data->scb_array[i]->scb_dma;
+	pci_free_consistent(p->pdev, scb_dma->dma_len,
+			    (void *)((unsigned long)scb_dma->dma_address
+                                     - scb_dma->dma_offset),
+			    scb_dma->dma_address);
+      }
+      if (p->scb_data->scb_array[i]->kmalloc_ptr != NULL)
+        kfree(p->scb_data->scb_array[i]->kmalloc_ptr);
+      p->scb_data->scb_array[i] = NULL;
+    }
+  
+    /*
+     * Free the SCB data area.
+     */
+    kfree(p->scb_data);
+  }
+
+  /*
+   * Free any alloced Scsi_Cmnd structures that might be around for
+   * negotiation purposes....
+   */
+  for (i = 0; i < MAX_TARGETS; i++)
+  {
+    if(p->dev_dtr_cmnd[i])
+    {
+      if(p->dev_dtr_cmnd[i]->request_buffer)
+      {
+        kfree(p->dev_dtr_cmnd[i]->request_buffer);
+      }
+      kfree(p->dev_dtr_cmnd[i]);
+    }
+  }
+
+  pci_free_consistent(p->pdev, 3*256, (void *)p->untagged_scbs, p->fifo_dma);
+}
+
+/*+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 void
+aic7xxx_load_seeprom(struct aic7xxx_host *p, unsigned char *sxfrctl1)
+{
+  int have_seeprom = 0;
+  int i, max_targets, mask;
+  unsigned char scsirate, scsi_conf;
+  unsigned short scarray[128];
+  struct seeprom_config *sc = (struct seeprom_config *) scarray;
+
+  if (aic7xxx_verbose & VERBOSE_PROBE2)
+  {
+    printk(KERN_INFO "aic7xxx: Loading serial EEPROM...");
+  }
+  switch (p->chip)
+  {
+    case (AHC_AIC7770|AHC_EISA):  /* None of these adapters have seeproms. */
+      if (aic_inb(p, SCSICONF) & TERM_ENB)
+        p->flags |= AHC_TERM_ENB_A;
+      if ( (p->features & AHC_TWIN) && (aic_inb(p, SCSICONF + 1) & TERM_ENB) )
+        p->flags |= AHC_TERM_ENB_B;
+      break;
+
+    case (AHC_AIC7770|AHC_VL):
+      have_seeprom = read_284x_seeprom(p, (struct seeprom_config *) scarray);
+      break;
+
+    default:
+      have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                  scarray, p->sc_size, p->sc_type);
+      if (!have_seeprom)
+      {
+        if(p->sc_type == C46)
+          have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                      scarray, p->sc_size, C56_66);
+        else
+          have_seeprom = read_seeprom(p, (p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                      scarray, p->sc_size, C46);
+      }
+      if (!have_seeprom)
+      {
+        p->sc_size = 128;
+        have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                    scarray, p->sc_size, p->sc_type);
+        if (!have_seeprom)
+        {
+          if(p->sc_type == C46)
+            have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                        scarray, p->sc_size, C56_66);
+          else
+            have_seeprom = read_seeprom(p, 4*(p->flags & (AHC_CHNLB|AHC_CHNLC)),
+                                        scarray, p->sc_size, C46);
+        }
+      }
+      break;
+  }
+
+  if (!have_seeprom)
+  {
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+    {
+      printk("\naic7xxx: No SEEPROM available.\n");
+    }
+    p->flags |= AHC_NEWEEPROM_FMT;
+    if (aic_inb(p, SCSISEQ) == 0)
+    {
+      p->flags |= AHC_USEDEFAULTS;
+      p->flags &= ~AHC_BIOS_ENABLED;
+      p->scsi_id = p->scsi_id_b = 7;
+      *sxfrctl1 |= STPWEN;
+      if (aic7xxx_verbose & VERBOSE_PROBE2)
+      {
+        printk("aic7xxx: Using default values.\n");
+      }
+    }
+    else if (aic7xxx_verbose & VERBOSE_PROBE2)
+    {
+      printk("aic7xxx: Using leftover BIOS values.\n");
+    }
+    if ( ((p->chip & ~AHC_CHIPID_MASK) == AHC_PCI) && (*sxfrctl1 & STPWEN) )
+    {
+      p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
+      sc->adapter_control &= ~CFAUTOTERM;
+      sc->adapter_control |= CFSTERM | CFWSTERM | CFLVDSTERM;
+    }
+    if (aic7xxx_extended)
+      p->flags |= (AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
+    else
+      p->flags &= ~(AHC_EXTEND_TRANS_A | AHC_EXTEND_TRANS_B);
+  }
+  else
+  {
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+    {
+      printk("done\n");
+    }
+
+    /*
+     * Note things in our flags
+     */
+    p->flags |= AHC_SEEPROM_FOUND;
+
+    /*
+     * Update the settings in sxfrctl1 to match the termination settings.
+     */
+    *sxfrctl1 = 0;
+
+    /*
+     * Get our SCSI ID from the SEEPROM setting...
+     */
+    p->scsi_id = (sc->brtime_id & CFSCSIID);
+
+    /*
+     * First process the settings that are different between the VLB
+     * and PCI adapter seeproms.
+     */
+    if ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7770)
+    {
+      /* VLB adapter seeproms */
+      if (sc->bios_control & CF284XEXTEND)
+        p->flags |= AHC_EXTEND_TRANS_A;
+
+      if (sc->adapter_control & CF284XSTERM)
+      {
+        *sxfrctl1 |= STPWEN;
+        p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
+      }
+    }
+    else
+    {
+      /* PCI adapter seeproms */
+      if (sc->bios_control & CFEXTEND)
+        p->flags |= AHC_EXTEND_TRANS_A;
+      if (sc->bios_control & CFBIOSEN)
+        p->flags |= AHC_BIOS_ENABLED;
+      else
+        p->flags &= ~AHC_BIOS_ENABLED;
+
+      if (sc->adapter_control & CFSTERM)
+      {
+        *sxfrctl1 |= STPWEN;
+        p->flags |= AHC_TERM_ENB_SE_LOW | AHC_TERM_ENB_SE_HIGH;
+      }
+    }
+    memcpy(&p->sc, sc, sizeof(struct seeprom_config));
+  }
+
+  p->discenable = 0;
+    
+  /*
+   * Limit to 16 targets just in case.  The 2842 for one is known to
+   * blow the max_targets setting, future cards might also.
+   */
+  max_targets = ((p->features & (AHC_TWIN | AHC_WIDE)) ? 16 : 8);
+
+  if (have_seeprom)
+  {
+    for (i = 0; i < max_targets; i++)
+    {
+      if( ((p->features & AHC_ULTRA) &&
+          !(sc->adapter_control & CFULTRAEN) &&
+           (sc->device_flags[i] & CFSYNCHISULTRA)) ||
+          (sc->device_flags[i] & CFNEWULTRAFORMAT) )
+      {
+        p->flags |= AHC_NEWEEPROM_FMT;
+        break;
+      }
+    }
+  }
+
+  for (i = 0; i < max_targets; i++)
+  {
+    mask = (0x01 << i);
+    if (!have_seeprom)
+    {
+      if (aic_inb(p, SCSISEQ) != 0)
+      {
+        /*
+         * OK...the BIOS set things up and left behind the settings we need.
+         * Just make our sc->device_flags[i] entry match what the card has
+         * set for this device.
+         */
+        p->discenable = 
+          ~(aic_inb(p, DISC_DSB) | (aic_inb(p, DISC_DSB + 1) << 8) );
+        p->ultraenb =
+          (aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8) );
+        sc->device_flags[i] = (p->discenable & mask) ? CFDISC : 0;
+        if (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER)
+          sc->device_flags[i] |= CFWIDEB;
+        if (p->features & AHC_ULTRA2)
+        {
+          if (aic_inb(p, TARG_OFFSET + i))
+          {
+            sc->device_flags[i] |= CFSYNCH;
+            sc->device_flags[i] |= (aic_inb(p, TARG_SCSIRATE + i) & 0x07);
+            if ( (aic_inb(p, TARG_SCSIRATE + i) & 0x18) == 0x18 )
+              sc->device_flags[i] |= CFSYNCHISULTRA;
+          }
+        }
+        else
+        {
+          if (aic_inb(p, TARG_SCSIRATE + i) & ~WIDEXFER)
+          {
+            sc->device_flags[i] |= CFSYNCH;
+            if (p->features & AHC_ULTRA)
+              sc->device_flags[i] |= ((p->ultraenb & mask) ?
+                                      CFSYNCHISULTRA : 0);
+          }
+        }
+      }
+      else
+      {
+        /*
+         * Assume the BIOS has NOT been run on this card and nothing between
+         * the card and the devices is configured yet.
+         */
+        sc->device_flags[i] = CFDISC;
+        if (p->features & AHC_WIDE)
+          sc->device_flags[i] |= CFWIDEB;
+        if (p->features & AHC_ULTRA3)
+          sc->device_flags[i] |= 2;
+        else if (p->features & AHC_ULTRA2)
+          sc->device_flags[i] |= 3;
+        else if (p->features & AHC_ULTRA)
+          sc->device_flags[i] |= CFSYNCHISULTRA;
+        sc->device_flags[i] |= CFSYNCH;
+        aic_outb(p, 0, TARG_SCSIRATE + i);
+        if (p->features & AHC_ULTRA2)
+          aic_outb(p, 0, TARG_OFFSET + i);
+      }
+    }
+    if (sc->device_flags[i] & CFDISC)
+    {
+      p->discenable |= mask;
+    }
+    if (p->flags & AHC_NEWEEPROM_FMT)
+    {
+      if ( !(p->features & AHC_ULTRA2) )
+      {
+        /*
+         * I know of two different Ultra BIOSes that do this differently.
+         * One on the Gigabyte 6BXU mb that wants flags[i] & CFXFER to
+         * be == to 0x03 and SYNCHISULTRA to be true to mean 40MByte/s
+         * while on the IBM Netfinity 5000 they want the same thing
+         * to be something else, while flags[i] & CFXFER == 0x03 and
+         * SYNCHISULTRA false should be 40MByte/s.  So, we set both to
+         * 40MByte/s and the lower speeds be damned.  People will have
+         * to select around the conversely mapped lower speeds in order
+         * to select lower speeds on these boards.
+         */
+        if ( (sc->device_flags[i] & CFNEWULTRAFORMAT) &&
+            ((sc->device_flags[i] & CFXFER) == 0x03) )
+        {
+          sc->device_flags[i] &= ~CFXFER;
+          sc->device_flags[i] |= CFSYNCHISULTRA;
+        }
+        if (sc->device_flags[i] & CFSYNCHISULTRA)
+        {
+          p->ultraenb |= mask;
+        }
+      }
+      else if ( !(sc->device_flags[i] & CFNEWULTRAFORMAT) &&
+                 (p->features & AHC_ULTRA2) &&
+		 (sc->device_flags[i] & CFSYNCHISULTRA) )
+      {
+        p->ultraenb |= mask;
+      }
+    }
+    else if (sc->adapter_control & CFULTRAEN)
+    {
+      p->ultraenb |= mask;
+    }
+    if ( (sc->device_flags[i] & CFSYNCH) == 0)
+    {
+      sc->device_flags[i] &= ~CFXFER;
+      p->ultraenb &= ~mask;
+      p->transinfo[i].user_offset = 0;
+      p->transinfo[i].user_period = 0;
+      p->transinfo[i].user_options = 0;
+      p->transinfo[i].cur_offset = 0;
+      p->transinfo[i].cur_period = 0;
+      p->transinfo[i].cur_options = 0;
+      p->needsdtr_copy &= ~mask;
+    }
+    else
+    {
+      if (p->features & AHC_ULTRA3)
+      {
+        p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2;
+        p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i);
+        if( (sc->device_flags[i] & CFXFER) < 0x03 )
+        {
+          scsirate = (sc->device_flags[i] & CFXFER);
+          p->transinfo[i].user_options = MSG_EXT_PPR_OPTION_DT_CRC;
+          if( (aic_inb(p, TARG_SCSIRATE + i) & CFXFER) < 0x03 )
+          {
+            p->transinfo[i].cur_options = 
+              ((aic_inb(p, TARG_SCSIRATE + i) & 0x40) ?
+                 MSG_EXT_PPR_OPTION_DT_CRC : MSG_EXT_PPR_OPTION_DT_UNITS);
+          }
+          else
+          {
+            p->transinfo[i].cur_options = 0;
+          }
+        }
+        else
+        {
+          scsirate = (sc->device_flags[i] & CFXFER) |
+                     ((p->ultraenb & mask) ? 0x18 : 0x10);
+          p->transinfo[i].user_options = 0;
+          p->transinfo[i].cur_options = 0;
+        }
+        p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate,
+                                       AHC_SYNCRATE_ULTRA3);
+        p->transinfo[i].cur_period = aic7xxx_find_period(p,
+                                       aic_inb(p, TARG_SCSIRATE + i),
+                                       AHC_SYNCRATE_ULTRA3);
+      }
+      else if (p->features & AHC_ULTRA2)
+      {
+        p->transinfo[i].user_offset = MAX_OFFSET_ULTRA2;
+        p->transinfo[i].cur_offset = aic_inb(p, TARG_OFFSET + i);
+        scsirate = (sc->device_flags[i] & CFXFER) |
+                   ((p->ultraenb & mask) ? 0x18 : 0x10);
+        p->transinfo[i].user_options = 0;
+        p->transinfo[i].cur_options = 0;
+        p->transinfo[i].user_period = aic7xxx_find_period(p, scsirate,
+                                       AHC_SYNCRATE_ULTRA2);
+        p->transinfo[i].cur_period = aic7xxx_find_period(p,
+                                       aic_inb(p, TARG_SCSIRATE + i),
+                                       AHC_SYNCRATE_ULTRA2);
+      }
+      else
+      {
+        scsirate = (sc->device_flags[i] & CFXFER) << 4;
+        p->transinfo[i].user_options = 0;
+        p->transinfo[i].cur_options = 0;
+        p->transinfo[i].user_offset = MAX_OFFSET_8BIT;
+        if (p->features & AHC_ULTRA)
+        {
+          short ultraenb;
+          ultraenb = aic_inb(p, ULTRA_ENB) |
+            (aic_inb(p, ULTRA_ENB + 1) << 8);
+          p->transinfo[i].user_period = aic7xxx_find_period(p,
+                                          scsirate,
+                                          (p->ultraenb & mask) ?
+                                          AHC_SYNCRATE_ULTRA :
+                                          AHC_SYNCRATE_FAST);
+          p->transinfo[i].cur_period = aic7xxx_find_period(p,
+                                         aic_inb(p, TARG_SCSIRATE + i),
+                                         (ultraenb & mask) ? 
+                                         AHC_SYNCRATE_ULTRA :
+                                         AHC_SYNCRATE_FAST);
+        }
+        else
+          p->transinfo[i].user_period = aic7xxx_find_period(p,
+                                          scsirate, AHC_SYNCRATE_FAST);
+      }
+      p->needsdtr_copy |= mask;
+    }
+    if ( (sc->device_flags[i] & CFWIDEB) && (p->features & AHC_WIDE) )
+    {
+      p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_16_BIT;
+      p->needwdtr_copy |= mask;
+    }
+    else
+    {
+      p->transinfo[i].user_width = MSG_EXT_WDTR_BUS_8_BIT;
+      p->needwdtr_copy &= ~mask;
+    }
+    p->transinfo[i].cur_width =
+      (aic_inb(p, TARG_SCSIRATE + i) & WIDEXFER) ?
+      MSG_EXT_WDTR_BUS_16_BIT : MSG_EXT_WDTR_BUS_8_BIT;
+  }
+  aic_outb(p, ~(p->discenable & 0xFF), DISC_DSB);
+  aic_outb(p, ~((p->discenable >> 8) & 0xFF), DISC_DSB + 1);
+  p->needppr = p->needppr_copy = p->needdv = 0;
+  p->needwdtr = p->needwdtr_copy;
+  p->needsdtr = p->needsdtr_copy;
+  p->dtr_pending = 0;
+
+  /*
+   * We set the p->ultraenb from the SEEPROM to begin with, but now we make
+   * it match what is already down in the card.  If we are doing a reset
+   * on the card then this will get put back to a default state anyway.
+   * This allows us to not have to pre-emptively negotiate when using the
+   * no_reset option.
+   */
+  if (p->features & AHC_ULTRA)
+    p->ultraenb = aic_inb(p, ULTRA_ENB) | (aic_inb(p, ULTRA_ENB + 1) << 8);
+
+  
+  scsi_conf = (p->scsi_id & HSCSIID);
+
+  if(have_seeprom)
+  {
+    p->adapter_control = sc->adapter_control;
+    p->bios_control = sc->bios_control;
+
+    switch (p->chip & AHC_CHIPID_MASK)
+    {
+      case AHC_AIC7895:
+      case AHC_AIC7896:
+      case AHC_AIC7899:
+        if (p->adapter_control & CFBPRIMARY)
+          p->flags |= AHC_CHANNEL_B_PRIMARY;
+      default:
+        break;
+    }
+
+    if (sc->adapter_control & CFSPARITY)
+      scsi_conf |= ENSPCHK;
+  }
+  else
+  {
+    scsi_conf |= ENSPCHK | RESET_SCSI;
+  }
+
+  /*
+   * Only set the SCSICONF and SCSICONF + 1 registers if we are a PCI card.
+   * The 2842 and 2742 cards already have these registers set and we don't
+   * want to muck with them since we don't set all the bits they do.
+   */
+  if ( (p->chip & ~AHC_CHIPID_MASK) == AHC_PCI )
+  {
+    /* Set the host ID */
+    aic_outb(p, scsi_conf, SCSICONF);
+    /* In case we are a wide card */
+    aic_outb(p, p->scsi_id, SCSICONF + 1);
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_detect
+ *
+ * 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)
+{
+  struct aic7xxx_host *temp_p = NULL;
+  struct aic7xxx_host *current_p = NULL;
+  struct aic7xxx_host *list_p = NULL;
+  int found = 0;
+#if defined(__i386__) || defined(__alpha__)
+  ahc_flag_type flags = 0;
+  int type;
+#endif
+  unsigned char sxfrctl1;
+#if defined(__i386__) || defined(__alpha__)
+  unsigned char hcntrl, hostconf;
+  unsigned int slot, base;
+#endif
+
+#ifdef MODULE
+  /*
+   * If we are called as a module, the aic7xxx pointer may not be null
+   * and it would point to our bootup string, just like on the lilo
+   * command line.  IF not NULL, then process this config string with
+   * aic7xxx_setup
+   */
+  if(aic7xxx)
+    aic7xxx_setup(aic7xxx);
+  if(dummy_buffer[0] != 'P')
+    printk(KERN_WARNING "aic7xxx: Please read the file /usr/src/linux/drivers"
+      "/scsi/README.aic7xxx\n"
+      "aic7xxx: to see the proper way to specify options to the aic7xxx "
+      "module\n"
+      "aic7xxx: Specifically, don't use any commas when passing arguments to\n"
+      "aic7xxx: insmod or else it might trash certain memory areas.\n");
+#endif
+
+  template->proc_name = "aic7xxx";
+  template->sg_tablesize = AIC7XXX_MAX_SG;
+
+
+#ifdef CONFIG_PCI
+  /*
+   * PCI-bus probe.
+   */
+  if (pci_present())
+  {
+    struct
+    {
+      unsigned short      vendor_id;
+      unsigned short      device_id;
+      ahc_chip            chip;
+      ahc_flag_type       flags;
+      ahc_feature         features;
+      int                 board_name_index;
+      unsigned short      seeprom_size;
+      unsigned short      seeprom_type;
+    } const aic_pdevs[] = {
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7810, AHC_NONE,
+       AHC_FNONE, AHC_FENONE,                                1,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AHC_AIC7850,
+       AHC_PAGESCBS, AHC_AIC7850_FE,                         5,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7855, AHC_AIC7850,
+       AHC_PAGESCBS, AHC_AIC7850_FE,                         6,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7821, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_3860, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_38602, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_38602, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7860, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MOTHERBOARD,
+       AHC_AIC7860_FE,                                       7,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7861, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7860_FE,                                       8,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MOTHERBOARD,
+       AHC_AIC7870_FE,                                       9,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE,     10,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7870_FE,                                      11,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7870_FE,                                      12,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AHC_AIC7870,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7870_FE,     13,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MOTHERBOARD,
+       AHC_AIC7880_FE,                                      14,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     15,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7880_FE,                                      16,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7880_FE,                                      17,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7885, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7886, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE | AHC_NEW_AUTOTERM, 19,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880,
+       AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE,     18,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7895, AHC_AIC7895,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7895_FE,                                      20,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      21,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7890B, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      21,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2930U2, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      22,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_2940U2, AHC_AIC7890,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7890_FE,                                      23,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7896, AHC_AIC7896,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7896_FE,                                      24,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3940U2, AHC_AIC7896,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7896_FE,                                      25,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_3950U2D, AHC_AIC7896,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7896_FE,                                      26,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_1480A, AHC_AIC7860,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_NO_STPWEN,
+       AHC_AIC7860_FE,                                      27,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892A, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      28,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892B, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      28,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892D, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      28,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7892P, AHC_AIC7892,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED,
+       AHC_AIC7892_FE,                                      28,
+       32, C46 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899A, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7899_FE,                                      29,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899B, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7899_FE,                                      29,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899D, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7899_FE,                                      29,
+       32, C56_66 },
+      {PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_7899P, AHC_AIC7899,
+       AHC_PAGESCBS | AHC_NEWEEPROM_FMT | AHC_BIOS_ENABLED | AHC_MULTI_CHANNEL,
+       AHC_AIC7899_FE,                                      29,
+       32, C56_66 },
+    };
+
+    unsigned short command;
+    unsigned int  devconfig, i, oldverbose;
+    struct pci_dev *pdev = NULL;
+
+    for (i = 0; i < NUMBER(aic_pdevs); i++)
+    {
+      pdev = NULL;
+      while ((pdev = pci_find_device(aic_pdevs[i].vendor_id,
+                                     aic_pdevs[i].device_id,
+                                     pdev))) {
+	if (pci_enable_device(pdev))
+		continue;
+        if ( i == 0 ) /* We found one, but it's the 7810 RAID cont. */
+        {
+          if (aic7xxx_verbose & (VERBOSE_PROBE|VERBOSE_PROBE2))
+          {
+            printk(KERN_INFO "aic7xxx: The 7810 RAID controller is not "
+              "supported by\n");
+            printk(KERN_INFO "         this driver, we are ignoring it.\n");
+          }
+        }
+        else if ( (temp_p = kmalloc(sizeof(struct aic7xxx_host),
+                                    GFP_ATOMIC)) != NULL )
+        {
+          memset(temp_p, 0, sizeof(struct aic7xxx_host));
+          temp_p->chip = aic_pdevs[i].chip | AHC_PCI;
+          temp_p->flags = aic_pdevs[i].flags;
+          temp_p->features = aic_pdevs[i].features;
+          temp_p->board_name_index = aic_pdevs[i].board_name_index;
+          temp_p->sc_size = aic_pdevs[i].seeprom_size;
+          temp_p->sc_type = aic_pdevs[i].seeprom_type;
+
+          /*
+           * Read sundry information from PCI BIOS.
+           */
+          temp_p->irq = pdev->irq;
+          temp_p->pdev = pdev;
+          temp_p->pci_bus = pdev->bus->number;
+          temp_p->pci_device_fn = pdev->devfn;
+          temp_p->base = pci_resource_start(pdev, 0);
+          temp_p->mbase = pci_resource_start(pdev, 1);
+          current_p = list_p;
+	  while(current_p && temp_p)
+	  {
+	    if ( ((current_p->pci_bus == temp_p->pci_bus) &&
+	          (current_p->pci_device_fn == temp_p->pci_device_fn)) ||
+                 (temp_p->base && (current_p->base == temp_p->base)) ||
+                 (temp_p->mbase && (current_p->mbase == temp_p->mbase)) )
+	    {
+              /* duplicate PCI entry, skip it */
+	      kfree(temp_p);
+	      temp_p = NULL;
+	    }
+	    current_p = current_p->next;
+	  }
+	  if ( temp_p == NULL )
+            continue;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk("aic7xxx: <%s> at PCI %d/%d\n", 
+              board_names[aic_pdevs[i].board_name_index],
+              PCI_SLOT(pdev->devfn),
+              PCI_FUNC(pdev->devfn));
+          pci_read_config_word(pdev, PCI_COMMAND, &command);
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+          {
+            printk("aic7xxx: Initial PCI_COMMAND value was 0x%x\n",
+              (int)command);
+          }
+#ifdef AIC7XXX_STRICT_PCI_SETUP
+          command |= PCI_COMMAND_SERR | PCI_COMMAND_PARITY |
+            PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#else
+          command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
+#endif
+          command &= ~PCI_COMMAND_INVALIDATE;
+          if (aic7xxx_pci_parity == 0)
+            command &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
+          pci_write_config_word(pdev, PCI_COMMAND, command);
+#ifdef AIC7XXX_STRICT_PCI_SETUP
+          pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+          {
+            printk("aic7xxx: Initial DEVCONFIG value was 0x%x\n", devconfig);
+          }
+          devconfig |= 0x80000040;
+          pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+#endif /* AIC7XXX_STRICT_PCI_SETUP */
+
+          if(temp_p->base && check_region(temp_p->base, MAXREG - MINREG))
+          {
+            printk("aic7xxx: <%s> at PCI %d/%d/%d\n", 
+              board_names[aic_pdevs[i].board_name_index],
+              temp_p->pci_bus,
+              PCI_SLOT(temp_p->pci_device_fn),
+              PCI_FUNC(temp_p->pci_device_fn));
+            printk("aic7xxx: I/O ports already in use, ignoring.\n");
+            kfree(temp_p);
+            temp_p = NULL;
+            continue;
+          }
+
+          temp_p->unpause = INTEN;
+          temp_p->pause = temp_p->unpause | PAUSE;
+          if ( ((temp_p->base == 0) &&
+                (temp_p->mbase == 0)) ||
+               (temp_p->irq == 0) )
+          {
+            printk("aic7xxx: <%s> at PCI %d/%d/%d\n", 
+              board_names[aic_pdevs[i].board_name_index],
+              temp_p->pci_bus,
+              PCI_SLOT(temp_p->pci_device_fn),
+              PCI_FUNC(temp_p->pci_device_fn));
+            printk("aic7xxx: Controller disabled by BIOS, ignoring.\n");
+            kfree(temp_p);
+            temp_p = NULL;
+            continue;
+          }
+
+#ifdef MMAPIO
+          if ( !(temp_p->base) || !(temp_p->flags & AHC_MULTI_CHANNEL) ||
+               ((temp_p->chip != (AHC_AIC7870 | AHC_PCI)) &&
+                (temp_p->chip != (AHC_AIC7880 | AHC_PCI))) )
+          {
+            unsigned long page_offset, base;
+
+            base = temp_p->mbase & PAGE_MASK;
+            page_offset = temp_p->mbase - base;
+            temp_p->maddr = ioremap_nocache(base, page_offset + 256);
+            if(temp_p->maddr)
+            {
+              temp_p->maddr += page_offset;
+              /*
+               * We need to check the I/O with the MMAPed address.  Some machines
+               * simply fail to work with MMAPed I/O and certain controllers.
+               */
+              if(aic_inb(temp_p, HCNTRL) == 0xff)
+              {
+                /*
+                 * OK.....we failed our test....go back to programmed I/O
+                 */
+                printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n", 
+                  board_names[aic_pdevs[i].board_name_index],
+                  temp_p->pci_bus,
+                  PCI_SLOT(temp_p->pci_device_fn),
+                  PCI_FUNC(temp_p->pci_device_fn));
+                printk(KERN_INFO "aic7xxx: MMAPed I/O failed, reverting to "
+                                 "Programmed I/O.\n");
+                iounmap((void *) (((unsigned long) temp_p->maddr) & PAGE_MASK));
+                temp_p->maddr = 0;
+                if(temp_p->base == 0)
+                {
+                  printk("aic7xxx: <%s> at PCI %d/%d/%d\n", 
+                    board_names[aic_pdevs[i].board_name_index],
+                    temp_p->pci_bus,
+                    PCI_SLOT(temp_p->pci_device_fn),
+                    PCI_FUNC(temp_p->pci_device_fn));
+                  printk("aic7xxx: Controller disabled by BIOS, ignoring.\n");
+                  kfree(temp_p);
+                  temp_p = NULL;
+                  continue;
+                }
+              }
+            }
+          }
+#endif
+
+          /*
+           * Lock out other contenders for our i/o space.
+           */
+          if(temp_p->base)
+            request_region(temp_p->base, MAXREG - MINREG, "aic7xxx");
+
+          /*
+           * We HAVE to make sure the first pause_sequencer() and all other
+           * subsequent I/O that isn't PCI config space I/O takes place
+           * after the MMAPed I/O region is configured and tested.  The
+           * problem is the PowerPC architecture that doesn't support
+           * programmed I/O at all, so we have to have the MMAP I/O set up
+           * for this pause to even work on those machines.
+           */
+          pause_sequencer(temp_p);
+
+          /*
+           * Clear out any pending PCI error status messages.  Also set
+           * verbose to 0 so that we don't emit strange PCI error messages
+           * while cleaning out the current status bits.
+           */
+          oldverbose = aic7xxx_verbose;
+          aic7xxx_verbose = 0;
+          aic7xxx_pci_intr(temp_p);
+          aic7xxx_verbose = oldverbose;
+
+          temp_p->bios_address = 0;
+
+          /*
+           * Remember how the card was setup in case there is no seeprom.
+           */
+          if (temp_p->features & AHC_ULTRA2)
+            temp_p->scsi_id = aic_inb(temp_p, SCSIID_ULTRA2) & OID;
+          else
+            temp_p->scsi_id = aic_inb(temp_p, SCSIID) & OID;
+          /*
+           * Get current termination setting
+           */
+          sxfrctl1 = aic_inb(temp_p, SXFRCTL1);
+
+          if (aic7xxx_chip_reset(temp_p) == -1)
+          {
+            release_region(temp_p->base, MAXREG - MINREG);
+            kfree(temp_p);
+            temp_p = NULL;
+            continue;
+          }
+          /*
+           * Very quickly put the term setting back into the register since
+           * the chip reset may cause odd things to happen.  This is to keep
+           * LVD busses with lots of drives from draining the power out of
+           * the diffsense line before we get around to running the
+           * configure_termination() function.  Also restore the STPWLEVEL
+           * bit of DEVCONFIG
+           */
+          aic_outb(temp_p, sxfrctl1, SXFRCTL1);
+          pci_write_config_dword(temp_p->pdev, DEVCONFIG, devconfig);
+          sxfrctl1 &= STPWEN;
+
+          /*
+           * We need to set the CHNL? assignments before loading the SEEPROM
+           * The 3940 and 3985 cards (original stuff, not any of the later
+           * stuff) are 7870 and 7880 class chips.  The Ultra2 stuff falls
+           * under 7896 and 7897.  The 7895 is in a class by itself :)
+           */
+          switch (temp_p->chip & AHC_CHIPID_MASK)
+          {
+            case AHC_AIC7870: /* 3840 / 3985 */
+            case AHC_AIC7880: /* 3840 UW / 3985 UW */
+              if(temp_p->flags & AHC_MULTI_CHANNEL)
+              {
+                switch(PCI_SLOT(temp_p->pci_device_fn))
+                {
+                  case 5:
+                    temp_p->flags |= AHC_CHNLB;
+                    break;
+                  case 8:
+                    temp_p->flags |= AHC_CHNLB;
+                    break;
+                  case 12:
+                    temp_p->flags |= AHC_CHNLC;
+                    break;
+                  default:
+                    break;
+                }
+              }
+              break;
+
+            case AHC_AIC7895: /* 7895 */
+            case AHC_AIC7896: /* 7896/7 */
+            case AHC_AIC7899: /* 7899 */
+              if (PCI_FUNC(pdev->devfn) != 0)
+              {
+                temp_p->flags |= AHC_CHNLB;
+              }
+              /*
+               * The 7895 is the only chipset that sets the SCBSIZE32 param
+               * in the DEVCONFIG register.  The Ultra2 chipsets use
+               * the DSCOMMAND0 register instead.
+               */
+              if ((temp_p->chip & AHC_CHIPID_MASK) == AHC_AIC7895)
+              {
+                pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+                devconfig |= SCBSIZE32;
+                pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+              }
+              break;
+            default:
+              break;
+          }
+
+          /*
+           * Loading of the SEEPROM needs to come after we've set the flags
+           * to indicate possible CHNLB and CHNLC assigments.  Otherwise,
+           * on 394x and 398x cards we'll end up reading the wrong settings
+           * for channels B and C
+           */
+          switch (temp_p->chip & AHC_CHIPID_MASK)
+          {
+            case AHC_AIC7892:
+            case AHC_AIC7899:
+              aic_outb(temp_p, 0, SCAMCTL);
+              /*
+               * Switch to the alt mode of the chip...
+               */
+              aic_outb(temp_p, aic_inb(temp_p, SFUNCT) | ALT_MODE, SFUNCT);
+              /*
+               * Set our options...the last two items set our CRC after x byte
+	       * count in target mode...
+               */
+              aic_outb(temp_p, AUTO_MSGOUT_DE | DIS_MSGIN_DUALEDGE, OPTIONMODE);
+	      aic_outb(temp_p, 0x00, 0x0b);
+	      aic_outb(temp_p, 0x10, 0x0a);
+              /*
+               * switch back to normal mode...
+               */
+              aic_outb(temp_p, aic_inb(temp_p, SFUNCT) & ~ALT_MODE, SFUNCT);
+              aic_outb(temp_p, CRCVALCHKEN | CRCENDCHKEN | CRCREQCHKEN |
+			       TARGCRCENDEN | TARGCRCCNTEN,
+                       CRCCONTROL1);
+              aic_outb(temp_p, ((aic_inb(temp_p, DSCOMMAND0) | USCBSIZE32 |
+                                 MPARCKEN | CIOPARCKEN | CACHETHEN) & 
+                               ~DPARCKEN), DSCOMMAND0);
+              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+              break;
+            case AHC_AIC7890:
+            case AHC_AIC7896:
+              aic_outb(temp_p, 0, SCAMCTL);
+              aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+                                CACHETHEN | MPARCKEN | USCBSIZE32 |
+                                CIOPARCKEN) & ~DPARCKEN, DSCOMMAND0);
+              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+              break;
+            case AHC_AIC7850:
+            case AHC_AIC7860:
+              /*
+               * Set the DSCOMMAND0 register on these cards different from
+               * on the 789x cards.  Also, read the SEEPROM as well.
+               */
+              aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+                                CACHETHEN | MPARCKEN) & ~DPARCKEN,
+                       DSCOMMAND0);
+              /* FALLTHROUGH */
+            default:
+              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+              break;
+            case AHC_AIC7880:
+              /*
+               * Check the rev of the chipset before we change DSCOMMAND0
+               */
+              pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+              if ((devconfig & 0xff) >= 1)
+              {
+                aic_outb(temp_p, (aic_inb(temp_p, DSCOMMAND0) |
+                                  CACHETHEN | MPARCKEN) & ~DPARCKEN,
+                         DSCOMMAND0);
+              }
+              aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+              break;
+          }
+          
+
+          /*
+           * and then we need another switch based on the type in order to
+           * make sure the channel B primary flag is set properly on 7895
+           * controllers....Arrrgggghhh!!!  We also have to catch the fact
+           * that when you disable the BIOS on the 7895 on the Intel DK440LX
+           * motherboard, and possibly others, it only sets the BIOS disabled
+           * bit on the A channel...I think I'm starting to lean towards
+           * going postal....
+           */
+          switch(temp_p->chip & AHC_CHIPID_MASK)
+          {
+            case AHC_AIC7895:
+            case AHC_AIC7896:
+            case AHC_AIC7899:
+              current_p = list_p;
+              while(current_p != NULL)
+              {
+                if ( (current_p->pci_bus == temp_p->pci_bus) &&
+                     (PCI_SLOT(current_p->pci_device_fn) ==
+                      PCI_SLOT(temp_p->pci_device_fn)) )
+                {
+                  if ( PCI_FUNC(current_p->pci_device_fn) == 0 )
+                  {
+                    temp_p->flags |= 
+                      (current_p->flags & AHC_CHANNEL_B_PRIMARY);
+                    temp_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
+                    temp_p->flags |=
+                      (current_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
+                  }
+                  else
+                  {
+                    current_p->flags |=
+                      (temp_p->flags & AHC_CHANNEL_B_PRIMARY);
+                    current_p->flags &= ~(AHC_BIOS_ENABLED|AHC_USEDEFAULTS);
+                    current_p->flags |=
+                      (temp_p->flags & (AHC_BIOS_ENABLED|AHC_USEDEFAULTS));
+                  }
+                }
+                current_p = current_p->next;
+              }
+              break;
+            default:
+              break;
+          }
+
+          /*
+           * We only support external SCB RAM on the 7895/6/7 chipsets.
+           * We could support it on the 7890/1 easy enough, but I don't
+           * know of any 7890/1 based cards that have it.  I do know
+           * of 7895/6/7 cards that have it and they work properly.
+           */
+          switch(temp_p->chip & AHC_CHIPID_MASK)
+          {
+            default:
+              break;
+            case AHC_AIC7895:
+            case AHC_AIC7896:
+            case AHC_AIC7899:
+              pci_read_config_dword(pdev, DEVCONFIG, &devconfig);
+              if (temp_p->features & AHC_ULTRA2)
+              {
+                if ( (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) &&
+                     (aic7xxx_scbram) )
+                {
+                  aic_outb(temp_p,
+                           aic_inb(temp_p, DSCOMMAND0) & ~SCBRAMSEL_ULTRA2,
+                           DSCOMMAND0);
+                  temp_p->flags |= AHC_EXTERNAL_SRAM;
+                  devconfig |= EXTSCBPEN;
+                }
+                else if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2)
+                {
+                  printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n", 
+                    board_names[aic_pdevs[i].board_name_index],
+                    temp_p->pci_bus,
+                    PCI_SLOT(temp_p->pci_device_fn),
+                    PCI_FUNC(temp_p->pci_device_fn));
+                  printk("aic7xxx: external SCB RAM detected, "
+                         "but not enabled\n");
+                }
+              }
+              else
+              {
+                if ((devconfig & RAMPSM) && (aic7xxx_scbram))
+                {
+                  devconfig &= ~SCBRAMSEL;
+                  devconfig |= EXTSCBPEN;
+                  temp_p->flags |= AHC_EXTERNAL_SRAM;
+                }
+                else if (devconfig & RAMPSM)
+                {
+                  printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d/%d\n", 
+                    board_names[aic_pdevs[i].board_name_index],
+                    temp_p->pci_bus,
+                    PCI_SLOT(temp_p->pci_device_fn),
+                    PCI_FUNC(temp_p->pci_device_fn));
+                  printk("aic7xxx: external SCB RAM detected, "
+                         "but not enabled\n");
+                }
+              }
+              pci_write_config_dword(pdev, DEVCONFIG, devconfig);
+              if ( (temp_p->flags & AHC_EXTERNAL_SRAM) &&
+                   (temp_p->flags & AHC_CHNLB) )
+                aic_outb(temp_p, 1, CCSCBBADDR);
+              break;
+          }
+
+          /*
+           * Take the LED out of diagnostic mode
+           */
+          aic_outb(temp_p, 
+            (aic_inb(temp_p, SBLKCTL) & ~(DIAGLEDEN | DIAGLEDON)),
+            SBLKCTL);
+
+          /*
+           * We don't know where this is set in the SEEPROM or by the
+           * BIOS, so we default to 100%.  On Ultra2 controllers, use 75%
+           * instead.
+           */
+          if (temp_p->features & AHC_ULTRA2)
+          {
+            aic_outb(temp_p, RD_DFTHRSH_MAX | WR_DFTHRSH_MAX, DFF_THRSH);
+          }
+          else
+          {
+            aic_outb(temp_p, DFTHRSH_100, DSPCISTATUS);
+          }
+
+          if ( list_p == NULL )
+          {
+            list_p = current_p = temp_p;
+          }
+          else
+          {
+            current_p = list_p;
+            while(current_p->next != NULL)
+              current_p = current_p->next;
+            current_p->next = temp_p;
+          }
+          temp_p->next = NULL;
+          found++;
+        }  /* Found an Adaptec PCI device. */
+        else /* Well, we found one, but we couldn't get any memory */
+        {
+          printk("aic7xxx: Found <%s>\n", 
+            board_names[aic_pdevs[i].board_name_index]);
+          printk(KERN_INFO "aic7xxx: Unable to allocate device memory, "
+            "skipping.\n");
+        }
+      } /* while(pdev=....) */
+    } /* for PCI_DEVICES */
+  } /* PCI BIOS present */
+#endif CONFIG_PCI
+
+#if defined(__i386__) || defined(__alpha__)
+  /*
+   * EISA/VL-bus card signature probe.
+   */
+  slot = MINSLOT;
+  while ( (slot <= MAXSLOT) && 
+         !(aic7xxx_no_probe) )
+  {
+    base = SLOTBASE(slot) + MINREG;
+
+    if (check_region(base, MAXREG - MINREG))
+    {
+      /*
+       * Some other driver has staked a
+       * claim to this i/o region already.
+       */
+      slot++;
+      continue; /* back to the beginning of the for loop */
+    }
+    flags = 0;
+    type = aic7xxx_probe(slot, base + AHC_HID0, &flags);
+    if (type == -1)
+    {
+      slot++;
+      continue;
+    }
+    temp_p = kmalloc(sizeof(struct aic7xxx_host), GFP_ATOMIC);
+    if (temp_p == NULL)
+    {
+      printk(KERN_WARNING "aic7xxx: Unable to allocate device space.\n");
+      slot++;
+      continue; /* back to the beginning of the while loop */
+    }
+    /*
+     * Lock out other contenders for our i/o space.
+     */
+    request_region(base, MAXREG - MINREG, "aic7xxx");
+
+    /*
+     * Pause the card preserving the IRQ type.  Allow the operator
+     * to override the IRQ trigger.
+     */
+    if (aic7xxx_irq_trigger == 1)
+      hcntrl = IRQMS;  /* Level */
+    else if (aic7xxx_irq_trigger == 0)
+      hcntrl = 0;  /* Edge */
+    else
+      hcntrl = inb(base + HCNTRL) & IRQMS;  /* Default */
+    memset(temp_p, 0, sizeof(struct aic7xxx_host));
+    temp_p->unpause = hcntrl | INTEN;
+    temp_p->pause = hcntrl | PAUSE | INTEN;
+    temp_p->base = base;
+    temp_p->mbase = 0;
+    temp_p->maddr = 0;
+    temp_p->pci_bus = 0;
+    temp_p->pci_device_fn = slot;
+    aic_outb(temp_p, hcntrl | PAUSE, HCNTRL);
+    while( (aic_inb(temp_p, HCNTRL) & PAUSE) == 0 ) ;
+    if (aic7xxx_chip_reset(temp_p) == -1)
+      temp_p->irq = 0;
+    else
+      temp_p->irq = aic_inb(temp_p, INTDEF) & 0x0F;
+    temp_p->flags |= AHC_PAGESCBS;
+
+    switch (temp_p->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 %d, ignoring.\n", temp_p->irq);
+        kfree(temp_p);
+        release_region(base, MAXREG - MINREG);
+        slot++;
+        continue; /* back to the beginning of the while loop */
+    }
+
+    /*
+     * We are commited now, everything has been checked and this card
+     * has been found, now we just set it up
+     */
+
+    /*
+     * Insert our new struct into the list at the end
+     */
+    if (list_p == NULL)
+    {
+      list_p = current_p = temp_p;
+    }
+    else
+    {
+      current_p = list_p;
+      while (current_p->next != NULL)
+        current_p = current_p->next;
+      current_p->next = temp_p;
+    }
+
+    switch (type)
+    {
+      case 0:
+        temp_p->board_name_index = 2;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("aic7xxx: <%s> at EISA %d\n",
+               board_names[2], slot);
+        /* FALLTHROUGH */
+      case 1:
+      {
+        temp_p->chip = AHC_AIC7770 | AHC_EISA;
+        temp_p->features |= AHC_AIC7770_FE;
+        temp_p->bios_control = aic_inb(temp_p, 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 (temp_p->board_name_index == 0)
+        {
+          temp_p->board_name_index = 3;
+          if (aic7xxx_verbose & VERBOSE_PROBE2)
+            printk("aic7xxx: <%s> at EISA %d\n",
+                 board_names[3], slot);
+        }
+        if (temp_p->bios_control & CHANNEL_B_PRIMARY)
+        {
+          temp_p->flags |= AHC_CHANNEL_B_PRIMARY;
+        }
+
+        if ((temp_p->bios_control & BIOSMODE) == BIOSDISABLED)
+        {
+          temp_p->flags &= ~AHC_BIOS_ENABLED;
+        }
+        else
+        {
+          temp_p->flags &= ~AHC_USEDEFAULTS;
+          temp_p->flags |= AHC_BIOS_ENABLED;
+          if ( (temp_p->bios_control & 0x20) == 0 )
+          {
+            temp_p->bios_address = 0xcc000;
+            temp_p->bios_address += (0x4000 * (temp_p->bios_control & 0x07));
+          }
+          else
+          {
+            temp_p->bios_address = 0xd0000;
+            temp_p->bios_address += (0x8000 * (temp_p->bios_control & 0x06));
+          }
+        }
+        temp_p->adapter_control = aic_inb(temp_p, SCSICONF) << 8;
+        temp_p->adapter_control |= aic_inb(temp_p, SCSICONF + 1);
+        if (temp_p->features & AHC_WIDE)
+        {
+          temp_p->scsi_id = temp_p->adapter_control & HWSCSIID;
+          temp_p->scsi_id_b = temp_p->scsi_id;
+        }
+        else
+        {
+          temp_p->scsi_id = (temp_p->adapter_control >> 8) & HSCSIID;
+          temp_p->scsi_id_b = temp_p->adapter_control & HSCSIID;
+        }
+        aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+        break;
+      }
+
+      case 2:
+      case 3:
+        temp_p->chip = AHC_AIC7770 | AHC_VL;
+        temp_p->features |= AHC_AIC7770_FE;
+        if (type == 2)
+          temp_p->flags |= AHC_BIOS_ENABLED;
+        else
+          temp_p->flags &= ~AHC_BIOS_ENABLED;
+        if (aic_inb(temp_p, SCSICONF) & TERM_ENB)
+          sxfrctl1 = STPWEN;
+        aic7xxx_load_seeprom(temp_p, &sxfrctl1);
+        temp_p->board_name_index = 4;
+        if (aic7xxx_verbose & VERBOSE_PROBE2)
+          printk("aic7xxx: <%s> at VLB %d\n",
+               board_names[2], slot);
+        switch( aic_inb(temp_p, STATUS_2840) & BIOS_SEL )
+        {
+          case 0x00:
+            temp_p->bios_address = 0xe0000;
+            break;
+          case 0x20:
+            temp_p->bios_address = 0xc8000;
+            break;
+          case 0x40:
+            temp_p->bios_address = 0xd0000;
+            break;
+          case 0x60:
+            temp_p->bios_address = 0xd8000;
+            break;
+          default:
+            break; /* can't get here */
+        }
+        break;
+
+      default:  /* Won't get here. */
+        break;
+    }
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+    {
+      printk(KERN_INFO "aic7xxx: BIOS %sabled, IO Port 0x%lx, IRQ %d (%s)\n",
+        (temp_p->flags & AHC_USEDEFAULTS) ? "dis" : "en", temp_p->base,
+        temp_p->irq,
+        (temp_p->pause & IRQMS) ? "level sensitive" : "edge triggered");
+      printk(KERN_INFO "aic7xxx: Extended translation %sabled.\n",
+             (temp_p->flags & AHC_EXTEND_TRANS_A) ? "en" : "dis");
+    }
+
+    /*
+     * Set the FIFO threshold and the bus off time.
+     */
+    hostconf = aic_inb(temp_p, HOSTCONF);
+    aic_outb(temp_p, hostconf & DFTHRSH, BUSSPD);
+    aic_outb(temp_p, (hostconf << 2) & BOFF, BUSTIME);
+    slot++;
+    found++;
+  }
+
+#endif /* defined(__i386__) || defined(__alpha__) */
+
+  /*
+   * Now, we re-order the probed devices by BIOS address and BUS class.
+   * In general, we follow this algorithm to make the adapters show up
+   * in the same order under linux that the computer finds them.
+   *  1: All VLB/EISA cards with BIOS_ENABLED first, according to BIOS
+   *     address, going from lowest to highest.
+   *  2: All PCI controllers with BIOS_ENABLED next, according to BIOS
+   *     address, going from lowest to highest.
+   *  3: Remaining VLB/EISA controllers going in slot order.
+   *  4: Remaining PCI controllers, going in PCI device order (reversable)
+   */
+
+  {
+    struct aic7xxx_host *sort_list[4] = { NULL, NULL, NULL, NULL };
+    struct aic7xxx_host *vlb, *pci;
+    struct aic7xxx_host *prev_p;
+    struct aic7xxx_host *p;
+    unsigned char left;
+
+    prev_p = vlb = pci = NULL;
+
+    temp_p = list_p;
+    while (temp_p != NULL)
+    {
+      switch(temp_p->chip & ~AHC_CHIPID_MASK)
+      {
+        case AHC_EISA:
+        case AHC_VL:
+        {
+          p = temp_p;
+          if (p->flags & AHC_BIOS_ENABLED)
+            vlb = sort_list[0];
+          else
+            vlb = sort_list[2];
+
+          if (vlb == NULL)
+          {
+            vlb = temp_p;
+            temp_p = temp_p->next;
+            vlb->next = NULL;
+          }
+          else
+          {
+            current_p = vlb;
+            prev_p = NULL;
+            while ( (current_p != NULL) &&
+                    (current_p->bios_address < temp_p->bios_address))
+            {
+              prev_p = current_p;
+              current_p = current_p->next;
+            }
+            if (prev_p != NULL)
+            {
+              prev_p->next = temp_p;
+              temp_p = temp_p->next;
+              prev_p->next->next = current_p;
+            }
+            else
+            {
+              vlb = temp_p;
+              temp_p = temp_p->next;
+              vlb->next = current_p;
+            }
+          }
+          
+          if (p->flags & AHC_BIOS_ENABLED)
+            sort_list[0] = vlb;
+          else
+            sort_list[2] = vlb;
+          
+          break;
+        }
+        default:  /* All PCI controllers fall through to default */
+        {
+
+          p = temp_p;
+          if (p->flags & AHC_BIOS_ENABLED) 
+            pci = sort_list[1];
+          else
+            pci = sort_list[3];
+
+          if (pci == NULL)
+          {
+            pci = temp_p;
+            temp_p = temp_p->next;
+            pci->next = NULL;
+          }
+          else
+          {
+            current_p = pci;
+            prev_p = NULL;
+            if (!aic7xxx_reverse_scan)
+            {
+              while ( (current_p != NULL) &&
+                      ( (PCI_SLOT(current_p->pci_device_fn) |
+                        (current_p->pci_bus << 8)) < 
+                        (PCI_SLOT(temp_p->pci_device_fn) |
+                        (temp_p->pci_bus << 8)) ) )
+              {
+                prev_p = current_p;
+                current_p = current_p->next;
+              }
+            }
+            else
+            {
+              while ( (current_p != NULL) &&
+                      ( (PCI_SLOT(current_p->pci_device_fn) |
+                        (current_p->pci_bus << 8)) > 
+                        (PCI_SLOT(temp_p->pci_device_fn) |
+                        (temp_p->pci_bus << 8)) ) )
+              {
+                prev_p = current_p;
+                current_p = current_p->next;
+              }
+            }
+            /*
+             * Are we dealing with a 7895/6/7/9 where we need to sort the
+             * channels as well, if so, the bios_address values should
+             * be the same
+             */
+            if ( (current_p) && (temp_p->flags & AHC_MULTI_CHANNEL) &&
+                 (temp_p->pci_bus == current_p->pci_bus) &&
+                 (PCI_SLOT(temp_p->pci_device_fn) ==
+                  PCI_SLOT(current_p->pci_device_fn)) )
+            {
+              if (temp_p->flags & AHC_CHNLB)
+              {
+                if ( !(temp_p->flags & AHC_CHANNEL_B_PRIMARY) )
+                {
+                  prev_p = current_p;
+                  current_p = current_p->next;
+                }
+              }
+              else
+              {
+                if (temp_p->flags & AHC_CHANNEL_B_PRIMARY)
+                {
+                  prev_p = current_p;
+                  current_p = current_p->next;
+                }
+              }
+            }
+            if (prev_p != NULL)
+            {
+              prev_p->next = temp_p;
+              temp_p = temp_p->next;
+              prev_p->next->next = current_p;
+            }
+            else
+            {
+              pci = temp_p;
+              temp_p = temp_p->next;
+              pci->next = current_p;
+            }
+          }
+
+          if (p->flags & AHC_BIOS_ENABLED)
+            sort_list[1] = pci;
+          else
+            sort_list[3] = pci;
+
+          break;
+        }
+      }  /* End of switch(temp_p->type) */
+    } /* End of while (temp_p != NULL) */
+    /*
+     * At this point, the cards have been broken into 4 sorted lists, now
+     * we run through the lists in order and register each controller
+     */
+    {
+      int i;
+      
+      left = found;
+      for (i=0; i<NUMBER(sort_list); i++)
+      {
+        temp_p = sort_list[i];
+        while(temp_p != NULL)
+        {
+          template->name = board_names[temp_p->board_name_index];
+          p = aic7xxx_alloc(template, temp_p);
+          if (p != NULL)
+          {
+            p->instance = found - left;
+            if (aic7xxx_register(template, p, (--left)) == 0)
+            {
+              found--;
+              aic7xxx_release(p->host);
+              scsi_unregister(p->host);
+            }
+            else if (aic7xxx_dump_card)
+            {
+              pause_sequencer(p);
+              aic7xxx_print_card(p);
+              aic7xxx_print_scratch_ram(p);
+              unpause_sequencer(p, TRUE);
+            }
+          }
+          current_p = temp_p;
+          temp_p = (struct aic7xxx_host *)temp_p->next;
+          kfree(current_p);
+        }
+      }
+    }
+  }
+  return (found);
+}
+
+static void aic7xxx_build_negotiation_cmnd(struct aic7xxx_host *p,
+                                           Scsi_Cmnd *old_cmd, int tindex);
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_allocate_negotiation_command
+ *
+ * Description:
+ *   allocate the actual command struct and fill in the gaps...
+ *-F*************************************************************************/
+static Scsi_Cmnd *
+aic7xxx_allocate_negotiation_command(struct aic7xxx_host *p,
+                                     Scsi_Cmnd *old_cmd, int tindex)
+{
+  Scsi_Cmnd *cmd;
+  char *buffer;
+
+  if (!(p->dev_dtr_cmnd[tindex] = kmalloc(sizeof(Scsi_Cmnd), GFP_ATOMIC)) )
+  {
+    return(NULL);
+  }
+  if (!(buffer = kmalloc(256, GFP_ATOMIC)))
+  {
+    kfree(p->dev_dtr_cmnd[tindex]);
+    p->dev_dtr_cmnd[tindex] = NULL;
+    return(NULL);
+  }
+  cmd = p->dev_dtr_cmnd[tindex];
+  memset(cmd, 0, sizeof(Scsi_Cmnd));
+  memcpy(cmd, old_cmd, sizeof(Scsi_Cmnd));
+  memset(&cmd->cmnd[0], 0, sizeof(cmd->cmnd));
+  memset(&cmd->data_cmnd[0], 0, sizeof(cmd->data_cmnd));
+  cmd->lun = 0;
+  cmd->request_bufflen = 255;
+  cmd->request_buffer = buffer;
+  cmd->sc_data_direction = SCSI_DATA_READ;
+  cmd->use_sg = cmd->old_use_sg = cmd->sglist_len = 0;
+  cmd->bufflen = 0;
+  cmd->buffer = NULL;
+  cmd->underflow = 0;
+  cmd->cmd_len = 6;
+  cmd->cmnd[0] = cmd->data_cmnd[0] = INQUIRY;
+  cmd->cmnd[1] = cmd->data_cmnd[1] = 0;
+  cmd->cmnd[2] = cmd->data_cmnd[2] = 0;
+  cmd->cmnd[3] = cmd->data_cmnd[3] = 0;
+  cmd->cmnd[4] = cmd->data_cmnd[4] = 255; /* match what scsi.c does here */
+  cmd->cmnd[5] = cmd->data_cmnd[5] = 0;
+  return(cmd);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_negotiation_complete
+ *
+ * Description:
+ *   Handle completion events for our Negotiation commands.  Clear out the
+ *   struct and get it ready for its next use.
+ *-F*************************************************************************/
+static void
+aic7xxx_negotiation_complete(Scsi_Cmnd *cmd)
+{
+  unsigned int checksum;
+  int i;
+  int *ibuffer;
+  struct aic7xxx_host *p = (struct aic7xxx_host *)cmd->host->hostdata;
+  int tindex = TARGET_INDEX(cmd);
+  struct aic7xxx_syncrate *syncrate;
+
+  /*
+   * perform our minimalistic domain validation
+   */
+  if(p->dev_flags[tindex] & DEVICE_SCANNED)
+  {
+    ibuffer = (int *)cmd->request_buffer;
+    checksum = 0;
+    for(i = 0; i < (cmd->request_bufflen >> 2); i++)
+    {
+      checksum += ibuffer[i];
+    }
+    if( (checksum != p->dev_checksum[tindex]) &&
+        (p->transinfo[tindex].cur_offset != 0) )
+    {
+      unsigned int period = p->transinfo[tindex].cur_period;
+      unsigned char options = p->transinfo[tindex].cur_options;
+
+      if (p->needdv & (1<<tindex))
+      {
+        /*
+         * oops, we had a failure, lower the transfer rate and try again.  It's
+         * worth noting here that it might be wise to also check for typical
+         * wide setting on narrow cable type problems and try disabling wide
+         * instead of slowing down if those exist.  That's hard to do with simple
+         * checksums though.
+         */
+        if(aic7xxx_verbose & VERBOSE_NEGOTIATION2) 
+        {
+          printk(INFO_LEAD "reducing SCSI transfer speed due to Domain "
+                 "validation failure.\n", p->host_no, CTL_OF_CMD(cmd));
+        }
+        if((syncrate = aic7xxx_find_syncrate(p, &period, 0, &options)) != NULL)
+        {
+          syncrate++;
+          if( (syncrate->rate[0] != NULL) &&
+              (!(p->features & AHC_ULTRA2) || (syncrate->sxfr_ultra2 == 0)) )
+          {
+            p->transinfo[tindex].goal_period = syncrate->period;
+            if( p->transinfo[tindex].goal_period > 9 )
+            {
+              p->transinfo[tindex].goal_options = 0;
+              p->needppr &= ~(1<<tindex);
+              p->needsdtr |= (1<<tindex);
+              p->needppr_copy &= ~(1<<tindex);
+              p->needsdtr_copy |= (1<<tindex);
+              if (p->transinfo[tindex].goal_width)
+              {
+                p->needwdtr |= (1<<tindex);
+                p->needwdtr_copy |= (1<<tindex);
+              }
+            }
+          }
+          else if (p->transinfo[tindex].goal_width)
+          {
+            p->transinfo[tindex].goal_width = 0;
+            p->needwdtr &= ~(1<<tindex);
+            p->needwdtr_copy &= ~(1<<tindex);
+            p->transinfo[tindex].goal_offset =
+              p->transinfo[tindex].user_offset;
+            p->transinfo[tindex].goal_period =
+              p->transinfo[tindex].user_period;
+            p->transinfo[tindex].goal_options =
+              p->transinfo[tindex].user_options;
+            if( p->transinfo[tindex].goal_period <= 9 )
+            {
+              p->needppr |= (1<<tindex);
+              p->needsdtr &= ~(1<<tindex);
+              p->needppr_copy |= (1<<tindex);
+              p->needsdtr_copy &= ~(1<<tindex);
+            }
+            else
+            {
+              p->needppr &= ~(1<<tindex);
+              p->needsdtr |= (1<<tindex);
+              p->needppr_copy &= ~(1<<tindex);
+              p->needsdtr_copy |= (1<<tindex);
+            }
+          }
+          else
+          {
+            p->transinfo[tindex].goal_offset = 0;
+            p->transinfo[tindex].goal_period = 255;
+            p->transinfo[tindex].goal_options = 0;
+            p->transinfo[tindex].goal_width = 0;
+            p->needppr &= ~(1<<tindex);
+            p->needsdtr &= ~(1<<tindex);
+            p->needwdtr &= ~(1<<tindex);
+            p->needppr_copy &= ~(1<<tindex);
+            p->needsdtr_copy &= ~(1<<tindex);
+            p->needwdtr_copy &= ~(1<<tindex);
+          }
+        }
+        p->needdv &= ~(1<<tindex);
+      }
+      else
+      {
+        if(aic7xxx_verbose & VERBOSE_NEGOTIATION2) 
+        {
+          printk(INFO_LEAD "Performing Domain validation.\n",
+                 p->host_no, CTL_OF_CMD(cmd));
+        }
+        /*
+         * Update the checksum in case the INQUIRY data has changed, maybe
+         * in relation to a change in the mode pages, or whatever.
+         */
+        p->dev_checksum[tindex] = checksum;
+        /*
+         * Signal that we are trying out the domain validation
+         */
+        p->needdv |= (1<<tindex);
+        /*
+         * Signal that we need to re-negotiate things, this also gets us our
+         * INQUIRY command to re-checksum off of.
+         */
+        p->needppr |= (p->needppr_copy & (1<<tindex));
+        p->needsdtr |= (p->needsdtr_copy & (1<<tindex));
+        p->needwdtr |= (p->needwdtr_copy & (1<<tindex));
+      }
+    } 
+    else
+    {
+      if( (aic7xxx_verbose & VERBOSE_NEGOTIATION2) &&
+          (p->needdv & (1<<tindex)) )
+      {
+        printk(INFO_LEAD "Successfully completed Domain validation.\n",
+               p->host_no, CTL_OF_CMD(cmd));
+      }
+      /*
+       * We successfully did our checksum, so don't leave the needdv flag set
+       * in case we might have set it last time through.
+       */
+      p->needdv &= ~(1<<tindex);
+    }
+  }
+
+  p->dtr_pending &= ~(0x01 << tindex);
+  /*
+   * This looks recursive in the extreme, but if this was a WDTR negotiation
+   * and we didn't follow up with SDTR yet, then this will get it started.
+   * For all other cases, this should work out to be a no-op, unless we are
+   * doing domain validation and happen to need a new negotiation command.
+   *
+   * In case we don't want this to go any further, the cmdcmplt interrupt
+   * handler will NULL out the cmd->next entry so that the real SCSI command
+   * can be sent back to the mid layer code with SENSE data intact.  We'll
+   * finish things up when the cmd gets sent back down to us, so no worries.
+   */
+  if(cmd->next)
+  {
+    aic7xxx_build_negotiation_cmnd(p, cmd->next, tindex);
+  }
+  return;
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_build_negotiation_command
+ *
+ * Description:
+ *   Build a Scsi_Cmnd structure to perform negotiation with or else send
+ *   a pre-built command specifically for this purpose.
+ *-F*************************************************************************/
+static void
+aic7xxx_build_negotiation_cmnd(struct aic7xxx_host *p, Scsi_Cmnd *old_cmd,
+  int tindex)
+{
+
+  if ( !(p->dtr_pending & (1<<tindex)) &&
+       ( (p->needppr & (1<<tindex)) ||
+         (p->needwdtr & (1<<tindex)) ||
+         (p->needsdtr & (1<<tindex)) ) )
+  {
+    if ( (p->dev_dtr_cmnd[tindex] == NULL) &&
+         (aic7xxx_allocate_negotiation_command(p, old_cmd, tindex) == NULL) )
+    {
+      return;
+    }
+    /*
+     * Before sending this thing out, we also make the cmd->next pointer
+     * point to the real command so we can stuff any possible SENSE data
+     * into the real command instead of this fake command.  This has to be
+     * done each time the command is built, not just the first time, hence
+     * it's outside of the above if()...
+     */
+    p->dev_dtr_cmnd[tindex]->next = old_cmd;
+    /*
+     * Clear the buffer so checksums come out right....
+     */
+    memset(p->dev_dtr_cmnd[tindex]->request_buffer, 0,
+           p->dev_dtr_cmnd[tindex]->request_bufflen);
+    /*
+     * Remove any commands for this particular device that might be on the
+     * waiting_scbs queue or qinfifo so that this command goes out first.
+     * This is vital for our implementation of domain validation.
+     */
+    pause_sequencer(p);
+    aic7xxx_search_qinfifo(p, old_cmd->target, old_cmd->channel, ALL_LUNS,
+                SCB_LIST_NULL, 0, TRUE, &p->delayed_scbs[tindex]);
+    unpause_sequencer(p, FALSE);
+    {
+      struct aic7xxx_scb *scb, *next;
+
+      scb = p->waiting_scbs.head;
+      while(scb != NULL)
+      {
+        if( aic7xxx_match_scb(p, scb, old_cmd->target, old_cmd->channel,
+                              ALL_LUNS, SCB_LIST_NULL) )
+        {
+          next = scb->q_next;
+          scbq_remove(&p->waiting_scbs, scb);
+          scbq_insert_tail(&p->delayed_scbs[tindex], scb);
+          scb = next;
+        }
+        else
+        {
+          scb = scb->q_next;
+        }
+      }
+    }
+    aic7xxx_queue(p->dev_dtr_cmnd[tindex], 
+                  aic7xxx_negotiation_complete);
+  }
+}
+
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_print_scb
+ *
+ * Description:
+ *   Dump the byte codes for an about to be sent SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+  int i;
+  unsigned char *x;  
+
+  x = (unsigned char *)&scb->hscb->control;
+
+  for(i=0; i<32; i++)
+  {
+    printk("%02x ", x[i]);
+  }
+  printk("\n");
+}
+#endif
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_buildscb
+ *
+ * Description:
+ *   Build a SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
+    struct aic7xxx_scb *scb)
+{
+  unsigned short mask;
+  struct aic7xxx_hwscb *hscb;
+  unsigned char tindex = TARGET_INDEX(cmd);
+
+  mask = (0x01 << tindex);
+  hscb = scb->hscb;
+
+  /*
+   * Setup the control byte if we need negotiation and have not
+   * already requested it.
+   */
+  hscb->control = 0;
+  scb->tag_action = 0;
+  cmd->tag = hscb->tag;
+  if (p->discenable & mask)
+  {
+    hscb->control |= DISCENB;
+    if ( (p->tagenable & mask) &&
+         (cmd->cmnd[0] != TEST_UNIT_READY) )
+    {
+      p->dev_commands_sent[tindex]++;
+      if (p->dev_commands_sent[tindex] < 200)
+      {
+        hscb->control |= MSG_SIMPLE_Q_TAG;
+        scb->tag_action = MSG_SIMPLE_Q_TAG;
+      }
+      else
+      {
+        if (p->orderedtag & mask)
+        {
+          hscb->control |= MSG_ORDERED_Q_TAG;
+          scb->tag_action = MSG_ORDERED_Q_TAG;
+        }
+        else
+        {
+          hscb->control |= MSG_SIMPLE_Q_TAG;
+          scb->tag_action = MSG_SIMPLE_Q_TAG;
+        }
+        p->dev_commands_sent[tindex] = 0;
+      }
+    }
+  }
+  if ( cmd == p->dev_dtr_cmnd[tindex] )
+  {
+    p->dtr_pending |= mask;
+    scb->tag_action = 0;
+    if (p->dev_flags[tindex] & DEVICE_SCANNED)
+    {
+      hscb->control &= DISCENB;
+      hscb->control |= MK_MESSAGE;
+      if(p->needppr & mask)
+      {
+        scb->flags |= SCB_MSGOUT_PPR;
+      }
+      else if(p->needwdtr & mask)
+      {
+        scb->flags |= SCB_MSGOUT_WDTR;
+      }
+      else if(p->needsdtr & mask)
+      {
+        scb->flags |= SCB_MSGOUT_SDTR;
+      }
+    }
+  }
+  if ( !(p->dtr_pending & mask) &&
+        ( (p->needppr & mask) ||
+          (p->needwdtr & mask) ||
+          (p->needsdtr & mask) ) )
+  {
+    aic7xxx_build_negotiation_cmnd(p, cmd, tindex);
+  }
+  hscb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
+        ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
+
+  /*
+   * The interpretation of request_buffer and request_bufflen
+   * changes depending on whether or not use_sg is zero; a
+   * non-zero use_sg indicates the number of elements in the
+   * scatter-gather array.
+   */
+
+  /*
+   * XXX - this relies on the host data being stored in a
+   *       little-endian format.
+   */
+  hscb->SCSI_cmd_length = cmd->cmd_len;
+  memcpy(scb->cmnd, cmd->cmnd, cmd->cmd_len);
+  hscb->SCSI_cmd_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, scb->cmnd));
+
+  if (cmd->use_sg)
+  {
+    struct scatterlist *sg;  /* Must be mid-level SCSI code scatterlist */
+
+    /*
+     * We must build an SG list in adapter format, as the kernel's SG list
+     * cannot be used directly because of data field size (__alpha__)
+     * differences and the kernel SG list uses virtual addresses where
+     * we need physical addresses.
+     */
+    int i, use_sg;
+
+    sg = (struct scatterlist *)cmd->request_buffer;
+    scb->sg_length = 0;
+    use_sg = pci_map_sg(p->pdev, sg, cmd->use_sg, scsi_to_pci_dma_dir(cmd->sc_data_direction));
+    /*
+     * Copy the segments into the SG array.  NOTE!!! - We used to
+     * have the first entry both in the data_pointer area and the first
+     * SG element.  That has changed somewhat.  We still have the first
+     * entry in both places, but now we download the address of
+     * scb->sg_list[1] instead of 0 to the sg pointer in the hscb.
+     */
+    for (i = 0; i < use_sg; i++)
+    {
+      unsigned int len = sg_dma_len(sg+i);
+      scb->sg_list[i].address = cpu_to_le32(sg_dma_address(sg+i));
+      scb->sg_list[i].length = cpu_to_le32(len);
+      scb->sg_length += len;
+    }
+    /* 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->sg_count = i;
+    hscb->SG_segment_count = i;
+    hscb->SG_list_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, &scb->sg_list[1]));
+  }
+  else
+  {
+    if (cmd->request_bufflen)
+    {
+      unsigned int address = pci_map_single(p->pdev, cmd->request_buffer,
+					    cmd->request_bufflen,
+                                            scsi_to_pci_dma_dir(cmd->sc_data_direction));
+      aic7xxx_mapping(cmd) = address;
+      scb->sg_list[0].address = cpu_to_le32(address);
+      scb->sg_list[0].length = cpu_to_le32(cmd->request_bufflen);
+      scb->sg_count = 1;
+      scb->sg_length = cmd->request_bufflen;
+      hscb->SG_segment_count = 1;
+      hscb->SG_list_pointer = cpu_to_le32(SCB_DMA_ADDR(scb, &scb->sg_list[0]));
+      hscb->data_count = scb->sg_list[0].length;
+      hscb->data_pointer = scb->sg_list[0].address;
+    }
+    else
+    {
+      scb->sg_count = 0;
+      scb->sg_length = 0;
+      hscb->SG_segment_count = 0;
+      hscb->SG_list_pointer = 0;
+      hscb->data_count = 0;
+      hscb->data_pointer = 0;
+    }
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_queue
+ *
+ * Description:
+ *   Queue a SCB to the controller.
+ *-F*************************************************************************/
+int
+aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
+{
+  struct aic7xxx_host *p;
+  struct aic7xxx_scb *scb;
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+  int tindex = TARGET_INDEX(cmd);
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags = 0;
+#endif
+
+  p = (struct aic7xxx_host *) cmd->host->hostdata;
+  /*
+   * Check to see if channel was scanned.
+   */
+  
+#ifdef AIC7XXX_VERBOSE_DEBUGGING
+  if (!(p->flags & AHC_A_SCANNED) && (cmd->channel == 0))
+  {
+    if (aic7xxx_verbose & VERBOSE_PROBE2)
+      printk(INFO_LEAD "Scanning channel for devices.\n",
+        p->host_no, 0, -1, -1);
+    p->flags |= AHC_A_SCANNED;
+  }
+  else
+  {
+    if (!(p->flags & AHC_B_SCANNED) && (cmd->channel == 1))
+    {
+      if (aic7xxx_verbose & VERBOSE_PROBE2)
+        printk(INFO_LEAD "Scanning channel for devices.\n",
+          p->host_no, 1, -1, -1);
+      p->flags |= AHC_B_SCANNED;
+    }
+  }
+
+  if (p->dev_active_cmds[tindex] > (cmd->device->queue_depth + 1))
+  {
+    printk(WARN_LEAD "Commands queued exceeds queue "
+           "depth, active=%d\n",
+           p->host_no, CTL_OF_CMD(cmd), 
+           p->dev_active_cmds[tindex]);
+    if ( p->dev_active_cmds[tindex] > 220 )
+      p->dev_active_cmds[tindex] = 0;
+  }
+#endif
+
+  scb = scbq_remove_head(&p->scb_data->free_scbs);
+  if (scb == NULL)
+  {
+    DRIVER_LOCK
+    aic7xxx_allocate_scb(p);
+    DRIVER_UNLOCK
+    scb = scbq_remove_head(&p->scb_data->free_scbs);
+  }
+  if (scb == NULL)
+  {
+    printk(WARN_LEAD "Couldn't get a free SCB.\n", p->host_no,
+           CTL_OF_CMD(cmd));
+    cmd->result = (DID_BUS_BUSY << 16);
+    DRIVER_LOCK
+    aic7xxx_queue_cmd_complete(p, cmd);
+    DRIVER_UNLOCK
+    return 0;
+  }
+  else
+  {
+    scb->cmd = cmd;
+    aic7xxx_position(cmd) = scb->hscb->tag;
+
+    /*
+     * Construct the SCB beforehand, so the sequencer is
+     * paused a minimal amount of time.
+     */
+    aic7xxx_buildscb(p, cmd, scb);
+
+    /*
+     * Make sure the Scsi_Cmnd pointer is saved, the struct it points to
+     * is set up properly, and the parity error flag is reset, then send
+     * the SCB to the sequencer and watch the fun begin.
+     */
+    cmd->scsi_done = fn;
+    cmd->result = DID_OK;
+    memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+    aic7xxx_error(cmd) = DID_OK;
+    aic7xxx_status(cmd) = 0;
+    cmd->host_scribble = NULL;
+
+    scb->flags |= SCB_ACTIVE | SCB_WAITINGQ;
+
+    DRIVER_LOCK
+    scbq_insert_tail(&p->waiting_scbs, scb);
+    if ( (p->flags & (AHC_IN_ISR | AHC_IN_ABORT | AHC_IN_RESET)) == 0)
+    {
+      aic7xxx_run_waiting_queues(p);
+    }
+    DRIVER_UNLOCK
+  }
+  return (0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_bus_device_reset
+ *
+ * Description:
+ *   Abort or reset the current SCSI command(s).  If the scb has not
+ *   previously been aborted, then we attempt to send a BUS_DEVICE_RESET
+ *   message to the target.  If the scb has previously been unsuccessfully
+ *   aborted, then we will reset the channel and have all devices renegotiate.
+ *   Returns an enumerated type that indicates the status of the operation.
+ *-F*************************************************************************/
+static int
+aic7xxx_bus_device_reset(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
+{
+  struct aic7xxx_scb   *scb;
+  struct aic7xxx_hwscb *hscb;
+  int result = -1;
+  int channel;
+  unsigned char saved_scbptr, lastphase;
+  unsigned char hscb_index;
+  int disconnected;
+
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+  hscb = scb->hscb;
+
+  lastphase = aic_inb(p, LASTPHASE);
+  if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+  {
+    printk(INFO_LEAD "Bus Device reset, scb flags 0x%x, ",
+         p->host_no, CTL_OF_SCB(scb), scb->flags);
+    switch (lastphase)
+    {
+      case P_DATAOUT:
+        printk("Data-Out phase\n");
+        break;
+      case P_DATAIN:
+        printk("Data-In phase\n");
+        break;
+      case P_COMMAND:
+        printk("Command phase\n");
+        break;
+      case P_MESGOUT:
+        printk("Message-Out phase\n");
+        break;
+      case P_STATUS:
+        printk("Status phase\n");
+        break;
+      case P_MESGIN:
+        printk("Message-In phase\n");
+        break;
+      default:
+      /*
+       * We're not in a valid phase, so assume we're idle.
+       */
+        printk("while idle, LASTPHASE = 0x%x\n", lastphase);
+        break;
+    }
+    printk(INFO_LEAD "SCSISIGI 0x%x, SEQADDR 0x%x, SSTAT0 0x%x, SSTAT1 "
+         "0x%x\n", p->host_no, CTL_OF_SCB(scb),
+         aic_inb(p, SCSISIGI),
+         aic_inb(p, SEQADDR0) | (aic_inb(p, SEQADDR1) << 8),
+         aic_inb(p, SSTAT0), aic_inb(p, SSTAT1));
+  }
+
+  channel = cmd->channel;
+
+    /*
+     * Send a Device Reset 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 = aic_inb(p, SCBPTR);
+  disconnected = FALSE;
+
+  if (lastphase != P_BUSFREE)
+  {
+    if (aic_inb(p, SCB_TAG) >= p->scb_data->numscbs)
+    {
+      printk(WARN_LEAD "Invalid SCB ID %d is active, "
+             "SCB flags = 0x%x.\n", p->host_no,
+            CTL_OF_CMD(cmd), scb->hscb->tag, scb->flags);
+      return(SCSI_RESET_ERROR);
+    }
+    if (scb->hscb->tag == aic_inb(p, SCB_TAG))
+    { 
+      if ( (lastphase != P_MESGOUT) && (lastphase != P_MESGIN) )
+      {
+        if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+          printk(INFO_LEAD "Device reset message in "
+                "message buffer\n", p->host_no, CTL_OF_SCB(scb));
+        scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+        aic7xxx_error(scb->cmd) = DID_RESET;
+        p->dev_flags[TARGET_INDEX(scb->cmd)] |= 
+                BUS_DEVICE_RESET_PENDING;
+        /* Send the abort message to the active SCB. */
+        aic_outb(p, HOST_MSG, MSG_OUT);
+        aic_outb(p, lastphase | ATNO, SCSISIGO);
+        return(SCSI_RESET_PENDING);
+      }
+      else
+      {
+        /* We want to send out the message, but it could screw an already */
+        /* in place and being used message.  Instead, we return an error  */
+        /* to try and start the bus reset phase since this command is     */
+        /* probably hung (aborts failed, and now reset is failing).  We   */
+        /* also make sure to set BUS_DEVICE_RESET_PENDING so we won't try */
+        /* any more on this device, but instead will escalate to a bus or */
+        /* host reset (additionally, we won't try to abort any more).     */
+        printk(WARN_LEAD "Device reset, Message buffer "
+                "in use\n", p->host_no, CTL_OF_SCB(scb));
+        scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+        aic7xxx_error(scb->cmd) = DID_RESET;
+        p->dev_flags[TARGET_INDEX(scb->cmd)] |= 
+                BUS_DEVICE_RESET_PENDING;
+        return(SCSI_RESET_ERROR);
+      }
+    }
+  } /* if (last_phase != P_BUSFREE).....indicates we are idle and can work */
+  hscb_index = aic7xxx_find_scb(p, scb);
+  if (hscb_index == SCB_LIST_NULL)
+  {
+    disconnected = (aic7xxx_scb_on_qoutfifo(p, scb)) ? FALSE : TRUE;
+  }
+  else
+  {
+    aic_outb(p, hscb_index, SCBPTR);
+    if (aic_inb(p, SCB_CONTROL) & DISCONNECTED)
+    {
+      disconnected = TRUE;
+    }
+  }
+  if (disconnected)
+  {
+        /*
+         * Simply set the MK_MESSAGE flag and the SEQINT handler will do
+         * the rest on a reconnect.
+         */
+    scb->hscb->control |= MK_MESSAGE;
+    scb->flags |= SCB_RESET | SCB_DEVICE_RESET;
+    p->dev_flags[TARGET_INDEX(scb->cmd)] |= 
+        BUS_DEVICE_RESET_PENDING;
+    if (hscb_index != SCB_LIST_NULL)
+    {
+      unsigned char scb_control;
+
+      aic_outb(p, hscb_index, SCBPTR);
+      scb_control = aic_inb(p, SCB_CONTROL);
+      aic_outb(p, scb_control | MK_MESSAGE, 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, then this will be the only
+         * outstanding command and we can simply shove it on the
+         * qoutfifo and be done.  If it is tagged, then it goes right
+         * in with all the others, no problem :)  We need to add it
+         * to the qinfifo and let the sequencer know it is there.
+         * Now, the only problem left to deal with is, *IF* this
+         * command completes, in spite of the MK_MESSAGE bit in the
+         * control byte, then we need to pick that up in the interrupt
+         * routine and clean things up.  This *shouldn't* ever happen.
+         */
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+      printk(INFO_LEAD "Queueing device reset "
+           "command.\n", p->host_no, CTL_OF_SCB(scb));
+    p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+    if (p->features & AHC_QUEUE_REGS)
+      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+    else
+      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+    scb->flags |= SCB_QUEUED_ABORT;
+    result = SCSI_RESET_PENDING;
+  }
+  else if (result == -1)
+  {
+    result = SCSI_RESET_ERROR;
+  }
+  aic_outb(p, saved_scbptr, SCBPTR);
+  return (result);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_panic_abort
+ *
+ * Description:
+ *   Abort the current SCSI command(s).
+ *-F*************************************************************************/
+void
+aic7xxx_panic_abort(struct aic7xxx_host *p, Scsi_Cmnd *cmd)
+{
+
+  printk("aic7xxx driver version %s/%s\n", AIC7XXX_C_VERSION,
+         UTS_RELEASE);
+  printk("Controller type:\n    %s\n", board_names[p->board_name_index]);
+  printk("p->flags=0x%lx, p->chip=0x%x, p->features=0x%x, "
+         "sequencer %s paused\n",
+     p->flags, p->chip, p->features,
+    (aic_inb(p, HCNTRL) & PAUSE) ? "is" : "isn't" );
+  pause_sequencer(p);
+  disable_irq(p->irq);
+  aic7xxx_print_card(p);
+  aic7xxx_print_scratch_ram(p);
+  spin_unlock_irq(&io_request_lock);
+  for(;;) barrier();
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_abort
+ *
+ * Description:
+ *   Abort the current SCSI command(s).
+ *-F*************************************************************************/
+int
+aic7xxx_abort(Scsi_Cmnd *cmd)
+{
+  struct aic7xxx_scb  *scb = NULL;
+  struct aic7xxx_host *p;
+  int    result, found=0;
+  unsigned char tmp_char, saved_hscbptr, next_hscbptr, prev_hscbptr;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags = 0;
+#endif
+  Scsi_Cmnd *cmd_next, *cmd_prev;
+
+  p = (struct aic7xxx_host *) cmd->host->hostdata;
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+
+  /*
+   * I added a new config option to the driver: "panic_on_abort" that will
+   * cause the driver to panic and the machine to stop on the first abort
+   * or reset call into the driver.  At that point, it prints out a lot of
+   * usefull information for me which I can then use to try and debug the
+   * problem.  Simply enable the boot time prompt in order to activate this
+   * code.
+   */
+  if (aic7xxx_panic_on_abort)
+    aic7xxx_panic_abort(p, cmd);
+
+  DRIVER_LOCK
+
+/*
+ *  Run the isr to grab any command in the QOUTFIFO and any other misc.
+ *  assundry tasks.  This should also set up the bh handler if there is
+ *  anything to be done, but it won't run until we are done here since
+ *  we are following a straight code path without entering the scheduler
+ *  code.
+ */
+  pause_sequencer(p);
+  while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
+  {
+    aic7xxx_isr(p->irq, p, (void *)NULL);
+    pause_sequencer(p);
+    aic7xxx_done_cmds_complete(p);
+  }
+
+  if ((scb == NULL) || (cmd->serial_number != cmd->serial_number_at_timeout))
+                      /*  Totally bogus cmd since it points beyond our  */
+  {                   /*  valid SCB range or doesn't even match it's own*/
+                      /*  timeout serial number.                        */
+    if (aic7xxx_verbose & VERBOSE_ABORT_MID)
+      printk(INFO_LEAD "Abort called with bogus Scsi_Cmnd "
+        "pointer.\n", p->host_no, CTL_OF_CMD(cmd));
+    unpause_sequencer(p, FALSE);
+    DRIVER_UNLOCK
+    return(SCSI_ABORT_NOT_RUNNING);
+  }
+  if (scb->cmd != cmd)  /*  Hmmm...either this SCB is currently free with a */
+  {                     /*  NULL cmd pointer (NULLed out when freed) or it  */
+                        /*  has already been recycled for another command   */
+                        /*  Either way, this SCB has nothing to do with this*/
+                        /*  command and we need to deal with cmd without    */
+                        /*  touching the SCB.                               */
+                        /*  The theory here is to return a value that will  */
+                        /*  make the queued for complete command actually   */
+                        /*  finish successfully, or to indicate that we     */
+                        /*  don't have this cmd any more and the mid level  */
+                        /*  code needs to find it.                          */
+    cmd_next = p->completeq.head;
+    cmd_prev = NULL;
+    while (cmd_next != NULL) 
+    {
+      if (cmd_next == cmd) 
+      {
+        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+          printk(INFO_LEAD "Abort called for command "
+          "on completeq, completing.\n", p->host_no, CTL_OF_CMD(cmd));
+        if ( cmd_prev == NULL )
+          p->completeq.head = (Scsi_Cmnd *)cmd_next->host_scribble;
+        else
+          cmd_prev->host_scribble = cmd_next->host_scribble;
+        cmd_next->scsi_done(cmd_next);
+        unpause_sequencer(p, FALSE);
+        DRIVER_UNLOCK
+        return(SCSI_ABORT_NOT_RUNNING); /* It's already back as a successful
+                                         * completion */
+      }                                  
+      cmd_prev = cmd_next;
+      cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
+    }
+    if (aic7xxx_verbose & VERBOSE_ABORT_MID)
+      printk(INFO_LEAD "Abort called for already completed"
+        " command.\n", p->host_no, CTL_OF_CMD(cmd));
+    unpause_sequencer(p, FALSE);
+    DRIVER_UNLOCK
+    return(SCSI_ABORT_NOT_RUNNING);
+  }
+    
+/*   At this point we know the following:
+ *     the SCB pointer is valid
+ *     the command pointer passed in to us and the scb->cmd pointer match
+ *     this then means that the command we need to abort is the same as the
+ *     command held by the scb pointer and is a valid abort request.
+ *   Now, we just have to figure out what to do from here.  Current plan is:
+ *     if we have already been here on this command, escalate to a reset
+ *     if scb is on waiting list or QINFIFO, send it back as aborted, but
+ *       we also need to be aware of the possibility that we could be using
+ *       a faked negotiation command that is holding this command up,  if
+ *       so we need to take care of that command instead, which means we
+ *       would then treat this one like it was sitting around disconnected
+ *       instead.
+ *     if scb is on WAITING_SCB list in sequencer, free scb and send back
+ *     if scb is disconnected and not completed, abort with abort message
+ *     if scb is currently running, then it may be causing the bus to hang
+ *       so we want a return value that indicates a reset would be appropriate
+ *       if the command does not finish shortly
+ *     if scb is already complete but not on completeq, we're screwed because
+ *       this can't happen (except if the command is in the QOUTFIFO, in which
+ *       case we would like it to complete successfully instead of having to
+ *       to be re-done)
+ *   All other scenarios already dealt with by previous code.
+ */
+
+  if ( scb->flags & (SCB_ABORT | SCB_RESET | SCB_QUEUED_ABORT) )
+  {
+    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+      printk(INFO_LEAD "SCB aborted once already, "
+        "escalating.\n", p->host_no, CTL_OF_SCB(scb));
+    unpause_sequencer(p, FALSE);
+    DRIVER_UNLOCK
+    return(SCSI_ABORT_SNOOZE);
+  }
+  if ( (p->flags & (AHC_RESET_PENDING | AHC_ABORT_PENDING)) || 
+          (p->dev_flags[TARGET_INDEX(scb->cmd)] & 
+           BUS_DEVICE_RESET_PENDING) )
+  {
+    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+      printk(INFO_LEAD "Reset/Abort pending for this "
+        "device, not wasting our time.\n", p->host_no, CTL_OF_SCB(scb));
+    unpause_sequencer(p, FALSE);
+    DRIVER_UNLOCK
+    return(SCSI_ABORT_PENDING);
+  }
+
+  found = 0;
+  p->flags |= AHC_IN_ABORT;
+  if (aic7xxx_verbose & VERBOSE_ABORT)
+    printk(INFO_LEAD "Aborting scb %d, flags 0x%x\n",
+         p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+
+/*
+ *   First, let's check to see if the currently running command is our target
+ *    since if it is, the return is fairly easy and quick since we don't want
+ *    to touch the command in case it might complete, but we do want a timeout
+ *    in case it's actually hung, so we really do nothing, but tell the mid
+ *    level code to reset the timeout.
+ */
+
+  if ( scb->hscb->tag == aic_inb(p, SCB_TAG) )
+  {
+   /*
+    *  Check to see if the sequencer is just sitting on this command, or
+    *   if it's actively being run.
+    */
+    result = aic_inb(p, LASTPHASE);
+    switch (result)
+    {
+      case P_DATAOUT:    /*    For any of these cases, we can assume we are */
+      case P_DATAIN:     /*    an active command and act according.  For    */
+      case P_COMMAND:    /*    anything else we are going to fall on through*/
+      case P_STATUS:     /*    The SCSI_ABORT_SNOOZE will give us two abort */
+      case P_MESGOUT:    /*    chances to finish and then escalate to a     */
+      case P_MESGIN:     /*    reset call                                   */
+        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+          printk(INFO_LEAD "SCB is currently active.  "
+                "Waiting on completion.\n", p->host_no, CTL_OF_SCB(scb));
+        unpause_sequencer(p, FALSE);
+        p->flags &= ~AHC_IN_ABORT;
+        scb->flags |= SCB_RECOVERY_SCB; /*  Note the fact that we've been  */
+        p->flags |= AHC_ABORT_PENDING;  /*  here so we will know not to    */
+        DRIVER_UNLOCK                   /*  muck with other SCBs if this   */
+        return(SCSI_ABORT_PENDING);     /*  one doesn't complete and clear */
+        break;                          /*  out.                           */
+      default:
+        break;
+    }
+  }
+
+  if ((found == 0) && (scb->flags & SCB_WAITINGQ))
+  {
+    int tindex = TARGET_INDEX(cmd);
+    unsigned short mask;
+
+    mask = (1 << tindex);
+
+    if (p->dtr_pending & mask)
+    {
+      if (p->dev_dtr_cmnd[tindex]->next != cmd)
+        found = 1;
+      else
+        found = 0;
+    }
+    else
+    {
+      found = 1;
+    }
+    if (found == 0)
+    {
+      /*
+       * OK..this means the command we are currently getting an abort
+       * for has an outstanding negotiation command in front of it.
+       * We don't really have a way to tie back into the negotiation
+       * commands, so we just send this back as pending, then it
+       * will get reset in 2 seconds.
+       */
+      unpause_sequencer(p, TRUE);
+      scb->flags |= SCB_ABORT;
+      DRIVER_UNLOCK
+      return(SCSI_ABORT_PENDING);
+    }
+    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS) 
+      printk(INFO_LEAD "SCB found on waiting list and "
+          "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+    scbq_remove(&p->waiting_scbs, scb);
+    scbq_remove(&p->delayed_scbs[tindex], scb);
+    p->dev_active_cmds[tindex]++;
+    p->activescbs++;
+    scb->flags &= ~(SCB_WAITINGQ | SCB_ACTIVE);
+    scb->flags |= SCB_ABORT | SCB_QUEUED_FOR_DONE;
+    found = 1;
+  }
+
+/*
+ *  We just checked the waiting_q, now for the QINFIFO
+ */
+  if ( found == 0 )
+  {
+    if ( ((found = aic7xxx_search_qinfifo(p, cmd->target, 
+                     cmd->channel,
+                     cmd->lun, scb->hscb->tag, SCB_ABORT | SCB_QUEUED_FOR_DONE,
+                     FALSE, NULL)) != 0) &&
+                    (aic7xxx_verbose & VERBOSE_ABORT_PROCESS))
+      printk(INFO_LEAD "SCB found in QINFIFO and "
+        "aborted.\n", p->host_no, CTL_OF_SCB(scb));
+  }
+
+/*
+ *  QINFIFO, waitingq, completeq done.  Next, check WAITING_SCB list in card
+ */
+
+  if ( found == 0 )
+  {
+    unsigned char scb_next_ptr;
+    prev_hscbptr = SCB_LIST_NULL;
+    saved_hscbptr = aic_inb(p, SCBPTR);
+    next_hscbptr = aic_inb(p, WAITING_SCBH);
+    while ( next_hscbptr != SCB_LIST_NULL )
+    {
+      aic_outb(p,  next_hscbptr, SCBPTR );
+      if ( scb->hscb->tag == aic_inb(p, SCB_TAG) )
+      {
+        found = 1;
+        if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+          printk(INFO_LEAD "SCB found on hardware waiting"
+            " list and aborted.\n", p->host_no, CTL_OF_SCB(scb));
+        if ( prev_hscbptr == SCB_LIST_NULL )
+        {
+            aic_outb(p, aic_inb(p, SCB_NEXT), WAITING_SCBH);
+            /* stop the selection since we just
+             * grabbed the scb out from under the
+             * card
+             */
+            aic_outb(p, aic_inb(p, SCSISEQ) & ~ENSELO, SCSISEQ);
+            aic_outb(p, CLRSELTIMEO, CLRSINT1);
+        }
+        else
+        {
+            scb_next_ptr = aic_inb(p, SCB_NEXT);
+            aic_outb(p, prev_hscbptr, SCBPTR);
+            aic_outb(p, scb_next_ptr, SCB_NEXT);
+            aic_outb(p, next_hscbptr, SCBPTR);
+        }
+        aic_outb(p, SCB_LIST_NULL, SCB_TAG);
+        aic_outb(p, 0, SCB_CONTROL);
+        aic7xxx_add_curscb_to_free_list(p);
+        scb->flags = SCB_ABORT | SCB_QUEUED_FOR_DONE;
+        break;
+      }
+      prev_hscbptr = next_hscbptr;
+      next_hscbptr = aic_inb(p, SCB_NEXT);
+    }
+    aic_outb(p,  saved_hscbptr, SCBPTR );
+  }
+        
+/*
+ *  Hmmm...completeq, QOUTFIFO, QINFIFO, WAITING_SCBH, waitingq all checked.
+ *  OK...the sequencer's paused, interrupts are off, and we haven't found the
+ *  command anyplace where it could be easily aborted.  Time for the hard
+ *  work.  We also know the command is valid.  This essentially means the
+ *  command is disconnected, or connected but not into any phases yet, which
+ *  we know due to the tests we ran earlier on the current active scb phase.
+ *  At this point we can queue the abort tag and go on with life.
+ */
+
+  if ( found == 0 )
+  {
+    p->flags |= AHC_ABORT_PENDING;
+    scb->flags |= SCB_QUEUED_ABORT | SCB_ABORT | SCB_RECOVERY_SCB;
+    scb->hscb->control |= MK_MESSAGE;
+    result=aic7xxx_find_scb(p, scb);
+    if ( result != SCB_LIST_NULL ) 
+    {
+      saved_hscbptr = aic_inb(p, SCBPTR);
+      aic_outb(p, result, SCBPTR);
+      tmp_char = aic_inb(p, SCB_CONTROL);
+      aic_outb(p,  tmp_char | MK_MESSAGE, SCB_CONTROL);
+      aic_outb(p, saved_hscbptr, SCBPTR);
+    }
+    if (aic7xxx_verbose & VERBOSE_ABORT_PROCESS)
+      printk(INFO_LEAD "SCB disconnected.  Queueing Abort"
+        " SCB.\n", p->host_no, CTL_OF_SCB(scb));
+    p->qinfifo[p->qinfifonext++] = scb->hscb->tag;
+    if (p->features & AHC_QUEUE_REGS)
+      aic_outb(p, p->qinfifonext, HNSCB_QOFF);
+    else
+      aic_outb(p, p->qinfifonext, KERNEL_QINPOS);
+  }
+  if (found)
+  {
+    aic7xxx_run_done_queue(p, TRUE);
+    aic7xxx_run_waiting_queues(p);
+  }
+  p->flags &= ~AHC_IN_ABORT;
+  unpause_sequencer(p, FALSE);
+  DRIVER_UNLOCK
+
+/*
+ *  On the return value.  If we found the command and aborted it, then we know
+ *  it's already sent back and there is no reason for a further timeout, so
+ *  we use SCSI_ABORT_SUCCESS.  On the queued abort side, we aren't so certain
+ *  there hasn't been a bus hang or something that might keep the abort from
+ *  from completing.  Therefore, we use SCSI_ABORT_PENDING.  The first time this
+ *  is passed back, the timeout on the command gets extended, the second time
+ *  we pass this back, the mid level SCSI code calls our reset function, which
+ *  would shake loose a hung bus.
+ */
+  if ( found != 0 )
+    return(SCSI_ABORT_SUCCESS);
+  else
+    return(SCSI_ABORT_PENDING); 
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_reset
+ *
+ * Description:
+ *   Resetting the bus always succeeds - is has to, otherwise the
+ *   kernel will panic! Try a surgical technique - sending a BUS
+ *   DEVICE RESET message - on the offending target before pulling
+ *   the SCSI bus reset line.
+ *-F*************************************************************************/
+int
+aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags)
+{
+  struct aic7xxx_scb *scb = NULL;
+  struct aic7xxx_host *p;
+  int    tindex;
+  int    result = -1;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,95)
+  unsigned long cpu_flags = 0;
+#endif
+#define DEVICE_RESET 0x01
+#define BUS_RESET    0x02
+#define HOST_RESET   0x04
+#define FAIL         0x08
+#define RESET_DELAY  0x10
+  int        action;
+  Scsi_Cmnd *cmd_prev, *cmd_next;
+
+
+  if ( cmd == NULL )
+  {
+    printk(KERN_WARNING "(scsi?:?:?:?) Reset called with NULL Scsi_Cmnd "
+      "pointer, failing.\n");
+    return(SCSI_RESET_SNOOZE);
+  }
+
+  p = (struct aic7xxx_host *) cmd->host->hostdata;
+  scb = (p->scb_data->scb_array[aic7xxx_position(cmd)]);
+  tindex = TARGET_INDEX(cmd);
+
+  /*
+   * I added a new config option to the driver: "panic_on_abort" that will
+   * cause the driver to panic and the machine to stop on the first abort
+   * or reset call into the driver.  At that point, it prints out a lot of
+   * usefull information for me which I can then use to try and debug the
+   * problem.  Simply enable the boot time prompt in order to activate this
+   * code.
+   */
+  if (aic7xxx_panic_on_abort)
+    aic7xxx_panic_abort(p, cmd);
+
+  DRIVER_LOCK
+
+  pause_sequencer(p);
+  while ( (aic_inb(p, INTSTAT) & INT_PEND) && !(p->flags & AHC_IN_ISR))
+  {
+    aic7xxx_isr(p->irq, p, (void *)NULL );
+    pause_sequencer(p);
+    aic7xxx_done_cmds_complete(p);
+  }
+
+  if (scb == NULL)
+  {
+    if (aic7xxx_verbose & VERBOSE_RESET_MID)
+      printk(INFO_LEAD "Reset called with bogus Scsi_Cmnd"
+           "->SCB mapping, improvising.\n", p->host_no, CTL_OF_CMD(cmd));
+    if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
+    {
+      action = HOST_RESET;
+    }
+    else
+    {
+      action = BUS_RESET;
+    }
+  }
+  else if (scb->cmd != cmd) 
+  {
+    if (aic7xxx_verbose & VERBOSE_RESET_MID)
+    printk(INFO_LEAD "Reset called with recycled SCB "
+        "for cmd.\n", p->host_no, CTL_OF_CMD(cmd));
+    cmd_prev = NULL;
+    cmd_next = p->completeq.head;
+    while ( cmd_next != NULL )
+    {
+      if (cmd_next == cmd)
+      {
+        if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+          printk(INFO_LEAD "Reset, found cmd on completeq"
+          ", completing.\n", p->host_no, CTL_OF_CMD(cmd));
+        unpause_sequencer(p, FALSE);
+        DRIVER_UNLOCK
+        return(SCSI_RESET_NOT_RUNNING);
+      }
+      cmd_prev = cmd_next;
+      cmd_next = (Scsi_Cmnd *)cmd_next->host_scribble;
+    }
+    if ( !(flags & SCSI_RESET_SYNCHRONOUS) )
+    {
+      if (aic7xxx_verbose & VERBOSE_RESET_RETURN)
+        printk(INFO_LEAD "Reset, cmd not found,"
+          " failing.\n", p->host_no, CTL_OF_CMD(cmd));
+      unpause_sequencer(p, FALSE);
+      DRIVER_UNLOCK
+      return(SCSI_RESET_NOT_RUNNING);
+    }
+    else
+    {
+      if (aic7xxx_verbose & VERBOSE_RESET_MID)
+        printk(INFO_LEAD "Reset called, no scb, "
+          "flags 0x%x\n", p->host_no, CTL_OF_CMD(cmd), flags);
+      scb = NULL;
+      action = HOST_RESET;
+    }
+  }
+  else
+  {
+    if (aic7xxx_verbose & VERBOSE_RESET_MID)
+      printk(INFO_LEAD "Reset called, scb %d, flags "
+        "0x%x\n", p->host_no, CTL_OF_SCB(scb), scb->hscb->tag, scb->flags);
+    if ( aic7xxx_scb_on_qoutfifo(p, scb) )
+    {
+      if(aic7xxx_verbose & VERBOSE_RESET_RETURN)
+        printk(INFO_LEAD "SCB on qoutfifo, completing.\n", p->host_no,
+          CTL_OF_SCB(scb));
+      if ((aic_inb(p,INTSTAT) & CMDCMPLT) == 0)
+        printk(INFO_LEAD "missed CMDCMPLT interrupt!\n", p->host_no,
+          CTL_OF_SCB(scb));
+      aic7xxx_handle_command_completion_intr(p);
+      aic7xxx_done_cmds_complete(p);
+      aic7xxx_run_waiting_queues(p);
+      unpause_sequencer(p, FALSE);
+      DRIVER_UNLOCK
+      return(SCSI_RESET_SUCCESS);
+    }
+    if ( flags & SCSI_RESET_SUGGEST_HOST_RESET )
+    {
+      action = HOST_RESET;
+    }
+    else if ( flags & SCSI_RESET_SUGGEST_BUS_RESET )
+    {
+      action = BUS_RESET;
+    }
+    else 
+    {
+      action = DEVICE_RESET;
+    }
+  }
+  if ( (action & DEVICE_RESET) && 
+        (p->dev_flags[tindex] & BUS_DEVICE_RESET_PENDING) )
+  {
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+      printk(INFO_LEAD "Bus device reset already sent to "
+        "device, escalating.\n", p->host_no, CTL_OF_CMD(cmd));
+    action = BUS_RESET;
+  }
+  if ( (action & DEVICE_RESET) &&
+       (scb->flags & SCB_QUEUED_ABORT) )
+  {
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+    {
+      printk(INFO_LEAD "Have already attempted to reach "
+        "device with queued\n", p->host_no, CTL_OF_CMD(cmd));
+      printk(INFO_LEAD "message, will escalate to bus "
+        "reset.\n", p->host_no, CTL_OF_CMD(cmd));
+    }
+    action = BUS_RESET;
+  }
+  if ( (action & DEVICE_RESET) && 
+       (p->flags & (AHC_RESET_PENDING | AHC_ABORT_PENDING)) )
+  {
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+     printk(INFO_LEAD "Bus device reset stupid when "
+        "other action has failed.\n", p->host_no, CTL_OF_CMD(cmd));
+    action = BUS_RESET;
+  }
+  if ( (action & BUS_RESET) && !(p->features & AHC_TWIN) )
+  {
+    action = HOST_RESET;
+  }
+  if ( (p->dev_flags[tindex] & DEVICE_RESET_DELAY) &&
+       !(action & (HOST_RESET | BUS_RESET)))
+  {
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+    {
+      printk(INFO_LEAD "Reset called too soon after last "
+        "reset without requesting\n", p->host_no, CTL_OF_CMD(cmd));
+      printk(INFO_LEAD "bus or host reset, escalating.\n", p->host_no,
+        CTL_OF_CMD(cmd));
+    }
+    action = BUS_RESET;
+  }
+  if ( (p->flags & AHC_RESET_DELAY) &&
+       (action & (HOST_RESET | BUS_RESET)) )
+  {
+    if (aic7xxx_verbose & VERBOSE_RESET_PROCESS)
+      printk(INFO_LEAD "Reset called too soon after "
+        "last bus reset, delaying.\n", p->host_no, CTL_OF_CMD(cmd));
+    action = RESET_DELAY;
+  }
+/*
+ *  By this point, we want to already know what we are going to do and
+ *  only have the following code implement our course of action.
+ */
+  switch (action)
+  {
+    case RESET_DELAY:
+      unpause_sequencer(p, FALSE);
+      DRIVER_UNLOCK
+      return(SCSI_RESET_PENDING);
+      break;
+    case FAIL:
+      unpause_sequencer(p, FALSE);
+      DRIVER_UNLOCK
+      return(SCSI_RESET_ERROR);
+      break;
+    case DEVICE_RESET:
+      p->flags |= AHC_IN_RESET;
+      result = aic7xxx_bus_device_reset(p, cmd);
+      aic7xxx_run_done_queue(p, TRUE);
+      /*  We can't rely on run_waiting_queues to unpause the sequencer for
+       *  PCI based controllers since we use AAP */
+      aic7xxx_run_waiting_queues(p);
+      unpause_sequencer(p, FALSE);
+      p->flags &= ~AHC_IN_RESET;
+      DRIVER_UNLOCK
+      return(result);
+      break;
+    case BUS_RESET:
+    case HOST_RESET:
+    default:
+      p->flags |= AHC_IN_RESET | AHC_RESET_DELAY;
+      p->dev_expires[p->scsi_id] = jiffies + (3 * HZ);
+      p->dev_timer_active |= (0x01 << p->scsi_id);
+      if ( !(p->dev_timer_active & (0x01 << MAX_TARGETS)) ||
+            time_after_eq(p->dev_timer.expires, p->dev_expires[p->scsi_id]) )
+      {
+        mod_timer(&p->dev_timer, p->dev_expires[p->scsi_id]);
+        p->dev_timer_active |= (0x01 << MAX_TARGETS);
+      }
+      aic7xxx_reset_channel(p, cmd->channel, TRUE);
+      if ( (p->features & AHC_TWIN) && (action & HOST_RESET) )
+      {
+        aic7xxx_reset_channel(p, cmd->channel ^ 0x01, TRUE);
+        restart_sequencer(p);
+      }
+      if (action != HOST_RESET)
+        result = SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+      else
+      {
+        result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
+        aic_outb(p,  aic_inb(p, SIMODE1) & ~(ENREQINIT|ENBUSFREE),
+          SIMODE1);
+        aic7xxx_clear_intstat(p);
+        p->flags &= ~AHC_HANDLING_REQINITS;
+        p->msg_type = MSG_TYPE_NONE;
+        p->msg_index = 0;
+        p->msg_len = 0;
+      }
+      aic7xxx_run_done_queue(p, TRUE);
+      /*
+       * If this a SCSI_RESET_SYNCHRONOUS then the command we were given is
+       * in need of being re-started, so send it on through to aic7xxx_queue
+       * and let it set until the delay is over.  This keeps it from dying
+       * entirely and avoids getting a bogus dead command back through the
+       * mid-level code due to too many retries.
+       */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,132)
+      if ( flags & SCSI_RESET_SYNCHRONOUS )
+      {
+        cmd->result = DID_BUS_BUSY << 16;
+        cmd->done(cmd);
+      }
+#endif
+      p->flags &= ~AHC_IN_RESET;
+      /*
+       * We can't rely on run_waiting_queues to unpause the sequencer for
+       * PCI based controllers since we use AAP.  NOTE: this also sets
+       * the timer for the one command we might have queued in the case
+       * of a synch reset.
+       */
+      aic7xxx_run_waiting_queues(p);
+      unpause_sequencer(p, FALSE);
+      DRIVER_UNLOCK
+      return(result);
+      break;
+  }
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_biosparam
+ *
+ * Description:
+ *   Return the disk geometry for the given SCSI device.
+ *-F*************************************************************************/
+int
+aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[])
+{
+  int heads, sectors, cylinders, ret;
+  struct aic7xxx_host *p;
+  struct buffer_head *bh;
+
+  p = (struct aic7xxx_host *) disk->device->host->hostdata;
+  bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024);
+
+  if ( bh )
+  {
+    ret = scsi_partsize(bh, disk->capacity, &geom[2], &geom[0], &geom[1]);
+    brelse(bh);
+    if ( ret != -1 )
+      return(ret);
+  }
+  
+  heads = 64;
+  sectors = 32;
+  cylinders = disk->capacity / (heads * sectors);
+
+  if ((p->flags & AHC_EXTEND_TRANS_A) && (cylinders > 1024))
+  {
+    heads = 255;
+    sectors = 63;
+    cylinders = disk->capacity / (heads * sectors);
+  }
+
+  geom[0] = heads;
+  geom[1] = sectors;
+  geom[2] = cylinders;
+
+  return (0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_release
+ *
+ * Description:
+ *   Free the passed in Scsi_Host memory structures prior to unloading the
+ *   module.
+ *-F*************************************************************************/
+int
+aic7xxx_release(struct Scsi_Host *host)
+{
+  struct aic7xxx_host *p = (struct aic7xxx_host *) host->hostdata;
+  struct aic7xxx_host *next, *prev;
+
+  if(p->irq)
+    free_irq(p->irq, p);
+  if(p->base)
+    release_region(p->base, MAXREG - MINREG);
+#ifdef MMAPIO
+  if(p->maddr)
+  {
+    iounmap((void *) (((unsigned long) p->maddr) & PAGE_MASK));
+  }
+#endif /* MMAPIO */
+  prev = NULL;
+  next = first_aic7xxx;
+  while(next != NULL)
+  {
+    if(next == p)
+    {
+      if(prev == NULL)
+        first_aic7xxx = next->next;
+      else
+        prev->next = next->next;
+    }
+    else
+    {
+      prev = next;
+    }
+    next = next->next;
+  }
+  aic7xxx_free(p);
+  return(0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_print_card
+ *
+ * Description:
+ *   Print out all of the control registers on the card
+ *
+ *   NOTE: This function is not yet safe for use on the VLB and EISA
+ *   controllers, so it isn't used on those controllers at all.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_card(struct aic7xxx_host *p)
+{
+  int i, j, k, chip;
+  static struct register_ranges {
+    int num_ranges;
+    int range_val[32];
+  } cards_ds[] = {
+    { 0, {0,} }, /* none */
+    {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1f, 0x1f, 0x60, 0x60, /*7771*/
+          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9b, 0x9f} },
+    { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7850*/
+          0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+    { 9, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7860*/
+          0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+    {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1c, 0x1f, 0x60, 0x60, /*7870*/
+          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+    {10, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1a, 0x1c, 0x1f, 0x60, 0x60, /*7880*/
+          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9f} },
+    {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7890*/
+          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
+          0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
+          0xfe, 0xff} },
+    {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x19, 0x1b, 0x1f, 0x60, 0x60, /*7895*/
+          0x62, 0x66, 0x80, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a,
+          0x9f, 0x9f, 0xe0, 0xf1} },
+    {16, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7896*/
+          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9f, 0x9f,
+          0xe0, 0xf1, 0xf4, 0xf4, 0xf6, 0xf6, 0xf8, 0xf8, 0xfa, 0xfc,
+          0xfe, 0xff} },
+    {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7892*/
+          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f,
+          0xe0, 0xf1, 0xf4, 0xfc} },
+    {12, {0x00, 0x05, 0x08, 0x11, 0x18, 0x1f, 0x60, 0x60, 0x62, 0x66, /*7899*/
+          0x84, 0x8e, 0x90, 0x95, 0x97, 0x97, 0x9a, 0x9a, 0x9c, 0x9f,
+          0xe0, 0xf1, 0xf4, 0xfc} },
+  };
+  chip = p->chip & AHC_CHIPID_MASK;
+  printk("%s at ",
+         board_names[p->board_name_index]);
+  switch(p->chip & ~AHC_CHIPID_MASK)
+  {
+    case AHC_VL:
+      printk("VLB Slot %d.\n", p->pci_device_fn);
+      break;
+    case AHC_EISA:
+      printk("EISA Slot %d.\n", p->pci_device_fn);
+      break;
+    case AHC_PCI:
+    default:
+      printk("PCI %d/%d/%d.\n", p->pci_bus, PCI_SLOT(p->pci_device_fn),
+             PCI_FUNC(p->pci_device_fn));
+      break;
+  }
+
+  /*
+   * the registers on the card....
+   */
+  printk("Card Dump:\n");
+  k = 0;
+  for(i=0; i<cards_ds[chip].num_ranges; i++)
+  {
+    for(j  = cards_ds[chip].range_val[ i * 2 ];
+        j <= cards_ds[chip].range_val[ i * 2 + 1 ] ;
+        j++)
+    {
+      printk("%02x:%02x ", j, aic_inb(p, j));
+      if(++k == 13)
+      {
+        printk("\n");
+        k=0;
+      }
+    }
+  }
+  if(k != 0)
+    printk("\n");
+
+  /*
+   * If this was an Ultra2 controller, then we just hosed the card in terms
+   * of the QUEUE REGS.  This function is only called at init time or by
+   * the panic_abort function, so it's safe to assume a generic init time
+   * setting here
+   */
+
+  if(p->features & AHC_QUEUE_REGS)
+  {
+    aic_outb(p, 0, SDSCB_QOFF);
+    aic_outb(p, 0, SNSCB_QOFF);
+    aic_outb(p, 0, HNSCB_QOFF);
+  }
+
+}
+
+/*+F*************************************************************************
+ * Function:
+ *   aic7xxx_print_scratch_ram
+ *
+ * Description:
+ *   Print out the scratch RAM values on the card.
+ *-F*************************************************************************/
+static void
+aic7xxx_print_scratch_ram(struct aic7xxx_host *p)
+{
+  int i, k;
+
+  k = 0;
+  printk("Scratch RAM:\n");
+  for(i = SRAM_BASE; i < SEQCTL; i++)
+  {
+    printk("%02x:%02x ", i, aic_inb(p, i));
+    if(++k == 13)
+    {
+      printk("\n");
+      k=0;
+    }
+  }
+  if (p->features & AHC_MORE_SRAM)
+  {
+    for(i = TARG_OFFSET; i < 0x80; i++)
+    {
+      printk("%02x:%02x ", i, aic_inb(p, i));
+      if(++k == 13)
+      {
+        printk("\n");
+        k=0;
+      }
+    }
+  }
+  printk("\n");
+}
+
+
+#include "aic7xxx_old/aic7xxx_proc.c"
+
+/* Eventually this will go into an include file, but this will be later */
+static Scsi_Host_Template driver_template = AIC7XXX;
+
+#include "scsi_module.c"
+
+/*
+ * Overrides for Emacs so that we almost follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-indent-level: 2
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -2
+ * c-argdecl-indent: 2
+ * c-label-offset: -2
+ * c-continued-statement-offset: 2
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_proc.c linux/drivers/scsi/aic7xxx_proc.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_proc.c	Wed Jun 21 17:25:03 2000
+++ linux/drivers/scsi/aic7xxx_proc.c	Wed Dec 31 16:00:00 1969
@@ -1,413 +0,0 @@
-/*+M*************************************************************************
- * Adaptec AIC7xxx device driver proc support for Linux.
- *
- * Copyright (c) 1995, 1996 Dean W. Gehnert
- *
- * 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.
- *
- * ----------------------------------------------------------------
- *  o Modified from the EATA-DMA /proc support.
- *  o Additional support for device block statistics provided by
- *    Matthew Jacob.
- *  o Correction of overflow by Heinz Mauelshagen
- *  o Adittional corrections by Doug Ledford
- *
- *  Dean W. Gehnert, deang@teleport.com, 05/01/96
- *
- *  $Id: aic7xxx_proc.c,v 4.1 1997/06/97 08:23:42 deang Exp $
- *-M*************************************************************************/
-
-#include <linux/config.h>
-
-#define	BLS	(&aic7xxx_buffer[size])
-#define HDRB \
-"             < 2K      2K+     4K+     8K+    16K+    32K+    64K+   128K+"
-
-#ifdef PROC_DEBUG
-extern int vsprintf(char *, const char *, va_list);
-
-static void
-proc_debug(const char *fmt, ...)
-{
-  va_list ap;
-  char buf[256];
-
-  va_start(ap, fmt);
-  vsprintf(buf, fmt, ap);
-  printk(buf);
-  va_end(ap);
-}
-#else /* PROC_DEBUG */
-#  define proc_debug(fmt, args...)
-#endif /* PROC_DEBUG */
-
-static int aic7xxx_buffer_size = 0;
-static char *aic7xxx_buffer = NULL;
-
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_set_info
- *
- * Description:
- *   Set parameters for the driver from the /proc filesystem.
- *-F*************************************************************************/
-int
-aic7xxx_set_info(char *buffer, int length, struct Scsi_Host *HBAptr)
-{
-  proc_debug("aic7xxx_set_info(): %s\n", buffer);
-  return (-ENOSYS);  /* Currently this is a no-op */
-}
-
-
-/*+F*************************************************************************
- * Function:
- *   aic7xxx_proc_info
- *
- * Description:
- *   Return information to handle /proc support for the driver.
- *-F*************************************************************************/
-int
-aic7xxx_proc_info ( char *buffer, char **start, off_t offset, int length, 
-                    int hostno, int inout)
-{
-  struct Scsi_Host *HBAptr;
-  struct aic7xxx_host *p;
-  int    size = 0;
-  unsigned char i;
-  struct aic7xxx_xferstats *sp;
-  unsigned char target;
-
-  HBAptr = NULL;
-
-  for(p=first_aic7xxx; p->host->host_no != hostno; p=p->next)
-    ;
-
-  if (!p)
-  {
-    size += sprintf(buffer, "Can't find adapter for host number %d\n", hostno);
-    if (size > length)
-    {
-      return (size);
-    }
-    else
-    {
-      return (length);
-    }
-  }
-
-  HBAptr = p->host;
-
-  if (inout == TRUE) /* Has data been written to the file? */ 
-  {
-    return (aic7xxx_set_info(buffer, length, HBAptr));
-  }
-
-  p = (struct aic7xxx_host *) HBAptr->hostdata;
-
-  /*
-   * It takes roughly 1K of space to hold all relevant card info, not
-   * counting any proc stats, so we start out with a 1.5k buffer size and
-   * if proc_stats is defined, then we sweep the stats structure to see
-   * how many drives we will be printing out for and add 384 bytes per
-   * device with active stats.
-   *
-   * Hmmmm...that 1.5k seems to keep growing as items get added so they
-   * can be easily viewed for debugging purposes.  So, we bumped that
-   * 1.5k to 4k so we can quit having to bump it all the time.
-   */
-
-  size = 4096;
-  for (target = 0; target < MAX_TARGETS; target++)
-  {
-    if (p->dev_flags[target] & DEVICE_PRESENT)
-#ifdef AIC7XXX_PROC_STATS
-      size += 512;
-#else
-      size += 256;
-#endif
-  }
-  if (aic7xxx_buffer_size != size)
-  {
-    if (aic7xxx_buffer != NULL) 
-    {
-      kfree(aic7xxx_buffer);
-      aic7xxx_buffer_size = 0;
-    }
-    aic7xxx_buffer = kmalloc(size, GFP_KERNEL);
-  }
-  if (aic7xxx_buffer == NULL)
-  {
-    size = sprintf(buffer, "AIC7xxx - kmalloc error at line %d\n",
-        __LINE__);
-    return size;
-  }
-  aic7xxx_buffer_size = size;
-
-  size = 0;
-  size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
-  size += sprintf(BLS, "%s/", AIC7XXX_C_VERSION);
-  size += sprintf(BLS, "%s", AIC7XXX_H_VERSION);
-  size += sprintf(BLS, "\n");
-  size += sprintf(BLS, "Compile Options:\n");
-#ifdef CONFIG_AIC7XXX_TCQ_ON_BY_DEFAULT
-  size += sprintf(BLS, "  TCQ Enabled By Default : Enabled\n");
-#else
-  size += sprintf(BLS, "  TCQ Enabled By Default : Disabled\n");
-#endif
-#ifdef AIC7XXX_PROC_STATS
-  size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Enabled\n");
-#else
-  size += sprintf(BLS, "  AIC7XXX_PROC_STATS     : Disabled\n");
-#endif
-  size += sprintf(BLS, "\n");
-  size += sprintf(BLS, "Adapter Configuration:\n");
-  size += sprintf(BLS, "           SCSI Adapter: %s\n",
-      board_names[p->board_name_index]);
-  if (p->flags & AHC_TWIN)
-    size += sprintf(BLS, "                         Twin Channel Controller ");
-  else
-  {
-    char *channel = "";
-    char *ultra = "";
-    char *wide = "Narrow ";
-    if (p->flags & AHC_MULTI_CHANNEL)
-    {
-      channel = " Channel A";
-      if (p->flags & (AHC_CHNLB|AHC_CHNLC))
-        channel = (p->flags & AHC_CHNLB) ? " Channel B" : " Channel C";
-    }
-    if (p->features & AHC_WIDE)
-      wide = "Wide ";
-    if (p->features & AHC_ULTRA3)
-    {
-      switch(p->chip & AHC_CHIPID_MASK)
-      {
-        case AHC_AIC7892:
-        case AHC_AIC7899:
-          ultra = "Ultra-160/m LVD/SE ";
-          break;
-        default:
-          ultra = "Ultra-3 LVD/SE ";
-          break;
-      }
-    }
-    else if (p->features & AHC_ULTRA2)
-      ultra = "Ultra-2 LVD/SE ";
-    else if (p->features & AHC_ULTRA)
-      ultra = "Ultra ";
-    size += sprintf(BLS, "                           %s%sController%s ",
-      ultra, wide, channel);
-  }
-  switch(p->chip & ~AHC_CHIPID_MASK)
-  {
-    case AHC_VL:
-      size += sprintf(BLS, "at VLB slot %d\n", p->pci_device_fn);
-      break;
-    case AHC_EISA:
-      size += sprintf(BLS, "at EISA slot %d\n", p->pci_device_fn);
-      break;
-    default:
-      size += sprintf(BLS, "at PCI %d/%d/%d\n", p->pci_bus,
-        PCI_SLOT(p->pci_device_fn), PCI_FUNC(p->pci_device_fn));
-      break;
-  }
-  if( !(p->maddr) )
-  {
-    size += sprintf(BLS, "    Programmed I/O Base: %lx\n", p->base);
-  }
-  else
-  {
-    size += sprintf(BLS, "    PCI MMAPed I/O Base: 0x%lx\n", p->mbase);
-  }
-  if( (p->chip & (AHC_VL | AHC_EISA)) )
-  {
-    size += sprintf(BLS, "    BIOS Memory Address: 0x%08x\n", p->bios_address);
-  }
-  size += sprintf(BLS, " Adapter SEEPROM Config: %s\n",
-          (p->flags & AHC_SEEPROM_FOUND) ? "SEEPROM found and used." :
-         ((p->flags & AHC_USEDEFAULTS) ? "SEEPROM not found, using defaults." :
-           "SEEPROM not found, using leftover BIOS values.") );
-  size += sprintf(BLS, "      Adaptec SCSI BIOS: %s\n",
-          (p->flags & AHC_BIOS_ENABLED) ? "Enabled" : "Disabled");
-  size += sprintf(BLS, "                    IRQ: %d\n", HBAptr->irq);
-  size += sprintf(BLS, "                   SCBs: Active %d, Max Active %d,\n",
-            p->activescbs, p->max_activescbs);
-  size += sprintf(BLS, "                         Allocated %d, HW %d, "
-            "Page %d\n", p->scb_data->numscbs, p->scb_data->maxhscbs,
-            p->scb_data->maxscbs);
-  if (p->flags & AHC_EXTERNAL_SRAM)
-    size += sprintf(BLS, "                         Using External SCB SRAM\n");
-  size += sprintf(BLS, "             Interrupts: %ld", p->isr_count);
-  if (p->chip & AHC_EISA)
-  {
-    size += sprintf(BLS, " %s\n",
-        (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
-  }
-  else
-  {
-    size += sprintf(BLS, "\n");
-  }
-  size += sprintf(BLS, "      BIOS Control Word: 0x%04x\n",
-            p->bios_control);
-  size += sprintf(BLS, "   Adapter Control Word: 0x%04x\n",
-            p->adapter_control);
-  size += sprintf(BLS, "   Extended Translation: %sabled\n",
-      (p->flags & AHC_EXTEND_TRANS_A) ? "En" : "Dis");
-  size += sprintf(BLS, "Disconnect Enable Flags: 0x%04x\n", p->discenable);
-  if (p->features & (AHC_ULTRA | AHC_ULTRA2))
-  {
-    size += sprintf(BLS, "     Ultra Enable Flags: 0x%04x\n", p->ultraenb);
-  }
-  size += sprintf(BLS, " Tag Queue Enable Flags: 0x%04x\n", p->tagenable);
-  size += sprintf(BLS, "Ordered Queue Tag Flags: 0x%04x\n", p->orderedtag);
-  size += sprintf(BLS, "Default Tag Queue Depth: %d\n", AIC7XXX_CMDS_PER_DEVICE);
-  size += sprintf(BLS, "    Tagged Queue By Device array for aic7xxx host "
-                       "instance %d:\n", p->instance);
-  size += sprintf(BLS, "      {");
-  for(i=0; i < (MAX_TARGETS - 1); i++)
-    size += sprintf(BLS, "%d,",aic7xxx_tag_info[p->instance].tag_commands[i]);
-  size += sprintf(BLS, "%d}\n",aic7xxx_tag_info[p->instance].tag_commands[i]);
-  size += sprintf(BLS, "    Actual queue depth per device for aic7xxx host "
-                       "instance %d:\n", p->instance);
-  size += sprintf(BLS, "      {");
-  for(i=0; i < (MAX_TARGETS - 1); i++)
-    size += sprintf(BLS, "%d,", p->dev_max_queue_depth[i]);
-  size += sprintf(BLS, "%d}\n", p->dev_max_queue_depth[i]);
-
-  size += sprintf(BLS, "\n");
-  size += sprintf(BLS, "Statistics:\n\n");
-  for (target = 0; target < MAX_TARGETS; target++)
-  {
-    sp = &p->stats[target];
-    if ((p->dev_flags[target] & DEVICE_PRESENT) == 0)
-    {
-      continue;
-    }
-    if (p->features & AHC_TWIN)
-    {
-      size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
-          p->host_no, (target >> 3), (target & 0x7), 0);
-    }
-    else
-    {
-      size += sprintf(BLS, "(scsi%d:%d:%d:%d)\n",
-          p->host_no, 0, target, 0);
-    }
-    size += sprintf(BLS, "  Device using %s/%s",
-          (p->transinfo[target].cur_width == MSG_EXT_WDTR_BUS_16_BIT) ?
-          "Wide" : "Narrow",
-          (p->transinfo[target].cur_offset != 0) ?
-          "Sync transfers at " : "Async transfers.\n" );
-    if (p->transinfo[target].cur_offset != 0)
-    {
-      struct aic7xxx_syncrate *sync_rate;
-      unsigned char options = p->transinfo[target].cur_options;
-      int period = p->transinfo[target].cur_period;
-      int rate = (p->transinfo[target].cur_width ==
-                  MSG_EXT_WDTR_BUS_16_BIT) ? 1 : 0;
-
-      sync_rate = aic7xxx_find_syncrate(p, &period, 0, &options);
-      if (sync_rate != NULL)
-      {
-        size += sprintf(BLS, "%s MByte/sec, offset %d\n",
-                        sync_rate->rate[rate],
-                        p->transinfo[target].cur_offset );
-      }
-      else
-      {
-        size += sprintf(BLS, "3.3 MByte/sec, offset %d\n",
-                        p->transinfo[target].cur_offset );
-      }
-    }
-    size += sprintf(BLS, "  Transinfo settings: ");
-    size += sprintf(BLS, "current(%d/%d/%d/%d), ",
-                    p->transinfo[target].cur_period,
-                    p->transinfo[target].cur_offset,
-                    p->transinfo[target].cur_width,
-                    p->transinfo[target].cur_options);
-    size += sprintf(BLS, "goal(%d/%d/%d/%d), ",
-                    p->transinfo[target].goal_period,
-                    p->transinfo[target].goal_offset,
-                    p->transinfo[target].goal_width,
-                    p->transinfo[target].goal_options);
-    size += sprintf(BLS, "user(%d/%d/%d/%d)\n",
-                    p->transinfo[target].user_period,
-                    p->transinfo[target].user_offset,
-                    p->transinfo[target].user_width,
-                    p->transinfo[target].user_options);
-#ifdef AIC7XXX_PROC_STATS
-    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
-        sp->r_total + sp->w_total, sp->r_total, sp->w_total);
-    size += sprintf(BLS, "%s\n", HDRB);
-    size += sprintf(BLS, "   Reads:");
-    for (i = 0; i < NUMBER(sp->r_bins); i++)
-    {
-      size += sprintf(BLS, " %7ld", sp->r_bins[i]);
-    }
-    size += sprintf(BLS, "\n");
-    size += sprintf(BLS, "  Writes:");
-    for (i = 0; i < NUMBER(sp->w_bins); i++)
-    {
-      size += sprintf(BLS, " %7ld", sp->w_bins[i]);
-    }
-    size += sprintf(BLS, "\n");
-#else
-    size += sprintf(BLS, "  Total transfers %ld (%ld reads and %ld writes)\n",
-        sp->r_total + sp->w_total, sp->r_total, sp->w_total);
-#endif /* AIC7XXX_PROC_STATS */
-    size += sprintf(BLS, "\n\n");
-  }
-
-  if (size >= aic7xxx_buffer_size)
-  {
-    printk(KERN_WARNING "aic7xxx: Overflow in aic7xxx_proc.c\n");
-  }
-
-  if (offset > size - 1)
-  {
-    kfree(aic7xxx_buffer);
-    aic7xxx_buffer = NULL;
-    aic7xxx_buffer_size = length = 0;
-    *start = NULL;
-  }
-  else
-  {
-    *start = buffer;
-    length = MIN(length, size - offset);
-    memcpy(buffer, &aic7xxx_buffer[offset], length);
-  }
-
-  return (length);
-}
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only.  This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 2
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -2
- * c-argdecl-indent: 2
- * c-label-offset: -2
- * c-continued-statement-offset: 2
- * c-continued-brace-offset: 0
- * indent-tabs-mode: nil
- * tab-width: 8
- * End:
- */
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_reg.h linux/drivers/scsi/aic7xxx_reg.h
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_reg.h	Thu Feb 10 19:00:35 2000
+++ linux/drivers/scsi/aic7xxx_reg.h	Wed Dec 31 16:00:00 1969
@@ -1,629 +0,0 @@
-/*
-  * DO NOT EDIT - This file is automatically generated.
-  */
-
-#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_ULTRA2     	0x7f
-#define		SXFR            	0x70
-#define		SOFS            	0x0f
-
-#define	SCSIID          		0x05
-#define	SCSIOFFSET      		0x05
-#define		SOFS_ULTRA2     	0x7f
-
-#define	SCSIDATL        		0x06
-
-#define	SCSIDATH        		0x07
-
-#define	STCNT           		0x08
-
-#define	OPTIONMODE      		0x08
-#define		AUTORATEEN      	0x80
-#define		AUTOACKEN       	0x40
-#define		ATNMGMNTEN      	0x20
-#define		BUSFREEREV      	0x10
-#define		EXPPHASEDIS     	0x08
-#define		SCSIDATL_IMGEN  	0x04
-#define		AUTO_MSGOUT_DE  	0x02
-#define		DIS_MSGIN_DUALEDGE	0x01
-
-#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		IOERR           	0x08
-#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		SHVALID         	0x40
-#define		WIDE_RES        	0x20
-#define		SFCNT           	0x1f
-#define		EXP_ACTIVE      	0x10
-#define		CRCVALERR       	0x08
-#define		CRCENDERR       	0x04
-#define		CRCREQERR       	0x02
-#define		DUAL_EDGE_ERROR 	0x01
-
-#define	SSTAT3          		0x0e
-#define		SCSICNT         	0xf0
-#define		OFFCNT          	0x0f
-
-#define	SCSIID_ULTRA2   		0x0f
-#define		OID             	0x0f
-
-#define	SIMODE0         		0x10
-#define		ENSELDO         	0x40
-#define		ENSELDI         	0x20
-#define		ENSELINGO       	0x10
-#define		ENIOERR         	0x08
-#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	SPIOCAP         		0x1b
-#define		SOFT1           	0x80
-#define		SOFT0           	0x40
-#define		SOFTCMDEN       	0x20
-#define		HAS_BRDCTL      	0x10
-#define		SEEPROM         	0x08
-#define		EEPROM          	0x04
-#define		ROM             	0x02
-#define		SSPIOCPS        	0x01
-
-#define	BRDCTL          		0x1d
-#define		BRDDAT7         	0x80
-#define		BRDDAT6         	0x40
-#define		BRDDAT5         	0x20
-#define		BRDDAT4         	0x10
-#define		BRDSTB          	0x10
-#define		BRDCS           	0x08
-#define		BRDDAT3         	0x08
-#define		BRDDAT2         	0x04
-#define		BRDRW           	0x04
-#define		BRDRW_ULTRA2    	0x02
-#define		BRDCTL1         	0x02
-#define		BRDCTL0         	0x01
-#define		BRDSTB_ULTRA2   	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		ENAB40          	0x08
-#define		ENAB20          	0x04
-#define		SELWIDE         	0x02
-#define		XCVR            	0x01
-
-#define	SRAM_BASE       		0x20
-
-#define	TARG_SCSIRATE   		0x20
-
-#define	ULTRA_ENB       		0x30
-
-#define	DISC_DSB        		0x32
-
-#define	MSG_OUT         		0x34
-
-#define	DMAPARAMS       		0x35
-#define		PRELOADEN       	0x80
-#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	SEQ_FLAGS       		0x36
-#define		IDENTIFY_SEEN   	0x80
-#define		SCBPTR_VALID    	0x20
-#define		DPHASE          	0x10
-#define		AMTARGET        	0x08
-#define		WIDE_BUS        	0x02
-#define		TWIN_BUS        	0x01
-
-#define	SAVED_TCL       		0x37
-
-#define	SG_COUNT        		0x38
-
-#define	SG_NEXT         		0x39
-
-#define	LASTPHASE       		0x3d
-#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	WAITING_SCBH    		0x3e
-
-#define	DISCONNECTED_SCBH		0x3f
-
-#define	FREE_SCBH       		0x40
-
-#define	HSCB_ADDR       		0x41
-
-#define	SCBID_ADDR      		0x45
-
-#define	TMODE_CMDADDR   		0x49
-
-#define	KERNEL_QINPOS   		0x4d
-
-#define	QINPOS          		0x4e
-
-#define	QOUTPOS         		0x4f
-
-#define	TMODE_CMDADDR_NEXT		0x50
-
-#define	ARG_1           		0x51
-#define	RETURN_1        		0x51
-#define		SEND_MSG        	0x80
-#define		SEND_SENSE      	0x40
-#define		SEND_REJ        	0x20
-#define		MSGOUT_PHASEMIS 	0x10
-
-#define	ARG_2           		0x52
-#define	RETURN_2        		0x52
-
-#define	LAST_MSG        		0x53
-
-#define	PREFETCH_CNT    		0x54
-
-#define	SCSICONF        		0x5a
-#define		TERM_ENB        	0x80
-#define		RESET_SCSI      	0x40
-#define		HWSCSIID        	0x0f
-#define		HSCSIID         	0x07
-
-#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	TARG_OFFSET     		0x70
-
-#define	BCTL            		0x84
-#define		ACE             	0x08
-#define		ENABLE          	0x01
-
-#define	DSCOMMAND0      		0x84
-#define		INTSCBRAMSEL    	0x08
-#define		RAMPS           	0x04
-#define		USCBSIZE32      	0x02
-#define		CIOPARCKEN      	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         	0xc0
-#define		STBOFF          	0x38
-#define		STBON           	0x07
-
-#define	DSPCISTATUS     		0x86
-#define		DFTHRSH_100     	0xc0
-
-#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		TRACEPOINT2     	0xc1
-#define		TRACEPOINT      	0xb1
-#define		AWAITING_MSG    	0xa1
-#define		RESIDUAL        	0x81
-#define		BAD_STATUS      	0x71
-#define		REJECT_MSG      	0x61
-#define		WIDE_RESIDUE    	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		CLRPARERR       	0x10
-#define		CLRBRKADRINT    	0x08
-#define		CLRSCSIINT      	0x04
-#define		CLRCMDINT       	0x02
-#define		CLRSEQINT       	0x01
-
-#define	ERROR           		0x92
-#define		CIOPARERR       	0x80
-#define		PCIERRSTAT      	0x40
-#define		MPARERR         	0x20
-#define		DPARERR         	0x10
-#define		SQPARERR        	0x08
-#define		ILLOPCODE       	0x04
-#define		DSCTMOUT        	0x02
-#define		ILLSADDR        	0x02
-#define		ILLHADDR        	0x01
-
-#define	DFCNTRL         		0x93
-
-#define	DFSTATUS        		0x94
-#define		PRELOAD_AVAIL   	0x80
-#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	SCSIDATL_IMG    		0x9c
-
-#define	QOUTFIFO        		0x9d
-
-#define	CRCCONTROL1     		0x9d
-#define		CRCONSEEN       	0x80
-#define		CRCVALCHKEN     	0x40
-#define		CRCENDCHKEN     	0x20
-#define		CRCREQCHKEN     	0x10
-#define		TARGCRCENDEN    	0x08
-#define		TARGCRCCNTEN    	0x04
-
-#define	QOUTCNT         		0x9e
-
-#define	SCSIPHASE       		0x9e
-#define		SP_STATUS       	0x20
-#define		SP_COMMAND      	0x10
-#define		SP_MSG_IN       	0x08
-#define		SP_MSG_OUT      	0x04
-#define		SP_DATA_IN      	0x02
-#define		SP_DATA_OUT     	0x01
-
-#define	SFUNCT          		0x9f
-#define		ALT_MODE        	0x80
-
-#define	SCB_CONTROL     		0xa0
-#define		MK_MESSAGE      	0x80
-#define		DISCENB         	0x40
-#define		TAG_ENB         	0x20
-#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_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	CCHADDR         		0xe0
-
-#define	CCHCNT          		0xe8
-
-#define	CCSGRAM         		0xe9
-
-#define	CCSGADDR        		0xea
-
-#define	CCSGCTL         		0xeb
-#define		CCSGDONE        	0x80
-#define		CCSGEN          	0x08
-#define		FLAG            	0x02
-#define		CCSGRESET       	0x01
-
-#define	CCSCBRAM        		0xec
-
-#define	CCSCBADDR       		0xed
-
-#define	CCSCBCTL        		0xee
-#define		CCSCBDONE       	0x80
-#define		ARRDONE         	0x40
-#define		CCARREN         	0x10
-#define		CCSCBEN         	0x08
-#define		CCSCBDIR        	0x04
-#define		CCSCBRESET      	0x01
-
-#define	CCSCBCNT        		0xef
-
-#define	CCSCBPTR        		0xf1
-
-#define	HNSCB_QOFF      		0xf4
-
-#define	HESCB_QOFF      		0xf5
-
-#define	SNSCB_QOFF      		0xf6
-
-#define	SESCB_QOFF      		0xf7
-
-#define	SDSCB_QOFF      		0xf8
-
-#define	QOFF_CTLSTA     		0xfa
-#define		ESTABLISH_SCB_AVAIL	0x80
-#define		SCB_AVAIL       	0x40
-#define		SNSCB_ROLLOVER  	0x20
-#define		SDSCB_ROLLOVER  	0x10
-#define		SESCB_ROLLOVER  	0x08
-#define		SCB_QSIZE       	0x07
-#define		SCB_QSIZE_256   	0x06
-
-#define	DFF_THRSH       		0xfb
-#define		WR_DFTHRSH      	0x70
-#define		WR_DFTHRSH_MAX  	0x70
-#define		WR_DFTHRSH_90   	0x60
-#define		WR_DFTHRSH_85   	0x50
-#define		WR_DFTHRSH_75   	0x40
-#define		WR_DFTHRSH_63   	0x30
-#define		WR_DFTHRSH_50   	0x20
-#define		WR_DFTHRSH_25   	0x10
-#define		RD_DFTHRSH_MAX  	0x07
-#define		RD_DFTHRSH      	0x07
-#define		RD_DFTHRSH_90   	0x06
-#define		RD_DFTHRSH_85   	0x05
-#define		RD_DFTHRSH_75   	0x04
-#define		RD_DFTHRSH_63   	0x03
-#define		RD_DFTHRSH_50   	0x02
-#define		RD_DFTHRSH_25   	0x01
-#define		RD_DFTHRSH_MIN  	0x00
-#define		WR_DFTHRSH_MIN  	0x00
-
-#define	SG_CACHEPTR     		0xfc
-#define		SG_USER_DATA    	0xfc
-#define		LAST_SEG        	0x02
-#define		LAST_SEG_DONE   	0x01
-
-
-#define	CMD_GROUP_CODE_SHIFT	0x05
-#define	BUS_8_BIT	0x00
-#define	QOUTFIFO_OFFSET	0x01
-#define	CCSGRAM_MAXSEGS	0x10
-#define	CMD_GROUP2_BYTE_DELTA	0xfa
-#define	MAX_OFFSET_8BIT	0x0f
-#define	BUS_16_BIT	0x01
-#define	QINFIFO_OFFSET	0x02
-#define	CMD_GROUP5_BYTE_DELTA	0x0b
-#define	MAX_OFFSET_ULTRA2	0x7f
-#define	MAX_OFFSET_16BIT	0x08
-#define	UNTAGGEDSCB_OFFSET	0x00
-#define	SCB_LIST_NULL	0xff
-#define	SG_SIZEOF	0x08
-#define	CMD_GROUP4_BYTE_DELTA	0x04
-#define	CMD_GROUP0_BYTE_DELTA	0xfc
-#define	HOST_MSG	0xff
-#define	BUS_32_BIT	0x02
-#define	CCSGADDR_MAX	0x80
-
-
-/* Downloaded Constant Definitions */
-#define	TMODE_NUMCMDS	0x00
diff -urN linux-2.4.3-pre1/drivers/scsi/aic7xxx_seq.c linux/drivers/scsi/aic7xxx_seq.c
--- linux-2.4.3-pre1/drivers/scsi/aic7xxx_seq.c	Wed Jun 21 17:25:03 2000
+++ linux/drivers/scsi/aic7xxx_seq.c	Wed Dec 31 16:00:00 1969
@@ -1,745 +0,0 @@
-/*
-  * DO NOT EDIT - This file is automatically generated.
-  */
-static unsigned char seqprog[] = {
-	0xff, 0x6a, 0x06, 0x08,
-	0x7f, 0x02, 0x04, 0x08,
-	0x12, 0x6a, 0x00, 0x00,
-	0xff, 0x6a, 0xd6, 0x09,
-	0xff, 0x6a, 0xdc, 0x09,
-	0x00, 0x65, 0xca, 0x58,
-	0xf7, 0x01, 0x02, 0x08,
-	0xff, 0x4e, 0xc8, 0x08,
-	0xbf, 0x60, 0xc0, 0x08,
-	0x60, 0x0b, 0x86, 0x68,
-	0x40, 0x00, 0x0c, 0x68,
-	0x08, 0x1f, 0x3e, 0x10,
-	0x60, 0x0b, 0x86, 0x68,
-	0x40, 0x00, 0x0c, 0x68,
-	0x08, 0x1f, 0x3e, 0x10,
-	0xff, 0x3e, 0x48, 0x60,
-	0x40, 0xfa, 0x10, 0x78,
-	0xff, 0xf6, 0xd4, 0x08,
-	0x01, 0x4e, 0x9c, 0x18,
-	0x40, 0x60, 0xc0, 0x00,
-	0x00, 0x4d, 0x10, 0x70,
-	0x01, 0x4e, 0x9c, 0x18,
-	0xbf, 0x60, 0xc0, 0x08,
-	0x00, 0x6a, 0x3e, 0x5c,
-	0xff, 0x4e, 0xc8, 0x18,
-	0x02, 0x6a, 0x54, 0x5b,
-	0xff, 0x52, 0x20, 0x09,
-	0x0d, 0x6a, 0x6a, 0x00,
-	0x00, 0x52, 0xca, 0x5b,
-	0x03, 0xb0, 0x52, 0x31,
-	0xff, 0xb0, 0x52, 0x09,
-	0xff, 0xb1, 0x54, 0x09,
-	0xff, 0xb2, 0x56, 0x09,
-	0xff, 0xa3, 0x50, 0x09,
-	0xff, 0x3e, 0x74, 0x09,
-	0xff, 0x90, 0x7c, 0x08,
-	0xff, 0x3e, 0x20, 0x09,
-	0x00, 0x65, 0x4e, 0x58,
-	0x00, 0x65, 0x0c, 0x40,
-	0xf7, 0x1f, 0xca, 0x08,
-	0x08, 0xa1, 0xc8, 0x08,
-	0x00, 0x65, 0xca, 0x00,
-	0xff, 0x65, 0x3e, 0x08,
-	0xf0, 0xa1, 0xc8, 0x08,
-	0x0f, 0x0f, 0x1e, 0x08,
-	0x00, 0x0f, 0x1e, 0x00,
-	0xf0, 0xa1, 0xc8, 0x08,
-	0x0f, 0x05, 0x0a, 0x08,
-	0x00, 0x05, 0x0a, 0x00,
-	0xff, 0x6a, 0x0c, 0x08,
-	0x5a, 0x6a, 0x00, 0x04,
-	0x12, 0x65, 0x02, 0x00,
-	0x31, 0x6a, 0xca, 0x00,
-	0x80, 0x37, 0x6e, 0x68,
-	0xff, 0x65, 0xca, 0x18,
-	0xff, 0x37, 0xdc, 0x08,
-	0xff, 0x6e, 0xc8, 0x08,
-	0x00, 0x6c, 0x76, 0x78,
-	0x20, 0x01, 0x02, 0x00,
-	0x4c, 0x37, 0xc8, 0x28,
-	0x08, 0x1f, 0x7e, 0x78,
-	0x08, 0x37, 0x6e, 0x00,
-	0x08, 0x64, 0xc8, 0x00,
-	0x70, 0x64, 0xca, 0x18,
-	0xff, 0x6c, 0x0a, 0x08,
-	0x20, 0x64, 0xca, 0x18,
-	0xff, 0x6c, 0x08, 0x0c,
-	0x40, 0x0b, 0x96, 0x68,
-	0x20, 0x6a, 0x16, 0x00,
-	0xf0, 0x19, 0x6e, 0x08,
-	0x08, 0x6a, 0x18, 0x00,
-	0x08, 0x11, 0x22, 0x00,
-	0x08, 0x6a, 0x66, 0x58,
-	0x08, 0x6a, 0x68, 0x00,
-	0x00, 0x65, 0xaa, 0x40,
-	0x12, 0x6a, 0x00, 0x00,
-	0x40, 0x6a, 0x16, 0x00,
-	0xff, 0x3e, 0x20, 0x09,
-	0xff, 0xba, 0x7c, 0x08,
-	0xff, 0xa1, 0x6e, 0x08,
-	0x08, 0x6a, 0x18, 0x00,
-	0x08, 0x11, 0x22, 0x00,
-	0x08, 0x6a, 0x66, 0x58,
-	0x80, 0x6a, 0x68, 0x00,
-	0x80, 0x36, 0x6c, 0x00,
-	0x00, 0x65, 0x9e, 0x5b,
-	0xff, 0x3d, 0xc8, 0x08,
-	0xbf, 0x64, 0xe2, 0x78,
-	0x80, 0x64, 0xac, 0x71,
-	0xa0, 0x64, 0xdc, 0x71,
-	0xc0, 0x64, 0xd4, 0x71,
-	0xe0, 0x64, 0x1c, 0x72,
-	0x01, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0xaa, 0x40,
-	0xf7, 0x11, 0x22, 0x08,
-	0x00, 0x65, 0xca, 0x58,
-	0xff, 0x06, 0xd4, 0x08,
-	0xf7, 0x01, 0x02, 0x08,
-	0x09, 0x0c, 0xc4, 0x78,
-	0x08, 0x0c, 0x0c, 0x68,
-	0x01, 0x6a, 0x22, 0x01,
-	0xff, 0x6a, 0x26, 0x09,
-	0x02, 0x6a, 0x08, 0x30,
-	0xff, 0x6a, 0x08, 0x08,
-	0xdf, 0x01, 0x02, 0x08,
-	0x01, 0x6a, 0x7a, 0x00,
-	0xff, 0x6a, 0x6c, 0x0c,
-	0x04, 0x14, 0x10, 0x31,
-	0x03, 0xa9, 0x18, 0x31,
-	0x03, 0xa9, 0x10, 0x30,
-	0x08, 0x6a, 0xcc, 0x00,
-	0xa9, 0x6a, 0xb4, 0x5b,
-	0x00, 0x65, 0x02, 0x41,
-	0xa8, 0x6a, 0x6a, 0x00,
-	0x79, 0x6a, 0x6a, 0x00,
-	0x40, 0x3d, 0xea, 0x68,
-	0x04, 0x35, 0x6a, 0x00,
-	0x00, 0x65, 0x0e, 0x5b,
-	0x80, 0x6a, 0xd4, 0x01,
-	0x10, 0x36, 0xd6, 0x68,
-	0x10, 0x36, 0x6c, 0x00,
-	0x07, 0xac, 0x10, 0x31,
-	0x03, 0x8c, 0x10, 0x30,
-	0x05, 0xa3, 0x70, 0x30,
-	0x88, 0x6a, 0xcc, 0x00,
-	0xac, 0x6a, 0xac, 0x5b,
-	0x00, 0x65, 0xa6, 0x5b,
-	0x38, 0x6a, 0xcc, 0x00,
-	0xa3, 0x6a, 0xb0, 0x5b,
-	0xff, 0x38, 0x12, 0x69,
-	0x80, 0x02, 0x04, 0x00,
-	0xe7, 0x35, 0x6a, 0x08,
-	0x03, 0x69, 0x18, 0x31,
-	0x03, 0x69, 0x10, 0x30,
-	0xff, 0x6a, 0x10, 0x00,
-	0xff, 0x6a, 0x12, 0x00,
-	0xff, 0x6a, 0x14, 0x00,
-	0x01, 0x38, 0x18, 0x61,
-	0xbf, 0x35, 0x6a, 0x08,
-	0x02, 0x6a, 0xf8, 0x01,
-	0xff, 0x69, 0xca, 0x08,
-	0xff, 0x35, 0x26, 0x09,
-	0x04, 0x0b, 0x1c, 0x69,
-	0x04, 0x0b, 0x28, 0x69,
-	0x10, 0x0c, 0x1e, 0x79,
-	0x04, 0x0b, 0x28, 0x69,
-	0xff, 0x6a, 0xca, 0x08,
-	0x00, 0x35, 0xee, 0x5a,
-	0x80, 0x02, 0x7c, 0x69,
-	0xff, 0x65, 0x6c, 0x79,
-	0xff, 0x38, 0x70, 0x18,
-	0xff, 0x38, 0x6c, 0x79,
-	0x80, 0xea, 0x48, 0x61,
-	0xef, 0x38, 0xc8, 0x18,
-	0x80, 0x6a, 0xc8, 0x00,
-	0x00, 0x65, 0x3a, 0x49,
-	0x33, 0x38, 0xc8, 0x28,
-	0xff, 0x64, 0xd0, 0x09,
-	0x04, 0x39, 0xc0, 0x31,
-	0x09, 0x6a, 0xd6, 0x01,
-	0x80, 0xeb, 0x40, 0x79,
-	0xf7, 0xeb, 0xd6, 0x09,
-	0x08, 0xeb, 0x44, 0x69,
-	0x01, 0x6a, 0xd6, 0x01,
-	0x08, 0xe9, 0x10, 0x31,
-	0x03, 0x8c, 0x10, 0x30,
-	0x88, 0x6a, 0xcc, 0x00,
-	0x39, 0x6a, 0xb2, 0x5b,
-	0x08, 0x6a, 0x18, 0x01,
-	0xff, 0x6a, 0x1a, 0x09,
-	0xff, 0x6a, 0x1c, 0x09,
-	0x0d, 0x93, 0x26, 0x01,
-	0x00, 0x65, 0x30, 0x5c,
-	0x88, 0x6a, 0x20, 0x5c,
-	0x00, 0x65, 0xa6, 0x5b,
-	0xff, 0x6a, 0xc8, 0x08,
-	0x08, 0x39, 0x72, 0x18,
-	0x00, 0x3a, 0x74, 0x20,
-	0x01, 0x0c, 0x64, 0x79,
-	0x10, 0x0c, 0x02, 0x79,
-	0xff, 0x35, 0x26, 0x09,
-	0x04, 0x0b, 0x6a, 0x69,
-	0x00, 0x65, 0x84, 0x59,
-	0x03, 0x08, 0x52, 0x31,
-	0xff, 0x38, 0x50, 0x09,
-	0xff, 0x08, 0x52, 0x09,
-	0xff, 0x09, 0x54, 0x09,
-	0xff, 0x0a, 0x56, 0x09,
-	0xff, 0x38, 0x50, 0x09,
-	0x00, 0x65, 0xaa, 0x40,
-	0x00, 0x65, 0x84, 0x59,
-	0x7f, 0x02, 0x04, 0x08,
-	0xe1, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0xaa, 0x40,
-	0x04, 0x93, 0x9a, 0x69,
-	0xdf, 0x93, 0x26, 0x09,
-	0x20, 0x93, 0x88, 0x69,
-	0x02, 0x93, 0x26, 0x01,
-	0x01, 0x94, 0x8a, 0x79,
-	0x01, 0x94, 0x8a, 0x79,
-	0x01, 0x94, 0x8a, 0x79,
-	0x01, 0x94, 0x8a, 0x79,
-	0x01, 0x94, 0x8a, 0x79,
-	0x01, 0x94, 0x8a, 0x79,
-	0x10, 0x94, 0x98, 0x69,
-	0x7f, 0x05, 0xa0, 0x69,
-	0x02, 0x03, 0xa0, 0x79,
-	0x11, 0x0c, 0x9c, 0x79,
-	0xd7, 0x93, 0x26, 0x09,
-	0x28, 0x93, 0xa2, 0x69,
-	0x03, 0x08, 0x52, 0x31,
-	0xff, 0x38, 0x50, 0x09,
-	0x12, 0x01, 0x02, 0x00,
-	0xff, 0x6a, 0xd4, 0x0c,
-	0x00, 0x65, 0x0e, 0x5b,
-	0x05, 0xb4, 0x10, 0x31,
-	0x02, 0x6a, 0x1a, 0x31,
-	0x03, 0x8c, 0x10, 0x30,
-	0x88, 0x6a, 0xcc, 0x00,
-	0xb4, 0x6a, 0xb0, 0x5b,
-	0xff, 0x6a, 0x1a, 0x09,
-	0xff, 0x6a, 0x1c, 0x09,
-	0x00, 0x65, 0xa6, 0x5b,
-	0x3d, 0x6a, 0xee, 0x5a,
-	0xac, 0x6a, 0x26, 0x01,
-	0x04, 0x0b, 0xc2, 0x69,
-	0x04, 0x0b, 0xc8, 0x69,
-	0x10, 0x0c, 0xc4, 0x79,
-	0x02, 0x03, 0xcc, 0x79,
-	0x11, 0x0c, 0xc8, 0x79,
-	0xd7, 0x93, 0x26, 0x09,
-	0x28, 0x93, 0xce, 0x69,
-	0x12, 0x01, 0x02, 0x00,
-	0x00, 0x65, 0xaa, 0x40,
-	0x00, 0x65, 0x0e, 0x5b,
-	0xff, 0x06, 0x44, 0x09,
-	0x00, 0x65, 0xaa, 0x40,
-	0x10, 0x3d, 0x06, 0x00,
-	0xff, 0x34, 0xca, 0x08,
-	0x80, 0x65, 0x00, 0x62,
-	0x0f, 0xa1, 0xca, 0x08,
-	0x07, 0xa1, 0xca, 0x08,
-	0x40, 0xa0, 0xc8, 0x08,
-	0x00, 0x65, 0xca, 0x00,
-	0x80, 0x65, 0xca, 0x00,
-	0x80, 0xa0, 0xf0, 0x79,
-	0xff, 0x65, 0x0c, 0x08,
-	0x00, 0x65, 0x02, 0x42,
-	0x20, 0xa0, 0x08, 0x7a,
-	0xff, 0x65, 0x0c, 0x08,
-	0x00, 0x65, 0x9e, 0x5b,
-	0xa0, 0x3d, 0x10, 0x62,
-	0x23, 0xa0, 0x0c, 0x08,
-	0x00, 0x65, 0x9e, 0x5b,
-	0xa0, 0x3d, 0x10, 0x62,
-	0x00, 0xb9, 0x08, 0x42,
-	0xff, 0x65, 0x08, 0x62,
-	0xa1, 0x6a, 0x22, 0x01,
-	0xff, 0x6a, 0xd4, 0x08,
-	0x10, 0x51, 0x10, 0x72,
-	0x40, 0x6a, 0x18, 0x00,
-	0xff, 0x65, 0x0c, 0x08,
-	0x00, 0x65, 0x9e, 0x5b,
-	0xa0, 0x3d, 0xda, 0x71,
-	0x40, 0x6a, 0x18, 0x00,
-	0xff, 0x34, 0xa6, 0x08,
-	0x80, 0x34, 0x18, 0x62,
-	0x7f, 0xa0, 0x40, 0x09,
-	0x08, 0x6a, 0x68, 0x00,
-	0x00, 0x65, 0xaa, 0x40,
-	0x64, 0x6a, 0xe4, 0x5a,
-	0x80, 0x64, 0x8e, 0x6a,
-	0x04, 0x64, 0x70, 0x72,
-	0x02, 0x64, 0x76, 0x72,
-	0x00, 0x6a, 0x38, 0x72,
-	0x03, 0x64, 0x8a, 0x72,
-	0x01, 0x64, 0x6c, 0x72,
-	0x07, 0x64, 0xcc, 0x72,
-	0x08, 0x64, 0x34, 0x72,
-	0x23, 0x64, 0xd0, 0x72,
-	0x11, 0x6a, 0x22, 0x01,
-	0x07, 0x6a, 0xd6, 0x5a,
-	0xff, 0x06, 0xd4, 0x08,
-	0x00, 0x65, 0xaa, 0x40,
-	0xff, 0xa8, 0x3c, 0x6a,
-	0xff, 0xa2, 0x54, 0x7a,
-	0x01, 0x6a, 0x6a, 0x00,
-	0x00, 0xb9, 0xca, 0x5b,
-	0xff, 0xa2, 0x54, 0x7a,
-	0x71, 0x6a, 0x22, 0x01,
-	0xff, 0x6a, 0xd4, 0x08,
-	0x40, 0x51, 0x54, 0x62,
-	0x0d, 0x6a, 0x6a, 0x00,
-	0x00, 0xb9, 0xca, 0x5b,
-	0xff, 0x3e, 0x74, 0x09,
-	0xff, 0x90, 0x7c, 0x08,
-	0x00, 0x65, 0x4e, 0x58,
-	0x00, 0x65, 0xbc, 0x40,
-	0x20, 0xa0, 0x5c, 0x6a,
-	0xff, 0x37, 0xc8, 0x08,
-	0x00, 0x6a, 0x74, 0x5b,
-	0xff, 0x6a, 0x8a, 0x5b,
-	0xff, 0xf8, 0xc8, 0x08,
-	0xff, 0x4f, 0xc8, 0x08,
-	0x01, 0x6a, 0x74, 0x5b,
-	0x00, 0xb9, 0x8a, 0x5b,
-	0x01, 0x4f, 0x9e, 0x18,
-	0x02, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0x38, 0x5c,
-	0x00, 0x65, 0xbc, 0x40,
-	0x41, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0xaa, 0x40,
-	0x04, 0xa0, 0x40, 0x01,
-	0x00, 0x65, 0x50, 0x5c,
-	0x00, 0x65, 0xbc, 0x40,
-	0x10, 0x36, 0x34, 0x7a,
-	0x05, 0x38, 0x46, 0x31,
-	0x04, 0x14, 0x58, 0x31,
-	0x03, 0xa9, 0x60, 0x31,
-	0xa3, 0x6a, 0xcc, 0x00,
-	0x38, 0x6a, 0xb0, 0x5b,
-	0xac, 0x6a, 0xcc, 0x00,
-	0x14, 0x6a, 0xb2, 0x5b,
-	0xa9, 0x6a, 0xb4, 0x5b,
-	0x00, 0x65, 0x34, 0x42,
-	0xef, 0x36, 0x6c, 0x08,
-	0x00, 0x65, 0x34, 0x42,
-	0x0f, 0x64, 0xc8, 0x08,
-	0x07, 0x64, 0xc8, 0x08,
-	0x00, 0x37, 0x6e, 0x00,
-	0xff, 0x6a, 0xa4, 0x00,
-	0x00, 0x65, 0x44, 0x5b,
-	0xff, 0x51, 0xa0, 0x72,
-	0x20, 0x36, 0xaa, 0x7a,
-	0x00, 0x90, 0x32, 0x5b,
-	0x00, 0x65, 0xac, 0x42,
-	0xff, 0x06, 0xd4, 0x08,
-	0x00, 0x65, 0x9e, 0x5b,
-	0xe0, 0x3d, 0xc6, 0x62,
-	0x20, 0x12, 0xc6, 0x62,
-	0x51, 0x6a, 0xda, 0x5a,
-	0x00, 0x65, 0x2c, 0x5b,
-	0xff, 0x37, 0xc8, 0x08,
-	0x00, 0xa1, 0xbe, 0x62,
-	0x04, 0xa0, 0xbe, 0x7a,
-	0xfb, 0xa0, 0x40, 0x09,
-	0x80, 0x36, 0x6c, 0x00,
-	0x80, 0xa0, 0x34, 0x7a,
-	0x7f, 0xa0, 0x40, 0x09,
-	0xff, 0x6a, 0xd6, 0x5a,
-	0x00, 0x65, 0x34, 0x42,
-	0x04, 0xa0, 0xc4, 0x7a,
-	0x00, 0x65, 0x50, 0x5c,
-	0x00, 0x65, 0xc6, 0x42,
-	0x00, 0x65, 0x38, 0x5c,
-	0x31, 0x6a, 0x22, 0x01,
-	0x0c, 0x6a, 0xd6, 0x5a,
-	0x00, 0x65, 0x34, 0x42,
-	0x61, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0x34, 0x42,
-	0x51, 0x6a, 0xda, 0x5a,
-	0x51, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0x34, 0x42,
-	0x10, 0x3d, 0x06, 0x00,
-	0xff, 0x65, 0x68, 0x0c,
-	0xff, 0x06, 0xd4, 0x08,
-	0x01, 0x0c, 0xdc, 0x7a,
-	0x04, 0x0c, 0xde, 0x6a,
-	0xe0, 0x03, 0x7a, 0x08,
-	0xe0, 0x3d, 0xea, 0x62,
-	0xff, 0x65, 0xcc, 0x08,
-	0xff, 0x12, 0xda, 0x0c,
-	0xff, 0x06, 0xd4, 0x0c,
-	0xd1, 0x6a, 0x22, 0x01,
-	0x00, 0x65, 0xaa, 0x40,
-	0xff, 0x65, 0x26, 0x09,
-	0x01, 0x0b, 0xfe, 0x6a,
-	0x10, 0x0c, 0xf0, 0x7a,
-	0x04, 0x0b, 0xf8, 0x6a,
-	0xff, 0x6a, 0xca, 0x08,
-	0x04, 0x93, 0xfc, 0x6a,
-	0x01, 0x94, 0xfa, 0x7a,
-	0x10, 0x94, 0xfc, 0x6a,
-	0x80, 0x3d, 0x02, 0x73,
-	0x0f, 0x04, 0x06, 0x6b,
-	0x02, 0x03, 0x06, 0x7b,
-	0x11, 0x0c, 0x02, 0x7b,
-	0xc7, 0x93, 0x26, 0x09,
-	0xff, 0x99, 0xd4, 0x08,
-	0x38, 0x93, 0x08, 0x6b,
-	0xff, 0x6a, 0xd4, 0x0c,
-	0x80, 0x36, 0x0c, 0x6b,
-	0x21, 0x6a, 0x22, 0x05,
-	0xff, 0x65, 0x20, 0x09,
-	0xff, 0x51, 0x1a, 0x63,
-	0xff, 0x37, 0xc8, 0x08,
-	0xa1, 0x6a, 0x26, 0x43,
-	0xff, 0x51, 0xc8, 0x08,
-	0xb9, 0x6a, 0x26, 0x43,
-	0xff, 0x90, 0xa4, 0x08,
-	0xff, 0xba, 0x2a, 0x73,
-	0xff, 0xba, 0x20, 0x09,
-	0xff, 0x65, 0xca, 0x18,
-	0x00, 0x6c, 0x1e, 0x63,
-	0xff, 0x90, 0xca, 0x0c,
-	0xff, 0x6a, 0xca, 0x04,
-	0x20, 0x36, 0x3e, 0x7b,
-	0x00, 0x90, 0x12, 0x5b,
-	0xff, 0x65, 0x3e, 0x73,
-	0xff, 0x52, 0x3c, 0x73,
-	0xff, 0xba, 0xcc, 0x08,
-	0xff, 0x52, 0x20, 0x09,
-	0xff, 0x66, 0x74, 0x09,
-	0xff, 0x65, 0x20, 0x0d,
-	0xff, 0xba, 0x7e, 0x0c,
-	0x00, 0x6a, 0x3e, 0x5c,
-	0x0d, 0x6a, 0x6a, 0x00,
-	0x00, 0x51, 0xca, 0x43,
-	0xff, 0x3f, 0x98, 0x73,
-	0xff, 0x6a, 0xa2, 0x00,
-	0x00, 0x3f, 0x12, 0x5b,
-	0xff, 0x65, 0x98, 0x73,
-	0x20, 0x36, 0x6c, 0x00,
-	0x20, 0xa0, 0x52, 0x6b,
-	0xff, 0xb9, 0xa2, 0x0c,
-	0xff, 0x6a, 0xa2, 0x04,
-	0xff, 0x65, 0xa4, 0x08,
-	0xe0, 0x6a, 0xcc, 0x00,
-	0x45, 0x6a, 0xbe, 0x5b,
-	0x01, 0x6a, 0xd0, 0x01,
-	0x09, 0x6a, 0xd6, 0x01,
-	0x80, 0xeb, 0x5e, 0x7b,
-	0x01, 0x6a, 0xd6, 0x01,
-	0x01, 0xe9, 0xa4, 0x34,
-	0x88, 0x6a, 0xcc, 0x00,
-	0x45, 0x6a, 0xbe, 0x5b,
-	0x01, 0x6a, 0x18, 0x01,
-	0xff, 0x6a, 0x1a, 0x09,
-	0xff, 0x6a, 0x1c, 0x09,
-	0x0d, 0x6a, 0x26, 0x01,
-	0x00, 0x65, 0x30, 0x5c,
-	0xff, 0x99, 0xa4, 0x0c,
-	0xff, 0x65, 0xa4, 0x08,
-	0xe0, 0x6a, 0xcc, 0x00,
-	0x45, 0x6a, 0xbe, 0x5b,
-	0x01, 0x6a, 0xd0, 0x01,
-	0x01, 0x6a, 0xdc, 0x05,
-	0x88, 0x6a, 0xcc, 0x00,
-	0x45, 0x6a, 0xbe, 0x5b,
-	0x01, 0x6a, 0x18, 0x01,
-	0xff, 0x6a, 0x1a, 0x09,
-	0xff, 0x6a, 0x1c, 0x09,
-	0x01, 0x6a, 0x26, 0x05,
-	0x01, 0x65, 0xd8, 0x31,
-	0x09, 0xee, 0xdc, 0x01,
-	0x80, 0xee, 0x8e, 0x7b,
-	0xff, 0x6a, 0xdc, 0x0d,
-	0xff, 0x65, 0x32, 0x09,
-	0x0a, 0x93, 0x26, 0x01,
-	0x00, 0x65, 0x30, 0x44,
-	0xff, 0x37, 0xc8, 0x08,
-	0x00, 0x6a, 0x54, 0x5b,
-	0xff, 0x52, 0xa2, 0x0c,
-	0x01, 0x0c, 0x9e, 0x7b,
-	0x04, 0x0c, 0x9e, 0x6b,
-	0xe0, 0x03, 0x06, 0x08,
-	0xe0, 0x03, 0x7a, 0x0c,
-	0xff, 0x8c, 0x10, 0x08,
-	0xff, 0x8d, 0x12, 0x08,
-	0xff, 0x8e, 0x14, 0x0c,
-	0xff, 0x6c, 0xda, 0x08,
-	0xff, 0x6c, 0xda, 0x08,
-	0xff, 0x6c, 0xda, 0x08,
-	0xff, 0x6c, 0xda, 0x08,
-	0xff, 0x6c, 0xda, 0x08,
-	0xff, 0x6c, 0xda, 0x08,
-	0xff, 0x6c, 0xda, 0x0c,
-	0x3d, 0x64, 0xa4, 0x28,
-	0x55, 0x64, 0xc8, 0x28,
-	0x00, 0x6c, 0xda, 0x18,
-	0xff, 0x52, 0xc8, 0x08,
-	0x00, 0x6c, 0xda, 0x20,
-	0xff, 0x6a, 0xc8, 0x08,
-	0x00, 0x6c, 0xda, 0x20,
-	0x00, 0x6c, 0xda, 0x24,
-	0xff, 0x65, 0xc8, 0x08,
-	0xe0, 0x6a, 0xcc, 0x00,
-	0x41, 0x6a, 0xba, 0x5b,
-	0xff, 0x90, 0xe2, 0x09,
-	0x20, 0x6a, 0xd0, 0x01,
-	0x04, 0x35, 0xdc, 0x7b,
-	0x1d, 0x6a, 0xdc, 0x01,
-	0xdc, 0xee, 0xd8, 0x63,
-	0x00, 0x65, 0xe8, 0x43,
-	0x01, 0x6a, 0xdc, 0x01,
-	0x20, 0xa0, 0xd8, 0x31,
-	0x09, 0xee, 0xdc, 0x01,
-	0x80, 0xee, 0xe2, 0x7b,
-	0x19, 0x6a, 0xdc, 0x01,
-	0xd8, 0xee, 0xe6, 0x63,
-	0xff, 0x6a, 0xdc, 0x09,
-	0x18, 0xee, 0xea, 0x6b,
-	0xff, 0x6a, 0xd4, 0x0c,
-	0x88, 0x6a, 0xcc, 0x00,
-	0x41, 0x6a, 0xba, 0x5b,
-	0x20, 0x6a, 0x18, 0x01,
-	0xff, 0x6a, 0x1a, 0x09,
-	0xff, 0x6a, 0x1c, 0x09,
-	0xff, 0x35, 0x26, 0x09,
-	0x04, 0x35, 0x14, 0x6c,
-	0xa0, 0x6a, 0xca, 0x00,
-	0x20, 0x65, 0xc8, 0x18,
-	0xff, 0x6c, 0x32, 0x09,
-	0xff, 0x6c, 0x32, 0x09,
-	0xff, 0x6c, 0x32, 0x09,
-	0xff, 0x6c, 0x32, 0x09,
-	0xff, 0x6c, 0x32, 0x09,
-	0xff, 0x6c, 0x32, 0x09,
-	0xff, 0x6c, 0x32, 0x09,
-	0xff, 0x6c, 0x32, 0x09,
-	0x00, 0x65, 0x00, 0x64,
-	0x0a, 0x93, 0x26, 0x01,
-	0x00, 0x65, 0x30, 0x5c,
-	0x04, 0x35, 0x0c, 0x7b,
-	0xa0, 0x6a, 0x20, 0x5c,
-	0x00, 0x65, 0x22, 0x5c,
-	0x00, 0x65, 0x22, 0x5c,
-	0x00, 0x65, 0x22, 0x44,
-	0xff, 0x65, 0xcc, 0x08,
-	0xff, 0x99, 0xda, 0x08,
-	0xff, 0x99, 0xda, 0x08,
-	0xff, 0x99, 0xda, 0x08,
-	0xff, 0x99, 0xda, 0x08,
-	0xff, 0x99, 0xda, 0x08,
-	0xff, 0x99, 0xda, 0x08,
-	0xff, 0x99, 0xda, 0x0c,
-	0x08, 0x94, 0x30, 0x7c,
-	0xf7, 0x93, 0x26, 0x09,
-	0x08, 0x93, 0x34, 0x6c,
-	0xff, 0x6a, 0xd4, 0x0c,
-	0xff, 0x40, 0x74, 0x09,
-	0xff, 0x90, 0x80, 0x08,
-	0xff, 0x6a, 0x72, 0x05,
-	0xff, 0x40, 0x4c, 0x64,
-	0xff, 0x3f, 0x44, 0x64,
-	0xff, 0x6a, 0xca, 0x04,
-	0xff, 0x3f, 0x20, 0x09,
-	0x01, 0x6a, 0x6a, 0x00,
-	0x00, 0xb9, 0xca, 0x5b,
-	0xff, 0xba, 0x7e, 0x0c,
-	0xff, 0x40, 0x20, 0x09,
-	0xff, 0xba, 0x80, 0x0c,
-	0xff, 0x3f, 0x74, 0x09,
-	0xff, 0x90, 0x7e, 0x0c,
-};
-
-static int aic7xxx_patch12_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch12_func(struct aic7xxx_host *p)
-{
-	return ((p->features & AHC_WIDE) != 0);
-}
-
-static int aic7xxx_patch11_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch11_func(struct aic7xxx_host *p)
-{
-	return ((p->features & AHC_ULTRA2) == 0);
-}
-
-static int aic7xxx_patch10_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch10_func(struct aic7xxx_host *p)
-{
-	return ((p->features & AHC_CMD_CHAN) == 0);
-}
-
-static int aic7xxx_patch9_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch9_func(struct aic7xxx_host *p)
-{
-	return ((p->chip & AHC_CHIPID_MASK) == AHC_AIC7895);
-}
-
-static int aic7xxx_patch8_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch8_func(struct aic7xxx_host *p)
-{
-	return ((p->features & AHC_ULTRA) != 0);
-}
-
-static int aic7xxx_patch7_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch7_func(struct aic7xxx_host *p)
-{
-	return ((p->features & AHC_ULTRA2) != 0);
-}
-
-static int aic7xxx_patch6_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch6_func(struct aic7xxx_host *p)
-{
-	return ((p->flags & AHC_PAGESCBS) == 0);
-}
-
-static int aic7xxx_patch5_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch5_func(struct aic7xxx_host *p)
-{
-	return ((p->flags & AHC_PAGESCBS) != 0);
-}
-
-static int aic7xxx_patch4_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch4_func(struct aic7xxx_host *p)
-{
-	return ((p->features & AHC_QUEUE_REGS) != 0);
-}
-
-static int aic7xxx_patch3_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch3_func(struct aic7xxx_host *p)
-{
-	return ((p->features & AHC_TWIN) != 0);
-}
-
-static int aic7xxx_patch2_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch2_func(struct aic7xxx_host *p)
-{
-	return ((p->features & AHC_QUEUE_REGS) == 0);
-}
-
-static int aic7xxx_patch1_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch1_func(struct aic7xxx_host *p)
-{
-	return ((p->features & AHC_CMD_CHAN) != 0);
-}
-
-static int aic7xxx_patch0_func(struct aic7xxx_host *p);
-
-static int
-aic7xxx_patch0_func(struct aic7xxx_host *p)
-{
-	return (0);
-}
-
-struct sequencer_patch {
-	int		(*patch_func)(struct aic7xxx_host *);
-	unsigned int	begin	   :10,
-			skip_instr :10,
-			skip_patch :12;
-} sequencer_patches[] = {
-	{ aic7xxx_patch1_func, 3, 2, 1 },
-	{ aic7xxx_patch2_func, 7, 1, 1 },
-	{ aic7xxx_patch2_func, 8, 1, 1 },
-	{ aic7xxx_patch3_func, 11, 4, 1 },
-	{ aic7xxx_patch4_func, 16, 3, 2 },
-	{ aic7xxx_patch0_func, 19, 4, 1 },
-	{ aic7xxx_patch5_func, 23, 1, 1 },
-	{ aic7xxx_patch6_func, 26, 1, 1 },
-	{ aic7xxx_patch1_func, 29, 1, 2 },
-	{ aic7xxx_patch0_func, 30, 3, 1 },
-	{ aic7xxx_patch3_func, 39, 4, 1 },
-	{ aic7xxx_patch7_func, 43, 3, 2 },
-	{ aic7xxx_patch0_func, 46, 3, 1 },
-	{ aic7xxx_patch8_func, 52, 7, 1 },
-	{ aic7xxx_patch3_func, 60, 3, 1 },
-	{ aic7xxx_patch7_func, 63, 2, 1 },
-	{ aic7xxx_patch7_func, 102, 1, 2 },
-	{ aic7xxx_patch0_func, 103, 2, 1 },
-	{ aic7xxx_patch7_func, 107, 2, 1 },
-	{ aic7xxx_patch9_func, 109, 1, 1 },
-	{ aic7xxx_patch10_func, 110, 2, 1 },
-	{ aic7xxx_patch7_func, 113, 1, 2 },
-	{ aic7xxx_patch0_func, 114, 1, 1 },
-	{ aic7xxx_patch1_func, 118, 1, 1 },
-	{ aic7xxx_patch1_func, 121, 3, 2 },
-	{ aic7xxx_patch0_func, 124, 5, 1 },
-	{ aic7xxx_patch1_func, 132, 2, 3 },
-	{ aic7xxx_patch7_func, 132, 1, 1 },
-	{ aic7xxx_patch0_func, 134, 3, 1 },
-	{ aic7xxx_patch11_func, 138, 1, 2 },
-	{ aic7xxx_patch0_func, 139, 1, 1 },
-	{ aic7xxx_patch7_func, 140, 7, 2 },
-	{ aic7xxx_patch0_func, 147, 1, 1 },
-	{ aic7xxx_patch1_func, 152, 14, 3 },
-	{ aic7xxx_patch11_func, 165, 1, 1 },
-	{ aic7xxx_patch0_func, 166, 9, 1 },
-	{ aic7xxx_patch7_func, 180, 2, 1 },
-	{ aic7xxx_patch7_func, 182, 1, 1 },
-	{ aic7xxx_patch11_func, 183, 6, 3 },
-	{ aic7xxx_patch1_func, 183, 2, 2 },
-	{ aic7xxx_patch0_func, 185, 4, 1 },
-	{ aic7xxx_patch7_func, 190, 1, 1 },
-	{ aic7xxx_patch7_func, 194, 20, 1 },
-	{ aic7xxx_patch1_func, 215, 3, 3 },
-	{ aic7xxx_patch11_func, 217, 1, 1 },
-	{ aic7xxx_patch0_func, 218, 5, 1 },
-	{ aic7xxx_patch11_func, 223, 1, 2 },
-	{ aic7xxx_patch0_func, 224, 9, 1 },
-	{ aic7xxx_patch12_func, 240, 1, 2 },
-	{ aic7xxx_patch0_func, 241, 1, 1 },
-	{ aic7xxx_patch4_func, 302, 1, 2 },
-	{ aic7xxx_patch0_func, 303, 1, 1 },
-	{ aic7xxx_patch2_func, 306, 1, 1 },
-	{ aic7xxx_patch1_func, 316, 3, 2 },
-	{ aic7xxx_patch0_func, 319, 5, 1 },
-	{ aic7xxx_patch12_func, 327, 1, 2 },
-	{ aic7xxx_patch0_func, 328, 1, 1 },
-	{ aic7xxx_patch5_func, 333, 1, 1 },
-	{ aic7xxx_patch11_func, 375, 15, 1 },
-	{ aic7xxx_patch1_func, 427, 7, 2 },
-	{ aic7xxx_patch0_func, 434, 8, 1 },
-	{ aic7xxx_patch1_func, 443, 4, 2 },
-	{ aic7xxx_patch0_func, 447, 6, 1 },
-	{ aic7xxx_patch1_func, 453, 4, 2 },
-	{ aic7xxx_patch0_func, 457, 3, 1 },
-	{ aic7xxx_patch10_func, 467, 10, 1 },
-	{ aic7xxx_patch1_func, 486, 17, 4 },
-	{ aic7xxx_patch9_func, 494, 4, 2 },
-	{ aic7xxx_patch0_func, 498, 2, 1 },
-	{ aic7xxx_patch0_func, 503, 33, 1 },
-	{ aic7xxx_patch10_func, 536, 4, 1 },
-	{ aic7xxx_patch5_func, 540, 2, 1 },
-	{ aic7xxx_patch5_func, 543, 9, 1 },
-
-};
diff -urN linux-2.4.3-pre1/drivers/scsi/blz2060.c linux/drivers/scsi/blz2060.c
--- linux-2.4.3-pre1/drivers/scsi/blz2060.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/blz2060.c	Mon Dec  3 22:29:50 2001
@@ -53,7 +53,7 @@
 
 volatile unsigned char cmd_buffer[16];
 				/* This is where all commands are put
-				 * before they are transfered to the ESP chip
+				 * before they are transferred to the ESP chip
 				 * via PIO.
 				 */
 
diff -urN linux-2.4.3-pre1/drivers/scsi/cpqfcTScontrol.c linux/drivers/scsi/cpqfcTScontrol.c
--- linux-2.4.3-pre1/drivers/scsi/cpqfcTScontrol.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/cpqfcTScontrol.c	Mon Dec  3 22:29:50 2001
@@ -1204,7 +1204,7 @@
           // open Login exchanges, in case the LinkDown happened in the
           // middle of logins.  It's possible that some ports already
           // ACCepted login commands which we have not processed before
-          // another LinkDown occured.  Any accepted Login exhanges are
+          // another LinkDown occurred.  Any accepted Login exhanges are
           // invalidated by LinkDown, even before they are acknowledged.
           // It's also possible for a port to have a Queued Reply or Request
           // for login which was interrupted by LinkDown; it may come later,
diff -urN linux-2.4.3-pre1/drivers/scsi/cpqfcTSinit.c linux/drivers/scsi/cpqfcTSinit.c
--- linux-2.4.3-pre1/drivers/scsi/cpqfcTSinit.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/cpqfcTSinit.c	Mon Dec  3 22:29:50 2001
@@ -1701,7 +1701,7 @@
 // removal time (load and unload times)
 // ALGORITHM notes:
 // Memory allocation varies by compiler and platform.  In the worst case,
-// we are only assured BYTE allignment, but in the best case, we can
+// we are only assured BYTE alignment, but in the best case, we can
 // request allocation on any desired boundary.  Our strategy: pad the
 // allocation request size (i.e. waste memory) so that we are assured
 // of passing desired boundary near beginning of contiguous space, then
diff -urN linux-2.4.3-pre1/drivers/scsi/cpqfcTSworker.c linux/drivers/scsi/cpqfcTSworker.c
--- linux-2.4.3-pre1/drivers/scsi/cpqfcTSworker.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/cpqfcTSworker.c	Mon Dec  3 22:29:50 2001
@@ -422,7 +422,7 @@
     
 //      printk(" *ELS %Xh* ", fcLQ->Qitem[QconsumerNdx].Type);
       // if PortDiscDone is not set, it means the SendLogins routine
-      // failed to complete -- assume that LDn occured, so login frames
+      // failed to complete -- assume that LDn occurred, so login frames
       // are invalid
       if( !cpqfcHBAdata->PortDiscDone) // cleared by LDn
       {
@@ -4143,7 +4143,7 @@
   TachLiteIRE* pIRE;
   TachLiteTWE* pTWE;
   TachLiteTRE* pTRE;
-  ULONG fcp_dl;           // total byte length of DATA transfered
+  ULONG fcp_dl;           // total byte length of DATA transferred
   ULONG fl;               // frame length (FC frame size, 128, 256, 512, 1024)
   ULONG sgPairs;          // number of valid scatter/gather pairs
   int FCP_SCSI_command;
diff -urN linux-2.4.3-pre1/drivers/scsi/cyberstorm.c linux/drivers/scsi/cyberstorm.c
--- linux-2.4.3-pre1/drivers/scsi/cyberstorm.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/cyberstorm.c	Mon Dec  3 22:29:50 2001
@@ -62,7 +62,7 @@
 
 volatile unsigned char cmd_buffer[16];
 				/* This is where all commands are put
-				 * before they are transfered to the ESP chip
+				 * before they are transferred to the ESP chip
 				 * via PIO.
 				 */
 
diff -urN linux-2.4.3-pre1/drivers/scsi/cyberstormII.c linux/drivers/scsi/cyberstormII.c
--- linux-2.4.3-pre1/drivers/scsi/cyberstormII.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/cyberstormII.c	Mon Dec  3 22:29:50 2001
@@ -52,7 +52,7 @@
 
 volatile unsigned char cmd_buffer[16];
 				/* This is where all commands are put
-				 * before they are transfered to the ESP chip
+				 * before they are transferred to the ESP chip
 				 * via PIO.
 				 */
 
diff -urN linux-2.4.3-pre1/drivers/scsi/i60uscsi.c linux/drivers/scsi/i60uscsi.c
--- linux-2.4.3-pre1/drivers/scsi/i60uscsi.c	Thu Sep  2 10:03:26 1999
+++ linux/drivers/scsi/i60uscsi.c	Mon Dec  3 22:29:50 2001
@@ -33,7 +33,7 @@
  *    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 
+ * the GNU General 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
diff -urN linux-2.4.3-pre1/drivers/scsi/i60uscsi.h linux/drivers/scsi/i60uscsi.h
--- linux-2.4.3-pre1/drivers/scsi/i60uscsi.h	Fri Nov 12 04:29:47 1999
+++ linux/drivers/scsi/i60uscsi.h	Mon Dec  3 22:29:50 2001
@@ -33,7 +33,7 @@
  *    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 
+ * the GNU General 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
diff -urN linux-2.4.3-pre1/drivers/scsi/i91uscsi.c linux/drivers/scsi/i91uscsi.c
--- linux-2.4.3-pre1/drivers/scsi/i91uscsi.c	Mon Aug 23 10:23:23 1999
+++ linux/drivers/scsi/i91uscsi.c	Mon Dec  3 22:29:50 2001
@@ -34,7 +34,7 @@
  *    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 
+ * the GNU General 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
diff -urN linux-2.4.3-pre1/drivers/scsi/i91uscsi.h linux/drivers/scsi/i91uscsi.h
--- linux-2.4.3-pre1/drivers/scsi/i91uscsi.h	Fri Dec 18 10:12:25 1998
+++ linux/drivers/scsi/i91uscsi.h	Mon Dec  3 22:29:50 2001
@@ -33,7 +33,7 @@
  *    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 
+ * the GNU General 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
diff -urN linux-2.4.3-pre1/drivers/scsi/ibmmca.c linux/drivers/scsi/ibmmca.c
--- linux-2.4.3-pre1/drivers/scsi/ibmmca.c	Sun Jan 28 16:08:00 2001
+++ linux/drivers/scsi/ibmmca.c	Mon Dec  3 22:29:50 2001
@@ -1594,7 +1594,7 @@
       if ((pos[2] & 1) == 1) /* is the subsystem chip enabled ? */
 	port = IM_IO_PORT;
       else { /* if disabled, no IRQs will be generated, as the chip won't
-	      * listen to the incomming commands and will do really nothing,
+	      * listen to the incoming commands and will do really nothing,
 	      * except for listening to the pos-register settings. If this
 	      * happens, I need to hugely think about it, as one has to
 	      * write something to the MCA-Bus pos register in order to
diff -urN linux-2.4.3-pre1/drivers/scsi/ibmmca.h linux/drivers/scsi/ibmmca.h
--- linux-2.4.3-pre1/drivers/scsi/ibmmca.h	Sun Dec 31 09:36:15 2000
+++ linux/drivers/scsi/ibmmca.h	Mon Dec  3 22:29:50 2001
@@ -1,7 +1,7 @@
 /* 
  * Low Level Driver for the IBM Microchannel SCSI Subsystem
  * (Headerfile, see README.ibmmca for description of the IBM MCA SCSI-driver
- * For use under the GNU public license within the Linux-kernel project.
+ * For use under the GNU General Public License within the Linux-kernel project.
  * This include file works only correctly with kernel 2.4.0 or higher!!! */
 
 #ifndef _IBMMCA_H
diff -urN linux-2.4.3-pre1/drivers/scsi/imm.c linux/drivers/scsi/imm.c
--- linux-2.4.3-pre1/drivers/scsi/imm.c	Mon Dec 11 13:43:20 2000
+++ linux/drivers/scsi/imm.c	Mon Dec  3 22:29:50 2001
@@ -36,7 +36,7 @@
     int mode;			/* Transfer mode                */
     int host;			/* Host number (for proc)       */
     Scsi_Cmnd *cur_cmd;		/* Current queued command       */
-    struct tq_struct imm_tq;	/* Polling interupt stuff       */
+    struct tq_struct imm_tq;	/* Polling interrupt stuff       */
     unsigned long jstart;	/* Jiffies at start             */
     unsigned failed:1;		/* Failure flag                 */
     unsigned dp:1;		/* Data phase present           */
diff -urN linux-2.4.3-pre1/drivers/scsi/imm.h linux/drivers/scsi/imm.h
--- linux-2.4.3-pre1/drivers/scsi/imm.h	Mon Dec 11 13:43:20 2000
+++ linux/drivers/scsi/imm.h	Mon Dec  3 22:29:50 2001
@@ -118,7 +118,7 @@
 #define IMM_BURST_SIZE	512	/* data burst size */
 #define IMM_SELECT_TMO  500	/* 500 how long to wait for target ? */
 #define IMM_SPIN_TMO    5000	/* 50000 imm_wait loop limiter */
-#define IMM_DEBUG	0	/* debuging option */
+#define IMM_DEBUG	0	/* debugging option */
 #define IN_EPP_MODE(x) (x == IMM_EPP_8 || x == IMM_EPP_16 || x == IMM_EPP_32)
 
 /* args to imm_connect */
diff -urN linux-2.4.3-pre1/drivers/scsi/in2000.c linux/drivers/scsi/in2000.c
--- linux-2.4.3-pre1/drivers/scsi/in2000.c	Sat Nov 11 19:01:11 2000
+++ linux/drivers/scsi/in2000.c	Mon Dec  3 22:29:50 2001
@@ -681,7 +681,7 @@
          else {
             write1_io(0, IO_FIFO_READ);   /* put fifo in read mode */
             hostdata->fifo = FI_FIFO_READING;
-            cmd->SCp.have_data_in = 0;    /* nothing transfered yet */
+            cmd->SCp.have_data_in = 0;    /* nothing transferred yet */
             }
 
          }
diff -urN linux-2.4.3-pre1/drivers/scsi/ini9100u.c linux/drivers/scsi/ini9100u.c
--- linux-2.4.3-pre1/drivers/scsi/ini9100u.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/ini9100u.c	Mon Dec  3 22:29:50 2001
@@ -34,7 +34,7 @@
  *    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 
+ * the GNU General 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
diff -urN linux-2.4.3-pre1/drivers/scsi/ini9100u.h linux/drivers/scsi/ini9100u.h
--- linux-2.4.3-pre1/drivers/scsi/ini9100u.h	Mon Dec 11 13:19:45 2000
+++ linux/drivers/scsi/ini9100u.h	Mon Dec  3 22:29:50 2001
@@ -33,7 +33,7 @@
  *    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 
+ * the GNU General 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
diff -urN linux-2.4.3-pre1/drivers/scsi/inia100.c linux/drivers/scsi/inia100.c
--- linux-2.4.3-pre1/drivers/scsi/inia100.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/inia100.c	Mon Dec  3 22:29:50 2001
@@ -33,7 +33,7 @@
  *    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 
+ * the GNU General 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
diff -urN linux-2.4.3-pre1/drivers/scsi/inia100.h linux/drivers/scsi/inia100.h
--- linux-2.4.3-pre1/drivers/scsi/inia100.h	Mon Dec 11 13:19:47 2000
+++ linux/drivers/scsi/inia100.h	Mon Dec  3 22:29:50 2001
@@ -33,7 +33,7 @@
  *    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 
+ * the GNU General 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
diff -urN linux-2.4.3-pre1/drivers/scsi/ips.c linux/drivers/scsi/ips.c
--- linux-2.4.3-pre1/drivers/scsi/ips.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/ips.c	Mon Dec  3 22:29:50 2001
@@ -4831,7 +4831,7 @@
       }
 
       if (j >= 45)
-         /* error occured */
+         /* error occurred */
          return (0);
 
       PostByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
@@ -4855,7 +4855,7 @@
       }
 
       if (j >= 240)
-         /* error occured */
+         /* error occurred */
          return (0);
 
       ConfigByte[i] = inb(ha->io_addr + IPS_REG_ISPR);
@@ -4920,7 +4920,7 @@
       }
 
       if (j >= 45)
-         /* error occured */
+         /* error occurred */
          return (0);
 
       PostByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
@@ -4944,7 +4944,7 @@
       }
 
       if (j >= 240)
-         /* error occured */
+         /* error occurred */
          return (0);
 
       ConfigByte[i] = readb(ha->mem_ptr + IPS_REG_ISPR);
@@ -4961,7 +4961,7 @@
    }
 
    if (i >= 240)
-      /* error occured */
+      /* error occurred */
       return (0);
 
    /* setup CCCR */
@@ -5011,7 +5011,7 @@
    }
 
    if (i >= 45) {
-      /* error occured */
+      /* error occurred */
       printk(KERN_WARNING "(%s%d) timeout waiting for post.\n",
              ips_name, ha->host_num);
 
@@ -5042,7 +5042,7 @@
    }
 
    if (i >= 240) {
-      /* error occured */
+      /* error occurred */
       printk(KERN_WARNING "(%s%d) timeout waiting for config.\n",
              ips_name, ha->host_num);
 
@@ -5657,7 +5657,7 @@
       } else if (intr == IPS_INTR_IORL) {
          if (ha->waitflag == FALSE) {
             /*
-             * controller generated an interupt to
+             * controller generated an interrupt to
              * acknowledge completion of the command
              * and ips_intr() has serviced the interrupt.
              */
@@ -5681,7 +5681,7 @@
       } else if (intr == IPS_INTR_HAL) {
          if (ha->waitflag == FALSE) {
             /*
-             * controller generated an interupt to
+             * controller generated an interrupt to
              * acknowledge completion of the command
              * and ips_intr() has serviced the interrupt.
              */
diff -urN linux-2.4.3-pre1/drivers/scsi/mac_esp.c linux/drivers/scsi/mac_esp.c
--- linux-2.4.3-pre1/drivers/scsi/mac_esp.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/mac_esp.c	Mon Dec  3 22:29:50 2001
@@ -67,7 +67,7 @@
 
 volatile unsigned char cmd_buffer[16];
 				/* This is where all commands are put
-				 * before they are transfered to the ESP chip
+				 * before they are transferred to the ESP chip
 				 * via PIO.
 				 */
 
diff -urN linux-2.4.3-pre1/drivers/scsi/pci2220i.c linux/drivers/scsi/pci2220i.c
--- linux-2.4.3-pre1/drivers/scsi/pci2220i.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/pci2220i.c	Mon Dec  3 22:29:50 2001
@@ -2016,7 +2016,7 @@
 	del_timer (&padapter->timer);
 	if ( status )
 		{
-		DEB (printk ("\npci2220i Interupt hanlder return error"));
+		DEB (printk ("\npci2220i Interrupt handler return error"));
 		zl = DecodeError (padapter, status);
 		}
 	else
diff -urN linux-2.4.3-pre1/drivers/scsi/pcmcia/Config.in linux/drivers/scsi/pcmcia/Config.in
--- linux-2.4.3-pre1/drivers/scsi/pcmcia/Config.in	Tue Feb 15 08:53:46 2000
+++ linux/drivers/scsi/pcmcia/Config.in	Mon Dec  3 22:29:50 2001
@@ -10,13 +10,10 @@
    dep_tristate '  Adaptec AHA152X PCMCIA support' CONFIG_PCMCIA_AHA152X m
    dep_tristate '  Qlogic PCMCIA support' CONFIG_PCMCIA_QLOGIC m
    dep_tristate '  Future Domain PCMCIA support' CONFIG_PCMCIA_FDOMAIN m
-   if [ "$CONFIG_CARDBUS" = "y" ]; then
-      dep_tristate '  Adaptec APA1480 CardBus support' CONFIG_PCMCIA_APA1480 m
-   fi
 fi
 
 if [ "$CONFIG_PCMCIA_QLOGIC" = "y" -o "$CONFIG_PCMCIA_AHA152X" = "y" -o \
-     "$CONFIG_PCMCIA_FDOMAIN" = "y" -o "$CONFIG_PCMCIA_APA1480" = "y" ]; then
+     "$CONFIG_PCMCIA_FDOMAIN" = "y" ]; then
    define_bool CONFIG_PCMCIA_SCSICARD y
 fi
 
diff -urN linux-2.4.3-pre1/drivers/scsi/pcmcia/Makefile linux/drivers/scsi/pcmcia/Makefile
--- linux-2.4.3-pre1/drivers/scsi/pcmcia/Makefile	Fri Dec 29 14:07:22 2000
+++ linux/drivers/scsi/pcmcia/Makefile	Mon Dec  3 22:29:50 2001
@@ -12,7 +12,6 @@
 vpath %c ..
 
 CFLAGS_aha152x.o = -DPCMCIA -D__NO_VERSION__ -DAHA152X_STAT
-CFLAGS_aic7xxx.o = -DPCMCIA -D__NO_VERSION__
 CFLAGS_fdomain.o = -DPCMCIA -D__NO_VERSION__
 CFLAGS_qlogicfas.o = -DPCMCIA -D__NO_VERSION__
 
@@ -21,12 +20,8 @@
 obj-$(CONFIG_PCMCIA_FDOMAIN)	+= fdomain_cs.o
 obj-$(CONFIG_PCMCIA_AHA152X)	+= aha152x_cs.o
 
-# Cardbus client drivers
-obj-$(CONFIG_PCMCIA_APA1480)	+= apa1480_cb.o
-
-list-multi	:= qlogic_cs.o fdomain_cs.o aha152x_cs.o apa1480_cb.o
+list-multi	:= qlogic_cs.o fdomain_cs.o aha152x_cs.o
 aha152x-objs	:= aha152x_stub.o aha152x.o
-apa1480-objs	:= apa1480_stub.o aic7xxx.o
 fdomain-objs	:= fdomain_stub.o fdomain.o
 qlogic-objs	:= qlogic_stub.o qlogicfas.o
 
@@ -34,9 +29,6 @@
 
 aha152x_cs.o: $(aha152x-objs)
 	$(LD) -r -o $@ $(aha152x-objs)
-
-apa1480_cb.o: $(apa1480-objs)
-	$(LD) -r -o $@ $(apa1480-objs)
 
 fdomain_cs.o: $(fdomain-objs)
 	$(LD) -r -o $@ $(fdomain-objs)
diff -urN linux-2.4.3-pre1/drivers/scsi/pcmcia/aha152x_stub.c linux/drivers/scsi/pcmcia/aha152x_stub.c
--- linux-2.4.3-pre1/drivers/scsi/pcmcia/aha152x_stub.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/pcmcia/aha152x_stub.c	Mon Dec  3 22:29:50 2001
@@ -22,7 +22,7 @@
     are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
 
     Alternatively, the contents of this file may be used under the
-    terms of the GNU Public License version 2 (the "GPL"), in which
+    terms of the GNU General Public License version 2 (the "GPL"), in which
     case the provisions of the GPL are applicable instead of the
     above.  If you wish to allow the use of your version of this file
     only under the terms of the GPL and not to allow others to use
diff -urN linux-2.4.3-pre1/drivers/scsi/pcmcia/apa1480_stub.c linux/drivers/scsi/pcmcia/apa1480_stub.c
--- linux-2.4.3-pre1/drivers/scsi/pcmcia/apa1480_stub.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/pcmcia/apa1480_stub.c	Wed Dec 31 16:00:00 1969
@@ -1,174 +0,0 @@
-/*======================================================================
-
-    A driver for the Adaptec APA1480 CardBus SCSI Host Adapter
-
-    apa1480_cb.c 1.22 2000/06/12 21:27:25
-
-    The contents of this file are subject to the Mozilla Public
-    License Version 1.1 (the "License"); you may not use this file
-    except in compliance with the License. You may obtain a copy of
-    the License at http://www.mozilla.org/MPL/
-
-    Software distributed under the License is distributed on an "AS
-    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
-    implied. See the License for the specific language governing
-    rights and limitations under the License.
-
-    The initial developer of the original code is David A. Hinds
-    <dahinds@users.sourceforge.net>.  Portions created by David A. Hinds
-    are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
-
-    Alternatively, the contents of this file may be used under the
-    terms of the GNU Public License version 2 (the "GPL"), in which
-    case the provisions of the GPL are applicable instead of the
-    above.  If you wish to allow the use of your version of this file
-    only under the terms of the GPL and not to allow others to use
-    your version of this file under the MPL, indicate your decision
-    by deleting the provisions above and replace them with the notice
-    and other provisions required by the GPL.  If you do not delete
-    the provisions above, a recipient may use your version of this
-    file under either the MPL or the GPL.
-    
-======================================================================*/
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/timer.h>
-#include <linux/ioport.h>
-#include <scsi/scsi.h>
-#include <linux/major.h>
-#include <linux/blk.h>
-
-#include <../drivers/scsi/scsi.h>
-#include <../drivers/scsi/hosts.h>
-#include <scsi/scsi_ioctl.h>
-#include <../drivers/scsi/aic7xxx.h>
-
-#include <pcmcia/driver_ops.h>
-
-#ifdef PCMCIA_DEBUG
-static int pc_debug = PCMCIA_DEBUG;
-MODULE_PARM(pc_debug, "i");
-#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)
-static char *version =
-"apa1480_cb.c 1.22 2000/06/12 21:27:25 (David Hinds)";
-#else
-#define DEBUG(n, args...)
-#endif
-
-/*====================================================================*/
-
-/* Parameters that can be set with 'insmod' */
-
-static int reset = 1;
-static int ultra = 0;
-
-MODULE_PARM(reset, "i");
-MODULE_PARM(ultra, "i");
-
-/*====================================================================*/
-
-static Scsi_Host_Template driver_template = AIC7XXX;
-
-extern void aic7xxx_setup(char *, int *);
-
-static dev_node_t *apa1480_attach(dev_locator_t *loc);
-static void apa1480_detach(dev_node_t *node);
-
-struct driver_operations apa1480_ops = {
-    "apa1480_cb", apa1480_attach, NULL, NULL, apa1480_detach
-};
-
-/*====================================================================*/
-
-static dev_node_t *apa1480_attach(dev_locator_t *loc)
-{
-    u_char bus, devfn;
-    Scsi_Device *dev;
-    dev_node_t *node;
-    char s[60];
-    int n = 0;
-    struct Scsi_Host *host;
-    
-    if (loc->bus != LOC_PCI) return NULL;
-    bus = loc->b.pci.bus; devfn = loc->b.pci.devfn;
-    printk(KERN_INFO "apa1480_attach(bus %d, function %d)\n",
-	   bus, devfn);
-
-    driver_template.module = &__this_module;
-
-    sprintf(s, "no_probe:1,no_reset:%d,ultra:%d",
-	    (reset==0), (ultra!=0));
-    aic7xxx_setup(s, NULL);
-    scsi_register_module(MODULE_SCSI_HA, &driver_template);
-
-    node = kmalloc(7 * sizeof(dev_node_t), GFP_KERNEL);
-    for (host = scsi_hostlist; host; host = host->next)
-	if (host->hostt == &driver_template)
-	    for (dev = host->host_queue; dev; dev = dev->next) {
-	    u_long arg[2], id;
-	    kernel_scsi_ioctl(dev, SCSI_IOCTL_GET_IDLUN, arg);
-	    id = (arg[0]&0x0f) + ((arg[0]>>4)&0xf0) +
-		((arg[0]>>8)&0xf00) + ((arg[0]>>12)&0xf000);
-	    node[n].minor = 0;
-	    switch (dev->type) {
-	    case TYPE_TAPE:
-		node[n].major = SCSI_TAPE_MAJOR;
-		sprintf(node[n].dev_name, "st#%04lx", id);
-		break;
-	    case TYPE_DISK:
-	    case TYPE_MOD:
-		node[n].major = SCSI_DISK0_MAJOR;
-		sprintf(node[n].dev_name, "sd#%04lx", id);
-		break;
-	    case TYPE_ROM:
-	    case TYPE_WORM:
-		node[n].major = SCSI_CDROM_MAJOR;
-		sprintf(node[n].dev_name, "sr#%04lx", id);
-		break;
-	    default:
-		node[n].major = SCSI_GENERIC_MAJOR;
-		sprintf(node[n].dev_name, "sg#%04lx", id);
-		break;
-	    }
-	    if (n) node[n-1].next = &node[n];
-	    n++;
-	}
-    if (n == 0) {
-	printk(KERN_INFO "apa1480_cs: no SCSI devices found\n");
-	scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
-	kfree(node);
-	return NULL;
-    } else
-	node[n-1].next = NULL;
-    
-    MOD_INC_USE_COUNT;
-    return node;
-}
-
-static void apa1480_detach(dev_node_t *node)
-{
-    MOD_DEC_USE_COUNT;
-    scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
-    kfree(node);
-}
-
-/*====================================================================*/
-
-static int __init init_apa1480_cb(void) {
-    DEBUG(0, "%s\n", version);
-    register_driver(&apa1480_ops);
-    return 0;
-}
-
-static void __exit exit_apa1480_cb(void) {
-    DEBUG(0, "apa1480_cs: unloading\n");
-    unregister_driver(&apa1480_ops);
-}
-
-module_init(init_apa1480_cb);
-module_exit(exit_apa1480_cb);
diff -urN linux-2.4.3-pre1/drivers/scsi/pcmcia/fdomain_stub.c linux/drivers/scsi/pcmcia/fdomain_stub.c
--- linux-2.4.3-pre1/drivers/scsi/pcmcia/fdomain_stub.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/pcmcia/fdomain_stub.c	Mon Dec  3 22:29:50 2001
@@ -19,7 +19,7 @@
     are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
 
     Alternatively, the contents of this file may be used under the
-    terms of the GNU Public License version 2 (the "GPL"), in which
+    terms of the GNU General Public License version 2 (the "GPL"), in which
     case the provisions of the GPL are applicable instead of the
     above.  If you wish to allow the use of your version of this file
     only under the terms of the GPL and not to allow others to use
diff -urN linux-2.4.3-pre1/drivers/scsi/pcmcia/qlogic_stub.c linux/drivers/scsi/pcmcia/qlogic_stub.c
--- linux-2.4.3-pre1/drivers/scsi/pcmcia/qlogic_stub.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/pcmcia/qlogic_stub.c	Mon Dec  3 22:29:50 2001
@@ -19,7 +19,7 @@
     are Copyright (C) 1999 David A. Hinds.  All Rights Reserved.
 
     Alternatively, the contents of this file may be used under the
-    terms of the GNU Public License version 2 (the "GPL"), in which
+    terms of the GNU General Public License version 2 (the "GPL"), in which
     case the provisions of the GPL are applicable instead of the
     above.  If you wish to allow the use of your version of this file
     only under the terms of the GPL and not to allow others to use
diff -urN linux-2.4.3-pre1/drivers/scsi/ppa.c linux/drivers/scsi/ppa.c
--- linux-2.4.3-pre1/drivers/scsi/ppa.c	Tue Jan  9 10:40:03 2001
+++ linux/drivers/scsi/ppa.c	Mon Dec  3 22:29:50 2001
@@ -4,7 +4,7 @@
  * (The PPA3 is the embedded controller in the ZIP drive.)
  * 
  * (c) 1995,1996 Grant R. Guenther, grant@torque.net,
- * under the terms of the GNU Public License.
+ * under the terms of the GNU General Public License.
  * 
  * Current Maintainer: David Campbell (Perth, Western Australia, GMT+0800)
  *                     campbell@torque.net
@@ -29,7 +29,7 @@
     int mode;			/* Transfer mode                */
     int host;			/* Host number (for proc)       */
     Scsi_Cmnd *cur_cmd;		/* Current queued command       */
-    struct tq_struct ppa_tq;	/* Polling interupt stuff       */
+    struct tq_struct ppa_tq;	/* Polling interrupt stuff       */
     unsigned long jstart;	/* Jiffies at start             */
     unsigned long recon_tmo;    /* How many usecs to wait for reconnection (6th bit) */
     unsigned int failed:1;	/* Failure flag                 */
diff -urN linux-2.4.3-pre1/drivers/scsi/ppa.h linux/drivers/scsi/ppa.h
--- linux-2.4.3-pre1/drivers/scsi/ppa.h	Thu Jan  4 13:00:55 2001
+++ linux/drivers/scsi/ppa.h	Mon Dec  3 22:29:50 2001
@@ -126,7 +126,7 @@
 #define PPA_SELECT_TMO  5000	/* how long to wait for target ? */
 #define PPA_SPIN_TMO    50000	/* ppa_wait loop limiter */
 #define PPA_RECON_TMO   500	/* scsi reconnection loop limiter */
-#define PPA_DEBUG	0	/* debuging option */
+#define PPA_DEBUG	0	/* debugging option */
 #define IN_EPP_MODE(x) (x == PPA_EPP_8 || x == PPA_EPP_16 || x == PPA_EPP_32)
 
 /* args to ppa_connect */
diff -urN linux-2.4.3-pre1/drivers/scsi/psi_dale.h linux/drivers/scsi/psi_dale.h
--- linux-2.4.3-pre1/drivers/scsi/psi_dale.h	Fri Feb 18 14:50:03 2000
+++ linux/drivers/scsi/psi_dale.h	Mon Dec  3 22:29:50 2001
@@ -527,7 +527,7 @@
 	USHORT	PIOCycleTime		:8;		// 51 Transfer Cycle Timing - PIO
 	USHORT	Reserved6			:8;		// 52                       - DMA
 	USHORT	DMACycleTime		:8;		// 52                       - DMA
-	USHORT	Valid_54_58			:1;		// 53 words 54 - 58 are vaild
+	USHORT	Valid_54_58			:1;		// 53 words 54 - 58 are valid
 	USHORT	Valid_64_70			:1;		// 53 words 64 - 70 are valid
 	USHORT	Reserved7			:14;	// 53
 	USHORT	LogNumCyl;					// 54 Current Translation - Num Cyl
diff -urN linux-2.4.3-pre1/drivers/scsi/scsi_error.c linux/drivers/scsi/scsi_error.c
--- linux-2.4.3-pre1/drivers/scsi/scsi_error.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/scsi_error.c	Mon Dec  3 22:29:50 2001
@@ -492,31 +492,20 @@
 {
 	static unsigned char tur_command[6] =
 	{TEST_UNIT_READY, 0, 0, 0, 0, 0};
-	unsigned char scsi_result0[256], *scsi_result = NULL;
 
 	memcpy((void *) SCpnt->cmnd, (void *) tur_command,
 	       sizeof(tur_command));
 
 	SCpnt->cmnd[1] = SCpnt->lun << 5;
 
-	scsi_result = (!SCpnt->host->hostt->unchecked_isa_dma)
-	    ? &scsi_result0[0] : kmalloc(512, GFP_ATOMIC | GFP_DMA);
-
-	if (scsi_result == NULL) {
-		printk("cannot allocate scsi_result in scsi_test_unit_ready.\n");
-		return FAILED;
-	}
 	/*
-	 * Zero the sense buffer.  Some host adapters automatically always request
-	 * sense, so it is not a good idea that SCpnt->request_buffer and
-	 * SCpnt->sense_buffer point to the same address (DB).
-	 * 0 is not a valid sense code. 
+	 * Zero the sense buffer.  The SCSI spec mandates that any
+	 * untransferred sense data should be interpreted as being zero.
 	 */
 	memset((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
-	memset((void *) scsi_result, 0, 256);
 
-	SCpnt->request_buffer = scsi_result;
-	SCpnt->request_bufflen = 256;
+	SCpnt->request_buffer = NULL;
+	SCpnt->request_bufflen = 0;
 	SCpnt->use_sg = 0;
 	SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
 	SCpnt->underflow = 0;
@@ -524,15 +513,6 @@
 
 	scsi_send_eh_cmnd(SCpnt, SENSE_TIMEOUT);
 
-	/* Last chance to have valid sense data */
-	if (!scsi_sense_valid(SCpnt))
-		memcpy((void *) SCpnt->sense_buffer,
-		       SCpnt->request_buffer,
-		       sizeof(SCpnt->sense_buffer));
-
-	if (scsi_result != &scsi_result0[0] && scsi_result != NULL)
-		kfree(scsi_result);
-
 	/*
 	 * When we eventually call scsi_finish, we really wish to complete
 	 * the original request, so let's restore the original data. (DB)
@@ -549,6 +529,9 @@
 	/*
 	 * Hey, we are done.  Let's look to see what happened.
 	 */
+	SCSI_LOG_ERROR_RECOVERY(3,
+		printk("scsi_test_unit_ready: SCpnt %p eh_state %x\n",
+		SCpnt, SCpnt->eh_state));
 	return SCpnt->eh_state;
 }
 
@@ -671,11 +654,8 @@
 		spin_unlock_irqrestore(&io_request_lock, flags);
 
 		SCpnt->result = temp;
-		if (scsi_eh_completed_normally(SCpnt)) {
-			SCpnt->eh_state = SUCCESS;
-		} else {
-			SCpnt->eh_state = FAILED;
-		}
+		/* Fall through to code below to examine status. */
+		SCpnt->eh_state = SUCCESS;
 	}
 
 	/*
@@ -683,7 +663,10 @@
 	 * did complete normally.
 	 */
 	if (SCpnt->eh_state == SUCCESS) {
-		switch (scsi_eh_completed_normally(SCpnt)) {
+		int ret = scsi_eh_completed_normally(SCpnt);
+		SCSI_LOG_ERROR_RECOVERY(3,
+			printk("scsi_send_eh_cmnd: scsi_eh_completed_normally %x\n", ret));
+		switch (ret) {
 		case SUCCESS:
 			SCpnt->eh_state = SUCCESS;
 			break;
@@ -1104,7 +1087,6 @@
  */
 STATIC int scsi_eh_completed_normally(Scsi_Cmnd * SCpnt)
 {
-	int rtn;
 	/*
 	 * First check the host byte, to see if there is anything in there
 	 * that would indicate what we need to do.
@@ -1144,11 +1126,7 @@
 	case COMMAND_TERMINATED:
 		return SUCCESS;
 	case CHECK_CONDITION:
-		rtn = scsi_check_sense(SCpnt);
-		if (rtn == NEEDS_RETRY) {
-			return FAILED;
-		}
-		return rtn;
+		return scsi_check_sense(SCpnt);
 	case CONDITION_GOOD:
 	case INTERMEDIATE_GOOD:
 	case INTERMEDIATE_C_GOOD:
@@ -1634,8 +1612,10 @@
 						 * FIXME(eric) - is this really the correct thing to do?
 						 */
 						if (rtn != SUCCESS) {
-							SCloop->device->online = FALSE;
-							SCloop->host->host_failed--;
+							printk(KERN_INFO "scsi: device set offline - not ready or command retry failed after bus reset: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun);
+
+							SDloop->online = FALSE;
+							SDloop->host->host_failed--;
 							scsi_eh_finish_command(&SCdone, SCloop);
 						}
 					}
@@ -1725,8 +1705,9 @@
 							}
 						}
 						if (rtn != SUCCESS) {
-							SCloop->device->online = FALSE;
-							SCloop->host->host_failed--;
+							printk(KERN_INFO "scsi: device set offline - not ready or command retry failed after host reset: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun);
+							SDloop->online = FALSE;
+							SDloop->host->host_failed--;
 							scsi_eh_finish_command(&SCdone, SCloop);
 						}
 					}
@@ -1753,7 +1734,11 @@
 	for (SDpnt = host->host_queue; SDpnt; SDpnt = SDpnt->next) {
 		for (SCloop = SDpnt->device_queue; SCloop; SCloop = SCloop->next) {
 			if (SCloop->state == SCSI_STATE_FAILED || SCloop->state == SCSI_STATE_TIMEOUT) {
-				SCloop->device->online = FALSE;
+				SDloop = SCloop->device;
+				if (SDloop->online == TRUE) {
+					printk(KERN_INFO "scsi: device set offline - command error recover failed: host %d channel %d id %d lun %d\n", SDloop->host->host_no, SDloop->channel, SDloop->id, SDloop->lun);
+					SDloop->online = FALSE;
+				}
 
 				/*
 				 * This should pass the failure up to the top level driver, and
@@ -1765,7 +1750,7 @@
 					SCloop->result |= (DRIVER_TIMEOUT << 24);
 				}
 				SCSI_LOG_ERROR_RECOVERY(3, printk("Finishing command for device %d %x\n",
-				    SCloop->device->id, SCloop->result));
+				    SDloop->id, SCloop->result));
 
 				scsi_eh_finish_command(&SCdone, SCloop);
 			}
diff -urN linux-2.4.3-pre1/drivers/scsi/scsi_lib.c linux/drivers/scsi/scsi_lib.c
--- linux-2.4.3-pre1/drivers/scsi/scsi_lib.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/scsi/scsi_lib.c	Mon Dec  3 22:29:50 2001
@@ -549,7 +549,7 @@
 	 *	number of sectors.  If we are done, the command block will
 	 *	be released, and the queue function will be goosed.  If we
 	 *	are not done, then scsi_end_request will directly goose
-	 *	the the queue.
+	 *	the queue.
 	 *
 	 *	We can just use scsi_queue_next_request() here.  This
 	 *	would be used if we just wanted to retry, for example.
diff -urN linux-2.4.3-pre1/drivers/scsi/sun3_scsi.c linux/drivers/scsi/sun3_scsi.c
--- linux-2.4.3-pre1/drivers/scsi/sun3_scsi.c	Fri Dec 29 14:07:22 2000
+++ linux/drivers/scsi/sun3_scsi.c	Mon Dec  3 22:29:50 2001
@@ -34,7 +34,7 @@
 
 
 /*
- * This is from mac_scsi.h, but hey, maybe this is usefull for Sun3 too! :)
+ * This is from mac_scsi.h, but hey, maybe this is useful for Sun3 too! :)
  *
  * Options :
  *
@@ -118,7 +118,7 @@
 /* dvma buffer to allocate -- 32k should hopefully be more than sufficient */
 #define SUN3_DVMA_BUFSIZE 0xe000
 
-/* minimum number of bytes to to dma on */
+/* minimum number of bytes to do dma on */
 #define SUN3_DMA_MINSIZE 128
 
 static volatile unsigned char *sun3_scsi_regp;
diff -urN linux-2.4.3-pre1/drivers/sound/maestro3.c linux/drivers/sound/maestro3.c
--- linux-2.4.3-pre1/drivers/sound/maestro3.c	Fri Feb 16 16:02:37 2001
+++ linux/drivers/sound/maestro3.c	Mon Dec  3 22:29:50 2001
@@ -28,6 +28,9 @@
  * Shouts go out to Mike "DJ XPCom" Ang.
  *
  * History
+ *  v1.22 - Feb 28 2001 - Zach Brown <zab@zabbo.net>
+ *   allocate mem at insmod/setup, rather than open
+ *   limit pci dma addresses to 28bit, thanks guys.
  *  v1.21 - Feb 04 2001 - Zach Brown <zab@zabbo.net>
  *   fix up really dumb notifier -> suspend oops
  *  v1.20 - Jan 30 2001 - Zach Brown <zab@zabbo.net>
@@ -150,7 +153,7 @@
 
 #define M_DEBUG 1
 
-#define DRIVER_VERSION      "1.21"
+#define DRIVER_VERSION      "1.22"
 #define M3_MODULE_NAME      "maestro3"
 #define PFX                 M3_MODULE_NAME ": "
 
@@ -330,6 +333,12 @@
 
 MODULE_DEVICE_TABLE (pci, m3_id_table);
 
+/*
+ * reports seem to indicate that the m3 is limited
+ * to 28bit bus addresses.  aaaargggh...
+ */
+#define M3_PCI_DMA_MASK 0x0fffffff
+
 static unsigned 
 ld2(unsigned int x)
 {
@@ -1943,6 +1952,9 @@
 static void
 free_dmabuf(struct pci_dev *pci_dev, struct dmabuf *db)
 {
+    if(db->rawbuf == NULL)
+        return;
+
     DPRINTK(DPSTR,"freeing %p from dmabuf %p\n",db->rawbuf, db);
 
     {
@@ -1967,7 +1979,7 @@
     int minor = MINOR(inode->i_rdev);
     struct m3_card *c;
     struct m3_state *s = NULL;
-    int i, ret = 0;
+    int i;
     unsigned char fmtm = ~0, fmts = 0;
     unsigned long flags;
 
@@ -2013,10 +2025,6 @@
     spin_lock_irqsave(&s->lock, flags);
 
     if (file->f_mode & FMODE_READ) {
-        if(allocate_dmabuf(s->card->pcidev, &(s->dma_adc)))  {
-            ret = -ENOMEM;
-            goto out;
-        }
         fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_ADC_SHIFT);
         if ((minor & 0xf) == SND_DEV_DSP16)
             fmts |= ESS_FMT_16BIT << ESS_ADC_SHIFT; 
@@ -2025,10 +2033,6 @@
         set_adc_rate(s, 8000);
     }
     if (file->f_mode & FMODE_WRITE) {
-        if(allocate_dmabuf(s->card->pcidev, &(s->dma_dac)))  {
-            ret = -ENOMEM;
-            goto out;
-        }
         fmtm &= ~((ESS_FMT_STEREO | ESS_FMT_16BIT) << ESS_DAC_SHIFT);
         if ((minor & 0xf) == SND_DEV_DSP16)
             fmts |= ESS_FMT_16BIT << ESS_DAC_SHIFT;
@@ -2040,7 +2044,6 @@
     s->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
 
     MOD_INC_USE_COUNT;
-out:
     up(&s->open_sem);
     spin_unlock_irqrestore(&s->lock, flags);
     return 0;
@@ -2064,7 +2067,6 @@
             m3_remove_list(s->card, &(s->card->mixer_list), s->dma_dac.mixer_index);
             nuke_lists(s->card, &(s->dma_dac));
         }
-        free_dmabuf(s->card->pcidev, &(s->dma_dac));
     }
     if (file->f_mode & FMODE_READ) {
         stop_adc(s);
@@ -2072,7 +2074,6 @@
             m3_remove_list(s->card, &(s->card->adc1_list), s->dma_adc.adc1_index);
             nuke_lists(s->card, &(s->dma_adc));
         }
-        free_dmabuf(s->card->pcidev, &(s->dma_adc));
     }
         
     s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
@@ -2594,8 +2595,8 @@
 
     DPRINTK(DPMOD, "in maestro_install\n");
 
-    if (!pci_dma_supported(pci_dev, 0xffffffff)) {
-        printk(KERN_ERR PFX "architecture does not support 32bit PCI busmaster DMA\n");
+    if (!pci_dma_supported(pci_dev, M3_PCI_DMA_MASK)) {
+        printk(KERN_ERR PFX "architecture does not support limiting to 28bit PCI bus addresses\n");
         return -ENODEV;
     }
         
@@ -2604,6 +2605,8 @@
 
     pci_set_master(pci_dev);
 
+    pci_dev->dma_mask = M3_PCI_DMA_MASK;
+
     if( (card = kmalloc(sizeof(struct m3_card), GFP_KERNEL)) == NULL) {
         printk(KERN_WARNING PFX "out of memory\n");
         return -ENOMEM;
@@ -2681,6 +2684,12 @@
         if ((s->dev_audio = register_sound_dsp(&m3_audio_fops, -1)) < 0) {
             break;
         }
+
+        if( allocate_dmabuf(card->pcidev, &(s->dma_adc)) ||
+                allocate_dmabuf(card->pcidev, &(s->dma_dac)))  { 
+            ret = -ENOMEM;
+            goto out;
+        }
     }
     
     if(request_irq(card->irq, m3_interrupt, SA_SHIRQ, card_names[card->card_type], card)) {
@@ -2734,8 +2743,12 @@
         for(i=0;i<NR_DSPS;i++)
         {
             struct m3_state *s = &card->channels[i];
-            if(s->dev_audio != -1)
-                unregister_sound_dsp(s->dev_audio);
+            if(s->dev_audio < 0)
+                continue;
+
+            unregister_sound_dsp(s->dev_audio);
+            free_dmabuf(card->pcidev, &s->dma_adc);
+            free_dmabuf(card->pcidev, &s->dma_dac);
         }
 
         release_region(card->iobase, 256);
diff -urN linux-2.4.3-pre1/drivers/sound/opl3sa2.c linux/drivers/sound/opl3sa2.c
--- linux-2.4.3-pre1/drivers/sound/opl3sa2.c	Fri Feb 16 16:02:37 2001
+++ linux/drivers/sound/opl3sa2.c	Mon Dec  3 22:29:50 2001
@@ -806,6 +806,16 @@
 
 
 #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE
+
+struct isapnp_device_id isapnp_opl3sa2_list[] __initdata = {
+	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+		ISAPNP_VENDOR('Y','M','H'), ISAPNP_FUNCTION(0x0021),
+		NULL },
+	{0}
+};
+
+MODULE_DEVICE_TABLE(isapnp, isapnp_opl3sa2_list);
+
 static int __init opl3sa2_isapnp_probe(struct address_info* hw_cfg,
 				       struct address_info* mss_cfg,
 				       struct address_info* mpu_cfg,
@@ -908,13 +918,17 @@
 						  &cfg_mpu[card],
 						  card) < 0) {
 			if(!opl3sa2_cards_num)
-				printk(KERN_NOTICE "opl3sa2: No cards found\n");
-			break;
+				printk(KERN_INFO "opl3sa2: No PnP cards found\n");
+			if(io == -1)
+				break;
+			isapnp=0;
+			printk(KERN_INFO "opl3sa2: Search for a card at 0x%d.\n", io);
+			/* Fall through */
 		}
 #endif
 		/* If a user wants an I/O then assume they meant it */
 		
-		if(!isapnp && io == -1 ) {
+		if(!isapnp) {
 			if(io == -1 || irq == -1 || dma == -1 ||
 			   dma2 == -1 || mss_io == -1) {
 				printk(KERN_ERR
diff -urN linux-2.4.3-pre1/drivers/sound/skeleton.c linux/drivers/sound/skeleton.c
--- linux-2.4.3-pre1/drivers/sound/skeleton.c	Fri Aug 11 08:26:44 2000
+++ linux/drivers/sound/skeleton.c	Mon Dec  3 22:29:50 2001
@@ -4,7 +4,7 @@
  *	(c) 1998 Red Hat Software
  *
  *	This software may be used and distributed according to the 
- *	terms of the GNU Public License, incorporated herein by 
+ *	terms of the GNU General Public License, incorporated herein by 
  *	reference.
  *
  *	This example is designed to be built in the linux/drivers/sound
diff -urN linux-2.4.3-pre1/drivers/sound/trident.c linux/drivers/sound/trident.c
--- linux-2.4.3-pre1/drivers/sound/trident.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/sound/trident.c	Mon Dec  3 22:29:50 2001
@@ -13,6 +13,7 @@
  *	Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
  *	Ollie Lho <ollie@sis.com.tw> SiS 7018 Audio Core Support
  *	Ching-Ling Lee <cling-li@ali.com.tw> ALi 5451 Audio Core Support 
+ *	Matt Wu <mattwu@acersoftech.com.cn> ALi 5451 Audio Core Support
  *
  *
  *	This program is free software; you can redistribute it and/or modify
@@ -30,6 +31,12 @@
  *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  *
  *  History
+ *  v0.14.7
+ *	Feb 06 2001 Matt Wu
+ *	Fix ac97 initialization
+ *	Fix bug: an extra tail will be played when playing
+ *	Jan 05 2001 Matt Wu
+ *	Implement multi-channels and S/PDIF in support for ALi 1535+
  *  v0.14.6 
  *	Nov 1 2000 Ching-Ling Lee
  *	Fix the bug of memory leak when swithing 5.1-channels to 2 channels.
@@ -140,12 +147,12 @@
 
 /* maxinum nuber of AC97 codecs connected, AC97 2.0 defined 4, but 7018 and 4D-NX only
    have 2 SDATA_IN lines (currently) */
-#define NR_AC97		2
+#define NR_AC97		2	
 
 /* minor number of /dev/swmodem (temporary, experimental) */
 #define SND_DEV_SWMODEM	7
 
-static const unsigned ali_multi_channels_5_1[] = { ALI_CENTER_CHANNEL, ALI_LEF_CHANNEL, ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL};
+static const unsigned ali_multi_channels_5_1[] = { /*ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL,*/ ALI_CENTER_CHANNEL, ALI_LEF_CHANNEL, ALI_SURR_LEFT_CHANNEL, ALI_SURR_RIGHT_CHANNEL};
 
 static const unsigned sample_size[] = { 1, 2, 2, 4 };
 static const unsigned sample_shift[] = { 0, 1, 1, 2 };
@@ -312,6 +319,10 @@
 	struct trident_channel *(*alloc_rec_pcm_channel)(struct trident_card *);
 	void (*free_pcm_channel)(struct trident_card *, unsigned int chan);
 	void (*address_interrupt)(struct trident_card *);
+	
+	/* Add by Matt Wu 01-05-2001 for spdif in */
+	int multi_channel_use_count;
+	int rec_channel_use_count;
 };
 
 /* table to map from CHANNELMASK to channel attribute for SiS 7018 */
@@ -327,6 +338,11 @@
 	DSP_BIND_I2S, DSP_BIND_CENTER_LFE, DSP_BIND_SURR, DSP_BIND_SPDIF
 };
 
+/* Add by Matt Wu 01-05-2001 for spdif in */
+static int ali_close_multi_channels(void);
+static void ali_delay(struct trident_card *card,int interval);
+static void ali_detect_spdif_rate(struct trident_card *card);
+
 static struct trident_card *devs;
 
 static void trident_ac97_set(struct ac97_codec *codec, u8 reg, u16 val);
@@ -760,6 +776,8 @@
 static void trident_rec_setup(struct trident_state *state)
 {
 	u16 w;
+	u8  bval;
+
 	struct trident_card *card = state->card;
 	struct dmabuf *dmabuf = &state->dmabuf;
 	struct trident_channel *channel = dmabuf->channel;
@@ -794,6 +812,18 @@
 	channel->delta = compute_rate_rec(dmabuf->rate);
 	if ((card->pci_id == PCI_DEVICE_ID_ALI_5451) && (channel->num == ALI_SPDIF_IN_CHANNEL)) {
 		rate = ali_get_spdif_in_rate(card);
+		if (rate == 0)
+		{
+			printk(KERN_WARNING "trident: ALi 5451 S/PDIF input setup error!\n");
+			rate = 48000;
+		}
+		bval = inb(TRID_REG(card,ALI_SPDIF_CTRL));
+		if (bval & 0x10)
+		{
+			outb(bval,TRID_REG(card,ALI_SPDIF_CTRL));
+			printk(KERN_WARNING "trident: cleared ALi 5451 S/PDIF parity error flag.\n");
+		}
+
 		if (rate != 48000)
 			channel->delta = ((rate << 12) / dmabuf->rate) & 0x0000ffff;
 	}
@@ -959,7 +989,7 @@
 static int alloc_dmabuf(struct trident_state *state)
 {
 	struct dmabuf *dmabuf = &state->dmabuf;
-	void *rawbuf;
+	void *rawbuf=NULL;
 	int order;
 	struct page *page, *pend;
 
@@ -1138,11 +1168,13 @@
 		len = dmabuf->dmasize - swptr;
 
 	memset(dmabuf->rawbuf + swptr, silence, len);
-
-	spin_lock_irqsave(&state->card->lock, flags);
-	dmabuf->swptr += len;
-	dmabuf->count += len;
-	spin_unlock_irqrestore(&state->card->lock, flags);
+	if(state->card->pci_id != PCI_DEVICE_ID_ALI_5451)
+	{
+		spin_lock_irqsave(&state->card->lock, flags);
+		dmabuf->swptr += len;
+		dmabuf->count += len;
+		spin_unlock_irqrestore(&state->card->lock, flags);
+	}
 
 	/* restart the dma machine in case it is halted */
 	start_dac(state);
@@ -1155,6 +1187,7 @@
 	unsigned long flags;
 	unsigned long tmo;
 	int count;
+	unsigned long diff = 0;
 
 	if (dmabuf->mapped || !dmabuf->ready)
 		return 0;
@@ -1183,10 +1216,19 @@
 
 		/* No matter how much data left in the buffer, we have to wait untill
 		   CSO == ESO/2 or CSO == ESO when address engine interrupts */
-		tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
+	 	if (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)
+		{	
+			diff = dmabuf->swptr - trident_get_dma_addr(state) + dmabuf->dmasize ;
+			diff = diff % (dmabuf->dmasize);
+			tmo  = (diff * HZ) / dmabuf->rate;
+		}
+		else
+		{
+			tmo = (dmabuf->dmasize * HZ) / dmabuf->rate;
+		}
 		tmo >>= sample_shift[dmabuf->fmt];
+//		printk("trident: diff=%d count= %d/%d total=%d tmo=%d hwptr=%d swptr=%d curptr=%d\n",diff,dmabuf->count,dmabuf->dmasize,dmabuf->total_bytes,tmo,dmabuf->hwptr,dmabuf->swptr,trident_get_dma_addr(state));
 		if (!schedule_timeout(tmo ? tmo : 1) && tmo){
-			printk(KERN_ERR "trident: drain_dac, dma timeout?\n");
 			break;
 		}
 	}
@@ -1412,7 +1454,7 @@
 				if (!ret) ret = -EAGAIN;
 				return ret;
 			}
-			/* No matter how much space left in the buffer, we have to wait untill
+			/* No matter how much space left in the buffer, we have to wait until
 			   CSO == ESO/2 or CSO == ESO when address engine interrupts */
 			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
 			tmo >>= sample_shift[dmabuf->fmt];
@@ -1430,7 +1472,7 @@
 				       dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
 				       dmabuf->hwptr, dmabuf->swptr);
 #endif
-				/* a buffer overrun, we delay the recovery untill next time the
+				/* a buffer overrun, we delay the recovery until next time the
 				   while loop begin and we REALLY have space to record */
 			}
 			if (signal_pending(current)) {
@@ -1512,7 +1554,7 @@
 				if (!ret) ret = -EAGAIN;
 				return ret;
 			}
-			/* No matter how much data left in the buffer, we have to wait untill
+			/* No matter how much data left in the buffer, we have to wait until
 			   CSO == ESO/2 or CSO == ESO when address engine interrupts */
 			lock_set_fmt(state);
 			tmo = (dmabuf->dmasize * HZ) / (dmabuf->rate * 2);
@@ -1532,7 +1574,7 @@
 				       dmabuf->dmasize, dmabuf->fragsize, dmabuf->count,
 				       dmabuf->hwptr, dmabuf->swptr);
 #endif
-				/* a buffer underrun, we delay the recovery untill next time the
+				/* a buffer underrun, we delay the recovery until next time the
 				   while loop begin and we REALLY have data to play */
 			}
 			if (signal_pending(current)) {
@@ -1671,6 +1713,8 @@
 	count_info cinfo;
 	int val, mapped, ret;
 
+	struct trident_card *card = state->card;
+
 	VALIDATE_STATE(state);
 	mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) ||
 		((file->f_mode & FMODE_READ) && dmabuf->mapped);
@@ -1807,17 +1851,32 @@
 					state->chans_num = 1;
 				}
 				
-				if (val >= 2) {
+				if (val >= 2)
+				{
+
 					dmabuf->fmt |= TRIDENT_FMT_STEREO;
 					if ((val == 6) && (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)) {
-						ali_setup_multi_channels(state->card, 6);
+
+						if( card->rec_channel_use_count > 0 )
+						{
+							printk("Err: Record is working on the card!\n");
+							return -EBUSY;
+						}
+
+						ret = ali_setup_multi_channels(state->card, 6);
+						if (ret < 0) {
+							unlock_set_fmt(state);
+							return ret;
+						}
 						down(&state->card->open_sem);
 						ret = ali_allocate_other_states_resources(state, 6);
-						up(&state->card->open_sem);
 						if (ret < 0) {
+							up(&state->card->open_sem);
 							unlock_set_fmt(state);
 							return ret;
 						}
+						state->card->multi_channel_use_count ++;
+						up(&state->card->open_sem);
 					}
 					else val = 2;	/*yield to 2-channels*/
 				}
@@ -2031,10 +2090,20 @@
 	struct trident_card *card = devs;
 	struct trident_state *state = NULL;
 	struct dmabuf *dmabuf = NULL;
-
+	
 	/* find an avaiable virtual channel (instance of /dev/dsp) */
 	while (card != NULL) {
 		down(&card->open_sem);
+		if(file->f_mode & FMODE_READ)
+		{
+			/* Skip opens on cards that are in 6 channel mode */
+			if (card->multi_channel_use_count > 0)
+			{
+				up(&card->open_sem);
+				card = card->next;
+				continue;
+			}
+		}
 		for (i = 0; i < NR_HW_CH; i++) {
 			if (card->states[i] == NULL) {
 				state = card->states[i] = (struct trident_state *)
@@ -2107,6 +2176,7 @@
 				(CHANNEL_REC|PCM_LR|MONO_MIX);
 		}
 		trident_set_adc_rate(state, 8000);
+		card->rec_channel_use_count ++;
 	}
 
 	state->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE);
@@ -2143,16 +2213,29 @@
 	if (file->f_mode & FMODE_WRITE) {
 		stop_dac(state);
 		lock_set_fmt(state);
-		if (state->chans_num > 2)
-			ali_free_other_states_resources(state);
+
 		unlock_set_fmt(state);
 		dealloc_dmabuf(state);
 		state->card->free_pcm_channel(state->card, dmabuf->channel->num);
+
+		if (state->chans_num > 2)
+		{
+			if( card->multi_channel_use_count-- < 0 )
+				card->multi_channel_use_count = 0;
+
+			if (card->multi_channel_use_count == 0)
+				ali_close_multi_channels();
+
+			ali_free_other_states_resources(state);
+		}
 	}
 	if (file->f_mode & FMODE_READ) {
 		stop_adc(state);
 		dealloc_dmabuf(state);
 		state->card->free_pcm_channel(state->card, dmabuf->channel->num);
+
+		if( card->rec_channel_use_count-- < 0 )
+			card->rec_channel_use_count = 0;
 	}
 
 	card->states[state->virt] = NULL;
@@ -2295,6 +2378,9 @@
 
 	data = ((u32) val) << 16;
 
+	if(!card)
+		BUG();
+		
 	address = ALI_AC97_WRITE;
 	mask = ALI_AC97_WRITE_ACTION | ALI_AC97_AUDIO_BUSY;
 	if (codec->id)
@@ -2391,11 +2477,29 @@
 flag:	ALI_SPDIF_OUT_TO_SPDIF_OUT
 	ALI_PCM_TO_SPDIF_OUT
 */
+
 static void ali_setup_spdif_out(struct trident_card *card, int flag)
 {
 	unsigned long spdif;
 	unsigned char ch;
 
+        char temp;
+        struct pci_dev *pci_dev = NULL;
+
+        pci_dev = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev);
+        if (pci_dev == NULL)
+                return;
+        pci_read_config_byte(pci_dev, 0x61, &temp);
+        temp |= 0x40;
+        pci_write_config_byte(pci_dev, 0x61, temp);
+        pci_read_config_byte(pci_dev, 0x7d, &temp);
+        temp |= 0x01;
+        pci_write_config_byte(pci_dev, 0x7d, temp);
+        pci_read_config_byte(pci_dev, 0x7e, &temp);
+        temp &= (~0x20);
+        temp |= 0x10;
+        pci_write_config_byte(pci_dev, 0x7e, temp);
+
 	ch = inb(TRID_REG(card, ALI_SCTRL));
 	outb(ch | ALI_SPDIF_OUT_ENABLE, TRID_REG(card, ALI_SCTRL));
 	ch = inb(TRID_REG(card, ALI_SPDIF_CTRL));
@@ -2448,112 +2552,207 @@
 	spdif |= ALI_SPDIF_IN_SUPPORT;
 	outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
 
-	spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL));
-	spdif |= ALI_SPDIF_IN_CH_STATUS;
-	outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL));
-
 	//Set SPDIF IN Rec
 	spdif = inl(TRID_REG(card, ALI_GLOBAL_CONTROL));
 	spdif |= ALI_SPDIF_IN_CH_ENABLE;
 	outl(spdif, TRID_REG(card, ALI_GLOBAL_CONTROL));
 
 	spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL));
+	spdif |= ALI_SPDIF_IN_CH_STATUS;
+	outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL));
+/*
+	spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL));
 	spdif |= ALI_SPDIF_IN_FUNC_ENABLE;
 	outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL));
+*/
 }
 
-static unsigned int ali_get_spdif_in_rate(struct trident_card *card)
+static void ali_delay(struct trident_card *card,int interval)
+{
+	unsigned long  begintimer,currenttimer;
+
+	begintimer   = inl(TRID_REG(card,  ALI_STIMER));
+	currenttimer = inl(TRID_REG(card,  ALI_STIMER));
+
+	while (currenttimer < begintimer + interval)
+		currenttimer = inl(TRID_REG(card,  ALI_STIMER));
+}
+
+static void ali_detect_spdif_rate(struct trident_card *card)
 {
-	unsigned long spdif, time1, time2;
-	unsigned count1, count2, count3;
-	unsigned char R1, R2 = 0;	
-	
-	outb(0xAA, TRID_REG(card, ALI_SPDIF_CTRL + 1));
-	count1 = 0xFFFF;
-	while(--count1)
+	u16 wval  = 0;
+	u16 count = 0;
+	u8  bval = 0, R1 = 0, R2 = 0;
+
+	bval  = inb(TRID_REG(card,ALI_SPDIF_CTRL));
+	bval |= 0x02;
+	outb(bval,TRID_REG(card,ALI_SPDIF_CTRL));
+
+	bval  = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1));
+	bval |= 0x1F;
+	outb(bval,TRID_REG(card,ALI_SPDIF_CTRL + 1));
+
+	while (((R1 < 0x0B )||(R1 > 0x0E)) && (R1 != 0x12) && count <= 50000)
 	{
-		count2 = 0xffff;
-		do{
-			count3 = 0xffff;
-			time1 = inl(TRID_REG(card, ALI_STIMER));
-
-			do{
-				time2 = inl(TRID_REG(card, ALI_STIMER));
-			}while((count3--) && (time2 <= (time1 + 5)));
-			if (!count3) {
-				printk("ali: STimer is stopped! Error!\n");
-				return FALSE;
-			}			
-			R1 = inb(TRID_REG(card, ALI_SPDIF_CTRL + 1));
-		}while((count2--) && (!((R1 == 0x0B)||(R1 == 0x0C)||(R1 == 0x0D)||(R1 == 0x0E)||(R1 == 0x12))));
-		if (!count2)
-			continue;
+		count ++;
 
-		count2 = 0xffff;
-		time1 = inl(TRID_REG(card, ALI_STIMER));
-		do{
-			time2 = inl(TRID_REG(card, ALI_STIMER));
-		}while((count2--) && (time2 <= (time1 + 5)));
-		if (!count2)
-			continue;
+		ali_delay(card, 6);
 
-		R2 = inb(TRID_REG(card, ALI_SPDIF_CTRL + 1));
-		count2 = 0xffff;
-		while((--count2) && (R2 != R1))
-		{
+		bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1));
+		R1 = bval & 0x1F;
+	}
+
+	if (count > 50000)
+	{
+		printk(KERN_WARNING "trident: Error in ali_detect_spdif_rate!\n");
+		return;
+	}
+
+	count = 0;
+
+	while (count <= 50000)
+	{
+		count ++;
+
+		ali_delay(card, 6);
+
+		bval = inb(TRID_REG(card,ALI_SPDIF_CTRL + 1));
+		R2 = bval & 0x1F;
+
+		if(R2 != R1)
 			R1 = R2;
-			count3 = 0xffff;
-			time1 = inl(TRID_REG(card, ALI_STIMER));
-			do{
-				time2 = inl(TRID_REG(card, ALI_STIMER));
-			}while((count3--) && (time2 <= (time1 + 5)));
-			if (!count3) {
-				printk("ali: STimer is stopped! Error!\n");
-				return FALSE;
-			}
-			R2 = inb(TRID_REG(card, ALI_SPDIF_CTRL + 1));
-		}
-		if(R2 == R1)
+		else
 			break;
 	}
 
-	if(!count1) {
-		printk("ali: Can not Detect the sample rate from SPDIF IN!\n");
-		return FALSE;
+	if (count > 50000)
+	{
+		printk(KERN_WARNING "trident: Error in ali_detect_spdif_rate!\n");
+		return;
 	}
 
-	spdif = inb(TRID_REG(card, ALI_SPDIF_CTRL)) | ALI_SPDIF_IN_CH_STATUS;
-	outb(spdif, TRID_REG(card, ALI_SPDIF_CTRL));
+	switch (R2)
+	{
+	case 0x0b:
+	case 0x0c:
+	case 0x0d:
+	case 0x0e:
+		wval  = inw(TRID_REG(card,ALI_SPDIF_CTRL + 2));
+		wval &= 0xE0F0;
+		wval |= (u16)0x09 << 8 | (u16)0x05;
+		outw(wval,TRID_REG(card,ALI_SPDIF_CTRL + 2));
+
+		bval  = inb(TRID_REG(card,ALI_SPDIF_CS +3)) & 0xF0;
+		outb(bval|0x02,TRID_REG(card,ALI_SPDIF_CS + 3));
+		break;
 
-	/* SPDIF only supprts 48k, 44.1k, 32k */
-	switch(R2) {
 	case 0x12:
-		outw(0x0E08, TRID_REG(card, ALI_SPDIF_CTRL + 2));
-		return 32000;
-	case 0x0B:
-	case 0x0C:
-	case 0x0D:
-	case 0x0E:
+		wval  = inw(TRID_REG(card,ALI_SPDIF_CTRL + 2));
+		wval &= 0xE0F0;
+		wval |= (u16)0x0E << 8 | (u16)0x08;
+		outw(wval,TRID_REG(card,ALI_SPDIF_CTRL + 2));
+
+		bval  = inb(TRID_REG(card,ALI_SPDIF_CS +3)) & 0xF0;
+		outb(bval|0x03,TRID_REG(card,ALI_SPDIF_CS + 3));
+		break;
+
 	default:
-		outw(0x0905, TRID_REG(card, ALI_SPDIF_CTRL + 2));
 		break;
 	}
+
+}
+
+static unsigned int ali_get_spdif_in_rate(struct trident_card *card)
+{
+	u32	dwRate = 0;
+	u8	bval = 0;
+
+	ali_detect_spdif_rate(card);
+
+	bval  = inb(TRID_REG(card,ALI_SPDIF_CTRL));
+	bval &= 0x7F;
+	bval |= 0x40;
+	outb(bval,TRID_REG(card,ALI_SPDIF_CTRL));
+
+	bval  = inb(TRID_REG(card,ALI_SPDIF_CS + 3));
+	bval &= 0x0F;
+
+	switch (bval)
+	{
+	case 0:
+		dwRate = 44100;
+		break;
+	case 1:
+		dwRate = 48000;
+		break;
+	case 2:
+		dwRate = 32000;
+		break;
+	default:
+		// Error occurs
+		break;
+	}
+
+	return dwRate;
 	
-	spdif = inb(TRID_REG(card, ALI_SPDIF_CS + 3)) & 0xf;
-	if (spdif == 0)
-		return 44100;
-	else	return 48000;
+}
+
+static int ali_close_multi_channels(void)
+{
+	char temp = 0;
+	struct pci_dev *pci_dev = NULL;
+
+        pci_dev = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev);
+        if (pci_dev == NULL)
+                return -1;
+	temp = 0x80;
+	pci_write_config_byte(pci_dev, 0x59, ~temp);
+	
+	pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, pci_dev);
+	if (pci_dev == NULL)
+                return -1;
+
+	temp = 0x20;
+	pci_write_config_byte(pci_dev, 0xB8, ~temp);
+
+	return 0;
 }
 
 static int ali_setup_multi_channels(struct trident_card *card, int chan_nums)
 {
 	unsigned long dwValue;
-	
+	char temp = 0;
+	struct pci_dev *pci_dev = NULL;
+
+	pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev);
+	if (pci_dev == NULL)
+                return -1;
+	temp = 0x80;
+	pci_write_config_byte(pci_dev, 0x59, temp);
+	
+	pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, pci_dev);
+ 	if (pci_dev == NULL)
+                return -1;
+	temp = 0x20;
+	pci_write_config_byte(pci_dev, (int)0xB8,(u8) temp);
 	if (chan_nums == 6) {
 		dwValue = inl(TRID_REG(card, ALI_SCTRL)) | 0x000f0000;
 		outl(dwValue, TRID_REG(card, ALI_SCTRL));
+		mdelay(4);
+		dwValue = inl(TRID_REG(card, ALI_SCTRL));
+		if (dwValue & 0x2000000) {
+			ali_ac97_set(card->ac97_codec[0], 0x02, 8080);
+			ali_ac97_set(card->ac97_codec[0], 0x36, 0);
+			ali_ac97_set(card->ac97_codec[0], 0x38, 0);
+			ali_ac97_set(card->ac97_codec[1], 0x36, 0);
+			ali_ac97_set(card->ac97_codec[1], 0x38, 0);
+			ali_ac97_set(card->ac97_codec[1], 0x02, 0);
+			ali_ac97_set(card->ac97_codec[1], 0x18, 0x0808);
+			ali_ac97_set(card->ac97_codec[1], 0x74, 0x3);
+			return 1;
+		}
 	}
-	return 1;
+	return -EINVAL;
 }
 
 static void ali_free_pcm_channel(struct trident_card *card, unsigned int channel)
@@ -2746,7 +2945,7 @@
 	}
 
 	/* no more free channels avaliable */
-	printk(KERN_ERR "ali: no more channels available on Bank A.\n");
+//	printk(KERN_ERR "ali: no more channels available on Bank A.\n");
 	return NULL;
 }
 
@@ -2769,7 +2968,7 @@
 	}
 	
 	/* no free recordable channels avaliable */
-	printk(KERN_ERR "ali: no recordable channels available on Bank A.\n");
+//	printk(KERN_ERR "ali: no recordable channels available on Bank A.\n");
 	return NULL;
 }
 
@@ -2778,9 +2977,6 @@
 	unsigned char ch_st_sel;
 	unsigned short status_rate;
 	
-#ifdef DEBUG
-	printk("ali: spdif out rate =%d\n", rate);
-#endif
 	switch(rate) {
 	case 44100:
 		status_rate = 0;
@@ -2803,9 +2999,6 @@
 	ch_st_sel &= (~0x80);	//select left
 	outb(ch_st_sel, TRID_REG(card, ALI_SPDIF_CTRL));
 	outw(status_rate | 0x10, TRID_REG(card, ALI_SPDIF_CS + 2));
-#ifdef DEBUG
-	printk("ali: SPDIF_CS=%lxh\n", inl(TRID_REG(card, ALI_SPDIF_CS)));
-#endif
 }
 
 static void ali_address_interrupt(struct trident_card *card)
@@ -2822,6 +3015,7 @@
 		if ((channel_mask = 1 << channel) & mask) {
 			mask &= ~channel_mask;
 			trident_ack_channel_interrupt(card, channel);
+			udelay(100);
 			state->dmabuf.update_flag |= ALI_ADDRESS_INT_UPDATE;
 			trident_update_ptr(state);
 		}
@@ -2986,7 +3180,7 @@
 /* OSS /dev/mixer file operation methods */
 static int trident_open_mixdev(struct inode *inode, struct file *file)
 {
-	int i;
+	int i = 0;
 	int minor = MINOR(inode->i_rdev);
 	struct trident_card *card = devs;
 
@@ -3033,10 +3227,17 @@
 	switch (card->pci_id)
 	{
 	case PCI_DEVICE_ID_ALI_5451:
+		outl(0x80000000,TRID_REG(card, ALI_GLOBAL_CONTROL));
+		outl(0x00000000,TRID_REG(card, 0xa4));
+		outl(0xffffffff,TRID_REG(card, 0x98));
+		outl(0x00000000,TRID_REG(card, 0xa8));
+		outb(0x10, 	TRID_REG(card, 0x22));
 		ready_2nd = inl(TRID_REG(card, ALI_SCTRL));
-		outl(ready_2nd | PCMOUT | SECONDARY_ID, TRID_REG(card, ALI_SCTRL));
+		ready_2nd &= 0x3fff;
+		outl(ready_2nd | PCMOUT | 0x8000, TRID_REG(card, ALI_SCTRL));
 		ready_2nd = inl(TRID_REG(card, ALI_SCTRL)); 
 		ready_2nd &= SI_AC97_SECONDARY_READY;
+//		printk("codec 2 ready flag= %lx\n", ready_2nd);
 		break;
 	case PCI_DEVICE_ID_SI_7018:
 		/* disable AC97 GPIO interrupt */
@@ -3097,11 +3298,11 @@
 			return num_ac97+1;
 	}
 
-	return num_ac97;
+	return 1/*num_ac97*/;
 }
 
 /* install the driver, we do not allocate hardware channel nor DMA buffer now, they are defered 
-   untill "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */
+   until "ACCESS" time (in prog_dmabuf called by open/read/write/ioctl/mmap) */
 static int __init trident_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)
 {
 	unsigned long iobase;
@@ -3167,6 +3368,10 @@
 		
 		card->address_interrupt = ali_address_interrupt;
 
+		/* Added by Matt Wu 01-05-2001 for spdif in */
+		card->multi_channel_use_count = 0;
+		card->rec_channel_use_count = 0;
+
 		/* ALi SPDIF OUT function */
 		if(card->revision == ALI_5451_V02) {
 			ali_setup_spdif_out(card, ALI_PCM_TO_SPDIF_OUT);		
@@ -3203,7 +3408,7 @@
 		kfree(card);
 		return -ENODEV;
 	}
-	/* initilize AC97 codec and register /dev/mixer */
+	/* initialize AC97 codec and register /dev/mixer */
 	if (trident_ac97_init(card) <= 0) {
 		unregister_sound_dsp(card->dev_audio);
 		release_region(iobase, 256);
diff -urN linux-2.4.3-pre1/drivers/telephony/ixj.c linux/drivers/telephony/ixj.c
--- linux-2.4.3-pre1/drivers/telephony/ixj.c	Fri Feb 16 16:02:37 2001
+++ linux/drivers/telephony/ixj.c	Mon Dec  3 22:29:50 2001
@@ -6300,6 +6300,7 @@
 {
 	return 0;
 }
+#endif /* CONFIG_PCMCIA */
 
 #if defined(CONFIG_ISAPNP)
 extern __inline__ int ixj_probe_isa(int *cnt)
@@ -6448,7 +6449,6 @@
 	return 0;
 }
 #endif /* CONFIG_ISAPNP */
-#endif /* CONFIG_PCMCIA */
 
 #if defined(CONFIG_PCI)
 int __init ixj_probe_pci(int *cnt)
diff -urN linux-2.4.3-pre1/drivers/usb/inode.c linux/drivers/usb/inode.c
--- linux-2.4.3-pre1/drivers/usb/inode.c	Thu Sep  7 08:38:06 2000
+++ linux/drivers/usb/inode.c	Mon Dec  3 22:29:50 2001
@@ -596,7 +596,7 @@
 	return NULL;
 }
 
-static DECLARE_FSTYPE(usbdevice_fs_type, "usbdevfs", usbdevfs_read_super, 0);
+static DECLARE_FSTYPE(usbdevice_fs_type, "usbdevfs", usbdevfs_read_super, FS_SINGLE);
 
 /* --------------------------------------------------------------------- */
 
@@ -691,6 +691,7 @@
 		return ret;
 	if ((ret = register_filesystem(&usbdevice_fs_type)))
 		usb_deregister(&usbdevfs_driver);
+	kern_mount(&usbdevice_fs_type);
 #ifdef CONFIG_PROC_FS		
 	/* create mount point for usbdevfs */
 	usbdir = proc_mkdir("usb", proc_bus);
@@ -702,6 +703,7 @@
 {
 	usb_deregister(&usbdevfs_driver);
 	unregister_filesystem(&usbdevice_fs_type);
+	kern_umount(usbdevice_fs_type.kern_mnt);
 #ifdef CONFIG_PROC_FS	
         if (usbdir)
                 remove_proc_entry("usb", proc_bus);
diff -urN linux-2.4.3-pre1/drivers/usb/printer.c linux/drivers/usb/printer.c
--- linux-2.4.3-pre1/drivers/usb/printer.c	Tue Feb 13 16:40:24 2001
+++ linux/drivers/usb/printer.c	Mon Dec  3 22:29:50 2001
@@ -1,5 +1,5 @@
 /*
- * printer.c  Version 0.6
+ * printer.c  Version 0.8
  *
  * Copyright (c) 1999 Michael Gee	<michael@linuxspecific.com>
  * Copyright (c) 1999 Pavel Machek	<pavel@suse.cz>
@@ -17,7 +17,8 @@
  *	v0.4 - fixes in unidirectional mode
  *	v0.5 - add DEVICE_ID string support
  *	v0.6 - never time out
- *	v0.? - fixed bulk-IN read and poll (David Paschal, paschal@rcsis.com)
+ *	v0.7 - fixed bulk-IN read and poll (David Paschal, paschal@rcsis.com)
+ *	v0.8 - add devfs support
  */
 
 /*
@@ -45,6 +46,7 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/lp.h>
+#include <linux/devfs_fs_kernel.h>
 #undef DEBUG
 #include <linux/usb.h>
 
@@ -78,6 +80,7 @@
 
 struct usblp {
 	struct usb_device 	*dev;			/* USB device */
+	devfs_handle_t		devfs;			/* devfs device */
 	struct urb		readurb, writeurb;	/* The urbs */
 	wait_queue_head_t	wait;			/* Zzzzz ... */
 	int			readcount;		/* Counter for reads */
@@ -90,6 +93,8 @@
 							/* first 2 bytes are (big-endian) length */
 };
 
+extern devfs_handle_t usb_devfs_handle;			/* /dev/usb dir. */
+
 static struct usblp *usblp_table[USBLP_MINORS];
 
 /* Quirks: various printer quirks are handled by this table & its flags. */
@@ -245,6 +250,7 @@
 		return 0;
 	}
 
+	devfs_unregister(usblp->devfs);
 	usblp_table[usblp->minor] = NULL;
 	kfree(usblp->device_id_string);
 	kfree(usblp);
@@ -454,6 +460,16 @@
 	return 0;
 }
 
+static struct file_operations usblp_fops = {
+	owner:		THIS_MODULE,
+	read:		usblp_read,
+	write:		usblp_write,
+	poll:		usblp_poll,
+	ioctl:		usblp_ioctl,
+	open:		usblp_open,
+	release:	usblp_release,
+};
+
 static void *usblp_probe(struct usb_device *dev, unsigned int ifnum,
 			 const struct usb_device_id *id)
 {
@@ -464,6 +480,7 @@
 	int alts = dev->actconfig->interface[ifnum].act_altsetting;
 	int length, err;
 	char *buf;
+	char name[6];
 
 	/* If a bidirectional interface exists, use it. */
 	for (i = 0; i < dev->actconfig->interface[ifnum].num_altsetting; i++) {
@@ -573,6 +590,17 @@
 	usblp_check_status(usblp, 0);
 #endif
 
+	sprintf(name, "lp%d", minor);
+	
+	/* Create with perms=664 */
+	usblp->devfs = devfs_register(usb_devfs_handle, name,
+				      DEVFS_FL_DEFAULT, USB_MAJOR,
+				      USBLP_MINOR_BASE + minor,
+				      S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP |
+				      S_IWGRP, &usblp_fops, NULL);
+	if (usblp->devfs == NULL)
+		err("usblp%d: device node registration failed", minor);
+
 	info("usblp%d: USB %sdirectional printer dev %d if %d alt %d",
 		minor, bidir ? "Bi" : "Uni", dev->devnum, ifnum, alts);
 
@@ -600,19 +628,10 @@
 
 	kfree(usblp->device_id_string);
 
+	devfs_unregister(usblp->devfs);
 	usblp_table[usblp->minor] = NULL;
 	kfree(usblp);
 }
-
-static struct file_operations usblp_fops = {
-	owner:		THIS_MODULE,
-	read:		usblp_read,
-	write:		usblp_write,
-	poll:		usblp_poll,
-	ioctl:		usblp_ioctl,
-	open:		usblp_open,
-	release:	usblp_release,
-};
 
 static struct usb_device_id usblp_ids [] = {
 	{ USB_INTERFACE_INFO(7, 1, 1) },
diff -urN linux-2.4.3-pre1/drivers/usb/scanner.c linux/drivers/usb/scanner.c
--- linux-2.4.3-pre1/drivers/usb/scanner.c	Thu Jan  4 13:15:32 2001
+++ linux/drivers/usb/scanner.c	Mon Dec  3 22:29:50 2001
@@ -1,7 +1,7 @@
 /* -*- linux-c -*- */
 
 /* 
- * Driver for USB Scanners (linux-2.4.0test1-ac7)
+ * Driver for USB Scanners (linux-2.4.0)
  *
  * Copyright (C) 1999, 2000 David E. Nelson
  *
@@ -208,6 +208,26 @@
  *    - Added the Epson Expression1600 ID's. Thanks to Karl Heinz
  *      Kremer <khk@khk.net>.
  *
+ * 0.4.5  2/28/2001
+ *    - Added Mustek ID's (BearPaw 2400, 1200 CU Plus, BearPaw 1200F).
+ *      Thanks to Henning Meier-Geinitz <henningmg@gmx.de>.
+ *    - Added read_timeout module parameter to override RD_NAK_TIMEOUT
+ *      when read()'ing from devices.
+ *    - Stalled pipes are now checked and cleared with
+ *      usb_clear_halt() for the read_scanner() function. This should
+ *      address the "funky result: -32" error messages.
+ *    - Removed Microtek scanner ID's.  Microtek scanners are now
+ *      supported via the drivers/usb/microtek.c driver.
+ *    - Added scanner specific read timeout's.
+ *    - Return status errors are NEGATIVE!!!  This should address the
+ *      "funky result: -110" error messages.
+ *    - Replaced USB_ST_TIMEOUT with ETIMEDOUT.
+ *    - rd_nak was still defined in MODULE_PARM.  It's been updated with
+ *      read_timeout.  Thanks to Mark W. Webb <markwebb@adelphia.net> for
+ *      reporting this bug.
+ *    - Added Epson Perfection 1640SU and 1640SU Photo.  Thanks to
+ *      Jean-Luc <f5ibh@db0bm.ampr.org>.
+ *
  *  TODO
  *
  *    - Performance
@@ -235,8 +255,7 @@
  *    System: Pentium 120, 80 MB RAM, OHCI, Linux 2.3.23, HP 4100C USB Scanner
  *            300 dpi scan of the entire bed
  *      24 Bit Color ~ 70 secs - 3.6 Mbit/sec
- *       8 Bit Gray  ~ 17 secs - 4.2 Mbit/sec
- */
+ *       8 Bit Gray ~ 17 secs - 4.2 Mbit/sec */
 
 /* 
  * Scanner definitions, macros, module info, 
@@ -244,81 +263,6 @@
  */ 
 #include "scanner.h"
 
-/* Table of scanners that may work with this driver */
-static struct usb_device_id scanner_device_ids [] = {
-	/* Acer */
-	{ USB_DEVICE(0x04a5, 0x2060) },	/* Prisa Acerscan 620U & 640U (!)*/
-	{ USB_DEVICE(0x04a5, 0x2040) },	/* Prisa AcerScan 620U (!) */
-	{ USB_DEVICE(0x04a5, 0x2022) },	/* Vuego Scan Brisa 340U */
-	/* Agfa */
-	{ USB_DEVICE(0x06bd, 0x0001) },	/* SnapScan 1212U */
-	{ USB_DEVICE(0x06bd, 0x0002) },	/* SnapScan 1236U */
-	{ USB_DEVICE(0x06bd, 0x2061) },	/* Another SnapScan 1212U (?)*/
-	{ USB_DEVICE(0x06bd, 0x0100) },	/* SnapScan Touch */
-	/* Colorado -- See Primax/Colorado below */
-	/* Epson -- See Seiko/Epson below */
-	/* Genius */
-	{ USB_DEVICE(0x0458, 0x2001) },	/* ColorPage-Vivid Pro */
-	/* Hewlett Packard */
-	{ USB_DEVICE(0x03f0, 0x0205) },	/* 3300C */
-	{ USB_DEVICE(0x03f0, 0x0101) },	/* 4100C */
-	{ USB_DEVICE(0x03f0, 0x0105) },	/* 4200C */
-	{ USB_DEVICE(0x03f0, 0x0102) },	/* PhotoSmart S20 */
-	{ USB_DEVICE(0x03f0, 0x0401) },	/* 5200C */
-	{ USB_DEVICE(0x03f0, 0x0701) },	/* 5300C */
-	{ USB_DEVICE(0x03f0, 0x0201) },	/* 6200C */
-	{ USB_DEVICE(0x03f0, 0x0601) },	/* 6300C */
-	/* iVina */
-	{ USB_DEVICE(0x0638, 0x0268) },     /* 1200U */
-	/* Microtek */
-	{ USB_DEVICE(0x05da, 0x0099) },	/* ScanMaker X6 - X6U */
-	{ USB_DEVICE(0x05da, 0x0094) },	/* Phantom 336CX - C3 */
-	{ USB_DEVICE(0x05da, 0x00a0) },	/* Phantom 336CX - C3 #2 */
-	{ USB_DEVICE(0x05da, 0x009a) },	/* Phantom C6 */
-	{ USB_DEVICE(0x05da, 0x00a3) },	/* ScanMaker V6USL */
-	{ USB_DEVICE(0x05da, 0x80a3) },	/* ScanMaker V6USL #2 */
-	{ USB_DEVICE(0x05da, 0x80ac) },	/* ScanMaker V6UL - SpicyU */
-	/* Mustek */
-	{ USB_DEVICE(0x055f, 0x0001) },	/* 1200 CU */
-	{ USB_DEVICE(0x0400, 0x1000) },	/* BearPaw 1200 */
-	{ USB_DEVICE(0x055f, 0x0002) },	/* 600 CU */
-	{ USB_DEVICE(0x055f, 0x0003) },	/* 1200 USB */
-	{ USB_DEVICE(0x055f, 0x0006) },	/* 1200 UB */
-	/* Primax/Colorado */
-	{ USB_DEVICE(0x0461, 0x0300) },	/* G2-300 #1 */
-	{ USB_DEVICE(0x0461, 0x0380) },	/* G2-600 #1 */
-	{ USB_DEVICE(0x0461, 0x0301) },	/* G2E-300 #1 */
-	{ USB_DEVICE(0x0461, 0x0381) },	/* ReadyScan 636i */
-	{ USB_DEVICE(0x0461, 0x0302) },	/* G2-300 #2 */
-	{ USB_DEVICE(0x0461, 0x0382) },	/* G2-600 #2 */
-	{ USB_DEVICE(0x0461, 0x0303) },	/* G2E-300 #2 */
-	{ USB_DEVICE(0x0461, 0x0383) },	/* G2E-600 */
-	{ USB_DEVICE(0x0461, 0x0340) },	/* Colorado USB 9600 */
-	{ USB_DEVICE(0x0461, 0x0360) },	/* Colorado USB 19200 */
-	{ USB_DEVICE(0x0461, 0x0341) },	/* Colorado 600u */
-	{ USB_DEVICE(0x0461, 0x0361) },	/* Colorado 1200u */
-	/* Seiko/Epson Corp. */
-	{ USB_DEVICE(0x04b8, 0x0101) },	/* Perfection 636U and 636Photo */
-	{ USB_DEVICE(0x04b8, 0x0103) },	/* Perfection 610 */
-	{ USB_DEVICE(0x04b8, 0x0104) },	/* Perfection 1200U and 1200Photo*/
-	{ USB_DEVICE(0x04b8, 0x0106) },	/* Stylus Scan 2500 */
-	{ USB_DEVICE(0x04b8, 0x0107) },	/* Expression 1600 */
-	/* Umax */
-	{ USB_DEVICE(0x1606, 0x0010) },	/* Astra 1220U */
-	{ USB_DEVICE(0x1606, 0x0030) },	/* Astra 2000U */
-	{ USB_DEVICE(0x1606, 0x0230) },	/* Astra 2200U */
-	/* Visioneer */
-	{ USB_DEVICE(0x04a7, 0x0221) },	/* OneTouch 5300 USB */
-	{ USB_DEVICE(0x04a7, 0x0211) },	/* OneTouch 7600 USB */
-	{ USB_DEVICE(0x04a7, 0x0231) },	/* 6100 USB */
-	{ USB_DEVICE(0x04a7, 0x0311) },	/* 6200 EPP/USB */
-	{ USB_DEVICE(0x04a7, 0x0321) },	/* OneTouch 8100 EPP/USB */
-	{ USB_DEVICE(0x04a7, 0x0331) }, /* OneTouch 8600 EPP/USB */
-	{ }				/* Terminating entry */
-};
-
-MODULE_DEVICE_TABLE (usb, scanner_device_ids);
-
 
 static void
 irq_scanner(struct urb *urb)
@@ -475,9 +419,9 @@
 		result = usb_bulk_msg(dev,usb_sndbulkpipe(dev, scn->bulk_out_ep), obuf, this_write, &partial, 60*HZ);
 		dbg("write stats(%d): result:%d this_write:%d partial:%d", scn_minor, result, this_write, partial);
 
-		if (result == USB_ST_TIMEOUT) {	/* NAK -- shouldn't happen */
+		if (result == -ETIMEDOUT) {	/* NAK -- shouldn't happen */
 			warn("write_scanner: NAK recieved.");
-			ret = -ETIME;
+			ret = result;
 			break;
 		} else if (result < 0) { /* We should not get any I/O errors */
 			warn("write_scanner(%d): funky result: %d. Please notify the maintainer.", scn_minor, result);
@@ -559,7 +503,7 @@
 
 		this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count;
 
-		result = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, scn->bulk_in_ep), ibuf, this_read, &partial, RD_NAK_TIMEOUT);
+		result = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, scn->bulk_in_ep), ibuf, this_read, &partial, scn->rd_nak_timeout);
 		dbg("read stats(%d): result:%d this_read:%d partial:%d count:%d", scn_minor, result, this_read, partial, count);
 
 /*
@@ -576,23 +520,35 @@
  * Ctrl-C's are acted upon in a reasonable amount of time.
  */
 
-		if (result == USB_ST_TIMEOUT && !partial) { /* Timeout
-                                                               and no
-                                                               data */
-			if (--rd_expire <= 0) {
-				warn("read_scanner(%d): excessive NAK's received", scn_minor);
-				ret = -ETIME;
-				break;
-			} else {
-				interruptible_sleep_on_timeout(&scn->rd_wait_q, RD_NAK_TIMEOUT);
-				continue;
+		if (result == -ETIMEDOUT) { /* NAK */
+			if (!partial) { /* No data */
+				if (--rd_expire <= 0) {	/* Give it up */
+					warn("read_scanner(%d): excessive NAK's received", scn_minor);
+					ret = result;
+					break;
+				} else { /* Keep trying to read data */
+					interruptible_sleep_on_timeout(&scn->rd_wait_q, scn->rd_nak_timeout);
+					continue;
+				}
+			} else { /* Timeout w/ some data */
+				goto data_recvd;
 			}
+		}
+		
+		if (result == -EPIPE) { /* No hope */
+			if(usb_clear_halt(dev, scn->bulk_in_ep)) {
+				err("read_scanner(%d): Failure to clear endpoint halt condition (%d).", scn_minor, ret);
+			}
+			ret = result;
+			break;
 		} else if ((result < 0) && (result != USB_ST_DATAUNDERRUN)) {
 			warn("read_scanner(%d): funky result:%d. Please notify the maintainer.", scn_minor, (int)result);
 			ret = -EIO;
 			break;
 		}
 
+	data_recvd:
+
 #ifdef RD_DATA_DUMP
 		if (partial) {
 			unsigned char cnt, cnt_max;
@@ -826,6 +782,26 @@
 		return NULL;
 	}
 	dbg("probe_scanner(%d): ibuf address:%p", scn_minor, scn->ibuf);
+	
+
+	switch (dev->descriptor.idVendor) { /* Scanner specific read timeout parameters */
+	case 0x04b8:		/* Seiko/Epson */
+		scn->rd_nak_timeout = HZ * 40;
+		break;
+	case 0x055f:		/* Mustek */
+	case 0x0400:		/* Another Mustek */
+	case 0x0ff5:		/* And yet another Mustek */
+		scn->rd_nak_timeout = HZ * 1;
+	default:
+		scn->rd_nak_timeout = RD_NAK_TIMEOUT;
+	}
+
+
+	if (read_timeout > 0) {	/* User specified read timeout overrides everything */
+		info("probe_scanner: User specified USB read timeout - %d", read_timeout);
+		scn->rd_nak_timeout = read_timeout;
+	}
+
 
 	scn->bulk_in_ep = have_bulk_in;
 	scn->bulk_out_ep = have_bulk_out;
diff -urN linux-2.4.3-pre1/drivers/usb/scanner.h linux/drivers/usb/scanner.h
--- linux-2.4.3-pre1/drivers/usb/scanner.h	Fri Feb  9 11:30:23 2001
+++ linux/drivers/usb/scanner.h	Mon Dec  3 22:29:50 2001
@@ -1,5 +1,5 @@
 /*
- * Driver for USB Scanners (linux-2.4.0test1-ac7)
+ * Driver for USB Scanners (linux-2.4.0)
  *
  * Copyright (C) 1999, 2000 David E. Nelson
  *
@@ -36,7 +36,7 @@
 
 #include <linux/usb.h>
 
-static __s32 vendor=-1, product=-1;
+static __s32 vendor=-1, product=-1, read_timeout=0;
 
 MODULE_AUTHOR("David E. Nelson, dnelson@jump.net, http://www.jump.net/~dnelson");
 MODULE_DESCRIPTION("USB Scanner Driver");
@@ -47,6 +47,9 @@
 MODULE_PARM(product, "i");
 MODULE_PARM_DESC(product, "User specified USB idProduct");
 
+MODULE_PARM(read_timeout, "i");
+MODULE_PARM_DESC(read_timeout, "User specified read timeout in seconds");
+
 
 /* Enable to activate the ioctl interface.  This is mainly meant for */
 /* development purposes until an ioctl number is officially registered */
@@ -56,6 +59,85 @@
 // #define RD_DATA_DUMP /* Enable to dump data - limited to 24 bytes */
 // #define WR_DATA_DUMP /* DEBUG does not have to be defined. */
 
+static struct usb_device_id scanner_device_ids [] = {
+	/* Acer */
+	{ USB_DEVICE(0x04a5, 0x2060) },	/* Prisa Acerscan 620U & 640U (!)*/
+	{ USB_DEVICE(0x04a5, 0x2040) },	/* Prisa AcerScan 620U (!) */
+	{ USB_DEVICE(0x04a5, 0x2022) },	/* Vuego Scan Brisa 340U */
+	/* Agfa */
+	{ USB_DEVICE(0x06bd, 0x0001) },	/* SnapScan 1212U */
+	{ USB_DEVICE(0x06bd, 0x0002) },	/* SnapScan 1236U */
+	{ USB_DEVICE(0x06bd, 0x2061) },	/* Another SnapScan 1212U (?)*/
+	{ USB_DEVICE(0x06bd, 0x0100) },	/* SnapScan Touch */
+	/* Colorado -- See Primax/Colorado below */
+	/* Epson -- See Seiko/Epson below */
+	/* Genius */
+	{ USB_DEVICE(0x0458, 0x2001) },	/* ColorPage-Vivid Pro */
+	/* Hewlett Packard */
+	{ USB_DEVICE(0x03f0, 0x0205) },	/* 3300C */
+	{ USB_DEVICE(0x03f0, 0x0101) },	/* 4100C */
+	{ USB_DEVICE(0x03f0, 0x0105) },	/* 4200C */
+	{ USB_DEVICE(0x03f0, 0x0102) },	/* PhotoSmart S20 */
+	{ USB_DEVICE(0x03f0, 0x0401) },	/* 5200C */
+	//	{ USB_DEVICE(0x03f0, 0x0701) },	/* 5300C - NOT SUPPORTED - see http://www.neatech.nl/oss/HP5300C/ */
+	{ USB_DEVICE(0x03f0, 0x0201) },	/* 6200C */
+	{ USB_DEVICE(0x03f0, 0x0601) },	/* 6300C */
+	/* iVina */
+	{ USB_DEVICE(0x0638, 0x0268) },     /* 1200U */
+	/* Microtek -- No longer supported - Enable SCSI and USB Microtek in kernel config */
+	//	{ USB_DEVICE(0x05da, 0x0099) },	/* ScanMaker X6 - X6U */
+	//	{ USB_DEVICE(0x05da, 0x0094) },	/* Phantom 336CX - C3 */
+	//	{ USB_DEVICE(0x05da, 0x00a0) },	/* Phantom 336CX - C3 #2 */
+	//	{ USB_DEVICE(0x05da, 0x009a) },	/* Phantom C6 */
+	//	{ USB_DEVICE(0x05da, 0x00a3) },	/* ScanMaker V6USL */
+	//	{ USB_DEVICE(0x05da, 0x80a3) },	/* ScanMaker V6USL #2 */
+	//	{ USB_DEVICE(0x05da, 0x80ac) },	/* ScanMaker V6UL - SpicyU */
+	/* Mustek */
+	{ USB_DEVICE(0x055f, 0x0001) },	/* 1200 CU */
+	{ USB_DEVICE(0x0400, 0x1000) },	/* BearPaw 1200 */
+	{ USB_DEVICE(0x055f, 0x0002) },	/* 600 CU */
+	{ USB_DEVICE(0x055f, 0x0003) },	/* 1200 USB */
+	{ USB_DEVICE(0x055f, 0x0006) },	/* 1200 UB */
+	{ USB_DEVICE(0x0400, 0x1001) }, /* BearPaw 2400 */
+	{ USB_DEVICE(0x055f, 0x0008) }, /* 1200 CU Plus */
+	{ USB_DEVICE(0x0ff5, 0x0010) }, /* BearPaw 1200F */
+	/* Primax/Colorado */
+	{ USB_DEVICE(0x0461, 0x0300) },	/* G2-300 #1 */
+	{ USB_DEVICE(0x0461, 0x0380) },	/* G2-600 #1 */
+	{ USB_DEVICE(0x0461, 0x0301) },	/* G2E-300 #1 */
+	{ USB_DEVICE(0x0461, 0x0381) },	/* ReadyScan 636i */
+	{ USB_DEVICE(0x0461, 0x0302) },	/* G2-300 #2 */
+	{ USB_DEVICE(0x0461, 0x0382) },	/* G2-600 #2 */
+	{ USB_DEVICE(0x0461, 0x0303) },	/* G2E-300 #2 */
+	{ USB_DEVICE(0x0461, 0x0383) },	/* G2E-600 */
+	{ USB_DEVICE(0x0461, 0x0340) },	/* Colorado USB 9600 */
+	{ USB_DEVICE(0x0461, 0x0360) },	/* Colorado USB 19200 */
+	{ USB_DEVICE(0x0461, 0x0341) },	/* Colorado 600u */
+	{ USB_DEVICE(0x0461, 0x0361) },	/* Colorado 1200u */
+	/* Seiko/Epson Corp. */
+	{ USB_DEVICE(0x04b8, 0x0101) },	/* Perfection 636U and 636Photo */
+	{ USB_DEVICE(0x04b8, 0x0103) },	/* Perfection 610 */
+	{ USB_DEVICE(0x04b8, 0x0104) },	/* Perfection 1200U and 1200Photo*/
+	{ USB_DEVICE(0x04b8, 0x0106) },	/* Stylus Scan 2500 */
+	{ USB_DEVICE(0x04b8, 0x0107) },	/* Expression 1600 */
+	{ USB_DEVICE(0x04b8, 0x010b) }, /* Perfection 1240U */
+	{ USB_DEVICE(0x04b8, 0x010a) }, /* Perfection 1640SU and 1640SU Photo */
+	/* Umax */
+	{ USB_DEVICE(0x1606, 0x0010) },	/* Astra 1220U */
+	{ USB_DEVICE(0x1606, 0x0030) },	/* Astra 2000U */
+	{ USB_DEVICE(0x1606, 0x0230) },	/* Astra 2200U */
+	/* Visioneer */
+	{ USB_DEVICE(0x04a7, 0x0221) },	/* OneTouch 5300 USB */
+	{ USB_DEVICE(0x04a7, 0x0211) },	/* OneTouch 7600 USB */
+	{ USB_DEVICE(0x04a7, 0x0231) },	/* 6100 USB */
+	{ USB_DEVICE(0x04a7, 0x0311) },	/* 6200 EPP/USB */
+	{ USB_DEVICE(0x04a7, 0x0321) },	/* OneTouch 8100 EPP/USB */
+	{ USB_DEVICE(0x04a7, 0x0331) }, /* OneTouch 8600 EPP/USB */
+	{ }				/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, scanner_device_ids);
+
 #define IS_EP_BULK(ep)  ((ep).bmAttributes == USB_ENDPOINT_XFER_BULK ? 1 : 0)
 #define IS_EP_BULK_IN(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
 #define IS_EP_BULK_OUT(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT)
@@ -73,7 +155,7 @@
 #define OBUF_SIZE 4096
 
 /* read_scanner timeouts -- RD_NAK_TIMEOUT * RD_EXPIRE = Number of seconds */
-#define RD_NAK_TIMEOUT (10*HZ)	/* Number of X seconds to wait */
+#define RD_NAK_TIMEOUT (10*HZ)	/* Default number of X seconds to wait */
 #define RD_EXPIRE 12		/* Number of attempts to wait X seconds */
 
 
@@ -90,12 +172,13 @@
 	unsigned int ifnum;	/* Interface number of the USB device */
 	kdev_t scn_minor;	/* Scanner minor - used in disconnect() */
 	unsigned char button;	/* Front panel buffer */
-        char isopen;		/* Not zero if the device is open */
+	char isopen;		/* Not zero if the device is open */
 	char present;		/* Not zero if device is present */
 	char *obuf, *ibuf;	/* transfer buffers */
 	char bulk_in_ep, bulk_out_ep, intr_ep; /* Endpoint assignments */
 	wait_queue_head_t rd_wait_q; /* read timeouts */
 	struct semaphore gen_lock; /* lock to prevent concurrent reads or writes */
+	unsigned int rd_nak_timeout; /* Seconds to wait before read() timeout. */
 };
 
 static struct scn_usb_data *p_scn_table[SCN_MAX_MNR] = { NULL, /* ... */};
diff -urN linux-2.4.3-pre1/drivers/usb/serial/keyspan.c linux/drivers/usb/serial/keyspan.c
--- linux-2.4.3-pre1/drivers/usb/serial/keyspan.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/usb/serial/keyspan.c	Mon Dec  3 22:29:50 2001
@@ -839,9 +839,6 @@
 	p_priv = (struct keyspan_port_private *)(port->private);
 
 	/* Set some sane defaults */
-	p_priv->baud = 9600;
-	p_priv->cflag = CREAD | CLOCAL;
-	p_priv->flow_control = flow_none;
 	p_priv->rts_state = 1;
 	p_priv->dtr_state = 1;
 
@@ -854,16 +851,8 @@
 			dbg(__FUNCTION__ " submit urb %d failed (%d)", i, err);
 		}
 	}
-/*    Now done in startup routine
-	if (atomic_inc_return(&s_priv->active_count) == 1) {
-		s_priv->instat_urb->dev = serial->dev;
-		if ((err = usb_submit_urb(s_priv->instat_urb)) != 0) {
-			dbg(__FUNCTION__ " submit instat urb failed %d", err);
-		}
-	}
-*/
 
-	keyspan_send_setup(port);
+	keyspan_set_termios(port, NULL);
 
 	return (0);
 }
diff -urN linux-2.4.3-pre1/drivers/usb/usb-uhci.c linux/drivers/usb/usb-uhci.c
--- linux-2.4.3-pre1/drivers/usb/usb-uhci.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/usb/usb-uhci.c	Mon Dec  3 22:29:50 2001
@@ -2945,10 +2945,9 @@
 	/* Search for the IO base address.. */
 	for (i = 0; i < 6; i++) {
 
-		unsigned int io_addr = dev->resource[i].start;
-		unsigned int io_size =
-		dev->resource[i].end - dev->resource[i].start + 1;
-		if (!(dev->resource[i].flags & IORESOURCE_IO))
+		unsigned int io_addr = pci_resource_start(dev, i);
+		unsigned int io_size = pci_resource_len(dev, i);
+		if (!(pci_resource_flags(dev,i) & IORESOURCE_IO))
 			continue;
 
 		/* Is it already in use? */
diff -urN linux-2.4.3-pre1/drivers/usb/usb.c linux/drivers/usb/usb.c
--- linux-2.4.3-pre1/drivers/usb/usb.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/usb/usb.c	Mon Dec  3 22:29:50 2001
@@ -153,7 +153,9 @@
 			down(&driver->serialize);
 			driver->disconnect(dev, interface->private_data);
 			up(&driver->serialize);
-			usb_driver_release_interface(driver, interface);
+			/* if driver->disconnect didn't release the interface */
+			if (interface->driver)
+				usb_driver_release_interface(driver, interface);
 			/*
 			 * This will go through the list looking for another
 			 * driver that can handle the device
@@ -349,6 +351,7 @@
  *
  *	Creates a USB host controller bus structure with the specified 
  *	usb_operations and initializes all the necessary internal objects.
+ *	(For use only by USB Host Controller Drivers.)
  *
  *	If no memory is available, NULL is returned.
  *
@@ -382,6 +385,7 @@
  *	usb_free_bus - frees the memory used by a bus structure
  *	@bus: pointer to the bus to free
  *
+ *	(For use only by USB Host Controller Drivers.)
  */
 void usb_free_bus(struct usb_bus *bus)
 {
@@ -395,6 +399,7 @@
  *	usb_register_bus - registers the USB host controller with the usb core
  *	@bus: pointer to the bus to register
  *
+ *	(For use only by USB Host Controller Drivers.)
  */
 void usb_register_bus(struct usb_bus *bus)
 {
@@ -415,6 +420,12 @@
 	info("new USB bus registered, assigned bus number %d", bus->busnum);
 }
 
+/**
+ *	usb_deregister_bus - deregisters the USB host controller
+ *	@bus: pointer to the bus to deregister
+ *
+ *	(For use only by USB Host Controller Drivers.)
+ */
 void usb_deregister_bus(struct usb_bus *bus)
 {
 	info("USB bus %d deregistered", bus->busnum);
@@ -502,56 +513,69 @@
 }
 
 
-/* usb_match_id searches an array of usb_device_id's and returns
-   the first one that matches the device and interface.
-
-   Parameters:
-   	"id" is an array of usb_device_id's is terminated by an entry
-	 containing all zeroes.
-
-	 "dev" and "interface" are the device and interface for which
-	 a match is sought.
-
-   If no match is found or if the "id" pointer is NULL, then
-   usb_match_id returns NULL.
-
-
-   What constitutes a match:
-
-   A zero in any element of a usb_device_id entry is a wildcard
-   (i.e., that field always matches).  For there to be a match,
-   *every* nonzero element of the usb_device_id must match the
-   provided device and interface in.  The comparison is for equality,
-   except for one pair of fields: usb_match_id.bcdDevice_{lo,hi} define
-   an inclusive range that dev->descriptor.bcdDevice must be in.
-
-   If interface->altsettings does not exist (i.e., there are no
-   interfaces defined), then bInterface{Class,SubClass,Protocol}
-   only match if they are all zeroes.
-
-
-   What constitutes a good "usb_device_id"?
-
-   The match algorithm is very simple, so that intelligence in
-   driver selection must come from smart driver id records.
-   Unless you have good reasons to use another selection policy,
-   provide match elements only in related groups:
-
-    * device specifiers (vendor and product IDs; and maybe
-      a revision range for that product);
-    * generic device specs (class/subclass/protocol);
-    * interface specs (class/subclass/protocol).
-    
-   Within those groups, work from least specific to most specific.
-   For example, don't give a product version range without vendor
-   and product IDs.
-
-   "driver_info" is not considered by the kernel matching algorithm,
-   but you can create a wildcard "matches anything" usb_device_id
-   as your driver's "modules.usbmap" entry if you provide only an
-   id with a nonzero "driver_info" field.
-*/   
-
+/**
+ * usb_match_id - find first usb_device_id matching device or interface
+ * @dev: the device whose descriptors are considered when matching
+ * @interface: the interface of interest
+ * @id: array of usb_device_id structures, terminated by zero entry
+ *
+ * usb_match_id searches an array of usb_device_id's and returns
+ * the first one matching the device or interface, or null.
+ * This is used when binding (or rebinding) a driver to an interface.
+ * Most USB device drivers will use this indirectly, through the usb core,
+ * but some layered driver frameworks use it directly.
+ * These device tables are exported with MODULE_DEVICE_TABLE, through
+ * modutils and "modules.usbmap", to support the driver loading
+ * functionality of USB hotplugging.
+ *
+ * What Matches:
+ *
+ * The "match_flags" element in a usb_device_id controls which
+ * members are used.  If the corresponding bit is set, the
+ * value in the device_id must match its corresponding member
+ * in the device or interface descriptor, or else the device_id
+ * does not match.
+ *
+ * "driver_info" is normally used only by device drivers,
+ * but you can create a wildcard "matches anything" usb_device_id
+ * as a driver's "modules.usbmap" entry if you provide an id with
+ * only a nonzero "driver_info" field.  If you do this, the USB device
+ * driver's probe() routine should use additional intelligence to
+ * decide whether to bind to the specified interface.
+ * 
+ * What Makes Good usb_device_id Tables:
+ *
+ * The match algorithm is very simple, so that intelligence in
+ * driver selection must come from smart driver id records.
+ * Unless you have good reasons to use another selection policy,
+ * provide match elements only in related groups, and order match
+ * specifiers from specific to general.  Use the macros provided
+ * for that purpose if you can.
+ *
+ * The most specific match specifiers use device descriptor
+ * data.  These are commonly used with product-specific matches;
+ * the USB_DEVICE macro lets you provide vendor and product IDs,
+ * and you can also matche against ranges of product revisions.
+ * These are widely used for devices with application or vendor
+ * specific bDeviceClass values.
+ *
+ * Matches based on device class/subclass/protocol specifications
+ * are slightly more general; use the USB_DEVICE_INFO macro, or
+ * its siblings.  These are used with single-function devices
+ * where bDeviceClass doesn't specify that each interface has
+ * its own class. 
+ *
+ * Matches based on interface class/subclass/protocol are the
+ * most general; they let drivers bind to any interface on a
+ * multiple-function device.  Use the USB_INTERFACE_INFO
+ * macro, or its siblings, to match class-per-interface style 
+ * devices (as recorded in bDeviceClass).
+ *  
+ * Within those groups, remember that not all combinations are
+ * meaningful.  For example, don't give a product version range
+ * without vendor and product IDs; or specify a protocol without
+ * its associated class and subclass.
+ */   
 const struct usb_device_id *
 usb_match_id(struct usb_device *dev, struct usb_interface *interface,
 	     const struct usb_device_id *id)
@@ -827,7 +851,7 @@
 call_policy (char *verb, struct usb_device *dev)
 { } 
 
-#endif	/* KMOD */
+#endif	/* CONFIG_HOTPLUG */
 
 
 /*
@@ -1639,7 +1663,9 @@
 				down(&driver->serialize);
 				driver->disconnect(dev, interface->private_data);
 				up(&driver->serialize);
-				usb_driver_release_interface(driver, interface);
+				/* if driver->disconnect didn't release the interface */
+				if (interface->driver)
+					usb_driver_release_interface(driver, interface);
 			}
 		}
 	}
diff -urN linux-2.4.3-pre1/drivers/video/fbcmap.c linux/drivers/video/fbcmap.c
--- linux-2.4.3-pre1/drivers/video/fbcmap.c	Tue Mar  7 10:52:41 2000
+++ linux/drivers/video/fbcmap.c	Mon Dec  3 22:29:50 2001
@@ -3,6 +3,9 @@
  *
  *	Created 15 Jun 1997 by Geert Uytterhoeven
  *
+ *	2001 - Documented with DocBook
+ *	- Brad Douglas <brad@neruo.com>
+ *
  *  This file is subject to the terms and conditions of the GNU General Public
  *  License.  See the file COPYING in the main directory of this archive for
  *  more details.
@@ -73,9 +76,18 @@
 };
 
 
-    /*
-     *  Allocate a colormap
-     */
+/**
+ *	fb_alloc_cmap - allocate a colormap
+ *	@cmap: frame buffer colormap structure
+ *	@len: length of @cmap
+ *	@transp: boolean, 1 if there is transparency, 0 otherwise
+ *
+ *	Allocates memory for a colormap @cmap.  @len is the
+ *	number of entries in the palette.
+ *
+ *	Returns -1 errno on error, or zero on success.
+ *
+ */
 
 int fb_alloc_cmap(struct fb_cmap *cmap, int len, int transp)
 {
@@ -113,9 +125,20 @@
 }
 
 
-    /*
-     *  Copy a colormap
-     */
+/**
+ *	fb_copy_cmap - copy a colormap
+ *	@from: frame buffer colormap structure
+ *	@to: frame buffer colormap structure
+ *	@fsfromto: determine copy method
+ *
+ *	Copy contents of colormap from @from to @to.
+ *
+ *	@fsfromto accepts the following integer parameters:
+ *	0: memcpy function
+ *	1: copy_from_user() function to copy from userspace
+ *	2: copy_to_user() function to copy to userspace
+ *
+ */
 
 void fb_copy_cmap(struct fb_cmap *from, struct fb_cmap *to, int fsfromto)
 {
@@ -159,9 +182,18 @@
 }
 
 
-    /*
-     *  Get the colormap for a screen
-     */
+/**
+ *	fb_get_cmap - get a colormap
+ *	@cmap: frame buffer colormap
+ *	@kspc: boolean, 0 copy local, 1 put_user() function
+ *	@getcolreg: pointer to a function to get a color register
+ *	@info: frame buffer info structure
+ *
+ *	Get a colormap @cmap for a screen of device @info.
+ *
+ *	Returns negative errno on error, or zero on success.
+ *
+ */
 
 int fb_get_cmap(struct fb_cmap *cmap, int kspc,
     	    	int (*getcolreg)(u_int, u_int *, u_int *, u_int *, u_int *,
@@ -205,9 +237,17 @@
 }
 
 
-    /*
-     *  Set the colormap for a screen
-     */
+/**
+ *	fb_set_cmap - set the colormap
+ *	@cmap: frame buffer colormap structure
+ *	@kspc: boolean, 0 copy local, 1 get_user() function
+ *	@info: frame buffer info structure
+ *
+ *	Sets the colormap @cmap for a screen of device @info.
+ *
+ *	Returns negative errno on error, or zero on success.
+ *
+ */
 
 int fb_set_cmap(struct fb_cmap *cmap, int kspc,
     	    	int (*setcolreg)(u_int, u_int, u_int, u_int, u_int,
@@ -253,9 +293,16 @@
 }
 
 
-    /*
-     *  Get the default colormap for a specific screen depth
-     */
+/**
+ *	fb_default_cmap - get default colormap
+ *	@len: size of palette for a depth
+ *
+ *	Gets the default colormap for a specific screen depth.  @len
+ *	is the size of the palette for a particular screen depth.
+ *
+ *	Returns pointer to a frame buffer colormap structure.
+ *
+ */
 
 struct fb_cmap *fb_default_cmap(int len)
 {
@@ -269,9 +316,12 @@
 }
 
 
-    /*
-     *  Invert all default colormaps
-     */
+/**
+ *	fb_invert_cmaps - invert all defaults colormaps
+ *
+ *	Invert all default colormaps.
+ *
+ */
 
 void fb_invert_cmaps(void)
 {
diff -urN linux-2.4.3-pre1/drivers/video/fbgen.c linux/drivers/video/fbgen.c
--- linux-2.4.3-pre1/drivers/video/fbgen.c	Wed Jul 26 11:08:41 2000
+++ linux/drivers/video/fbgen.c	Mon Dec  3 22:29:50 2001
@@ -3,6 +3,9 @@
  *
  *  Created 3 Jan 1998 by Geert Uytterhoeven
  *
+ *	2001 - Documented with DocBook
+ *	- Brad Douglas <brad@neruo.com>
+ *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file COPYING in the main directory of this archive
  * for more details.
@@ -25,9 +28,18 @@
 /* ---- `Generic' versions of the frame buffer device operations ----------- */
 
 
-    /*
-     *  Get the Fixed Part of the Display
-     */
+/**
+ *	fbgen_get_fix - get fixed part of display
+ *	@fix: fb_fix_screeninfo structure
+ *	@con: virtual console number
+ *	@info: frame buffer info structure
+ *
+ *	Get the fixed information part of the display and place it
+ *	into @fix for virtual console @con on device @info.
+ *
+ *	Returns negative errno on error, or zero on success.
+ *
+ */
 
 int fbgen_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
 {
@@ -48,9 +60,18 @@
 }
 
 
-    /*
-     *  Get the User Defined Part of the Display
-     */
+/**
+ *	fbgen_get_var - get user defined part of display
+ *	@var: fb_var_screeninfo structure
+ *	@con: virtual console number
+ *	@info: frame buffer info structure
+ *
+ *	Get the user defined part of the display and place it into @var
+ *	for virtual console @con on device @info.
+ *
+ *	Returns negative errno on error, or zero for success.
+ *
+ */
 
 int fbgen_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
 {
@@ -67,9 +88,18 @@
 }
 
 
-    /*
-     *  Set the User Defined Part of the Display
-     */
+/**
+ *	fbgen_set_var - set the user defined part of display
+ *	@var: fb_var_screeninfo user defined part of the display
+ *	@con: virtual console number
+ *	@info: frame buffer info structure
+ *
+ *	Set the user defined part of the display as dictated by @var
+ *	for virtual console @con on device @info.
+ *
+ *	Returns negative errno on error, or zero for success.
+ *
+ */
 
 int fbgen_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
 {
@@ -105,9 +135,19 @@
 }
 
 
-    /*
-     *  Get the Colormap
-     */
+/**
+ *	fbgen_get_cmap - get the colormap
+ *	@cmap: frame buffer colormap structure
+ *	@kspc: boolean, 0 copy local, 1 put_user() function
+ *	@con: virtual console number
+ *	@info: frame buffer info structure
+ *
+ *	Gets the colormap for virtual console @con and places it into
+ *	@cmap for device @info.
+ *
+ *	Returns negative errno on error, or zero for success.
+ *
+ */
 
 int fbgen_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 		   struct fb_info *info)
@@ -128,9 +168,19 @@
 }
 
 
-    /*
-     *  Set the Colormap
-     */
+/**
+ *	fbgen_set_cmap - set the colormap
+ *	@cmap: frame buffer colormap structure
+ *	@kspc: boolean, 0 copy local, 1 get_user() function
+ *	@con: virtual console number
+ *	@info: frame buffer info structure
+ *
+ *	Sets the colormap @cmap for virtual console @con on
+ *	device @info.
+ *
+ *	Returns negative errno on error, or zero for success.
+ *
+ */
 
 int fbgen_set_cmap(struct fb_cmap *cmap, int kspc, int con,
 		   struct fb_info *info)
@@ -152,11 +202,20 @@
 }
 
 
-    /*
-     *  Pan or Wrap the Display
-     *
-     *  This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
-     */
+/**
+ *	fbgen_pan_display - pan or wrap the display
+ *	@var: frame buffer user defined part of display
+ *	@con: virtual console number
+ *	@info: frame buffer info structure
+ *
+ *	Pan or wrap virtual console @con for device @info.
+ *
+ *	This call looks only at xoffset, yoffset and the
+ *	FB_VMODE_YWRAP flag in @var.
+ *
+ *	Returns negative errno on error, or zero for success.
+ *
+ */
 
 int fbgen_pan_display(struct fb_var_screeninfo *var, int con,
 		      struct fb_info *info)
@@ -193,9 +252,18 @@
 /* ---- Helper functions --------------------------------------------------- */
 
 
-    /*
-     *  Change the video mode
-     */
+/**
+ *	fbgen_do_set_var - change the video mode
+ *	@var: frame buffer user defined part of display
+ *	@isactive: boolean, 0 inactive, 1 active
+ *	@info: generic frame buffer info structure
+ *
+ *	Change the video mode settings for device @info.  If @isactive
+ *	is non-zero, the changes will be activated immediately.
+ *
+ *	Return negative errno on error, or zero for success.
+ *
+ */
 
 int fbgen_do_set_var(struct fb_var_screeninfo *var, int isactive,
 		     struct fb_info_gen *info)
@@ -215,6 +283,15 @@
 }
 
 
+/**
+ *	fbgen_set_disp - set generic display
+ *	@con: virtual console number
+ *	@info: generic frame buffer info structure
+ *
+ *	Sets a display on virtual console @con for device @info.
+ *
+ */
+
 void fbgen_set_disp(int con, struct fb_info_gen *info)
 {
     struct fbgen_hwswitch *fbhw = info->fbhw;
@@ -254,9 +331,15 @@
 }
 
 
-    /*
-     *  Install the current colormap
-     */
+/**
+ *	fbgen_install_cmap - install the current colormap
+ *	@con: virtual console number
+ *	@info: generic frame buffer info structure
+ *
+ *	Installs the current colormap for virtual console @con on
+ *	device @info.
+ *
+ */
 
 void fbgen_install_cmap(int con, struct fb_info_gen *info)
 {
@@ -272,9 +355,18 @@
 }
 
 
-    /*
-     *  Update the `var' structure (called by fbcon.c)
-     */
+/**
+ *	fbgen_update_var - update user defined part of display
+ *	@con: virtual console number
+ *	@info: frame buffer info structure
+ *
+ *	Updates the user defined part of the display ('var'
+ *	structure) on virtual console @con for device @info.
+ *	This function is called by fbcon.c.
+ *
+ *	Returns negative errno on error, or zero for success.
+ *
+ */
 
 int fbgen_update_var(int con, struct fb_info *info)
 {
@@ -290,9 +382,16 @@
 }
 
 
-    /*
-     *  Switch to a different virtual console
-     */
+/**
+ *	fbgen_switch - switch to a different virtual console.
+ *	@con: virtual console number
+ *	@info: frame buffer info structure
+ *
+ *	Switch to virtuall console @con on device @info.
+ *
+ *	Returns zero.
+ *
+ */
 
 int fbgen_switch(int con, struct fb_info *info)
 {
@@ -311,9 +410,14 @@
 }
 
 
-    /*
-     *  Blank the screen
-     */
+/**
+ *	fbgen_blank - blank the screen
+ *	@blank: boolean, 0 unblank, 1 blank
+ *	@info: frame buffer info structure
+ *
+ *	Blank the screen on device @info.
+ *
+ */
 
 void fbgen_blank(int blank, struct fb_info *info)
 {
diff -urN linux-2.4.3-pre1/drivers/video/fbmem.c linux/drivers/video/fbmem.c
--- linux-2.4.3-pre1/drivers/video/fbmem.c	Fri Feb  9 11:30:23 2001
+++ linux/drivers/video/fbmem.c	Mon Dec  3 22:29:50 2001
@@ -3,6 +3,9 @@
  *
  *  Copyright (C) 1994 Martin Schaller
  *
+ *	2001 - Documented with DocBook
+ *	- Brad Douglas <brad@neruo.com>
+ *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file COPYING in the main directory of this archive
  * for more details.
@@ -115,6 +118,8 @@
 extern int sisfb_setup(char*);
 extern int stifb_init(void);
 extern int stifb_setup(char*);
+extern int radeonfb_init(void);
+extern int radeonfb_setup(char*);
 
 static struct {
 	const char *name;
@@ -167,6 +172,9 @@
 #ifdef CONFIG_FB_RIVA
 	{ "riva", rivafb_init, rivafb_setup },
 #endif
+#ifdef CONFIG_FB_RADEON
+	{ "radeon", radeonfb_init, radeonfb_setup },
+#endif
 #ifdef CONFIG_FB_CONTROL
 	{ "controlfb", control_init, control_setup },
 #endif
@@ -683,6 +691,17 @@
 
 static devfs_handle_t devfs_handle;
 
+
+/**
+ *	register_framebuffer - registers a frame buffer device
+ *	@fb_info: frame buffer info structure
+ *
+ *	Registers a frame buffer device @fb_info.
+ *
+ *	Returns negative errno on error, or zero for success.
+ *
+ */
+
 int
 register_framebuffer(struct fb_info *fb_info)
 {
@@ -732,6 +751,17 @@
 	return 0;
 }
 
+
+/**
+ *	unregister_framebuffer - releases a frame buffer device
+ *	@fb_info: frame buffer info structure
+ *
+ *	Unregisters a frame buffer device @fb_info.
+ *
+ *	Returns negative errno on error, or zero for success.
+ *
+ */
+
 int
 unregister_framebuffer(struct fb_info *fb_info)
 {
@@ -752,6 +782,16 @@
 	return 0;
 }
 
+
+/**
+ *	fbmem_init - init frame buffer subsystem
+ *
+ *	Initialize the frame buffer subsystem.
+ *
+ *	NOTE: This function is _only_ to be called by drivers/char/mem.c.
+ *
+ */
+
 void __init 
 fbmem_init(void)
 {
@@ -781,9 +821,18 @@
 			fb_drivers[i].init();
 }
 
-    /*
-     *  Command line options
-     */
+
+/**
+ *	video_setup - process command line options
+ *	@options: string of options
+ *
+ *	Process command line options for frame buffer subsystem.
+ *
+ *	NOTE: This function is a __setup and __init function.
+ *
+ *	Returns zero.
+ *
+ */
 
 int __init video_setup(char *options)
 {
diff -urN linux-2.4.3-pre1/drivers/video/fonts.c linux/drivers/video/fonts.c
--- linux-2.4.3-pre1/drivers/video/fonts.c	Tue Jul 11 15:35:51 2000
+++ linux/drivers/video/fonts.c	Mon Dec  3 22:29:50 2001
@@ -4,6 +4,9 @@
  *    Created 1995 by Geert Uytterhoeven
  *    Rewritten 1998 by Martin Mares <mj@ucw.cz>
  *
+ *	2001 - Documented with DocBook
+ *	- Brad Douglas <brad@neruo.com>
+ *
  * This file is subject to the terms and conditions of the GNU General Public
  * License.  See the file COPYING in the main directory of this archive
  * for more details.
@@ -61,9 +64,17 @@
 #error No fonts configured.
 #endif
 
-   /*
-    *    Find a font with a specific name
-    */
+
+/**
+ *	fbcon_find_font - find a font
+ *	@name: string name of a font
+ *
+ *	Find a specified font with string name @name.
+ *
+ *	Returns %NULL if no font found, or a pointer to the
+ *	specified font.
+ *
+ */
 
 struct fbcon_font_desc *fbcon_find_font(char *name)
 {
@@ -76,9 +87,18 @@
 }
 
 
-   /*
-    *    Get the default font for a specific screen size
-    */
+/**
+ *	fbcon_get_default_font - get default font
+ *	@xres: screen size of X
+ *	@yres: screen size of Y
+ *
+ *	Get the default font for a specified screen size.
+ *	Dimensions are in pixels.
+ *
+ *	Returns %NULL if no font is found, or a pointer to the
+ *	chosen font.
+ *
+ */
 
 struct fbcon_font_desc *fbcon_get_default_font(int xres, int yres)
 {
diff -urN linux-2.4.3-pre1/drivers/video/macmodes.c linux/drivers/video/macmodes.c
--- linux-2.4.3-pre1/drivers/video/macmodes.c	Mon Nov 27 17:11:26 2000
+++ linux/drivers/video/macmodes.c	Mon Dec  3 22:29:50 2001
@@ -7,6 +7,9 @@
  *      - Ani Joshi
  *      - Brad Douglas <brad@neruo.com>
  *
+ *	2001 - Documented with DocBook
+ *	- Brad Douglas <brad@neruo.com>
+ *
  *  This file is subject to the terms and conditions of the GNU General Public
  *  License. See the file COPYING in the main directory of this archive for
  *  more details.
@@ -203,12 +206,39 @@
 };
 
 
+/**
+ *	console_getmode - get current mode
+ *	@mode: virtual console mode structure
+ *
+ *	Populates @mode with the current mode held in the global
+ *	display_info structure.
+ *
+ *	Note, this function is only for XPMAC compatibility.
+ *
+ *	Returns zero.
+ */
+
 int console_getmode(struct vc_mode *mode)
 {
     *mode = display_info;
     return 0;
 }
 
+
+/**
+ *	console_setmode - sets current console mode
+ *	@mode: virtual console mode structure
+ *	@doit: boolean, 0 test mode, 1 test and activate mode
+ *
+ *	Sets @mode for all virtual consoles if @doit is non-zero,
+ *	otherwise, test a mode for validity.
+ *
+ *	Note, this function is only for XPMAC compatibility.
+ *
+ *	Returns negative errno on error, or zero for success.
+ *
+ */
+
 int console_setmode(struct vc_mode *mode, int doit)
 {
     struct fb_var_screeninfo var;
@@ -255,6 +285,23 @@
     return 0;
 }
 
+
+/**
+ *	console_setcmap - sets palette color map for console
+ *	@n_entries: number of entries in the palette (max 16)
+ *	@red: value for red component of palette
+ *	@green: value for green component of palette
+ *	@blue: value for blue component of palette
+ *
+ *	Sets global palette_cmap structure and activates the palette
+ *	on the current console.
+ *
+ *	Note, this function is only for XPMAC compatibility.
+ *
+ *	Returns negative errno on error, or zero for success.
+ *
+ */
+
 int console_setcmap(int n_entries, unsigned char *red, unsigned char *green,
                     unsigned char *blue)
 {
@@ -285,6 +332,21 @@
     return 0;
 }
 
+
+/**
+ *	console_powermode - sets monitor power mode
+ *	@mode: power state to set
+ *
+ *	Sets power state as dictated by @mode.
+ *
+ *	Note that this function is only for XPMAC compatibility and
+ *	doesn't do much.
+ *
+ *	Returns 0 for %VC_POWERMODE_INQUIRY, -EINVAL for VESA power
+ *	settings, or -ENIXIO on failure.
+ *
+ */
+
 int console_powermode(int mode)
 {
     if (mode == VC_POWERMODE_INQUIRY)
@@ -297,9 +359,18 @@
 #endif /* CONFIG_FB_COMPAT_XPMAC */
 
 
-    /*
-     *  Convert a MacOS vmode/cmode pair to a frame buffer video mode structure
-     */
+/**
+ *	mac_vmode_to_var - converts vmode/cmode pair to var structure
+ *	@vmode: MacOS video mode
+ *	@cmode: MacOS color mode
+ *	@var: frame buffer video mode structure
+ *
+ *	Converts a MacOS vmode/cmode pair to a frame buffer video
+ *	mode structure.
+ *
+ *	Returns negative errno on error, or zero for success.
+ *
+ */
 
 int mac_vmode_to_var(int vmode, int cmode, struct fb_var_screeninfo *var)
 {
@@ -370,9 +441,18 @@
 }
 
 
-    /*
-     *  Convert a frame buffer video mode structure to a MacOS vmode/cmode pair
-     */
+/**
+ *	mac_var_to_vmode - convert var structure to MacOS vmode/cmode pair
+ *	@var: frame buffer video mode structure
+ *	@vmode: MacOS video mode
+ *	@cmode: MacOS color mode
+ *
+ *	Converts a frame buffer video mode structure to a MacOS
+ *	vmode/cmode pair.
+ *
+ *	Returns negative errno on error, or zero for success.
+ *
+ */
 
 int mac_var_to_vmode(const struct fb_var_screeninfo *var, int *vmode,
 		     int *cmode)
@@ -406,9 +486,16 @@
 }
 
 
-    /*
-     *  Convert a Mac monitor sense number to a MacOS vmode number
-     */
+/**
+ *	mac_map_monitor_sense - Convert monitor sense to vmode
+ *	@sense: Macintosh monitor sense number
+ *
+ *	Converts a Macintosh monitor sense number to a MacOS
+ *	vmode number.
+ *
+ *	Returns MacOS vmode video mode number.
+ *
+ */
 
 int mac_map_monitor_sense(int sense)
 {
@@ -421,12 +508,25 @@
 }
 
 
-    /*
-     *  Find a suitable video mode
-     *
-     *  If the name of the wanted mode begins with `mac', use the Mac video
-     *  mode database, else fall back to the standard video mode database.
-     */
+/**
+ *	mac_find_mode - find a video mode
+ *	@var: frame buffer user defined part of display
+ *	@info: frame buffer info structure
+ *	@mode_option: video mode name (see mac_modedb[])
+ *	@default_bpp: default color depth in bits per pixel
+ *
+ *	Finds a suitable video mode.  Tries to set mode specified
+ *	by @mode_option.  If the name of the wanted mode begins with
+ *	'mac', the Mac video mode database will be used, otherwise it
+ *	will fall back to the standard video mode database.
+ *
+ *	Note: Function marked as __init and can only be used during
+ *	system boot.
+ *
+ *	Returns error code from fb_find_mode (see fb_find_mode
+ *	function).
+ *
+ */
 
 int __init mac_find_mode(struct fb_var_screeninfo *var, struct fb_info *info,
 			 const char *mode_option, unsigned int default_bpp)
diff -urN linux-2.4.3-pre1/drivers/video/modedb.c linux/drivers/video/modedb.c
--- linux-2.4.3-pre1/drivers/video/modedb.c	Thu Mar 23 10:07:42 2000
+++ linux/drivers/video/modedb.c	Mon Dec  3 22:29:50 2001
@@ -3,6 +3,9 @@
  *
  *	Copyright (C) 1999 Geert Uytterhoeven
  *
+ *	2001 - Documented with DocBook
+ *	- Brad Douglas <brad@neruo.com>
+ *
  *  This file is subject to the terms and conditions of the GNU General Public
  *  License. See the file COPYING in the main directory of this archive for
  *  more details.
@@ -276,6 +279,20 @@
 	return MINOR(current->tty->device) - 1;
 }
 
+
+/**
+ *	__fb_try_mode - test a video mode
+ *	@var: frame buffer user defined part of display
+ *	@info: frame buffer info structure
+ *	@mode: frame buffer video mode structure
+ *	@bpp: color depth in bits per pixel
+ *
+ *	Tries a video mode to test it's validity for device @info.
+ *
+ *	Returns 1 on success.
+ *
+ */
+
 int __fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info,
 		  const struct fb_videomode *mode, unsigned int bpp)
 {
@@ -306,21 +323,37 @@
 }
 
 
-    /*
-     *
-     *  Find a suitable video mode
-     *
-     *  Valid mode specifiers (mode_option):
-     *
-     *	<xres>x<yres>[-<bpp>][@<refresh>]
-     *	<name>[-<bpp>][@<refresh>]
-     *
-     *  with <xres>, <yres>, <bpp> and <refresh> decimal numbers and <name> a
-     *  string
-     *
-     *  The passed struct fb_var_screeninfo is _not_ cleared! This allows you
-     *  to supply values for e.g. the grayscale and accel_flags fields.
-     */
+/**
+ *	fb_find_mode - finds a valid video mode
+ *	@var: frame buffer user defined part of display
+ *	@info: frame buffer info structure
+ *	@mode_option: string video mode to find
+ *	@db: video mode database
+ *	@dbsize: size of @db
+ *	@default_mode: default video mode to fall back to
+ *	@default_bpp: default color depth in bits per pixel
+ *
+ *	Finds a suitable video mode, starting with the specified mode
+ *	in @mode_option with fallback to @default_mode.  If
+ *	@default_mode fails, all modes in the video mode database will
+ *	be tried.
+ *
+ *	Valid mode specifiers for @mode_option:
+ *
+ *	<xres>x<yres>[-<bpp>][@<refresh>] or
+ *	<name>[-<bpp>][@<refresh>]
+ *
+ *	with <xres>, <yres>, <bpp> and <refresh> decimal numbers and
+ *	<name> a string.
+ *
+ *	NOTE: The passed struct @var is _not_ cleared!  This allows you
+ *	to supply values for e.g. the grayscale and accel_flags fields.
+ *
+ *	Returns zero for failure, 1 if using specified @mode_option,
+ *	2 if using specified @mode_option with an ignored refresh rate,
+ *	3 if default mode is used, 4 if fall back to any valid mode.
+ *
+ */
 
 int __init fb_find_mode(struct fb_var_screeninfo *var,
 			struct fb_info *info, const char *mode_option,
diff -urN linux-2.4.3-pre1/fs/Makefile linux/fs/Makefile
--- linux-2.4.3-pre1/fs/Makefile	Mon Jan 15 12:42:32 2001
+++ linux/fs/Makefile	Mon Dec  3 22:29:50 2001
@@ -7,7 +7,7 @@
 
 O_TARGET := fs.o
 
-export-objs :=	filesystems.o
+export-objs :=	filesystems.o dcache.o
 mod-subdirs :=	nls
 
 obj-y :=	open.o read_write.o devices.o file_table.o buffer.o \
diff -urN linux-2.4.3-pre1/fs/dcache.c linux/fs/dcache.c
--- linux-2.4.3-pre1/fs/dcache.c	Fri Feb 16 16:06:17 2001
+++ linux/fs/dcache.c	Mon Dec  3 22:29:50 2001
@@ -22,6 +22,7 @@
 #include <linux/init.h>
 #include <linux/smp_lock.h>
 #include <linux/cache.h>
+#include <linux/module.h>
 
 #include <asm/uaccess.h>
 
@@ -1250,6 +1251,7 @@
 
 /* SLAB cache for buffer_head structures */
 kmem_cache_t *bh_cachep;
+EXPORT_SYMBOL(bh_cachep);
 
 void __init vfs_caches_init(unsigned long mempages)
 {
diff -urN linux-2.4.3-pre1/fs/inode.c linux/fs/inode.c
--- linux-2.4.3-pre1/fs/inode.c	Fri Feb  9 11:34:18 2001
+++ linux/fs/inode.c	Mon Dec  3 22:29:50 2001
@@ -613,6 +613,7 @@
 	inode->i_bdev = NULL;
 	inode->i_data.a_ops = &empty_aops;
 	inode->i_data.host = inode;
+	inode->i_data.gfp_mask = GFP_HIGHUSER;
 	inode->i_mapping = &inode->i_data;
 }
 
diff -urN linux-2.4.3-pre1/fs/ncpfs/mmap.c linux/fs/ncpfs/mmap.c
--- linux-2.4.3-pre1/fs/ncpfs/mmap.c	Fri Feb  9 11:29:44 2001
+++ linux/fs/ncpfs/mmap.c	Mon Dec  3 22:29:50 2001
@@ -43,7 +43,7 @@
 	int bufsize;
 	int pos;
 
-	page = alloc_page(GFP_HIGHMEM); /* ncpfs has nothing against GFP_HIGHMEM
+	page = alloc_page(GFP_HIGHUSER); /* ncpfs has nothing against high pages
 	           as long as recvmsg and memset works on it */
 	if (!page)
 		return page;
diff -urN linux-2.4.3-pre1/fs/nfs/dir.c linux/fs/nfs/dir.c
--- linux-2.4.3-pre1/fs/nfs/dir.c	Fri Feb  9 11:29:44 2001
+++ linux/fs/nfs/dir.c	Mon Dec  3 22:29:50 2001
@@ -321,7 +321,7 @@
 		desc->page = NULL;
 	}
 
-	page = page_cache_alloc();
+	page = alloc_page(GFP_HIGHUSER);
 	if (!page) {
 		status = -ENOMEM;
 		goto out;
diff -urN linux-2.4.3-pre1/fs/reiserfs/fix_node.c linux/fs/reiserfs/fix_node.c
--- linux-2.4.3-pre1/fs/reiserfs/fix_node.c	Mon Jan 15 15:31:19 2001
+++ linux/fs/reiserfs/fix_node.c	Mon Dec  3 22:29:50 2001
@@ -840,7 +840,7 @@
 
 
 /* Get new buffers for storing new nodes that are created while balancing.
- * Returns:	SCHEDULE_OCCURED - schedule occured while the function worked;
+ * Returns:	SCHEDULE_OCCURRED - schedule occurred while the function worked;
  *	        CARRY_ON - schedule didn't occur while the function worked;
  *	        NO_DISK_SPACE - no disk space.
  */
@@ -1077,7 +1077,7 @@
  * Calculate left/right common parent of the current node and L[h]/R[h].
  * Calculate left/right delimiting key position.
  * Returns:	PATH_INCORRECT   - path in the tree is not correct;
- 		SCHEDULE_OCCURRED - schedule occured while the function worked;
+ 		SCHEDULE_OCCURRED - schedule occurred while the function worked;
  *	        CARRY_ON         - schedule didn't occur while the function worked;
  */
 static int  get_far_parent (struct tree_balance *   p_s_tb,
@@ -1198,7 +1198,7 @@
  * S[n_path_offset] and L[n_path_offset]/R[n_path_offset]: F[n_path_offset], FL[n_path_offset],
  * FR[n_path_offset], CFL[n_path_offset], CFR[n_path_offset].
  * Calculate numbers of left and right delimiting keys position: lkey[n_path_offset], rkey[n_path_offset].
- * Returns:	SCHEDULE_OCCURRED - schedule occured while the function worked;
+ * Returns:	SCHEDULE_OCCURRED - schedule occurred while the function worked;
  *	        CARRY_ON - schedule didn't occur while the function worked;
  */
 static int  get_parents (struct tree_balance * p_s_tb, int n_h)
@@ -1340,7 +1340,7 @@
  *	h	current level of the node;
  *	inum	item number in S[h];
  *	mode	i - insert, p - paste;
- * Returns:	1 - schedule occured; 
+ * Returns:	1 - schedule occurred; 
  *	        0 - balancing for higher levels needed;
  *	       -1 - no balancing for higher levels needed;
  *	       -2 - no disk space.
@@ -1678,7 +1678,7 @@
  *	h	current level of the node;
  *	inum	item number in S[h];
  *	mode	i - insert, p - paste;
- * Returns:	1 - schedule occured; 
+ * Returns:	1 - schedule occurred; 
  *	        0 - balancing for higher levels needed;
  *	       -1 - no balancing for higher levels needed;
  *	       -2 - no disk space.
@@ -1848,7 +1848,7 @@
  *	h	current level of the node;
  *	inum	item number in S[h];
  *	mode	i - insert, p - paste;
- * Returns:	1 - schedule occured; 
+ * Returns:	1 - schedule occurred; 
  *	        0 - balancing for higher levels needed;
  *	       -1 - no balancing for higher levels needed;
  *	       -2 - no disk space.
@@ -1951,7 +1951,7 @@
  *	h	current level of the node;
  *	inum	item number in S[h];
  *	mode	d - delete, c - cut.
- * Returns:	1 - schedule occured; 
+ * Returns:	1 - schedule occurred; 
  *	        0 - balancing for higher levels needed;
  *	       -1 - no balancing for higher levels needed;
  *	       -2 - no disk space.
@@ -1985,7 +1985,7 @@
  *	h	current level of the node;
  *	inum	item number in S[h];
  *	mode	i - insert, p - paste, d - delete, c - cut.
- * Returns:	1 - schedule occured; 
+ * Returns:	1 - schedule occurred; 
  *	        0 - balancing for higher levels needed;
  *	       -1 - no balancing for higher levels needed;
  *	       -2 - no disk space.
@@ -2075,7 +2075,7 @@
 /* Using lnum[n_h] and rnum[n_h] we should determine what neighbors
  * of S[n_h] we
  * need in order to balance S[n_h], and get them if necessary.
- * Returns:	SCHEDULE_OCCURRED - schedule occured while the function worked;
+ * Returns:	SCHEDULE_OCCURRED - schedule occurred while the function worked;
  *	        CARRY_ON - schedule didn't occur while the function worked;
  */
 static int  get_neighbors(
diff -urN linux-2.4.3-pre1/fs/reiserfs/inode.c linux/fs/reiserfs/inode.c
--- linux-2.4.3-pre1/fs/reiserfs/inode.c	Mon Dec  3 22:29:33 2001
+++ linux/fs/reiserfs/inode.c	Mon Dec  3 22:29:50 2001
@@ -1261,7 +1261,7 @@
     retval = search_item (sb, &key, path);
     if (retval == IO_ERROR) {
 	reiserfs_warning ("vs-13080: reiserfs_new_directory: "
-			  "i/o failure occured creating new directory\n");
+			  "i/o failure occurred creating new directory\n");
 	return -EIO;
     }
     if (retval == ITEM_FOUND) {
@@ -1297,7 +1297,7 @@
     retval = search_item (sb, &key, path);
     if (retval == IO_ERROR) {
 	reiserfs_warning ("vs-13080: reiserfs_new_symlinik: "
-			  "i/o failure occured creating new symlink\n");
+			  "i/o failure occurred creating new symlink\n");
 	return -EIO;
     }
     if (retval == ITEM_FOUND) {
diff -urN linux-2.4.3-pre1/fs/reiserfs/stree.c linux/fs/reiserfs/stree.c
--- linux-2.4.3-pre1/fs/reiserfs/stree.c	Mon Dec  3 22:29:33 2001
+++ linux/fs/reiserfs/stree.c	Mon Dec  3 22:29:50 2001
@@ -734,7 +734,7 @@
 	    return IO_ERROR;
 	}
 
-	/* It is possible that schedule occured. We must check whether the key
+	/* It is possible that schedule occurred. We must check whether the key
 	   to search is still in the tree rooted from the current buffer. If
 	   not then repeat search from the root. */
 	if ( fs_changed (fs_gen, p_s_sb) && 
@@ -1509,7 +1509,7 @@
 	retval = search_item (th->t_super, &cpu_key, &path);
 	if (retval == IO_ERROR) {
 	    reiserfs_warning ("vs-: reiserfs_delete_solid_item: "
-			      "i/o failure occured trying to delete %K\n", &cpu_key);
+			      "i/o failure occurred trying to delete %K\n", &cpu_key);
 	    break;
 	}
 	if (retval != ITEM_FOUND) {
@@ -1857,7 +1857,7 @@
     retval = search_for_position_by_key(p_s_inode->i_sb, &s_item_key, &s_search_path);
     if (retval == IO_ERROR) {
 	reiserfs_warning ("vs-5657: reiserfs_do_truncate: "
-			  "i/o failure occured trying to truncate %K\n", &s_item_key);
+			  "i/o failure occurred trying to truncate %K\n", &s_item_key);
 	return;
     }
     if (retval == POSITION_FOUND || retval == FILE_NOT_FOUND) {
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/bits.h linux/include/asm-arm/arch-integrator/bits.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/bits.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/bits.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,61 @@
+/*
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* DO NOT EDIT!! - this file automatically generated
+ *                 from .s file by awk -f s2h.awk
+ */
+/*  Bit field defintions
+ *  Copyright (C) ARM Limited 1998. All rights reserved.
+ */
+
+#ifndef __bits_h
+#define __bits_h                        1
+
+#define BIT0                            0x00000001
+#define BIT1                            0x00000002
+#define BIT2                            0x00000004
+#define BIT3                            0x00000008
+#define BIT4                            0x00000010
+#define BIT5                            0x00000020
+#define BIT6                            0x00000040
+#define BIT7                            0x00000080
+#define BIT8                            0x00000100
+#define BIT9                            0x00000200
+#define BIT10                           0x00000400
+#define BIT11                           0x00000800
+#define BIT12                           0x00001000
+#define BIT13                           0x00002000
+#define BIT14                           0x00004000
+#define BIT15                           0x00008000
+#define BIT16                           0x00010000
+#define BIT17                           0x00020000
+#define BIT18                           0x00040000
+#define BIT19                           0x00080000
+#define BIT20                           0x00100000
+#define BIT21                           0x00200000
+#define BIT22                           0x00400000
+#define BIT23                           0x00800000
+#define BIT24                           0x01000000
+#define BIT25                           0x02000000
+#define BIT26                           0x04000000
+#define BIT27                           0x08000000
+#define BIT28                           0x10000000
+#define BIT29                           0x20000000
+#define BIT30                           0x40000000
+#define BIT31                           0x80000000
+
+#endif
+
+/*         END */
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/dma.h linux/include/asm-arm/arch-integrator/dma.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/dma.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/dma.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,28 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/dma.h
+ *
+ *  Copyright (C) 1997,1998 Russell King
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_DMA_H
+#define __ASM_ARCH_DMA_H
+
+#define MAX_DMA_ADDRESS		0xffffffff
+
+#define MAX_DMA_CHANNELS	0
+
+#endif /* _ASM_ARCH_DMA_H */
+
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/hardware.h linux/include/asm-arm/arch-integrator/hardware.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/hardware.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/hardware.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,57 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/hardware.h
+ *
+ *  This file contains the hardware definitions of the Integrator.
+ *
+ *  Copyright (C) 1999 ARM Limited.
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_HARDWARE_H
+#define __ASM_ARCH_HARDWARE_H
+
+#include <asm/arch/sizes.h>
+#include <asm/arch/platform.h>
+
+/*
+ * Where in virtual memory the IO devices (timers, system controllers
+ * and so on)
+ */
+#define IO_BASE			0xF0000000                 // VA of IO 
+#define IO_SIZE			0x0B000000                 // How much?
+#define IO_START		INTEGRATOR_HDR_BASE        // PA of IO
+
+/*
+ * Similar to above, but for PCI addresses (memory, IO, Config and the
+ * V3 chip itself).  WARNING: this has to mirror definitions in platform.h
+ */
+#define PCI_MEMORY_VADDR        0xe8000000
+#define PCI_CONFIG_VADDR        0xec000000
+#define PCI_V3_VADDR            0xed000000
+#define PCI_IO_VADDR            0xee000000
+
+#define PCIO_BASE		PCI_IO_VADDR
+#define PCIMEM_BASE		PCI_MEMORY_VADDR
+
+/* macro to get at IO space when running virtually */
+#define IO_ADDRESS(x) (((x) >> 4) + IO_BASE) 
+
+#define pcibios_assign_all_busses()	1
+
+#define PCIBIOS_MIN_IO		0x6000
+#define PCIBIOS_MIN_MEM 	0x00100000
+
+#endif
+
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/io.h linux/include/asm-arm/arch-integrator/io.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/io.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/io.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,46 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/io.h
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARM_ARCH_IO_H
+#define __ASM_ARM_ARCH_IO_H
+
+#define IO_SPACE_LIMIT 0xffff
+
+#define __io(a)			(PCI_IO_VADDR + (a))
+#define __mem_pci(a)		((unsigned long)(a))
+#define __mem_isa(a)		(PCI_MEMORY_VADDR + (unsigned long)(a))
+
+/*
+ * Generic virtual read/write
+ */
+#define __arch_getw(a)		(*(volatile unsigned short *)(a))
+#define __arch_putw(v,a)	(*(volatile unsigned short *)(a) = (v))
+
+/*
+ * Validate the pci memory address for ioremap.
+ */
+#define iomem_valid_addr(iomem,size)	\
+	((iomem) > 0 && (iomem) + (size) <= 0x20000000)
+
+/*
+ * Convert PCI memory space to a CPU physical address
+ */
+#define iomem_to_phys(iomem)	((iomem) + PHYS_PCI_MEM_BASE)
+
+#endif
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/irq.h linux/include/asm-arm/arch-integrator/irq.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/irq.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/irq.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,20 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/irq.h
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#define fixup_irq(i)	(i)
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/irqs.h linux/include/asm-arm/arch-integrator/irqs.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/irqs.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/irqs.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,134 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/irqs.h
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/* Use the integrator definitions */
+#include <asm/arch/platform.h>
+
+/* 
+ *  IRQ interrupts definitions are the same the INT definitions
+ *  held within platform.h
+ */
+#define IRQ_SOFTINT                     INT_SOFTINT
+#define IRQ_UARTINT0                    INT_UARTINT0
+#define IRQ_UARTINT1                    INT_UARTINT1
+#define IRQ_KMIINT0                     INT_KMIINT0
+#define IRQ_KMIINT1                     INT_KMIINT1
+#define IRQ_TIMERINT0                   INT_TIMERINT0
+#define IRQ_TIMERINT1                   INT_TIMERINT1
+#define IRQ_TIMERINT2                   INT_TIMERINT2
+#define IRQ_RTCINT                      INT_RTCINT
+#define IRQ_EXPINT0                     INT_EXPINT0
+#define IRQ_EXPINT1                     INT_EXPINT1
+#define IRQ_EXPINT2                     INT_EXPINT2
+#define IRQ_EXPINT3                     INT_EXPINT3
+#define IRQ_PCIINT0                     INT_PCIINT0
+#define IRQ_PCIINT1                     INT_PCIINT1
+#define IRQ_PCIINT2                     INT_PCIINT2
+#define IRQ_PCIINT3                     INT_PCIINT3
+#define IRQ_V3INT                       INT_V3INT
+#define IRQ_CPINT0                      INT_CPINT0
+#define IRQ_CPINT1                      INT_CPINT1
+#define IRQ_LBUSTIMEOUT                 INT_LBUSTIMEOUT
+#define IRQ_APCINT                      INT_APCINT
+
+#define IRQMASK_SOFTINT                 INTMASK_SOFTINT
+#define IRQMASK_UARTINT0                INTMASK_UARTINT0
+#define IRQMASK_UARTINT1                INTMASK_UARTINT1
+#define IRQMASK_KMIINT0                 INTMASK_KMIINT0
+#define IRQMASK_KMIINT1                 INTMASK_KMIINT1
+#define IRQMASK_TIMERINT0               INTMASK_TIMERINT0
+#define IRQMASK_TIMERINT1               INTMASK_TIMERINT1
+#define IRQMASK_TIMERINT2               INTMASK_TIMERINT2
+#define IRQMASK_RTCINT                  INTMASK_RTCINT
+#define IRQMASK_EXPINT0                 INTMASK_EXPINT0
+#define IRQMASK_EXPINT1                 INTMASK_EXPINT1
+#define IRQMASK_EXPINT2                 INTMASK_EXPINT2
+#define IRQMASK_EXPINT3                 INTMASK_EXPINT3
+#define IRQMASK_PCIINT0                 INTMASK_PCIINT0
+#define IRQMASK_PCIINT1                 INTMASK_PCIINT1
+#define IRQMASK_PCIINT2                 INTMASK_PCIINT2
+#define IRQMASK_PCIINT3                 INTMASK_PCIINT3
+#define IRQMASK_V3INT                   INTMASK_V3INT
+#define IRQMASK_CPINT0                  INTMASK_CPINT0
+#define IRQMASK_CPINT1                  INTMASK_CPINT1
+#define IRQMASK_LBUSTIMEOUT             INTMASK_LBUSTIMEOUT
+#define IRQMASK_APCINT                  INTMASK_APCINT
+
+/* 
+ *  FIQ interrupts definitions are the same the INT definitions.
+ */
+#define FIQ_SOFTINT                     INT_SOFTINT
+#define FIQ_UARTINT0                    INT_UARTINT0
+#define FIQ_UARTINT1                    INT_UARTINT1
+#define FIQ_KMIINT0                     INT_KMIINT0
+#define FIQ_KMIINT1                     INT_KMIINT1
+#define FIQ_TIMERINT0                   INT_TIMERINT0
+#define FIQ_TIMERINT1                   INT_TIMERINT1
+#define FIQ_TIMERINT2                   INT_TIMERINT2
+#define FIQ_RTCINT                      INT_RTCINT
+#define FIQ_EXPINT0                     INT_EXPINT0
+#define FIQ_EXPINT1                     INT_EXPINT1
+#define FIQ_EXPINT2                     INT_EXPINT2
+#define FIQ_EXPINT3                     INT_EXPINT3
+#define FIQ_PCIINT0                     INT_PCIINT0
+#define FIQ_PCIINT1                     INT_PCIINT1
+#define FIQ_PCIINT2                     INT_PCIINT2
+#define FIQ_PCIINT3                     INT_PCIINT3
+#define FIQ_V3INT                       INT_V3INT
+#define FIQ_CPINT0                      INT_CPINT0
+#define FIQ_CPINT1                      INT_CPINT1
+#define FIQ_LBUSTIMEOUT                 INT_LBUSTIMEOUT
+#define FIQ_APCINT                      INT_APCINT
+
+#define FIQMASK_SOFTINT                 INTMASK_SOFTINT
+#define FIQMASK_UARTINT0                INTMASK_UARTINT0
+#define FIQMASK_UARTINT1                INTMASK_UARTINT1
+#define FIQMASK_KMIINT0                 INTMASK_KMIINT0
+#define FIQMASK_KMIINT1                 INTMASK_KMIINT1
+#define FIQMASK_TIMERINT0               INTMASK_TIMERINT0
+#define FIQMASK_TIMERINT1               INTMASK_TIMERINT1
+#define FIQMASK_TIMERINT2               INTMASK_TIMERINT2
+#define FIQMASK_RTCINT                  INTMASK_RTCINT
+#define FIQMASK_EXPINT0                 INTMASK_EXPINT0
+#define FIQMASK_EXPINT1                 INTMASK_EXPINT1
+#define FIQMASK_EXPINT2                 INTMASK_EXPINT2
+#define FIQMASK_EXPINT3                 INTMASK_EXPINT3
+#define FIQMASK_PCIINT0                 INTMASK_PCIINT0
+#define FIQMASK_PCIINT1                 INTMASK_PCIINT1
+#define FIQMASK_PCIINT2                 INTMASK_PCIINT2
+#define FIQMASK_PCIINT3                 INTMASK_PCIINT3
+#define FIQMASK_V3INT                   INTMASK_V3INT
+#define FIQMASK_CPINT0                  INTMASK_CPINT0
+#define FIQMASK_CPINT1                  INTMASK_CPINT1
+#define FIQMASK_LBUSTIMEOUT             INTMASK_LBUSTIMEOUT
+#define FIQMASK_APCINT                  INTMASK_APCINT
+
+/* 
+ *  Misc. interrupt definitions
+ */
+#define IRQ_KEYBDINT                    INT_KMIINT0
+#define IRQ_MOUSEINT                    INT_KMIINT1
+
+#define IRQMASK_KEYBDINT                INTMASK_KMIINT0
+#define IRQMASK_MOUSEINT                INTMASK_KMIINT1
+
+#define NR_IRQS                         (MAXIRQNUM + 1)
+
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/keyboard.h linux/include/asm-arm/arch-integrator/keyboard.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/keyboard.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/keyboard.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,45 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/keyboard.h
+ *
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *  Keyboard driver definitions for the Integrator architecture
+ */
+#include <asm/irq.h>
+
+#define NR_SCANCODES 128
+
+extern unsigned char kmi_kbd_sysrq_xlate[NR_SCANCODES];
+
+extern int kmi_kbd_setkeycode(u_int scancode, u_int keycode);
+extern int kmi_kbd_getkeycode(u_int scancode);
+extern int kmi_kbd_translate(u_char scancode, u_char *keycode, char raw_mode);
+extern char kmi_kbd_unexpected_up(u_char keycode);
+extern void kmi_kbd_leds(u_char leds);
+extern int kmi_kbd_init(void);
+
+#define kbd_setkeycode(sc,kc)		kmi_kbd_setkeycode(sc,kc)
+#define kbd_getkeycode(sc)		kmi_kbd_getkeycode(sc)
+
+#define kbd_translate(sc, kcp, rm)	kmi_kbd_translate(sc,kcp,rm)
+#define kbd_unexpected_up(kc)		kmi_kbd_unexpected_up(kc)
+#define kbd_leds(leds)			kmi_kbd_leds(leds)
+#define kbd_init_hw()			kmi_kbd_init()
+#define kbd_sysrq_xlate			kmi_kbd_sysrq_xlate
+#define kbd_disable_irq()		disable_irq(IRQ_KMIINT0)
+#define kbd_enable_irq()		enable_irq(IRQ_KMIINT0)
+
+#define SYSRQ_KEY			0x54
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/memory.h linux/include/asm-arm/arch-integrator/memory.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/memory.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/memory.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,61 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/mmu.h
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_MMU_H
+#define __ASM_ARCH_MMU_H
+
+/*
+ * Task size: 3GB
+ */
+#define TASK_SIZE	(0xc0000000UL)
+#define TASK_SIZE_26	(0x04000000UL)
+
+/*
+ * This decides where the kernel will search for a free chunk of vm
+ * space during mmap's.
+ */
+#define TASK_UNMAPPED_BASE (TASK_SIZE / 3)
+
+/*
+ * Page offset: 3GB
+ */
+#define PAGE_OFFSET	(0xc0000000UL)
+#define PHYS_OFFSET	(0x00000000UL)
+
+/*
+ * On integrator, the dram is contiguous
+ */
+#define __virt_to_phys__is_a_macro
+#define __virt_to_phys(vpage) ((vpage) - PAGE_OFFSET)
+#define __phys_to_virt__is_a_macro
+#define __phys_to_virt(ppage) ((ppage) + PAGE_OFFSET)
+
+/*
+ * Virtual view <-> DMA view memory address translations
+ * virt_to_bus: Used to translate the virtual address to an
+ *              address suitable to be passed to set_dma_addr
+ * bus_to_virt: Used to convert an address for DMA operations
+ *              to an address that the kernel can use.
+ */
+#define __virt_to_bus__is_a_macro
+#define __virt_to_bus(x)	(x - PAGE_OFFSET + INTEGRATOR_HDR0_SDRAM_BASE)
+#define __bus_to_virt__is_a_macro
+#define __bus_to_virt(x)	(x - INTEGRATOR_HDR0_SDRAM_BASE + PAGE_OFFSET)
+
+#endif
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/param.h linux/include/asm-arm/arch-integrator/param.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/param.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/param.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,21 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/param.h
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define HZ 100
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/platform.h linux/include/asm-arm/arch-integrator/platform.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/platform.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/platform.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,544 @@
+/*
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* DO NOT EDIT!! - this file automatically generated
+ *                 from .s file by awk -f s2h.awk
+ */
+/**************************************************************************
+ * * Copyright İ ARM Limited 1998.  All rights reserved.
+ * ***********************************************************************/
+/* ************************************************************************
+ * 
+ *   Integrator address map
+ * 
+ * 	NOTE: This is a multi-hosted header file for use with uHAL and
+ * 	      supported debuggers.
+ * 
+ * 	$Id: platform.s,v 1.32 2000/02/18 10:51:39 asims Exp $
+ * 
+ * ***********************************************************************/
+
+#ifndef __address_h
+#define __address_h                     1
+
+/* ========================================================================
+ *  Integrator definitions
+ * ========================================================================
+ * ------------------------------------------------------------------------
+ *  Memory definitions
+ * ------------------------------------------------------------------------
+ *  Integrator memory map
+ * 
+ */
+#define INTEGRATOR_BOOT_ROM_LO          0x00000000
+#define INTEGRATOR_BOOT_ROM_HI          0x20000000
+#define INTEGRATOR_BOOT_ROM_BASE        INTEGRATOR_BOOT_ROM_HI	 /*  Normal position */
+#define INTEGRATOR_BOOT_ROM_SIZE        SZ_512K
+
+/* 
+ *  New Core Modules have different amounts of SSRAM, the amount of SSRAM
+ *  fitted can be found in HDR_STAT.
+ * 
+ *  The symbol INTEGRATOR_SSRAM_SIZE is kept, however this now refers to
+ *  the minimum amount of SSRAM fitted on any core module.
+ * 
+ *  New Core Modules also alias the SSRAM.
+ * 
+ */
+#define INTEGRATOR_SSRAM_BASE           0x00000000
+#define INTEGRATOR_SSRAM_ALIAS_BASE     0x10800000
+#define INTEGRATOR_SSRAM_SIZE           SZ_256K
+
+#define INTEGRATOR_FLASH_BASE           0x24000000
+#define INTEGRATOR_FLASH_SIZE           SZ_32M
+
+#define INTEGRATOR_MBRD_SSRAM_BASE      0x28000000
+#define INTEGRATOR_MBRD_SSRAM_SIZE      SZ_512K
+
+/* 
+ *  SDRAM is a SIMM therefore the size is not known.
+ * 
+ */
+#define INTEGRATOR_SDRAM_BASE           0x00040000
+
+#define INTEGRATOR_SDRAM_ALIAS_BASE     0x80000000
+#define INTEGRATOR_HDR0_SDRAM_BASE      0x80000000
+#define INTEGRATOR_HDR1_SDRAM_BASE      0x90000000
+#define INTEGRATOR_HDR2_SDRAM_BASE      0xA0000000
+#define INTEGRATOR_HDR3_SDRAM_BASE      0xB0000000
+
+/* 
+ *  Logic expansion modules
+ * 
+ */
+#define INTEGRATOR_LOGIC_MODULES_BASE   0xC0000000
+#define INTEGRATOR_LOGIC_MODULE0_BASE   0xC0000000
+#define INTEGRATOR_LOGIC_MODULE1_BASE   0xD0000000
+#define INTEGRATOR_LOGIC_MODULE2_BASE   0xE0000000
+#define INTEGRATOR_LOGIC_MODULE3_BASE   0xF0000000
+
+/* ------------------------------------------------------------------------
+ *  Integrator header card registers
+ * ------------------------------------------------------------------------
+ * 
+ */
+#define INTEGRATOR_HDR_ID_OFFSET        0x00
+#define INTEGRATOR_HDR_PROC_OFFSET      0x04
+#define INTEGRATOR_HDR_OSC_OFFSET       0x08
+#define INTEGRATOR_HDR_CTRL_OFFSET      0x0C
+#define INTEGRATOR_HDR_STAT_OFFSET      0x10
+#define INTEGRATOR_HDR_LOCK_OFFSET      0x14
+#define INTEGRATOR_HDR_SDRAM_OFFSET     0x20
+#define INTEGRATOR_HDR_INIT_OFFSET      0x24	 /*  CM9x6 */
+#define INTEGRATOR_HDR_IC_OFFSET        0x40
+#define INTEGRATOR_HDR_SPDBASE_OFFSET   0x100
+#define INTEGRATOR_HDR_SPDTOP_OFFSET    0x200
+
+#define INTEGRATOR_HDR_BASE             0x10000000
+#define INTEGRATOR_HDR_ID               (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_ID_OFFSET)
+#define INTEGRATOR_HDR_PROC             (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_PROC_OFFSET)
+#define INTEGRATOR_HDR_OSC              (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_OSC_OFFSET)
+#define INTEGRATOR_HDR_CTRL             (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_CTRL_OFFSET)
+#define INTEGRATOR_HDR_STAT             (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_STAT_OFFSET)
+#define INTEGRATOR_HDR_LOCK             (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_LOCK_OFFSET)
+#define INTEGRATOR_HDR_SDRAM            (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_SDRAM_OFFSET)
+#define INTEGRATOR_HDR_INIT             (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_INIT_OFFSET)
+#define INTEGRATOR_HDR_IC               (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_IC_OFFSET)
+#define INTEGRATOR_HDR_SPDBASE          (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_SPDBASE_OFFSET)
+#define INTEGRATOR_HDR_SPDTOP           (INTEGRATOR_HDR_BASE + INTEGRATOR_HDR_SPDTOP_OFFSET)
+
+#define INTEGRATOR_HDR_CTRL_LED         0x01
+#define INTEGRATOR_HDR_CTRL_MBRD_DETECH 0x02
+#define INTEGRATOR_HDR_CTRL_REMAP       0x04
+#define INTEGRATOR_HDR_CTRL_RESET       0x08
+#define INTEGRATOR_HDR_CTRL_HIGHVECTORS 0x10
+#define INTEGRATOR_HDR_CTRL_BIG_ENDIAN  0x20
+#define INTEGRATOR_HDR_CTRL_FASTBUS     0x40
+#define INTEGRATOR_HDR_CTRL_SYNC        0x80
+
+#define INTEGRATOR_HDR_OSC_CORE_10MHz   0x102
+#define INTEGRATOR_HDR_OSC_CORE_15MHz   0x107
+#define INTEGRATOR_HDR_OSC_CORE_20MHz   0x10C
+#define INTEGRATOR_HDR_OSC_CORE_25MHz   0x111
+#define INTEGRATOR_HDR_OSC_CORE_30MHz   0x116
+#define INTEGRATOR_HDR_OSC_CORE_35MHz   0x11B
+#define INTEGRATOR_HDR_OSC_CORE_40MHz   0x120
+#define INTEGRATOR_HDR_OSC_CORE_45MHz   0x125
+#define INTEGRATOR_HDR_OSC_CORE_50MHz   0x12A
+#define INTEGRATOR_HDR_OSC_CORE_55MHz   0x12F
+#define INTEGRATOR_HDR_OSC_CORE_60MHz   0x134
+#define INTEGRATOR_HDR_OSC_CORE_65MHz   0x139
+#define INTEGRATOR_HDR_OSC_CORE_70MHz   0x13E
+#define INTEGRATOR_HDR_OSC_CORE_75MHz   0x143
+#define INTEGRATOR_HDR_OSC_CORE_80MHz   0x148
+#define INTEGRATOR_HDR_OSC_CORE_85MHz   0x14D
+#define INTEGRATOR_HDR_OSC_CORE_90MHz   0x152
+#define INTEGRATOR_HDR_OSC_CORE_95MHz   0x157
+#define INTEGRATOR_HDR_OSC_CORE_100MHz  0x15C
+#define INTEGRATOR_HDR_OSC_CORE_105MHz  0x161
+#define INTEGRATOR_HDR_OSC_CORE_110MHz  0x166
+#define INTEGRATOR_HDR_OSC_CORE_115MHz  0x16B
+#define INTEGRATOR_HDR_OSC_CORE_120MHz  0x170
+#define INTEGRATOR_HDR_OSC_CORE_125MHz  0x175
+#define INTEGRATOR_HDR_OSC_CORE_130MHz  0x17A
+#define INTEGRATOR_HDR_OSC_CORE_135MHz  0x17F
+#define INTEGRATOR_HDR_OSC_CORE_140MHz  0x184
+#define INTEGRATOR_HDR_OSC_CORE_145MHz  0x189
+#define INTEGRATOR_HDR_OSC_CORE_150MHz  0x18E
+#define INTEGRATOR_HDR_OSC_CORE_155MHz  0x193
+#define INTEGRATOR_HDR_OSC_CORE_160MHz  0x198
+#define INTEGRATOR_HDR_OSC_CORE_MASK    0x7FF
+
+#define INTEGRATOR_HDR_OSC_MEM_10MHz    0x10C000
+#define INTEGRATOR_HDR_OSC_MEM_15MHz    0x116000
+#define INTEGRATOR_HDR_OSC_MEM_20MHz    0x120000
+#define INTEGRATOR_HDR_OSC_MEM_25MHz    0x12A000
+#define INTEGRATOR_HDR_OSC_MEM_30MHz    0x134000
+#define INTEGRATOR_HDR_OSC_MEM_33MHz    0x13A000
+#define INTEGRATOR_HDR_OSC_MEM_40MHz    0x148000
+#define INTEGRATOR_HDR_OSC_MEM_50MHz    0x15C000
+#define INTEGRATOR_HDR_OSC_MEM_60MHz    0x170000
+#define INTEGRATOR_HDR_OSC_MEM_66MHz    0x17C000
+#define INTEGRATOR_HDR_OSC_MEM_MASK     0x7FF000
+
+#define INTEGRATOR_HDR_OSC_BUS_MODE_CM7x0  0x0
+#define INTEGRATOR_HDR_OSC_BUS_MODE_CM9x0  0x0800000
+#define INTEGRATOR_HDR_OSC_BUS_MODE_CM9x6  0x1000000
+#define INTEGRATOR_HDR_OSC_BUS_MODE_CM10x00  0x1800000
+#define INTEGRATOR_HDR_OSC_BUS_MODE_MASK  0x1800000
+
+#define INTEGRATOR_HDR_SDRAM_SPD_OK     (1 << 5)
+
+
+/* ------------------------------------------------------------------------
+ *  Integrator system registers
+ * ------------------------------------------------------------------------
+ * 
+ */
+
+/* 
+ *  System Controller
+ * 
+ */
+#define INTEGRATOR_SC_ID_OFFSET         0x00
+#define INTEGRATOR_SC_OSC_OFFSET        0x04
+#define INTEGRATOR_SC_CTRLS_OFFSET      0x08
+#define INTEGRATOR_SC_CTRLC_OFFSET      0x0C
+#define INTEGRATOR_SC_DEC_OFFSET        0x10
+#define INTEGRATOR_SC_ARB_OFFSET        0x14
+#define INTEGRATOR_SC_PCIENABLE_OFFSET  0x18
+#define INTEGRATOR_SC_LOCK_OFFSET       0x1C
+
+#define INTEGRATOR_SC_BASE              0x11000000
+#define INTEGRATOR_SC_ID                (INTEGRATOR_SC_BASE + INTEGRATOR_SC_ID_OFFSET)
+#define INTEGRATOR_SC_OSC               (INTEGRATOR_SC_BASE + INTEGRATOR_SC_OSC_OFFSET)
+#define INTEGRATOR_SC_CTRLS             (INTEGRATOR_SC_BASE + INTEGRATOR_SC_CTRLS_OFFSET)
+#define INTEGRATOR_SC_CTRLC             (INTEGRATOR_SC_BASE + INTEGRATOR_SC_CTRLC_OFFSET)
+#define INTEGRATOR_SC_DEC               (INTEGRATOR_SC_BASE + INTEGRATOR_SC_DEC_OFFSET)
+#define INTEGRATOR_SC_ARB               (INTEGRATOR_SC_BASE + INTEGRATOR_SC_ARB_OFFSET)
+#define INTEGRATOR_SC_PCIENABLE         (INTEGRATOR_SC_BASE + INTEGRATOR_SC_PCIENABLE_OFFSET)
+#define INTEGRATOR_SC_LOCK              (INTEGRATOR_SC_BASE + INTEGRATOR_SC_LOCK_OFFSET)
+
+#define INTEGRATOR_SC_OSC_SYS_10MHz     0x20
+#define INTEGRATOR_SC_OSC_SYS_15MHz     0x34
+#define INTEGRATOR_SC_OSC_SYS_20MHz     0x48
+#define INTEGRATOR_SC_OSC_SYS_25MHz     0x5C
+#define INTEGRATOR_SC_OSC_SYS_33MHz     0x7C
+#define INTEGRATOR_SC_OSC_SYS_MASK      0xFF
+
+#define INTEGRATOR_SC_OSC_PCI_25MHz     0x100
+#define INTEGRATOR_SC_OSC_PCI_33MHz     0x0
+#define INTEGRATOR_SC_OSC_PCI_MASK      0x100
+
+#define INTEGRATOR_SC_CTRL_SOFTRST      (1 << 0)
+#define INTEGRATOR_SC_CTRL_nFLVPPEN     (1 << 1)
+#define INTEGRATOR_SC_CTRL_nFLWP        (1 << 2)
+#define INTEGRATOR_SC_CTRL_URTS0        (1 << 4)
+#define INTEGRATOR_SC_CTRL_UDTR0        (1 << 5)
+#define INTEGRATOR_SC_CTRL_URTS1        (1 << 6)
+#define INTEGRATOR_SC_CTRL_UDTR1        (1 << 7)
+
+/* 
+ *  External Bus Interface
+ * 
+ */
+#define INTEGRATOR_EBI_BASE             0x12000000 
+
+#define INTEGRATOR_EBI_CSR0_OFFSET      0x00
+#define INTEGRATOR_EBI_CSR1_OFFSET      0x04
+#define INTEGRATOR_EBI_CSR2_OFFSET      0x08
+#define INTEGRATOR_EBI_CSR3_OFFSET      0x0C
+#define INTEGRATOR_EBI_LOCK_OFFSET      0x20
+
+#define INTEGRATOR_EBI_CSR0             (INTEGRATOR_EBI_BASE + INTEGRATOR_EBI_CSR0_OFFSET)
+#define INTEGRATOR_EBI_CSR1             (INTEGRATOR_EBI_BASE + INTEGRATOR_EBI_CSR1_OFFSET)
+#define INTEGRATOR_EBI_CSR2             (INTEGRATOR_EBI_BASE + INTEGRATOR_EBI_CSR2_OFFSET)
+#define INTEGRATOR_EBI_CSR3             (INTEGRATOR_EBI_BASE + INTEGRATOR_EBI_CSR3_OFFSET)
+#define INTEGRATOR_EBI_LOCK             (INTEGRATOR_EBI_BASE + INTEGRATOR_EBI_LOCK_OFFSET)
+
+#define INTEGRATOR_EBI_8_BIT            0x00
+#define INTEGRATOR_EBI_16_BIT           0x01
+#define INTEGRATOR_EBI_32_BIT           0x02
+#define INTEGRATOR_EBI_WRITE_ENABLE     0x04
+#define INTEGRATOR_EBI_SYNC             0x08
+#define INTEGRATOR_EBI_WS_2             0x00
+#define INTEGRATOR_EBI_WS_3             0x10
+#define INTEGRATOR_EBI_WS_4             0x20
+#define INTEGRATOR_EBI_WS_5             0x30
+#define INTEGRATOR_EBI_WS_6             0x40
+#define INTEGRATOR_EBI_WS_7             0x50
+#define INTEGRATOR_EBI_WS_8             0x60
+#define INTEGRATOR_EBI_WS_9             0x70
+#define INTEGRATOR_EBI_WS_10            0x80
+#define INTEGRATOR_EBI_WS_11            0x90
+#define INTEGRATOR_EBI_WS_12            0xA0
+#define INTEGRATOR_EBI_WS_13            0xB0
+#define INTEGRATOR_EBI_WS_14            0xC0
+#define INTEGRATOR_EBI_WS_15            0xD0
+#define INTEGRATOR_EBI_WS_16            0xE0
+#define INTEGRATOR_EBI_WS_17            0xF0
+
+
+#define INTEGRATOR_CT_BASE              0x13000000	 /*  Counter/Timers */
+#define INTEGRATOR_IC_BASE              0x14000000	 /*  Interrupt Controller */
+#define INTEGRATOR_RTC_BASE             0x15000000	 /*  Real Time Clock */
+#define INTEGRATOR_UART0_BASE           0x16000000	 /*  UART 0 */
+#define INTEGRATOR_UART1_BASE           0x17000000	 /*  UART 1 */
+#define INTEGRATOR_KBD_BASE             0x18000000	 /*  Keyboard */
+#define INTEGRATOR_MOUSE_BASE           0x19000000	 /*  Mouse */
+
+/* 
+ *  LED's & Switches
+ * 
+ */
+#define INTEGRATOR_DBG_ALPHA_OFFSET     0x00
+#define INTEGRATOR_DBG_LEDS_OFFSET      0x04
+#define INTEGRATOR_DBG_SWITCH_OFFSET    0x08
+
+#define INTEGRATOR_DBG_BASE             0x1A000000
+#define INTEGRATOR_DBG_ALPHA            (INTEGRATOR_DBG_BASE + INTEGRATOR_DBG_ALPHA_OFFSET)
+#define INTEGRATOR_DBG_LEDS             (INTEGRATOR_DBG_BASE + INTEGRATOR_DBG_LEDS_OFFSET)
+#define INTEGRATOR_DBG_SWITCH           (INTEGRATOR_DBG_BASE + INTEGRATOR_DBG_SWITCH_OFFSET)
+
+
+#define INTEGRATOR_GPIO_BASE            0x1B000000	 /*  GPIO */
+
+/* ------------------------------------------------------------------------
+ *  KMI keyboard/mouse definitions
+ * ------------------------------------------------------------------------
+ */
+/* PS2 Keyboard interface */
+#define KMI0_BASE                       INTEGRATOR_KBD_BASE		
+
+/* PS2 Mouse interface */
+#define KMI1_BASE                       INTEGRATOR_MOUSE_BASE
+
+/* KMI definitions are now in include/asm-arm/hardware/amba_kmi.h -- rmk */
+
+/* ------------------------------------------------------------------------
+ *  Where in the memory map does PCI live?
+ * ------------------------------------------------------------------------
+ *  This represents a fairly liberal usage of address space.  Even though
+ *  the V3 only has two windows (therefore we need to map stuff on the fly),
+ *  we maintain the same addresses, even if they're not mapped.
+ * 
+ */
+#define PHYS_PCI_MEM_BASE               0x40000000   /* 512M to xxx */
+/*  unused 256M from A0000000-AFFFFFFF might be used for I2O ???
+ */
+#define PHYS_PCI_IO_BASE                0x60000000   /* 16M to xxx */
+/*  unused (128-16)M from B1000000-B7FFFFFF
+ */
+#define PHYS_PCI_CONFIG_BASE            0x61000000   /* 16M to xxx */
+/*  unused ((128-16)M - 64K) from XXX
+ */
+#define PHYS_PCI_V3_BASE                0x62000000
+
+#define PCI_DRAMSIZE                    INTEGRATOR_SSRAM_SIZE	
+
+/* 'export' these to UHAL */
+#define UHAL_PCI_IO                     PCI_IO_BASE
+#define UHAL_PCI_MEM                    PCI_MEM_BASE
+#define UHAL_PCI_ALLOC_IO_BASE          0x00004000
+#define UHAL_PCI_ALLOC_MEM_BASE         PCI_MEM_BASE
+#define UHAL_PCI_MAX_SLOT               20
+	
+/* ========================================================================
+ *  Start of uHAL definitions
+ * ========================================================================
+ */
+
+/* ------------------------------------------------------------------------
+ *  Integrator Interrupt Controllers
+ * ------------------------------------------------------------------------
+ * 
+ *  Offsets from interrupt controller base 
+ * 
+ *  System Controller interrupt controller base is
+ * 
+ * 	INTEGRATOR_IC_BASE + (header_number << 6)
+ * 
+ *  Core Module interrupt controller base is
+ * 
+ * 	INTEGRATOR_HDR_IC 
+ * 
+ */
+#define IRQ_STATUS                      0
+#define IRQ_RAW_STATUS                  0x04
+#define IRQ_ENABLE                      0x08
+#define IRQ_ENABLE_SET                  0x08
+#define IRQ_ENABLE_CLEAR                0x0C
+
+#define INT_SOFT_SET                    0x10
+#define INT_SOFT_CLEAR                  0x14
+
+#define FIQ_STATUS                      0x20
+#define FIQ_RAW_STATUS                  0x24
+#define FIQ_ENABLE                      0x28
+#define FIQ_ENABLE_SET                  0x28
+#define FIQ_ENABLE_CLEAR                0x2C
+
+
+/* ------------------------------------------------------------------------
+ *  Interrupts
+ * ------------------------------------------------------------------------
+ * 
+ *  
+ *  Each Core Module has two interrupts controllers, one on the core module
+ *  itself and one in the system controller on the motherboard.  The
+ *  READ_INT macro in target.s reads both interrupt controllers and returns
+ *  a 32 bit bitmask, bits 0 to 23 are interrupts from the system controller
+ *  and bits 24 to 31 are from the core module.
+ *  
+ *  The following definitions relate to the bitmask returned by READ_INT.
+ * 
+ */
+
+/* 
+ *  As the interrupt bit definitions for FIQ/IRQ there is a common
+ *  set of definitions prefixed INT/INTMASK.  The FIQ/IRQ definitions
+ *  have been left to maintain backwards compatible.
+ * 
+ */
+
+/* 
+ *  Interrupt numbers
+ * 
+ */
+#define INT_SOFTINT                     0
+#define INT_UARTINT0                    1
+#define INT_UARTINT1                    2
+#define INT_KMIINT0                     3
+#define INT_KMIINT1                     4
+#define INT_TIMERINT0                   5
+#define INT_TIMERINT1                   6
+#define INT_TIMERINT2                   7
+#define INT_RTCINT                      8
+#define INT_EXPINT0                     9
+#define INT_EXPINT1                     10
+#define INT_EXPINT2                     11
+#define INT_EXPINT3                     12
+#define INT_PCIINT0                     13
+#define INT_PCIINT1                     14
+#define INT_PCIINT2                     15
+#define INT_PCIINT3                     16
+#define INT_V3INT                       17
+#define INT_CPINT0                      18
+#define INT_CPINT1                      19
+#define INT_LBUSTIMEOUT                 20
+#define INT_APCINT                      21
+#define INT_CM_SOFTINT                  24
+#define INT_CM_COMMRX                   25
+#define INT_CM_COMMTX                   26
+
+/* 
+ *  Interrupt bit positions
+ * 
+ */
+#define INTMASK_SOFTINT                 (1 << INT_SOFTINT)
+#define INTMASK_UARTINT0                (1 << INT_UARTINT0)
+#define INTMASK_UARTINT1                (1 << INT_UARTINT1)
+#define INTMASK_KMIINT0                 (1 << INT_KMIINT0)
+#define INTMASK_KMIINT1                 (1 << INT_KMIINT1)
+#define INTMASK_TIMERINT0               (1 << INT_TIMERINT0)
+#define INTMASK_TIMERINT1               (1 << INT_TIMERINT1)
+#define INTMASK_TIMERINT2               (1 << INT_TIMERINT2)
+#define INTMASK_RTCINT                  (1 << INT_RTCINT)
+#define INTMASK_EXPINT0                 (1 << INT_EXPINT0)
+#define INTMASK_EXPINT1                 (1 << INT_EXPINT1)
+#define INTMASK_EXPINT2                 (1 << INT_EXPINT2)
+#define INTMASK_EXPINT3                 (1 << INT_EXPINT3)
+#define INTMASK_PCIINT0                 (1 << INT_PCIINT0)
+#define INTMASK_PCIINT1                 (1 << INT_PCIINT1)
+#define INTMASK_PCIINT2                 (1 << INT_PCIINT2)
+#define INTMASK_PCIINT3                 (1 << INT_PCIINT3)
+#define INTMASK_V3INT                   (1 << INT_V3INT)
+#define INTMASK_CPINT0                  (1 << INT_CPINT0)
+#define INTMASK_CPINT1                  (1 << INT_CPINT1)
+#define INTMASK_LBUSTIMEOUT             (1 << INT_LBUSTIMEOUT)
+#define INTMASK_APCINT                  (1 << INT_APCINT)
+#define INTMASK_CM_SOFTINT              (1 << INT_CM_SOFTINT)
+#define INTMASK_CM_COMMRX               (1 << INT_CM_COMMRX)
+#define INTMASK_CM_COMMTX               (1 << INT_CM_COMMTX)
+
+/* 
+ *  INTEGRATOR_CM_INT0      - Interrupt number of first CM interrupt
+ *  INTEGRATOR_SC_VALID_INT - Mask of valid system controller interrupts
+ * 
+ */
+#define INTEGRATOR_CM_INT0              INT_CM_SOFTINT
+#define INTEGRATOR_SC_VALID_INT         0x003FFFFF
+
+#define MAXIRQNUM                       31
+#define MAXFIQNUM                       31
+#define MAXSWINUM                       31
+ 
+/* ------------------------------------------------------------------------
+ *  LED's - The header LED is not accessable via the uHAL API
+ * ------------------------------------------------------------------------
+ * 
+ */
+#define GREEN_LED                       0x01
+#define YELLOW_LED                      0x02
+#define RED_LED                         0x04
+#define GREEN_LED_2                     0x08
+#define ALL_LEDS                        0x0F
+
+#define LED_BANK                        INTEGRATOR_DBG_LEDS
+
+/* 
+ *  Memory definitions - run uHAL out of SSRAM.
+ * 
+ */
+#define uHAL_MEMORY_SIZE                INTEGRATOR_SSRAM_SIZE
+
+/* 
+ *  Application Flash
+ * 
+ */
+#define FLASH_BASE                      INTEGRATOR_FLASH_BASE
+#define FLASH_SIZE                      INTEGRATOR_FLASH_SIZE
+#define FLASH_END                       (FLASH_BASE + FLASH_SIZE - 1)
+#define FLASH_BLOCK_SIZE                SZ_128K
+
+/* 
+ *  Boot Flash
+ * 
+ */
+#define EPROM_BASE                      INTEGRATOR_BOOT_ROM_HI
+#define EPROM_SIZE                      INTEGRATOR_BOOT_ROM_SIZE
+#define EPROM_END                       (EPROM_BASE + EPROM_SIZE - 1)
+
+/* 
+ *  Clean base - dummy
+ * 
+ */
+#define CLEAN_BASE                      EPROM_BASE
+
+/* 
+ *  Timer definitions
+ * 
+ *  Only use timer 1 & 2
+ *  (both run at 24MHz and will need the clock divider set to 16).
+ * 
+ *  Timer 0 runs at bus frequency and therefore could vary and currently
+ *  uHAL can't handle that.
+ * 
+ */
+
+#define INTEGRATOR_TIMER0_BASE          INTEGRATOR_CT_BASE
+#define INTEGRATOR_TIMER1_BASE          (INTEGRATOR_CT_BASE + 0x100)
+#define INTEGRATOR_TIMER2_BASE          (INTEGRATOR_CT_BASE + 0x200)
+
+#define MAX_TIMER                       2
+#define MAX_PERIOD                      699050
+#define TICKS_PER_uSEC                  24
+
+/* 
+ *  These are useconds NOT ticks.  
+ * 
+ */
+#define mSEC_1                          1000
+#define mSEC_5                          (mSEC_1 * 5)
+#define mSEC_10                         (mSEC_1 * 10)
+#define mSEC_25                         (mSEC_1 * 25)
+#define SEC_1                           (mSEC_1 * 1000)
+
+#define INTEGRATOR_CSR_BASE             0x10000000
+#define INTEGRATOR_CSR_SIZE             0x10000000
+
+#endif
+
+/* 	END */
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/serial.h linux/include/asm-arm/arch-integrator/serial.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/serial.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/serial.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,49 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/serial.h
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_SERIAL_H
+#define __ASM_ARCH_SERIAL_H
+
+#include <asm/arch/platform.h>
+#include <asm/irq.h>
+
+/*
+ * This assumes you have a 1.8432 MHz clock for your UART.
+ *
+ * It'd be nice if someone built a serial card with a 24.576 MHz
+ * clock, since the 16550A is capable of handling a top speed of 1.5
+ * megabits/second; but this requires the faster clock.
+ */
+#define BASE_BAUD (1843200 / 16)
+
+#define _SER_IRQ0	IRQ_UARTINT0
+#define _SER_IRQ1	IRQ_UARTINT1
+
+#define RS_TABLE_SIZE	2
+
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+
+     /* UART CLK        PORT  IRQ     FLAGS        */
+#define STD_SERIAL_PORT_DEFNS \
+	{ 0, BASE_BAUD, 0x3F8, _SER_IRQ0, STD_COM_FLAGS },	/* ttyS0 */	\
+	{ 0, BASE_BAUD, 0x2F8, _SER_IRQ1, STD_COM_FLAGS },	/* ttyS1 */
+
+#define EXTRA_SERIAL_PORT_DEFNS
+
+#endif
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/sizes.h linux/include/asm-arm/arch-integrator/sizes.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/sizes.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/sizes.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,52 @@
+/*
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/* DO NOT EDIT!! - this file automatically generated
+ *                 from .s file by awk -f s2h.awk
+ */
+/*  Size defintions
+ *  Copyright (C) ARM Limited 1998. All rights reserved.
+ */
+
+#ifndef __sizes_h
+#define __sizes_h                       1
+
+/* handy sizes */
+#define SZ_1K                           0x00000400
+#define SZ_4K                           0x00001000
+#define SZ_8K                           0x00002000
+#define SZ_16K                          0x00004000
+#define SZ_64K                          0x00010000
+#define SZ_128K                         0x00020000
+#define SZ_256K                         0x00040000
+#define SZ_512K                         0x00080000
+
+#define SZ_1M                           0x00100000
+#define SZ_2M                           0x00200000
+#define SZ_4M                           0x00400000
+#define SZ_8M                           0x00800000
+#define SZ_16M                          0x01000000
+#define SZ_32M                          0x02000000
+#define SZ_64M                          0x04000000
+#define SZ_128M                         0x08000000
+#define SZ_256M                         0x10000000
+#define SZ_512M                         0x20000000
+
+#define SZ_1G                           0x40000000
+#define SZ_2G                           0x80000000
+
+#endif
+
+/*         END */
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/system.h linux/include/asm-arm/arch-integrator/system.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/system.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/system.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,49 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/system.h
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#ifndef __ASM_ARCH_SYSTEM_H
+#define __ASM_ARCH_SYSTEM_H
+
+#include <asm/arch/platform.h>
+
+static void arch_idle(void)
+{
+	/*
+	 * This should do all the clock switching
+	 * and wait for interrupt tricks
+	 */
+	cpu_do_idle(0);
+}
+
+extern __inline__ void arch_reset(char mode)
+{
+	unsigned int hdr_ctrl =	(IO_ADDRESS(INTEGRATOR_HDR_BASE) + INTEGRATOR_HDR_CTRL_OFFSET);
+	unsigned int val;
+
+	/*
+	 * To reset, we hit the on-board reset register
+	 * in the system FPGA
+	 */
+	val = __raw_readl(hdr_ctrl);
+	val |= INTEGRATOR_HDR_CTRL_RESET;
+	__raw_writel(val, hdr_ctrl);
+}
+
+#endif
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/time.h linux/include/asm-arm/arch-integrator/time.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/time.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/time.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,140 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/time.h
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <asm/system.h>
+#include <asm/leds.h>
+
+/*
+ * Where is the timer (VA)?
+ */
+#define TIMER0_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000000)
+#define TIMER1_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000100)
+#define TIMER2_VA_BASE (IO_ADDRESS(INTEGRATOR_CT_BASE)+0x00000200)
+#define VA_IC_BASE     IO_ADDRESS(INTEGRATOR_IC_BASE) 
+
+/*
+ * How long is the timer interval?
+ */
+#define TIMER_INTERVAL	(TICKS_PER_uSEC * mSEC_10)
+#if TIMER_INTERVAL >= 0x100000
+#define TIMER_RELOAD	(TIMER_INTERVAL >> 8)		/* Divide by 256 */
+#define TIMER_CTRL	0x88				/* Enable, Clock / 256 */
+#define TICKS2USECS(x)	(256 * (x) / TICKS_PER_uSEC)
+#elif TIMER_INTERVAL >= 0x10000
+#define TIMER_RELOAD	(TIMER_INTERVAL >> 4)		/* Divide by 16 */
+#define TIMER_CTRL	0x84				/* Enable, Clock / 16 */
+#define TICKS2USECS(x)	(16 * (x) / TICKS_PER_uSEC)
+#else
+#define TIMER_RELOAD	(TIMER_INTERVAL)
+#define TIMER_CTRL	0x80				/* Enable */
+#define TICKS2USECS(x)	((x) / TICKS_PER_uSEC)
+#endif
+
+/*
+ * What does it look like?
+ */
+typedef struct TimerStruct {
+	unsigned long TimerLoad;
+	unsigned long TimerValue;
+	unsigned long TimerControl;
+	unsigned long TimerClear;
+} TimerStruct_t;
+
+extern unsigned long (*gettimeoffset)(void);
+
+/*
+ * Returns number of ms since last clock interrupt.  Note that interrupts
+ * will have been disabled by do_gettimeoffset()
+ */
+static unsigned long integrator_gettimeoffset(void)
+{
+	volatile TimerStruct_t *timer1 = (TimerStruct_t *)TIMER1_VA_BASE;
+	unsigned long ticks1, ticks2, status;
+
+	/*
+	 * Get the current number of ticks.  Note that there is a race
+	 * condition between us reading the timer and checking for
+	 * an interrupt.  We get around this by ensuring that the
+	 * counter has not reloaded between our two reads.
+	 */
+	ticks2 = timer1->TimerValue & 0xffff;
+	do {
+		ticks1 = ticks2;
+		status = __raw_readl(VA_IC_BASE + IRQ_RAW_STATUS);
+		ticks2 = timer1->TimerValue & 0xffff;
+	} while (ticks2 > ticks1);
+
+	/*
+	 * Number of ticks since last interrupt.
+	 */
+	ticks1 = TIMER_RELOAD - ticks2;
+
+	/*
+	 * Interrupt pending?  If so, we've reloaded once already.
+	 */
+	if (status & IRQMASK_TIMERINT1)
+		ticks1 += TIMER_RELOAD;
+
+	/*
+	 * Convert the ticks to usecs
+	 */
+	return TICKS2USECS(ticks1);
+}
+
+/*
+ * IRQ handler for the timer
+ */
+static void integrator_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	volatile TimerStruct_t *timer1 = (volatile TimerStruct_t *)TIMER1_VA_BASE;
+
+	// ...clear the interrupt
+	timer1->TimerClear = 1;
+
+	do_leds();
+	do_timer(regs);
+	do_profile(regs);
+}
+
+/*
+ * Set up timer interrupt, and return the current time in seconds.
+ */
+extern __inline__ void setup_timer(void)
+{
+	volatile TimerStruct_t *timer0 = (volatile TimerStruct_t *)TIMER0_VA_BASE;
+	volatile TimerStruct_t *timer1 = (volatile TimerStruct_t *)TIMER1_VA_BASE;
+	volatile TimerStruct_t *timer2 = (volatile TimerStruct_t *)TIMER2_VA_BASE;
+
+	timer_irq.handler = integrator_timer_interrupt;
+
+	/*
+	 * Initialise to a known state (all timers off)
+	 */
+	timer0->TimerControl = 0;
+	timer1->TimerControl = 0;
+	timer2->TimerControl = 0;
+
+	timer1->TimerLoad    = TIMER_RELOAD;
+	timer1->TimerValue   = TIMER_RELOAD;
+	timer1->TimerControl = TIMER_CTRL | 0x40;	/* periodic */
+
+	/* 
+	 * Make irqs happen for the system timer
+	 */
+	setup_arm_irq(IRQ_TIMERINT1, &timer_irq);
+	gettimeoffset = integrator_gettimeoffset;
+}
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/timex.h linux/include/asm-arm/arch-integrator/timex.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/timex.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/timex.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,26 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/timex.h
+ *
+ *  Integrator architecture timex specifications
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * ??
+ */
+#define CLOCK_TICK_RATE		(50000000 / 16)
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/uncompress.h linux/include/asm-arm/arch-integrator/uncompress.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/uncompress.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/uncompress.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,53 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/uncompress.h
+ *
+ *  Copyright (C) 1999 ARM Limited
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define AMBA_UART_DR	(*(volatile unsigned char *)0x16000000)
+#define AMBA_UART_LCRH	(*(volatile unsigned char *)0x16000008)
+#define AMBA_UART_LCRM	(*(volatile unsigned char *)0x1600000c)
+#define AMBA_UART_LCRL	(*(volatile unsigned char *)0x16000010)
+#define AMBA_UART_CR	(*(volatile unsigned char *)0x16000014)
+#define AMBA_UART_FR	(*(volatile unsigned char *)0x16000018)
+
+/*
+ * This does not append a newline
+ */
+static void puts(const char *s)
+{
+	while (*s) {
+		while (AMBA_UART_FR & (1 << 5));
+
+		AMBA_UART_DR = *s;
+
+		if (*s == '\n') {
+			while (AMBA_UART_FR & (1 << 5));
+
+			AMBA_UART_DR = '\r';
+		}
+		s++;
+	}
+	while (AMBA_UART_FR & (1 << 3));
+}
+
+/*
+ * nothing to do
+ */
+#define arch_decomp_setup()
+
+#define arch_decomp_wdog()
diff -urN linux-2.4.3-pre1/include/asm-arm/arch-integrator/vmalloc.h linux/include/asm-arm/arch-integrator/vmalloc.h
--- linux-2.4.3-pre1/include/asm-arm/arch-integrator/vmalloc.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/arch-integrator/vmalloc.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,32 @@
+/*
+ *  linux/include/asm-arm/arch-integrator/vmalloc.h
+ *
+ *  Copyright (C) 2000 Russell King.
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * Just any arbitrary offset to the start of the vmalloc VM area: the
+ * current 8MB value just means that there will be a 8MB "hole" after the
+ * physical memory until the kernel virtual memory starts.  That means that
+ * any out-of-bounds memory accesses will hopefully be caught.
+ * The vmalloc() routines leaves a hole of 4kB between each vmalloced
+ * area for the same reason. ;)
+ */
+#define VMALLOC_OFFSET	  (8*1024*1024)
+#define VMALLOC_START	  (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
+#define VMALLOC_VMADDR(x) ((unsigned long)(x))
+#define VMALLOC_END       (PAGE_OFFSET + 0x10000000)
diff -urN linux-2.4.3-pre1/include/asm-arm/hardware/amba_kmi.h linux/include/asm-arm/hardware/amba_kmi.h
--- linux-2.4.3-pre1/include/asm-arm/hardware/amba_kmi.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/hardware/amba_kmi.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,92 @@
+/*
+ *  linux/include/asm-arm/hardware/amba_kmi.h
+ *
+ *  Internal header file for AMBA KMI ports
+ *
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ * ---------------------------------------------------------------------------
+ *  From ARM PrimeCell(tm) PS2 Keyboard/Mouse Interface (PL050) Technical
+ *  Reference Manual - ARM DDI 0143B - see http://www.arm.com/
+ * ---------------------------------------------------------------------------
+ */
+#ifndef ASM_ARM_HARDWARE_AMBA_KMI_H
+#define ASM_ARM_HARDWARE_AMBA_KMI_H
+
+/*
+ * KMI control register:
+ *  KMICR_TYPE       0 = PS2/AT mode, 1 = No line control bit mode
+ *  KMICR_RXINTREN   1 = enable RX interrupts
+ *  KMICR_TXINTREN   1 = enable TX interrupts
+ *  KMICR_EN         1 = enable KMI
+ *  KMICR_FD         1 = force KMI data low
+ *  KMICR_FC         1 = force KMI clock low
+ */
+#define KMICR		(KMI_BASE + 0x00)
+#define KMICR_TYPE		(1 << 5)
+#define KMICR_RXINTREN		(1 << 4)
+#define KMICR_TXINTREN		(1 << 3)
+#define KMICR_EN		(1 << 2)
+#define KMICR_FD		(1 << 1)
+#define KMICR_FC		(1 << 0)
+
+/*
+ * KMI status register:
+ *  KMISTAT_TXEMPTY  1 = transmitter register empty
+ *  KMISTAT_TXBUSY   1 = currently sending data
+ *  KMISTAT_RXFULL   1 = receiver register ready to be read
+ *  KMISTAT_RXBUSY   1 = currently receiving data
+ *  KMISTAT_RXPARITY parity of last databyte received
+ *  KMISTAT_IC       current level of KMI clock input
+ *  KMISTAT_ID       current level of KMI data input
+ */
+#define KMISTAT		(KMI_BASE + 0x04)
+#define KMISTAT_TXEMPTY		(1 << 6)
+#define KMISTAT_TXBUSY		(1 << 5)
+#define KMISTAT_RXFULL		(1 << 4)
+#define KMISTAT_RXBUSY		(1 << 3)
+#define KMISTAT_RXPARITY	(1 << 2)
+#define KMISTAT_IC		(1 << 1)
+#define KMISTAT_ID		(1 << 0)
+
+/*
+ * KMI data register
+ */
+#define KMIDATA		(KMI_BASE + 0x08)
+
+/*
+ * KMI clock divisor: to generate 8MHz internal clock
+ *  div = (ref / 8MHz) - 1; 0 <= div <= 15
+ */
+#define KMICLKDIV	(KMI_BASE + 0x0c)
+
+/*
+ * KMI interrupt register:
+ *  KMIIR_TXINTR     1 = transmit interrupt asserted
+ *  KMIIR_RXINTR     1 = receive interrupt asserted
+ */
+#define KMIIR		(KMI_BASE + 0x10)
+#define KMIIR_TXINTR		(1 << 1)
+#define KMIIR_RXINTR		(1 << 0)
+
+/*
+ * The size of the KMI primecell
+ */
+#define KMI_SIZE	(0x100)
+
+#endif
diff -urN linux-2.4.3-pre1/include/asm-arm/hardware/pci_v3.h linux/include/asm-arm/hardware/pci_v3.h
--- linux-2.4.3-pre1/include/asm-arm/hardware/pci_v3.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/hardware/pci_v3.h	Mon Dec  3 22:29:50 2001
@@ -88,41 +88,41 @@
 
 /*  PCI COMMAND REGISTER bits
  */
-#define V3_COMMAND_M_FBB_EN             BIT9
-#define V3_COMMAND_M_SERR_EN            BIT8
-#define V3_COMMAND_M_PAR_EN             BIT6
-#define V3_COMMAND_M_MASTER_EN          BIT2
-#define V3_COMMAND_M_MEM_EN             BIT1
-#define V3_COMMAND_M_IO_EN              BIT0
+#define V3_COMMAND_M_FBB_EN             (1 << 9)
+#define V3_COMMAND_M_SERR_EN            (1 << 8)
+#define V3_COMMAND_M_PAR_EN             (1 << 6)
+#define V3_COMMAND_M_MASTER_EN          (1 << 2)
+#define V3_COMMAND_M_MEM_EN             (1 << 1)
+#define V3_COMMAND_M_IO_EN              (1 << 0)
 
 /*  SYSTEM REGISTER bits
  */
-#define V3_SYSTEM_M_RST_OUT             BIT15
-#define V3_SYSTEM_M_LOCK                BIT14 
+#define V3_SYSTEM_M_RST_OUT             (1 << 15)
+#define V3_SYSTEM_M_LOCK                (1 << 14)
 
 /*  PCI_CFG bits
  */
-#define V3_PCI_CFG_M_RETRY_EN           BIT10
-#define V3_PCI_CFG_M_AD_LOW1            BIT9
-#define V3_PCI_CFG_M_AD_LOW0            BIT8
+#define V3_PCI_CFG_M_RETRY_EN           (1 << 10)
+#define V3_PCI_CFG_M_AD_LOW1            (1 << 9)
+#define V3_PCI_CFG_M_AD_LOW0            (1 << 8)
 
 /*  PCI_BASE register bits (PCI -> Local Bus)
  */
 #define V3_PCI_BASE_M_ADR_BASE          0xFFF00000
 #define V3_PCI_BASE_M_ADR_BASEL         0x000FFF00
-#define V3_PCI_BASE_M_PREFETCH          BIT3
-#define V3_PCI_BASE_M_TYPE              BIT2+BIT1
-#define V3_PCI_BASE_M_IO                BIT0
+#define V3_PCI_BASE_M_PREFETCH          (1 << 3)
+#define V3_PCI_BASE_M_TYPE              (3 << 1)
+#define V3_PCI_BASE_M_IO                (1 << 0)
 
 /*  PCI MAP register bits (PCI -> Local bus)
  */
 #define V3_PCI_MAP_M_MAP_ADR            0xFFF00000
-#define V3_PCI_MAP_M_RD_POST_INH        BIT15
-#define V3_PCI_MAP_M_ROM_SIZE           BIT11+BIT10
-#define V3_PCI_MAP_M_SWAP               BIT9+BIT8
+#define V3_PCI_MAP_M_RD_POST_INH        (1 << 15)
+#define V3_PCI_MAP_M_ROM_SIZE           (3 << 10)
+#define V3_PCI_MAP_M_SWAP               (3 << 8)
 #define V3_PCI_MAP_M_ADR_SIZE           0x000000F0
-#define V3_PCI_MAP_M_REG_EN             BIT1	
-#define V3_PCI_MAP_M_ENABLE             BIT0
+#define V3_PCI_MAP_M_REG_EN             (1 << 1)
+#define V3_PCI_MAP_M_ENABLE             (1 << 0)
 
 /*  9 => 512M window size
  */
@@ -134,15 +134,15 @@
 /*  LB_BASE register bits (Local bus -> PCI)
  */
 #define V3_LB_BASE_M_MAP_ADR            0xFFF00000
-#define V3_LB_BASE_M_SWAP               BIT9+BIT8
+#define V3_LB_BASE_M_SWAP               (3 << 8)
 #define V3_LB_BASE_M_ADR_SIZE           0x000000F0
-#define V3_LB_BASE_M_PREFETCH           BIT3
-#define V3_LB_BASE_M_ENABLE             BIT0
+#define V3_LB_BASE_M_PREFETCH           (1 << 3)
+#define V3_LB_BASE_M_ENABLE             (1 << 0)
 
 /*  LB_MAP register bits (Local bus -> PCI)
  */
 #define V3_LB_MAP_M_MAP_ADR             0xFFF0
 #define V3_LB_MAP_M_TYPE                0x000E
-#define V3_LB_MAP_M_AD_LOW_EN           BIT0
+#define V3_LB_MAP_M_AD_LOW_EN           (1 << 0)
 
 #endif
diff -urN linux-2.4.3-pre1/include/asm-arm/hardware/serial_amba.h linux/include/asm-arm/hardware/serial_amba.h
--- linux-2.4.3-pre1/include/asm-arm/hardware/serial_amba.h	Mon Sep 18 15:15:23 2000
+++ linux/include/asm-arm/hardware/serial_amba.h	Mon Dec  3 22:29:50 2001
@@ -88,4 +88,7 @@
 #define ARM_BAUD_2400                   383
 #define ARM_BAUD_1200                   767
 
+#define AMBA_UARTRSR_ANY	(AMBA_UARTRSR_OE|AMBA_UARTRSR_BE|AMBA_UARTRSR_PE|AMBA_UARTRSR_FE)
+#define AMBA_UARTFR_MODEM_ANY	(AMBA_UARTFR_DCD|AMBA_UARTFR_DSR|AMBA_UARTFR_CTS)
+
 #endif
diff -urN linux-2.4.3-pre1/include/asm-arm/mach/amba_kmi.h linux/include/asm-arm/mach/amba_kmi.h
--- linux-2.4.3-pre1/include/asm-arm/mach/amba_kmi.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-arm/mach/amba_kmi.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,39 @@
+/*
+ *  linux/include/asm-arm/mach/amba_kmi.h
+ *
+ *  Copyright (C) 2000 Deep Blue Solutions Ltd.
+ *
+ * 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 of the License, 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; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+struct kmi_info {
+	u_int			base;
+	u_int			irq;
+	u_char			divisor;
+	u_char			type;
+	u_char			state;
+	u_char			prev_rx;
+	u_char			last_tx;
+	u_char			resend_count;
+	u_short			res;
+	wait_queue_head_t	wait_q;
+	void			(*rx)(struct kmi_info *, u_int val,
+				      struct pt_regs *regs);
+	char			name[8];
+};
+
+#define KMI_KEYBOARD		0
+#define KMI_MOUSE		1
+
+int register_kmi(struct kmi_info *kmi);
diff -urN linux-2.4.3-pre1/include/asm-i386/atomic.h linux/include/asm-i386/atomic.h
--- linux-2.4.3-pre1/include/asm-i386/atomic.h	Wed Feb 21 16:09:56 2001
+++ linux/include/asm-i386/atomic.h	Mon Dec  3 22:29:50 2001
@@ -23,9 +23,33 @@
 
 #define ATOMIC_INIT(i)	{ (i) }
 
+/**
+ * atomic_read - read atomic variable
+ * @v: pointer of type atomic_t
+ * 
+ * Atomically reads the value of @v.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */ 
 #define atomic_read(v)		((v)->counter)
+
+/**
+ * atomic_set - set atomic variable
+ * @v: pointer of type atomic_t
+ * @i: required value
+ * 
+ * Atomically sets the value of @v to @i.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */ 
 #define atomic_set(v,i)		(((v)->counter) = (i))
 
+/**
+ * atomic_add - add integer to atomic variable
+ * @i: integer value to add
+ * @v: pointer of type atomic_t
+ * 
+ * Atomically adds @i to @v.  Note that the guaranteed useful range
+ * of an atomic_t is only 24 bits.
+ */
 static __inline__ void atomic_add(int i, atomic_t *v)
 {
 	__asm__ __volatile__(
@@ -34,6 +58,14 @@
 		:"ir" (i), "m" (v->counter));
 }
 
+/**
+ * atomic_sub - subtract the atomic variable
+ * @i: integer value to subtract
+ * @v: pointer of type atomic_t
+ * 
+ * Atomically subtracts @i from @v.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */
 static __inline__ void atomic_sub(int i, atomic_t *v)
 {
 	__asm__ __volatile__(
@@ -42,6 +74,16 @@
 		:"ir" (i), "m" (v->counter));
 }
 
+/**
+ * atomic_sub_and_test - test variable then subtract 
+ * @i: integer value to subtract
+ * @v: pointer of type atomic_t
+ * 
+ * Atomically subtracts @i from @v and returns
+ * true if the result is zero, or false for all
+ * other cases.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */
 static __inline__ int atomic_sub_and_test(int i, atomic_t *v)
 {
 	unsigned char c;
@@ -53,6 +95,13 @@
 	return c;
 }
 
+/**
+ * atomic_inc - increment atomic variable
+ * @v: pointer of type atomic_t
+ * 
+ * Atomically increments @v by 1.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */ 
 static __inline__ void atomic_inc(atomic_t *v)
 {
 	__asm__ __volatile__(
@@ -61,6 +110,13 @@
 		:"m" (v->counter));
 }
 
+/**
+ * atomic_dec - decrement the atomic variable
+ * @v: pointer of type atomic_t
+ * 
+ * Atomically decrements @v by 1.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */ 
 static __inline__ void atomic_dec(atomic_t *v)
 {
 	__asm__ __volatile__(
@@ -69,6 +125,15 @@
 		:"m" (v->counter));
 }
 
+/**
+ * atomic_dec_and_test - decrement by 1 and test
+ * @v: pointer of type atomic_t
+ * 
+ * Atomically decrements @v by 1 and
+ * returns true if the result is 0, or false for all other
+ * cases.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */ 
 static __inline__ int atomic_dec_and_test(atomic_t *v)
 {
 	unsigned char c;
@@ -80,6 +145,15 @@
 	return c != 0;
 }
 
+/**
+ * atomic_inc_and_test - increment by 1 and test 
+ * @v: pointer of type atomic_t
+ * 
+ * Atomically increments @v by 1
+ * and returns true if the result is zero, or false for all
+ * other cases.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */ 
 static __inline__ int atomic_inc_and_test(atomic_t *v)
 {
 	unsigned char c;
@@ -91,6 +165,16 @@
 	return c != 0;
 }
 
+/**
+ * atomic_add_negative - add and test if negative
+ * @v: pointer of type atomic_t
+ * @i: integer value to add
+ * 
+ * Atomically adds @i to @v and returns true
+ * if the result is negative, or false when
+ * result is greater than or equal to zero.  Note that the guaranteed
+ * useful range of an atomic_t is only 24 bits.
+ */ 
 static __inline__ int atomic_add_negative(int i, atomic_t *v)
 {
 	unsigned char c;
diff -urN linux-2.4.3-pre1/include/asm-ppc/cpm_8260.h linux/include/asm-ppc/cpm_8260.h
--- linux-2.4.3-pre1/include/asm-ppc/cpm_8260.h	Sat Nov 11 18:23:10 2000
+++ linux/include/asm-ppc/cpm_8260.h	Mon Dec  3 22:29:50 2001
@@ -112,7 +112,7 @@
 	uint	cbd_bufaddr;	/* Buffer address in host memory */
 } cbd_t;
 
-#define BD_SC_EMPTY	((ushort)0x8000)	/* Recieve is empty */
+#define BD_SC_EMPTY	((ushort)0x8000)	/* Receive is empty */
 #define BD_SC_READY	((ushort)0x8000)	/* Transmit is ready */
 #define BD_SC_WRAP	((ushort)0x2000)	/* Last buffer descriptor */
 #define BD_SC_INTRPT	((ushort)0x1000)	/* Interrupt on change */
diff -urN linux-2.4.3-pre1/include/asm-ppc/highmem.h linux/include/asm-ppc/highmem.h
--- linux-2.4.3-pre1/include/asm-ppc/highmem.h	Wed Nov  8 19:01:34 2000
+++ linux/include/asm-ppc/highmem.h	Mon Dec  3 22:29:50 2001
@@ -4,7 +4,7 @@
  * PowerPC version, stolen from the i386 version.
  *
  * Used in CONFIG_HIGHMEM systems for memory pages which
- * are not addressable by direct kernel virtual adresses.
+ * are not addressable by direct kernel virtual addresses.
  *
  * Copyright (C) 1999 Gerhard Wichert, Siemens AG
  *		      Gerhard.Wichert@pdb.siemens.de
diff -urN linux-2.4.3-pre1/include/asm-ppc/irq.h linux/include/asm-ppc/irq.h
--- linux-2.4.3-pre1/include/asm-ppc/irq.h	Mon Jan 22 15:41:15 2001
+++ linux/include/asm-ppc/irq.h	Mon Dec  3 22:29:50 2001
@@ -88,23 +88,31 @@
 #define	SIU_IRQ7	(14)
 #define	SIU_LEVEL7	(15)
 
+/* Now include the board configuration specific associations.
+*/
+#include <asm/mpc8xx.h>
+
 /* The internal interrupts we can configure as we see fit.
  * My personal preference is CPM at level 2, which puts it above the
  * MBX PCI/ISA/IDE interrupts.
  */
+#ifndef PIT_INTERRUPT
 #define PIT_INTERRUPT		SIU_LEVEL0
+#endif
+#ifndef	CPM_INTERRUPT
 #define CPM_INTERRUPT		SIU_LEVEL2
+#endif
+#ifndef	PCMCIA_INTERRUPT
 #define PCMCIA_INTERRUPT	SIU_LEVEL6
+#endif
+#ifndef	DEC_INTERRUPT
 #define DEC_INTERRUPT		SIU_LEVEL7
+#endif
 
 /* Some internal interrupt registers use an 8-bit mask for the interrupt
  * level instead of a number.
  */
 #define	mk_int_int_mask(IL) (1 << (7 - (IL/2)))
-
-/* Now include the board configuration specific associations.
-*/
-#include <asm/mpc8xx.h>
 
 /* always the same on 8xx -- Cort */
 static __inline__ int irq_cannonicalize(int irq)
diff -urN linux-2.4.3-pre1/include/asm-ppc/ivms8.h linux/include/asm-ppc/ivms8.h
--- linux-2.4.3-pre1/include/asm-ppc/ivms8.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/ivms8.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,92 @@
+/*
+ * A collection of structures, addresses, and values associated with
+ * Speech Design Integrated Voicemail Systems (IVMS8) boards.
+ *
+ * Copyright (c) 2000 Wolfgang Denk (wd@denx.de)
+ */
+#ifndef __MACH_IVMS8_DEFS
+#define __MACH_IVMS8_DEFS
+
+#ifndef __ASSEMBLY__
+
+typedef	void (interrupt_handler_t)(void *);
+
+typedef struct serial_io {
+	int	(*getc)(void);
+	int	(*tstc)(void);
+	void	(*putc)(const char c);
+	void	(*printf)(const char *fmt, ...);
+} serial_io_t;
+
+typedef struct intr_util {
+	void	(*install_hdlr)(int, interrupt_handler_t *, void *);
+	void	(*free_hdlr)(int);
+} intr_util_t;
+
+
+/* A Board Information structure that is given to a program when
+ * ppcboot starts it up.
+ */
+typedef struct bd_info {
+	 unsigned long	bi_memstart;	/* start of  DRAM memory		*/
+	 unsigned long	bi_memsize;	/* size	 of  DRAM memory in bytes	*/
+	 unsigned long	bi_flashstart;	/* start of FLASH memory		*/
+	 unsigned long	bi_flashsize;	/* size	 of FLASH memory		*/
+	 unsigned long	bi_flashoffset; /* reserved area for startup monitor	*/
+	 unsigned long	bi_sramstart;	/* start of  SRAM memory		*/
+	 unsigned long	bi_sramsize;	/* size	 of  SRAM memory		*/
+	 unsigned long	bi_immr_base;	/* base of IMMR register		*/
+	 unsigned long	bi_bootflags;	/* boot / reboot flag (for LynxOS)	*/
+	 unsigned long	bi_ip_addr;	/* IP Address				*/
+	 unsigned char	bi_enetaddr[6]; /* Ethernet adress			*/
+	 unsigned char	bi_reserved[2]; /* -- just for alignment --		*/
+	 unsigned long	bi_intfreq;	/* Internal Freq, in MHz		*/
+	 unsigned long	bi_busfreq;	/* Bus Freq, in MHz			*/
+	 unsigned long	bi_baudrate;	/* Console Baudrate			*/
+	 serial_io_t	bi_serial_io;	/* Addr of monitor fnc for Console I/O	*/
+	 intr_util_t	bi_interrupt;	/* Addr of monitor fnc for Interrupts	*/
+} bd_t;
+
+#endif /* __ASSEMBLY__ */
+
+#define	IVMS_IMMR_BASE	0xFFF00000	/* phys. addr of IMMR			*/
+#define	IVMS_IMAP_SIZE	(64 * 1024)	/* size of mapped area			*/
+
+#define	IMAP_ADDR	IVMS_IMMR_BASE	/* physical base address of IMMR area	*/
+#define IMAP_SIZE	IVMS_IMAP_SIZE	/* mapped size of IMMR area		*/
+
+#define PCMCIA_MEM_ADDR	((uint)0xFE100000)
+#define PCMCIA_MEM_SIZE	((uint)(64 * 1024))
+
+#define	FEC_INTERRUPT	 9		/* = SIU_LEVEL4				*/
+#define IDE0_INTERRUPT	10		/* = IRQ5				*/
+#define	CPM_INTERRUPT	11		/* = SIU_LEVEL5 (was: SIU_LEVEL2)	*/
+#define PHY_INTERRUPT	12		/* = IRQ6				*/
+
+#define	MAX_HWIFS	1	/* overwrite default in include/asm-ppc/ide.h	*/
+
+/*
+ * Definitions for IDE0 Interface
+ */
+#define IDE0_BASE_OFFSET		0x0000	/* Offset in PCMCIA memory	*/
+#define IDE0_DATA_REG_OFFSET		0x0000
+#define IDE0_ERROR_REG_OFFSET		0x0081
+#define IDE0_NSECTOR_REG_OFFSET		0x0082
+#define IDE0_SECTOR_REG_OFFSET		0x0083
+#define IDE0_LCYL_REG_OFFSET		0x0084
+#define IDE0_HCYL_REG_OFFSET		0x0085
+#define IDE0_SELECT_REG_OFFSET		0x0086
+#define IDE0_STATUS_REG_OFFSET		0x0087
+#define IDE0_CONTROL_REG_OFFSET		0x0106
+#define IDE0_IRQ_REG_OFFSET		0x000A	/* not used			*/
+
+/* We don't use the 8259.
+*/
+#define NR_8259_INTS	0
+
+/* Generic 8xx type
+*/
+#define _MACH_8xx (_MACH_ivms8)
+
+#endif	/* __MACH_IVMS8_DEFS */
+
diff -urN linux-2.4.3-pre1/include/asm-ppc/mpc8xx.h linux/include/asm-ppc/mpc8xx.h
--- linux-2.4.3-pre1/include/asm-ppc/mpc8xx.h	Sat Nov 11 18:23:11 2000
+++ linux/include/asm-ppc/mpc8xx.h	Mon Dec  3 22:29:50 2001
@@ -33,12 +33,16 @@
 #include <asm/rpxclassic.h>
 #endif
 
-#if (defined(CONFIG_TQM860) || defined(CONFIG_TQM860L))
-#include <asm/tqm860.h>
+#if (defined(CONFIG_TQM860) || defined(CONFIG_TQM8xxL))
+#include <asm/tqm8xx.h>
 #endif
 
-#ifdef CONFIG_TQM8xxL
-#include <asm/tqm8xxL.h>
+#if defined(CONFIG_SPD823TS)
+#include <asm/spd8xx.h>
+#endif
+
+#if defined(CONFIG_IVMS8)
+#include <asm/ivms8.h>
 #endif
 
 /* I need this to get pt_regs.......
diff -urN linux-2.4.3-pre1/include/asm-ppc/pci.h linux/include/asm-ppc/pci.h
--- linux-2.4.3-pre1/include/asm-ppc/pci.h	Mon Jan 22 15:41:15 2001
+++ linux/include/asm-ppc/pci.h	Mon Dec  3 22:29:50 2001
@@ -34,6 +34,7 @@
  * 
  * Obsolete ! Drivers should now use pci_resource_to_bus
  */
+extern unsigned long phys_to_bus(unsigned long pa);
 extern unsigned long pci_phys_to_bus(unsigned long pa, int busnr);
 extern unsigned long pci_bus_to_phys(unsigned int ba, int busnr);
     
diff -urN linux-2.4.3-pre1/include/asm-ppc/processor.h linux/include/asm-ppc/processor.h
--- linux-2.4.3-pre1/include/asm-ppc/processor.h	Mon Jan 22 15:41:15 2001
+++ linux/include/asm-ppc/processor.h	Mon Dec  3 22:29:50 2001
@@ -516,6 +516,8 @@
 #define _MACH_8260	0x00002000	/* Generic 8260 */
 #define _MACH_tqm860	0x00004000	/* TQM860/L */
 #define _MACH_tqm8xxL	0x00008000	/* TQM8xxL */
+#define _MACH_spd8xxL	0x00010000	/* SPD8xx */
+#define _MACH_ibms8	0x00020000	/* IVMS8 */
 
 /* see residual.h for these */
 #define _PREP_Motorola 0x01  /* motorola prep */
diff -urN linux-2.4.3-pre1/include/asm-ppc/semaphore.h linux/include/asm-ppc/semaphore.h
--- linux-2.4.3-pre1/include/asm-ppc/semaphore.h	Tue May  2 13:05:40 2000
+++ linux/include/asm-ppc/semaphore.h	Mon Dec  3 22:29:50 2001
@@ -132,7 +132,7 @@
 
 #define DECLARE_RWSEM(name) __DECLARE_RWSEM_GENERIC(name, 0, 0)
 #define DECLARE_RWSEM_READ_LOCKED(name) __DECLARE_RWSEM_GENERIC(name, 1, 0)
-#define DECLAER_RWSEM_WRITE_LOCKED(name) __DECLARE_RWSEM_GENERIC(name, 0, 1)
+#define DECLARE_RWSEM_WRITE_LOCKED(name) __DECLARE_RWSEM_GENERIC(name, 0, 1)
 
 extern inline void init_rwsem(struct rw_semaphore *sem)
 {
diff -urN linux-2.4.3-pre1/include/asm-ppc/spd8xx.h linux/include/asm-ppc/spd8xx.h
--- linux-2.4.3-pre1/include/asm-ppc/spd8xx.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/spd8xx.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,105 @@
+/*
+ * A collection of structures, addresses, and values associated with
+ * Speech Design SPD8xxTS boards.
+ *
+ * Copyright (c) 2000 Wolfgang Denk (wd@denx.de)
+ */
+#ifndef __MACH_SPD8xx_DEFS
+#define __MACH_SPD8xx_DEFS
+
+#ifndef __ASSEMBLY__
+
+typedef	void (interrupt_handler_t)(void *);
+
+typedef struct serial_io {
+	int	(*getc)(void);
+	int	(*tstc)(void);
+	void	(*putc)(const char c);
+	void	(*printf)(const char *fmt, ...);
+} serial_io_t;
+
+typedef struct intr_util {
+	void	(*install_hdlr)(int, interrupt_handler_t *, void *);
+	void	(*free_hdlr)(int);
+} intr_util_t;
+
+
+/* A Board Information structure that is given to a program when
+ * ppcboot starts it up.
+ */
+typedef struct bd_info {
+	 unsigned long	bi_memstart;	/* start of  DRAM memory		*/
+	 unsigned long	bi_memsize;	/* size	 of  DRAM memory in bytes	*/
+	 unsigned long	bi_flashstart;	/* start of FLASH memory		*/
+	 unsigned long	bi_flashsize;	/* size	 of FLASH memory		*/
+	 unsigned long	bi_flashoffset; /* reserved area for startup monitor	*/
+	 unsigned long	bi_sramstart;	/* start of  SRAM memory		*/
+	 unsigned long	bi_sramsize;	/* size	 of  SRAM memory		*/
+	 unsigned long	bi_immr_base;	/* base of IMMR register		*/
+	 unsigned long	bi_bootflags;	/* boot / reboot flag (for LynxOS)	*/
+	 unsigned long	bi_ip_addr;	/* IP Address				*/
+	 unsigned char	bi_enetaddr[6]; /* Ethernet adress			*/
+	 unsigned char	bi_reserved[2]; /* -- just for alignment --		*/
+	 unsigned long	bi_intfreq;	/* Internal Freq, in MHz		*/
+	 unsigned long	bi_busfreq;	/* Bus Freq, in MHz			*/
+	 unsigned long	bi_baudrate;	/* Console Baudrate			*/
+	 serial_io_t	bi_serial_io;	/* Addr of monitor fnc for Console I/O	*/
+	 intr_util_t	bi_interrupt;	/* Addr of monitor fnc for Interrupts	*/
+} bd_t;
+
+#endif /* __ASSEMBLY__ */
+
+#define	SPD_IMMR_BASE	0xFFF00000	/* phys. addr of IMMR			*/
+#define	SPD_IMAP_SIZE	(64 * 1024)	/* size of mapped area			*/
+
+#define	IMAP_ADDR	SPD_IMMR_BASE	/* physical base address of IMMR area	*/
+#define IMAP_SIZE	SPD_IMAP_SIZE	/* mapped size of IMMR area		*/
+
+#define PCMCIA_MEM_ADDR	((uint)0xFE100000)
+#define PCMCIA_MEM_SIZE	((uint)(64 * 1024))
+
+#define IDE0_INTERRUPT	10		/* = IRQ5				*/
+#define	IDE1_INTERRUPT	12		/* = IRQ6				*/
+#define	CPM_INTERRUPT	13		/* = SIU_LEVEL6 (was: SIU_LEVEL2)	*/
+
+#define	MAX_HWIFS	2	/* overwrite default in include/asm-ppc/ide.h	*/
+
+/*
+ * Definitions for IDE0 Interface
+ */
+#define IDE0_BASE_OFFSET		0x0000	/* Offset in PCMCIA memory	*/
+#define IDE0_DATA_REG_OFFSET		0x0000
+#define IDE0_ERROR_REG_OFFSET		0x0081
+#define IDE0_NSECTOR_REG_OFFSET		0x0082
+#define IDE0_SECTOR_REG_OFFSET		0x0083
+#define IDE0_LCYL_REG_OFFSET		0x0084
+#define IDE0_HCYL_REG_OFFSET		0x0085
+#define IDE0_SELECT_REG_OFFSET		0x0086
+#define IDE0_STATUS_REG_OFFSET		0x0087
+#define IDE0_CONTROL_REG_OFFSET		0x0106
+#define IDE0_IRQ_REG_OFFSET		0x000A	/* not used			*/
+
+/*
+ * Definitions for IDE1 Interface
+ */
+#define IDE1_BASE_OFFSET		0x0C00	/* Offset in PCMCIA memory	*/
+#define IDE1_DATA_REG_OFFSET		0x0000
+#define IDE1_ERROR_REG_OFFSET		0x0081
+#define IDE1_NSECTOR_REG_OFFSET		0x0082
+#define IDE1_SECTOR_REG_OFFSET		0x0083
+#define IDE1_LCYL_REG_OFFSET		0x0084
+#define IDE1_HCYL_REG_OFFSET		0x0085
+#define IDE1_SELECT_REG_OFFSET		0x0086
+#define IDE1_STATUS_REG_OFFSET		0x0087
+#define IDE1_CONTROL_REG_OFFSET		0x0106
+#define IDE1_IRQ_REG_OFFSET		0x000A	/* not used			*/
+
+/* We don't use the 8259.
+*/
+#define NR_8259_INTS	0
+
+/* Generic 8xx type
+*/
+#define _MACH_8xx (_MACH_spd8xx)
+
+#endif	/* __MACH_SPD8xx_DEFS */
diff -urN linux-2.4.3-pre1/include/asm-ppc/system.h linux/include/asm-ppc/system.h
--- linux-2.4.3-pre1/include/asm-ppc/system.h	Sat Nov 11 18:23:11 2000
+++ linux/include/asm-ppc/system.h	Mon Dec  3 22:29:50 2001
@@ -49,15 +49,6 @@
 
 extern void xmon_irq(int, void *, struct pt_regs *);
 extern void xmon(struct pt_regs *excp);
-
-
-/* Data cache block flush - write out the cache line containing the
-   specified address and then invalidate it in the cache. */
-extern __inline__ void dcbf(void *line)
-{
-	asm("dcbf %0,%1; sync" : : "r" (line), "r" (0));
-}
-
 extern void print_backtrace(unsigned long *);
 extern void show_regs(struct pt_regs * regs);
 extern void flush_instruction_cache(void);
diff -urN linux-2.4.3-pre1/include/asm-ppc/time.h linux/include/asm-ppc/time.h
--- linux-2.4.3-pre1/include/asm-ppc/time.h	Sat Nov 11 18:23:11 2000
+++ linux/include/asm-ppc/time.h	Mon Dec  3 22:29:50 2001
@@ -2,7 +2,7 @@
  * $Id: time.h,v 1.12 1999/08/27 04:21:23 cort Exp $
  * Common time prototypes and such for all ppc machines.
  *
- * Written by Cort Dougan (cort@cs.nmt.edu) to merge
+ * Written by Cort Dougan (cort@fsmlabs.com) to merge
  * Paul Mackerras' version and mine for PReP and Pmac.
  */
 
@@ -16,6 +16,7 @@
 extern unsigned tb_ticks_per_jiffy;
 extern unsigned tb_to_us;
 extern unsigned tb_last_stamp;
+extern unsigned long disarm_decr[NR_CPUS];
 
 extern void to_tm(int tim, struct rtc_time * tm);
 extern time_t last_rtc_update;
diff -urN linux-2.4.3-pre1/include/asm-ppc/tlb.h linux/include/asm-ppc/tlb.h
--- linux-2.4.3-pre1/include/asm-ppc/tlb.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/tlb.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1 @@
+#include <asm-generic/tlb.h>
diff -urN linux-2.4.3-pre1/include/asm-ppc/tqm860.h linux/include/asm-ppc/tqm860.h
--- linux-2.4.3-pre1/include/asm-ppc/tqm860.h	Sat Nov 11 18:23:11 2000
+++ linux/include/asm-ppc/tqm860.h	Wed Dec 31 16:00:00 1969
@@ -1,67 +0,0 @@
-
-/*
- * A collection of structures, addresses, and values associated with
- * the TQ Systems TQM860 modules.  This was originally created for the
- * MBX860, and probably needs revisions for other boards (like the 821).
- * When this file gets out of control, we can split it up into more
- * meaningful pieces.
- *
- * Based on mbx.h, Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
- *
- * Copyright (c) 1999 Wolfgang Denk (wd@denx.de)
- */
-#ifdef __KERNEL__
-#ifndef __MACH_TQM860_DEFS
-#define __MACH_TQM860_DEFS
-
-/* A Board Information structure that is given to a program when
- * EPPC-Bug starts it up.
- */
-typedef struct bd_info {
-	 unsigned long	bi_memstart;	/* start of  DRAM memory */
-	 unsigned long	bi_memsize;	/* size  of  DRAM memory in bytes */
-	 unsigned long	bi_flashstart;	/* start of FLASH memory */
-	 unsigned long	bi_flashsize;	/* size  of FLASH memory */
-	 unsigned long	bi_flashoffset;	/* reserved area for startup monitor */
-	 unsigned long	bi_sramstart;	/* start of  SRAM memory */
-	 unsigned long	bi_sramsize;	/* size  of  SRAM memory */
-	 unsigned long	bi_immr_base;	/* base of IMMR register */
-	 unsigned long	bi_bootflags;	/* boot / reboot flag (for LynxOS) */
-	 unsigned long	bi_ip_addr;	/* IP Address */
-	 unsigned char	bi_enetaddr[6];	/* Ethernet adress */
-	 unsigned char	bi_reserved[2];	/* -- just for alignment -- */
-	 unsigned long	bi_putchar;	/* Addr of monitor putchar() to Console	*/
-	 unsigned long	bi_intfreq;	/* Internal Freq, in MHz */
-	 unsigned long	bi_busfreq;	/* Bus Freq, in MHz */
-	 unsigned long	bi_baudrate;	/* Console Baudrate */
-} bd_t;
-
-/* Configuration options for TQ Systems TQM860 mini module
- */
-
-#define	TQM_RESET_ADDR	0x40000100	/* Monitor Reset Entry */
-
-#define	TQM_IMMR_BASE	0xFFF00000	/* phys. addr of IMMR */
-#define TQM_IMAP_SIZE	(64 * 1024)	/* size of mapped area */
-
-#define	TQM_CLOCKRATE	50		/* 50 MHz Clock */
-#define	TQM_BAUDRATE	115200		/* Console baud rate */
-#define	TQM_IP_ADDR	0x0A000063	/* IP addr: 10.0.0.99 */
-
-#define	TQM_SERVER_IP	"10.0.0.3"	/* NFS server IP addr */
-#define	TQM_SERVER_DIR	"/LinuxPPC"	/* NFS exported root */
-
-#define	IMAP_ADDR	TQM_IMMR_BASE	/* physical base address of IMMR area */
-#define IMAP_SIZE	TQM_IMAP_SIZE	/* mapped size of IMMR area */
-
-/* We don't use the 8259.
-*/
-#define NR_8259_INTS	0
-
-/* Generic 8xx type
-*/
-#define _MACH_8xx (_MACH_tqm860)
-
-#endif	/* __MACH_TQM860_DEFS */
-
-#endif /* __KERNEL__ */
diff -urN linux-2.4.3-pre1/include/asm-ppc/tqm8xx.h linux/include/asm-ppc/tqm8xx.h
--- linux-2.4.3-pre1/include/asm-ppc/tqm8xx.h	Wed Dec 31 16:00:00 1969
+++ linux/include/asm-ppc/tqm8xx.h	Mon Dec  3 22:29:50 2001
@@ -0,0 +1,76 @@
+/*
+ * A collection of structures, addresses, and values associated with
+ * the TQ Systems TQM8xx(L) modules.  This was originally created for the
+ * MBX860, and probably needs revisions for other boards (like the 821).
+ * When this file gets out of control, we can split it up into more
+ * meaningful pieces.
+ *
+ * Based on mbx.h, Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
+ *
+ * Copyright (c) 1999,2000 Wolfgang Denk (wd@denx.de)
+ */
+#ifndef __MACH_TQM8xx_DEFS
+#define __MACH_TQM8xx_DEFS
+
+#ifndef __ASSEMBLY__
+
+typedef	void (interrupt_handler_t)(void *);
+
+typedef struct serial_io {
+	int	(*getc)(void);
+	int	(*tstc)(void);
+	void	(*putc)(const char c);
+	void	(*printf)(const char *fmt, ...);
+} serial_io_t;
+
+typedef struct intr_util {
+	void	(*install_hdlr)(int, interrupt_handler_t *, void *);
+	void	(*free_hdlr)(int);
+} intr_util_t;
+
+
+/* A Board Information structure that is given to a program when
+ * ppcboot starts it up.
+ */
+typedef struct bd_info {
+	 unsigned long	bi_memstart;	/* start of  DRAM memory		*/
+	 unsigned long	bi_memsize;	/* size	 of  DRAM memory in bytes	*/
+	 unsigned long	bi_flashstart;	/* start of FLASH memory		*/
+	 unsigned long	bi_flashsize;	/* size	 of FLASH memory		*/
+	 unsigned long	bi_flashoffset; /* reserved area for startup monitor	*/
+	 unsigned long	bi_sramstart;	/* start of  SRAM memory		*/
+	 unsigned long	bi_sramsize;	/* size	 of  SRAM memory		*/
+	 unsigned long	bi_immr_base;	/* base of IMMR register		*/
+	 unsigned long	bi_bootflags;	/* boot / reboot flag (for LynxOS)	*/
+	 unsigned long	bi_ip_addr;	/* IP Address				*/
+	 unsigned char	bi_enetaddr[6]; /* Ethernet adress			*/
+	 unsigned char	bi_reserved[2]; /* -- just for alignment --		*/
+	 unsigned long	bi_intfreq;	/* Internal Freq, in MHz		*/
+	 unsigned long	bi_busfreq;	/* Bus Freq, in MHz			*/
+	 unsigned long	bi_baudrate;	/* Console Baudrate			*/
+	 serial_io_t	bi_serial_io;	/* Addr of monitor fnc for Console I/O	*/
+	 intr_util_t	bi_interrupt;	/* Addr of monitor fnc for Interrupts	*/
+} bd_t;
+
+#endif /* __ASSEMBLY__ */
+
+#define	TQM_IMMR_BASE	0xFFF00000	/* phys. addr of IMMR			*/
+#define	TQM_IMAP_SIZE	(64 * 1024)	/* size of mapped area			*/
+
+#define	IMAP_ADDR	TQM_IMMR_BASE	/* physical base address of IMMR area	*/
+#define IMAP_SIZE	TQM_IMAP_SIZE	/* mapped size of IMMR area		*/
+
+/* We don't use the 8259.
+*/
+#define NR_8259_INTS	0
+
+/* Generic 8xx type
+*/
+#if defined(CONFIG_TQM8xxL)
+#define _MACH_8xx (_MACH_tqm8xxL)
+#endif
+#if defined(CONFIG_TQM860)
+#define _MACH_8xx (_MACH_tqm860)
+#endif
+
+#endif	/* __MACH_TQM8xx_DEFS */
diff -urN linux-2.4.3-pre1/include/asm-ppc/tqm8xxL.h linux/include/asm-ppc/tqm8xxL.h
--- linux-2.4.3-pre1/include/asm-ppc/tqm8xxL.h	Sat Nov 11 18:23:11 2000
+++ linux/include/asm-ppc/tqm8xxL.h	Wed Dec 31 16:00:00 1969
@@ -1,68 +0,0 @@
-
-/*
- * A collection of structures, addresses, and values associated with
- * the TQ Systems TQM850L modules.  This was originally created for the
- * MBX860, and probably needs revisions for other boards (like the 821).
- * When this file gets out of control, we can split it up into more
- * meaningful pieces.
- *
- * Based on mbx.h, Copyright (c) 1997 Dan Malek (dmalek@jlc.net)
- *
- * Copyright (c) 1999 Wolfgang Denk (wd@denx.de)
- */
-#ifdef __KERNEL__
-#ifndef __MACH_TQM8xxL_DEFS
-#define __MACH_TQM8xxL_DEFS
-
-/* A Board Information structure that is given to a program when
- * EPPC-Bug starts it up.
- */
-typedef struct bd_info {
-	 unsigned long	bi_memstart;	/* start of  DRAM memory */
-	 unsigned long	bi_memsize;	/* size  of  DRAM memory in bytes */
-	 unsigned long	bi_flashstart;	/* start of FLASH memory */
-	 unsigned long	bi_flashsize;	/* size  of FLASH memory */
-	 unsigned long	bi_flashoffset;	/* reserved area for startup monitor */
-	 unsigned long	bi_sramstart;	/* start of  SRAM memory */
-	 unsigned long	bi_sramsize;	/* size  of  SRAM memory */
-	 unsigned long	bi_immr_base;	/* base of IMMR register */
-	 unsigned long	bi_bootflags;	/* boot / reboot flag (for LynxOS) */
-	 unsigned long	bi_ip_addr;	/* IP Address */
-	 unsigned char	bi_enetaddr[6];	/* Ethernet adress */
-	 unsigned char	bi_reserved[2];	/* -- just for alignment -- */
-	 unsigned long	bi_putchar;	/* Addr of monitor putchar() to Console	*/
-	 unsigned long	bi_intfreq;	/* Internal Freq, in MHz */
-	 unsigned long	bi_busfreq;	/* Bus Freq, in MHz */
-	 unsigned long	bi_baudrate;	/* Console Baudrate */
-} bd_t;
-
-/* Configuration options for TQ Systems TQM850L mini module
- */
-
-#define	TQM_RESET_ADDR	0x40000100	/* Monitor Reset Entry */
-
-#define	TQM_IMMR_BASE	0xFFF00000	/* phys. addr of IMMR */
-#define	TQM_IMAP_SIZE	(64 * 1024)	/* size of mapped area */
-
-#define	TQM_CLOCKRATE	50		/* 50 MHz Clock */
-/*#define	TQM_BAUDRATE	115200	*/	/* Console baud rate */
-#define	TQM_BAUDRATE	38400		/* Console baud rate */
-#define	TQM_IP_ADDR	0x0A000063	/* IP addr: 10.0.0.99 */
-
-#define	TQM_SERVER_IP	"10.0.0.2"	/* NFS server IP addr */
-#define	TQM_SERVER_DIR	"/LinuxPPC"	/* NFS exported root */
-
-#define	IMAP_ADDR	TQM_IMMR_BASE	/* physical base address of IMMR area */
-#define IMAP_SIZE	TQM_IMAP_SIZE	/* mapped size of IMMR area */
-
-/* We don't use the 8259.
-*/
-#define NR_8259_INTS	0
-
-/* Generic 8xx type
-*/
-#define _MACH_8xx (_MACH_tqm8xxL)
-
-#endif	/* __MACH_TQM8xxL_DEFS */
-
-#endif /* __KERNEL__ */
diff -urN linux-2.4.3-pre1/include/linux/fs.h linux/include/linux/fs.h
--- linux-2.4.3-pre1/include/linux/fs.h	Mon Dec  3 22:29:33 2001
+++ linux/include/linux/fs.h	Mon Dec  3 22:29:50 2001
@@ -381,6 +381,7 @@
 	struct vm_area_struct	*i_mmap;	/* list of private mappings */
 	struct vm_area_struct	*i_mmap_shared; /* list of shared mappings */
 	spinlock_t		i_shared_lock;  /* and spinlock protecting it */
+	int			gfp_mask;	/* how to allocate the pages */
 };
 
 struct block_device {
diff -urN linux-2.4.3-pre1/include/linux/init.h linux/include/linux/init.h
--- linux-2.4.3-pre1/include/linux/init.h	Wed Feb 21 16:09:56 2001
+++ linux/include/linux/init.h	Mon Dec  3 22:29:50 2001
@@ -86,7 +86,27 @@
 #define __FINIT		.previous
 #define __INITDATA	.section	".data.init","aw"
 
+/**
+ * module_init() - driver initialization entry point
+ * @x: function to be run at kernel boot time or module insertion
+ * 
+ * module_init() will add the driver initialization routine in
+ * the "__initcall.int" code segment if the driver is checked as
+ * "y" or static, or else it will wrap the driver initialization
+ * routine with init_module() which is used by insmod and
+ * modprobe when the driver is used as a module.
+ */
 #define module_init(x)	__initcall(x);
+
+/**
+ * module_exit() - driver exit entry point
+ * @x: function to be run when driver is removed
+ * 
+ * module_exit() will wrap the driver clean-up code
+ * with cleanup_module() when used with rmmod when
+ * the driver is a module.  If the driver is statically
+ * compiled into the kernel, module_exit() has no effect.
+ */
 #define module_exit(x)	__exitcall(x);
 
 #else
diff -urN linux-2.4.3-pre1/include/linux/loop.h linux/include/linux/loop.h
--- linux-2.4.3-pre1/include/linux/loop.h	Mon Dec 11 12:50:30 2000
+++ linux/include/linux/loop.h	Mon Dec  3 22:29:50 2001
@@ -9,17 +9,23 @@
  * Written by Theodore Ts'o, 3/29/93.
  *
  * Copyright 1993 by Theodore Ts'o.  Redistribution of this file is
- * permitted under the GNU Public License.
+ * permitted under the GNU General Public License.
  */
 
 #define LO_NAME_SIZE	64
 #define LO_KEY_SIZE	32
 
 #ifdef __KERNEL__
-       
+
+/* Possible states of device */
+enum {
+	Lo_unbound,
+	Lo_bound,
+	Lo_rundown,
+};
+
 struct loop_device {
 	int		lo_number;
-	struct dentry	*lo_dentry;
 	int		lo_refcnt;
 	kdev_t		lo_device;
 	int		lo_offset;
@@ -39,19 +45,45 @@
 	struct file *	lo_backing_file;
 	void		*key_data; 
 	char		key_reserved[48]; /* for use by the filter modules */
+
+	int		lo_blksize;
+	int		old_gfp_mask;
+
+	spinlock_t		lo_lock;
+	struct buffer_head	*lo_bh;
+	struct buffer_head	*lo_bhtail;
+	int			lo_state;
+	struct semaphore	lo_sem;
+	struct semaphore	lo_ctl_mutex;
+	struct semaphore	lo_bh_mutex;
+	atomic_t		lo_pending;
 };
 
 typedef	int (* transfer_proc_t)(struct loop_device *, int cmd,
 				char *raw_buf, char *loop_buf, int size,
 				int real_block);
 
+extern inline int lo_do_transfer(struct loop_device *lo, int cmd, char *rbuf,
+				 char *lbuf, int size, int rblock)
+{
+	if (!lo->transfer)
+		return 0;
+
+	return lo->transfer(lo, cmd, rbuf, lbuf, size, rblock);
+}
+
+/*
+ * used to throttle loop_thread so bdflush/kswapd doesn't go nuts
+ */
+#define LOOP_MAX_BUFFERS	2048
+
 #endif /* __KERNEL__ */
 
 /*
  * Loop flags
  */
-#define LO_FLAGS_DO_BMAP	0x00000001
-#define LO_FLAGS_READ_ONLY	0x00000002
+#define LO_FLAGS_DO_BMAP	1
+#define LO_FLAGS_READ_ONLY	2
 
 /* 
  * Note that this structure gets the wrong offsets when directly used
@@ -102,9 +134,8 @@
 /* Support for loadable transfer modules */
 struct loop_func_table {
 	int number; 	/* filter type */ 
-	int (*transfer)(struct loop_device *lo, int cmd, 
-			char *raw_buf, char *loop_buf, int size,
-			int real_block);
+	int (*transfer)(struct loop_device *lo, int cmd, char *raw_buf,
+			char *loop_buf, int size, int real_block);
 	int (*init)(struct loop_device *, struct loop_info *); 
 	/* release is called from loop_unregister_transfer or clr_fd */
 	int (*release)(struct loop_device *); 
diff -urN linux-2.4.3-pre1/include/linux/mm.h linux/include/linux/mm.h
--- linux-2.4.3-pre1/include/linux/mm.h	Wed Feb 21 16:09:58 2001
+++ linux/include/linux/mm.h	Mon Dec  3 22:29:50 2001
@@ -481,11 +481,6 @@
 
 #define GFP_DMA		__GFP_DMA
 
-/* Flag - indicates that the buffer can be taken from high memory which is not
-   permanently mapped by the kernel */
-
-#define GFP_HIGHMEM	__GFP_HIGHMEM
-
 /* vma is the first one with  address < vma->vm_end,
  * and even  address < vma->vm_start. Have to extend vma. */
 static inline int expand_stack(struct vm_area_struct * vma, unsigned long address)
diff -urN linux-2.4.3-pre1/include/linux/openpic.h linux/include/linux/openpic.h
--- linux-2.4.3-pre1/include/linux/openpic.h	Wed Feb  9 19:43:47 2000
+++ linux/include/linux/openpic.h	Wed Dec 31 16:00:00 1969
@@ -1,367 +0,0 @@
-/*
- *  linux/openpic.h -- OpenPIC definitions
- *
- *  Copyright (C) 1997 Geert Uytterhoeven
- *
- *  This file is based on the following documentation:
- *
- *	The Open Programmable Interrupt Controller (PIC)
- *	Register Interface Specification Revision 1.2
- *
- *	Issue Date: October 1995
- *
- *	Issued jointly by Advanced Micro Devices and Cyrix Corporation
- *
- *	AMD is a registered trademark of Advanced Micro Devices, Inc.
- *	Copyright (C) 1995, Advanced Micro Devices, Inc. and Cyrix, Inc.
- *	All Rights Reserved.
- *
- *  To receive a copy of this documentation, send an email to openpic@amd.com.
- *
- *  This file is subject to the terms and conditions of the GNU General Public
- *  License.  See the file COPYING in the main directory of this archive
- *  for more details.
- */
-
-#ifndef _LINUX_OPENPIC_H
-#define _LINUX_OPENPIC_H
-
-#if !defined(__powerpc__) && !defined(__i386__)
-#error Unsupported OpenPIC platform
-#endif
-
-
-#ifdef __KERNEL__
-
-    /*
-     *  OpenPIC supports up to 2048 interrupt sources and up to 32 processors
-     */
-
-#define OPENPIC_MAX_SOURCES	2048
-#define OPENPIC_MAX_PROCESSORS	32
-
-#define OPENPIC_NUM_TIMERS	4
-#define OPENPIC_NUM_IPI		4
-#define OPENPIC_NUM_PRI		16
-#define OPENPIC_NUM_VECTORS	256
-
-
-    /*
-     *  Vector numbers
-     */
-
-#define OPENPIC_VEC_SOURCE      16    /* and up */
-#define OPENPIC_VEC_TIMER       64    /* and up */
-#define OPENPIC_VEC_IPI         72    /* and up */
-#define OPENPIC_VEC_SPURIOUS    127
-
-
-    /*
-     *  OpenPIC Registers are 32 bits and aligned on 128 bit boundaries
-     */
-
-typedef struct _OpenPIC_Reg {
-    u_int Reg;					/* Little endian! */
-    char Pad[0xc];
-} OpenPIC_Reg;
-
-
-    /*
-     *  Per Processor Registers
-     */
-
-typedef struct _OpenPIC_Processor {
-    /*
-     *  Private Shadow Registers (for SLiC backwards compatibility)
-     */
-    u_int IPI0_Dispatch_Shadow;			/* Write Only */
-    char Pad1[0x4];
-    u_int IPI0_Vector_Priority_Shadow;		/* Read/Write */
-    char Pad2[0x34];
-    /*
-     *  Interprocessor Interrupt Command Ports
-     */
-    OpenPIC_Reg _IPI_Dispatch[OPENPIC_NUM_IPI];	/* Write Only */
-    /*
-     *  Current Task Priority Register
-     */
-    OpenPIC_Reg _Current_Task_Priority;		/* Read/Write */
-#ifndef __powerpc__
-    /*
-     *  Who Am I Register
-     */
-    OpenPIC_Reg _Who_Am_I;			/* Read Only */
-#else
-    char Pad3[0x10];
-#endif
-#ifndef __i386__
-    /*
-     *  Interrupt Acknowledge Register
-     */
-    OpenPIC_Reg _Interrupt_Acknowledge;		/* Read Only */
-#else
-    char Pad4[0x10];
-#endif
-    /*
-     *  End of Interrupt (EOI) Register
-     */
-    OpenPIC_Reg _EOI;				/* Read/Write */
-    char Pad5[0xf40];
-} OpenPIC_Processor;
-
-
-    /*
-     *  Timer Registers
-     */
-
-typedef struct _OpenPIC_Timer {
-    OpenPIC_Reg _Current_Count;			/* Read Only */
-    OpenPIC_Reg _Base_Count;			/* Read/Write */
-    OpenPIC_Reg _Vector_Priority;		/* Read/Write */
-    OpenPIC_Reg _Destination;			/* Read/Write */
-} OpenPIC_Timer;
-
-
-    /*
-     *  Global Registers
-     */
-
-typedef struct _OpenPIC_Global {
-    /*
-     *  Feature Reporting Registers
-     */
-    OpenPIC_Reg _Feature_Reporting0;		/* Read Only */
-    OpenPIC_Reg _Feature_Reporting1;		/* Future Expansion */
-    /*
-     *  Global Configuration Registers
-     */
-    OpenPIC_Reg _Global_Configuration0;		/* Read/Write */
-    OpenPIC_Reg _Global_Configuration1;		/* Future Expansion */
-    /*
-     *  Vendor Specific Registers
-     */
-    OpenPIC_Reg _Vendor_Specific[4];
-    /*
-     *  Vendor Identification Register
-     */
-    OpenPIC_Reg _Vendor_Identification;		/* Read Only */
-    /*
-     *  Processor Initialization Register
-     */
-    OpenPIC_Reg _Processor_Initialization;	/* Read/Write */
-    /*
-     *  IPI Vector/Priority Registers
-     */
-    OpenPIC_Reg _IPI_Vector_Priority[OPENPIC_NUM_IPI];	/* Read/Write */
-    /*
-     *  Spurious Vector Register
-     */
-    OpenPIC_Reg _Spurious_Vector;		/* Read/Write */
-    /*
-     *  Global Timer Registers
-     */
-    OpenPIC_Reg _Timer_Frequency;		/* Read/Write */
-    OpenPIC_Timer Timer[OPENPIC_NUM_TIMERS];
-    char Pad1[0xee00];
-} OpenPIC_Global;
-
-
-    /*
-     *  Interrupt Source Registers
-     */
-
-typedef struct _OpenPIC_Source {
-    OpenPIC_Reg _Vector_Priority;		/* Read/Write */
-    OpenPIC_Reg _Destination;			/* Read/Write */
-} OpenPIC_Source;
-
-
-    /*
-     *  OpenPIC Register Map
-     */
-
-struct OpenPIC {
-#ifndef __powerpc__
-    /*
-     *  Per Processor Registers --- Private Access
-     */
-    OpenPIC_Processor Private;
-#else
-    char Pad1[0x1000];
-#endif
-    /*
-     *  Global Registers
-     */
-    OpenPIC_Global Global;
-    /*
-     *  Interrupt Source Configuration Registers
-     */
-    OpenPIC_Source Source[OPENPIC_MAX_SOURCES];
-    /*
-     *  Per Processor Registers
-     */
-    OpenPIC_Processor Processor[OPENPIC_MAX_PROCESSORS];
-};
-
-extern volatile struct OpenPIC *OpenPIC;
-extern u_int OpenPIC_NumInitSenses;
-extern u_char *OpenPIC_InitSenses;
-
-
-    /*
-     *  Current Task Priority Register
-     */
-
-#define OPENPIC_CURRENT_TASK_PRIORITY_MASK	0x0000000f
-
-    /*
-     *  Who Am I Register
-     */
-
-#define OPENPIC_WHO_AM_I_ID_MASK		0x0000001f
-
-    /*
-     *  Feature Reporting Register 0
-     */
-
-#define OPENPIC_FEATURE_LAST_SOURCE_MASK	0x07ff0000
-#define OPENPIC_FEATURE_LAST_SOURCE_SHIFT	16
-#define OPENPIC_FEATURE_LAST_PROCESSOR_MASK	0x00001f00
-#define OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT	8
-#define OPENPIC_FEATURE_VERSION_MASK		0x000000ff
-
-    /*
-     *  Global Configuration Register 0
-     */
-
-#define OPENPIC_CONFIG_RESET			0x80000000
-#define OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE	0x20000000
-#define OPENPIC_CONFIG_BASE_MASK		0x000fffff
-
-    /*
-     *  Vendor Identification Register
-     */
-
-#define OPENPIC_VENDOR_ID_STEPPING_MASK		0x00ff0000
-#define OPENPIC_VENDOR_ID_STEPPING_SHIFT	16
-#define OPENPIC_VENDOR_ID_DEVICE_ID_MASK	0x0000ff00
-#define OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT	8
-#define OPENPIC_VENDOR_ID_VENDOR_ID_MASK	0x000000ff
-
-    /*
-     *  Vector/Priority Registers
-     */
-
-#define OPENPIC_MASK				0x80000000
-#define OPENPIC_ACTIVITY			0x40000000	/* Read Only */
-#define OPENPIC_PRIORITY_MASK			0x000f0000
-#define OPENPIC_PRIORITY_SHIFT			16
-#define OPENPIC_VECTOR_MASK			0x000000ff
-
-
-    /*
-     *  Interrupt Source Registers
-     */
-
-#define OPENPIC_POLARITY_POSITIVE		0x00800000
-#define OPENPIC_POLARITY_NEGATIVE		0x00000000
-#define OPENPIC_POLARITY_MASK			0x00800000
-#define OPENPIC_SENSE_LEVEL			0x00400000
-#define OPENPIC_SENSE_EDGE			0x00000000
-#define OPENPIC_SENSE_MASK			0x00400000
-
-
-    /*
-     *  Timer Registers
-     */
-
-#define OPENPIC_COUNT_MASK			0x7fffffff
-#define OPENPIC_TIMER_TOGGLE			0x80000000
-#define OPENPIC_TIMER_COUNT_INHIBIT		0x80000000
-
-
-    /*
-     *  Aliases to make life simpler
-     */
-
-/* Per Processor Registers */
-#define IPI_Dispatch(i)			_IPI_Dispatch[i].Reg
-#define Current_Task_Priority		_Current_Task_Priority.Reg
-#ifndef __powerpc__
-#define Who_Am_I			_Who_Am_I.Reg
-#endif
-#ifndef __i386__
-#define Interrupt_Acknowledge		_Interrupt_Acknowledge.Reg
-#endif
-#define EOI				_EOI.Reg
-
-/* Global Registers */
-#define Feature_Reporting0		_Feature_Reporting0.Reg
-#define Feature_Reporting1		_Feature_Reporting1.Reg
-#define Global_Configuration0		_Global_Configuration0.Reg
-#define Global_Configuration1		_Global_Configuration1.Reg
-#define Vendor_Specific(i)		_Vendor_Specific[i].Reg
-#define Vendor_Identification		_Vendor_Identification.Reg
-#define Processor_Initialization	_Processor_Initialization.Reg
-#define IPI_Vector_Priority(i)		_IPI_Vector_Priority[i].Reg
-#define Spurious_Vector			_Spurious_Vector.Reg
-#define Timer_Frequency			_Timer_Frequency.Reg
-
-/* Timer Registers */
-#define Current_Count			_Current_Count.Reg
-#define Base_Count			_Base_Count.Reg
-#define Vector_Priority			_Vector_Priority.Reg
-#define Destination			_Destination.Reg
-
-/* Interrupt Source Registers */
-#define Vector_Priority			_Vector_Priority.Reg
-#define Destination			_Destination.Reg
-
-    /*
-     *  OpenPIC Operations
-     */
-
-/* Global Operations */
-extern void openpic_init(int);
-extern void openpic_reset(void);
-extern void openpic_enable_8259_pass_through(void);
-extern void openpic_disable_8259_pass_through(void);
-#ifndef __i386__
-extern u_int openpic_irq(u_int cpu);
-#endif
-#ifndef __powerpc__
-extern void openpic_eoi(void);
-extern u_int openpic_get_priority(void);
-extern void openpic_set_priority(u_int pri);
-#else
-extern void openpic_eoi(u_int cpu);
-extern u_int openpic_get_priority(u_int cpu);
-extern void openpic_set_priority(u_int cpu, u_int pri);
-#endif
-extern u_int openpic_get_spurious(void);
-extern void openpic_set_spurious(u_int vector);
-extern void openpic_init_processor(u_int cpumask);
-
-/* Interprocessor Interrupts */
-extern void openpic_initipi(u_int ipi, u_int pri, u_int vector);
-#ifndef __powerpc__
-extern void openpic_cause_IPI(u_int ipi, u_int cpumask);
-#else
-extern void openpic_cause_IPI(u_int cpu, u_int ipi, u_int cpumask);
-#endif
-
-/* Timer Interrupts */
-extern void openpic_inittimer(u_int timer, u_int pri, u_int vector);
-extern void openpic_maptimer(u_int timer, u_int cpumask);
-
-/* Interrupt Sources */
-extern void openpic_enable_irq(u_int irq);
-extern void openpic_disable_irq(u_int irq);
-extern void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity,
-			    int is_level);
-extern void openpic_mapirq(u_int irq, u_int cpumask);
-extern void openpic_set_sense(u_int irq, int sense);
-
-#endif /* __KERNEL__ */
-
-#endif /* _LINUX_OPENPIC_H */
diff -urN linux-2.4.3-pre1/include/linux/pagemap.h linux/include/linux/pagemap.h
--- linux-2.4.3-pre1/include/linux/pagemap.h	Wed Feb 21 16:10:01 2001
+++ linux/include/linux/pagemap.h	Mon Dec  3 22:29:50 2001
@@ -29,9 +29,13 @@
 #define PAGE_CACHE_ALIGN(addr)	(((addr)+PAGE_CACHE_SIZE-1)&PAGE_CACHE_MASK)
 
 #define page_cache_get(x)	get_page(x)
-#define page_cache_alloc()	alloc_pages(GFP_HIGHUSER, 0)
 #define page_cache_free(x)	__free_page(x)
 #define page_cache_release(x)	__free_page(x)
+
+static inline struct page *page_cache_alloc(struct address_space *x)
+{
+	return alloc_pages(x->gfp_mask, 0);
+}
 
 /*
  * From a kernel address, get the "struct page *"
diff -urN linux-2.4.3-pre1/include/linux/pci_ids.h linux/include/linux/pci_ids.h
--- linux-2.4.3-pre1/include/linux/pci_ids.h	Sat Feb  3 12:48:00 2001
+++ linux/include/linux/pci_ids.h	Mon Dec  3 22:29:50 2001
@@ -505,7 +505,10 @@
 #define PCI_DEVICE_ID_APPLE_BANDIT	0x0001
 #define PCI_DEVICE_ID_APPLE_GC		0x0002
 #define PCI_DEVICE_ID_APPLE_HYDRA	0x000e
-#define PCI_DEVICE_ID_APPLE_UNINORTH	0x0020
+#define PCI_DEVICE_ID_APPLE_UNI_N_FW	0x0018
+#define PCI_DEVICE_ID_APPLE_KL_USB	0x0019
+#define PCI_DEVICE_ID_APPLE_UNI_N_AGP	0x0020
+#define PCI_DEVICE_ID_APPLE_UNI_N_GMAC	0x0021
 
 #define PCI_VENDOR_ID_YAMAHA		0x1073
 #define PCI_DEVICE_ID_YAMAHA_724	0x0004
diff -urN linux-2.4.3-pre1/init/main.c linux/init/main.c
--- linux-2.4.3-pre1/init/main.c	Wed Jan  3 20:45:26 2001
+++ linux/init/main.c	Mon Dec  3 22:29:51 2001
@@ -151,6 +151,7 @@
 	{ "nfs",     0x00ff },
 	{ "hda",     0x0300 },
 	{ "hdb",     0x0340 },
+	{ "loop",    0x0700 },
 	{ "hdc",     0x1600 },
 	{ "hdd",     0x1640 },
 	{ "hde",     0x2100 },
diff -urN linux-2.4.3-pre1/kernel/ksyms.c linux/kernel/ksyms.c
--- linux-2.4.3-pre1/kernel/ksyms.c	Fri Feb  9 11:29:44 2001
+++ linux/kernel/ksyms.c	Mon Dec  3 22:29:51 2001
@@ -121,6 +121,7 @@
 EXPORT_SYMBOL(kmap_high);
 EXPORT_SYMBOL(kunmap_high);
 EXPORT_SYMBOL(highmem_start_page);
+EXPORT_SYMBOL(create_bounce);
 #endif
 
 /* filesystem internal functions */
diff -urN linux-2.4.3-pre1/mm/filemap.c linux/mm/filemap.c
--- linux-2.4.3-pre1/mm/filemap.c	Fri Feb 16 16:06:17 2001
+++ linux/mm/filemap.c	Mon Dec  3 22:29:51 2001
@@ -559,7 +559,7 @@
 	if (page)
 		return 0;
 
-	page = page_cache_alloc();
+	page = page_cache_alloc(mapping);
 	if (!page)
 		return -ENOMEM;
 
@@ -1174,7 +1174,7 @@
 		 */
 		if (!cached_page) {
 			spin_unlock(&pagecache_lock);
-			cached_page = page_cache_alloc();
+			cached_page = page_cache_alloc(mapping);
 			if (!cached_page) {
 				desc->error = -ENOMEM;
 				break;
@@ -1474,7 +1474,7 @@
 	 */
 	old_page = page;
 	if (no_share) {
-		struct page *new_page = page_cache_alloc();
+		struct page *new_page = alloc_page(GFP_HIGHUSER);
 
 		if (new_page) {
 			copy_user_highpage(new_page, old_page, address);
@@ -2319,7 +2319,7 @@
 	page = __find_get_page(mapping, index, hash);
 	if (!page) {
 		if (!cached_page) {
-			cached_page = page_cache_alloc();
+			cached_page = page_cache_alloc(mapping);
 			if (!cached_page)
 				return ERR_PTR(-ENOMEM);
 		}
@@ -2382,7 +2382,7 @@
 	page = __find_lock_page(mapping, index, hash);
 	if (!page) {
 		if (!*cached_page) {
-			*cached_page = page_cache_alloc();
+			*cached_page = page_cache_alloc(mapping);
 			if (!*cached_page)
 				return NULL;
 		}
diff -urN linux-2.4.3-pre1/mm/memory.c linux/mm/memory.c
--- linux-2.4.3-pre1/mm/memory.c	Tue Feb  6 22:49:21 2001
+++ linux/mm/memory.c	Mon Dec  3 22:29:51 2001
@@ -870,7 +870,7 @@
 	 * Ok, we need to copy. Oh, well..
 	 */
 	spin_unlock(&mm->page_table_lock);
-	new_page = page_cache_alloc();
+	new_page = alloc_page(GFP_HIGHUSER);
 	if (!new_page)
 		return -1;
 	spin_lock(&mm->page_table_lock);
diff -urN linux-2.4.3-pre1/mm/shmem.c linux/mm/shmem.c
--- linux-2.4.3-pre1/mm/shmem.c	Sat Jan 27 19:50:08 2001
+++ linux/mm/shmem.c	Mon Dec  3 22:29:51 2001
@@ -321,7 +321,7 @@
 		inode->i_sb->u.shmem_sb.free_blocks--;
 		spin_unlock (&inode->i_sb->u.shmem_sb.stat_lock);
 		/* Ok, get a new page */
-		page = page_cache_alloc();
+		page = page_cache_alloc(mapping);
 		if (!page)
 			goto oom;
 		clear_user_highpage(page, address);
@@ -338,7 +338,7 @@
 	up(&inode->i_sem);
 
 	if (no_share) {
-		struct page *new_page = page_cache_alloc();
+		struct page *new_page = page_cache_alloc(inode->i_mapping);
 
 		if (new_page) {
 			copy_user_highpage(new_page, page, address);
