diff -u --recursive --new-file v2.1.27/linux/Documentation/Changes linux/Documentation/Changes
--- v2.1.27/linux/Documentation/Changes	Thu Feb 27 10:57:28 1997
+++ linux/Documentation/Changes	Sun Mar  2 16:34:34 1997
@@ -26,7 +26,7 @@
 http://www.datanet.hu/generations/linux/Changes2.html is an
 English-language HTML version.
 
-Last updated: February 11, 1997.
+Last updated: February 27, 1997.
 Current Author: Chris Ricker (gt1355b@prism.gatech.edu).
 
 Current Minimal Requirements
@@ -38,7 +38,7 @@
 - Kernel modules	 modutils-2.1.23
 - Gnu C 		 2.7.2.1
 - Binutils		 2.7.0.3
-- Linux C Library	 5.4.17
+- Linux C Library	 5.4.23
 - Dynamic Linker (ld.so) 1.8.5
 - Linux C++ Library	 2.7.2.1
 - Procps		 1.01
@@ -73,7 +73,7 @@
    For modules to work, you need to be running libc-5.4.x or greater.
 Since updates to libc fix other problems as well (security flaws, for
 example) and since 5.4.7 is missing a few needed symbols, try to get
-the latest 5.4.x you can.  Currently, libc-5.4.17 is the latest public
+the latest 5.4.x you can.  Currently, libc-5.4.23 is the latest public
 release.
 
    If you upgrade to libc-5.4.x, you also have to upgrade your dynamic
@@ -113,12 +113,6 @@
 in some standard tools.  Check in /proc/net/rt_local to verify their
 presence.
 
-SysVinit
-========
-
-   To avoid an oops while shutting down or rebooting the computer,
-upgrade to SysVinit 2.69.
-
 RPM
 ===
 
@@ -160,8 +154,6 @@
 Modutils: insmod -V
 Mount: mount --version
 Procps: ps --version
-SysVinit: strings `egrep -li INIT_VERSION=sysvinit- /proc/*/environ | head
--1` | egrep -i INIT_VERSION=sysvinit-
 RPM:  rpm --version
 Sh-utils:  expr --v
 
@@ -191,12 +183,12 @@
 Linux C Library
 ===============
 
-The 5.4.17 release:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.17.bin.tar.gz
-ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.17.bin.tar.gz
-Installation notes for 5.4.17:
-ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.17
-ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.13
+The 5.4.23 release:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/libc-5.4.23.bin.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/GCC/libc-5.4.23.bin.tar.gz
+Installation notes for 5.4.23:
+ftp://tsx-11.mit.edu/pub/linux/packages/GCC/release.libc-5.4.23
+ftp://sunsite.unc.edu/pub/Linux/GCC/release.libc-5.4.23
 
 Linux C++ Library
 =================
@@ -227,14 +219,7 @@
 
 The 1.01 release:
 ftp://tsx-11.mit.edu/pub/linux/sources/usr.bin/procps-1.01.tgz
-ftp://sunsite.unc.edu/pub/Linux/system/Status/ps/procps-1.01.tgz
-
-SysVinit utilities
-==================
-
-The 2.69 release (when it gets there):
-ftp://tsx-11.mit.edu/pub/linux/sources/sbin/sysvinit-2.69.tar.gz
-ftp://sunsite.unc.edu/pub/Linux/system/Daemons/init/sysvinit-2.69.tar.gz
+ftp://sunsite.unc.edu/pub/Linux/system/status/ps/procps-1.01.tgz
 
 RPM utilities
 =============
@@ -254,7 +239,7 @@
 
 The 0.64.3.1 release:
 ftp://tsx-11.mit.edu/pub/linux/ALPHA/dosemu/dosemu0.64.3.1.tgz
-ftp://sunsite.unc.edu/pub/Linux/system/Emulators/dosemu0.64.3.1.tgz
+ftp://sunsite.unc.edu/pub/Linux/system/emulators/dosemu0.64.3.1.tgz
 
 Loadlin
 =======
diff -u --recursive --new-file v2.1.27/linux/Documentation/Configure.help linux/Documentation/Configure.help
--- v2.1.27/linux/Documentation/Configure.help	Thu Feb 27 10:57:29 1997
+++ linux/Documentation/Configure.help	Fri Feb 28 15:15:42 1997
@@ -543,7 +543,7 @@
   choices:
   ** Avanti: This is for Mustang (AS200), M3 (AS250), Avanti (AS400)
      and XL (a.k.a. "Windows NT Dream Machine" :-) AlphaStations.
-     These usually come with a TGA graphics adaptor, so you'll want to
+     These usually come with a TGA graphics adapter, so you'll want to
      say Y to "TGA Console support", below, if you have one of these.
   ** Jensen: a.k.a. DEC 2000 a.k.a. DECpc AXP 150, the oldest Alpha
      PC; it sports an EISA bus. The boot process on Jensen machines is
@@ -595,11 +595,52 @@
   corresponds to a serial port; this could be useful if you attached
   a terminal or printer to that port.
 
+Non-standard serial port support
+CONFIG_SERIAL_NONSTANDARD
+  Say Y here if you have any non-standard serial boards --- boards
+  which aren't supported using the standard "dumb" serial driver.
+  This includes intelligent serial boards such as Cyclades,
+  Digiboards, etc.
+
+Extended dumb serial driver options
+CONFIG_SERIAL_EXTENDED  
+  If you wish to use any non-standard features of the standard "dumb"
+  driver, say Y here.  This includes HUB6 support, shared serial
+  interrupts, special multiport support, support for more than the
+  four COM 1/2/3/4 boards, etc.
+
+Support more than 4 serial ports
+CONFIG_SERIAL_MANY_PORTS
+  Enable this option if you have dumb serial boards other than the
+  four standard COM 1/2/3/4 ports.  This may happen if you have an AST
+  FourPort, Accent Async, Boca, or other custom serial port hardware
+  which acts similar to standard serial port hardware.  If you only
+  use the standard COM 1/2/3/4 ports, you can say N here to save some
+  memory.
+
+Support for sharing serial interrupts
+CONFIG_SERIAL_SHARE_IRQ
+  Some serial boards have hardware support which allows multiple dumb
+  serial ports on the same board to share a single IRQ.  To enable
+  support for this in the serial driver, say Y here.
+
+Support special multiport boards
+CONFIG_SERIAL_MULTIPORT
+  Some multiport serial ports have special ports which are used to
+  signal when there are any serial ports on the board which need
+  servicing.  Say Y here to enable the serial driver to take advantage
+  of those special I/O ports.
+
+Support the Bell Technologies HUB6 card
+CONFIG_HUB6
+  Say Y here to enable support in the dumb serial driver to support
+  the HUB6 card.
+
 TGA Console Support
 CONFIG_TGA_CONSOLE
   Many Alpha systems (e.g the Multia) are shipped with a graphics card
   that implements the TGA interface (much like the VGA standard, but
-  older TGA adaptors are *not* VGA compatible).  On such systems, you
+  older TGA adapters are *not* VGA compatible).  On such systems, you
   should say Y here so that the TGA driver rather than the standard
   VGA driver is used.  Note that, at this time, there is no X server
   for these systems. If unsure, try N.
@@ -1393,7 +1434,7 @@
 CONFIG_SCSI
   If you want to use a SCSI harddisk, SCSI tapedrive, SCSI CDROM or
   any other SCSI device under Linux, say Y and make sure that you know
-  the name of your SCSI host adaptor (the card inside your computer
+  the name of your SCSI host adapter (the card inside your computer
   that "speaks" the SCSI protocol), because you will be asked for
   it. You also need to say Y here if you want support for the parallel
   port version of the 100MB IOMEGA ZIP drive. Please read the
@@ -1480,7 +1521,7 @@
 
 AdvanSys SCSI support
 CONFIG_SCSI_ADVANSYS
-  This is a driver for all SCSI host adaptors manufactured by
+  This is a driver for all SCSI host adapters manufactured by
   AdvanSys. It is documented in the kernel source in
   drivers/scsi/advansys.c. This driver is also available as a module (
   = code which can be inserted in and removed from the running kernel
@@ -1491,7 +1532,7 @@
 Adaptec AHA152X/2825 support
 CONFIG_SCSI_AHA152X
   This is support for the AHA-1510, AHA-1520, AHA-1522, and AHA-2825
-  SCSI host adaptors. It is explained in section 3.3 of the
+  SCSI host adapters. It is explained in section 3.3 of the
   SCSI-HOWTO, available via ftp (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. You might also want to read
   the comments at the top of drivers/scsi/aha152x.c. This driver is
@@ -1502,7 +1543,7 @@
 
 Adaptec AHA1542 support
 CONFIG_SCSI_AHA1542
-  This is support for a SCSI host adaptor. It is explained in section
+  This is support for a SCSI host adapter. It is explained in section
   3.4 of the SCSI-HOWTO, available via ftp (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  Note that Trantor was
   recently purchased by Adaptec, and some former Trantor products are
@@ -1515,7 +1556,7 @@
  
 Adaptec AHA1740 support
 CONFIG_SCSI_AHA1740
-  This is support for a SCSI host adaptor. It is explained in section
+  This is support for a SCSI host adapter. It is explained in section
   3.5 of the SCSI-HOWTO, available via ftp (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If it doesn't work out of
   the box, you may have to change some settings in
@@ -1527,7 +1568,7 @@
 
 Adaptec AHA274X/284X/294X support
 CONFIG_SCSI_AIC7XXX
-  Information about this SCSI host adaptor is contained in
+  Information about this SCSI host adapter is contained in
   drivers/scsi/README.aic7xxx and in the SCSI-HOWTO, available via ftp
   (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it
   doesn't work out of the box, you may have to change some settings in
@@ -1538,24 +1579,25 @@
 
 BusLogic SCSI support
 CONFIG_SCSI_BUSLOGIC
-  This is support for BusLogic MultiMaster SCSI Host Adaptors.
-  Consult the SCSI-HOWTO, available via anonymous ftp from
-  sunsite.unc.edu:/pub/Linux/docs/HOWTO and the file
-  drivers/scsi/README.BusLogic for more information. BusLogic
-  FlashPoint SCSI Host Adapters are not supported by this driver, but
-  BusLogic has initiated an upgrade program which allows you to get a
-  better adaptor for few $$. Read about it in
-  drivers/scsi/README.FlashPoint.  If this driver does not work
-  correctly without modification, please contact the author.  You can
-  build this driver also as a module ( = code which can be inserted in
-  and removed from the running kernel whenever you want), but only a
-  single instance may be loaded. The module will be called
-  BusLogic.o. If you want to compile it as a module, say M here and
-  read Documentation/modules.txt.
+  This is support for BusLogic MultiMaster and FlashPoint SCSI Host Adapters.
+  Consult the SCSI-HOWTO, available via anonymous ftp from sunsite.unc.edu in
+  /pub/Linux/docs/HOWTO, and the files README.BusLogic and README.FlashPoint in
+  drivers/scsi for more information.  If this driver does not work correctly
+  without modification, please contact the author, Leonard N. Zubkoff, by email
+  to lnz@dandelion.com.  You can also build this driver as a module ( = code
+  which can be inserted in and removed from the running kernel whenever you
+  want), but only a single instance may be loaded.  If you want to compile it
+  as a module, say M here and read Documentation/modules.txt.
+
+Omit BusLogic SCSI FlashPoint support
+CONFIG_SCSI_OMIT_FLASHPOINT
+  This option allows you to omit the FlashPoint support from the BusLogic
+  SCSI driver.  The FlashPoint SCCB Manager code is substantial, so users of
+  MultiMaster Host Adapters may wish to omit it.
 
 DTC3180/3280 SCSI support
 CONFIG_SCSI_DTC3280
-  This is support for DTC 3180/3280 SCSI Host Adaptors.  Please read
+  This is support for DTC 3180/3280 SCSI Host Adapters.  Please read
   the SCSI-HOWTO, available via ftp (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO and the file
   drivers/scsi/README.dtc3x80.  This driver is also available as a
@@ -1567,7 +1609,7 @@
 EATA-DMA (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support
 CONFIG_SCSI_EATA_DMA
   This is support for the EATA-DMA protocol compliant SCSI Host
-  Adaptors like the SmartCache III/IV, SmartRAID controller families
+  Adapters like the SmartCache III/IV, SmartRAID controller families
   and the DPT PM2011B and PM2012B controllers.  Note that there is
   also another driver for the same hardware: "EATA ISA/EISA/PCI
   support". You should only say Y to one of them. Please read the
@@ -1581,8 +1623,8 @@
 EATA-PIO (old DPT PM2001, PM2012A) support
 CONFIG_SCSI_EATA_PIO
   This driver supports all EATA-PIO protocol compliant SCSI Host
-  Adaptors like the DPT PM2001 and the PM2012A. EATA-DMA compliant
-  host adaptors could also use this driver but are discouraged from
+  Adapters like the DPT PM2001 and the PM2012A. EATA-DMA compliant
+  host adapters could also use this driver but are discouraged from
   doing so, since this driver only supports harddisks and lacks
   numerous features.  You might want to have a look at the SCSI-HOWTO,
   available via ftp (user: anonymous) at
@@ -1609,7 +1651,7 @@
 enable linked commands
 CONFIG_SCSI_U14_34F_LINKED_COMMANDS
   This is a feature of SCSI-2 which improves performance: the host
-  adaptor can send a whole list of commands to a device in one
+  adapter can send a whole list of commands to a device in one
   batch. Some SCSI devices might not implement this properly, so the
   save answer is N.
 
@@ -1621,9 +1663,9 @@
 
 Future Domain 16xx SCSI support
 CONFIG_SCSI_FUTURE_DOMAIN
-  This is support for Future Domain's 16-bit SCSI host adaptors
+  This is support for Future Domain's 16-bit SCSI host adapters
   (TMC-1660/1680, TMC-1650/1670, TMC-3260, TMC-1610M/MER/MEX) and
-  other adaptors based on the Future Domain chipsets (Quantum
+  other adapters based on the Future Domain chipsets (Quantum
   ISA-200S, ISA-250MG; Adaptec AHA-2920; and at least one IBM board).
   It is explained in section 3.7 of the SCSI-HOWTO, available via ftp
   (user: anonymous) at sunsite.unc.edu:/pub/Linux/docs/HOWTO.  This
@@ -1682,7 +1724,7 @@
 allow FAST-SCSI [10MHz]
 CONFIG_SCSI_NCR53C7xx_FAST
   This will enable 10MHz FAST-SCSI transfers with your host
-  adaptor. Some systems have problems with that speed, so it's safest
+  adapter. Some systems have problems with that speed, so it's safest
   to say N here.
  
 allow DISCONNECT
@@ -1779,7 +1821,7 @@
 
 Always IN2000 SCSI support
 CONFIG_SCSI_IN2000
-  This is support for an ISA bus SCSI host adaptor. You'll find more
+  This is support for an ISA bus SCSI host adapter. You'll find more
   information in drivers/scsi/in2000.readme. If it doesn't work out of
   the box, you may have to change the jumpers for IRQ or address
   selection. If you want to compile this as a module ( = code which
@@ -1789,7 +1831,7 @@
 
 PAS16 SCSI support
 CONFIG_SCSI_PAS16
-  This is support for a SCSI host adaptor. It is explained in section
+  This is support for a SCSI host adapter. It is explained in section
   3.10 of the SCSI-HOWTO, available via ftp (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the
   box, you may have to change some settings in drivers/scsi/pas16.h.
@@ -1816,7 +1858,7 @@
 
 Qlogic ISP SCSI support (EXPERIMENTAL)
 CONFIG_SCSI_QLOGIC_ISP
-  This driver works for all QLogic PCI SCSI host adaptors (IQ-PCI,
+  This driver works for all QLogic PCI SCSI host adapters (IQ-PCI,
   IQ-PCI-10, IQ_PCI-D) except for the PCI-basic card. (This latter
   card is supported by the "AM53/79C974 PCI SCSI" driver). If you say
   Y here, make sure to say Y to "PCI BIOS support" as well. More
@@ -1843,7 +1885,7 @@
 
 Trantor T128/T128F/T228 SCSI support
 CONFIG_SCSI_T128
-  This is support for a SCSI host adaptor. It is explained in section
+  This is support for a SCSI host adapter. It is explained in section
   3.11 of the SCSI-HOWTO, available via ftp (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  If it doesn't work out of
   the box, you may have to change some settings in
@@ -1857,7 +1899,7 @@
 UltraStor SCSI support
 CONFIG_SCSI_ULTRASTOR
   This is support for the UltraStor 14F, 24F and 34F SCSI-2 host
-  adaptor family. This driver is explained in section 3.12 of the
+  adapter family. This driver is explained in section 3.12 of the
   SCSI-HOWTO, available via ftp (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO. If it doesn't work out of the
   box, you may have to change some settings in
@@ -1870,7 +1912,7 @@
  
 7000FASST SCSI support
 CONFIG_SCSI_7000FASST
-  This driver supports the Western Digital 7000 SCSI host adaptor.
+  This driver supports the Western Digital 7000 SCSI host adapter.
   Some information is in the source: drivers/scsi/wd7000.c.  This
   driver is also available as a module ( = code which can be inserted
   in and removed from the running kernel whenever you want). The
@@ -1896,14 +1938,14 @@
 enable tagged command queuing
 CONFIG_SCSI_EATA_TAGGED_QUEUE
   This is a feature of SCSI-2 which improves performance: the host
-  adaptor can send several SCSI commands to a device's queue even if
+  adapter can send several SCSI commands to a device's queue even if
   previous commands haven't finished yet. Some SCSI devices don't
   implement this properly, so the save answer is N.
 
 enable linked commands
 CONFIG_SCSI_EATA_LINKED_COMMANDS
   This is a feature of SCSI-2 which improves performance: the host
-  adaptor can send a whole list of commands to a device in one
+  adapter can send a whole list of commands to a device in one
   batch. Some SCSI devices might not implement this properly, so the
   save answer is N.
 
@@ -3081,10 +3123,10 @@
   by this driver. Read the Ethernet-HOWTO, available via ftp (user:
   anonymous) in sunsite.unc.edu:/pub/Linux/docs/HOWTO.
 
-Pocket and portable adaptors
+Pocket and portable adapters
 CONFIG_NET_POCKET
   Cute little network (ethernet) devices which attach to the parallel
-  port ("pocket adaptors"), commonly used with laptops. If you have
+  port ("pocket adapters"), commonly used with laptops. If you have
   one of those, say Y and read the Ethernet-HOWTO, available via ftp
   (user: anonymous) from sunsite.unc.edu:/pub/Linux/docs/HOWTO. If you
   want to plug a network card into the PCMCIA slot of your laptop
@@ -3098,10 +3140,10 @@
   plan to use more than one network device under linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini. If you intend to use an
-  adaptor attaching to the parallel port as well as a parallel
+  adapter attaching to the parallel port as well as a parallel
   printer, you should compile both drivers as modules (if possible).
 
-AT-LAN-TEC/RealTek pocket adaptor support
+AT-LAN-TEC/RealTek pocket adapter support
 CONFIG_ATP
   This is a network (ethernet) device which attaches to your parallel
   port. Read drivers/net/atp.c as well as the Ethernet-HOWTO,
@@ -3113,7 +3155,7 @@
   this driver, you should have said N to the Parallel Printer support,
   because the two drivers don't like each other.
 
-D-Link DE600 pocket adaptor support
+D-Link DE600 pocket adapter support
 CONFIG_DE600
   This is a network (ethernet) device which attaches to your parallel
   port. Read drivers/net/README.DLINK as well as the Ethernet-HOWTO,
@@ -3122,13 +3164,13 @@
   you want to compile this 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
-  de600.o. If you intend to use this pocket adaptor as well as a
+  de600.o. If you intend to use this pocket adapter as well as a
   parallel printer, you should compile both drivers as modules. If you
   plan to use more than one network card under linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
   sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
 
-D-Link DE620 pocket adaptor support
+D-Link DE620 pocket adapter support
 CONFIG_DE620
   This is a network (ethernet) device which attaches to your parallel
   port. Read drivers/net/README.DLINK as well as the Ethernet-HOWTO,
@@ -3137,7 +3179,7 @@
   you want to compile this 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
-  de620.o. If you intend to use this pocket adaptor as well as a
+  de620.o. If you intend to use this pocket adapter as well as a
   parallel printer, you should compile both drivers as modules. If you
   plan to use more than one network card under linux, read the
   Multiple-Ethernet-mini-HOWTO, available from
@@ -3150,7 +3192,7 @@
   ring network and want to use your Token Ring card under Linux, say Y.
   Most people can say N here.
 
-IBM Tropic chipset based adaptor support
+IBM Tropic chipset based adapter support
 CONFIG_IBMTR
   This is support for all IBM Token Ring cards that don't use DMA. If
   you have such a beast, say Y, otherwise N. Warning: this driver will
diff -u --recursive --new-file v2.1.27/linux/MAINTAINERS linux/MAINTAINERS
--- v2.1.27/linux/MAINTAINERS	Thu Feb 27 10:57:29 1997
+++ linux/MAINTAINERS	Fri Feb 28 15:15:42 1997
@@ -142,6 +142,7 @@
 P:	Leonard N. Zubkoff
 M:	Leonard N. Zubkoff <lnz@dandelion.com>
 L:	linux-scsi@vger.rutgers.edu
+W:	http://www.dandelion.com/Linux/
 S:	Maintained
 
 CYCLADES ASYNC MUX DRIVER
@@ -301,6 +302,12 @@
 P:	Eddie C. Dost
 M:	ecd@skynet.be
 L:	sparclinux@vger.rutgers.edu
+S:	Maintained
+
+SCSI SUBSYSTEM
+P:	Leonard N. Zubkoff
+M:	Leonard N. Zubkoff <lnz@dandelion.com>
+L:	linux-scsi@vger.rutgers.edu
 S:	Maintained
 
 SVGA HANDLING:
diff -u --recursive --new-file v2.1.27/linux/Makefile linux/Makefile
--- v2.1.27/linux/Makefile	Thu Feb 27 10:57:29 1997
+++ linux/Makefile	Thu Feb 27 10:57:04 1997
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 1
-SUBLEVEL = 27
+SUBLEVEL = 28
 
 ARCH = i386
 
diff -u --recursive --new-file v2.1.27/linux/arch/alpha/kernel/entry.S linux/arch/alpha/kernel/entry.S
--- v2.1.27/linux/arch/alpha/kernel/entry.S	Thu Feb 27 10:57:29 1997
+++ linux/arch/alpha/kernel/entry.S	Fri Feb 28 14:53:24 1997
@@ -125,7 +125,7 @@
 	lda	$0,intr_count
 	ldl	$1,0($0)
 	addq	$1,1,$1
-	stq	$1,0($0)
+	stl	$1,0($0)
 /* set up the arguments to the C interrupt handler */
 	ldq	$8,current_set
 	jsr	$26,do_entInt
@@ -133,7 +133,7 @@
 	lda	$0,intr_count
 	ldl	$1,0($0)
 	subq	$1,1,$1
-	stq	$1,0($0)
+	stl	$1,0($0)
 	br	$31,ret_from_sys_call
 .end entInt
 
@@ -710,7 +710,7 @@
 /*0*/	.quad do_entSys, sys_exit, sys_fork, sys_read, sys_write
 	.quad do_entSys, sys_close, sys_wait4, do_entSys, sys_link
 	.quad sys_unlink, do_entSys, sys_chdir, sys_fchdir, sys_mknod
-	.quad sys_chmod, sys_chown, sys_brk, do_entSys, sys_lseek
+	.quad sys_chmod, sys_chown, osf_brk, do_entSys, sys_lseek
 	.quad sys_getxpid, osf_mount, osf_umount, sys_setuid, sys_getxuid
 	.quad do_entSys, sys_ptrace, do_entSys, do_entSys, do_entSys
 	.quad do_entSys, do_entSys, do_entSys, sys_access, do_entSys
diff -u --recursive --new-file v2.1.27/linux/arch/alpha/kernel/osf_sys.c linux/arch/alpha/kernel/osf_sys.c
--- v2.1.27/linux/arch/alpha/kernel/osf_sys.c	Sun Jan 26 04:26:39 1997
+++ linux/arch/alpha/kernel/osf_sys.c	Thu Feb 27 10:49:38 1997
@@ -48,6 +48,20 @@
 extern asmlinkage unsigned long sys_brk(unsigned long);
 
 /*
+ * Brk needs to return an error.  Still support Linux's brk(0) query idiom,
+ * which OSF programs just shouldn't be doing.  We're still not quite
+ * identical to OSF as we don't return 0 on success, but doing otherwise
+ * would require changes to libc.  Hopefully this is good enough.
+ */
+asmlinkage unsigned long osf_brk(unsigned long brk)
+{
+	unsigned long retval = sys_brk(brk);
+	if (brk && brk != retval)
+		retval = -ENOMEM;
+	return retval;
+}
+ 
+/*
  * This is pure guess-work..
  */
 asmlinkage int osf_set_program_attributes(
diff -u --recursive --new-file v2.1.27/linux/arch/alpha/lib/csum_partial_copy.c linux/arch/alpha/lib/csum_partial_copy.c
--- v2.1.27/linux/arch/alpha/lib/csum_partial_copy.c	Thu Feb  6 02:44:41 1997
+++ linux/arch/alpha/lib/csum_partial_copy.c	Thu Feb 27 14:15:47 1997
@@ -304,20 +304,14 @@
 	return checksum;
 }
 
-unsigned int
-csum_partial_copy_from_user(int *errp, char *src, char *dst,
-			    int len, unsigned int sum)
+static unsigned int
+do_csum_partial_copy_from_user(char *src, char *dst, int len,
+			       unsigned int sum, int *errp)
 {
 	unsigned long checksum = (unsigned) sum;
 	unsigned long soff = 7 & (unsigned long) src;
 	unsigned long doff = 7 & (unsigned long) dst;
 
-	if (!access_ok(src, len, VERIFY_READ)) {
-		*errp = -EFAULT;
-		memset(dst, 0, len);
-		return checksum;
-	}
-
 	if (len) {
 		if (!doff) {
 			if (!soff)
@@ -358,12 +352,25 @@
 }
 
 unsigned int
+csum_partial_copy_from_user(char *src, char *dst, int len,
+			    unsigned int sum, int *errp)
+{
+	if (!access_ok(src, len, VERIFY_READ)) {
+		*errp = -EFAULT;
+		memset(dst, 0, len);
+		return sum;
+	}
+
+	return do_csum_partial_copy_from_user(src, dst, len, sum, errp);
+}
+
+unsigned int
 csum_partial_copy (const char *src, char *dst, int len, unsigned int sum)
 {
 	unsigned int ret;
 	int error = 0;
 
-	ret = csum_partial_copy_from_user(&error, src, dst, len, sum);
+	ret = do_csum_partial_copy_from_user(src, dst, len, sum, &error);
 	if (error)
 		printk("csum_partial_copy_old(): tell mingo to convert me!\n");
 
diff -u --recursive --new-file v2.1.27/linux/arch/i386/defconfig linux/arch/i386/defconfig
--- v2.1.27/linux/arch/i386/defconfig	Thu Feb 27 10:57:29 1997
+++ linux/arch/i386/defconfig	Fri Feb 28 15:39:41 1997
@@ -120,6 +120,7 @@
 # CONFIG_SCSI_IN2000 is not set
 # CONFIG_SCSI_AM53C974 is not set
 CONFIG_SCSI_BUSLOGIC=y
+CONFIG_SCSI_OMIT_FLASHPOINT=y
 # CONFIG_SCSI_DTC3280 is not set
 # CONFIG_SCSI_EATA_DMA is not set
 # CONFIG_SCSI_EATA_PIO is not set
@@ -201,11 +202,8 @@
 # Character devices
 #
 CONFIG_SERIAL=y
-# CONFIG_DIGI is not set
-# CONFIG_CYCLADES is not set
-# CONFIG_STALDRV is not set
-# CONFIG_RISCOM8 is not set
-# CONFIG_ESPSERIAL is not set
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
 # CONFIG_PRINTER is not set
 CONFIG_MOUSE=y
 # CONFIG_ATIXL_BUSMOUSE is not set
diff -u --recursive --new-file v2.1.27/linux/arch/i386/kernel/irq.c linux/arch/i386/kernel/irq.c
--- v2.1.27/linux/arch/i386/kernel/irq.c	Sun Jan 26 02:07:04 1997
+++ linux/arch/i386/kernel/irq.c	Fri Feb 28 17:57:40 1997
@@ -349,7 +349,7 @@
 	int do_random = 0;
 
 	lock_kernel();
-	intr_count++;
+	atomic_inc(&intr_count);
 #ifdef __SMP__
 	if(smp_threads_ready && active_kernel_processor!=smp_processor_id())
 		panic("IRQ %d: active processor set wrongly(%d not %d).\n", irq, active_kernel_processor, smp_processor_id());
@@ -366,7 +366,7 @@
 	}
 	if (do_random & SA_SAMPLE_RANDOM)
 		add_interrupt_randomness(irq);
-	intr_count--;
+	atomic_dec(&intr_count);
 	unlock_kernel();
 }
 
diff -u --recursive --new-file v2.1.27/linux/drivers/char/ChangeLog linux/drivers/char/ChangeLog
--- v2.1.27/linux/drivers/char/ChangeLog	Sun Dec  8 11:21:15 1996
+++ linux/drivers/char/ChangeLog	Fri Feb 28 15:00:52 1997
@@ -1,3 +1,52 @@
+Thu Feb 27 01:53:08 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* serial.c (change_speed): Add support for the termios flag
+		CMSPAR, which allows the user to select stick parity.
+		(i.e, if PARODD is set, the the parity bit is always 1; if
+		PARRODD is not set, then the parity bit is always 0).
+
+Wed Feb 26 19:03:10 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* serial.c (cleanup_module): Fix memory leak when using the serial
+		driver as a module; make sure tmp_buf gets freed!
+
+Tue Feb 25 11:01:59 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* serial.c (set_modem_info): Add support for setting and clearing
+		the OUT1 and OUT2 bits.  (For special case UART's, usually
+		for half-duplex.)
+		(autoconfig, change_speed): Fix TI 16750 support.
+
+Sun Feb 16 00:14:43 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* tty_io.c (release_dev): Add sanity check to make sure there are
+		no waiters on tty->read_wait or tty->write_wait.
+
+	* serial.c (rs_init): Don't autoconfig a device if the I/O region
+		is already reserved.
+
+	* serial.c (serial_proc_info): Add support for /proc/serial.
+
+Thu Feb 13 00:49:10 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* serial.c (receive_chars): When the UART repotrs an overrun
+ 		condition, it does so with a valid character.  Changed to
+ 		not throw away the valid character, but instead report the
+		overrun after the valid character.
+
+	* serial.c: Added new #ifdef's for some of the advanced serial
+		driver features.  A minimal driver that only supports COM
+		1/2/3/4 without sharing serial interrupts only takes 17k;
+		the full driver takes 32k.
+		
+Wed Feb 12 14:50:44 1997  Theodore Ts'o  <tytso@rsts-11.mit.edu>
+
+	* vt.c:
+	* pty.c: 
+	* tty_ioctl.c: 
+	* serial.c: Update routines to use the new 2.1 memory access
+	 	routines.
+
 Wed Dec  4 07:51:52 1996  Theodre Ts'o  <tytso@localhost.mit.edu>
 
 	* serial.c (change_speed): Use save_flags(); cli() and
diff -u --recursive --new-file v2.1.27/linux/drivers/char/Config.in linux/drivers/char/Config.in
--- v2.1.27/linux/drivers/char/Config.in	Sun Feb  2 05:51:42 1997
+++ linux/drivers/char/Config.in	Fri Feb 28 15:00:53 1997
@@ -4,19 +4,29 @@
 mainmenu_option next_comment
 comment 'Character devices'
 
-tristate 'Standard/generic serial support' CONFIG_SERIAL
-bool 'Digiboard PC/Xx Support' CONFIG_DIGI
-tristate 'Cyclades async mux support' CONFIG_CYCLADES
-bool 'Stallion multiport serial support' CONFIG_STALDRV
-if [ "$CONFIG_STALDRV" = "y" ]; then
-  tristate '  Stallion EasyIO or EC8/32 support' CONFIG_STALLION
-  tristate '  Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION
+tristate 'Standard/generic (dumb) serial support' CONFIG_SERIAL
+bool 'Extended dumb serial driver options' CONFIG_SERIAL_EXTENDED
+if [ "$CONFIG_SERIAL_EXTENDED" = "y" ]; then
+   bool '   Support more than 4 serial ports' CONFIG_SERIAL_MANY_PORTS
+   bool '   Support for sharing serial interrupts' CONFIG_SERIAL_SHARE_IRQ
+   bool '   Support special multiport boards' CONFIG_SERIAL_MULTIPORT
+   bool '   Support the Bell Technologies HUB6 card' CONFIG_HUB6
 fi
-tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8
-tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL
-if [ "$CONFIG_ESPSERIAL" = "y" -o "$CONFIG_ESPSERIAL" = "m" ]; then
-  int '  DMA channel' CONFIG_ESPSERIAL_DMA_CHANNEL 1
-  int '  FIFO trigger level' CONFIG_ESPSERIAL_TRIGGER_LEVEL 768
+bool 'Non-standard serial port support' CONFIG_SERIAL_NONSTANDARD
+if [ "$CONFIG_SERIAL_NONSTANDARD" = "y" ]; then
+   bool 'Digiboard PC/Xx Support' CONFIG_DIGI
+   tristate 'Cyclades async mux support' CONFIG_CYCLADES
+   bool 'Stallion multiport serial support' CONFIG_STALDRV
+   if [ "$CONFIG_STALDRV" = "y" ]; then
+     tristate '  Stallion EasyIO or EC8/32 support' CONFIG_STALLION
+     tristate '  Stallion EC8/64, ONboard, Brumby support' CONFIG_ISTALLION
+   fi
+   tristate 'SDL RISCom/8 card support' CONFIG_RISCOM8
+   tristate 'Hayes ESP serial port support' CONFIG_ESPSERIAL
+   if [ "$CONFIG_ESPSERIAL" = "y" -o "$CONFIG_ESPSERIAL" = "m" ]; then
+     int '  DMA channel' CONFIG_ESPSERIAL_DMA_CHANNEL 1
+     int '  FIFO trigger level' CONFIG_ESPSERIAL_TRIGGER_LEVEL 768
+   fi
 fi
 tristate 'Parallel printer support' CONFIG_PRINTER
 
diff -u --recursive --new-file v2.1.27/linux/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- v2.1.27/linux/drivers/char/n_tty.c	Sat Jan 25 13:46:13 1997
+++ linux/drivers/char/n_tty.c	Fri Feb 28 15:00:53 1997
@@ -1010,6 +1010,7 @@
 
 struct tty_ldisc tty_ldisc_N_TTY = {
 	TTY_LDISC_MAGIC,	/* magic */
+	"n_tty",		/* name */
 	0,			/* num */
 	0,			/* flags */
 	n_tty_open,		/* open */
diff -u --recursive --new-file v2.1.27/linux/drivers/char/pty.c linux/drivers/char/pty.c
--- v2.1.27/linux/drivers/char/pty.c	Mon Oct 28 04:29:21 1996
+++ linux/drivers/char/pty.c	Fri Feb 28 15:00:55 1997
@@ -4,14 +4,6 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
-/*
- *	pty.c
- *
- * This module exports the following pty function:
- * 
- * 	int  pty_open(struct tty_struct * tty, struct file * filp);
- */
-
 #include <linux/errno.h>
 #include <linux/sched.h>
 #include <linux/interrupt.h>
@@ -47,8 +39,8 @@
 static unsigned char *tmp_buf;
 static struct semaphore tmp_buf_sem = MUTEX;
 
-struct tty_driver pty_driver, pty_slave_driver;
-struct tty_driver old_pty_driver, old_pty_slave_driver;
+static struct tty_driver pty_driver, pty_slave_driver;
+static struct tty_driver old_pty_driver, old_pty_slave_driver;
 static int pty_refcount;
 
 static struct tty_struct *pty_table[NR_PTYS];
@@ -74,8 +66,10 @@
 	}
 	wake_up_interruptible(&tty->read_wait);
 	wake_up_interruptible(&tty->write_wait);
+	tty->packet = 0;
 	if (!tty->link)
 		return;
+	tty->link->packet = 0;
 	wake_up_interruptible(&tty->link->read_wait);
 	wake_up_interruptible(&tty->link->write_wait);
 	set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
@@ -125,7 +119,12 @@
 			((tty->driver.subtype-1) * PTY_BUF_SIZE);
 		while (count > 0) {
 			n = MIN(count, PTY_BUF_SIZE);
-			copy_from_user(temp_buffer, buf, n);
+			n -= copy_from_user(temp_buffer, buf, n);
+			if (!n) {
+				if (!c)
+					c = -EFAULT;
+				break;
+			}
 			r = to->ldisc.receive_room(to);
 			if (r <= 0)
 				break;
@@ -179,7 +178,7 @@
 	}
 }
 
-int pty_open(struct tty_struct *tty, struct file * filp)
+static int pty_open(struct tty_struct *tty, struct file * filp)
 {
 	int	retval;
 	int	line;
@@ -230,6 +229,7 @@
 	memset(&pty_state, 0, sizeof(pty_state));
 	memset(&pty_driver, 0, sizeof(struct tty_driver));
 	pty_driver.magic = TTY_DRIVER_MAGIC;
+	pty_driver.driver_name = "pty_master";
 	pty_driver.name = "pty";
 	pty_driver.major = PTY_MASTER_MAJOR;
 	pty_driver.minor_start = 0;
@@ -258,6 +258,8 @@
 	pty_driver.set_termios = pty_set_termios;
 
 	pty_slave_driver = pty_driver;
+	pty_slave_driver.driver_name = "pty_slave";
+	pty_slave_driver.proc_entry = 0;
 	pty_slave_driver.name = "ttyp";
 	pty_slave_driver.subtype = PTY_TYPE_SLAVE;
 	pty_slave_driver.major = PTY_SLAVE_MAJOR;
@@ -270,12 +272,16 @@
 	pty_slave_driver.other = &pty_driver;
 
 	old_pty_driver = pty_driver;
+	old_pty_driver.driver_name = "compat_pty_master";
+	old_pty_driver.proc_entry = 0;
 	old_pty_driver.major = TTY_MAJOR;
 	old_pty_driver.minor_start = 128;
 	old_pty_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS;
 	old_pty_driver.other = &old_pty_slave_driver;
 	
 	old_pty_slave_driver = pty_slave_driver;
+	old_pty_slave_driver.driver_name = "compat_pty_slave";
+	old_pty_slave_driver.proc_entry = 0;
 	old_pty_slave_driver.major = TTY_MAJOR;
 	old_pty_slave_driver.minor_start = 192;
 	old_pty_slave_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS;
diff -u --recursive --new-file v2.1.27/linux/drivers/char/random.c linux/drivers/char/random.c
--- v2.1.27/linux/drivers/char/random.c	Sat Jan 25 13:46:13 1997
+++ linux/drivers/char/random.c	Fri Feb 28 15:00:52 1997
@@ -1,9 +1,9 @@
 /*
  * random.c -- A strong random number generator
  *
- * Version 1.00, last modified 26-May-96
+ * Version 1.01, last modified 13-Feb-97
  * 
- * Copyright Theodore Ts'o, 1994, 1995, 1996.  All rights reserved.
+ * Copyright Theodore Ts'o, 1994, 1995, 1996, 1997.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -269,6 +269,21 @@
 #endif
 
 /*
+ * For the purposes of better mixing, we use the CRC-32 polynomial as
+ * well to make a twisted Generalized Feedback Shift Reigster
+ *
+ * (See M. Matsumoto & Y. Kurita, 1992.  Twisted GFSR generators.  ACM
+ * Transactions on Modeling and Computer Simulation 2(3):179-194.
+ * Also see M. Matsumoto & Y. Kurita, 1994.  Twisted GFSR generators
+ * II.  ACM Transactions on Mdeling and Computer Simulation 4:254-266)
+ *
+ * Thanks to Colin Plumb for suggesting this.  (Note that the behavior
+ * of the 1.0 version of the driver was equivalent to using a second
+ * element of 0x80000000).
+ */
+static __u32 twist_table[2] = { 0, 0xEDB88320 };
+
+/*
  * The minimum number of bits to release a "wait on input".  Should
  * probably always be 8, since a /dev/random read can return a single
  * byte.
@@ -287,7 +302,7 @@
 	unsigned add_ptr;
 	unsigned entropy_count;
 	int input_rotate;
-	__u32 *pool;
+	__u32 pool[POOLWORDS];
 };
 
 #ifdef RANDOM_BENCHMARK
@@ -321,13 +336,13 @@
 };
 
 static struct random_bucket random_state;
-static __u32 random_pool[POOLWORDS];
 static struct timer_rand_state keyboard_timer_state;
 static struct timer_rand_state mouse_timer_state;
 static struct timer_rand_state extract_timer_state;
 static struct timer_rand_state *irq_timer_state[NR_IRQS];
 static struct timer_rand_state *blkdev_timer_state[MAX_BLKDEV];
-static struct wait_queue *random_wait;
+static struct wait_queue *random_read_wait;
+static struct wait_queue *random_write_wait;
 
 static long random_read(struct inode * inode, struct file * file,
 		       char * buf, unsigned long nbytes);
@@ -432,11 +447,7 @@
 /* Clear the entropy pool and associated counters. */
 static void rand_clear_pool(void)
 {
-	random_state.add_ptr = 0;
-	random_state.entropy_count = 0;
-	random_state.pool = random_pool;
-	random_state.input_rotate = 0;
-	memset(random_pool, 0, sizeof(random_pool));
+	memset(&random_state, 0, sizeof(random_state));
 	init_std_data(&random_state);
 }
 
@@ -456,7 +467,8 @@
 	initialize_benchmark(&timer_benchmark, "timer", 0);
 #endif
 	extract_timer_state.dont_count_entropy = 1;
-	random_wait = NULL;
+	random_read_wait = NULL;
+	random_write_wait = NULL;
 }
 
 void rand_initialize_irq(int irq)
@@ -516,8 +528,6 @@
 	int new_rotate;
 	__u32 w;
 
-	w = rotate_left(r->input_rotate, input);
-	i = r->add_ptr = (r->add_ptr - 1) & (POOLWORDS-1);
 	/*
 	 * Normally, we add 7 bits of rotation to the pool.  At the
 	 * beginning of the pool, add an extra 7 bits rotation, so
@@ -525,19 +535,21 @@
 	 * pool evenly.
 	 */
 	new_rotate = r->input_rotate + 14;
-	if (i)
-		new_rotate = r->input_rotate + 7;
+	if ((i = r->add_ptr--))
+		new_rotate -= 7;
 	r->input_rotate = new_rotate & 31;
 
+	w = rotate_left(r->input_rotate, input);
+	
 	/* XOR in the various taps */
 	w ^= r->pool[(i+TAP1)&(POOLWORDS-1)];
 	w ^= r->pool[(i+TAP2)&(POOLWORDS-1)];
 	w ^= r->pool[(i+TAP3)&(POOLWORDS-1)];
 	w ^= r->pool[(i+TAP4)&(POOLWORDS-1)];
 	w ^= r->pool[(i+TAP5)&(POOLWORDS-1)];
-	w ^= r->pool[i];
-	/* Rotate w left 1 bit (stolen from SHA) and store */
-	r->pool[i] = (w << 1) | (w >> 31);
+	w ^= r->pool[i&(POOLWORDS-1)];
+	/* Use a twisted GFSR for the mixing operation */
+	r->pool[i&(POOLWORDS-1)] = (w >> 1) ^ twist_table[w & 1];
 }
 
 /*
@@ -619,7 +631,7 @@
 		
 	/* Wake up waiting processes, if we have enough entropy. */
 	if (r->entropy_count >= WAIT_INPUT_BITS)
-		wake_up_interruptible(&random_wait);
+		wake_up_interruptible(&random_read_wait);
 #ifdef RANDOM_BENCHMARK
 	end_benchmark(&timer_benchmark);
 #endif
@@ -662,6 +674,8 @@
 
 #ifdef USE_SHA
 
+#define SMALL_VERSION		/* Optimize for space over time */
+
 #define HASH_BUFFER_SIZE 5
 #define HASH_TRANSFORM SHATransform
 
@@ -694,10 +708,14 @@
     ( e += ROTL( 5, a ) + f( b, c, d ) + k + data, b = ROTL( 30, b ) )
 
 
-void SHATransform(__u32 *digest, __u32 *data)
+static void SHATransform(__u32 *digest, __u32 *data)
     {
     __u32 A, B, C, D, E;     /* Local vars */
     __u32 eData[ 16 ];       /* Expanded data */
+#ifdef SMALL_VERSION
+    int	i;
+    __u32 TEMP;
+#endif
 
     /* Set up first buffer and local data buffer */
     A = digest[ 0 ];
@@ -707,6 +725,25 @@
     E = digest[ 4 ];
     memcpy( eData, data, 16*sizeof(__u32));
 
+#ifdef SMALL_VERSION
+    /*
+     * Approximately 50% of the speed of the optimized version, but
+     * takes up 1/16 the space.  Saves about 6k on an i386 kernel.
+     */
+    for (i=0; i < 80; i++) {
+	    if (i < 20)
+		    TEMP = f1(B, C, D) + K1;
+	    else if (i < 40)
+		    TEMP = f2(B, C, D) + K2;
+	    else if (i < 60)
+		    TEMP = f3(B, C, D) + K3;
+	    else
+		    TEMP = f4(B, C, D) + K4;
+	    TEMP += ROTL (5, A) + E +
+		    ((i > 15) ? expand(eData, i) : eData[i]);
+	    E = D; D = C; C = ROTL(30, B); B = A; A = TEMP;
+    }
+#else
     /* Heavy mangling, in 4 sub-rounds of 20 iterations each. */
     subRound( A, B, C, D, E, f1, K1, eData[  0 ] );
     subRound( E, A, B, C, D, f1, K1, eData[  1 ] );
@@ -791,6 +828,7 @@
     subRound( D, E, A, B, C, f4, K4, expand( eData, 77 ) );
     subRound( C, D, E, A, B, f4, K4, expand( eData, 78 ) );
     subRound( B, C, D, E, A, f4, K4, expand( eData, 79 ) );
+#endif /* SMALL_VERSION */
 
     /* Build message digest */
     digest[ 0 ] += A;
@@ -938,12 +976,6 @@
 	__u32 tmp[HASH_BUFFER_SIZE];
 	char *cp,*dp;
 
-	if (to_user) {
-		ret = verify_area(VERIFY_WRITE, (void *) buf, nbytes);
-		if (ret)
-			return(ret);
-	}
-	
 	add_timer_randomness(r, &extract_timer_state, nbytes);
 	
 	/* Redundant, but just in case... */
@@ -956,6 +988,9 @@
 	else
 		r->entropy_count = 0;
 
+	if (r->entropy_count < WAIT_OUTPUT_BITS)
+		wake_up_interruptible(&random_write_wait);
+	
 	while (nbytes) {
 		/* Hash the pool to get the output */
 		tmp[0] = 0x67452301;
@@ -995,9 +1030,13 @@
 		
 		/* Copy data to destination buffer */
 		i = MIN(nbytes, HASH_BUFFER_SIZE*sizeof(__u32)/2);
-		if (to_user)
-			copy_to_user(buf, (__u8 const *)tmp, i);
-		else
+		if (to_user) {
+			i -= copy_to_user(buf, (__u8 const *)tmp, i);
+			if (!i) {
+				ret = -EFAULT;
+				break;
+			}
+		} else
 			memcpy(buf, (__u8 const *)tmp, i);
 		nbytes -= i;
 		buf += i;
@@ -1033,7 +1072,7 @@
 	if (nbytes == 0)
 		return 0;
 
-	add_wait_queue(&random_wait, &wait);
+	add_wait_queue(&random_read_wait, &wait);
 	while (nbytes > 0) {
 		current->state = TASK_INTERRUPTIBLE;
 		
@@ -1065,7 +1104,7 @@
 				/* like a named pipe */
 	}
 	current->state = TASK_RUNNING;
-	remove_wait_queue(&random_wait, &wait);
+	remove_wait_queue(&random_read_wait, &wait);
 
 	/*
 	 * If we gave the user some bytes and we have an inode pointer,
@@ -1091,9 +1130,10 @@
 {
 	unsigned int mask;
 
-	poll_wait(&random_wait, wait);
+	poll_wait(&random_read_wait, wait);
+	poll_wait(&random_write_wait, wait);
 	mask = 0;
-	if (random_state.entropy_count >= 8)
+	if (random_state.entropy_count >= WAIT_INPUT_BITS)
 		mask |= POLLIN | POLLRDNORM;
 	if (random_state.entropy_count < WAIT_OUTPUT_BITS)
 		mask |= POLLOUT | POLLWRNORM;
@@ -1104,25 +1144,33 @@
 random_write(struct inode * inode, struct file * file,
 	     const char * buffer, unsigned long count)
 {
-	int i;
-	__u32 word, *p;
-
-	for (i = count, p = (__u32 *)buffer;
-	     i >= sizeof(__u32);
-	     i-= sizeof(__u32), p++) {
-		copy_from_user(&word, p, sizeof(__u32));
-		add_entropy_word(&random_state, word);
-	}
-	if (i) {
-		word = 0;
-		copy_from_user(&word, p, i);
-		add_entropy_word(&random_state, word);
+	int 		i, bytes, ret = 0;
+	__u32 		buf[16];
+	const char 	*p = buffer;
+	int		c = count;
+
+	while (c > 0) {
+		bytes = MIN(c, sizeof(buf));
+
+		bytes -= copy_from_user(&buf, p, bytes);
+		if (!bytes) {
+			if (!ret)
+				ret = -EFAULT;
+			break;
+		}
+		c -= bytes;
+		p += bytes;
+		ret += bytes;
+		
+		i = (c+sizeof(__u32)-1) / sizeof(__u32);
+		while (i--)
+			add_entropy_word(&random_state, buf[i]);
 	}
-	if (inode) {
+	if ((ret > 0) && inode) {
 		inode->i_mtime = CURRENT_TIME;
 		inode->i_dirt = 1;
 	}
-	return count;
+	return ret;
 }
 
 static int
@@ -1132,20 +1180,6 @@
 	int *p, size, ent_count;
 	int retval;
 	
-	/*
-	 * Translate old 1.3.XX values.
-	 * Remove this code in 2.1.0.
-	 * <mec@duracef.shout.net>
-	 */
-	switch (cmd) {
-	case 0x01080000: cmd = RNDGETENTCNT;   break;
-	case 0x01080001: cmd = RNDADDTOENTCNT; break;
-	case 0x01080002: cmd = RNDGETPOOL;     break;
-	case 0x01080003: cmd = RNDADDENTROPY;  break;
-	case 0x01080004: cmd = RNDZAPENTCNT;   break;
-	case 0x01080006: cmd = RNDCLEARPOOL;   break;
-	}
-
 	switch (cmd) {
 	case RNDGETENTCNT:
 		retval = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
@@ -1181,7 +1215,7 @@
 		 * entropy.
 		 */
 		if (random_state.entropy_count >= WAIT_INPUT_BITS)
-			wake_up_interruptible(&random_wait);
+			wake_up_interruptible(&random_read_wait);
 		return 0;
 	case RNDGETPOOL:
 		if (!suser())
@@ -1201,11 +1235,8 @@
 			return -EINVAL;
 		if (size > POOLWORDS)
 			size = POOLWORDS;
-		retval = verify_area(VERIFY_WRITE, (void *) p,
-				     size * sizeof(__u32));
-		if (retval)
-			return retval;
-		copy_to_user(p, random_state.pool, size*sizeof(__u32));
+		if (copy_to_user(p, random_state.pool, size*sizeof(__u32)))
+			return -EFAULT;
 		return 0;
 	case RNDADDENTROPY:
 		if (!suser())
@@ -1242,7 +1273,7 @@
 		 * entropy.
 		 */
 		if (random_state.entropy_count >= WAIT_INPUT_BITS)
-			wake_up_interruptible(&random_wait);
+			wake_up_interruptible(&random_read_wait);
 		return 0;
 	case RNDZAPENTCNT:
 		if (!suser())
diff -u --recursive --new-file v2.1.27/linux/drivers/char/serial.c linux/drivers/char/serial.c
--- v2.1.27/linux/drivers/char/serial.c	Sun Feb  2 05:21:13 1997
+++ linux/drivers/char/serial.c	Fri Feb 28 15:02:02 1997
@@ -18,11 +18,13 @@
  *  rs_set_termios fixed to look also for changes of the input
  *      flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
  *                                            Bernd Anhäupl 05/17/96.
+ *
+ *  1/97:  Extended dumb serial ports are a config option now.  
+ *         Saves 4k.   Michael A. Griffith <grif@acm.org>
  * 
  * This module exports the following rs232 io functions:
  *
  *	int rs_init(void);
- * 	int rs_open(struct tty_struct * tty, struct file * filp)
  */
 
 #include <linux/config.h>
@@ -50,17 +52,13 @@
 #include <asm/bitops.h>
 
 static char *serial_name = "Serial driver";
-static char *serial_version = "4.22";
+static char *serial_version = "4.23";
 
-DECLARE_TASK_QUEUE(tq_serial);
+static DECLARE_TASK_QUEUE(tq_serial);
 
-struct tty_driver serial_driver, callout_driver;
+static struct tty_driver serial_driver, callout_driver;
 static int serial_refcount;
 
-/* serial subtype definitions */
-#define SERIAL_TYPE_NORMAL	1
-#define SERIAL_TYPE_CALLOUT	2
-
 /* number of characters left in xmit buffer before we ask for more */
 #define WAKEUP_CHARS 256
 
@@ -71,6 +69,16 @@
  *		Enables support for the venerable Bell Technologies
  *		HUB6 card.
  *
+ * CONFIG_SERIAL_MANY_PORTS
+ * 		Enables support for ports beyond the standard, stupid
+ * 		COM 1/2/3/4.
+ *
+ * CONFIG_SERIAL_MULTIPORT
+ * 		Enables support for special multiport board support.
+ *
+ * CONFIG_SERIAL_SHARE_IRQ
+ * 		Enables support for multiple serial ports on one IRQ
+ *
  * SERIAL_PARANOIA_CHECK
  * 		Check the magic number for the async_structure where
  * 		ever possible.
@@ -80,6 +88,34 @@
 #define CONFIG_SERIAL_NOPAUSE_IO
 #define SERIAL_DO_RESTART
 
+#if 0
+/* Normally these defines are controlled by the autoconf.h */
+
+#define CONFIG_SERIAL_MANY_PORTS
+#define CONFIG_SERIAL_SHARE_IRQ
+#define CONFIG_SERIAL_MULTIPORT
+#define CONFIG_HUB6
+#endif
+
+/* Sanity checks */
+
+#ifdef CONFIG_SERIAL_MULTIPORT
+#ifndef CONFIG_SERIAL_SHARE_IRQ
+#define CONFIG_SERIAL_SHARE_IRQ
+#endif
+#endif
+
+#ifdef CONFIG_HUB6
+#ifndef CONFIG_SERIAL_MANY_PORTS
+#define CONFIG_SERIAL_MANY_PORTS
+#endif
+#ifndef CONFIG_SERIAL_SHARE_IRQ
+#define CONFIG_SERIAL_SHARE_IRQ
+#endif
+#endif
+
+/* Set of debugging defines */
+
 #undef SERIAL_DEBUG_INTR
 #undef SERIAL_DEBUG_OPEN
 #undef SERIAL_DEBUG_FLOW
@@ -105,7 +141,9 @@
  */
 
 static struct async_struct *IRQ_ports[16];
+#ifdef CONFIG_SERIAL_MULTIPORT
 static struct rs_multiport_struct rs_multiport[16];
+#endif
 static int IRQ_timeout[16];
 static volatile int rs_irq_triggered;
 static volatile int rs_triggered;
@@ -146,10 +184,13 @@
 #define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST )
 #define STD_COM4_FLAGS ASYNC_BOOT_AUTOCONF
 
+
+#ifdef CONFIG_SERIAL_MANY_PORTS
 #define FOURPORT_FLAGS ASYNC_FOURPORT
 #define ACCENT_FLAGS 0
 #define BOCA_FLAGS 0
 #define HUB6_FLAGS 0
+#endif
 	
 /*
  * The following define the access methods for the HUB6 card. All
@@ -167,13 +208,13 @@
 
 #define C_P(card,port) (((card)<<6|(port)<<3) + 1)
 
-struct serial_state rs_table[] = {
+static struct serial_state rs_table[] = {
 	/* UART CLK   PORT IRQ     FLAGS        */
 	{ 0, BASE_BAUD, 0x3F8, 4, STD_COM_FLAGS },	/* ttyS0 */
 	{ 0, BASE_BAUD, 0x2F8, 3, STD_COM_FLAGS },	/* ttyS1 */
 	{ 0, BASE_BAUD, 0x3E8, 4, STD_COM_FLAGS },	/* ttyS2 */
 	{ 0, BASE_BAUD, 0x2E8, 3, STD_COM4_FLAGS },	/* ttyS3 */
-
+#ifdef CONFIG_SERIAL_MANY_PORTS
 	{ 0, BASE_BAUD, 0x1A0, 9, FOURPORT_FLAGS }, 	/* ttyS4 */
 	{ 0, BASE_BAUD, 0x1A8, 9, FOURPORT_FLAGS },	/* ttyS5 */
 	{ 0, BASE_BAUD, 0x1B0, 9, FOURPORT_FLAGS },	/* ttyS6 */
@@ -223,6 +264,7 @@
 	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,4) },	/* ttyS42 */
 	{ 0, BASE_BAUD, 0x302, 3, HUB6_FLAGS, C_P(1,5) },	/* ttyS43 */
 #endif
+#endif /* CONFIG_SERIAL_MANY_PORTS */
 #ifdef CONFIG_MCA
 	{ 0, BASE_BAUD, 0x3220, 3, STD_COM_FLAGS },
 	{ 0, BASE_BAUD, 0x3228, 3, STD_COM_FLAGS },
@@ -426,43 +468,79 @@
 	struct tty_struct *tty = info->tty;
 	unsigned char ch;
 	int ignored = 0;
+	struct	async_icount *icount;
 
+	icount = &info->state->icount;
 	do {
 		ch = serial_inp(info, UART_RX);
-		if (*status & UART_LSR_BI)
-			*status &= ~(UART_LSR_FE | UART_LSR_PE);
-		if (*status & info->ignore_status_mask) {
-			if (++ignored > 100)
-				break;
-			goto ignore_char;
-		}
-		*status &= info->read_status_mask;
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
 			break;
-		tty->flip.count++;
-		if (*status & (UART_LSR_BI)) {
+		*tty->flip.char_buf_ptr = ch;
+		icount->rx++;
+		
 #ifdef SERIAL_DEBUG_INTR
-			printk("handling break....");
+		printk("DR%02x:%02x...", ch, *status);
 #endif
-			*tty->flip.flag_buf_ptr++ = TTY_BREAK;
-			if (info->flags & ASYNC_SAK)
-				do_SAK(tty);
-		} else if (*status & UART_LSR_PE)
-			*tty->flip.flag_buf_ptr++ = TTY_PARITY;
-		else if (*status & UART_LSR_FE)
-			*tty->flip.flag_buf_ptr++ = TTY_FRAME;
-		else if (*status & UART_LSR_OE) 
-			*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
-		else
-			*tty->flip.flag_buf_ptr++ = 0;
-		*tty->flip.char_buf_ptr++ = ch;
+		*tty->flip.flag_buf_ptr = 0;
+		if (*status & (UART_LSR_BI | UART_LSR_PE |
+			       UART_LSR_FE | UART_LSR_OE)) {
+			/*
+			 * For statistics only
+			 */
+			if (*status & UART_LSR_BI) {
+				*status &= ~(UART_LSR_FE | UART_LSR_PE);
+				icount->brk++;
+			} else if (*status & UART_LSR_PE)
+				icount->parity++;
+			else if (*status & UART_LSR_FE)
+				icount->frame++;
+			if (*status & UART_LSR_OE)
+				icount->overrun++;
+
+			/*
+			 * Now check to see if character should be
+			 * ignored, and mask off conditions which
+			 * should be ignored.
+			 */
+			if (*status & info->ignore_status_mask) {
+				if (++ignored > 100)
+					break;
+				goto ignore_char;
+			}
+			*status &= info->read_status_mask;
+		
+			if (*status & (UART_LSR_BI)) {
+#ifdef SERIAL_DEBUG_INTR
+				printk("handling break....");
+#endif
+				*tty->flip.flag_buf_ptr = TTY_BREAK;
+				if (info->flags & ASYNC_SAK)
+					do_SAK(tty);
+			} else if (*status & UART_LSR_PE)
+				*tty->flip.flag_buf_ptr = TTY_PARITY;
+			else if (*status & UART_LSR_FE)
+				*tty->flip.flag_buf_ptr = TTY_FRAME;
+			if (*status & UART_LSR_OE) {
+				/*
+				 * Overrun is special, since it's
+				 * reported immediately, and doesn't
+				 * affect the current character
+				 */
+				if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+					tty->flip.count++;
+					tty->flip.flag_buf_ptr++;
+					tty->flip.char_buf_ptr++;
+					*tty->flip.flag_buf_ptr = TTY_OVERRUN;
+				}
+			}
+		}
+		tty->flip.flag_buf_ptr++;
+		tty->flip.char_buf_ptr++;
+		tty->flip.count++;
 	ignore_char:
 		*status = serial_inp(info, UART_LSR);
 	} while (*status & UART_LSR_DR);
 	queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
-#ifdef SERIAL_DEBUG_INTR
-	printk("DR...");
-#endif
 }
 
 static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
@@ -471,6 +549,7 @@
 	
 	if (info->x_char) {
 		serial_outp(info, UART_TX, info->x_char);
+		info->state->icount.tx++;
 		info->x_char = 0;
 		if (intr_done)
 			*intr_done = 0;
@@ -487,6 +566,7 @@
 	do {
 		serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]);
 		info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
+		info->state->icount.tx++;
 		if (--info->xmit_cnt <= 0)
 			break;
 	} while (--count > 0);
@@ -574,6 +654,7 @@
 	}
 }
 
+#ifdef CONFIG_SERIAL_SHARE_IRQ
 /*
  * This is the serial driver's generic interrupt routine
  */
@@ -583,8 +664,10 @@
 	struct async_struct * info;
 	int pass_counter = 0;
 	struct async_struct *end_mark = 0;
+#ifdef CONFIG_SERIAL_MULTIPORT	
 	int first_multi = 0;
 	struct rs_multiport_struct *multi;
+#endif
 
 #ifdef SERIAL_DEBUG_INTR
 	printk("rs_interrupt(%d)...", irq);
@@ -594,9 +677,11 @@
 	if (!info)
 		return;
 	
+#ifdef CONFIG_SERIAL_MULTIPORT	
 	multi = &rs_multiport[irq];
 	if (multi->port_monitor)
 		first_multi = inb(multi->port_monitor);
+#endif
 
 	do {
 		if (!info->tty ||
@@ -632,14 +717,18 @@
 			continue;
 		}
 	} while (end_mark != info);
+#ifdef CONFIG_SERIAL_MULTIPORT	
 	if (multi->port_monitor)
 		printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",
 		       info->state->irq, first_multi,
 		       inb(multi->port_monitor));
+#endif
 #ifdef SERIAL_DEBUG_INTR
 	printk("end.\n");
 #endif
 }
+#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */
+
 
 /*
  * This is the serial driver's interrupt routine for a single port
@@ -648,9 +737,11 @@
 {
 	int status;
 	int pass_counter = 0;
-	int first_multi = 0;
 	struct async_struct * info;
+#ifdef CONFIG_SERIAL_MULTIPORT	
+	int first_multi = 0;
 	struct rs_multiport_struct *multi;
+#endif
 	
 #ifdef SERIAL_DEBUG_INTR
 	printk("rs_interrupt_single(%d)...", irq);
@@ -660,9 +751,11 @@
 	if (!info || !info->tty)
 		return;
 
+#ifdef CONFIG_SERIAL_MULTIPORT	
 	multi = &rs_multiport[irq];
 	if (multi->port_monitor)
 		first_multi = inb(multi->port_monitor);
+#endif
 
 	do {
 		status = serial_inp(info, UART_LSR);
@@ -682,15 +775,18 @@
 		}
 	} while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
 	info->last_active = jiffies;
+#ifdef CONFIG_SERIAL_MULTIPORT	
 	if (multi->port_monitor)
 		printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",
 		       info->state->irq, first_multi,
 		       inb(multi->port_monitor));
+#endif
 #ifdef SERIAL_DEBUG_INTR
 	printk("end.\n");
 #endif
 }
 
+#ifdef CONFIG_SERIAL_MULTIPORT	
 /*
  * This is the serial driver's for multiport boards
  */
@@ -771,7 +867,7 @@
 	printk("end.\n");
 #endif
 }
-
+#endif
 
 /*
  * -------------------------------------------------------------------
@@ -851,6 +947,7 @@
 			if (!info)
 				continue;
 			cli();
+#ifdef CONFIG_SERIAL_SHARE_IRQ
 			if (info->next_port) {
 				do {
 					serial_out(info, UART_IER, 0);
@@ -858,11 +955,14 @@
 					serial_out(info, UART_IER, info->IER);
 					info = info->next_port;
 				} while (info);
+#ifdef CONFIG_SERIAL_MULTIPORT					
 				if (rs_multiport[i].port1)
 					rs_interrupt_multi(i, NULL, NULL);
 				else
+#endif
 					rs_interrupt(i, NULL, NULL);
 			} else
+#endif /* CONFIG_SERIAL_SHARE_IRQ */
 				rs_interrupt_single(i, NULL, NULL);
 			sti();
 		}
@@ -873,7 +973,11 @@
 
 	if (IRQ_ports[0]) {
 		cli();
+#ifdef CONFIG_SERIAL_SHARE_IRQ
 		rs_interrupt(0, NULL, NULL);
+#else
+		rs_interrupt_single(0, NULL, NULL);
+#endif
 		sti();
 
 		timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2;
@@ -948,12 +1052,15 @@
 
 static int startup(struct async_struct * info)
 {
-	unsigned short ICP;
 	unsigned long flags;
 	int	retval;
 	void (*handler)(int, void *, struct pt_regs *);
 	struct serial_state *state= info->state;
 	unsigned long page;
+#ifdef CONFIG_SERIAL_MANY_PORTS
+	unsigned short ICP;
+#endif
+
 
 	page = get_free_page(GFP_KERNEL);
 	if (!page)
@@ -1026,11 +1133,17 @@
 	if (state->irq && (!IRQ_ports[state->irq] ||
 			  !IRQ_ports[state->irq]->next_port)) {
 		if (IRQ_ports[state->irq]) {
+#ifdef CONFIG_SERIAL_SHARE_IRQ
 			free_irq(state->irq, NULL);
+#ifdef CONFIG_SERIAL_MULTIPORT				
 			if (rs_multiport[state->irq].port1)
 				handler = rs_interrupt_multi;
 			else
+#endif
 				handler = rs_interrupt;
+#else
+			return -EBUSY;
+#endif /* CONFIG_SERIAL_SHARE_IRQ */
 		} else 
 			handler = rs_interrupt_single;
 
@@ -1074,10 +1187,13 @@
 	info->MCR = 0;
 	if (info->tty->termios->c_cflag & CBAUD)
 		info->MCR = UART_MCR_DTR | UART_MCR_RTS;
+#ifdef CONFIG_SERIAL_MANY_PORTS
 	if (info->flags & ASYNC_FOURPORT) {
 		if (state->irq == 0)
 			info->MCR |= UART_MCR_OUT1;
-	} else {
+	} else
+#endif
+	{
 		if (state->irq != 0)
 			info->MCR |= UART_MCR_OUT2;
 	}
@@ -1095,12 +1211,14 @@
 	info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
 	serial_outp(info, UART_IER, info->IER);	/* enable interrupts */
 	
+#ifdef CONFIG_SERIAL_MANY_PORTS
 	if (info->flags & ASYNC_FOURPORT) {
 		/* Enable interrupts on the AST Fourport board */
 		ICP = (info->port & 0xFE0) | 0x01F;
 		outb_p(0x80, ICP);
 		(void) inb_p(ICP);
 	}
+#endif
 
 	/*
 	 * And clear the interrupt registers again for luck.
@@ -1193,11 +1311,13 @@
 
 	info->IER = 0;
 	serial_outp(info, UART_IER, 0x00);	/* disable all intrs */
+#ifdef CONFIG_SERIAL_MANY_PORTS
 	if (info->flags & ASYNC_FOURPORT) {
 		/* reset interrupts on the AST Fourport board */
 		(void) inb((info->port & 0xFE0) | 0x01F);
 		info->MCR |= UART_MCR_OUT1;
 	} else
+#endif
 		info->MCR &= ~UART_MCR_OUT2;
 #if defined(__alpha__) && !defined(CONFIG_PCI)
 	/*
@@ -1270,6 +1390,10 @@
 	}
 	if (!(cflag & PARODD))
 		cval |= UART_LCR_EPAR;
+#ifdef CMSPAR
+	if (cflag & CMSPAR)
+		cval |= UART_LCR_SPAR;
+#endif
 
 	/* Determine divisor based on baud rate */
 	i = cflag & CBAUD;
@@ -1303,6 +1427,7 @@
 		if (!quot)
 			quot = baud_base / 9600;
 	}
+	info->quot = quot;
 	info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base);
 	info->timeout += HZ/50;		/* Add .02 seconds of slop */
 
@@ -1376,7 +1501,8 @@
 	if (info->state->type == PORT_16750)
 		serial_outp(info, UART_FCR, fcr); 	/* set fcr */
 	serial_outp(info, UART_LCR, cval);		/* reset DLAB */
-	serial_outp(info, UART_FCR, fcr); 	/* set fcr */
+	if (info->state->type != PORT_16750)
+		serial_outp(info, UART_FCR, fcr); 	/* set fcr */
 	restore_flags(flags);
 }
 
@@ -1424,7 +1550,7 @@
 static int rs_write(struct tty_struct * tty, int from_user,
 		    const unsigned char *buf, int count)
 {
-	int	c, total = 0;
+	int	c, ret = 0;
 	struct async_struct *info = (struct async_struct *)tty->driver_data;
 	unsigned long flags;
 				
@@ -1445,7 +1571,12 @@
 			break;
 
 		if (from_user) {
-			copy_from_user(tmp_buf, buf, c);
+			c -= copy_from_user(tmp_buf, buf, c);
+			if (!c) {
+				if (!ret)
+					ret = -EFAULT;
+				break;
+			}
 			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
 				       SERIAL_XMIT_SIZE - info->xmit_head));
 			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
@@ -1456,7 +1587,7 @@
 		restore_flags(flags);
 		buf += c;
 		count -= c;
-		total += c;
+		ret += c;
 	}
 	if (from_user)
 		up(&tmp_buf_sem);
@@ -1466,7 +1597,7 @@
 		serial_out(info, UART_IER, info->IER);
 	}
 	restore_flags(flags);
-	return total;
+	return ret;
 }
 
 static int rs_write_room(struct tty_struct *tty)
@@ -1510,7 +1641,7 @@
  * This function is used to send a high-priority XON/XOFF character to
  * the device
  */
-void rs_send_xchar(struct tty_struct *tty, char ch)
+static void rs_send_xchar(struct tty_struct *tty, char ch)
 {
 	struct async_struct *info = (struct async_struct *)tty->driver_data;
 
@@ -1609,7 +1740,8 @@
 	tmp.closing_wait = state->closing_wait;
 	tmp.custom_divisor = state->custom_divisor;
 	tmp.hub6 = state->hub6;
-	copy_to_user(retinfo,&tmp,sizeof(*retinfo));
+	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+		return -EFAULT;
 	return 0;
 }
 
@@ -1621,9 +1753,8 @@
 	unsigned int		i,change_irq,change_port;
 	int 			retval = 0;
 
-	if (!new_info)
+	if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
 		return -EFAULT;
-	copy_from_user(&new_serial,new_info,sizeof(new_serial));
 	state = info->state;
 	old_state = *state;
   
@@ -1734,8 +1865,7 @@
 	status = serial_in(info, UART_LSR);
 	sti();
 	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
-	put_user(result,value);
-	return 0;
+	return put_user(result,value);
 }
 
 
@@ -1750,12 +1880,15 @@
 	sti();
 	result =  ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
 		| ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+#ifdef TIOCM_OUT1
+		| ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0)
+		| ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0)
+#endif
 		| ((status  & UART_MSR_DCD) ? TIOCM_CAR : 0)
 		| ((status  & UART_MSR_RI) ? TIOCM_RNG : 0)
 		| ((status  & UART_MSR_DSR) ? TIOCM_DSR : 0)
 		| ((status  & UART_MSR_CTS) ? TIOCM_CTS : 0);
-	put_user(result,value);
-	return 0;
+	return put_user(result,value);
 }
 
 static int set_modem_info(struct async_struct * info, unsigned int cmd,
@@ -1773,16 +1906,37 @@
 			info->MCR |= UART_MCR_RTS;
 		if (arg & TIOCM_DTR)
 			info->MCR |= UART_MCR_DTR;
+#ifdef TIOCM_OUT1
+		if (arg & TIOCM_OUT1)
+			info->MCR |= UART_MCR_OUT1;
+		if (arg & TIOCM_OUT2)
+			info->MCR |= UART_MCR_OUT2;
+#endif
 		break;
 	case TIOCMBIC:
 		if (arg & TIOCM_RTS)
 			info->MCR &= ~UART_MCR_RTS;
 		if (arg & TIOCM_DTR)
 			info->MCR &= ~UART_MCR_DTR;
+#ifdef TIOCM_OUT1
+		if (arg & TIOCM_OUT1)
+			info->MCR &= ~UART_MCR_OUT1;
+		if (arg & TIOCM_OUT2)
+			info->MCR &= ~UART_MCR_OUT2;
+#endif
 		break;
 	case TIOCMSET:
-		info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
+		info->MCR = ((info->MCR & ~(UART_MCR_RTS |
+#ifdef TIOCM_OUT1
+					    UART_MCR_OUT1 |
+					    UART_MCR_OUT2 |
+#endif
+					    UART_MCR_DTR))
 			     | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
+#ifdef TIOCM_OUT1
+			     | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0)
+			     | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0)
+#endif
 			     | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
 		break;
 	default:
@@ -1883,6 +2037,7 @@
 	return wild_interrupts;
 }
 
+#ifdef CONFIG_SERIAL_MULTIPORT
 static int get_multiport_struct(struct async_struct * info,
 				struct serial_multiport_struct *retinfo)
 {
@@ -1911,9 +2066,9 @@
 
 	ret.irq = info->state->irq;
 
-	copy_to_user(retinfo,&ret,sizeof(*retinfo));
+	if (copy_to_user(retinfo,&ret,sizeof(*retinfo)))
+		return -EFAULT;
 	return 0;
-	
 }
 
 static int set_multiport_struct(struct async_struct * info,
@@ -1928,13 +2083,12 @@
 
 	if (!suser())
 		return -EPERM;
-	if (!in_multi)
-		return -EFAULT;
 	state = info->state;
 	
-	copy_from_user(&new_multi, in_multi,
-		      sizeof(struct serial_multiport_struct));
-
+	if (copy_from_user(&new_multi, in_multi,
+			   sizeof(struct serial_multiport_struct)))
+		return -EFAULT;
+	
 	if (new_multi.irq != state->irq || state->irq == 0 ||
 	    !IRQ_ports[state->irq])
 		return -EINVAL;
@@ -1996,6 +2150,7 @@
 
 	return 0;
 }
+#endif
 
 static int rs_ioctl(struct tty_struct *tty, struct file * file,
 		    unsigned int cmd, unsigned long arg)
@@ -2053,82 +2208,52 @@
 				 (arg ? CLOCAL : 0));
 			return 0;
 		case TIOCMGET:
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(unsigned int));
-			if (error)
-				return error;
 			return get_modem_info(info, (unsigned int *) arg);
 		case TIOCMBIS:
 		case TIOCMBIC:
 		case TIOCMSET:
 			return set_modem_info(info, cmd, (unsigned int *) arg);
 		case TIOCGSERIAL:
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-						sizeof(struct serial_struct));
-			if (error)
-				return error;
 			return get_serial_info(info,
 					       (struct serial_struct *) arg);
 		case TIOCSSERIAL:
-			error = verify_area(VERIFY_READ, (void *) arg,
-						sizeof(struct serial_struct));
-			if (error)
-				return error;
 			return set_serial_info(info,
 					       (struct serial_struct *) arg);
 		case TIOCSERCONFIG:
 			return do_autoconfig(info);
 
 		case TIOCSERGWILD:
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-					    sizeof(int));
-			if (error)
-				return error;
-			put_user(rs_wild_int_mask, (unsigned int *) arg);
-			return 0;
-
+			return put_user(rs_wild_int_mask,
+					(unsigned int *) arg);
 		case TIOCSERGETLSR: /* Get line status register */
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(unsigned int));
-			if (error)
-				return error;
-			else
-			    return get_lsr_info(info, (unsigned int *) arg);
+			return get_lsr_info(info, (unsigned int *) arg);
 
 		case TIOCSERSWILD:
 			if (!suser())
 				return -EPERM;
-			error = verify_area(VERIFY_READ, (void *) arg,sizeof(long));
+			error = get_user(rs_wild_int_mask,
+					 (unsigned int *) arg);
 			if (error)
 				return error;
-			get_user(rs_wild_int_mask, (unsigned int *) arg);
 			if (rs_wild_int_mask < 0)
 				rs_wild_int_mask = check_wild_interrupts(0);
 			return 0;
 
 		case TIOCSERGSTRUCT:
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-						sizeof(struct async_struct));
-			if (error)
-				return error;
-			copy_to_user((struct async_struct *) arg,
-				    info, sizeof(struct async_struct));
+			if (copy_to_user((struct async_struct *) arg,
+					 info, sizeof(struct async_struct)))
+				return -EFAULT;
 			return 0;
-			
+				
+#ifdef CONFIG_SERIAL_MULTIPORT
 		case TIOCSERGETMULTI:
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-				    sizeof(struct serial_multiport_struct));
-			if (error)
-				return error;
 			return get_multiport_struct(info,
 				       (struct serial_multiport_struct *) arg);
 		case TIOCSERSETMULTI:
-			error = verify_area(VERIFY_READ, (void *) arg,
-				    sizeof(struct serial_multiport_struct));
-			if (error)
-				return error;
 			return set_multiport_struct(info,
 				       (struct serial_multiport_struct *) arg);
+#endif
+			
 		/*
 		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
 		 * - mask passed in arg for lines of interest
@@ -2168,18 +2293,18 @@
 		 *     RI where only 0->1 is counted.
 		 */
 		case TIOCGICOUNT:
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(struct serial_icounter_struct));
-			if (error)
-				return error;
 			cli();
 			cnow = info->state->icount;
 			sti();
 			p_cuser = (struct serial_icounter_struct *) arg;
-			put_user(cnow.cts, &p_cuser->cts);
-			put_user(cnow.dsr, &p_cuser->dsr);
-			put_user(cnow.rng, &p_cuser->rng);
-			put_user(cnow.dcd, &p_cuser->dcd);
+			error = put_user(cnow.cts, &p_cuser->cts);
+			if (error) return error;
+			error = put_user(cnow.dsr, &p_cuser->dsr);
+			if (error) return error;
+			error = put_user(cnow.rng, &p_cuser->rng);
+			if (error) return error;
+			error = put_user(cnow.dcd, &p_cuser->dcd);
+			if (error) return error;
 			return 0;
 
 		default:
@@ -2409,7 +2534,7 @@
 /*
  * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
  */
-void rs_hangup(struct tty_struct *tty)
+static void rs_hangup(struct tty_struct *tty)
 {
 	struct async_struct * info = (struct async_struct *)tty->driver_data;
 	struct serial_state *state = info->state;
@@ -2567,7 +2692,7 @@
 	return 0;
 }
 
-int get_async_struct(int line, struct async_struct **ret_info)
+static int get_async_struct(int line, struct async_struct **ret_info)
 {
 	struct async_struct *info;
 	struct serial_state *sstate;
@@ -2609,7 +2734,7 @@
  * the IRQ chain.   It also performs the serial-specific
  * initialization for the tty structure.
  */
-int rs_open(struct tty_struct *tty, struct file * filp)
+static int rs_open(struct tty_struct *tty, struct file * filp)
 {
 	struct async_struct	*info;
 	int 			retval, line;
@@ -2677,6 +2802,106 @@
 }
 
 /*
+ * /proc fs routines....
+ */
+
+static int inline line_info(char *buf, struct serial_state *state)
+{
+	struct async_struct *info = state->info, scr_info;
+	char	stat_buf[30], control, status;
+	int	ret;
+
+	ret = sprintf(buf, "%d: uart:%s port:%X irq:%d",
+		      state->line, uart_config[state->type].name, 
+		      state->port, state->irq);
+
+	if (!state->port || (state->type == PORT_UNKNOWN)) {
+		ret += sprintf(buf+ret, "\n");
+		return ret;
+	}
+
+	/*
+	 * Figure out the current RS-232 lines
+	 */
+	if (!info) {
+		info = &scr_info;	/* This is just for serial_{in,out} */
+
+		info->magic = SERIAL_MAGIC;
+		info->port = state->port;
+		info->flags = state->flags;
+		info->quot = 0;
+		info->tty = 0;
+	}
+	cli();
+	status = serial_in(info, UART_MSR);
+	control = info ? info->MCR : serial_in(info, UART_MCR);
+	sti();
+	
+	stat_buf[0] = 0;
+	stat_buf[1] = 0;
+	if (control & UART_MCR_RTS)
+		strcat(stat_buf, "|RTS");
+	if (status & UART_MSR_CTS)
+		strcat(stat_buf, "|CTS");
+	if (control & UART_MCR_DTR)
+		strcat(stat_buf, "|DTR");
+	if (status & UART_MSR_DSR)
+		strcat(stat_buf, "|DSR");
+	if (status & UART_MSR_DCD)
+		strcat(stat_buf, "|CD");
+	if (status & UART_MSR_RI)
+		strcat(stat_buf, "|RI");
+
+	if (info->quot) {
+		ret += sprintf(buf+ret, " baud:%d",
+			       state->baud_base / info->quot);
+	}
+
+	ret += sprintf(buf+ret, " tx:%d rx:%d",
+		      state->icount.tx, state->icount.rx);
+
+	if (state->icount.frame)
+		ret += sprintf(buf+ret, " fe:%d", state->icount.frame);
+	
+	if (state->icount.parity)
+		ret += sprintf(buf+ret, " pe:%d", state->icount.parity);
+	
+	if (state->icount.brk)
+		ret += sprintf(buf+ret, " brk:%d", state->icount.brk);	
+
+	if (state->icount.overrun)
+		ret += sprintf(buf+ret, " oe:%d", state->icount.overrun);
+
+	/*
+	 * Last thing is the RS-232 status lines
+	 */
+	ret += sprintf(buf+ret, " %s\n", stat_buf+1);
+	return ret;
+}
+
+int rs_read_proc(char *page, char **start, off_t off, int count, void
+		 *data)
+{
+	int i, len = 0;
+	off_t	begin = 0;
+
+	len += sprintf(page, "serinfo:1.0 driver:%s\n", serial_version);
+	for (i = 0; i < NR_PORTS && len < 4000; i++) {
+		len += line_info(page + len, &rs_table[i]);
+		if (len+begin > off+count)
+			break;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	if (off >= len+begin)
+		return 0;
+	*start = page + (begin-off);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
  * ---------------------------------------------------------------------
  * rs_init() and friends
  *
@@ -2711,9 +2936,14 @@
  */
 static int get_auto_irq(struct async_struct *info)
 {
-	unsigned char save_MCR, save_IER, save_ICP=0;
-	unsigned short ICP=0, port = info->port;
+        
+	unsigned char save_MCR, save_IER;
 	unsigned long timeout;
+#ifdef CONFIG_SERIAL_MANY_PORTS
+	unsigned char save_ICP=0;
+	unsigned short ICP=0, port = info->port;
+#endif
+
 	
 	/*
 	 * Enable interrupts and see who answers
@@ -2722,6 +2952,7 @@
 	cli();
 	save_IER = serial_inp(info, UART_IER);
 	save_MCR = serial_inp(info, UART_MCR);
+#ifdef CONFIG_SERIAL_MANY_PORTS
 	if (info->flags & ASYNC_FOURPORT)  {
 		serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
 		serial_outp(info, UART_IER, 0x0f);	/* enable all intrs */
@@ -2729,7 +2960,9 @@
 		save_ICP = inb_p(ICP);
 		outb_p(0x80, ICP);
 		(void) inb_p(ICP);
-	} else {
+	} else 
+#endif
+        {
 		serial_outp(info, UART_MCR,
 			    UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
 		serial_outp(info, UART_IER, 0x0f);	/* enable all intrs */
@@ -2754,8 +2987,10 @@
 	cli();
 	serial_outp(info, UART_IER, save_IER);
 	serial_outp(info, UART_MCR, save_MCR);
+#ifdef CONFIG_SERIAL_MANY_PORTS
 	if (info->flags & ASYNC_FOURPORT)
 		outb_p(save_ICP, ICP);
+#endif
 	sti();
 	return(rs_irq_triggered);
 }
@@ -2912,7 +3147,7 @@
 			serial_outp(info, UART_LCR, 0);
 			serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
 			scratch = serial_in(info, UART_IIR) >> 5;
-			if (scratch == 7)
+			if (scratch == 6)
 				state->type = PORT_16750;
 		}
 		serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
@@ -2981,7 +3216,10 @@
 	for (i = 0; i < 16; i++) {
 		IRQ_ports[i] = 0;
 		IRQ_timeout[i] = 0;
-		memset(&rs_multiport[i], 0, sizeof(struct rs_multiport_struct));
+#ifdef CONFIG_SERIAL_MULTIPORT
+		memset(&rs_multiport[i], 0,
+		       sizeof(struct rs_multiport_struct));
+#endif
 	}
 	
 	show_serial_version();
@@ -2990,6 +3228,7 @@
 	
 	memset(&serial_driver, 0, sizeof(struct tty_driver));
 	serial_driver.magic = TTY_DRIVER_MAGIC;
+	serial_driver.driver_name = "serial";
 	serial_driver.name = "ttyS";
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64;
@@ -3022,7 +3261,8 @@
 	serial_driver.start = rs_start;
 	serial_driver.hangup = rs_hangup;
 	serial_driver.wait_until_sent = rs_wait_until_sent;
-
+	serial_driver.read_proc = rs_read_proc;
+	
 	/*
 	 * The callout device is just like normal device except for
 	 * major number and the subtype code.
@@ -3031,6 +3271,8 @@
 	callout_driver.name = "cua";
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+	callout_driver.read_proc = 0;
+	callout_driver.proc_entry = 0;
 
 	if (tty_register_driver(&serial_driver))
 		panic("Couldn't register serial driver\n");
@@ -3048,22 +3290,26 @@
 		state->normal_termios = serial_driver.init_termios;
 		state->icount.cts = state->icount.dsr = 
 			state->icount.rng = state->icount.dcd = 0;
+		state->icount.rx = state->icount.tx = 0;
+		state->icount.frame = state->icount.parity = 0;
+		state->icount.overrun = state->icount.brk = 0;
 		if (state->irq == 2)
 			state->irq = 9;
 		if (state->type == PORT_UNKNOWN) {
 			if (!(state->flags & ASYNC_BOOT_AUTOCONF))
 				continue;
+			if (check_region(state->port,8))
+				continue;
 			autoconfig(state);
 			if (state->type == PORT_UNKNOWN)
 				continue;
 		}
-		printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d) is a %s\n",
+		printk(KERN_INFO "ttyS%02d%s at 0x%04x (irq = %d) is a %s\n",
 		       state->line,
 		       (state->flags & ASYNC_FOURPORT) ? " FourPort" : "",
 		       state->port, state->irq,
 		       uart_config[state->type].name);
 	}
-
 	return 0;
 }
 
@@ -3161,5 +3407,10 @@
 		if (rs_table[i].type != PORT_UNKNOWN)
 			release_region(rs_table[i].port, 8);
 	}
+	if (tmp_buf) {
+		free_page((unsigned long) tmp_buf);
+		tmp_buf = NULL;
+	}
 }
 #endif /* MODULE */
+
diff -u --recursive --new-file v2.1.27/linux/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- v2.1.27/linux/drivers/char/tty_io.c	Sun Jan 26 02:07:17 1997
+++ linux/drivers/char/tty_io.c	Fri Feb 28 15:00:53 1997
@@ -63,6 +63,9 @@
 #include <linux/string.h>
 #include <linux/malloc.h>
 #include <linux/poll.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -1135,6 +1138,25 @@
 		return;
 
 	/*
+	 * Sanity check --- if tty->count is zero, there shouldn't be
+	 * any waiters on tty->read_wait or tty->write_wait.  But just
+	 * in case....
+	 */
+	while (1) {
+		if (waitqueue_active(&tty->read_wait)) {
+			printk("release_dev: %s: read_wait active?!?\n",
+			       tty_name(tty));
+			wake_up(&tty->read_wait);
+		} else if (waitqueue_active(&tty->write_wait)) {
+			printk("release_dev: %s: write_wait active?!?\n",
+			       tty_name(tty));
+			wake_up(&tty->write_wait);
+		} else
+			break;
+		schedule();
+	}
+	
+	/*
 	 * We're committed; at this point, we must not block!
 	 */
 	if (o_tty) {
@@ -1232,7 +1254,9 @@
 	int minor;
 	int noctty, retval;
 	kdev_t device;
+	unsigned short saved_flags;
 
+	saved_flags = filp->f_flags;
 retry_open:
 	noctty = filp->f_flags & O_NOCTTY;
 	device = inode->i_rdev;
@@ -1240,6 +1264,7 @@
 		if (!current->tty)
 			return -ENXIO;
 		device = current->tty->device;
+		filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
 		/* noctty = 1; */
 	}
 	if (device == CONSOLE_DEV) {
@@ -1263,6 +1288,7 @@
 		retval = tty->driver.open(tty, filp);
 	else
 		retval = -ENODEV;
+	filp->f_flags = saved_flags;
 
 	if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
 		retval = -EBUSY;
@@ -1830,6 +1856,10 @@
 	driver->next = tty_drivers;
 	if (tty_drivers) tty_drivers->prev = driver;
 	tty_drivers = driver;
+	
+#ifdef CONFIG_PROC_FS
+	proc_tty_register_driver(driver);
+#endif	
 	return error;
 }
 
@@ -1871,6 +1901,9 @@
 	if (driver->next)
 		driver->next->prev = driver->prev;
 
+#ifdef CONFIG_PROC_FS
+	proc_tty_unregister_driver(driver);
+#endif
 	return 0;
 }
 
@@ -1925,18 +1958,24 @@
 	 */
 	memset(&dev_tty_driver, 0, sizeof(struct tty_driver));
 	dev_tty_driver.magic = TTY_DRIVER_MAGIC;
-	dev_tty_driver.name = "tty";
+	dev_tty_driver.driver_name = "/dev/tty";
+	dev_tty_driver.name = dev_tty_driver.driver_name + 5;
 	dev_tty_driver.name_base = 0;
 	dev_tty_driver.major = TTY_MAJOR;
 	dev_tty_driver.minor_start = 0;
 	dev_tty_driver.num = 1;
+	dev_tty_driver.type = TTY_DRIVER_TYPE_SYSTEM;
+	dev_tty_driver.subtype = SYSTEM_TYPE_TTY;
 	
 	if (tty_register_driver(&dev_tty_driver))
 		panic("Couldn't register /dev/tty driver\n");
 
 	dev_console_driver = dev_tty_driver;
-	dev_console_driver.name = "console";
+	dev_console_driver.driver_name = "/dev/console";
+	dev_console_driver.name = dev_console_driver.driver_name + 5;
 	dev_console_driver.major = TTYAUX_MAJOR;
+	dev_console_driver.type = TTY_DRIVER_TYPE_SYSTEM;
+	dev_console_driver.subtype = SYSTEM_TYPE_CONSOLE;
 
 	if (tty_register_driver(&dev_console_driver))
 		panic("Couldn't register /dev/console driver\n");
diff -u --recursive --new-file v2.1.27/linux/drivers/char/tty_ioctl.c linux/drivers/char/tty_ioctl.c
--- v2.1.27/linux/drivers/char/tty_ioctl.c	Mon Dec 30 02:44:34 1996
+++ linux/drivers/char/tty_ioctl.c	Fri Feb 28 15:00:55 1997
@@ -163,10 +163,10 @@
 		memcpy(&tmp_termios, tty->termios, sizeof(struct termios));
 		user_termio_to_kernel_termios(&tmp_termios, (struct termio *) arg);
 	} else {
-		retval = verify_area(VERIFY_READ, (void *) arg, sizeof(struct termios));
+		retval = user_termios_to_kernel_termios
+			(&tmp_termios, (struct termios *) arg);
 		if (retval)
 			return retval;
-		user_termios_to_kernel_termios(&tmp_termios, (struct termios *) arg);
 	}
 
 	if ((opt & TERMIOS_FLUSH) && tty->ldisc.flush_buffer)
@@ -240,15 +240,13 @@
 	int retval;
 	struct sgttyb tmp;
 
-	retval = verify_area(VERIFY_WRITE, sgttyb, sizeof(struct sgttyb));
-	if (retval)
-		return retval;
 	tmp.sg_ispeed = 0;
 	tmp.sg_ospeed = 0;
 	tmp.sg_erase = tty->termios->c_cc[VERASE];
 	tmp.sg_kill = tty->termios->c_cc[VKILL];
 	tmp.sg_flags = get_sgflags(tty);
-	copy_to_user(sgttyb, &tmp, sizeof(tmp));
+	if (copy_to_user(sgttyb, &tmp, sizeof(tmp)))
+		return -EFAULT;
 	return 0;
 }
 
@@ -283,14 +281,12 @@
 	struct sgttyb tmp;
 	struct termios termios;
 
-	retval = verify_area(VERIFY_READ, sgttyb, sizeof(struct sgttyb));
-	if (retval)
-		return retval;
 	retval = tty_check_change(tty);
 	if (retval)
 		return retval;
 	termios =  *tty->termios;
-	copy_from_user(&tmp, sgttyb, sizeof(tmp));
+	if (copy_from_user(&tmp, sgttyb, sizeof(tmp)))
+		return -EFAULT;
 	termios.c_cc[VERASE] = tmp.sg_erase;
 	termios.c_cc[VKILL] = tmp.sg_kill;
 	set_sgflags(&termios, tmp.sg_flags);
@@ -305,16 +301,14 @@
 	int retval;
 	struct tchars tmp;
 
-	retval = verify_area(VERIFY_WRITE, tchars, sizeof(struct tchars));
-	if (retval)
-		return retval;
 	tmp.t_intrc = tty->termios->c_cc[VINTR];
 	tmp.t_quitc = tty->termios->c_cc[VQUIT];
 	tmp.t_startc = tty->termios->c_cc[VSTART];
 	tmp.t_stopc = tty->termios->c_cc[VSTOP];
 	tmp.t_eofc = tty->termios->c_cc[VEOF];
 	tmp.t_brkc = tty->termios->c_cc[VEOL2];	/* what is brkc anyway? */
-	copy_to_user(tchars, &tmp, sizeof(tmp));
+	if (copy_to_user(tchars, &tmp, sizeof(tmp)))
+		return -EFAULT;
 	return 0;
 }
 
@@ -323,10 +317,8 @@
 	int retval;
 	struct tchars tmp;
 
-	retval = verify_area(VERIFY_READ, tchars, sizeof(struct tchars));
-	if (retval)
-		return retval;
-	copy_from_user(&tmp, tchars, sizeof(tmp));
+	if (copy_from_user(&tmp, tchars, sizeof(tmp)))
+		return -EFAULT;
 	tty->termios->c_cc[VINTR] = tmp.t_intrc;
 	tty->termios->c_cc[VQUIT] = tmp.t_quitc;
 	tty->termios->c_cc[VSTART] = tmp.t_startc;
@@ -343,16 +335,14 @@
 	int retval;
 	struct ltchars tmp;
 
-	retval = verify_area(VERIFY_WRITE, ltchars, sizeof(struct ltchars));
-	if (retval)
-		return retval;
 	tmp.t_suspc = tty->termios->c_cc[VSUSP];
 	tmp.t_dsuspc = tty->termios->c_cc[VSUSP];	/* what is dsuspc anyway? */
 	tmp.t_rprntc = tty->termios->c_cc[VREPRINT];
 	tmp.t_flushc = tty->termios->c_cc[VEOL2];	/* what is flushc anyway? */
 	tmp.t_werasc = tty->termios->c_cc[VWERASE];
 	tmp.t_lnextc = tty->termios->c_cc[VLNEXT];
-	copy_to_user(ltchars, &tmp, sizeof(tmp));
+	if (copy_to_user(ltchars, &tmp, sizeof(tmp)))
+		return -EFAULT;
 	return 0;
 }
 
@@ -361,10 +351,9 @@
 	int retval;
 	struct ltchars tmp;
 
-	retval = verify_area(VERIFY_READ, ltchars, sizeof(struct ltchars));
-	if (retval)
-		return retval;
-	copy_from_user(&tmp, ltchars, sizeof(tmp));
+	if (copy_from_user(&tmp, ltchars, sizeof(tmp)))
+		return -EFAULT;
+
 	tty->termios->c_cc[VSUSP] = tmp.t_suspc;
 	tty->termios->c_cc[VEOL2] = tmp.t_dsuspc;	/* what is dsuspc anyway? */
 	tty->termios->c_cc[VREPRINT] = tmp.t_rprntc;
@@ -427,13 +416,9 @@
 			return set_ltchars(real_tty, (struct ltchars *) arg);
 #endif
 		case TCGETS:
-			retval = verify_area(VERIFY_WRITE, (void *) arg,
-					     sizeof (struct termios));
-			if (retval)
-				return retval;
-			kernel_termios_to_user_termios((struct termios *)arg,
-						       real_tty->termios);
-			return 0;
+			return kernel_termios_to_user_termios
+				((struct termios *)arg,
+				 real_tty->termios);
 		case TCSETSF:
 			opt |= TERMIOS_FLUSH;
 		case TCSETSW:
@@ -499,53 +484,31 @@
 			}
 			return 0;
 		case TIOCOUTQ:
-			retval = verify_area(VERIFY_WRITE, (void *) arg,
-					     sizeof (int));
-			if (retval)
-				return retval;
-			if (tty->driver.chars_in_buffer)
-				put_user(tty->driver.chars_in_buffer(tty),
-					 (int *) arg);
-			else
-				put_user(0, (int *) arg);
-			return 0;
+			return put_user(tty->driver.chars_in_buffer ?
+					tty->driver.chars_in_buffer(tty) : 0,
+					(int *) arg);
 		case TIOCINQ:
-			retval = verify_area(VERIFY_WRITE, (void *) arg,
-					     sizeof (unsigned int));
-			if (retval)
-				return retval;
 			retval = tty->read_cnt;
 			if (L_ICANON(tty))
 				retval = inq_canon(tty);
-			put_user(retval, (unsigned int *) arg);
-			return 0;
+			return put_user(retval, (unsigned int *) arg);
 		case TIOCGLCKTRMIOS:
-			retval = verify_area(VERIFY_WRITE, (void *) arg,
-					     sizeof (struct termios));
-			if (retval)
-				return retval;
-			kernel_termios_to_user_termios((struct termios *)arg,
-						       real_tty->termios_locked);
-			return 0;
+			return kernel_termios_to_user_termios
+				((struct termios *)arg,
+				 real_tty->termios_locked);
 		case TIOCSLCKTRMIOS:
 			if (!suser())
 				return -EPERM;
-			retval = verify_area(VERIFY_READ, (void *) arg,
-					     sizeof (struct termios));
-			if (retval)
-				return retval;
-			user_termios_to_kernel_termios(real_tty->termios_locked,
-						       (struct termios *) arg);
-			return 0;
+			return user_termios_to_kernel_termios
+				(real_tty->termios_locked,
+				 (struct termios *) arg);
 		case TIOCPKT:
 			if (tty->driver.type != TTY_DRIVER_TYPE_PTY ||
 			    tty->driver.subtype != PTY_TYPE_MASTER)
 				return -ENOTTY;
-			retval = verify_area(VERIFY_READ, (void *) arg,
-					     sizeof (int));
+			retval = get_user(retval, (int *) arg);
 			if (retval)
 				return retval;
-			get_user(retval, (int *) arg);
 			if (retval) {
 				if (!tty->packet) {
 					tty->packet = 1;
diff -u --recursive --new-file v2.1.27/linux/drivers/net/ppp.c linux/drivers/net/ppp.c
--- v2.1.27/linux/drivers/net/ppp.c	Sun Feb  2 05:18:41 1997
+++ linux/drivers/net/ppp.c	Fri Feb 28 15:00:55 1997
@@ -358,6 +358,7 @@
  */
 	(void) memset (&ppp_ldisc, 0, sizeof (ppp_ldisc));
 	ppp_ldisc.magic		= TTY_LDISC_MAGIC;
+	ppp_ldisc.name          = "ppp";
 	ppp_ldisc.open		= ppp_tty_open;
 	ppp_ldisc.close		= ppp_tty_close;
 	ppp_ldisc.read		= ppp_tty_read;
diff -u --recursive --new-file v2.1.27/linux/drivers/net/slip.c linux/drivers/net/slip.c
--- v2.1.27/linux/drivers/net/slip.c	Sun Feb  2 05:18:42 1997
+++ linux/drivers/net/slip.c	Fri Feb 28 15:00:55 1997
@@ -1120,6 +1120,7 @@
 	/* Fill in our line protocol discipline, and register it */
 	memset(&sl_ldisc, 0, sizeof(sl_ldisc));
 	sl_ldisc.magic  = TTY_LDISC_MAGIC;
+	sl_ldisc.name   = "slip";
 	sl_ldisc.flags  = 0;
 	sl_ldisc.open   = slip_open;
 	sl_ldisc.close  = slip_close;
diff -u --recursive --new-file v2.1.27/linux/drivers/pci/pci.c linux/drivers/pci/pci.c
--- v2.1.27/linux/drivers/pci/pci.c	Thu Feb 27 10:57:31 1997
+++ linux/drivers/pci/pci.c	Fri Feb 28 15:15:42 1997
@@ -917,29 +917,47 @@
 			pcibios_write_config_word(bus->number, devfn,
 						  PCI_STATUS, 0xffff);
 			/*
-			 * Configure the bus numbers for this bridge:
+			 * Read the existing primary/secondary/subordinate bus
+			 * number configuration to determine if the PCI bridge
+			 * has already been configured by the system.  If so,
+			 * do not modify the configuration, merely note it.
 			 */
 			pcibios_read_config_dword(bus->number, devfn, 0x18,
 						  &buses);
-			buses &= 0xff000000;
-			buses |= (((unsigned int)(child->primary)     <<  0) |
-				  ((unsigned int)(child->secondary)   <<  8) |
-				  ((unsigned int)(child->subordinate) << 16));
-			pcibios_write_config_dword(bus->number, devfn, 0x18,
-						   buses);
-			/*
-			 * Now we can scan all subordinate buses:
-			 */
-			max = scan_bus(child, mem_startp);
-			/*
-			 * Set the subordinate bus number to its real
-			 * value:
-			 */
-			child->subordinate = max;
-			buses = (buses & 0xff00ffff)
-			  | ((unsigned int)(child->subordinate) << 16);
-			pcibios_write_config_dword(bus->number, devfn, 0x18,
-						   buses);
+			if ((buses & 0xFFFFFF) != 0)
+			  {
+			    child->primary = buses & 0xFF;
+			    child->secondary = (buses >> 8) & 0xFF;
+			    child->subordinate = (buses >> 16) & 0xFF;
+			    child->number = child->secondary;
+			    max = scan_bus(child, mem_startp);
+			  }
+			else
+			  {
+			    /*
+			     * Configure the bus numbers for this bridge:
+			     */
+			    buses &= 0xff000000;
+			    buses |=
+			      (((unsigned int)(child->primary)     <<  0) |
+			       ((unsigned int)(child->secondary)   <<  8) |
+			       ((unsigned int)(child->subordinate) << 16));
+			    pcibios_write_config_dword(bus->number, devfn, 0x18,
+						       buses);
+			    /*
+			     * Now we can scan all subordinate buses:
+			     */
+			    max = scan_bus(child, mem_startp);
+			    /*
+			     * Set the subordinate bus number to its real
+			     * value:
+			     */
+			    child->subordinate = max;
+			    buses = (buses & 0xff00ffff)
+			      | ((unsigned int)(child->subordinate) << 16);
+			    pcibios_write_config_dword(bus->number, devfn, 0x18,
+						       buses);
+			  }
 			pcibios_write_config_word(bus->number, devfn,
 						  PCI_COMMAND, cr);
 		}
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/AM53C974.c linux/drivers/scsi/AM53C974.c
--- v2.1.27/linux/drivers/scsi/AM53C974.c	Tue Oct  1 00:34:00 1996
+++ linux/drivers/scsi/AM53C974.c	Thu Feb 27 12:49:29 1997
@@ -684,6 +684,7 @@
 AM53C974_write_8(CMDREG, CMDREG_RBUS);     /* reset SCSI bus */
 udelay(10);
 AM53C974_config_after_reset(instance);
+udelay(500000);
 return(1);
 }
 
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/BusLogic.c linux/drivers/scsi/BusLogic.c
--- v2.1.27/linux/drivers/scsi/BusLogic.c	Sat Nov 30 23:44:53 1996
+++ linux/drivers/scsi/BusLogic.c	Sun Feb 23 16:14:00 1997
@@ -1,6 +1,6 @@
 /*
 
-  Linux Driver for BusLogic MultiMaster SCSI Host Adapters
+  Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters
 
   Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
 
@@ -17,15 +17,18 @@
   The author respectfully requests that any modifications to this software be
   sent directly to him for evaluation and testing.
 
-  Special thanks to Wayne Yen and Alex Win of BusLogic, whose advice has been
-  invaluable, to David Gentzel, for writing the original Linux BusLogic driver,
-  and to Paul Gortmaker, for being such a dedicated test site.
+  Special thanks to Wayne Yen, Jin-Lon Hon, and Alex Win of BusLogic, whose
+  advice has been invaluable, to David Gentzel, for writing the original Linux
+  BusLogic driver, and to Paul Gortmaker, for being such a dedicated test site.
+
+  Finally, special thanks to Mylex/BusLogic for making the FlashPoint SCCB
+  Manager available as freely redistributable source code.
 
 */
 
 
-#define BusLogic_DriverVersion		"2.0.6"
-#define BusLogic_DriverDate		"1 December 1996"
+#define BusLogic_DriverVersion		"2.0.7"
+#define BusLogic_DriverDate		"23 February 1997"
 
 
 #include <linux/module.h>
@@ -69,61 +72,49 @@
 
 
 /*
-  BusLogic_ProbeOptions is a bit mask of Probe Options to be applied
-  across all Host Adapters.
+  BusLogic_ProbeOptions is a set of Probe Options to be applied across
+  all BusLogic Host Adapters.
 */
 
-static int
-  BusLogic_ProbeOptions =		0;
+static BusLogic_ProbeOptions_T
+  BusLogic_ProbeOptions =		{ 0 };
 
 
 /*
-  BusLogic_GlobalOptions is a bit mask of Global Options to be applied
-  across all Host Adapters.
+  BusLogic_GlobalOptions is a set of Global Options to be applied across
+  all BusLogic Host Adapters.
 */
 
-static int
-  BusLogic_GlobalOptions =		0;
+static BusLogic_GlobalOptions_T
+  BusLogic_GlobalOptions =		{ 0 };
 
 
 /*
-  BusLogic_RegisteredHostAdapters is a linked list of all the registered
-  BusLogic Host Adapters.
+  BusLogic_RegisteredHostAdapters is an array of linked lists of all the
+  registered BusLogic Host Adapters, indexed by IRQ Channel.
 */
 
 static BusLogic_HostAdapter_T
-  *BusLogic_RegisteredHostAdapters =	NULL;
-
-
-/*
-  BusLogic_StandardAddresses is the list of standard ISA I/O Addresses at
-  which BusLogic Host Adapters may potentially be found.
-*/
-
-static unsigned int
-  BusLogic_IO_StandardAddresses[] =
-    { 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0 };
+  *BusLogic_RegisteredHostAdapters[NR_IRQS] =	{ NULL };
 
 
 /*
-  BusLogic_IO_AddressProbeList is the list of I/O Addresses to be probed for
-  potential BusLogic Host Adapters.  It is initialized by interrogating the
-  PCI Configuration Space on PCI machines as well as from the list of
-  standard BusLogic I/O Addresses.
+  BusLogic_ProbeInfoCount is the numbers of entries in BusLogic_ProbeInfoList.
 */
 
-static unsigned int
-  BusLogic_IO_AddressProbeList[BusLogic_IO_MaxProbeAddresses+1] =   { 0 };
+static int
+  BusLogic_ProbeInfoCount =		0;
 
 
 /*
-  BusLogic_IRQ_UsageCount stores a count of the number of Host Adapters using
-  a given IRQ Channel, which is necessary to support PCI, EISA, or MCA shared
-  interrupts.
+  BusLogic_ProbeInfoList is the list of I/O Addresses and Bus Probe Information
+  to be checked for potential BusLogic Host Adapters.  It is initialized by
+  interrogating the PCI Configuration Space on PCI machines as well as from the
+  list of standard BusLogic I/O Addresses.
 */
 
-static int
-  BusLogic_IRQ_UsageCount[NR_IRQS] =	{ 0 };
+static BusLogic_ProbeInfo_T
+  BusLogic_ProbeInfoList[BusLogic_MaxHostAdapters] =	{ { 0 } };
 
 
 /*
@@ -137,40 +128,49 @@
 
 
 /*
+  BusLogic_FirstCompletedCCB and BusLogic_LastCompletedCCB are pointers
+  to the first and last CCBs that are queued for completion processing.
+*/
+
+static BusLogic_CCB_T
+  *BusLogic_FirstCompletedCCB =		NULL,
+  *BusLogic_LastCompletedCCB =		NULL;
+
+
+/*
   BusLogic_ProcDirectoryEntry is the BusLogic /proc/scsi directory entry.
 */
 
-static struct proc_dir_entry
+PROC_DirectoryEntry_T
   BusLogic_ProcDirectoryEntry =
     { PROC_SCSI_BUSLOGIC, 8, "BusLogic", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
 
 
 /*
   BusLogic_AnnounceDriver announces the Driver Version and Date, Author's
-  Name, Copyright Notice, and Contact Address.
+  Name, Copyright Notice, and Electronic Mail Address.
 */
 
-static void BusLogic_AnnounceDriver(void)
+static void BusLogic_AnnounceDriver(BusLogic_HostAdapter_T *HostAdapter)
 {
-  static boolean DriverAnnouncementPrinted = false;
-  if (DriverAnnouncementPrinted) return;
-  printk("scsi: ***** BusLogic SCSI Driver Version "
-	 BusLogic_DriverVersion " of " BusLogic_DriverDate " *****\n");
-  printk("scsi: Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>\n");
-  DriverAnnouncementPrinted = true;
+  BusLogic_Announce("***** BusLogic SCSI Driver Version "
+		    BusLogic_DriverVersion " of "
+		    BusLogic_DriverDate " *****\n", HostAdapter);
+  BusLogic_Announce("Copyright 1995 by Leonard N. Zubkoff "
+		    "<lnz@dandelion.com>\n", HostAdapter);
 }
 
 
 /*
-  BusLogic_DriverInfo returns the Controller Name to identify this SCSI Driver
-  and Host Adapter.
+  BusLogic_DriverInfo returns the Host Adapter Name to identify this SCSI
+  Driver and Host Adapter.
 */
 
 const char *BusLogic_DriverInfo(SCSI_Host_T *Host)
 {
   BusLogic_HostAdapter_T *HostAdapter =
     (BusLogic_HostAdapter_T *) Host->hostdata;
-  return HostAdapter->ControllerName;
+  return HostAdapter->FullModelName;
 }
 
 
@@ -182,15 +182,16 @@
 static void BusLogic_RegisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
 {
   HostAdapter->Next = NULL;
-  if (BusLogic_RegisteredHostAdapters != NULL)
+  if (BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel] != NULL)
     {
-      BusLogic_HostAdapter_T *LastHostAdapter = BusLogic_RegisteredHostAdapters;
+      BusLogic_HostAdapter_T *LastHostAdapter =
+	BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel];
       BusLogic_HostAdapter_T *NextHostAdapter;
       while ((NextHostAdapter = LastHostAdapter->Next) != NULL)
 	LastHostAdapter = NextHostAdapter;
       LastHostAdapter->Next = HostAdapter;
     }
-  else BusLogic_RegisteredHostAdapters = HostAdapter;
+  else BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel] = HostAdapter;
 }
 
 
@@ -201,15 +202,17 @@
 
 static void BusLogic_UnregisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
 {
-  if (BusLogic_RegisteredHostAdapters != HostAdapter)
+  if (BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel] != HostAdapter)
     {
-      BusLogic_HostAdapter_T *LastHostAdapter = BusLogic_RegisteredHostAdapters;
+      BusLogic_HostAdapter_T *LastHostAdapter =
+	BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel];
       while (LastHostAdapter != NULL && LastHostAdapter->Next != HostAdapter)
 	LastHostAdapter = LastHostAdapter->Next;
       if (LastHostAdapter != NULL)
 	LastHostAdapter->Next = HostAdapter->Next;
     }
-  else BusLogic_RegisteredHostAdapters = HostAdapter->Next;
+  else BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel] =
+	 HostAdapter->Next;
   HostAdapter->Next = NULL;
 }
 
@@ -221,6 +224,13 @@
 
 static boolean BusLogic_CreateMailboxes(BusLogic_HostAdapter_T *HostAdapter)
 {
+  /*
+    FlashPoint Host Adapters do not use Outgoing and Incoming Mailboxes.
+  */
+  if (BusLogic_FlashPointHostAdapterP(HostAdapter)) return true;
+  /*
+    Allocate space for the Outgoing and Incoming Mailboxes.
+  */
   HostAdapter->FirstOutgoingMailbox =
     (BusLogic_OutgoingMailbox_T *)
       scsi_init_malloc(HostAdapter->MailboxCount
@@ -231,8 +241,8 @@
 			: GFP_ATOMIC));
   if (HostAdapter->FirstOutgoingMailbox == NULL)
     {
-      printk("scsi%d: UNABLE TO ALLOCATE MAILBOXES - DETACHING\n",
-	     HostAdapter->HostNumber);
+      BusLogic_Error("UNABLE TO ALLOCATE MAILBOXES - DETACHING\n",
+		     HostAdapter, HostAdapter->HostNumber);
       return false;
     }
   HostAdapter->LastOutgoingMailbox =
@@ -261,34 +271,48 @@
 
 
 /*
-  BusLogic_CreateCCBs allocates the initial Command Control Blocks (CCBs)
-  for Host Adapter.
+  BusLogic_CreateCCB allocates and initializes a single Command Control
+  Block (CCB) for Host Adapter, and adds it to Host Adapter's free list.
 */
 
-static boolean BusLogic_CreateCCBs(BusLogic_HostAdapter_T *HostAdapter)
+static boolean BusLogic_CreateCCB(BusLogic_HostAdapter_T *HostAdapter)
 {
-  int i;
-  for (i = 0; i < HostAdapter->InitialCCBs; i++)
+  BusLogic_CCB_T *CCB = (BusLogic_CCB_T *)
+    scsi_init_malloc(sizeof(BusLogic_CCB_T),
+		     (HostAdapter->BounceBuffersRequired
+		      ? GFP_ATOMIC | GFP_DMA
+		      : GFP_ATOMIC));
+  if (CCB == NULL) return false;
+  memset(CCB, 0, sizeof(BusLogic_CCB_T));
+  CCB->HostAdapter = HostAdapter;
+  CCB->Status = BusLogic_CCB_Free;
+  if (BusLogic_FlashPointHostAdapterP(HostAdapter))
     {
-      BusLogic_CCB_T *CCB = (BusLogic_CCB_T *)
-	scsi_init_malloc(sizeof(BusLogic_CCB_T),
-			 (HostAdapter->BounceBuffersRequired
-			  ? GFP_ATOMIC | GFP_DMA
-			  : GFP_ATOMIC));
-      if (CCB == NULL)
-	{
-	  printk("scsi%d: UNABLE TO ALLOCATE CCB %d - DETACHING\n",
-		 HostAdapter->HostNumber, i);
-	  return false;
-	}
-      memset(CCB, 0, sizeof(BusLogic_CCB_T));
-      CCB->HostAdapter = HostAdapter;
-      CCB->Status = BusLogic_CCB_Free;
-      CCB->Next = HostAdapter->Free_CCBs;
-      CCB->NextAll = HostAdapter->All_CCBs;
-      HostAdapter->Free_CCBs = CCB;
-      HostAdapter->All_CCBs = CCB;
+      CCB->CallbackFunction = BusLogic_QueueCompletedCCB;
+      CCB->BaseAddress = HostAdapter->IO_Address;
     }
+  CCB->Next = HostAdapter->Free_CCBs;
+  CCB->NextAll = HostAdapter->All_CCBs;
+  HostAdapter->Free_CCBs = CCB;
+  HostAdapter->All_CCBs = CCB;
+  return true;
+}
+
+
+/*
+  BusLogic_CreateCCBs allocates the initial CCBs for Host Adapter.
+*/
+
+static boolean BusLogic_CreateCCBs(BusLogic_HostAdapter_T *HostAdapter)
+{
+  int Allocated;
+  for (Allocated = 0; Allocated < HostAdapter->InitialCCBs; Allocated++)
+    if (!BusLogic_CreateCCB(HostAdapter))
+      {
+	BusLogic_Error("UNABLE TO ALLOCATE CCB %d - DETACHING\n",
+		       HostAdapter, Allocated);
+	return false;
+      }
   return true;
 }
 
@@ -311,12 +335,13 @@
 
 
 /*
-  BusLogic_AllocateCCB allocates a CCB from the Host Adapter's free list,
+  BusLogic_AllocateCCB allocates a CCB from Host Adapter's free list,
   allocating more memory from the Kernel if necessary.  The Host Adapter's
-  Lock should have already been acquired by the caller.
+  Lock should already have been acquired by the caller.
 */
 
-static BusLogic_CCB_T *BusLogic_AllocateCCB(BusLogic_HostAdapter_T *HostAdapter)
+static BusLogic_CCB_T *BusLogic_AllocateCCB(BusLogic_HostAdapter_T
+					    *HostAdapter)
 {
   static unsigned long SerialNumber = 0;
   BusLogic_CCB_T *CCB;
@@ -330,30 +355,14 @@
       return CCB;
     }
   for (Allocated = 0; Allocated < HostAdapter->IncrementalCCBs; Allocated++)
-    {
-      CCB = (BusLogic_CCB_T *)
-	scsi_init_malloc(sizeof(BusLogic_CCB_T),
-			 (HostAdapter->BounceBuffersRequired
-			  ? GFP_ATOMIC | GFP_DMA
-			  : GFP_ATOMIC));
-      if (CCB == NULL) break;
-      memset(CCB, 0, sizeof(BusLogic_CCB_T));
-      CCB->HostAdapter = HostAdapter;
-      CCB->Status = BusLogic_CCB_Free;
-      CCB->Next = HostAdapter->Free_CCBs;
-      CCB->NextAll = HostAdapter->All_CCBs;
-      HostAdapter->Free_CCBs = CCB;
-      HostAdapter->All_CCBs = CCB;
-    }
+    if (!BusLogic_CreateCCB(HostAdapter)) break;
   CCB = HostAdapter->Free_CCBs;
   if (CCB == NULL)
     {
-      printk("scsi%d: Failed to allocate additional CCBs\n",
-	     HostAdapter->HostNumber);
+      BusLogic_Notice("Failed to allocate additional CCBs\n", HostAdapter);
       return NULL;
     }
-  printk("scsi%d: Allocated %d additional CCBs\n",
-	 HostAdapter->HostNumber, Allocated);
+  BusLogic_Notice("Allocated %d additional CCBs\n", HostAdapter, Allocated);
   CCB->SerialNumber = ++SerialNumber;
   HostAdapter->Free_CCBs = CCB->Next;
   CCB->Next = NULL;
@@ -363,7 +372,7 @@
 
 /*
   BusLogic_DeallocateCCB deallocates a CCB, returning it to the Host Adapter's
-  free list.  The Host Adapter's Lock should have already been acquired by the
+  free list.  The Host Adapter's Lock should already have been acquired by the
   caller.
 */
 
@@ -378,6 +387,47 @@
 
 
 /*
+  BusLogic_CreateTargetDeviceStatistics creates the Target Device Statistics
+  structure for Host Adapter.
+*/
+
+static boolean BusLogic_CreateTargetDeviceStatistics(BusLogic_HostAdapter_T
+						     *HostAdapter)
+{
+  HostAdapter->TargetDeviceStatistics =
+    (BusLogic_TargetDeviceStatistics_T *)
+      scsi_init_malloc(HostAdapter->MaxTargetDevices
+		       * sizeof(BusLogic_TargetDeviceStatistics_T),
+		       GFP_ATOMIC);
+  if (HostAdapter->TargetDeviceStatistics == NULL)
+    {
+      BusLogic_Error("UNABLE TO ALLOCATE TARGET DEVICE STATISTICS - "
+		     "DETACHING\n", HostAdapter, HostAdapter->HostNumber);
+      return false;
+    }
+  memset(HostAdapter->TargetDeviceStatistics, 0,
+	 HostAdapter->MaxTargetDevices
+	 * sizeof(BusLogic_TargetDeviceStatistics_T));
+  return true;
+}
+
+
+/*
+  BusLogic_DestroyTargetDeviceStatistics destroys the Target Device Statistics
+  structure for Host Adapter.
+*/
+
+static void BusLogic_DestroyTargetDeviceStatistics(BusLogic_HostAdapter_T
+						   *HostAdapter)
+{
+  if (HostAdapter->TargetDeviceStatistics == NULL) return;
+  scsi_init_free((char *) HostAdapter->TargetDeviceStatistics,
+		 HostAdapter->MaxTargetDevices
+		 * sizeof(BusLogic_TargetDeviceStatistics_T));
+}
+
+
+/*
   BusLogic_Command sends the command OperationCode to HostAdapter, optionally
   providing ParameterLength bytes of ParameterData and receiving at most
   ReplyLength bytes of ReplyData; any excess reply data is received but
@@ -387,12 +437,12 @@
   the Host Adapter (including any discarded data); on failure, it returns
   -1 if the command was invalid, or -2 if a timeout occurred.
 
-  This function is only called during controller detection and initialization,
-  so performance and latency are not critical, and exclusive access to the Host
-  Adapter hardware is assumed.  Once the controller and driver are initialized,
-  the only Host Adapter command that is issued is the single byte Execute
-  Mailbox Command operation code, which does not require waiting for the Host
-  Adapter Ready bit to be set in the Status Register.
+  BusLogic_Command is called exclusively during host adapter detection and
+  initialization, so performance and latency are not critical, and exclusive
+  access to the Host Adapter hardware is assumed.  Once the host adapter and
+  driver are initialized, the only Host Adapter command that is issued is the
+  single byte Execute Mailbox Command operation code, which does not require
+  waiting for the Host Adapter Ready bit to be set in the Status Register.
 */
 
 static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
@@ -404,27 +454,45 @@
 {
   unsigned char *ParameterPointer = (unsigned char *) ParameterData;
   unsigned char *ReplyPointer = (unsigned char *) ReplyData;
-  unsigned char StatusRegister = 0, InterruptRegister;
-  int ReplyBytes = 0, TimeoutCounter;
+  BusLogic_StatusRegister_T StatusRegister;
+  BusLogic_InterruptRegister_T InterruptRegister;
+  unsigned long ProcessorFlags = 0;
+  int ReplyBytes = 0, Result;
+  long TimeoutCounter;
   /*
     Clear out the Reply Data if provided.
   */
   if (ReplyLength > 0)
     memset(ReplyData, 0, ReplyLength);
   /*
+    If the IRQ Channel has not yet been acquired, then interrupts must be
+    disabled while issuing host adapter commands since a Command Complete
+    interrupt could occur if the IRQ Channel was previously enabled by another
+    BusLogic Host Adapter or other driver sharing the same IRQ Channel.
+  */
+  if (!HostAdapter->IRQ_ChannelAcquired)
+    {
+      save_flags(ProcessorFlags);
+      cli();
+    }
+  /*
     Wait for the Host Adapter Ready bit to be set and the Command/Parameter
     Register Busy bit to be reset in the Status Register.
   */
   TimeoutCounter = loops_per_sec >> 3;
   while (--TimeoutCounter >= 0)
     {
-      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
-      if ((StatusRegister & BusLogic_HostAdapterReady) &&
-	  !(StatusRegister & BusLogic_CommandParameterRegisterBusy))
+      StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+      if (StatusRegister.Bits.HostAdapterReady &&
+	  !StatusRegister.Bits.CommandParameterRegisterBusy)
 	break;
     }
-  BusLogic_CommandFailureReason = "Timeout waiting for Host Adapter Ready";
-  if (TimeoutCounter < 0) return -2;
+  if (TimeoutCounter < 0)
+    {
+      BusLogic_CommandFailureReason = "Timeout waiting for Host Adapter Ready";
+      Result = -2;
+      goto Done;
+    }
   /*
     Write the OperationCode to the Command/Parameter Register.
   */
@@ -449,27 +517,40 @@
 	Register Busy bit in the Status Register to be reset.
       */
       udelay(100);
-      InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
-      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
-      if (InterruptRegister & BusLogic_CommandComplete) break;
+      InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+      StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+      if (InterruptRegister.Bits.CommandComplete) break;
       if (HostAdapter->HostAdapterCommandCompleted) break;
-      if (StatusRegister & BusLogic_DataInRegisterReady) break;
-      if (StatusRegister & BusLogic_CommandParameterRegisterBusy) continue;
+      if (StatusRegister.Bits.DataInRegisterReady) break;
+      if (StatusRegister.Bits.CommandParameterRegisterBusy) continue;
       BusLogic_WriteCommandParameterRegister(HostAdapter, *ParameterPointer++);
       ParameterLength--;
     }
-  BusLogic_CommandFailureReason = "Timeout waiting for Parameter Acceptance";
-  if (TimeoutCounter < 0) return -2;
+  if (TimeoutCounter < 0)
+    {
+      BusLogic_CommandFailureReason =
+	"Timeout waiting for Parameter Acceptance";
+      Result = -2;
+      goto Done;
+    }
   /*
     The Modify I/O Address command does not cause a Command Complete Interrupt.
   */
   if (OperationCode == BusLogic_ModifyIOAddress)
     {
-      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
-      BusLogic_CommandFailureReason = "Modify I/O Address Invalid";
-      if (StatusRegister & BusLogic_CommandInvalid) return -1;
-      BusLogic_CommandFailureReason = NULL;
-      return 0;
+      StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+      if (StatusRegister.Bits.CommandInvalid)
+	{
+	  BusLogic_CommandFailureReason = "Modify I/O Address Invalid";
+	  Result = -1;
+	  goto Done;
+	}
+      if (BusLogic_GlobalOptions.Bits.TraceConfiguration)
+	BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: "
+			"(Modify I/O Address)\n", HostAdapter,
+			OperationCode, StatusRegister.All);
+      Result = 0;
+      goto Done;
     }
   /*
     Select an appropriate timeout value for awaiting command completion.
@@ -494,19 +575,23 @@
   */
   while (--TimeoutCounter >= 0)
     {
-      InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
-      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
-      if (InterruptRegister & BusLogic_CommandComplete) break;
+      InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+      StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+      if (InterruptRegister.Bits.CommandComplete) break;
       if (HostAdapter->HostAdapterCommandCompleted) break;
-      if (StatusRegister & BusLogic_DataInRegisterReady)
+      if (StatusRegister.Bits.DataInRegisterReady)
 	if (++ReplyBytes <= ReplyLength)
 	  *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter);
 	else BusLogic_ReadDataInRegister(HostAdapter);
       if (OperationCode == BusLogic_FetchHostAdapterLocalRAM &&
-	  (StatusRegister & BusLogic_HostAdapterReady)) break;
+	  StatusRegister.Bits.HostAdapterReady) break;
+    }
+  if (TimeoutCounter < 0)
+    {
+      BusLogic_CommandFailureReason = "Timeout waiting for Command Complete";
+      Result = -2;
+      goto Done;
     }
-  BusLogic_CommandFailureReason = "Timeout waiting for Command Complete";
-  if (TimeoutCounter < 0) return -2;
   /*
     If testing Command Complete Interrupts, wait a short while in case the
     loop immediately above terminated due to the Command Complete bit being
@@ -519,22 +604,27 @@
   /*
     Clear any pending Command Complete Interrupt.
   */
-  BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset);
-  if (BusLogic_GlobalOptions & BusLogic_TraceConfiguration)
+  BusLogic_InterruptReset(HostAdapter);
+  /*
+    Provide tracing information if requested.
+  */
+  if (BusLogic_GlobalOptions.Bits.TraceConfiguration)
     if (OperationCode != BusLogic_TestCommandCompleteInterrupt)
       {
 	int i;
-	printk("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:",
-	       OperationCode, StatusRegister, ReplyLength, ReplyBytes);
+	BusLogic_Notice("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:",
+			HostAdapter, OperationCode,
+			StatusRegister.All, ReplyLength, ReplyBytes);
 	if (ReplyLength > ReplyBytes) ReplyLength = ReplyBytes;
 	for (i = 0; i < ReplyLength; i++)
-	  printk(" %02X", ((unsigned char *) ReplyData)[i]);
-	printk("\n");
+	  BusLogic_Notice(" %02X", HostAdapter,
+			  ((unsigned char *) ReplyData)[i]);
+	BusLogic_Notice("\n", HostAdapter);
       }
   /*
     Process Command Invalid conditions.
   */
-  if (StatusRegister & BusLogic_CommandInvalid)
+  if (StatusRegister.Bits.CommandInvalid)
     {
       /*
 	Some early BusLogic Host Adapters may not recover properly from
@@ -545,154 +635,545 @@
 	Soft Reset in response to a Command Invalid condition.
       */
       udelay(1000);
-      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
-      if (StatusRegister != (BusLogic_HostAdapterReady |
-			     BusLogic_InitializationRequired))
+      StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+      if (StatusRegister.Bits.CommandInvalid ||
+	  StatusRegister.Bits.Reserved ||
+	  StatusRegister.Bits.DataInRegisterReady ||
+	  StatusRegister.Bits.CommandParameterRegisterBusy ||
+	  !StatusRegister.Bits.HostAdapterReady ||
+	  !StatusRegister.Bits.InitializationRequired ||
+	  StatusRegister.Bits.DiagnosticActive ||
+	  StatusRegister.Bits.DiagnosticFailure)
 	{
-	  BusLogic_WriteControlRegister(HostAdapter, BusLogic_SoftReset);
+	  BusLogic_SoftReset(HostAdapter);
 	  udelay(1000);
 	}
       BusLogic_CommandFailureReason = "Command Invalid";
-      return -1;
+      Result = -1;
+      goto Done;
     }
   /*
     Handle Excess Parameters Supplied conditions.
   */
-  BusLogic_CommandFailureReason = "Excess Parameters Supplied";
-  if (ParameterLength > 0) return -1;
+  if (ParameterLength > 0)
+    {
+      BusLogic_CommandFailureReason = "Excess Parameters Supplied";
+      Result = -1;
+      goto Done;
+    }
   /*
     Indicate the command completed successfully.
   */
   BusLogic_CommandFailureReason = NULL;
-  return ReplyBytes;
+  Result = ReplyBytes;
+  /*
+    Restore the interrupt status if necessary and return.
+  */
+Done:
+  if (!HostAdapter->IRQ_ChannelAcquired)
+    restore_flags(ProcessorFlags);
+  return Result;
 }
 
 
 /*
-  BusLogic_InitializeAddressProbeList initializes the list of I/O Addresses
-  to be probed for potential BusLogic SCSI Host Adapters by interrogating the
-  PCI Configuration Space on PCI machines as well as from the list of standard
-  BusLogic I/O Addresses.
+  BusLogic_InitializeProbeInfoListISA initializes the list of I/O Address and
+  Bus Probe Information to be checked for potential BusLogic SCSI Host Adapters
+  only from the list of standard BusLogic MultiMaster ISA I/O Addresses.
 */
 
-static void BusLogic_InitializeAddressProbeList(void)
+static void BusLogic_InitializeProbeInfoListISA(void)
 {
-  int ProbeAddressCount = 0, StandardAddressIndex = 0;
+  int StandardAddressIndex;
   /*
     If BusLogic_Setup has provided an I/O Address probe list, do not override
     the Kernel Command Line specifications.
   */
-  if (BusLogic_IO_AddressProbeList[0] != 0) return;
+  if (BusLogic_ProbeInfoCount > 0) return;
+  /*
+    If a Kernel Command Line specification has requested that ISA Bus Probes
+    be inhibited, do not proceed further.
+  */
+  if (BusLogic_ProbeOptions.Bits.NoProbeISA) return;
+  /*
+    Append the list of standard BusLogic MultiMaster ISA I/O Addresses.
+  */
+  StandardAddressIndex = 0;
+  while (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters &&
+	 StandardAddressIndex < BusLogic_ISA_StandardAddressesCount)
+    {
+      BusLogic_ProbeInfo_T *ProbeInfo =
+	&BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+      ProbeInfo->IO_Address =
+	BusLogic_ISA_StandardAddresses[StandardAddressIndex++];
+      ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+      ProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
+    }
+}
+
+
 #ifdef CONFIG_PCI
+
+
+/*
+  BusLogic_SortProbeInfo sorts a section of BusLogic_ProbeInfoList in order
+  of increasing PCI Bus and Device Number.
+*/
+
+static void BusLogic_SortProbeInfo(BusLogic_ProbeInfo_T *ProbeInfoList,
+				   int ProbeInfoCount)
+{
+  int LastInterchange = ProbeInfoCount-1, Bound, j;
+  while (LastInterchange > 0)
+    {
+      Bound = LastInterchange;
+      LastInterchange = 0;
+      for (j = 0; j < Bound; j++)
+	{
+	  BusLogic_ProbeInfo_T *ProbeInfo1 = &ProbeInfoList[j];
+	  BusLogic_ProbeInfo_T *ProbeInfo2 = &ProbeInfoList[j+1];
+	  if (ProbeInfo1->Bus > ProbeInfo2->Bus ||
+	      (ProbeInfo1->Bus == ProbeInfo2->Bus &&
+	       (ProbeInfo1->Device > ProbeInfo2->Device)))
+	    {
+	      BusLogic_ProbeInfo_T TempProbeInfo;
+	      memcpy(&TempProbeInfo, ProbeInfo1, sizeof(BusLogic_ProbeInfo_T));
+	      memcpy(ProbeInfo1, ProbeInfo2, sizeof(BusLogic_ProbeInfo_T));
+	      memcpy(ProbeInfo2, &TempProbeInfo, sizeof(BusLogic_ProbeInfo_T));
+	      LastInterchange = j;
+	    }
+	}
+    }
+}
+
+
+/*
+  BusLogic_InitializeMultiMasterProbeInfo initializes the list of I/O Address
+  and Bus Probe Information to be checked for potential BusLogic MultiMaster
+  SCSI Host Adapters by interrogating the PCI Configuration Space on PCI
+  machines as well as from the list of standard BusLogic MultiMaster ISA
+  I/O Addresses.  It returns the number of PCI MultiMaster Host Adapters found.
+*/
+
+static int BusLogic_InitializeMultiMasterProbeInfo(void)
+{
+  boolean StandardAddressSeen[BusLogic_ISA_StandardAddressesCount];
+  BusLogic_ProbeInfo_T *PrimaryProbeInfo =
+    &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount];
+  int NonPrimaryPCIMultiMasterIndex = BusLogic_ProbeInfoCount;
+  int NonPrimaryPCIMultiMasterCount = 0, PCIMultiMasterCount = 0;
+  boolean ForceBusDeviceScanningOrder = false;
+  boolean ForceBusDeviceScanningOrderChecked = false;
+  unsigned char Bus, DeviceFunction, IRQ_Channel;
+  unsigned int BaseAddress0, BaseAddress1;
+  BusLogic_IO_Address_T IO_Address;
+  BusLogic_PCI_Address_T PCI_Address;
+  unsigned short Index = 0;
+  int StandardAddressIndex, i;
+  if (BusLogic_ProbeInfoCount >= BusLogic_MaxHostAdapters)
+    return 0;
+  BusLogic_ProbeInfoCount++;
+  for (i = 0; i < BusLogic_ISA_StandardAddressesCount; i++)
+    StandardAddressSeen[i] = false;
+  /*
+    Iterate over the MultiMaster PCI Host Adapters.  For each enumerated host
+    adapter, determine whether its ISA Compatible I/O Port is enabled and if
+    so, whether it is assigned the Primary I/O Address.  A host adapter that is
+    assigned the Primary I/O Address will always be the preferred boot device.
+    The MultiMaster BIOS will first recognize a host adapter at the Primary I/O
+    Address, then any other PCI host adapters, and finally any host adapters
+    located at the remaining standard ISA I/O Addresses.  When a PCI host
+    adapter is found with its ISA Compatible I/O Port enabled, a command is
+    issued to disable the ISA Compatible I/O Port, and it is noted that the
+    particular standard ISA I/O Address need not be probed.
+  */
+  PrimaryProbeInfo->IO_Address = 0;
+  while (pcibios_find_device(PCI_VENDOR_ID_BUSLOGIC,
+			     PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER,
+			     Index++, &Bus, &DeviceFunction) == 0)
+    if (pcibios_read_config_dword(Bus, DeviceFunction,
+				  PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
+	pcibios_read_config_dword(Bus, DeviceFunction,
+				  PCI_BASE_ADDRESS_1, &BaseAddress1) == 0 &&
+	pcibios_read_config_byte(Bus, DeviceFunction,
+				 PCI_INTERRUPT_LINE, &IRQ_Channel) == 0)
+      {
+	BusLogic_HostAdapter_T HostAdapterPrototype;
+	BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype;
+	BusLogic_PCIHostAdapterInformation_T PCIHostAdapterInformation;
+	BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest;
+	unsigned char Device = DeviceFunction >> 3;
+	IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+	PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
+	if ((BaseAddress0 & PCI_BASE_ADDRESS_SPACE)
+	    != PCI_BASE_ADDRESS_SPACE_IO)
+	  {
+	    BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for "
+			   "MultiMaster Host Adapter\n", NULL, BaseAddress0);
+	    BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
+			   NULL, Bus, Device, IO_Address);
+	    continue;
+	  }
+	if ((BaseAddress1 & PCI_BASE_ADDRESS_SPACE)
+	    != PCI_BASE_ADDRESS_SPACE_MEMORY)
+	  {
+	    BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for "
+			   "MultiMaster Host Adapter\n", NULL, BaseAddress1);
+	    BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n",
+			   NULL, Bus, Device, PCI_Address);
+	    continue;
+	  }
+	if (IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS)
+	  {
+	    BusLogic_Error("BusLogic: IRQ Channel %d illegal for "
+			   "MultiMaster Host Adapter\n", NULL, IRQ_Channel);
+	    BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
+			   NULL, Bus, Device, IO_Address);
+	    continue;
+	  }
+	if (BusLogic_GlobalOptions.Bits.TraceProbe)
+	  {
+	    BusLogic_Notice("BusLogic: PCI MultiMaster Host Adapter "
+			    "detected at\n", NULL);
+	    BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address "
+			    "0x%X PCI Address 0x%X\n", NULL,
+			    Bus, Device, IO_Address, PCI_Address);
+	  }
+	/*
+	  Issue the Inquire PCI Host Adapter Information command to determine
+	  the ISA Compatible I/O Port.  If the ISA Compatible I/O Port is
+	  known and enabled, note that the particular Standard ISA I/O
+	  Address need not be probed.
+	*/
+	HostAdapter->IO_Address = IO_Address;
+	if (BusLogic_Command(HostAdapter,
+			     BusLogic_InquirePCIHostAdapterInformation,
+			     NULL, 0, &PCIHostAdapterInformation,
+			     sizeof(PCIHostAdapterInformation))
+	    == sizeof(PCIHostAdapterInformation))
+	  {
+	    if (PCIHostAdapterInformation.ISACompatibleIOPort <
+		BusLogic_ISA_StandardAddressesCount)
+	      StandardAddressSeen[PCIHostAdapterInformation
+				  .ISACompatibleIOPort] = true;
+	  }
+	else PCIHostAdapterInformation.ISACompatibleIOPort =
+	       BusLogic_IO_Disable;
+	/*
+	  Issue the Modify I/O Address command to disable the ISA Compatible
+	  I/O Port.
+	*/
+	ModifyIOAddressRequest = BusLogic_IO_Disable;
+	BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress,
+			 &ModifyIOAddressRequest,
+			 sizeof(ModifyIOAddressRequest), NULL, 0);
+	/*
+	  For the first MultiMaster Host Adapter enumerated, issue the Fetch
+	  Host Adapter Local RAM command to read byte 45 of the AutoSCSI area,
+	  for the setting of the "Use Bus And Device # For PCI Scanning Seq."
+	  option.  Issue the Inquire Board ID command since this option is
+	  only valid for the BT-948/958/958D.
+	*/
+	if (!ForceBusDeviceScanningOrderChecked)
+	  {
+	    BusLogic_FetchHostAdapterLocalRAMRequest_T
+	      FetchHostAdapterLocalRAMRequest;
+	    BusLogic_AutoSCSIByte45_T AutoSCSIByte45;
+	    BusLogic_BoardID_T BoardID;
+	    FetchHostAdapterLocalRAMRequest.ByteOffset =
+	      BusLogic_AutoSCSI_BaseOffset + 45;
+	    FetchHostAdapterLocalRAMRequest.ByteCount =
+	      sizeof(AutoSCSIByte45);
+	    BusLogic_Command(HostAdapter,
+			     BusLogic_FetchHostAdapterLocalRAM,
+			     &FetchHostAdapterLocalRAMRequest,
+			     sizeof(FetchHostAdapterLocalRAMRequest),
+			     &AutoSCSIByte45, sizeof(AutoSCSIByte45));
+	    BusLogic_Command(HostAdapter, BusLogic_InquireBoardID,
+			     NULL, 0, &BoardID, sizeof(BoardID));
+	    if (BoardID.FirmwareVersion1stDigit == '5')
+	      ForceBusDeviceScanningOrder =
+		AutoSCSIByte45.ForceBusDeviceScanningOrder;
+	    ForceBusDeviceScanningOrderChecked = true;
+	  }
+	/*
+	  Determine whether this MultiMaster Host Adapter has its ISA
+	  Compatible I/O Port enabled and is assigned the Primary I/O Address.
+	  If it does, then it is the Primary MultiMaster Host Adapter and must
+	  be recognized first.  If it does not, then it is added to the list
+	  for probing after any Primary MultiMaster Host Adapter is probed.
+	*/
+	if (PCIHostAdapterInformation.ISACompatibleIOPort == BusLogic_IO_330)
+	  {
+	    PrimaryProbeInfo->IO_Address = IO_Address;
+	    PrimaryProbeInfo->PCI_Address = PCI_Address;
+	    PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+	    PrimaryProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+	    PrimaryProbeInfo->Bus = Bus;
+	    PrimaryProbeInfo->Device = Device;
+	    PrimaryProbeInfo->IRQ_Channel = IRQ_Channel;
+	    PCIMultiMasterCount++;
+	  }
+	else if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters)
+	  {
+	    BusLogic_ProbeInfo_T *ProbeInfo =
+	      &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+	    ProbeInfo->IO_Address = IO_Address;
+	    ProbeInfo->PCI_Address = PCI_Address;
+	    ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+	    ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+	    ProbeInfo->Bus = Bus;
+	    ProbeInfo->Device = Device;
+	    ProbeInfo->IRQ_Channel = IRQ_Channel;
+	    NonPrimaryPCIMultiMasterCount++;
+	    PCIMultiMasterCount++;
+	  }
+	else BusLogic_Warning("BusLogic: Too many Host Adapters "
+			      "detected\n", NULL);
+      }
   /*
-    Interrogate PCI Configuration Space for any BusLogic SCSI Host Adapters.
+    If the AutoSCSI "Use Bus And Device # For PCI Scanning Seq." option is ON
+    for the first enumerated MultiMaster Host Adapter, and if that host adapter
+    is a BT-948/958/958D, then the MultiMaster BIOS will recognize MultiMaster
+    Host Adapters in the order of increasing PCI Bus and Device Number.  In
+    that case, sort the probe information into the same order the BIOS uses.
+    If this option is OFF, then the MultiMaster BIOS will recognize MultiMaster
+    Host Adapters in the order they are enumerated by the PCI BIOS, and hence
+    no sorting is necessary.
+  */
+  if (ForceBusDeviceScanningOrder)
+    BusLogic_SortProbeInfo(&BusLogic_ProbeInfoList[
+			      NonPrimaryPCIMultiMasterIndex],
+			   NonPrimaryPCIMultiMasterCount);
+  /*
+    If no PCI MultiMaster Host Adapter is assigned the Primary I/O Address,
+    then the Primary I/O Address must be probed explicitly before any PCI
+    host adapters are probed.
+  */
+  if (PrimaryProbeInfo->IO_Address == 0 &&
+      !BusLogic_ProbeOptions.Bits.NoProbeISA)
+    {
+      PrimaryProbeInfo->IO_Address = BusLogic_ISA_StandardAddresses[0];
+      PrimaryProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+      PrimaryProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
+    }
+  /*
+    Append the list of standard BusLogic MultiMaster ISA I/O Addresses,
+    omitting the Primary I/O Address which has already been handled.
+  */
+  if (!BusLogic_ProbeOptions.Bits.NoProbeISA)
+    for (StandardAddressIndex = 1;
+	 StandardAddressIndex < BusLogic_ISA_StandardAddressesCount;
+	 StandardAddressIndex++)
+      if (!StandardAddressSeen[StandardAddressIndex] &&
+	  BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters)
+	{
+	  BusLogic_ProbeInfo_T *ProbeInfo =
+	    &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+	  ProbeInfo->IO_Address =
+	    BusLogic_ISA_StandardAddresses[StandardAddressIndex];
+	  ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+	  ProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
+	}
+  return PCIMultiMasterCount;
+}
+
+
+/*
+  BusLogic_InitializeFlashPointProbeInfo initializes the list of I/O Address
+  and Bus Probe Information to be checked for potential BusLogic FlashPoint
+  Host Adapters by interrogating the PCI Configuration Space.  It returns the
+  number of FlashPoint Host Adapters found.
+*/
+
+static int BusLogic_InitializeFlashPointProbeInfo(void)
+{
+  int FlashPointIndex = BusLogic_ProbeInfoCount, FlashPointCount = 0;
+  unsigned char Bus, DeviceFunction, IRQ_Channel;
+  unsigned int BaseAddress0, BaseAddress1;
+  BusLogic_IO_Address_T IO_Address;
+  BusLogic_PCI_Address_T PCI_Address;
+  unsigned short Index = 0;
+  /*
+    Interrogate PCI Configuration Space for any FlashPoint Host Adapters.
+  */
+  while (pcibios_find_device(PCI_VENDOR_ID_BUSLOGIC,
+			     PCI_DEVICE_ID_BUSLOGIC_FLASHPOINT,
+			     Index++, &Bus, &DeviceFunction) == 0)
+    if (pcibios_read_config_dword(Bus, DeviceFunction,
+				  PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
+	pcibios_read_config_dword(Bus, DeviceFunction,
+				  PCI_BASE_ADDRESS_1, &BaseAddress1) == 0 &&
+	pcibios_read_config_byte(Bus, DeviceFunction,
+				 PCI_INTERRUPT_LINE, &IRQ_Channel) == 0)
+      {
+	unsigned char Device = DeviceFunction >> 3;
+	IO_Address = BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+	PCI_Address = BaseAddress1 & PCI_BASE_ADDRESS_MEM_MASK;
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+	if ((BaseAddress0 & PCI_BASE_ADDRESS_SPACE)
+	    != PCI_BASE_ADDRESS_SPACE_IO)
+	  {
+	    BusLogic_Error("BusLogic: Base Address0 0x%X not I/O for "
+			   "FlashPoint Host Adapter\n", NULL, BaseAddress0);
+	    BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
+			   NULL, Bus, Device, IO_Address);
+	    continue;
+	  }
+	if ((BaseAddress1 & PCI_BASE_ADDRESS_SPACE)
+	    != PCI_BASE_ADDRESS_SPACE_MEMORY)
+	  {
+	    BusLogic_Error("BusLogic: Base Address1 0x%X not Memory for "
+			   "FlashPoint Host Adapter\n", NULL, BaseAddress1);
+	    BusLogic_Error("at PCI Bus %d Device %d PCI Address 0x%X\n",
+			   NULL, Bus, Device, PCI_Address);
+	    continue;
+	  }
+	if (IRQ_Channel == 0 || IRQ_Channel >= NR_IRQS)
+	  {
+	    BusLogic_Error("BusLogic: IRQ Channel %d illegal for "
+			   "FlashPoint Host Adapter\n", NULL, IRQ_Channel);
+	    BusLogic_Error("at PCI Bus %d Device %d I/O Address 0x%X\n",
+			   NULL, Bus, Device, IO_Address);
+	    continue;
+	  }
+	if (BusLogic_GlobalOptions.Bits.TraceProbe)
+	  {
+	    BusLogic_Notice("BusLogic: FlashPoint Host Adapter "
+			    "detected at\n", NULL);
+	    BusLogic_Notice("BusLogic: PCI Bus %d Device %d I/O Address "
+			    "0x%X PCI Address 0x%X\n", NULL,
+			    Bus, Device, IO_Address, PCI_Address);
+	  }
+	if (BusLogic_ProbeInfoCount < BusLogic_MaxHostAdapters)
+	  {
+	    BusLogic_ProbeInfo_T *ProbeInfo =
+	      &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+	    ProbeInfo->IO_Address = IO_Address;
+	    ProbeInfo->PCI_Address = PCI_Address;
+	    ProbeInfo->HostAdapterType = BusLogic_FlashPoint;
+	    ProbeInfo->HostAdapterBusType = BusLogic_PCI_Bus;
+	    ProbeInfo->Bus = Bus;
+	    ProbeInfo->Device = Device;
+	    ProbeInfo->IRQ_Channel = IRQ_Channel;
+	    FlashPointCount++;
+	  }
+	else BusLogic_Warning("BusLogic: Too many Host Adapters "
+			      "detected\n", NULL);
+#else
+	BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at "
+		       "PCI Bus %d Device %d\n", NULL, Bus, Device);
+	BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, "
+		       "but FlashPoint\n", NULL, IO_Address, PCI_Address);
+	BusLogic_Error("BusLogic: support was omitted in this kernel "
+		       "configuration.\n", NULL);
+#endif
+      }
+  /*
+    The FlashPoint BIOS will scan for FlashPoint Host Adapters in the order of
+    increasing PCI Bus and Device Number, so sort the probe information into
+    the same order the BIOS uses.
+  */
+  BusLogic_SortProbeInfo(&BusLogic_ProbeInfoList[FlashPointIndex],
+			 FlashPointCount);
+  return FlashPointCount;
+}
+
+
+/*
+  BusLogic_InitializeProbeInfoList initializes the list of I/O Address and Bus
+  Probe Information to be checked for potential BusLogic SCSI Host Adapters by
+  interrogating the PCI Configuration Space on PCI machines as well as from the
+  list of standard BusLogic MultiMaster ISA I/O Addresses.  By default, if both
+  FlashPoint and PCI MultiMaster Host Adapters are present, this driver will
+  probe for PCI MultiMaster Host Adapters first unless the BIOS primary disk is
+  not controlled by the first PCI MultiMaster Host Adapter, in which case
+  FlashPoint Host Adapters will be probed first.  The Kernel Command Line 
+  options "MultiMasterFirst" and "FlashPointFirst" can be used to force a
+  particular probe order.
+*/
+
+static void BusLogic_InitializeProbeInfoList(void)
+{
+  /*
+    If BusLogic_Setup has provided an I/O Address probe list, do not override
+    the Kernel Command Line specifications.
   */
-  if (pcibios_present())
+  if (BusLogic_ProbeInfoCount > 0) return;
+  /*
+    If a PCI BIOS is present, interrogate it for MultiMaster and FlashPoint
+    Host Adapters; otherwise, default to the standard ISA MultiMaster probe.
+  */
+  if (!BusLogic_ProbeOptions.Bits.NoProbePCI && pcibios_present())
     {
-      unsigned int BusDeviceFunction[BusLogic_IO_MaxProbeAddresses];
-      unsigned short Index = 0, VendorID, DeviceID;
-      boolean NonIncreasingScanningOrder = false;
-      unsigned char Bus, DeviceFunction;
-      unsigned int BaseAddress0;
-      while (pcibios_find_class(PCI_CLASS_STORAGE_SCSI<<8, Index++,
-				&Bus, &DeviceFunction) == 0)
-	if (pcibios_read_config_word(Bus, DeviceFunction,
-				     PCI_VENDOR_ID, &VendorID) == 0 &&
-	    VendorID == PCI_VENDOR_ID_BUSLOGIC &&
-	    pcibios_read_config_word(Bus, DeviceFunction,
-				     PCI_DEVICE_ID, &DeviceID) == 0 &&
-	    (DeviceID == PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER ||
-	     DeviceID == PCI_DEVICE_ID_BUSLOGIC_MULTIMASTER_NC) &&
-	    pcibios_read_config_dword(Bus, DeviceFunction,
-				      PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
-	    (BaseAddress0 & PCI_BASE_ADDRESS_SPACE) ==
-	      PCI_BASE_ADDRESS_SPACE_IO)
-	  {
-	    BusLogic_IO_AddressProbeList[ProbeAddressCount] =
-	      BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
-	    BusDeviceFunction[ProbeAddressCount] = (Bus << 8) | DeviceFunction;
-	    if (ProbeAddressCount > 0 &&
-		BusDeviceFunction[ProbeAddressCount] <
-		  BusDeviceFunction[ProbeAddressCount-1])
-	      NonIncreasingScanningOrder = true;
-	    ProbeAddressCount++;
-	  }
-      /*
-	If there are multiple BusLogic PCI SCSI Host Adapters present and if
-	they are enumerated by the PCI BIOS in an order other than by strictly
-	increasing Bus Number and Device Number, then interrogate the setting
-	of the AutoSCSI "Use Bus And Device # For PCI Scanning Seq." option.
-	If it is ON, and if the first enumeratedBusLogic Host Adapter is a
-	BT-948/958/958D, then sort the PCI Host Adapter I/O Addresses by
-	increasing Bus Number and Device Number so that the Host Adapters are
-	recognized in the same order by the Linux kernel as by the Host
-	Adapter's BIOS.
-      */
-      if (ProbeAddressCount > 1 && NonIncreasingScanningOrder &&
-	  !(BusLogic_ProbeOptions & BusLogic_NoSortPCI))
-	{
-	  BusLogic_HostAdapter_T HostAdapterPrototype;
-	  BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype;
-	  BusLogic_FetchHostAdapterLocalRAMRequest_T
-	    FetchHostAdapterLocalRAMRequest;
-	  BusLogic_AutoSCSIByte45_T AutoSCSIByte45;
-	  BusLogic_BoardID_T BoardID;
-	  HostAdapter->IO_Address = BusLogic_IO_AddressProbeList[0];
-	  FetchHostAdapterLocalRAMRequest.ByteOffset =
-	    BusLogic_AutoSCSI_BaseOffset + 45;
-	  FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(AutoSCSIByte45);
-	  AutoSCSIByte45.ForceBusDeviceScanningOrder = false;
-	  BusLogic_Command(HostAdapter,
-			   BusLogic_FetchHostAdapterLocalRAM,
-			   &FetchHostAdapterLocalRAMRequest,
-			   sizeof(FetchHostAdapterLocalRAMRequest),
-			   &AutoSCSIByte45, sizeof(AutoSCSIByte45));
-	  BoardID.FirmwareVersion1stDigit = '\0';
-	  BusLogic_Command(HostAdapter, BusLogic_InquireBoardID,
-			   NULL, 0, &BoardID, sizeof(BoardID));
-	  if (BoardID.FirmwareVersion1stDigit == '5' &&
-	      AutoSCSIByte45.ForceBusDeviceScanningOrder)
+      if (BusLogic_ProbeOptions.Bits.ProbeMultiMasterFirst)
+	{
+	  BusLogic_InitializeMultiMasterProbeInfo();
+	  BusLogic_InitializeFlashPointProbeInfo();
+	}
+      else if (BusLogic_ProbeOptions.Bits.ProbeFlashPointFirst)
+	{
+	  BusLogic_InitializeFlashPointProbeInfo();
+	  BusLogic_InitializeMultiMasterProbeInfo();
+	}
+      else
+	{
+	  int PCIMultiMasterCount = BusLogic_InitializeMultiMasterProbeInfo();
+	  int FlashPointCount = BusLogic_InitializeFlashPointProbeInfo();
+	  if (PCIMultiMasterCount > 0 && FlashPointCount > 0)
 	    {
+	      BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[0];
+	      BusLogic_HostAdapter_T HostAdapterPrototype;
+	      BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype;
+	      BusLogic_FetchHostAdapterLocalRAMRequest_T
+		FetchHostAdapterLocalRAMRequest;
+	      BusLogic_BIOSDriveMapByte_T Drive0MapByte;
+	      while (ProbeInfo->HostAdapterBusType != BusLogic_PCI_Bus)
+		ProbeInfo++;
+	      HostAdapter->IO_Address = ProbeInfo->IO_Address;
+	      FetchHostAdapterLocalRAMRequest.ByteOffset =
+		BusLogic_BIOS_BaseOffset + BusLogic_BIOS_DriveMapOffset + 0;
+	      FetchHostAdapterLocalRAMRequest.ByteCount =
+		sizeof(Drive0MapByte);
+	      BusLogic_Command(HostAdapter,
+			       BusLogic_FetchHostAdapterLocalRAM,
+			       &FetchHostAdapterLocalRAMRequest,
+			       sizeof(FetchHostAdapterLocalRAMRequest),
+			       &Drive0MapByte, sizeof(Drive0MapByte));
 	      /*
-		Sort the I/O Addresses such that the corresponding
-		PCI devices are in ascending order by Bus Number and
-		Device Number.
+		If the Map Byte for BIOS Drive 0 indicates that BIOS Drive 0
+		is not controlled by this PCI MultiMaster Host Adapter, then
+		reverse the probe order so that FlashPoint Host Adapters are
+		probed before MultiMaster Host Adapters.
 	      */
-	      int LastInterchange = ProbeAddressCount-1, Bound, j;
-	      while (LastInterchange > 0)
+	      if (Drive0MapByte.DiskGeometry ==
+		  BusLogic_BIOS_Disk_Not_Installed)
 		{
-		  Bound = LastInterchange;
-		  LastInterchange = 0;
-		  for (j = 0; j < Bound; j++)
-		    if (BusDeviceFunction[j] > BusDeviceFunction[j+1])
-		      {
-			unsigned int Temp;
-			Temp = BusDeviceFunction[j];
-			BusDeviceFunction[j] = BusDeviceFunction[j+1];
-			BusDeviceFunction[j+1] = Temp;
-			Temp = BusLogic_IO_AddressProbeList[j];
-			BusLogic_IO_AddressProbeList[j] =
-			  BusLogic_IO_AddressProbeList[j+1];
-			BusLogic_IO_AddressProbeList[j+1] = Temp;
-			LastInterchange = j;
-		      }
+		  BusLogic_ProbeInfo_T
+		    SavedProbeInfo[BusLogic_MaxHostAdapters];
+		  int MultiMasterCount =
+		    BusLogic_ProbeInfoCount - FlashPointCount;
+		  memcpy(&SavedProbeInfo[0],
+			 &BusLogic_ProbeInfoList[0],
+			 BusLogic_ProbeInfoCount
+			 * sizeof(BusLogic_ProbeInfo_T));
+		  memcpy(&BusLogic_ProbeInfoList[0],
+			 &SavedProbeInfo[MultiMasterCount],
+			 FlashPointCount * sizeof(BusLogic_ProbeInfo_T));
+		  memcpy(&BusLogic_ProbeInfoList[FlashPointCount],
+			 &SavedProbeInfo[0],
+			 MultiMasterCount * sizeof(BusLogic_ProbeInfo_T));
 		}
 	    }
 	}
     }
-#endif
-  /*
-    Append the list of standard BusLogic ISA I/O Addresses.
-  */
-  if (!(BusLogic_ProbeOptions & BusLogic_NoProbeISA))
-    while (ProbeAddressCount < BusLogic_IO_MaxProbeAddresses &&
-	   BusLogic_IO_StandardAddresses[StandardAddressIndex] > 0)
-      BusLogic_IO_AddressProbeList[ProbeAddressCount++] =
-	BusLogic_IO_StandardAddresses[StandardAddressIndex++];
-  BusLogic_IO_AddressProbeList[ProbeAddressCount] = 0;
+  else BusLogic_InitializeProbeInfoListISA();
 }
 
 
+#endif  /* CONFIG_PCI */
+
+
 /*
   BusLogic_Failure prints a standardized error message, and then returns false.
 */
@@ -700,12 +1181,19 @@
 static boolean BusLogic_Failure(BusLogic_HostAdapter_T *HostAdapter,
 				char *ErrorMessage)
 {
-  BusLogic_AnnounceDriver();
-  printk("While configuring BusLogic Host Adapter at I/O Address 0x%X:\n",
-	 HostAdapter->IO_Address);
-  printk("%s FAILED - DETACHING\n", ErrorMessage);
+  BusLogic_AnnounceDriver(HostAdapter);
+  if (HostAdapter->HostAdapterBusType == BusLogic_PCI_Bus)
+    BusLogic_Error("While configuring BusLogic PCI Host Adapter at\n"
+		   "Bus %d Device %d I/O Address 0x%X PCI Address 0x%X:\n",
+		   HostAdapter, HostAdapter->Bus, HostAdapter->Device,
+		   HostAdapter->IO_Address, HostAdapter->PCI_Address);
+  else BusLogic_Error("While configuring BusLogic Host Adapter at "
+		      "I/O Address 0x%X:\n", HostAdapter,
+		      HostAdapter->IO_Address);
+  BusLogic_Error("%s FAILED - DETACHING\n", HostAdapter, ErrorMessage);
   if (BusLogic_CommandFailureReason != NULL)
-    printk("ADDITIONAL FAILURE INFO - %s\n", BusLogic_CommandFailureReason);
+    BusLogic_Error("ADDITIONAL FAILURE INFO - %s\n", HostAdapter,
+		   BusLogic_CommandFailureReason);
   return false;
 }
 
@@ -716,37 +1204,76 @@
 
 static boolean BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
 {
-  boolean TraceProbe = (BusLogic_GlobalOptions & BusLogic_TraceProbe);
-  unsigned char StatusRegister, GeometryRegister;
+  BusLogic_StatusRegister_T StatusRegister;
+  BusLogic_InterruptRegister_T InterruptRegister;
+  BusLogic_GeometryRegister_T GeometryRegister;
+  /*
+    FlashPoint Host Adapters are Probed by the FlashPoint SCCB Manager.
+  */
+  if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+    {
+      FlashPoint_Info_T *FlashPointInfo = (FlashPoint_Info_T *)
+	scsi_init_malloc(sizeof(FlashPoint_Info_T), GFP_ATOMIC);
+      if (FlashPointInfo == NULL)
+	return BusLogic_Failure(HostAdapter, "ALLOCATING FLASHPOINT INFO");
+      FlashPointInfo->BaseAddress = HostAdapter->IO_Address;
+      FlashPointInfo->IRQ_Channel = HostAdapter->IRQ_Channel;
+      FlashPointInfo->Present = false;
+      if (!(FlashPoint_ProbeHostAdapter(FlashPointInfo) == 0 &&
+	    FlashPointInfo->Present))
+	{
+	  scsi_init_free((char *) FlashPointInfo, sizeof(FlashPoint_Info_T));
+	  if (BusLogic_GlobalOptions.Bits.TraceProbe)
+	    BusLogic_Notice("BusLogic_Probe(0x%X): FlashPoint Not Found\n",
+			    HostAdapter, HostAdapter->IO_Address);
+	  return false;
+	}
+      HostAdapter->FlashPointInfo = FlashPointInfo;
+      if (BusLogic_GlobalOptions.Bits.TraceProbe)
+	BusLogic_Notice("BusLogic_Probe(0x%X): FlashPoint Found\n",
+			HostAdapter, HostAdapter->IO_Address);
+      /*
+	Indicate the Host Adapter Probe completed successfully.
+      */
+      return true;
+    }
   /*
-    Read the Status Register to test if there is an I/O port that responds.  A
-    nonexistent I/O port will return 0xFF, in which case there is definitely no
-    BusLogic Host Adapter at this base I/O Address.
-  */
-  StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
-  if (TraceProbe)
-    printk("BusLogic_Probe(0x%X): Status 0x%02X\n",
-	   HostAdapter->IO_Address, StatusRegister);
-  if (StatusRegister == 0xFF) return false;
+    Read the Status, Interrupt, and Geometry Registers to test if there are I/O
+    ports that respond, and to check the values to determine if they are from a
+    BusLogic Host Adapter.  A nonexistent I/O port will return 0xFF, in which
+    case there is definitely no BusLogic Host Adapter at this base I/O Address.
+    The test here is a subset of that used by the BusLogic Host Adapter BIOS.
+  */
+  StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+  InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+  GeometryRegister.All = BusLogic_ReadGeometryRegister(HostAdapter);
+  if (BusLogic_GlobalOptions.Bits.TraceProbe)
+    BusLogic_Notice("BusLogic_Probe(0x%X): Status 0x%02X, Interrupt 0x%02X, "
+		    "Geometry 0x%02X\n", HostAdapter,
+		    HostAdapter->IO_Address, StatusRegister.All,
+		    InterruptRegister.All, GeometryRegister.All);
+  if (StatusRegister.All == 0 ||
+      StatusRegister.Bits.DiagnosticActive ||
+      StatusRegister.Bits.CommandParameterRegisterBusy ||
+      StatusRegister.Bits.Reserved ||
+      StatusRegister.Bits.CommandInvalid ||
+      InterruptRegister.Bits.Reserved != 0)
+    return false;
   /*
-    Read the undocumented BusLogic Geometry Register to test if there is an I/O
-    port that responds.  Adaptec Host Adapters do not implement the Geometry
+    Check the undocumented Geometry Register to test if there is an I/O port
+    that responded.  Adaptec Host Adapters do not implement the Geometry
     Register, so this test helps serve to avoid incorrectly recognizing an
     Adaptec 1542A or 1542B as a BusLogic.  Unfortunately, the Adaptec 1542C
     series does respond to the Geometry Register I/O port, but it will be
     rejected later when the Inquire Extended Setup Information command is
     issued in BusLogic_CheckHostAdapter.  The AMI FastDisk Host Adapter is a
     BusLogic clone that implements the same interface as earlier BusLogic
-    controllers, including the undocumented commands, and is therefore
+    Host Adapters, including the undocumented commands, and is therefore
     supported by this driver.  However, the AMI FastDisk always returns 0x00
     upon reading the Geometry Register, so the extended translation option
     should always be left disabled on the AMI FastDisk.
   */
-  GeometryRegister = BusLogic_ReadGeometryRegister(HostAdapter);
-  if (TraceProbe)
-    printk("BusLogic_Probe(0x%X): Geometry 0x%02X\n",
-	   HostAdapter->IO_Address, GeometryRegister);
-  if (GeometryRegister == 0xFF) return false;
+  if (GeometryRegister.All == 0xFF) return false;
   /*
     Indicate the Host Adapter Probe completed successfully.
   */
@@ -762,25 +1289,39 @@
 static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
 					     *HostAdapter)
 {
-  boolean TraceHardReset = (BusLogic_GlobalOptions & BusLogic_TraceHardReset);
+  BusLogic_StatusRegister_T StatusRegister;
   int TimeoutCounter = loops_per_sec;
-  unsigned char StatusRegister = 0;
+  /*
+    FlashPoint Host Adapters are Hard Reset by the FlashPoint SCCB Manager.
+  */
+  if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+    {
+      HostAdapter->FlashPointInfo->ReportDataUnderrun = true;
+      HostAdapter->CardHandle =
+	FlashPoint_HardResetHostAdapter(HostAdapter->FlashPointInfo);
+      if (HostAdapter->CardHandle == FlashPoint_BadCardHandle) return false;
+      /*
+	Indicate the Host Adapter Hard Reset completed successfully.
+      */
+      return true;
+    }
   /*
     Issue a Hard Reset Command to the Host Adapter.  The Host Adapter should
     respond by setting Diagnostic Active in the Status Register.
   */
-  BusLogic_WriteControlRegister(HostAdapter, BusLogic_HardReset);
+  BusLogic_HardReset(HostAdapter);
   /*
     Wait until Diagnostic Active is set in the Status Register.
   */
   while (--TimeoutCounter >= 0)
     {
-      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
-      if ((StatusRegister & BusLogic_DiagnosticActive)) break;
+      StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+      if (StatusRegister.Bits.DiagnosticActive) break;
     }
-  if (TraceHardReset)
-    printk("BusLogic_HardReset(0x%X): Diagnostic Active, Status 0x%02X\n",
-	   HostAdapter->IO_Address, StatusRegister);
+  if (BusLogic_GlobalOptions.Bits.TraceHardReset)
+    BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Active, "
+		    "Status 0x%02X\n", HostAdapter,
+		    HostAdapter->IO_Address, StatusRegister.All);
   if (TimeoutCounter < 0) return false;
   /*
     Wait 100 microseconds to allow completion of any initial diagnostic
@@ -793,12 +1334,13 @@
   */
   while (--TimeoutCounter >= 0)
     {
-      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
-      if (!(StatusRegister & BusLogic_DiagnosticActive)) break;
+      StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+      if (!StatusRegister.Bits.DiagnosticActive) break;
     }
-  if (TraceHardReset)
-    printk("BusLogic_HardReset(0x%X): Diagnostic Completed, Status 0x%02X\n",
-	   HostAdapter->IO_Address, StatusRegister);
+  if (BusLogic_GlobalOptions.Bits.TraceHardReset)
+    BusLogic_Notice("BusLogic_HardReset(0x%X): Diagnostic Completed, "
+		    "Status 0x%02X\n", HostAdapter,
+		    HostAdapter->IO_Address, StatusRegister.All);
   if (TimeoutCounter < 0) return false;
   /*
     Wait until at least one of the Diagnostic Failure, Host Adapter Ready,
@@ -806,31 +1348,34 @@
   */
   while (--TimeoutCounter >= 0)
     {
-      StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
-      if (StatusRegister & (BusLogic_DiagnosticFailure |
-			    BusLogic_HostAdapterReady |
-			    BusLogic_DataInRegisterReady))
+      StatusRegister.All = BusLogic_ReadStatusRegister(HostAdapter);
+      if (StatusRegister.Bits.DiagnosticFailure ||
+	  StatusRegister.Bits.HostAdapterReady ||
+	  StatusRegister.Bits.DataInRegisterReady)
 	break;
     }
-  if (TraceHardReset)
-    printk("BusLogic_HardReset(0x%X): Host Adapter Ready, Status 0x%02X\n",
-	   HostAdapter->IO_Address, StatusRegister);
+  if (BusLogic_GlobalOptions.Bits.TraceHardReset)
+    BusLogic_Notice("BusLogic_HardReset(0x%X): Host Adapter Ready, "
+		    "Status 0x%02X\n", HostAdapter,
+		    HostAdapter->IO_Address, StatusRegister.All);
   if (TimeoutCounter < 0) return false;
   /*
     If Diagnostic Failure is set or Host Adapter Ready is reset, then an
     error occurred during the Host Adapter diagnostics.  If Data In Register
     Ready is set, then there is an Error Code available.
   */
-  if ((StatusRegister & BusLogic_DiagnosticFailure) ||
-      !(StatusRegister & BusLogic_HostAdapterReady))
+  if (StatusRegister.Bits.DiagnosticFailure ||
+      !StatusRegister.Bits.HostAdapterReady)
     {
       BusLogic_CommandFailureReason = NULL;
       BusLogic_Failure(HostAdapter, "HARD RESET DIAGNOSTICS");
-      printk("HOST ADAPTER STATUS REGISTER = %02X\n", StatusRegister);
-      if (StatusRegister & BusLogic_DataInRegisterReady)
+      BusLogic_Error("HOST ADAPTER STATUS REGISTER = %02X\n",
+		     HostAdapter, StatusRegister.All);
+      if (StatusRegister.Bits.DataInRegisterReady)
 	{
 	  unsigned char ErrorCode = BusLogic_ReadDataInRegister(HostAdapter);
-	  printk("HOST ADAPTER ERROR CODE = %d\n", ErrorCode);
+	  BusLogic_Error("HOST ADAPTER ERROR CODE = %d\n",
+			 HostAdapter, ErrorCode);
 	}
       return false;
     }
@@ -843,43 +1388,70 @@
 
 /*
   BusLogic_CheckHostAdapter checks to be sure this really is a BusLogic
-  Host Adapter.
+  Host Adapter.  It also determines the IRQ Channel for non-PCI Host Adapters.
 */
 
 static boolean BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
 {
+  BusLogic_Configuration_T Configuration;
   BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
   BusLogic_RequestedReplyLength_T RequestedReplyLength;
-  unsigned long ProcessorFlags;
-  int Result;
+  boolean Result = true;
+  /*
+    FlashPoint Host Adapters do not require this protection.
+  */
+  if (BusLogic_FlashPointHostAdapterP(HostAdapter)) return true;
+  /*
+    Issue the Inquire Configuration command if the IRQ Channel is unknown.
+  */
+  if (HostAdapter->IRQ_Channel == 0)
+    if (BusLogic_Command(HostAdapter, BusLogic_InquireConfiguration,
+			 NULL, 0, &Configuration, sizeof(Configuration))
+	== sizeof(Configuration))
+      {
+	if (Configuration.IRQ_Channel9)
+	  HostAdapter->IRQ_Channel = 9;
+	else if (Configuration.IRQ_Channel10)
+	  HostAdapter->IRQ_Channel = 10;
+	else if (Configuration.IRQ_Channel11)
+	  HostAdapter->IRQ_Channel = 11;
+	else if (Configuration.IRQ_Channel12)
+	  HostAdapter->IRQ_Channel = 12;
+	else if (Configuration.IRQ_Channel14)
+	  HostAdapter->IRQ_Channel = 14;
+	else if (Configuration.IRQ_Channel15)
+	  HostAdapter->IRQ_Channel = 15;
+	else Result = false;
+      }
+    else Result = false;
   /*
     Issue the Inquire Extended Setup Information command.  Only genuine
     BusLogic Host Adapters and true clones support this command.  Adaptec 1542C
     series Host Adapters that respond to the Geometry Register I/O port will
-    fail this command.  Interrupts must be disabled around the call to
-    BusLogic_Command since a Command Complete interrupt could occur if the IRQ
-    Channel was previously enabled for another BusLogic Host Adapter sharing
-    the same IRQ Channel.
+    fail this command.
   */
-  save_flags(ProcessorFlags);
-  cli();
   RequestedReplyLength = sizeof(ExtendedSetupInformation);
-  Result = BusLogic_Command(HostAdapter,
-			    BusLogic_InquireExtendedSetupInformation,
-			    &RequestedReplyLength, sizeof(RequestedReplyLength),
-			    &ExtendedSetupInformation,
-			    sizeof(ExtendedSetupInformation));
-  restore_flags(ProcessorFlags);
-  if (BusLogic_GlobalOptions & BusLogic_TraceProbe)
-    printk("BusLogic_Check(0x%X): Result %d\n",
-	   HostAdapter->IO_Address, Result);
-  return (Result == sizeof(ExtendedSetupInformation));
+  if (BusLogic_Command(HostAdapter,
+		       BusLogic_InquireExtendedSetupInformation,
+		       &RequestedReplyLength,
+		       sizeof(RequestedReplyLength),
+		       &ExtendedSetupInformation,
+		       sizeof(ExtendedSetupInformation))
+      != sizeof(ExtendedSetupInformation))
+    Result = false;
+  /*
+    Provide tracing information if requested and return.
+  */
+  if (BusLogic_GlobalOptions.Bits.TraceProbe)
+    BusLogic_Notice("BusLogic_Check(0x%X): MultiMaster %s\n", HostAdapter,
+		    HostAdapter->IO_Address, (Result ? "Found" : "Not Found"));
+  return Result;
 }
 
 
 /*
   BusLogic_ReadHostAdapterConfiguration reads the Configuration Information
-  from Host Adapter.
+  from Host Adapter and initializes the Host Adapter structure.
 */
 
 static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
@@ -889,18 +1461,65 @@
   BusLogic_Configuration_T Configuration;
   BusLogic_SetupInformation_T SetupInformation;
   BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
-  BusLogic_ControllerModelNumber_T ControllerModelNumber;
+  BusLogic_HostAdapterModelNumber_T HostAdapterModelNumber;
   BusLogic_FirmwareVersion3rdDigit_T FirmwareVersion3rdDigit;
   BusLogic_FirmwareVersionLetter_T FirmwareVersionLetter;
-  BusLogic_GenericIOPortInformation_T GenericIOPortInformation;
+  BusLogic_PCIHostAdapterInformation_T PCIHostAdapterInformation;
   BusLogic_FetchHostAdapterLocalRAMRequest_T FetchHostAdapterLocalRAMRequest;
-  BusLogic_AutoSCSIByte15_T AutoSCSIByte15;
+  BusLogic_AutoSCSIData_T AutoSCSIData;
+  BusLogic_GeometryRegister_T GeometryRegister;
   BusLogic_RequestedReplyLength_T RequestedReplyLength;
-  unsigned char GeometryRegister, *TargetPointer, Character;
-  unsigned short AllTargetsMask, DisconnectPermitted;
-  unsigned short TaggedQueuingPermitted, TaggedQueuingPermittedDefault;
-  boolean CommonErrorRecovery;
-  int TargetID, i;
+  unsigned char *TargetPointer, Character;
+  int i;
+  /*
+    Configuration Information for FlashPoint Host Adapters is provided in the
+    FlashPoint_Info structure by the FlashPoint SCCB Manager's Probe Function.
+    Initialize fields in the Host Adapter structure from the FlashPoint_Info
+    structure.
+  */
+  if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+    {
+      FlashPoint_Info_T *FlashPointInfo = HostAdapter->FlashPointInfo;
+      TargetPointer = HostAdapter->ModelName;
+      *TargetPointer++ = 'B';
+      *TargetPointer++ = 'T';
+      *TargetPointer++ = '-';
+      for (i = 0; i < sizeof(FlashPointInfo->ModelNumber); i++)
+	*TargetPointer++ = FlashPointInfo->ModelNumber[i];
+      *TargetPointer++ = '\0';
+      strcpy(HostAdapter->FirmwareVersion, FlashPoint_FirmwareVersion);
+      HostAdapter->SCSI_ID = FlashPointInfo->SCSI_ID;
+      HostAdapter->ExtendedTranslationEnabled =
+	FlashPointInfo->ExtendedTranslationEnabled;
+      HostAdapter->ParityCheckingEnabled =
+	FlashPointInfo->ParityCheckingEnabled;
+      HostAdapter->BusResetEnabled = !FlashPointInfo->HostSoftReset;
+      HostAdapter->LevelSensitiveInterrupt = true;
+      HostAdapter->HostWideSCSI = FlashPointInfo->HostWideSCSI;
+      HostAdapter->HostDifferentialSCSI = false;
+      HostAdapter->HostSupportsSCAM = true;
+      HostAdapter->HostUltraSCSI = true;
+      HostAdapter->ExtendedLUNSupport = true;
+      HostAdapter->TerminationInfoValid = true;
+      HostAdapter->LowByteTerminated = FlashPointInfo->LowByteTerminated;
+      HostAdapter->HighByteTerminated = FlashPointInfo->HighByteTerminated;
+      HostAdapter->SCAM_Enabled = FlashPointInfo->SCAM_Enabled;
+      HostAdapter->SCAM_Level2 = FlashPointInfo->SCAM_Level2;
+      HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit;
+      HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8);
+      HostAdapter->MaxLogicalUnits = 32;
+      HostAdapter->InitialCCBs = 64;
+      HostAdapter->IncrementalCCBs = 32;
+      HostAdapter->DriverQueueDepth = 255;
+      HostAdapter->HostAdapterQueueDepth = HostAdapter->DriverQueueDepth;
+      HostAdapter->SynchronousPermitted = FlashPointInfo->SynchronousPermitted;
+      HostAdapter->FastPermitted = FlashPointInfo->FastPermitted;
+      HostAdapter->UltraPermitted = FlashPointInfo->UltraPermitted;
+      HostAdapter->WidePermitted = FlashPointInfo->WidePermitted;
+      HostAdapter->DisconnectPermitted = FlashPointInfo->DisconnectPermitted;
+      HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+      goto Common;
+    }
   /*
     Issue the Inquire Board ID command.
   */
@@ -934,39 +1553,47 @@
       != sizeof(ExtendedSetupInformation))
     return BusLogic_Failure(HostAdapter, "INQUIRE EXTENDED SETUP INFORMATION");
   /*
-    Issue the Inquire Controller Model Number command.
+    Issue the Inquire Firmware Version 3rd Digit command.
+  */
+  FirmwareVersion3rdDigit = '\0';
+  if (BoardID.FirmwareVersion1stDigit > '0')
+    if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersion3rdDigit,
+			 NULL, 0, &FirmwareVersion3rdDigit,
+			 sizeof(FirmwareVersion3rdDigit))
+	!= sizeof(FirmwareVersion3rdDigit))
+      return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT");
+  /*
+    Issue the Inquire Host Adapter Model Number command.
   */
   if (ExtendedSetupInformation.BusType == 'A' &&
       BoardID.FirmwareVersion1stDigit == '2')
     /* BusLogic BT-542B ISA 2.xx */
-    strcpy(ControllerModelNumber, "542B");
+    strcpy(HostAdapterModelNumber, "542B");
+  else if (ExtendedSetupInformation.BusType == 'E' &&
+	   BoardID.FirmwareVersion1stDigit == '2' &&
+	   (BoardID.FirmwareVersion2ndDigit <= '1' ||
+	    (BoardID.FirmwareVersion2ndDigit == '2' &&
+	     FirmwareVersion3rdDigit == '0')))
+    /* BusLogic BT-742A EISA 2.1x or 2.20 */
+    strcpy(HostAdapterModelNumber, "742A");
   else if (ExtendedSetupInformation.BusType == 'E' &&
 	   BoardID.FirmwareVersion1stDigit == '0')
     /* AMI FastDisk EISA Series 441 0.x */
-    strcpy(ControllerModelNumber, "747A");
+    strcpy(HostAdapterModelNumber, "747A");
   else
     {
-      RequestedReplyLength = sizeof(ControllerModelNumber);
-      if (BusLogic_Command(HostAdapter, BusLogic_InquireControllerModelNumber,
+      RequestedReplyLength = sizeof(HostAdapterModelNumber);
+      if (BusLogic_Command(HostAdapter, BusLogic_InquireHostAdapterModelNumber,
 			   &RequestedReplyLength, sizeof(RequestedReplyLength),
-			   &ControllerModelNumber,
-			   sizeof(ControllerModelNumber))
-	  != sizeof(ControllerModelNumber))
-	return BusLogic_Failure(HostAdapter, "INQUIRE CONTROLLER MODEL NUMBER");
+			   &HostAdapterModelNumber,
+			   sizeof(HostAdapterModelNumber))
+	  != sizeof(HostAdapterModelNumber))
+	return BusLogic_Failure(HostAdapter,
+				"INQUIRE HOST ADAPTER MODEL NUMBER");
     }
   /*
-    Issue the Inquire Firmware Version 3rd Digit command.
-  */
-  FirmwareVersion3rdDigit = '\0';
-  if (BoardID.FirmwareVersion1stDigit > '0')
-    if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersion3rdDigit,
-			 NULL, 0, &FirmwareVersion3rdDigit,
-			 sizeof(FirmwareVersion3rdDigit))
-	!= sizeof(FirmwareVersion3rdDigit))
-      return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT");
-  /*
-    BusLogic Host Adapters can be identified by their model number and
-    the major version number of their firmware as follows:
+    BusLogic MultiMaster Host Adapters can be identified by their model number
+    and the major version number of their firmware as follows:
 
     5.xx	BusLogic "W" Series Host Adapters:
 		  BT-948/958/958D
@@ -980,22 +1607,19 @@
     0.xx	AMI FastDisk VLB/EISA BusLogic Clone Host Adapter
   */
   /*
-    Save the Model Name and Controller Name in the Host Adapter structure.
+    Save the Model Name and Host Adapter Name in the Host Adapter structure.
   */
   TargetPointer = HostAdapter->ModelName;
   *TargetPointer++ = 'B';
   *TargetPointer++ = 'T';
   *TargetPointer++ = '-';
-  for (i = 0; i < sizeof(ControllerModelNumber); i++)
+  for (i = 0; i < sizeof(HostAdapterModelNumber); i++)
     {
-      Character = ControllerModelNumber[i];
+      Character = HostAdapterModelNumber[i];
       if (Character == ' ' || Character == '\0') break;
       *TargetPointer++ = Character;
     }
   *TargetPointer++ = '\0';
-  strcpy(HostAdapter->ControllerName, "BusLogic ");
-  strcat(HostAdapter->ControllerName, HostAdapter->ModelName);
-  strcpy(HostAdapter->InterruptLabel, HostAdapter->ControllerName);
   /*
     Save the Firmware Version in the Host Adapter structure.
   */
@@ -1015,144 +1639,39 @@
 			   NULL, 0, &FirmwareVersionLetter,
 			   sizeof(FirmwareVersionLetter))
 	  != sizeof(FirmwareVersionLetter))
-	return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER");
-      if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0')
-	*TargetPointer++ = FirmwareVersionLetter;
-      *TargetPointer = '\0';
-    }
-  /*
-    Issue the Inquire Generic I/O Port Information command to read the
-    IRQ Channel from all PCI Host Adapters, and the Termination Information
-    from "W" Series Host Adapters.
-  */
-  if (HostAdapter->ModelName[3] == '9' &&
-      strcmp(HostAdapter->FirmwareVersion, "4.25") >= 0)
-    {
-      if (BusLogic_Command(HostAdapter,
-			   BusLogic_InquireGenericIOPortInformation,
-			   NULL, 0, &GenericIOPortInformation,
-			   sizeof(GenericIOPortInformation))
-	  != sizeof(GenericIOPortInformation))
 	return BusLogic_Failure(HostAdapter,
-				"INQUIRE GENERIC I/O PORT INFORMATION");
-      /*
-	Save the IRQ Channel in the Host Adapter structure.
-      */
-      HostAdapter->IRQ_Channel = GenericIOPortInformation.PCIAssignedIRQChannel;
-      /*
-	Save the Termination Information in the Host Adapter structure.
-      */
-      if (HostAdapter->FirmwareVersion[0] == '5' &&
-	  GenericIOPortInformation.Valid)
-	{
-	  HostAdapter->TerminationInfoValid = true;
-	  HostAdapter->LowByteTerminated =
-	    GenericIOPortInformation.LowByteTerminated;
-	  HostAdapter->HighByteTerminated =
-	    GenericIOPortInformation.HighByteTerminated;
-	}
-    }
-  /*
-    Issue the Fetch Host Adapter Local RAM command to read the Termination
-    Information from the AutoSCSI area of "C" Series Host Adapters.
-  */
-  if (HostAdapter->FirmwareVersion[0] == '4')
-    {
-      FetchHostAdapterLocalRAMRequest.ByteOffset =
-	BusLogic_AutoSCSI_BaseOffset + 15;
-      FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(AutoSCSIByte15);
-      if (BusLogic_Command(HostAdapter,
-			   BusLogic_FetchHostAdapterLocalRAM,
-			   &FetchHostAdapterLocalRAMRequest,
-			   sizeof(FetchHostAdapterLocalRAMRequest),
-			   &AutoSCSIByte15, sizeof(AutoSCSIByte15))
-	  != sizeof(AutoSCSIByte15))
-	return BusLogic_Failure(HostAdapter, "FETCH HOST ADAPTER LOCAL RAM");
-      /*
-	Save the Termination Information in the Host Adapter structure.
-      */
-      HostAdapter->TerminationInfoValid = true;
-      HostAdapter->LowByteTerminated = AutoSCSIByte15.LowByteTerminated;
-      HostAdapter->HighByteTerminated = AutoSCSIByte15.HighByteTerminated;
-    }
-  /*
-    Determine the IRQ Channel and save it in the Host Adapter structure.
-  */
-  if (HostAdapter->IRQ_Channel == 0)
-    {
-      if (Configuration.IRQ_Channel9)
-	HostAdapter->IRQ_Channel = 9;
-      else if (Configuration.IRQ_Channel10)
-	HostAdapter->IRQ_Channel = 10;
-      else if (Configuration.IRQ_Channel11)
-	HostAdapter->IRQ_Channel = 11;
-      else if (Configuration.IRQ_Channel12)
-	HostAdapter->IRQ_Channel = 12;
-      else if (Configuration.IRQ_Channel14)
-	HostAdapter->IRQ_Channel = 14;
-      else if (Configuration.IRQ_Channel15)
-	HostAdapter->IRQ_Channel = 15;
+				"INQUIRE FIRMWARE VERSION LETTER");
+      if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0')
+	*TargetPointer++ = FirmwareVersionLetter;
+      *TargetPointer = '\0';
     }
   /*
     Save the Host Adapter SCSI ID in the Host Adapter structure.
   */
   HostAdapter->SCSI_ID = Configuration.HostAdapterID;
   /*
-    Save the Synchronous Initiation flag and SCSI Parity Checking flag
-    in the Host Adapter structure.
-  */
-  HostAdapter->SynchronousInitiation =
-    SetupInformation.SynchronousInitiationEnabled;
-  HostAdapter->ParityChecking = SetupInformation.ParityCheckEnabled;
-  /*
     Determine the Bus Type and save it in the Host Adapter structure,
     and determine and save the DMA Channel for ISA Host Adapters.
   */
-  switch (HostAdapter->ModelName[3])
-    {
-    case '4':
-      HostAdapter->BusType = BusLogic_VESA_Bus;
-      break;
-    case '5':
-      HostAdapter->BusType = BusLogic_ISA_Bus;
-      if (Configuration.DMA_Channel5)
-	HostAdapter->DMA_Channel = 5;
-      else if (Configuration.DMA_Channel6)
-	HostAdapter->DMA_Channel = 6;
-      else if (Configuration.DMA_Channel7)
-	HostAdapter->DMA_Channel = 7;
-      break;
-    case '6':
-      HostAdapter->BusType = BusLogic_MCA_Bus;
-      break;
-    case '7':
-      HostAdapter->BusType = BusLogic_EISA_Bus;
-      break;
-    case '9':
-      HostAdapter->BusType = BusLogic_PCI_Bus;
-      break;
-    }
+  HostAdapter->HostAdapterBusType =
+    BusLogic_HostAdapterBusTypes[HostAdapter->ModelName[3] - '4'];
+  if (HostAdapter->HostAdapterBusType == BusLogic_ISA_Bus)
+    if (Configuration.DMA_Channel5)
+      HostAdapter->DMA_Channel = 5;
+    else if (Configuration.DMA_Channel6)
+      HostAdapter->DMA_Channel = 6;
+    else if (Configuration.DMA_Channel7)
+      HostAdapter->DMA_Channel = 7;
   /*
     Determine whether Extended Translation is enabled and save it in
     the Host Adapter structure.
   */
-  GeometryRegister = BusLogic_ReadGeometryRegister(HostAdapter);
-  if (GeometryRegister & BusLogic_ExtendedTranslationEnabled)
-    HostAdapter->ExtendedTranslation = true;
-  /*
-    Save the Disconnect/Reconnect Permitted flag bits in the Host Adapter
-    structure.  The Disconnect Permitted information is only valid on "W" and
-    "C" Series controllers, but Disconnect/Reconnect is always permitted on "S"
-    and "A" Series controllers.
-  */
-  if (HostAdapter->FirmwareVersion[0] >= '4')
-    HostAdapter->DisconnectPermitted =
-      (SetupInformation.DisconnectPermittedID8to15 << 8)
-      | SetupInformation.DisconnectPermittedID0to7;
-  else HostAdapter->DisconnectPermitted = 0xFF;
+  GeometryRegister.All = BusLogic_ReadGeometryRegister(HostAdapter);
+  HostAdapter->ExtendedTranslationEnabled =
+    GeometryRegister.Bits.ExtendedTranslationEnabled;
   /*
-    Save the Scatter Gather Limits, Level Sensitive Interrupts flag, Wide
-    SCSI flag, Differential SCSI flag, Automatic Configuration flag, and
+    Save the Scatter Gather Limits, Level Sensitive Interrupt flag, Wide
+    SCSI flag, Differential SCSI flag, SCAM Supported flag, and
     Ultra SCSI flag in the Host Adapter structure.
   */
   HostAdapter->HostAdapterScatterGatherLimit =
@@ -1161,75 +1680,149 @@
     HostAdapter->HostAdapterScatterGatherLimit;
   if (HostAdapter->HostAdapterScatterGatherLimit > BusLogic_ScatterGatherLimit)
     HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit;
-  if (ExtendedSetupInformation.Misc.LevelSensitiveInterrupts)
-    HostAdapter->LevelSensitiveInterrupts = true;
+  if (ExtendedSetupInformation.Misc.LevelSensitiveInterrupt)
+    HostAdapter->LevelSensitiveInterrupt = true;
   HostAdapter->HostWideSCSI = ExtendedSetupInformation.HostWideSCSI;
   HostAdapter->HostDifferentialSCSI =
     ExtendedSetupInformation.HostDifferentialSCSI;
-  HostAdapter->HostAutomaticConfiguration =
-    ExtendedSetupInformation.HostAutomaticConfiguration;
+  HostAdapter->HostSupportsSCAM = ExtendedSetupInformation.HostSupportsSCAM;
   HostAdapter->HostUltraSCSI = ExtendedSetupInformation.HostUltraSCSI;
   /*
-    Determine whether 64 LUN Format CCBs are supported and save the information
-    in the Host Adapter structure.
+    Determine whether Extended LUN Format CCBs are supported and save the
+    information in the Host Adapter structure.
   */
   if (HostAdapter->FirmwareVersion[0] == '5' ||
       (HostAdapter->FirmwareVersion[0] == '4' && HostAdapter->HostWideSCSI))
-    HostAdapter->Host64LUNSupport = true;
+    HostAdapter->ExtendedLUNSupport = true;
   /*
-    Determine the Host Adapter BIOS Address if the BIOS is enabled and
-    save it in the Host Adapter structure.  The BIOS is disabled if the
-    BIOS_Address is 0.
+    Issue the Inquire PCI Host Adapter Information command to read the
+    Termination Information from "W" series MultiMaster Host Adapters.
   */
-  HostAdapter->BIOS_Address = ExtendedSetupInformation.BIOS_Address << 12;
+  if (HostAdapter->FirmwareVersion[0] == '5')
+    {
+      if (BusLogic_Command(HostAdapter,
+			   BusLogic_InquirePCIHostAdapterInformation,
+			   NULL, 0, &PCIHostAdapterInformation,
+			   sizeof(PCIHostAdapterInformation))
+	  != sizeof(PCIHostAdapterInformation))
+	return BusLogic_Failure(HostAdapter,
+				"INQUIRE PCI HOST ADAPTER INFORMATION");
+      /*
+	Save the Termination Information in the Host Adapter structure.
+      */
+      if (PCIHostAdapterInformation.GenericInfoValid)
+	{
+	  HostAdapter->TerminationInfoValid = true;
+	  HostAdapter->LowByteTerminated =
+	    PCIHostAdapterInformation.LowByteTerminated;
+	  HostAdapter->HighByteTerminated =
+	    PCIHostAdapterInformation.HighByteTerminated;
+	}
+    }
   /*
-    ISA Host Adapters require Bounce Buffers if there is more than 16MB memory.
+    Issue the Fetch Host Adapter Local RAM command to read the AutoSCSI data
+    from "W" and "C" series MultiMaster Host Adapters.
   */
-  if (HostAdapter->BusType == BusLogic_ISA_Bus &&
-      high_memory > (void *) MAX_DMA_ADDRESS)
-    HostAdapter->BounceBuffersRequired = true;
+  if (HostAdapter->FirmwareVersion[0] >= '4')
+    {
+      FetchHostAdapterLocalRAMRequest.ByteOffset =
+	BusLogic_AutoSCSI_BaseOffset;
+      FetchHostAdapterLocalRAMRequest.ByteCount = sizeof(AutoSCSIData);
+      if (BusLogic_Command(HostAdapter,
+			   BusLogic_FetchHostAdapterLocalRAM,
+			   &FetchHostAdapterLocalRAMRequest,
+			   sizeof(FetchHostAdapterLocalRAMRequest),
+			   &AutoSCSIData, sizeof(AutoSCSIData))
+	  != sizeof(AutoSCSIData))
+	return BusLogic_Failure(HostAdapter, "FETCH HOST ADAPTER LOCAL RAM");
+      /*
+	Save the Parity Checking Enabled, Bus Reset Enabled, and Termination
+	Information in the Host Adapter structure.
+      */
+      HostAdapter->ParityCheckingEnabled = AutoSCSIData.ParityCheckingEnabled;
+      HostAdapter->BusResetEnabled = AutoSCSIData.BusResetEnabled;
+      if (HostAdapter->FirmwareVersion[0] == '4')
+	{
+	  HostAdapter->TerminationInfoValid = true;
+	  HostAdapter->LowByteTerminated = AutoSCSIData.LowByteTerminated;
+	  HostAdapter->HighByteTerminated = AutoSCSIData.HighByteTerminated;
+	}
+      /*
+	Save the Wide Permitted, Fast Permitted, Synchronous Permitted,
+	Disconnect Permitted, Ultra Permitted, and SCAM Information in the
+	Host Adapter structure.
+      */
+      HostAdapter->WidePermitted = AutoSCSIData.WidePermitted;
+      HostAdapter->FastPermitted = AutoSCSIData.FastPermitted;
+      HostAdapter->SynchronousPermitted =
+	AutoSCSIData.SynchronousPermitted;
+      HostAdapter->DisconnectPermitted =
+	AutoSCSIData.DisconnectPermitted;
+      if (HostAdapter->HostUltraSCSI)
+	HostAdapter->UltraPermitted = AutoSCSIData.UltraPermitted;
+      if (HostAdapter->HostSupportsSCAM)
+	{
+	  HostAdapter->SCAM_Enabled = AutoSCSIData.SCAM_Enabled;
+	  HostAdapter->SCAM_Level2 = AutoSCSIData.SCAM_Level2;
+	}
+    }
   /*
-    BusLogic BT-445S Host Adapters prior to controller revision E have a
-    hardware bug whereby when the BIOS is enabled, transfers to/from the same
-    address range the BIOS occupies modulo 16MB are handled incorrectly.  Only
-    properly functioning BT-445S controllers have firmware version 3.37, so we
-    require that ISA Bounce Buffers be used for the buggy BT-445S models if
-    there is more than 16MB memory.
+    Initialize fields in the Host Adapter structure for "S" and "A" series
+    MultiMaster Host Adapters.
   */
-  if (HostAdapter->BIOS_Address > 0 &&
-      strcmp(HostAdapter->ModelName, "BT-445S") == 0 &&
-      strcmp(HostAdapter->FirmwareVersion, "3.37") < 0 &&
-      high_memory > (void *) MAX_DMA_ADDRESS)
-    HostAdapter->BounceBuffersRequired = true;
+  if (HostAdapter->FirmwareVersion[0] < '4')
+    {
+      if (SetupInformation.SynchronousInitiationEnabled)
+	{
+	  HostAdapter->SynchronousPermitted = 0xFF;
+	  if (HostAdapter->HostAdapterBusType == BusLogic_EISA_Bus)
+	    {
+	      if (ExtendedSetupInformation.Misc.FastOnEISA)
+		HostAdapter->FastPermitted = 0xFF;
+	      if (strcmp(HostAdapter->ModelName, "BT-757") == 0)
+		HostAdapter->WidePermitted = 0xFF;
+	    }
+	}
+      HostAdapter->DisconnectPermitted = 0xFF;
+      HostAdapter->ParityCheckingEnabled =
+	SetupInformation.ParityCheckingEnabled;
+      HostAdapter->BusResetEnabled = true;
+    }
   /*
     Determine the maximum number of Target IDs and Logical Units supported by
     this driver for Wide and Narrow Host Adapters.
   */
   HostAdapter->MaxTargetDevices = (HostAdapter->HostWideSCSI ? 16 : 8);
-  HostAdapter->MaxLogicalUnits = (HostAdapter->Host64LUNSupport ? 64 : 8);
+  HostAdapter->MaxLogicalUnits = (HostAdapter->ExtendedLUNSupport ? 32 : 8);
   /*
-    Select appropriate values for the Mailbox Count, Initial CCBs, and
-    Incremental CCBs variables based on whether or not Strict Round Robin Mode
-    is supported.  If Strict Round Robin Mode is supported, then there is no
-    performance degradation in using the maximum possible number of Outgoing
-    and Incoming Mailboxes and allowing the Tagged and Untagged Queue Depths to
-    determine the actual utilization.  If Strict Round Robin Mode is not
-    supported, then the Host Adapter must scan all the Outgoing Mailboxes
-    whenever an Outgoing Mailbox entry is made, which can cause a substantial
-    performance penalty.  The Host Adapters actually have room to store the
-    following number of CCBs internally; that is, they can internally queue and
-    manage this many active commands on the SCSI bus simultaneously.
-    Performance measurements demonstrate that the Mailbox Count should be set
-    to the maximum possible, rather than the internal CCB capacity, as it is
-    more efficient to have the queued commands waiting in Outgoing Mailboxes if
-    necessary than to block the process in the higher levels of the SCSI
-    Subsystem.
+    Select appropriate values for the Driver Queue Depth, Mailbox Count,
+    Initial CCBs, and Incremental CCBs variables based on whether or not Strict
+    Round Robin Mode is supported.  If Strict Round Robin Mode is supported,
+    then there is no performance degradation in using the maximum possible
+    number of Outgoing and Incoming Mailboxes and allowing the Tagged and
+    Untagged Queue Depths to determine the actual utilization.  If Strict Round
+    Robin Mode is not supported, then the Host Adapter must scan all the
+    Outgoing Mailboxes whenever an Outgoing Mailbox entry is made, which can
+    cause a substantial performance penalty.  The host adapters actually have
+    room to store the following number of CCBs internally; that is, they can
+    internally queue and manage this many active commands on the SCSI bus
+    simultaneously.  Performance measurements demonstrate that the Driver Queue
+    Depth should be set to the Mailbox Count, rather than the Host Adapter
+    Queue Depth (internal CCB capacity), as it is more efficient to have the
+    queued commands waiting in Outgoing Mailboxes if necessary than to block
+    the process in the higher levels of the SCSI Subsystem.
 
 	192	  BT-948/958/958D
 	100	  BT-946C/956C/956CD/747C/757C/757CD/445C
 	 50	  BT-545C/540CF
 	 30	  BT-747S/747D/757S/757D/445S/545S/542D/542B/742A
   */
+  if (HostAdapter->FirmwareVersion[0] == '5')
+    HostAdapter->HostAdapterQueueDepth = 192;
+  else if (HostAdapter->FirmwareVersion[0] == '4')
+    HostAdapter->HostAdapterQueueDepth =
+      (HostAdapter->HostAdapterBusType != BusLogic_ISA_Bus ? 100 : 50);
+  else HostAdapter->HostAdapterQueueDepth = 30;
   if (strcmp(HostAdapter->FirmwareVersion, "3.31") >= 0)
     {
       HostAdapter->StrictRoundRobinModeSupport = true;
@@ -1244,30 +1837,85 @@
       HostAdapter->InitialCCBs = 32;
       HostAdapter->IncrementalCCBs = 4;
     }
-  if (HostAdapter->FirmwareVersion[0] == '5')
-    HostAdapter->TotalQueueDepth = 192;
-  else if (HostAdapter->FirmwareVersion[0] == '4')
-    HostAdapter->TotalQueueDepth =
-      (HostAdapter->BusType != BusLogic_ISA_Bus ? 100 : 50);
-  else HostAdapter->TotalQueueDepth = 30;
+  HostAdapter->DriverQueueDepth = HostAdapter->MailboxCount;
+  /*
+    Tagged Queuing support is available and operates properly on all "W" series
+    MultiMaster Host Adapters, on "C" series MultiMaster Host Adapters with
+    firmware version 4.22 and above, and on "S" series MultiMaster Host
+    Adapters with firmware version 3.35 and above.
+  */
+  HostAdapter->TaggedQueuingPermitted = 0;
+  switch (HostAdapter->FirmwareVersion[0])
+    {
+    case '5':
+      HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+      break;
+    case '4':
+      if (strcmp(HostAdapter->FirmwareVersion, "4.22") >= 0)
+	HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+      break;
+    case '3':
+      if (strcmp(HostAdapter->FirmwareVersion, "3.35") >= 0)
+	HostAdapter->TaggedQueuingPermitted = 0xFFFF;
+      break;
+    }
+  /*
+    Determine the Host Adapter BIOS Address if the BIOS is enabled and
+    save it in the Host Adapter structure.  The BIOS is disabled if the
+    BIOS_Address is 0.
+  */
+  HostAdapter->BIOS_Address = ExtendedSetupInformation.BIOS_Address << 12;
+  /*
+    ISA Host Adapters require Bounce Buffers if there is more than 16MB memory.
+  */
+  if (HostAdapter->HostAdapterBusType == BusLogic_ISA_Bus &&
+      (void *) high_memory > (void *) MAX_DMA_ADDRESS)
+    HostAdapter->BounceBuffersRequired = true;
+  /*
+    BusLogic BT-445S Host Adapters prior to board revision E have a hardware
+    bug whereby when the BIOS is enabled, transfers to/from the same address
+    range the BIOS occupies modulo 16MB are handled incorrectly.  Only properly
+    functioning BT-445S Host Adapters have firmware version 3.37, so require
+    that ISA Bounce Buffers be used for the buggy BT-445S models if there is
+    more than 16MB memory.
+  */
+  if (HostAdapter->BIOS_Address > 0 &&
+      strcmp(HostAdapter->ModelName, "BT-445S") == 0 &&
+      strcmp(HostAdapter->FirmwareVersion, "3.37") < 0 &&
+      (void *) high_memory > (void *) MAX_DMA_ADDRESS)
+    HostAdapter->BounceBuffersRequired = true;
+  /*
+    Initialize parameters common to MultiMaster and FlashPoint Host Adapters.
+  */
+Common:
+  /*
+    Initialize the Host Adapter Name and Interrupt Label fields from the
+    Model Name.
+  */
+  strcpy(HostAdapter->FullModelName, "BusLogic ");
+  strcat(HostAdapter->FullModelName, HostAdapter->ModelName);
+  strcpy(HostAdapter->InterruptLabel, HostAdapter->FullModelName);
   /*
     Select an appropriate value for the Tagged Queue Depth either from a
-    Command Line Entry, or based on whether this Host Adapter requires that
-    ISA Bounce Buffers be used.  The Tagged Queue Depth is left at 0 for
-    automatic determination in BusLogic_SelectQueueDepths.  Initialize the
-    Untagged Queue Depth.
+    Command Line Entry, or based on whether this Host Adapter requires that ISA
+    Bounce Buffers be used.  The Tagged Queue Depth is left at 0 for automatic
+    determination in BusLogic_SelectQueueDepths.  Initialize the Untagged Queue
+    Depth.  Tagged Queuing is disabled by default when the Tagged Queue Depth
+    is 1 since queuing multiple commands is not possible.
   */
   if (HostAdapter->CommandLineEntry != NULL &&
       HostAdapter->CommandLineEntry->TaggedQueueDepth > 0)
     HostAdapter->TaggedQueueDepth =
       HostAdapter->CommandLineEntry->TaggedQueueDepth;
   else if (HostAdapter->BounceBuffersRequired)
-    HostAdapter->TaggedQueueDepth = BusLogic_TaggedQueueDepth_BB;
-  else HostAdapter->TaggedQueueDepth = 0;
+    HostAdapter->TaggedQueueDepth = BusLogic_TaggedQueueDepthBounceBuffers;
+  else HostAdapter->TaggedQueueDepth = BusLogic_TaggedQueueDepthAutomatic;
   HostAdapter->UntaggedQueueDepth = BusLogic_UntaggedQueueDepth;
   if (HostAdapter->UntaggedQueueDepth > HostAdapter->TaggedQueueDepth &&
       HostAdapter->TaggedQueueDepth > 0)
     HostAdapter->UntaggedQueueDepth = HostAdapter->TaggedQueueDepth;
+  if (HostAdapter->TaggedQueueDepth == 1)
+    HostAdapter->TaggedQueuingPermitted = 0;
   /*
     Select an appropriate value for Bus Settle Time either from a Command
     Line Entry, or from BusLogic_DefaultBusSettleTime.
@@ -1293,34 +1941,11 @@
 	      BusLogic_ErrorRecovery_Default,
 	      sizeof(HostAdapter->ErrorRecoveryStrategy));
   /*
-    Tagged Queuing support is available and operates properly on all "W" Series
-    controllers, on "C" Series controllers with firmware version 4.22 and
-    above, and on "S" Series controllers with firmware version 3.35 and above.
-    Tagged Queuing is disabled by default when the Tagged Queue Depth is 1
-    since queuing multiple commands is not possible.
-  */
-  TaggedQueuingPermittedDefault = 0;
-  if (HostAdapter->TaggedQueueDepth != 1)
-    switch (HostAdapter->FirmwareVersion[0])
-      {
-      case '5':
-	TaggedQueuingPermittedDefault = 0xFFFF;
-	break;
-      case '4':
-	if (strcmp(HostAdapter->FirmwareVersion, "4.22") >= 0)
-	  TaggedQueuingPermittedDefault = 0xFFFF;
-	break;
-      case '3':
-	if (strcmp(HostAdapter->FirmwareVersion, "3.35") >= 0)
-	  TaggedQueuingPermittedDefault = 0xFFFF;
-	break;
-      }
-  /*
-    Tagged Queuing is only useful if Disconnect/Reconnect is permitted.
+    Tagged Queuing is only allowed if Disconnect/Reconnect is permitted.
     Therefore, mask the Tagged Queuing Permitted Default bits with the
     Disconnect/Reconnect Permitted bits.
   */
-  TaggedQueuingPermittedDefault &= HostAdapter->DisconnectPermitted;
+  HostAdapter->TaggedQueuingPermitted &= HostAdapter->DisconnectPermitted;
   /*
     Combine the default Tagged Queuing Permitted bits with any Command
     Line Entry Tagged Queuing specification.
@@ -1329,78 +1954,190 @@
     HostAdapter->TaggedQueuingPermitted =
       (HostAdapter->CommandLineEntry->TaggedQueuingPermitted &
        HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask) |
-      (TaggedQueuingPermittedDefault &
+      (HostAdapter->TaggedQueuingPermitted &
        ~HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask);
-  else HostAdapter->TaggedQueuingPermitted = TaggedQueuingPermittedDefault;
   /*
-    Announce the Host Adapter Configuration.
+    Indicate reading the Host Adapter Configuration completed successfully.
   */
-  printk("scsi%d: Configuring BusLogic Model %s %s%s%s%s SCSI Host Adapter\n",
-	 HostAdapter->HostNumber, HostAdapter->ModelName,
-	 BusLogic_BusNames[HostAdapter->BusType],
-	 (HostAdapter->HostWideSCSI ? " Wide" : ""),
-	 (HostAdapter->HostDifferentialSCSI ? " Differential" : ""),
-	 (HostAdapter->HostUltraSCSI ? " Ultra" : ""));
-  printk("scsi%d:   Firmware Version: %s, I/O Address: 0x%X, "
-	 "IRQ Channel: %d/%s\n",
-	 HostAdapter->HostNumber, HostAdapter->FirmwareVersion,
-	 HostAdapter->IO_Address, HostAdapter->IRQ_Channel,
-	 (HostAdapter->LevelSensitiveInterrupts ? "Level" : "Edge"));
-  printk("scsi%d:   DMA Channel: ", HostAdapter->HostNumber);
-  if (HostAdapter->DMA_Channel > 0)
-    printk("%d, ", HostAdapter->DMA_Channel);
-  else printk("None, ");
-  if (HostAdapter->BIOS_Address > 0)
-    printk("BIOS Address: 0x%X, ", HostAdapter->BIOS_Address);
-  else printk("BIOS Address: None, ");
-  printk("Host Adapter SCSI ID: %d\n", HostAdapter->SCSI_ID);
-  printk("scsi%d:   Scatter/Gather Limit: %d of %d segments, "
-	 "Parity Checking: %s\n", HostAdapter->HostNumber,
-	 HostAdapter->DriverScatterGatherLimit,
-	 HostAdapter->HostAdapterScatterGatherLimit,
-	 (HostAdapter->ParityChecking ? "Enabled" : "Disabled"));
-  printk("scsi%d:   Synchronous Initiation: %s, "
-	 "Extended Disk Translation: %s\n", HostAdapter->HostNumber,
-	 (HostAdapter->SynchronousInitiation ? "Enabled" : "Disabled"),
-	 (HostAdapter->ExtendedTranslation ? "Enabled" : "Disabled"));
-  AllTargetsMask = (1 << HostAdapter->MaxTargetDevices) - 1;
+  return true;
+}
+
+
+/*
+  BusLogic_ReportHostAdapterConfiguration reports the configuration of
+  Host Adapter.
+*/
+
+static boolean BusLogic_ReportHostAdapterConfiguration(BusLogic_HostAdapter_T
+						       *HostAdapter)
+{
+  unsigned short AllTargetsMask = (1 << HostAdapter->MaxTargetDevices) - 1;
+  unsigned short SynchronousPermitted, FastPermitted;
+  unsigned short UltraPermitted, WidePermitted;
+  unsigned short DisconnectPermitted, TaggedQueuingPermitted;
+  boolean CommonSynchronousNegotiation, CommonErrorRecovery;
+  char SynchronousString[BusLogic_MaxTargetDevices+1];
+  char WideString[BusLogic_MaxTargetDevices+1];
+  char DisconnectString[BusLogic_MaxTargetDevices+1];
+  char TaggedQueuingString[BusLogic_MaxTargetDevices+1];
+  char ErrorRecoveryString[BusLogic_MaxTargetDevices+1];
+  char *SynchronousMessage = SynchronousString;
+  char *WideMessage = WideString;
+  char *DisconnectMessage = DisconnectString;
+  char *TaggedQueuingMessage = TaggedQueuingString;
+  char *ErrorRecoveryMessage = ErrorRecoveryString;
+  int TargetID;
+  BusLogic_Info("Configuring BusLogic Model %s %s%s%s%s SCSI Host Adapter\n",
+		HostAdapter, HostAdapter->ModelName,
+		BusLogic_HostAdapterBusNames[HostAdapter->HostAdapterBusType],
+		(HostAdapter->HostWideSCSI ? " Wide" : ""),
+		(HostAdapter->HostDifferentialSCSI ? " Differential" : ""),
+		(HostAdapter->HostUltraSCSI ? " Ultra" : ""));
+  BusLogic_Info("  Firmware Version: %s, I/O Address: 0x%X, "
+		"IRQ Channel: %d/%s\n", HostAdapter,
+		HostAdapter->FirmwareVersion,
+		HostAdapter->IO_Address, HostAdapter->IRQ_Channel,
+		(HostAdapter->LevelSensitiveInterrupt ? "Level" : "Edge"));
+  if (HostAdapter->HostAdapterBusType != BusLogic_PCI_Bus)
+    {
+      BusLogic_Info("  DMA Channel: ", HostAdapter);
+      if (HostAdapter->DMA_Channel > 0)
+	BusLogic_Info("%d, ", HostAdapter, HostAdapter->DMA_Channel);
+      else BusLogic_Info("None, ", HostAdapter);
+      if (HostAdapter->BIOS_Address > 0)
+	BusLogic_Info("BIOS Address: 0x%X, ", HostAdapter,
+		      HostAdapter->BIOS_Address);
+      else BusLogic_Info("BIOS Address: None, ", HostAdapter);
+    }
+  else
+    {
+      BusLogic_Info("  PCI Bus: %d, Device: %d, Address: ",
+		    HostAdapter, HostAdapter->Bus, HostAdapter->Device);
+      if (HostAdapter->PCI_Address > 0)
+	BusLogic_Info("0x%X, ", HostAdapter, HostAdapter->PCI_Address);
+      else BusLogic_Info("Unassigned, ", HostAdapter);
+    }
+  BusLogic_Info("Host Adapter SCSI ID: %d\n", HostAdapter,
+		HostAdapter->SCSI_ID);
+  BusLogic_Info("  Parity Checking: %s, Extended Translation: %s\n",
+		HostAdapter,
+		(HostAdapter->ParityCheckingEnabled
+		 ? "Enabled" : "Disabled"),
+		(HostAdapter->ExtendedTranslationEnabled
+		 ? "Enabled" : "Disabled"));
+  AllTargetsMask &= ~(1 << HostAdapter->SCSI_ID);
+  SynchronousPermitted = HostAdapter->SynchronousPermitted & AllTargetsMask;
+  FastPermitted = HostAdapter->FastPermitted & AllTargetsMask;
+  UltraPermitted = HostAdapter->UltraPermitted & AllTargetsMask;
+  if ((BusLogic_MultiMasterHostAdapterP(HostAdapter) &&
+       (HostAdapter->FirmwareVersion[0] >= '4' ||
+	HostAdapter->HostAdapterBusType == BusLogic_EISA_Bus)) ||
+      BusLogic_FlashPointHostAdapterP(HostAdapter))
+    {
+      CommonSynchronousNegotiation = false;
+      if (SynchronousPermitted == 0)
+	{
+	  SynchronousMessage = "Disabled";
+	  CommonSynchronousNegotiation = true;
+	}
+      else if (SynchronousPermitted == AllTargetsMask)
+	if (FastPermitted == 0)
+	  {
+	    SynchronousMessage = "Slow";
+	    CommonSynchronousNegotiation = true;
+	  }
+	else if (FastPermitted == AllTargetsMask)
+	  if (UltraPermitted == 0)
+	    {
+	      SynchronousMessage = "Fast";
+	      CommonSynchronousNegotiation = true;
+	    }
+	  else if (UltraPermitted == AllTargetsMask)
+	    {
+	      SynchronousMessage = "Ultra";
+	      CommonSynchronousNegotiation = true;
+	    }
+      if (!CommonSynchronousNegotiation)
+	{
+	  for (TargetID = 0;
+	       TargetID < HostAdapter->MaxTargetDevices;
+	       TargetID++)
+	    SynchronousString[TargetID] =
+	      ((!(SynchronousPermitted & (1 << TargetID))) ? 'N' :
+	       (!(FastPermitted & (1 << TargetID)) ? 'S' :
+		(!(UltraPermitted & (1 << TargetID)) ? 'F' : 'U')));
+	  SynchronousString[HostAdapter->SCSI_ID] = '#';
+	  SynchronousString[HostAdapter->MaxTargetDevices] = '\0';
+	}
+    }
+  else SynchronousMessage =
+	 (SynchronousPermitted == 0 ? "Disabled" : "Enabled");
+  WidePermitted = HostAdapter->WidePermitted & AllTargetsMask;
+  if (WidePermitted == 0)
+    WideMessage = "Disabled";
+  else if (WidePermitted == AllTargetsMask)
+    WideMessage = "Enabled";
+  else
+    {
+      for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+	 WideString[TargetID] =
+	   ((WidePermitted & (1 << TargetID)) ? 'Y' : 'N');
+      WideString[HostAdapter->SCSI_ID] = '#';
+      WideString[HostAdapter->MaxTargetDevices] = '\0';
+    }
   DisconnectPermitted = HostAdapter->DisconnectPermitted & AllTargetsMask;
-  printk("scsi%d:   Disconnect/Reconnect: ", HostAdapter->HostNumber);
   if (DisconnectPermitted == 0)
-    printk("Disabled");
+    DisconnectMessage = "Disabled";
   else if (DisconnectPermitted == AllTargetsMask)
-    printk("Enabled");
+    DisconnectMessage = "Enabled";
   else
-    for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
-      printk("%c", (DisconnectPermitted & (1 << TargetID)) ? 'Y' : 'N');
-  printk(", Tagged Queuing: ");
+    {
+      for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+	DisconnectString[TargetID] =
+	  ((DisconnectPermitted & (1 << TargetID)) ? 'Y' : 'N');
+      DisconnectString[HostAdapter->SCSI_ID] = '#';
+      DisconnectString[HostAdapter->MaxTargetDevices] = '\0';
+    }
   TaggedQueuingPermitted =
     HostAdapter->TaggedQueuingPermitted & AllTargetsMask;
   if (TaggedQueuingPermitted == 0)
-    printk("Disabled");
+    TaggedQueuingMessage = "Disabled";
   else if (TaggedQueuingPermitted == AllTargetsMask)
-    printk("Enabled");
+    TaggedQueuingMessage = "Enabled";
   else
-    for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
-      printk("%c", (TaggedQueuingPermitted & (1 << TargetID)) ? 'Y' : 'N');
-  printk("\n");
-  printk("scsi%d:   Total Queue Depth: %d, Mailboxes: %d, Initial CCBs: %d\n",
-	 HostAdapter->HostNumber, HostAdapter->TotalQueueDepth,
-	 HostAdapter->MailboxCount, HostAdapter->InitialCCBs);
-  printk("scsi%d:   Tagged Queue Depth: ", HostAdapter->HostNumber);
+    {
+      for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+	TaggedQueuingString[TargetID] =
+	  ((TaggedQueuingPermitted & (1 << TargetID)) ? 'Y' : 'N');
+      TaggedQueuingString[HostAdapter->SCSI_ID] = '#';
+      TaggedQueuingString[HostAdapter->MaxTargetDevices] = '\0';
+    }
+  BusLogic_Info("  Synchronous Negotiation: %s, Wide Negotiation: %s\n",
+		HostAdapter, SynchronousMessage, WideMessage);
+  BusLogic_Info("  Disconnect/Reconnect: %s, Tagged Queuing: %s\n",
+		HostAdapter, DisconnectMessage, TaggedQueuingMessage);
+  if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+    {
+      BusLogic_Info("  Scatter/Gather Limit: %d of %d segments, "
+		    "Mailboxes: %d\n", HostAdapter,
+		    HostAdapter->DriverScatterGatherLimit,
+		    HostAdapter->HostAdapterScatterGatherLimit,
+		    HostAdapter->MailboxCount);
+      BusLogic_Info("  Driver Queue Depth: %d, "
+		    "Host Adapter Queue Depth: %d\n",
+		    HostAdapter, HostAdapter->DriverQueueDepth,
+		    HostAdapter->HostAdapterQueueDepth);
+    }
+  else BusLogic_Info("  Driver Queue Depth: %d, "
+		     "Scatter/Gather Limit: %d segments\n",
+		     HostAdapter, HostAdapter->DriverQueueDepth,
+		     HostAdapter->DriverScatterGatherLimit);
+  BusLogic_Info("  Tagged Queue Depth: ", HostAdapter);
   if (HostAdapter->TaggedQueueDepth > 0)
-    printk("%d", HostAdapter->TaggedQueueDepth);
-  else printk("Automatic");
-  printk(", Untagged Queue Depth: %d\n", HostAdapter->UntaggedQueueDepth);
-  if (HostAdapter->TerminationInfoValid)
-    if (HostAdapter->HostWideSCSI)
-      printk("scsi%d:   Host Adapter SCSI Bus Termination (Low/High): %s/%s\n",
-	     HostAdapter->HostNumber,
-	     (HostAdapter->LowByteTerminated ? "Enabled" : "Disabled"),
-	     (HostAdapter->HighByteTerminated ? "Enabled" : "Disabled"));
-    else printk("scsi%d:   Host Adapter SCSI Bus Termination: %s\n",
-		HostAdapter->HostNumber,
-		(HostAdapter->LowByteTerminated ? "Enabled" : "Disabled"));
+    BusLogic_Info("%d", HostAdapter, HostAdapter->TaggedQueueDepth);
+  else BusLogic_Info("Automatic", HostAdapter);
+  BusLogic_Info(", Untagged Queue Depth: %d\n", HostAdapter,
+		HostAdapter->UntaggedQueueDepth);
   CommonErrorRecovery = true;
   for (TargetID = 1; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
     if (HostAdapter->ErrorRecoveryStrategy[TargetID] !=
@@ -1409,17 +2146,44 @@
 	CommonErrorRecovery = false;
 	break;
       }
-  printk("scsi%d:   Error Recovery Strategy: ", HostAdapter->HostNumber);
   if (CommonErrorRecovery)
-    printk("%s", BusLogic_ErrorRecoveryStrategyNames[
-		   HostAdapter->ErrorRecoveryStrategy[0]]);
+    ErrorRecoveryMessage =
+      BusLogic_ErrorRecoveryStrategyNames[
+	HostAdapter->ErrorRecoveryStrategy[0]];
   else
-    for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
-      printk("%s", BusLogic_ErrorRecoveryStrategyLetters[
-		     HostAdapter->ErrorRecoveryStrategy[TargetID]]);
-  printk("\n");
+    {
+      for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+	ErrorRecoveryString[TargetID] =
+	  BusLogic_ErrorRecoveryStrategyLetters[
+	    HostAdapter->ErrorRecoveryStrategy[TargetID]];
+      ErrorRecoveryString[HostAdapter->SCSI_ID] = '#';
+      ErrorRecoveryString[HostAdapter->MaxTargetDevices] = '\0';
+    }
+  BusLogic_Info("  Error Recovery Strategy: %s, SCSI Bus Reset: %s\n",
+		HostAdapter, ErrorRecoveryMessage,
+		(HostAdapter->BusResetEnabled ? "Enabled" : "Disabled"));
+  if (HostAdapter->TerminationInfoValid)
+    {
+      if (HostAdapter->HostWideSCSI)
+	BusLogic_Info("  SCSI Bus Termination: %s", HostAdapter,
+		      (HostAdapter->LowByteTerminated
+		       ? (HostAdapter->HighByteTerminated
+			  ? "Both Enabled" : "Low Enabled")
+		       : (HostAdapter->HighByteTerminated
+			  ? "High Enabled" : "Both Disabled")));
+      else BusLogic_Info("  SCSI Bus Termination: %s", HostAdapter,
+			 (HostAdapter->LowByteTerminated ?
+			  "Enabled" : "Disabled"));
+      if (HostAdapter->HostSupportsSCAM)
+	BusLogic_Info(", SCAM: %s", HostAdapter,
+		      (HostAdapter->SCAM_Enabled
+		       ? (HostAdapter->SCAM_Level2
+			  ? "Enabled, Level 2" : "Enabled, Level 1")
+		       : "Disabled"));
+      BusLogic_Info("\n", HostAdapter);
+    }
   /*
-    Indicate reading the Host Adapter Configuration completed successfully.
+    Indicate reporting the Host Adapter configuration completed successfully.
   */
   return true;
 }
@@ -1432,47 +2196,33 @@
 
 static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter)
 {
+  BusLogic_HostAdapter_T *FirstHostAdapter =
+    BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel];
   if (HostAdapter->IRQ_Channel == 0)
     {
-      printk("scsi%d: NO INTERRUPT CHANNEL ASSIGNED - DETACHING\n",
-	     HostAdapter->HostNumber);
+      BusLogic_Error("NO LEGAL INTERRUPT CHANNEL ASSIGNED - DETACHING\n",
+		     HostAdapter);
       return false;
     }
   /*
-    Acquire exclusive or shared access to the IRQ Channel.  A usage count is
-    maintained so that PCI, EISA, or MCA shared interrupts can be supported.
+    Acquire exclusive or shared access to the IRQ Channel if necessary.
   */
-  if (BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel]++ == 0)
+  if (FirstHostAdapter->Next == NULL)
     {
       if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler,
 		      SA_INTERRUPT | SA_SHIRQ,
 		      HostAdapter->InterruptLabel, NULL) < 0)
 	{
-	  BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel]--;
-	  printk("scsi%d: UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n",
-		 HostAdapter->HostNumber, HostAdapter->IRQ_Channel);
+	  BusLogic_Error("UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n",
+			 HostAdapter, HostAdapter->IRQ_Channel);
 	  return false;
 	}
     }
-  else
+  else if (strlen(FirstHostAdapter->InterruptLabel) + 11
+	   < sizeof(FirstHostAdapter->InterruptLabel))
     {
-      BusLogic_HostAdapter_T *FirstHostAdapter =
-	BusLogic_RegisteredHostAdapters;
-      while (FirstHostAdapter != NULL)
-	{
-	  if (FirstHostAdapter->IRQ_Channel == HostAdapter->IRQ_Channel)
-	    {
-	      if (strlen(FirstHostAdapter->InterruptLabel) + 11
-		  < sizeof(FirstHostAdapter->InterruptLabel))
-		{
-		  strcat(FirstHostAdapter->InterruptLabel, " + ");
-		  strcat(FirstHostAdapter->InterruptLabel,
-			 HostAdapter->ModelName);
-		}
-	      break;
-	    }
-	  FirstHostAdapter = FirstHostAdapter->Next;
-	}
+      strcat(FirstHostAdapter->InterruptLabel, " + ");
+      strcat(FirstHostAdapter->InterruptLabel, HostAdapter->ModelName);
     }
   HostAdapter->IRQ_ChannelAcquired = true;
   /*
@@ -1481,10 +2231,10 @@
   if (HostAdapter->DMA_Channel > 0)
     {
       if (request_dma(HostAdapter->DMA_Channel,
-		      HostAdapter->ControllerName) < 0)
+		      HostAdapter->FullModelName) < 0)
 	{
-	  printk("scsi%d: UNABLE TO ACQUIRE DMA CHANNEL %d - DETACHING\n",
-		 HostAdapter->HostNumber, HostAdapter->DMA_Channel);
+	  BusLogic_Error("UNABLE TO ACQUIRE DMA CHANNEL %d - DETACHING\n",
+			 HostAdapter, HostAdapter->DMA_Channel);
 	  return false;
 	}
       set_dma_mode(HostAdapter->DMA_Channel, DMA_MODE_CASCADE);
@@ -1505,11 +2255,13 @@
 
 static void BusLogic_ReleaseResources(BusLogic_HostAdapter_T *HostAdapter)
 {
+  BusLogic_HostAdapter_T *FirstHostAdapter =
+    BusLogic_RegisteredHostAdapters[HostAdapter->IRQ_Channel];
   /*
     Release exclusive or shared access to the IRQ Channel.
   */
   if (HostAdapter->IRQ_ChannelAcquired)
-    if (--BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel] == 0)
+    if (FirstHostAdapter->Next == NULL)
       free_irq(HostAdapter->IRQ_Channel, NULL);
   /*
     Release exclusive access to the DMA Channel.
@@ -1532,34 +2284,30 @@
 {
   unsigned int InitialInterruptCount, FinalInterruptCount;
   int TestCount = 5, i;
-  InitialInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel];
+  /*
+    FlashPoint Host Adapters do not provide for an interrupt test.
+  */
+  if (BusLogic_FlashPointHostAdapterP(HostAdapter)) return true;
+  /*
+    Inhibit the Interrupt Test if requested.
+  */
+  if (HostAdapter->LocalOptions.Bits.InhibitInterruptTest) return true;
   /*
     Issue the Test Command Complete Interrupt commands.
   */
+  InitialInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel];
   for (i = 0; i < TestCount; i++)
     BusLogic_Command(HostAdapter, BusLogic_TestCommandCompleteInterrupt,
 		     NULL, 0, NULL, 0);
+  FinalInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel];
   /*
-    Verify that BusLogic_InterruptHandler was called at least TestCount times.
-    Shared IRQ Channels could cause more than TestCount interrupts to occur,
-    but there should never be fewer than TestCount.
+    Verify that BusLogic_InterruptHandler was called at least TestCount
+    times.  Shared IRQ Channels could cause more than TestCount interrupts to
+    occur, but there should never be fewer than TestCount, unless one or more
+    interrupts were lost.
   */
-  FinalInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel];
   if (FinalInterruptCount < InitialInterruptCount + TestCount)
-    {
-      BusLogic_Failure(HostAdapter, "HOST ADAPTER INTERRUPT TEST");
-      printk("\n\
-Interrupts are not getting through from the Host Adapter to the BusLogic\n\
-Driver Interrupt Handler. The most likely cause is that either the Host\n\
-Adapter or Motherboard is configured incorrectly.  Please check the Host\n\
-Adapter configuration with AutoSCSI or by examining any dip switch and\n\
-jumper settings on the Host Adapter, and verify that no other device is\n\
-attempting to use the same IRQ Channel.  For PCI Host Adapters, it may also\n\
-be necessary to investigate and manually set the PCI interrupt assignments\n\
-and edge/level interrupt type selection in the BIOS Setup Program or with\n\
-Motherboard jumpers.\n\n");
-      return false;
-    }
+    return BusLogic_Failure(HostAdapter, "HOST ADAPTER INTERRUPT TEST");
   /*
     Indicate the Host Adapter Interrupt Test completed successfully.
   */
@@ -1579,11 +2327,10 @@
   BusLogic_ExtendedMailboxRequest_T ExtendedMailboxRequest;
   BusLogic_RoundRobinModeRequest_T RoundRobinModeRequest;
   BusLogic_SetCCBFormatRequest_T SetCCBFormatRequest;
-  BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest;
   int TargetID;
   /*
     Initialize the Bus Device Reset Pending CCB, Tagged Queuing Active,
-    Command Successful Flag, Active Command Count, and Total Command Count
+    Command Successful Flag, Active Commands, and Commands Since Reset
     for each Target Device.
   */
   for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
@@ -1592,10 +2339,14 @@
 	 sizeof(HostAdapter->TaggedQueuingActive));
   memset(HostAdapter->CommandSuccessfulFlag, false,
 	 sizeof(HostAdapter->CommandSuccessfulFlag));
-  memset(HostAdapter->ActiveCommandCount, 0,
-	 sizeof(HostAdapter->ActiveCommandCount));
-  memset(HostAdapter->TotalCommandCount, 0,
-	 sizeof(HostAdapter->TotalCommandCount));
+  memset(HostAdapter->ActiveCommands, 0,
+	 sizeof(HostAdapter->ActiveCommands));
+  memset(HostAdapter->CommandsSinceReset, 0,
+	 sizeof(HostAdapter->CommandsSinceReset));
+  /*
+    FlashPoint Host Adapters do not use Outgoing and Incoming Mailboxes.
+  */
+  if (BusLogic_FlashPointHostAdapterP(HostAdapter)) goto Done;
   /*
     Initialize the Outgoing and Incoming Mailbox structures.
   */
@@ -1634,42 +2385,27 @@
 	return BusLogic_Failure(HostAdapter, "ENABLE STRICT ROUND ROBIN MODE");
     }
   /*
-    For Host Adapters that support 64 LUN Format CCBs, issue the Set CCB Format
-    command to allow 64 Logical Units per Target Device.
+    For Host Adapters that support Extended LUN Format CCBs, issue the Set CCB
+    Format command to allow 32 Logical Units per Target Device.
   */
-  if (HostAdapter->Host64LUNSupport)
+  if (HostAdapter->ExtendedLUNSupport)
     {
-      SetCCBFormatRequest = BusLogic_64LUNFormatCCB;
+      SetCCBFormatRequest = BusLogic_ExtendedLUNFormatCCB;
       if (BusLogic_Command(HostAdapter, BusLogic_SetCCBFormat,
 			   &SetCCBFormatRequest, sizeof(SetCCBFormatRequest),
 			   NULL, 0) < 0)
 	return BusLogic_Failure(HostAdapter, "SET CCB FORMAT");
     }
   /*
-    For PCI Host Adapters being accessed through the PCI compliant I/O
-    Address, disable the ISA compatible I/O Address to avoid detecting the
-    same Host Adapter at both I/O Addresses.
-  */
-  if (HostAdapter->BusType == BusLogic_PCI_Bus)
-    {
-      int Index;
-      for (Index = 0; BusLogic_IO_StandardAddresses[Index] > 0; Index++)
-	if (HostAdapter->IO_Address == BusLogic_IO_StandardAddresses[Index])
-	  break;
-      if (BusLogic_IO_StandardAddresses[Index] == 0)
-	{
-	  ModifyIOAddressRequest = BusLogic_ModifyIO_Disable;
-	  if (BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress,
-			       &ModifyIOAddressRequest,
-			       sizeof(ModifyIOAddressRequest), NULL, 0) < 0)
-	    return BusLogic_Failure(HostAdapter, "MODIFY I/O ADDRESS");
-	}
-    }
-  /*
     Announce Successful Initialization.
   */
-  printk("scsi%d: *** %s Initialized Successfully ***\n",
-	 HostAdapter->HostNumber, HostAdapter->ControllerName);
+Done:
+  if (HostAdapter->HostAdapterInitialized)
+    BusLogic_Warning("*** %s Initialized Successfully ***\n",
+		     HostAdapter, HostAdapter->FullModelName);
+  else BusLogic_Info("*** %s Initialized Successfully ***\n",
+		     HostAdapter, HostAdapter->FullModelName);
+  HostAdapter->HostAdapterInitialized = true;
   /*
     Indicate the Host Adapter Initialization completed successfully.
   */
@@ -1698,18 +2434,21 @@
   */
   BusLogic_Delay(HostAdapter->BusSettleTime);
   /*
+    FlashPoint Host Adapters do not provide for Target Device Inquiry.
+  */
+  if (BusLogic_FlashPointHostAdapterP(HostAdapter)) return true;
+  /*
     Inhibit the Target Devices Inquiry if requested.
   */
-  if (HostAdapter->LocalOptions & BusLogic_InhibitTargetInquiry)
+  if (HostAdapter->LocalOptions.Bits.InhibitTargetInquiry)
     {
-      printk("scsi%d:   Target Device Inquiry Inhibited\n",
-	     HostAdapter->HostNumber);
+      BusLogic_Info("  Target Device Inquiry Inhibited\n", HostAdapter);
       return true;
     }
   /*
-    Issue the Inquire Target Devices command for controllers with firmware
+    Issue the Inquire Target Devices command for host adapters with firmware
     version 4.25 or later, or the Inquire Installed Devices ID 0 to 7 command
-    for older controllers.  This is necessary to force Synchronous Transfer
+    for older host adapters.  This is necessary to force Synchronous Transfer
     Negotiation so that the Inquire Setup Information and Inquire Synchronous
     Period commands will return valid data.  The Inquire Target Devices command
     is preferable to Inquire Installed Devices ID 0 to 7 since it only probes
@@ -1789,31 +2528,31 @@
 	    int SynchronousTransferRate = 100000000 / SynchronousPeriod;
 	    int RoundedSynchronousTransferRate =
 	      (SynchronousTransferRate + 5000) / 10000;
-	    printk("scsi%d:   Target %d: Synchronous at "
-		   "%d.%02d mega-transfers/second, offset %d\n",
-		   HostAdapter->HostNumber, TargetID,
-		   RoundedSynchronousTransferRate / 100,
-		   RoundedSynchronousTransferRate % 100,
-		   HostAdapter->SynchronousValues[TargetID].Offset);
+	    BusLogic_Info("  Target %d: Synchronous at "
+			  "%d.%02d mega-transfers/second, offset %d\n",
+			  HostAdapter, TargetID,
+			  RoundedSynchronousTransferRate / 100,
+			  RoundedSynchronousTransferRate % 100,
+			  HostAdapter->SynchronousValues[TargetID].Offset);
 	  }
 	else if (SynchronousPeriod > 0)
 	  {
 	    int SynchronousTransferRate = 100000000 / SynchronousPeriod;
 	    int RoundedSynchronousTransferRate =
 	      (SynchronousTransferRate + 50000) / 100000;
-	    printk("scsi%d:   Target %d: Synchronous at "
-		   "%d.%01d mega-transfers/second, offset %d\n",
-		   HostAdapter->HostNumber, TargetID,
-		   RoundedSynchronousTransferRate / 10,
-		   RoundedSynchronousTransferRate % 10,
-		   HostAdapter->SynchronousValues[TargetID].Offset);
+	    BusLogic_Info("  Target %d: Synchronous at "
+			  "%d.%01d mega-transfers/second, offset %d\n",
+			  HostAdapter, TargetID,
+			  RoundedSynchronousTransferRate / 10,
+			  RoundedSynchronousTransferRate % 10,
+			  HostAdapter->SynchronousValues[TargetID].Offset);
 	  }
-	else printk("scsi%d:   Target %d: Asynchronous\n",
-		    HostAdapter->HostNumber, TargetID);
+	else BusLogic_Info("  Target %d: Asynchronous\n",
+			   HostAdapter, TargetID);
 	TargetDevicesFound++;
       }
   if (TargetDevicesFound == 0)
-    printk("scsi%d:   No Target Devices Found\n", HostAdapter->HostNumber);
+    BusLogic_Info("  No Target Devices Found\n", HostAdapter);
   /*
     Indicate the Target Device Inquiry completed successfully.
   */
@@ -1839,7 +2578,7 @@
   Host->max_channel = 0;
   Host->unique_id = HostAdapter->IO_Address;
   Host->this_id = HostAdapter->SCSI_ID;
-  Host->can_queue = HostAdapter->MailboxCount;
+  Host->can_queue = HostAdapter->DriverQueueDepth;
   Host->sg_tablesize = HostAdapter->DriverScatterGatherLimit;
   Host->unchecked_isa_dma = HostAdapter->BounceBuffersRequired;
   Host->cmd_per_lun = HostAdapter->UntaggedQueueDepth;
@@ -1872,7 +2611,7 @@
   if (TaggedQueueDepth == 0 && TaggedDeviceCount > 0)
     {
       TaggedQueueDepth =
-	1 + ((HostAdapter->TotalQueueDepth
+	1 + ((HostAdapter->HostAdapterQueueDepth
 	      - UntaggedDeviceCount * UntaggedQueueDepth)
 	     / TaggedDeviceCount);
       if (TaggedQueueDepth > BusLogic_PreferredTaggedQueueDepth)
@@ -1885,9 +2624,7 @@
 	    (HostAdapter->TaggedQueuingPermitted & (1 << Device->id)))
 	  Device->queue_depth = TaggedQueueDepth;
 	else Device->queue_depth = UntaggedQueueDepth;
-	if (BusLogic_GlobalOptions & BusLogic_TraceQueueDepths)
-	  printk("scsi%d: Setting Queue Depth for Target %d to %d\n",
-		 HostAdapter->HostNumber, Device->id, Device->queue_depth);
+	HostAdapter->QueueDepth[Device->id] = Device->queue_depth;
       }
 }
 
@@ -1902,31 +2639,49 @@
 
 int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
 {
-  int BusLogicHostAdapterCount = 0, CommandLineEntryIndex = 0;
-  int AddressProbeIndex = 0;
-  if (BusLogic_ProbeOptions & BusLogic_NoProbe) return 0;
-  BusLogic_InitializeAddressProbeList();
-  while (BusLogic_IO_AddressProbeList[AddressProbeIndex] > 0)
+  int BusLogicHostAdapterCount = 0, CommandLineEntryIndex = 0, ProbeIndex;
+  char *MessageBuffer = NULL;
+  if (BusLogic_ProbeOptions.Bits.NoProbe) return 0;
+  BusLogic_InitializeProbeInfoList();
+  for (ProbeIndex = 0; ProbeIndex < BusLogic_ProbeInfoCount; ProbeIndex++)
     {
+      BusLogic_ProbeInfo_T *ProbeInfo = &BusLogic_ProbeInfoList[ProbeIndex];
       BusLogic_HostAdapter_T HostAdapterPrototype;
       BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype;
       SCSI_Host_T *Host;
+      if (ProbeInfo->IO_Address == 0) continue;
       memset(HostAdapter, 0, sizeof(BusLogic_HostAdapter_T));
-      HostAdapter->IO_Address =
-	BusLogic_IO_AddressProbeList[AddressProbeIndex++];
+      HostAdapter->IO_Address = ProbeInfo->IO_Address;
+      HostAdapter->PCI_Address = ProbeInfo->PCI_Address;
+      HostAdapter->HostAdapterType = ProbeInfo->HostAdapterType;
+      HostAdapter->HostAdapterBusType = ProbeInfo->HostAdapterBusType;
+      HostAdapter->Bus = ProbeInfo->Bus;
+      HostAdapter->Device = ProbeInfo->Device;
+      HostAdapter->IRQ_Channel = ProbeInfo->IRQ_Channel;
+      HostAdapter->AddressCount =
+	BusLogic_HostAdapter_AddressCount[HostAdapter->HostAdapterType];
+      if (MessageBuffer == NULL)
+	MessageBuffer =
+	  scsi_init_malloc(BusLogic_MessageBufferSize, GFP_ATOMIC);
+      if (MessageBuffer == NULL)
+	{
+	  BusLogic_Error("BusLogic: Unable to allocate Message Buffer\n",
+			 HostAdapter);
+	  return BusLogicHostAdapterCount;
+	}
+      HostAdapter->MessageBuffer = MessageBuffer;
       /*
-	Initialize the Command Line Entry field if an explicit I/O Address
-	was specified.
+	If an explicit I/O Address was specified, Initialize the Command Line
+	Entry field and inhibit the check for whether the I/O Address range is
+	already in use.
       */
       if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount &&
 	  BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address ==
 	  HostAdapter->IO_Address)
 	HostAdapter->CommandLineEntry =
 	  &BusLogic_CommandLineEntries[CommandLineEntryIndex++];
-      /*
-	Check whether the I/O Address range is already in use.
-      */
-      if (check_region(HostAdapter->IO_Address, BusLogic_IO_PortCount) < 0)
+      else if (check_region(HostAdapter->IO_Address,
+			    HostAdapter->AddressCount) < 0)
 	continue;
       /*
 	Probe the Host Adapter.  If unsuccessful, abort further initialization.
@@ -1953,7 +2708,7 @@
 	Announce the Driver Version and Date, Author's Name, Copyright Notice,
 	and Contact Address.
       */
-      BusLogic_AnnounceDriver();
+      BusLogic_AnnounceDriver(HostAdapter);
       /*
 	Register usage of the I/O Address range.  From this point onward, any
 	failure will be assumed to be due to a problem with the Host Adapter,
@@ -1962,12 +2717,11 @@
 	released, thereby preventing it from being incorrectly identified as
 	any other type of Host Adapter.
       */
-      request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount,
+      request_region(HostAdapter->IO_Address, HostAdapter->AddressCount,
 		     "BusLogic");
       /*
 	Register the SCSI Host structure.
       */
-      HostTemplate->proc_dir = &BusLogic_ProcDirectoryEntry;
       Host = scsi_register(HostTemplate, sizeof(BusLogic_HostAdapter_T));
       HostAdapter = (BusLogic_HostAdapter_T *) Host->hostdata;
       memcpy(HostAdapter, &HostAdapterPrototype,
@@ -1979,23 +2733,23 @@
 	Add Host Adapter to the end of the list of registered BusLogic
 	Host Adapters.  In order for Command Complete Interrupts to be
 	properly dismissed by BusLogic_InterruptHandler, the Host Adapter
-	must be registered.  This must be done before the IRQ Channel is
-	acquired, and in a shared IRQ Channel environment, must be done
-	before any Command Complete Interrupts occur, since the IRQ Channel
-	may have already been acquired by a previous BusLogic Host Adapter.
+	must be registered.
       */
       BusLogic_RegisterHostAdapter(HostAdapter);
       /*
-	Read the Host Adapter Configuration, Acquire the System Resources
-	necessary to use Host Adapter and initialize the fields in the SCSI
-	Host structure, then Test Interrupts, Create the Mailboxes and CCBs,
-	Initialize the Host Adapter, and finally perform Target Device Inquiry.
+	Read the Host Adapter Configuration, Configure the Host Adapter,
+	Acquire the System Resources necessary to use the Host Adapter,
+	then Test Interrupts, Create the Mailboxes, CCBs, and Target
+	Device Statistics, Initialize the Host Adapter, and finally
+	perform Target Device Inquiry.
       */
       if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) &&
+	  BusLogic_ReportHostAdapterConfiguration(HostAdapter) &&
 	  BusLogic_AcquireResources(HostAdapter) &&
 	  BusLogic_TestInterrupts(HostAdapter) &&
 	  BusLogic_CreateMailboxes(HostAdapter) &&
 	  BusLogic_CreateCCBs(HostAdapter) &&
+	  BusLogic_CreateTargetDeviceStatistics(HostAdapter) &&
 	  BusLogic_InitializeHostAdapter(HostAdapter) &&
 	  BusLogic_TargetDeviceInquiry(HostAdapter))
 	{
@@ -2005,9 +2759,12 @@
 	    Name of the Host Adapter will appear, and initialize the SCSI
 	    Host structure.
 	  */
-	  release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount);
-	  request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount,
-			 HostAdapter->ControllerName);
+	  MessageBuffer = NULL;
+	  release_region(HostAdapter->IO_Address,
+			 HostAdapter->AddressCount);
+	  request_region(HostAdapter->IO_Address,
+			 HostAdapter->AddressCount,
+			 HostAdapter->FullModelName);
 	  BusLogic_InitializeHostStructure(HostAdapter, Host);
 	  BusLogicHostAdapterCount++;
 	}
@@ -2015,12 +2772,14 @@
 	{
 	  /*
 	    An error occurred during Host Adapter Configuration Querying,
-	    Resource Acquisition, Interrupt Testing, CCB Creation, Host Adapter
-	    Initialization, or Target Device Inquiry, so remove Host Adapter
-	    from the list of registered BusLogic Host Adapters, destroy the
-	    CCBs and Mailboxes, Release the System Resources, and Unregister
-	    the SCSI Host.
+	    Host Adapter Configuration, Resource Acquisition, Interrupt
+	    Testing, CCB Creation, Host Adapter Initialization, or Target
+	    Device Inquiry, so remove Host Adapter from the list of
+	    registered BusLogic Host Adapters, destroy the Target Device
+	    Statistics, CCBs, and Mailboxes, Release the System Resources,
+	    and Unregister the SCSI Host.
 	  */
+	  BusLogic_DestroyTargetDeviceStatistics(HostAdapter);
 	  BusLogic_DestroyCCBs(HostAdapter);
 	  BusLogic_DestroyMailboxes(HostAdapter);
 	  BusLogic_ReleaseResources(HostAdapter);
@@ -2028,6 +2787,8 @@
 	  scsi_unregister(Host);
 	}
     }
+  if (MessageBuffer != NULL)
+    scsi_init_free(MessageBuffer, BusLogic_MessageBufferSize);
   return BusLogicHostAdapterCount;
 }
 
@@ -2043,6 +2804,16 @@
   BusLogic_HostAdapter_T *HostAdapter =
     (BusLogic_HostAdapter_T *) Host->hostdata;
   /*
+    FlashPoint Host Adapters must also be released by the FlashPoint
+    SCCB Manager.
+  */
+  if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+    {
+      FlashPoint_ReleaseHostAdapter(HostAdapter->CardHandle);
+      scsi_init_free((char *) HostAdapter->FlashPointInfo,
+		     sizeof(FlashPoint_Info_T));
+    }
+  /*
     Destroy the CCBs and Mailboxes, and release any system resources acquired
     to support Host Adapter.
   */
@@ -2052,7 +2823,7 @@
   /*
     Release usage of the I/O Address range.
   */
-  release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount);
+  release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
   /*
     Remove Host Adapter from the list of registered BusLogic Host Adapters.
   */
@@ -2062,11 +2833,34 @@
 
 
 /*
+  BusLogic_QueueCompletedCCB queues CCB for completion processing.
+*/
+
+static void BusLogic_QueueCompletedCCB(BusLogic_CCB_T *CCB)
+{
+  CCB->Status = BusLogic_CCB_Completed;
+  CCB->Next = NULL;
+  if (BusLogic_FirstCompletedCCB == NULL)
+    {
+      BusLogic_FirstCompletedCCB = CCB;
+      BusLogic_LastCompletedCCB = CCB;
+    }
+  else
+    {
+      BusLogic_LastCompletedCCB->Next = CCB;
+      BusLogic_LastCompletedCCB = CCB;
+    }
+  CCB->HostAdapter->ActiveCommands[CCB->TargetID]--;
+}
+
+
+/*
   BusLogic_ComputeResultCode computes a SCSI Subsystem Result Code from
   the Host Adapter Status and Target Device Status.
 */
 
-static int BusLogic_ComputeResultCode(BusLogic_HostAdapterStatus_T
+static int BusLogic_ComputeResultCode(BusLogic_HostAdapter_T *HostAdapter,
+				      BusLogic_HostAdapterStatus_T
 					HostAdapterStatus,
 				      BusLogic_TargetDeviceStatus_T
 					TargetDeviceStatus)
@@ -2085,9 +2879,10 @@
     case BusLogic_InvalidOutgoingMailboxActionCode:
     case BusLogic_InvalidCommandOperationCode:
     case BusLogic_InvalidCommandParameter:
-      printk("BusLogic: BusLogic Driver Protocol Error 0x%02X\n",
-	     HostAdapterStatus);
-    case BusLogic_DataOverUnderRun:
+      BusLogic_Warning("BusLogic Driver Protocol Error 0x%02X\n",
+		       HostAdapter, HostAdapterStatus);
+    case BusLogic_DataUnderRun:
+    case BusLogic_DataOverRun:
     case BusLogic_UnexpectedBusFree:
     case BusLogic_LinkedCCBhasInvalidLUN:
     case BusLogic_AutoRequestSenseFailed:
@@ -2109,8 +2904,8 @@
       HostStatus = DID_RESET;
       break;
     default:
-      printk("BusLogic: Unknown Host Adapter Status 0x%02X\n",
-	     HostAdapterStatus);
+      BusLogic_Warning("Unknown Host Adapter Status 0x%02X\n",
+		       HostAdapter, HostAdapterStatus);
       HostStatus = DID_ERROR;
       break;
     }
@@ -2119,157 +2914,97 @@
 
 
 /*
-  BusLogic_InterruptHandler handles hardware interrupts from BusLogic Host
-  Adapters.  To simplify handling shared IRQ Channels, all installed BusLogic
-  Host Adapters are scanned whenever any one of them signals a hardware
-  interrupt.
+  BusLogic_ScanIncomingMailboxes scans the Incoming Mailboxes saving any
+  Incoming Mailbox entries for completion processing.
 */
 
-static void BusLogic_InterruptHandler(int IRQ_Channel,
-				      void *DeviceIdentifier,
-				      Registers_T *InterruptRegisters)
+static void BusLogic_ScanIncomingMailboxes(BusLogic_HostAdapter_T *HostAdapter)
 {
-  BusLogic_CCB_T *FirstCompletedCCB = NULL, *LastCompletedCCB = NULL;
-  BusLogic_HostAdapter_T *HostAdapter;
-  int HostAdapterResetRequestedCount = 0;
-  BusLogic_Lock_T Lock;
   /*
-    Iterate over the installed BusLogic Host Adapters accepting any Incoming
-    Mailbox entries and saving the completed CCBs for processing.  This
-    interrupt handler is installed as a fast interrupt, so interrupts are
-    disabled when the interrupt handler is entered.
-  */
-  for (HostAdapter = BusLogic_RegisteredHostAdapters;
-       HostAdapter != NULL;
-       HostAdapter = HostAdapter->Next)
+    Scan through the Incoming Mailboxes in Strict Round Robin fashion, saving
+    any completed CCBs for further processing.  It is essential that for each
+    CCB and SCSI Command issued, command completion processing is performed
+    exactly once.  Therefore, only Incoming Mailboxes with completion code
+    Command Completed Without Error, Command Completed With Error, or Command
+    Aborted At Host Request are saved for completion processing.  When an
+    Incoming Mailbox has a completion code of Aborted Command Not Found, the
+    CCB had already completed or been aborted before the current Abort request
+    was processed, and so completion processing has already occurred and no
+    further action should be taken.
+  */
+  BusLogic_IncomingMailbox_T *NextIncomingMailbox =
+    HostAdapter->NextIncomingMailbox;
+  BusLogic_CompletionCode_T CompletionCode;
+  while ((CompletionCode = NextIncomingMailbox->CompletionCode) !=
+	 BusLogic_IncomingMailboxFree)
     {
-      unsigned char InterruptRegister;
-      /*
-	Acquire exclusive access to Host Adapter.
-      */
-      BusLogic_AcquireHostAdapterLockID(HostAdapter, &Lock);
-      /*
-	Read the Host Adapter Interrupt Register.
-      */
-      InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
-      if (InterruptRegister & BusLogic_InterruptValid)
-	{
-	  /*
-	    Acknowledge the interrupt and reset the Host Adapter
-	    Interrupt Register.
-	  */
-	  BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset);
-	  /*
-	    Process valid SCSI Reset State and Incoming Mailbox Loaded
-	    Interrupts.  Command Complete Interrupts are noted, and
-	    Outgoing Mailbox Available Interrupts are ignored, as they
-	    are never enabled.
-	  */
-	  if (InterruptRegister & BusLogic_SCSIResetState)
-	    {
-	      HostAdapter->HostAdapterResetRequested = true;
-	      HostAdapterResetRequestedCount++;
-	    }
-	  else if (InterruptRegister & BusLogic_IncomingMailboxLoaded)
-	    {
-	      /*
-		Scan through the Incoming Mailboxes in Strict Round Robin
-		fashion, saving any completed CCBs for further processing.
-		It is essential that for each CCB and SCSI Command issued,
-		command completion processing is performed exactly once.
-		Therefore, only Incoming Mailboxes with completion code
-		Command Completed Without Error, Command Completed With
-		Error, or Command Aborted At Host Request are saved for
-		completion processing.  When an Incoming Mailbox has a
-		completion code of Aborted Command Not Found, the CCB had
-		already completed or been aborted before the current Abort
-		request was processed, and so completion processing has
-		already occurred and no further action should be taken.
-	      */
-	      BusLogic_IncomingMailbox_T *NextIncomingMailbox =
-		HostAdapter->NextIncomingMailbox;
-	      BusLogic_CompletionCode_T MailboxCompletionCode;
-	      while ((MailboxCompletionCode =
-		      NextIncomingMailbox->CompletionCode) !=
-		     BusLogic_IncomingMailboxFree)
-		{
-		  BusLogic_CCB_T *CCB =
-		    (BusLogic_CCB_T *) Bus_to_Virtual(NextIncomingMailbox->CCB);
-		  if (MailboxCompletionCode != BusLogic_AbortedCommandNotFound)
-		    if (CCB->Status == BusLogic_CCB_Active ||
-			CCB->Status == BusLogic_CCB_Reset)
-		      {
-			/*
-			  Mark this CCB as completed and add it to the end
-			  of the list of completed CCBs.
-			*/
-			CCB->Status = BusLogic_CCB_Completed;
-			CCB->MailboxCompletionCode = MailboxCompletionCode;
-			CCB->Next = NULL;
-			if (FirstCompletedCCB == NULL)
-			  {
-			    FirstCompletedCCB = CCB;
-			    LastCompletedCCB = CCB;
-			  }
-			else
-			  {
-			    LastCompletedCCB->Next = CCB;
-			    LastCompletedCCB = CCB;
-			  }
-			HostAdapter->ActiveCommandCount[CCB->TargetID]--;
-		      }
-		    else
-		      {
-			/*
-			  If a CCB ever appears in an Incoming Mailbox and
-			  is not marked as status Active or Reset, then there
-			  is most likely a bug in the Host Adapter firmware.
-			*/
-			printk("scsi%d: Illegal CCB #%ld status %d in "
-			       "Incoming Mailbox\n", HostAdapter->HostNumber,
-			       CCB->SerialNumber, CCB->Status);
-		      }
-		  else printk("scsi%d: Aborted CCB #%ld to Target %d "
-			      "Not Found\n", HostAdapter->HostNumber,
-			      CCB->SerialNumber, CCB->TargetID);
-		  NextIncomingMailbox->CompletionCode =
-		    BusLogic_IncomingMailboxFree;
-		  if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
-		    NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
-		}
-	      HostAdapter->NextIncomingMailbox = NextIncomingMailbox;
-	    }
-	  else if (InterruptRegister & BusLogic_CommandComplete)
-	    HostAdapter->HostAdapterCommandCompleted = true;
-	}
-      /*
-	Release exclusive access to Host Adapter.
-      */
-      BusLogic_ReleaseHostAdapterLockID(HostAdapter, &Lock);
+      BusLogic_CCB_T *CCB = (BusLogic_CCB_T *)
+	Bus_to_Virtual(NextIncomingMailbox->CCB);
+      if (CompletionCode != BusLogic_AbortedCommandNotFound)
+	if (CCB->Status == BusLogic_CCB_Active ||
+	    CCB->Status == BusLogic_CCB_Reset)
+	  {
+	    /*
+	      Save the Completion Code for this CCB and queue the CCB
+	      for completion processing.
+	    */
+	    CCB->CompletionCode = CompletionCode;
+	    BusLogic_QueueCompletedCCB(CCB);
+	  }
+	else
+	  {
+	    /*
+	      If a CCB ever appears in an Incoming Mailbox and is not marked as
+	      status Active or Reset, then there is most likely a bug in the
+	      Host Adapter firmware.
+	    */
+	    BusLogic_Warning("Illegal CCB #%ld status %d in "
+			     "Incoming Mailbox\n", HostAdapter,
+			     CCB->SerialNumber, CCB->Status);
+	  }
+      else BusLogic_Warning("Aborted CCB #%ld to Target %d Not Found\n",
+			    HostAdapter, CCB->SerialNumber, CCB->TargetID);
+      NextIncomingMailbox->CompletionCode = BusLogic_IncomingMailboxFree;
+      if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
+	NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
     }
-  /*
-    Iterate over the completed CCBs setting the SCSI Command Result Codes,
-    deallocating the CCBs, and calling the Completion Routines.
-  */
-  while (FirstCompletedCCB != NULL)
+  HostAdapter->NextIncomingMailbox = NextIncomingMailbox;
+}
+
+
+/*
+  BusLogic_ProcessCompletedCCBs iterates over the completed CCBs setting
+  the SCSI Command Result Codes, deallocating the CCBs, and calling the
+  SCSI Subsystem Completion Routines.  Interrupts should already have been
+  disabled by the caller.
+*/
+
+static void BusLogic_ProcessCompletedCCBs(void)
+{
+  static boolean ProcessCompletedCCBsActive = false;
+  if (ProcessCompletedCCBsActive) return;
+  ProcessCompletedCCBsActive = true;
+  while (BusLogic_FirstCompletedCCB != NULL)
     {
-      BusLogic_CCB_T *CCB = FirstCompletedCCB;
+      BusLogic_CCB_T *CCB = BusLogic_FirstCompletedCCB;
       SCSI_Command_T *Command = CCB->Command;
-      FirstCompletedCCB = FirstCompletedCCB->Next;
-      HostAdapter = CCB->HostAdapter;
-      /*
-	Acquire exclusive access to Host Adapter.
-      */
-      BusLogic_AcquireHostAdapterLockID(HostAdapter, &Lock);
+      BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter;
+      BusLogic_FirstCompletedCCB = CCB->Next;
+      if (BusLogic_FirstCompletedCCB == NULL)
+	BusLogic_LastCompletedCCB = NULL;
       /*
 	Process the Completed CCB.
       */
       if (CCB->Opcode == BusLogic_BusDeviceReset)
 	{
 	  int TargetID = CCB->TargetID;
-	  printk("scsi%d: Bus Device Reset CCB #%ld to Target %d Completed\n",
-		 HostAdapter->HostNumber, CCB->SerialNumber, TargetID);
-	  HostAdapter->TotalCommandCount[TargetID] = 0;
+	  BusLogic_Warning("Bus Device Reset CCB #%ld to Target "
+			   "%d Completed\n", HostAdapter,
+			   CCB->SerialNumber, TargetID);
+	  BusLogic_IncrementErrorCounter(
+	    &HostAdapter->TargetDeviceStatistics[TargetID]
+			  .BusDeviceResetsCompleted);
+	  HostAdapter->CommandsSinceReset[TargetID] = 0;
 	  HostAdapter->TaggedQueuingActive[TargetID] = false;
 	  /*
 	    Place CCB back on the Host Adapter's free list.
@@ -2299,7 +3034,7 @@
 	      {
 		Command = CCB->Command;
 		BusLogic_DeallocateCCB(CCB);
-		HostAdapter->ActiveCommandCount[TargetID]--;
+		HostAdapter->ActiveCommands[TargetID]--;
 		Command->result = DID_RESET << 16;
 		Command->scsi_done(Command);
 	      }
@@ -2308,47 +3043,61 @@
       else
 	{
 	  /*
-	    Translate the Mailbox Completion Code, Host Adapter Status, and
-	    Target Device Status into a SCSI Subsystem Result Code.
+	    Translate the Completion Code, Host Adapter Status, and Target
+	    Device Status into a SCSI Subsystem Result Code.
 	  */
-	  switch (CCB->MailboxCompletionCode)
+	  switch (CCB->CompletionCode)
 	    {
 	    case BusLogic_IncomingMailboxFree:
 	    case BusLogic_AbortedCommandNotFound:
-	      printk("scsi%d: CCB #%ld to Target %d Impossible State\n",
-		     HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+	    case BusLogic_InvalidCCB:
+	      BusLogic_Warning("CCB #%ld to Target %d Impossible State\n",
+			       HostAdapter, CCB->SerialNumber, CCB->TargetID);
 	      break;
 	    case BusLogic_CommandCompletedWithoutError:
+	      HostAdapter->TargetDeviceStatistics[CCB->TargetID]
+			   .CommandsCompleted++;
 	      HostAdapter->CommandSuccessfulFlag[CCB->TargetID] = true;
 	      Command->result = DID_OK << 16;
 	      break;
 	    case BusLogic_CommandAbortedAtHostRequest:
-	      printk("scsi%d: CCB #%ld to Target %d Aborted\n",
-		     HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+	      BusLogic_Warning("CCB #%ld to Target %d Aborted\n",
+			       HostAdapter, CCB->SerialNumber, CCB->TargetID);
+	      BusLogic_IncrementErrorCounter(
+		&HostAdapter->TargetDeviceStatistics[CCB->TargetID]
+			      .CommandAbortsCompleted);
 	      Command->result = DID_ABORT << 16;
 	      break;
 	    case BusLogic_CommandCompletedWithError:
 	      Command->result =
-		BusLogic_ComputeResultCode(CCB->HostAdapterStatus,
+		BusLogic_ComputeResultCode(HostAdapter,
+					   CCB->HostAdapterStatus,
 					   CCB->TargetDeviceStatus);
-	      if (BusLogic_GlobalOptions & BusLogic_TraceErrors)
-		if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout)
-		  {
-		    int i;
-		    printk("scsi%d: CCB #%ld Target %d: Result %X "
-			   "Host Adapter Status %02X Target Status %02X\n",
-			   HostAdapter->HostNumber, CCB->SerialNumber,
-			   CCB->TargetID, Command->result,
-			   CCB->HostAdapterStatus, CCB->TargetDeviceStatus);
-		    printk("scsi%d: CDB   ", HostAdapter->HostNumber);
-		    for (i = 0; i < CCB->CDB_Length; i++)
-		      printk(" %02X", CCB->CDB[i]);
-		    printk("\n");
-		    printk("scsi%d: Sense ", HostAdapter->HostNumber);
-		    for (i = 0; i < CCB->SenseDataLength; i++)
-		      printk(" %02X", Command->sense_buffer[i]);
-		    printk("\n");
-		  }
+	      if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout)
+		{
+		  HostAdapter->TargetDeviceStatistics[CCB->TargetID]
+			       .CommandsCompleted++;
+		  if (BusLogic_GlobalOptions.Bits.TraceErrors)
+		      {
+			int i;
+			BusLogic_Notice("CCB #%ld Target %d: Result %X Host "
+					"Adapter Status %02X "
+					"Target Status %02X\n",
+					HostAdapter, CCB->SerialNumber,
+					CCB->TargetID, Command->result,
+					CCB->HostAdapterStatus,
+					CCB->TargetDeviceStatus);
+			BusLogic_Notice("CDB   ", HostAdapter);
+			for (i = 0; i < CCB->CDB_Length; i++)
+			  BusLogic_Notice(" %02X", HostAdapter, CCB->CDB[i]);
+			BusLogic_Notice("\n", HostAdapter);
+			BusLogic_Notice("Sense ", HostAdapter);
+			for (i = 0; i < CCB->SenseDataLength; i++)
+			  BusLogic_Notice(" %02X", HostAdapter,
+					  Command->sense_buffer[i]);
+			BusLogic_Notice("\n", HostAdapter);
+		      }
+		}
 	      break;
 	    }
 	  /*
@@ -2360,31 +3109,117 @@
 	  */
 	  Command->scsi_done(Command);
 	}
+    }
+  ProcessCompletedCCBsActive = false;
+}
+
+
+/*
+  BusLogic_InterruptHandler handles hardware interrupts from BusLogic Host
+  Adapters.
+*/
+
+static void BusLogic_InterruptHandler(int IRQ_Channel,
+				      void *DeviceIdentifier,
+				      Registers_T *InterruptRegisters)
+{
+  BusLogic_HostAdapter_T *FirstHostAdapter =
+    BusLogic_RegisteredHostAdapters[IRQ_Channel];
+  boolean HostAdapterResetRequested = false;
+  BusLogic_HostAdapter_T *HostAdapter;
+  BusLogic_Lock_T Lock;
+  /*
+    Iterate over the installed BusLogic Host Adapters accepting any Incoming
+    Mailbox entries and saving the completed CCBs for processing.  This
+    interrupt handler is installed as a fast interrupt, so interrupts are
+    disabled when the interrupt handler is entered.
+  */
+  for (HostAdapter = FirstHostAdapter;
+       HostAdapter != NULL;
+       HostAdapter = HostAdapter->Next)
+    {
+      /*
+	Acquire exclusive access to Host Adapter.
+      */
+      BusLogic_AcquireHostAdapterLockID(HostAdapter, &Lock);
+      /*
+	Handle Interrupts appropriately for each Host Adapter type.
+      */
+      if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+	{
+	  BusLogic_InterruptRegister_T InterruptRegister;
+	  /*
+	    Read the Host Adapter Interrupt Register.
+	  */
+	  InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+	  if (InterruptRegister.Bits.InterruptValid)
+	    {
+	      /*
+		Acknowledge the interrupt and reset the Host Adapter
+		Interrupt Register.
+	      */
+	      BusLogic_InterruptReset(HostAdapter);
+	      /*
+		Process valid External SCSI Bus Reset and Incoming Mailbox
+		Loaded Interrupts.  Command Complete Interrupts are noted,
+		and Outgoing Mailbox Available Interrupts are ignored, as
+		they are never enabled.
+	      */
+	      if (InterruptRegister.Bits.ExternalBusReset)
+		{
+		  HostAdapter->HostAdapterResetRequested = true;
+		  HostAdapterResetRequested = true;
+		}
+	      else if (InterruptRegister.Bits.IncomingMailboxLoaded)
+		BusLogic_ScanIncomingMailboxes(HostAdapter);
+	      else if (InterruptRegister.Bits.CommandComplete)
+		HostAdapter->HostAdapterCommandCompleted = true;
+	    }
+	}
+      else
+	{
+	  /*
+	    Check if there is a pending interrupt for this Host Adapter.
+	  */
+	  if (FlashPoint_InterruptPending(HostAdapter->CardHandle))
+	    if (FlashPoint_HandleInterrupt(HostAdapter->CardHandle)
+		== FlashPoint_ExternalBusReset)
+	      {
+		HostAdapter->HostAdapterResetRequested = true;
+		HostAdapterResetRequested = true;
+	      }
+	}
       /*
 	Release exclusive access to Host Adapter.
       */
       BusLogic_ReleaseHostAdapterLockID(HostAdapter, &Lock);
     }
   /*
-    Iterate over the Host Adapters performing any requested Host Adapter Resets.
+    Process any completed CCBs.
   */
-  if (HostAdapterResetRequestedCount == 0) return;
-  for (HostAdapter = BusLogic_RegisteredHostAdapters;
-       HostAdapter != NULL;
-       HostAdapter = HostAdapter->Next)
-    if (HostAdapter->HostAdapterResetRequested)
-      {
-	BusLogic_ResetHostAdapter(HostAdapter, NULL, 0);
-	HostAdapter->HostAdapterResetRequested = false;
-	scsi_mark_host_reset(HostAdapter->SCSI_Host);
-      }
+  if (BusLogic_FirstCompletedCCB != NULL)
+    BusLogic_ProcessCompletedCCBs();
+  /*
+    Iterate over the Host Adapters performing any requested
+    Host Adapter Resets.
+  */
+  if (HostAdapterResetRequested)
+    for (HostAdapter = FirstHostAdapter;
+	 HostAdapter != NULL;
+	 HostAdapter = HostAdapter->Next)
+      if (HostAdapter->HostAdapterResetRequested)
+	{
+	  BusLogic_ResetHostAdapter(HostAdapter, NULL, 0);
+	  HostAdapter->HostAdapterResetRequested = false;
+	  scsi_mark_host_reset(HostAdapter->SCSI_Host);
+	}
 }
 
 
 /*
   BusLogic_WriteOutgoingMailbox places CCB and Action Code into an Outgoing
-  Mailbox for execution by Host Adapter.  The Host Adapter's Lock should have
-  already been acquired by the caller.
+  Mailbox for execution by Host Adapter.  The Host Adapter's Lock should
+  already have been acquired by the caller.
 */
 
 static boolean BusLogic_WriteOutgoingMailbox(BusLogic_HostAdapter_T
@@ -2409,7 +3244,12 @@
 	NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
       HostAdapter->NextOutgoingMailbox = NextOutgoingMailbox;
       if (ActionCode == BusLogic_MailboxStartCommand)
-	HostAdapter->ActiveCommandCount[CCB->TargetID]++;
+	{
+	  HostAdapter->ActiveCommands[CCB->TargetID]++;
+	  if (CCB->Opcode != BusLogic_BusDeviceReset)
+	    HostAdapter->TargetDeviceStatistics[CCB->TargetID]
+			 .CommandsAttempted++;
+	}
       return true;
     }
   return false;
@@ -2426,6 +3266,8 @@
 {
   BusLogic_HostAdapter_T *HostAdapter =
     (BusLogic_HostAdapter_T *) Command->host->hostdata;
+  BusLogic_TargetDeviceStatistics_T *TargetDeviceStatistics =
+    HostAdapter->TargetDeviceStatistics;
   unsigned char *CDB = Command->cmnd;
   int CDB_Length = Command->cmd_len;
   int TargetID = Command->target;
@@ -2453,8 +3295,8 @@
   /*
     Allocate a CCB from the Host Adapter's free list.  In the unlikely event
     that there are none available and memory allocation fails, wait 1 second
-    and try again.  If that fails, the Host Adapter is probably hung so we
-    signal an error as a Host Adapter Hard Reset should be initiated soon.
+    and try again.  If that fails, the Host Adapter is probably hung so signal
+    an error as a Host Adapter Hard Reset should be initiated soon.
   */
   CCB = BusLogic_AllocateCCB(HostAdapter);
   if (CCB == NULL)
@@ -2483,7 +3325,9 @@
       int Segment;
       CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather;
       CCB->DataLength = SegmentCount * sizeof(BusLogic_ScatterGatherSegment_T);
-      CCB->DataPointer = Virtual_to_Bus(CCB->ScatterGatherList);
+      if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+	CCB->DataPointer = Virtual_to_Bus(CCB->ScatterGatherList);
+      else CCB->DataPointer = (BusLogic_BusAddress_T) CCB->ScatterGatherList;
       for (Segment = 0; Segment < SegmentCount; Segment++)
 	{
 	  CCB->ScatterGatherList[Segment].SegmentByteCount =
@@ -2497,10 +3341,22 @@
     case READ_6:
     case READ_10:
       CCB->DataDirection = BusLogic_DataInLengthChecked;
+      TargetDeviceStatistics[TargetID].ReadCommands++;
+      BusLogic_IncrementByteCounter(
+	&TargetDeviceStatistics[TargetID].TotalBytesRead, BufferLength);
+      BusLogic_IncrementSizeBucket(
+	TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets,
+	BufferLength);
       break;
     case WRITE_6:
     case WRITE_10:
       CCB->DataDirection = BusLogic_DataOutLengthChecked;
+      TargetDeviceStatistics[TargetID].WriteCommands++;
+      BusLogic_IncrementByteCounter(
+	&TargetDeviceStatistics[TargetID].TotalBytesWritten, BufferLength);
+      BusLogic_IncrementSizeBucket(
+	TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets,
+	BufferLength);
       break;
     default:
       CCB->DataDirection = BusLogic_UncheckedDataTransfer;
@@ -2512,17 +3368,8 @@
   CCB->TargetDeviceStatus = 0;
   CCB->TargetID = TargetID;
   CCB->LogicalUnit = LogicalUnit;
-  /*
-    For Host Adapters that support it, 64 LUN Format CCBs are used to allow
-    64 Logical Units per Target, and this requires setting the overloaded
-    TagEnable field to Logical Unit bit 5.
-  */
-  if (HostAdapter->Host64LUNSupport)
-    {
-      CCB->TagEnable = LogicalUnit >> 5;
-      CCB->TagEnable64LUN = false;
-    }
-  else CCB->TagEnable = false;
+  CCB->TagEnable = false;
+  CCB->LegacyTagEnable = false;
   /*
     BusLogic recommends that after a Reset the first couple of commands that
     are sent to a Target Device be sent in a non Tagged Queue fashion so that
@@ -2537,16 +3384,18 @@
     necessary to wait until there are no pending commands for a target device
     before queuing tagged commands.
   */
-  if (HostAdapter->TotalCommandCount[TargetID]++ >=
-        BusLogic_MaxTaggedQueueDepth &&
+  HostAdapter->TaggedQueuingSupported[TargetID] =
+    Command->device->tagged_supported;
+  if (HostAdapter->CommandsSinceReset[TargetID]++ >=
+	BusLogic_MaxTaggedQueueDepth &&
       !HostAdapter->TaggedQueuingActive[TargetID] &&
-      HostAdapter->ActiveCommandCount[TargetID] == 0 &&
-      (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)) &&
-      Command->device->tagged_supported)
+      HostAdapter->ActiveCommands[TargetID] == 0 &&
+      HostAdapter->TaggedQueuingSupported[TargetID] &&
+      (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)))
     {
       HostAdapter->TaggedQueuingActive[TargetID] = true;
-      printk("scsi%d: Tagged Queuing now active for Target %d\n",
-	     HostAdapter->HostNumber, TargetID);
+      BusLogic_Notice("Tagged Queuing now active for Target %d\n",
+		      HostAdapter, TargetID);
     }
   if (HostAdapter->TaggedQueuingActive[TargetID])
     {
@@ -2558,58 +3407,77 @@
 	write nearer the head position continue to arrive without interruption.
 	Therefore, for each Target Device this driver keeps track of the last
 	time either the queue was empty or an Ordered Queue Tag was issued.  If
-	more than 5 seconds (one third of the 15 second disk timeout) have
+	more than 3 seconds (one fifth of the 15 second disk timeout) have
 	elapsed since this last sequence point, this command will be issued
 	with an Ordered Queue Tag rather than a Simple Queue Tag, which forces
 	the Target Device to complete all previously queued commands before
 	this command may be executed.
       */
-      if (HostAdapter->ActiveCommandCount[TargetID] == 0)
+      if (HostAdapter->ActiveCommands[TargetID] == 0)
 	HostAdapter->LastSequencePoint[TargetID] = jiffies;
-      else if (jiffies - HostAdapter->LastSequencePoint[TargetID] > 5*HZ)
+      else if (jiffies - HostAdapter->LastSequencePoint[TargetID] > 3*HZ)
 	{
 	  HostAdapter->LastSequencePoint[TargetID] = jiffies;
 	  QueueTag = BusLogic_OrderedQueueTag;
 	}
-      if (HostAdapter->Host64LUNSupport)
+      if (HostAdapter->ExtendedLUNSupport)
 	{
-	  CCB->TagEnable64LUN = true;
-	  CCB->QueueTag64LUN = QueueTag;
+	  CCB->TagEnable = true;
+	  CCB->QueueTag = QueueTag;
 	}
       else
 	{
-	  CCB->TagEnable = true;
-	  CCB->QueueTag = QueueTag;
+	  CCB->LegacyTagEnable = true;
+	  CCB->LegacyQueueTag = QueueTag;
 	}
     }
   memcpy(CCB->CDB, CDB, CDB_Length);
   CCB->SenseDataPointer = Virtual_to_Bus(&Command->sense_buffer);
   CCB->Command = Command;
   Command->scsi_done = CompletionRoutine;
-  /*
-    Place the CCB in an Outgoing Mailbox.  The higher levels of the SCSI
-    Subsystem should not attempt to queue more commands than can be placed in
-    Outgoing Mailboxes, so there should always be one free.  In the unlikely
-    event that there are none available, wait 1 second and try again.  If
-    that fails, the Host Adapter is probably hung so we signal an error as
-    a Host Adapter Hard Reset should be initiated soon.
-  */
-  if (!BusLogic_WriteOutgoingMailbox(HostAdapter,
-				     BusLogic_MailboxStartCommand, CCB))
+  if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
     {
-      printk("scsi%d: cannot write Outgoing Mailbox - Pausing for 1 second\n",
-	     HostAdapter->HostNumber);
-      BusLogic_Delay(1);
-      if (!BusLogic_WriteOutgoingMailbox(HostAdapter,
-					 BusLogic_MailboxStartCommand, CCB))
+      /*
+	Place the CCB in an Outgoing Mailbox.  The higher levels of the SCSI
+	Subsystem should not attempt to queue more commands than can be placed
+	in Outgoing Mailboxes, so there should always be one free.  In the
+	unlikely event that there are none available, wait 1 second and try
+	again.  If that fails, the Host Adapter is probably hung so signal an
+	error as a Host Adapter Hard Reset should be initiated soon.
+      */
+      if (!BusLogic_WriteOutgoingMailbox(
+	     HostAdapter, BusLogic_MailboxStartCommand, CCB))
 	{
-	  printk("scsi%d: still cannot write Outgoing Mailbox - "
-		 "Host Adapter Dead?\n", HostAdapter->HostNumber);
-	  BusLogic_DeallocateCCB(CCB);
-	  Command->result = DID_ERROR << 16;
-	  Command->scsi_done(Command);
+	  BusLogic_Warning("Unable to write Outgoing Mailbox - "
+			   "Pausing for 1 second\n", HostAdapter);
+	  BusLogic_Delay(1);
+	  if (!BusLogic_WriteOutgoingMailbox(
+		 HostAdapter, BusLogic_MailboxStartCommand, CCB))
+	    {
+	      BusLogic_Warning("Still unable to write Outgoing Mailbox - "
+			       "Host Adapter Dead?\n", HostAdapter);
+	      BusLogic_DeallocateCCB(CCB);
+	      Command->result = DID_ERROR << 16;
+	      Command->scsi_done(Command);
+	    }
 	}
     }
+  else
+    {
+      /*
+	Call the FlashPoint SCCB Manager to start execution of the CCB.
+      */
+      CCB->Status = BusLogic_CCB_Active;
+      HostAdapter->ActiveCommands[TargetID]++;
+      TargetDeviceStatistics[TargetID].CommandsAttempted++;
+      FlashPoint_StartCCB(HostAdapter->CardHandle, CCB);
+      /*
+	The Command may have already completed and BusLogic_QueueCompletedCCB
+	been called, or it may still be pending.
+      */
+      if (CCB->Status == BusLogic_CCB_Completed)
+	BusLogic_ProcessCompletedCCBs();
+    }
   /*
     Release exclusive access to Host Adapter.
   */
@@ -2631,6 +3499,8 @@
   BusLogic_Lock_T Lock;
   BusLogic_CCB_T *CCB;
   int Result;
+  BusLogic_IncrementErrorCounter(
+    &HostAdapter->TargetDeviceStatistics[TargetID].CommandAbortsRequested);
   /*
     Acquire exclusive access to Host Adapter.
   */
@@ -2640,8 +3510,8 @@
   */
   if (Command->serial_number != Command->serial_number_at_timeout)
     {
-      printk("scsi%d: Unable to Abort Command to Target %d - "
-	     "Already Completed\n", HostAdapter->HostNumber, TargetID);
+      BusLogic_Warning("Unable to Abort Command to Target %d - "
+		       "Already Completed\n", HostAdapter, TargetID);
       Result = SCSI_ABORT_NOT_RUNNING;
       goto Done;
     }
@@ -2653,56 +3523,84 @@
     if (CCB->Command == Command) break;
   if (CCB == NULL)
     {
-      printk("scsi%d: Unable to Abort Command to Target %d - No CCB Found\n",
-	     HostAdapter->HostNumber, TargetID);
+      BusLogic_Warning("Unable to Abort Command to Target %d - "
+		       "No CCB Found\n", HostAdapter, TargetID);
       Result = SCSI_ABORT_NOT_RUNNING;
       goto Done;
     }
   else if (CCB->Status == BusLogic_CCB_Completed)
     {
-      printk("scsi%d: Unable to Abort Command to Target %d - CCB Completed\n",
-	     HostAdapter->HostNumber, TargetID);
+      BusLogic_Warning("Unable to Abort Command to Target %d - "
+		       "CCB Completed\n", HostAdapter, TargetID);
       Result = SCSI_ABORT_NOT_RUNNING;
       goto Done;
     }
   else if (CCB->Status == BusLogic_CCB_Reset)
     {
-      printk("scsi%d: Unable to Abort Command to Target %d - CCB Reset\n",
-	     HostAdapter->HostNumber, TargetID);
-      Result = SCSI_ABORT_NOT_RUNNING;
+      BusLogic_Warning("Unable to Abort Command to Target %d - "
+		       "CCB Reset\n", HostAdapter, TargetID);
+      Result = SCSI_ABORT_PENDING;
       goto Done;
     }
-  /*
-    Attempt to Abort this CCB.  Firmware versions prior to 5.xx do not generate
-    Abort Tag messages, but only generate the non-tagged Abort message.  Since
-    non-tagged commands are not sent by the Host Adapter until the queue of
-    outstanding tagged commands has completed, and the Abort message is treated
-    as a non-tagged command, it is effectively impossible to abort commands
-    when Tagged Queuing is active.  Firmware version 5.xx does generate Abort
-    Tag messages, so it is possible to abort commands when Tagged Queuing is
-    active.
-  */
-  if (HostAdapter->TaggedQueuingActive[TargetID] &&
-      HostAdapter->FirmwareVersion[0] < '5')
-    {
-      printk("scsi%d: Unable to Abort CCB #%ld to Target %d - "
-	     "Abort Tag Not Supported\n", HostAdapter->HostNumber,
-	     CCB->SerialNumber, TargetID);
-      Result = SCSI_ABORT_SNOOZE;
-    }
-  else if (BusLogic_WriteOutgoingMailbox(HostAdapter,
-					 BusLogic_MailboxAbortCommand, CCB))
+  if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
     {
-      printk("scsi%d: Aborting CCB #%ld to Target %d\n",
-	     HostAdapter->HostNumber, CCB->SerialNumber, TargetID);
-      Result = SCSI_ABORT_PENDING;
+      /*
+	Attempt to Abort this CCB.  MultiMaster Firmware versions prior to 5.xx
+	do not generate Abort Tag messages, but only generate the non-tagged
+	Abort message.  Since non-tagged commands are not sent by the Host
+	Adapter until the queue of outstanding tagged commands has completed,
+	and the Abort message is treated as a non-tagged command, it is
+	effectively impossible to abort commands when Tagged Queuing is active.
+	Firmware version 5.xx does generate Abort Tag messages, so it is
+	possible to abort commands when Tagged Queuing is active.
+      */
+      if (HostAdapter->TaggedQueuingActive[TargetID] &&
+	  HostAdapter->FirmwareVersion[0] < '5')
+	{
+	  BusLogic_Warning("Unable to Abort CCB #%ld to Target %d - "
+			   "Abort Tag Not Supported\n",
+			   HostAdapter, CCB->SerialNumber, TargetID);
+	  Result = SCSI_ABORT_SNOOZE;
+	}
+      else if (BusLogic_WriteOutgoingMailbox(
+		 HostAdapter, BusLogic_MailboxAbortCommand, CCB))
+	{
+	  BusLogic_Warning("Aborting CCB #%ld to Target %d\n",
+			   HostAdapter, CCB->SerialNumber, TargetID);
+	  BusLogic_IncrementErrorCounter(
+	    &HostAdapter->TargetDeviceStatistics[TargetID]
+			  .CommandAbortsAttempted);
+	  Result = SCSI_ABORT_PENDING;
+	}
+      else
+	{
+	  BusLogic_Warning("Unable to Abort CCB #%ld to Target %d - "
+			   "No Outgoing Mailboxes\n",
+			    HostAdapter, CCB->SerialNumber, TargetID);
+	  Result = SCSI_ABORT_BUSY;
+	}
     }
   else
     {
-      printk("scsi%d: Unable to Abort CCB #%ld to Target %d - "
-	     "No Outgoing Mailboxes\n", HostAdapter->HostNumber,
-	     CCB->SerialNumber, TargetID);
-      Result = SCSI_ABORT_BUSY;
+      /*
+	Call the FlashPoint SCCB Manager to abort execution of the CCB.
+      */
+      BusLogic_Warning("Aborting CCB #%ld to Target %d\n",
+		       HostAdapter, CCB->SerialNumber, TargetID);
+      BusLogic_IncrementErrorCounter(
+	&HostAdapter->TargetDeviceStatistics[TargetID].CommandAbortsAttempted);
+      FlashPoint_AbortCCB(HostAdapter->CardHandle, CCB);
+      /*
+	The Abort may have already been completed and
+	BusLogic_QueueCompletedCCB been called, or it
+	may still be pending.
+      */
+      Result = SCSI_ABORT_PENDING;
+      if (CCB->Status == BusLogic_CCB_Completed)
+	{
+	  BusLogic_ProcessCompletedCCBs();
+	  Result = SCSI_ABORT_SUCCESS;
+	}
     }
   /*
     Release exclusive access to Host Adapter.
@@ -2725,6 +3623,11 @@
   BusLogic_Lock_T Lock;
   BusLogic_CCB_T *CCB;
   int TargetID, Result;
+  if (Command == NULL)
+    BusLogic_IncrementErrorCounter(&HostAdapter->ExternalHostAdapterResets);
+  else BusLogic_IncrementErrorCounter(
+	 &HostAdapter->TargetDeviceStatistics[Command->target]
+		       .HostAdapterResetsRequested);
   /*
     Acquire exclusive access to Host Adapter.
   */
@@ -2738,9 +3641,9 @@
       TargetID = Command->target;
       if (Command->serial_number != Command->serial_number_at_timeout)
 	{
-	  printk("scsi%d: Unable to Reset Command to Target %d - "
-		 "Already Completed or Reset\n",
-		 HostAdapter->HostNumber, TargetID);
+	  BusLogic_Warning("Unable to Reset Command to Target %d - "
+			   "Already Completed or Reset\n",
+			   HostAdapter, TargetID);
 	  Result = SCSI_RESET_NOT_RUNNING;
 	  goto Done;
       }
@@ -2748,44 +3651,53 @@
 	if (CCB->Command == Command) break;
       if (CCB == NULL)
 	{
-	  printk("scsi%d: Unable to Reset Command to Target %d - "
-		 "No CCB Found\n", HostAdapter->HostNumber, TargetID);
+	  BusLogic_Warning("Unable to Reset Command to Target %d - "
+			   "No CCB Found\n", HostAdapter, TargetID);
 	  Result = SCSI_RESET_NOT_RUNNING;
 	  goto Done;
 	}
       else if (CCB->Status == BusLogic_CCB_Completed)
 	{
-	  printk("scsi%d: Unable to Reset Command to Target %d - "
-		 "CCB Completed\n", HostAdapter->HostNumber, TargetID);
+	  BusLogic_Warning("Unable to Reset Command to Target %d - "
+			   "CCB Completed\n", HostAdapter, TargetID);
 	  Result = SCSI_RESET_NOT_RUNNING;
 	  goto Done;
 	}
       else if (CCB->Status == BusLogic_CCB_Reset &&
 	       HostAdapter->BusDeviceResetPendingCCB[TargetID] == NULL)
 	{
-	  printk("scsi%d: Unable to Reset Command to Target %d - "
-		 "Reset Pending\n", HostAdapter->HostNumber, TargetID);
+	  BusLogic_Warning("Unable to Reset Command to Target %d - "
+			   "Reset Pending\n", HostAdapter, TargetID);
 	  Result = SCSI_RESET_PENDING;
 	  goto Done;
 	}
     }
   if (Command == NULL)
-    printk("scsi%d: Resetting %s due to SCSI Reset State Interrupt\n",
-	   HostAdapter->HostNumber, HostAdapter->ControllerName);
-  else printk("scsi%d: Resetting %s due to Target %d\n",
-	      HostAdapter->HostNumber, HostAdapter->ControllerName,
-	      Command->target);
+    BusLogic_Warning("Resetting %s due to External SCSI Bus Reset\n",
+		     HostAdapter, HostAdapter->FullModelName);
+  else
+    {
+      BusLogic_Warning("Resetting %s due to Target %d\n", HostAdapter,
+		       HostAdapter->FullModelName, Command->target);
+      BusLogic_IncrementErrorCounter(
+	&HostAdapter->TargetDeviceStatistics[Command->target]
+		      .HostAdapterResetsAttempted);
+    }
   /*
     Attempt to Reset and Reinitialize the Host Adapter.
   */
   if (!(BusLogic_HardResetHostAdapter(HostAdapter) &&
 	BusLogic_InitializeHostAdapter(HostAdapter)))
     {
-      printk("scsi%d: Resetting %s Failed\n",
-	      HostAdapter->HostNumber, HostAdapter->ControllerName);
+      BusLogic_Error("Resetting %s Failed\n", HostAdapter,
+		     HostAdapter->FullModelName);
       Result = SCSI_RESET_ERROR;
       goto Done;
     }
+  if (Command != NULL)
+    BusLogic_IncrementErrorCounter(
+      &HostAdapter->TargetDeviceStatistics[Command->target]
+		    .HostAdapterResetsCompleted);
   /*
     Mark all currently executing CCBs as having been Reset.
   */
@@ -2848,9 +3760,11 @@
 				       unsigned int ResetFlags)
 {
   int TargetID = Command->target;
+  BusLogic_CCB_T *CCB, *XCCB;
   BusLogic_Lock_T Lock;
-  BusLogic_CCB_T *CCB;
   int Result = -1;
+  BusLogic_IncrementErrorCounter(
+    &HostAdapter->TargetDeviceStatistics[TargetID].BusDeviceResetsRequested);
   /*
     Acquire exclusive access to Host Adapter.
   */
@@ -2863,8 +3777,8 @@
     {
       if (Command->serial_number != Command->serial_number_at_timeout)
 	{
-	  printk("scsi%d: Unable to Reset Command to Target %d - "
-		 "Already Completed\n", HostAdapter->HostNumber, TargetID);
+	  BusLogic_Warning("Unable to Reset Command to Target %d - "
+			   "Already Completed\n", HostAdapter, TargetID);
 	  Result = SCSI_RESET_NOT_RUNNING;
 	  goto Done;
 	}
@@ -2872,29 +3786,29 @@
 	if (CCB->Command == Command) break;
       if (CCB == NULL)
 	{
-	  printk("scsi%d: Unable to Reset Command to Target %d - "
-		 "No CCB Found\n", HostAdapter->HostNumber, TargetID);
+	  BusLogic_Warning("Unable to Reset Command to Target %d - "
+			   "No CCB Found\n", HostAdapter, TargetID);
 	  Result = SCSI_RESET_NOT_RUNNING;
 	  goto Done;
 	}
       else if (CCB->Status == BusLogic_CCB_Completed)
 	{
-	  printk("scsi%d: Unable to Reset Command to Target %d - "
-		 "CCB Completed\n", HostAdapter->HostNumber, TargetID);
+	  BusLogic_Warning("Unable to Reset Command to Target %d - "
+			   "CCB Completed\n", HostAdapter, TargetID);
 	  Result = SCSI_RESET_NOT_RUNNING;
 	  goto Done;
 	}
       else if (CCB->Status == BusLogic_CCB_Reset)
 	{
-	  printk("scsi%d: Unable to Reset Command to Target %d - "
-		 "Reset Pending\n", HostAdapter->HostNumber, TargetID);
+	  BusLogic_Warning("Unable to Reset Command to Target %d - "
+			   "Reset Pending\n", HostAdapter, TargetID);
 	  Result = SCSI_RESET_PENDING;
 	  goto Done;
 	}
       else if (HostAdapter->BusDeviceResetPendingCCB[TargetID] != NULL)
 	{
-	  printk("scsi%d: Bus Device Reset already pending to Target %d\n",
-		 HostAdapter->HostNumber, TargetID);
+	  BusLogic_Warning("Bus Device Reset already pending to Target %d\n",
+			   HostAdapter, TargetID);
 	  goto Done;
 	}
     }
@@ -2909,23 +3823,26 @@
       {
 	Command->reset_chain = CCB->Command;
 	CCB->Command = Command;
-	printk("scsi%d: Unable to Reset Command to Target %d - "
-	       "Reset Pending\n", HostAdapter->HostNumber, TargetID);
+	BusLogic_Warning("Unable to Reset Command to Target %d - "
+			 "Reset Pending\n", HostAdapter, TargetID);
 	Result = SCSI_RESET_PENDING;
 	goto Done;
       }
-  /*
-    Firmware versions prior to 5.xx treat a Bus Device Reset as a non-tagged
-    command.  Since non-tagged commands are not sent by the Host Adapter until
-    the queue of outstanding tagged commands has completed, it is effectively
-    impossible to send a Bus Device Reset while there are tagged commands
-    outstanding.  Therefore, in that case a full Host Adapter Hard Reset and
-    SCSI Bus Reset must be done.
-  */
-  if (HostAdapter->TaggedQueuingActive[TargetID] &&
-      HostAdapter->ActiveCommandCount[TargetID] > 0 &&
-      HostAdapter->FirmwareVersion[0] < '5')
-    goto Done;
+  if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+    {
+      /*
+	MultiMaster Firmware versions prior to 5.xx treat a Bus Device Reset as
+	a non-tagged command.  Since non-tagged commands are not sent by the
+	Host Adapter until the queue of outstanding tagged commands has
+	completed, it is effectively impossible to send a Bus Device Reset
+	while there are tagged commands outstanding.  Therefore, in that case a
+	full Host Adapter Hard Reset and SCSI Bus Reset must be done.
+      */
+      if (HostAdapter->TaggedQueuingActive[TargetID] &&
+	  HostAdapter->ActiveCommands[TargetID] > 0 &&
+	  HostAdapter->FirmwareVersion[0] < '5')
+	goto Done;
+    }
   /*
     Allocate a CCB from the Host Adapter's free list.  In the unlikely event
     that there are none available and memory allocation fails, attempt a full
@@ -2933,8 +3850,8 @@
   */
   CCB = BusLogic_AllocateCCB(HostAdapter);
   if (CCB == NULL) goto Done;
-  printk("scsi%d: Sending Bus Device Reset CCB #%ld to Target %d\n",
-	 HostAdapter->HostNumber, CCB->SerialNumber, TargetID);
+  BusLogic_Warning("Sending Bus Device Reset CCB #%ld to Target %d\n",
+		   HostAdapter, CCB->SerialNumber, TargetID);
   CCB->Opcode = BusLogic_BusDeviceReset;
   CCB->TargetID = TargetID;
   /*
@@ -2946,18 +3863,30 @@
       Command->reset_chain = NULL;
       CCB->Command = Command;
     }
-  /*
-    Attempt to write an Outgoing Mailbox with the Bus Device Reset CCB.
-    If sending a Bus Device Reset is impossible, attempt a full Host
-    Adapter Hard Reset and SCSI Bus Reset.
-  */
-  if (!(BusLogic_WriteOutgoingMailbox(HostAdapter,
-				      BusLogic_MailboxStartCommand, CCB)))
-    {
-      printk("scsi%d: cannot write Outgoing Mailbox for Bus Device Reset\n",
-	     HostAdapter->HostNumber);
-      BusLogic_DeallocateCCB(CCB);
-      goto Done;
+  if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+    {
+      /*
+	Attempt to write an Outgoing Mailbox with the Bus Device Reset CCB.
+	If sending a Bus Device Reset is impossible, attempt a full Host
+	Adapter Hard Reset and SCSI Bus Reset.
+      */
+      if (!(BusLogic_WriteOutgoingMailbox(
+	      HostAdapter, BusLogic_MailboxStartCommand, CCB)))
+	{
+	  BusLogic_Warning("Unable to write Outgoing Mailbox for "
+			   "Bus Device Reset\n", HostAdapter);
+	  BusLogic_DeallocateCCB(CCB);
+	  goto Done;
+	}
+    }
+  else
+    {
+      /*
+	Call the FlashPoint SCCB Manager to start execution of the CCB.
+      */
+      CCB->Status = BusLogic_CCB_Active;
+      HostAdapter->ActiveCommands[TargetID]++;
+      FlashPoint_StartCCB(HostAdapter->CardHandle, CCB);
     }
   /*
     If there is a currently executing CCB in the Host Adapter for this Command
@@ -2975,12 +3904,25 @@
     interrupt handler, any remaining CCBs marked as Reset will have completion
     processing performed.
   */
+  BusLogic_IncrementErrorCounter(
+    &HostAdapter->TargetDeviceStatistics[TargetID].BusDeviceResetsAttempted);
   HostAdapter->BusDeviceResetPendingCCB[TargetID] = CCB;
   HostAdapter->LastResetTime[TargetID] = jiffies;
-  for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
-    if (CCB->Status == BusLogic_CCB_Active && CCB->TargetID == TargetID)
-      CCB->Status = BusLogic_CCB_Reset;
+  for (XCCB = HostAdapter->All_CCBs; XCCB != NULL; XCCB = XCCB->NextAll)
+    if (XCCB->Status == BusLogic_CCB_Active && XCCB->TargetID == TargetID)
+      XCCB->Status = BusLogic_CCB_Reset;
+  /*
+    FlashPoint Host Adapters may have already completed the Bus Device
+    Reset and BusLogic_QueueCompletedCCB been called, or it may still be
+    pending.
+  */
   Result = SCSI_RESET_PENDING;
+  if (BusLogic_FlashPointHostAdapterP(HostAdapter))
+    if (CCB->Status == BusLogic_CCB_Completed)
+      {
+	BusLogic_ProcessCompletedCCBs();
+	Result = SCSI_RESET_SUCCESS;
+      }
   /*
     If a Bus Device Reset was not possible for some reason, force a full
     Host Adapter Hard Reset and SCSI Bus Reset.
@@ -3005,7 +3947,8 @@
   BusLogic_HostAdapter_T *HostAdapter =
     (BusLogic_HostAdapter_T *) Command->host->hostdata;
   int TargetID = Command->target;
-  int ErrorRecoveryStrategy = HostAdapter->ErrorRecoveryStrategy[TargetID];
+  BusLogic_ErrorRecoveryStrategy_T
+    ErrorRecoveryStrategy = HostAdapter->ErrorRecoveryStrategy[TargetID];
   /*
     Disable Tagged Queuing if it is active for this Target Device and if
     it has been less than 10 minutes since the last reset occurred, or since
@@ -3016,19 +3959,17 @@
     {
       HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID);
       HostAdapter->TaggedQueuingActive[TargetID] = false;
-      printk("scsi%d: Tagged Queuing now disabled for Target %d\n",
-	     HostAdapter->HostNumber, TargetID);
+      BusLogic_Warning("Tagged Queuing now disabled for Target %d\n",
+		       HostAdapter, TargetID);
     }
-  if (ErrorRecoveryStrategy == BusLogic_ErrorRecovery_Default)
-    if (ResetFlags & SCSI_RESET_SUGGEST_HOST_RESET)
-      ErrorRecoveryStrategy = BusLogic_ErrorRecovery_HardReset;
-    else if (ResetFlags & SCSI_RESET_SUGGEST_BUS_RESET)
-      ErrorRecoveryStrategy = BusLogic_ErrorRecovery_HardReset;
-    else ErrorRecoveryStrategy = BusLogic_ErrorRecovery_BusDeviceReset;
   switch (ErrorRecoveryStrategy)
     {
-    case BusLogic_ErrorRecovery_HardReset:
-      return BusLogic_ResetHostAdapter(HostAdapter, Command, ResetFlags);
+    case BusLogic_ErrorRecovery_Default:
+      if (ResetFlags & SCSI_RESET_SUGGEST_HOST_RESET)
+	return BusLogic_ResetHostAdapter(HostAdapter, Command, ResetFlags);
+      else if (ResetFlags & SCSI_RESET_SUGGEST_BUS_RESET)
+	return BusLogic_ResetHostAdapter(HostAdapter, Command, ResetFlags);
+      /* Fall through to Bus Device Reset case. */
     case BusLogic_ErrorRecovery_BusDeviceReset:
       /*
 	The Bus Device Reset Error Recovery Strategy only graduates to a Hard
@@ -3044,10 +3985,14 @@
 	  HostAdapter->CommandSuccessfulFlag[TargetID] = false;
 	  return BusLogic_SendBusDeviceReset(HostAdapter, Command, ResetFlags);
 	}
-      else return BusLogic_ResetHostAdapter(HostAdapter, Command, ResetFlags);
+      /* Fall through to Hard Reset case. */
+    case BusLogic_ErrorRecovery_HardReset:
+      return BusLogic_ResetHostAdapter(HostAdapter, Command, ResetFlags);
+    case BusLogic_ErrorRecovery_None:
+      BusLogic_Warning("Error Recovery for Target %d Suppressed\n",
+		       HostAdapter, TargetID);
+      break;
     }
-  printk("scsi%d: Error Recovery for Target %d Suppressed\n",
-	 HostAdapter->HostNumber, TargetID);
   return SCSI_RESET_PUNT;
 }
 
@@ -3058,10 +4003,11 @@
   the appropriate number of cylinders so as not to exceed drive capacity.  In
   order for disks equal to or larger than 1 GB to be addressable by the BIOS
   without exceeding the BIOS limitation of 1024 cylinders, Extended Translation
-  may be enabled in AutoSCSI on "W" and "C" Series controllers or by a dip
-  switch setting on older controllers.  With Extended Translation enabled,
-  drives between 1 GB inclusive and 2 GB exclusive are given a disk geometry of
-  128 heads and 32 sectors, and drives above 2 GB inclusive are given a disk
+  may be enabled in AutoSCSI on FlashPoint Host Adapters and on "W" and "C"
+  series MultiMaster Host Adapters, or by a dip switch setting on "S" and "A"
+  series MultiMaster Host Adapters.  With Extended Translation enabled, drives
+  between 1 GB inclusive and 2 GB exclusive are given a disk geometry of 128
+  heads and 32 sectors, and drives above 2 GB inclusive are given a disk
   geometry of 255 heads and 63 sectors.  However, if the BIOS detects that the
   Extended Translation setting does not match the geometry in the partition
   table, then the translation inferred from the partition table will be used by
@@ -3075,7 +4021,7 @@
     (BusLogic_HostAdapter_T *) Disk->device->host->hostdata;
   BIOS_DiskParameters_T *DiskParameters = (BIOS_DiskParameters_T *) Parameters;
   struct buffer_head *BufferHead;
-  if (HostAdapter->ExtendedTranslation &&
+  if (HostAdapter->ExtendedTranslationEnabled &&
       Disk->capacity >= 2*1024*1024 /* 1 GB in 512 byte sectors */)
     if (Disk->capacity >= 4*1024*1024 /* 2 GB in 512 byte sectors */)
       {
@@ -3134,12 +4080,13 @@
       DiskParameters->Cylinders =
 	Disk->capacity / (DiskParameters->Heads * DiskParameters->Sectors);
       if (SavedCylinders != DiskParameters->Cylinders)
-	printk("scsi%d: Warning: Extended Translation Setting "
-	       "(> 1GB Switch) does not match\n"
-	       "scsi%d: Partition Table - Adopting %d/%d Geometry "
-	       "from Partition Table\n",
-	       HostAdapter->HostNumber, HostAdapter->HostNumber,
-	       DiskParameters->Heads, DiskParameters->Sectors);
+	{
+	  BusLogic_Warning("Warning: Extended Translation Setting "
+			   "(> 1GB Switch) does not match\n", HostAdapter);
+	  BusLogic_Warning("Partition Table - Adopting %d/%d Geometry "
+			   "from Partition Table\n", HostAdapter,
+			   DiskParameters->Heads, DiskParameters->Sectors);
+	}
     }
   brelse(BufferHead);
   return 0;
@@ -3147,9 +4094,214 @@
 
 
 /*
+  BugLogic_ProcDirectoryInfo implements /proc/scsi/BusLogic/<N>.
+*/
+
+int BusLogic_ProcDirectoryInfo(char *ProcBuffer, char **StartPointer,
+			       off_t Offset, int BytesAvailable,
+			       int HostNumber, int WriteFlag)
+{
+  BusLogic_HostAdapter_T *HostAdapter;
+  BusLogic_TargetDeviceStatistics_T *TargetDeviceStatistics;
+  int IRQ_Channel, TargetID, Length;
+  char *Buffer;
+  if (WriteFlag) return 0;
+  for (IRQ_Channel = 0; IRQ_Channel < NR_IRQS; IRQ_Channel++)
+    {
+      HostAdapter = BusLogic_RegisteredHostAdapters[IRQ_Channel];
+      while (HostAdapter != NULL)
+	{
+	  if (HostAdapter->HostNumber == HostNumber) break;
+	  HostAdapter = HostAdapter->Next;
+	}
+      if (HostAdapter != NULL) break;
+    }
+  if (HostAdapter == NULL) return -1;
+  TargetDeviceStatistics = HostAdapter->TargetDeviceStatistics;
+  Buffer = HostAdapter->MessageBuffer;
+  Length = HostAdapter->MessageBufferLength;
+  Length += sprintf(&Buffer[Length], "\n\n\
+			   DATA TRANSFER STATISTICS\n\
+\n\
+Target	Tagged Queuing	Queue Depth  Commands Attempted	 Commands Completed\n\
+======	==============	===========  ==================	 ==================\n");
+  for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+    if (TargetDeviceStatistics[TargetID].CommandsCompleted > 0)
+      {
+	Length +=
+	  sprintf(&Buffer[Length], "  %2d	%s", TargetID,
+		  (HostAdapter->TaggedQueuingSupported[TargetID]
+		   ? (HostAdapter->TaggedQueuingActive[TargetID]
+		      ? "    Active"
+		      : (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)
+			 ? "  Permitted" : "   Disabled"))
+		   : "Not Supported"));
+	Length += sprintf(&Buffer[Length],
+			  "	    %3d		 %9u	     %9u\n",
+			  HostAdapter->QueueDepth[TargetID],
+			  TargetDeviceStatistics[TargetID].CommandsAttempted,
+			  TargetDeviceStatistics[TargetID].CommandsCompleted);
+      }
+  Length += sprintf(&Buffer[Length], "\n\
+Target  Read Commands  Write Commands   Total Bytes Read    Total Bytes Written\n\
+======  =============  ==============  ===================  ===================\n");
+  for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+    if (TargetDeviceStatistics[TargetID].CommandsCompleted > 0)
+      {
+	Length +=
+	  sprintf(&Buffer[Length], "  %2d	  %9u	 %9u", TargetID,
+		  TargetDeviceStatistics[TargetID].ReadCommands,
+		  TargetDeviceStatistics[TargetID].WriteCommands);
+	if (TargetDeviceStatistics[TargetID].TotalBytesRead.Billions > 0)
+	  Length +=
+	    sprintf(&Buffer[Length], "     %9u%09u",
+		    TargetDeviceStatistics[TargetID].TotalBytesRead.Billions,
+		    TargetDeviceStatistics[TargetID].TotalBytesRead.Units);
+	else
+	  Length +=
+	    sprintf(&Buffer[Length], "		%9u",
+		    TargetDeviceStatistics[TargetID].TotalBytesRead.Units);
+	if (TargetDeviceStatistics[TargetID].TotalBytesWritten.Billions > 0)
+	  Length +=
+	    sprintf(&Buffer[Length], "   %9u%09u\n",
+		    TargetDeviceStatistics[TargetID].TotalBytesWritten.Billions,
+		    TargetDeviceStatistics[TargetID].TotalBytesWritten.Units);
+	else
+	  Length +=
+	    sprintf(&Buffer[Length], "	     %9u\n",
+		    TargetDeviceStatistics[TargetID].TotalBytesWritten.Units);
+      }
+  Length += sprintf(&Buffer[Length], "\n\
+Target  Command    0-1KB      1-2KB      2-4KB      4-8KB     8-16KB\n\
+======  =======  =========  =========  =========  =========  =========\n");
+  for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+    if (TargetDeviceStatistics[TargetID].CommandsCompleted > 0)
+      {
+	Length +=
+	  sprintf(&Buffer[Length],
+		  "  %2d	 Read	 %9u  %9u  %9u  %9u  %9u\n", TargetID,
+		  TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[0],
+		  TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[1],
+		  TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[2],
+		  TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[3],
+		  TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[4]);
+	Length +=
+	  sprintf(&Buffer[Length],
+		  "  %2d	 Write	 %9u  %9u  %9u  %9u  %9u\n", TargetID,
+		  TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[0],
+		  TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[1],
+		  TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[2],
+		  TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[3],
+		  TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[4]);
+      }
+  Length += sprintf(&Buffer[Length], "\n\
+Target  Command   16-32KB    32-64KB   64-128KB   128-256KB   256KB+\n\
+======  =======  =========  =========  =========  =========  =========\n");
+  for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+    if (TargetDeviceStatistics[TargetID].CommandsCompleted > 0)
+      {
+	Length +=
+	  sprintf(&Buffer[Length],
+		  "  %2d	 Read	 %9u  %9u  %9u  %9u  %9u\n", TargetID,
+		  TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[5],
+		  TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[6],
+		  TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[7],
+		  TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[8],
+		  TargetDeviceStatistics[TargetID].ReadCommandSizeBuckets[9]);
+	Length +=
+	  sprintf(&Buffer[Length],
+		  "  %2d	 Write	 %9u  %9u  %9u  %9u  %9u\n", TargetID,
+		  TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[5],
+		  TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[6],
+		  TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[7],
+		  TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[8],
+		  TargetDeviceStatistics[TargetID].WriteCommandSizeBuckets[9]);
+      }
+  Length += sprintf(&Buffer[Length], "\n\n\
+			   ERROR RECOVERY STATISTICS\n\
+\n\
+	  Command Aborts      Bus Device Resets	  Host Adapter Resets\n\
+Target	Requested Completed  Requested Completed  Requested Completed\n\
+  ID	\\\\\\\\ Attempted ////  \\\\\\\\ Attempted ////  \\\\\\\\ Attempted ////\n\
+======	 ===== ===== =====    ===== ===== =====	   ===== ===== =====\n");
+  for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
+    if (TargetDeviceStatistics[TargetID].CommandsCompleted > 0)
+      Length +=
+	sprintf(&Buffer[Length], "\
+  %2d	 %5d %5d %5d    %5d %5d %5d	   %5d %5d %5d\n", TargetID,
+		TargetDeviceStatistics[TargetID].CommandAbortsRequested,
+		TargetDeviceStatistics[TargetID].CommandAbortsAttempted,
+		TargetDeviceStatistics[TargetID].CommandAbortsCompleted,
+		TargetDeviceStatistics[TargetID].BusDeviceResetsRequested,
+		TargetDeviceStatistics[TargetID].BusDeviceResetsAttempted,
+		TargetDeviceStatistics[TargetID].BusDeviceResetsCompleted,
+		TargetDeviceStatistics[TargetID].HostAdapterResetsRequested,
+		TargetDeviceStatistics[TargetID].HostAdapterResetsAttempted,
+		TargetDeviceStatistics[TargetID].HostAdapterResetsCompleted);
+  Length += sprintf(&Buffer[Length], "\nExternal Host Adapter Resets: %d\n",
+		    HostAdapter->ExternalHostAdapterResets);
+  if (Length >= BusLogic_MessageBufferSize)
+    BusLogic_Error("Message Buffer length %d exceeds size %d\n",
+		   HostAdapter, Length, BusLogic_MessageBufferSize);
+  if ((Length -= Offset) <= 0) return 0;
+  if (Length >= BytesAvailable) Length = BytesAvailable;
+  *StartPointer = &HostAdapter->MessageBuffer[Offset];
+  return Length;
+}
+
+
+/*
+  BusLogic_Message prints Driver Messages.
+*/
+
+static void BusLogic_Message(BusLogic_MessageLevel_T MessageLevel,
+			     char *Format,
+			     BusLogic_HostAdapter_T *HostAdapter,
+			     ...)
+{
+  static char Buffer[BusLogic_LineBufferSize];
+  static boolean BeginningOfLine = true;
+  va_list Arguments;
+  int Length = 0;
+  va_start(Arguments, HostAdapter);
+  Length = vsprintf(Buffer, Format, Arguments);
+  va_end(Arguments);
+  if (MessageLevel == BusLogic_AnnounceLevel)
+    {
+      static int AnnouncementLines = 0;
+      strcpy(&HostAdapter->MessageBuffer[HostAdapter->MessageBufferLength],
+	     Buffer);
+      HostAdapter->MessageBufferLength += Length;
+      if (++AnnouncementLines <= 2)
+	printk("%sscsi: %s", BusLogic_MessageLevelMap[MessageLevel], Buffer);
+    }
+  else if (MessageLevel == BusLogic_InfoLevel)
+    {
+      strcpy(&HostAdapter->MessageBuffer[HostAdapter->MessageBufferLength],
+	     Buffer);
+      HostAdapter->MessageBufferLength += Length;
+      if (BeginningOfLine)
+	printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel],
+	       HostAdapter->HostNumber, Buffer);
+      else printk("%s", Buffer);
+    }
+  else
+    {
+      if (BeginningOfLine)
+	if (HostAdapter != NULL && HostAdapter->HostAdapterInitialized)
+	  printk("%sscsi%d: %s", BusLogic_MessageLevelMap[MessageLevel],
+		 HostAdapter->HostNumber, Buffer);
+	else printk("%s%s", BusLogic_MessageLevelMap[MessageLevel], Buffer);
+      else printk("%s", Buffer);
+    }
+  BeginningOfLine = (Buffer[Length-1] == '\n');
+}
+
+
+/*
   BusLogic_Setup handles processing of Kernel Command Line Arguments.
 
-  For the BusLogic driver, a Kernel command line entry comprises the driver
+  For the BusLogic driver, a Kernel Command Line Entry comprises the driver
   identifier "BusLogic=" optionally followed by a comma-separated sequence of
   integers and then optionally followed by a comma-separated sequence of
   strings.  Each command line entry applies to one BusLogic Host Adapter.
@@ -3169,9 +4321,9 @@
   automatically based on the Host Adapter's Total Queue Depth and the number,
   type, speed, and capabilities of the detected Target Devices.  For Host
   Adapters that require ISA Bounce Buffers, the Tagged Queue Depth is
-  automatically set to BusLogic_TaggedQueueDepth_BB to avoid excessive
-  preallocation of DMA Bounce Buffer memory.  Target Devices that do not
-  support Tagged Queuing use a Queue Depth of BusLogic_UntaggedQueueDepth.
+  automatically set to BusLogic_TaggedQueueDepthBounceBuffers to avoid
+  excessive preallocation of DMA Bounce Buffer memory.  Target Devices that do
+  not support Tagged Queuing use a Queue Depth of BusLogic_UntaggedQueueDepth.
 
   The third integer specified is the Bus Settle Time in seconds.  This is
   the amount of time to wait between a Host Adapter Hard Reset which initiates
@@ -3260,56 +4412,85 @@
 			no BusLogic Host Adapters will be detected.
 
   NoProbeISA		No probing of the standard ISA I/O Addresses will
-			be done, and hence only PCI Host Adapters will be
-			detected.
+			be done, and hence only PCI MultiMaster and FlashPoint
+			Host Adapters will be detected.
+
+  NoProbePCI		No interrogation of PCI Configuration Space will be
+			made, and hence only ISA Multimaster Host Adapters
+			will be detected, as well as PCI Multimaster Host
+			Adapters that have their ISA Compatible I/O Port
+			set to "Primary" or "Alternate".
+
+  NoSortPCI		PCI MultiMaster Host Adapters will be enumerated in
+			the order provided by the PCI BIOS, ignoring any
+			setting of the AutoSCSI "Use Bus And Device # For PCI
+			Scanning Seq." option.
+
+  MultiMasterFirst	By default, if both FlashPoint and PCI MultiMaster
+			Host Adapters are present, this driver will probe for
+			PCI MultiMaster Host Adapters first unless the BIOS
+			primary disk is not controlled by the first PCI
+			MultiMaster Host Adapter, in which case FlashPoint
+			Host Adapters will be probed first.  This option
+			forces MultiMaster Host Adapters to be probed first.
+
+  FlashPointFirst	By default, if both FlashPoint and PCI MultiMaster
+			Host Adapters are present, this driver will probe for
+			PCI MultiMaster Host Adapters first unless the BIOS
+			primary disk is not controlled by the first PCI
+			MultiMaster Host Adapter, in which case FlashPoint
+			Host Adapters will be probed first.  This option
+			forces FlashPoint Host Adapters to be probed first.
+
+  Debug			Sets all the tracing bits in BusLogic_GlobalOptions.
 
-  NoSortPCI		PCI Host Adapters will be enumerated in the order
-			provided by the PCI BIOS, ignoring any setting of
-			the AutoSCSI "Use Bus And Device # For PCI Scanning
-			Seq." option.
 */
 
 void BusLogic_Setup(char *Strings, int *Integers)
 {
   BusLogic_CommandLineEntry_T *CommandLineEntry =
     &BusLogic_CommandLineEntries[BusLogic_CommandLineEntryCount++];
-  static int ProbeListIndex = 0;
   int IntegerCount = Integers[0];
   int TargetID, i;
   CommandLineEntry->IO_Address = 0;
   CommandLineEntry->TaggedQueueDepth = 0;
   CommandLineEntry->BusSettleTime = 0;
-  CommandLineEntry->LocalOptions = 0;
   CommandLineEntry->TaggedQueuingPermitted = 0;
   CommandLineEntry->TaggedQueuingPermittedMask = 0;
+  CommandLineEntry->LocalOptions.All = 0;
   memset(CommandLineEntry->ErrorRecoveryStrategy,
 	 BusLogic_ErrorRecovery_Default,
 	 sizeof(CommandLineEntry->ErrorRecoveryStrategy));
   if (IntegerCount > 5)
-    printk("BusLogic: Unexpected Command Line Integers ignored\n");
+    BusLogic_Error("BusLogic: Unexpected Command Line Integers "
+		   "ignored\n", NULL);
   if (IntegerCount >= 1)
     {
-      unsigned int IO_Address = Integers[1];
+      BusLogic_IO_Address_T IO_Address = Integers[1];
       if (IO_Address > 0)
 	{
+	  BusLogic_ProbeInfo_T *ProbeInfo;
 	  for (i = 0; ; i++)
-	    if (BusLogic_IO_StandardAddresses[i] == 0)
+	    if (BusLogic_ISA_StandardAddresses[i] == 0)
 	      {
-		printk("BusLogic: Invalid Command Line Entry "
-		       "(illegal I/O Address 0x%X)\n", IO_Address);
+		BusLogic_Error("BusLogic: Invalid Command Line Entry "
+			       "(illegal I/O Address 0x%X)\n",
+			       NULL, IO_Address);
 		return;
 	      }
-	    else if (i < ProbeListIndex &&
-		     IO_Address == BusLogic_IO_AddressProbeList[i])
+	    else if (i < BusLogic_ProbeInfoCount &&
+		     IO_Address == BusLogic_ProbeInfoList[i].IO_Address)
 	      {
-		printk("BusLogic: Invalid Command Line Entry "
-		       "(duplicate I/O Address 0x%X)\n", IO_Address);
+		BusLogic_Error("BusLogic: Invalid Command Line Entry "
+			       "(duplicate I/O Address 0x%X)\n",
+			       NULL, IO_Address);
 		return;
 	      }
 	    else if (IO_Address >= 0x400 ||
-		     IO_Address == BusLogic_IO_StandardAddresses[i]) break;
-	  BusLogic_IO_AddressProbeList[ProbeListIndex++] = IO_Address;
-	  BusLogic_IO_AddressProbeList[ProbeListIndex] = 0;
+		     IO_Address == BusLogic_ISA_StandardAddresses[i]) break;
+	  ProbeInfo = &BusLogic_ProbeInfoList[BusLogic_ProbeInfoCount++];
+	  ProbeInfo->HostAdapterType = BusLogic_MultiMaster;
+	  ProbeInfo->HostAdapterBusType = BusLogic_ISA_Bus;
 	}
       CommandLineEntry->IO_Address = IO_Address;
     }
@@ -3318,8 +4499,9 @@
       unsigned short TaggedQueueDepth = Integers[2];
       if (TaggedQueueDepth > BusLogic_MaxTaggedQueueDepth)
 	{
-	  printk("BusLogic: Invalid Command Line Entry "
-		 "(illegal Tagged Queue Depth %d)\n", TaggedQueueDepth);
+	  BusLogic_Error("BusLogic: Invalid Command Line Entry "
+			 "(illegal Tagged Queue Depth %d)\n",
+			 NULL, TaggedQueueDepth);
 	  return;
 	}
       CommandLineEntry->TaggedQueueDepth = TaggedQueueDepth;
@@ -3327,131 +4509,154 @@
   if (IntegerCount >= 3)
     CommandLineEntry->BusSettleTime = Integers[3];
   if (IntegerCount >= 4)
-    CommandLineEntry->LocalOptions = Integers[4];
+    CommandLineEntry->LocalOptions.All = Integers[4];
   if (IntegerCount >= 5)
-    BusLogic_GlobalOptions |= Integers[5];
-  if (!(BusLogic_CommandLineEntryCount == 0 || ProbeListIndex == 0 ||
-	BusLogic_CommandLineEntryCount == ProbeListIndex))
+    BusLogic_GlobalOptions.All |= Integers[5];
+  if (!(BusLogic_CommandLineEntryCount == 0 ||
+	BusLogic_ProbeInfoCount == 0 ||
+	BusLogic_CommandLineEntryCount == BusLogic_ProbeInfoCount))
     {
-      printk("BusLogic: Invalid Command Line Entry "
-	     "(all or no I/O Addresses must be specified)\n");
+      BusLogic_Error("BusLogic: Invalid Command Line Entry "
+		     "(all or no I/O Addresses must be specified)\n", NULL);
       return;
     }
   if (Strings == NULL) return;
   while (*Strings != '\0')
-    {
-      if (strncmp(Strings, "TQ:", 3) == 0)
-	{
-	  Strings += 3;
-	  if (strncmp(Strings, "Default", 7) == 0)
-	    Strings += 7;
-	  else if (strncmp(Strings, "Enable", 6) == 0)
-	    {
-	      Strings += 6;
-	      CommandLineEntry->TaggedQueuingPermitted = 0xFFFF;
-	      CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF;
-	    }
-	  else if (strncmp(Strings, "Disable", 7) == 0)
-	    {
-	      Strings += 7;
-	      CommandLineEntry->TaggedQueuingPermitted = 0x0000;
-	      CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF;
-	    }
-	  else
-	    for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++)
-	      switch (*Strings++)
-		{
-		case 'Y':
-		  CommandLineEntry->TaggedQueuingPermitted |= 1 << TargetID;
-		  CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID;
-		  break;
-		case 'N':
-		  CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID;
-		  break;
-		case 'X':
-		  break;
-		default:
-		  Strings--;
-		  TargetID = BusLogic_MaxTargetDevices;
-		  break;
-		}
-	}
-      else if (strncmp(Strings, "ER:", 3) == 0)
-	{
-	  Strings += 3;
-	  if (strncmp(Strings, "Default", 7) == 0)
+    if (strncmp(Strings, "TQ:", 3) == 0)
+      {
+	Strings += 3;
+	if (strncmp(Strings, "Default", 7) == 0)
+	  Strings += 7;
+	else if (strncmp(Strings, "Enable", 6) == 0)
+	  {
+	    Strings += 6;
+	    CommandLineEntry->TaggedQueuingPermitted = 0xFFFF;
+	    CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF;
+	  }
+	else if (strncmp(Strings, "Disable", 7) == 0)
+	  {
 	    Strings += 7;
-	  else if (strncmp(Strings, "HardReset", 9) == 0)
-	    {
-	      Strings += 9;
-	      memset(CommandLineEntry->ErrorRecoveryStrategy,
-		     BusLogic_ErrorRecovery_HardReset,
-		     sizeof(CommandLineEntry->ErrorRecoveryStrategy));
-	    }
-	  else if (strncmp(Strings, "BusDeviceReset", 14) == 0)
-	    {
-	      Strings += 14;
-	      memset(CommandLineEntry->ErrorRecoveryStrategy,
-		     BusLogic_ErrorRecovery_BusDeviceReset,
-		     sizeof(CommandLineEntry->ErrorRecoveryStrategy));
-	    }
-	  else if (strncmp(Strings, "None", 4) == 0)
-	    {
-	      Strings += 4;
-	      memset(CommandLineEntry->ErrorRecoveryStrategy,
-		     BusLogic_ErrorRecovery_None,
-		     sizeof(CommandLineEntry->ErrorRecoveryStrategy));
-	    }
-	  else
-	    for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++)
-	      switch (*Strings++)
-		{
-		case 'D':
-		  CommandLineEntry->ErrorRecoveryStrategy[TargetID] =
-		    BusLogic_ErrorRecovery_Default;
-		  break;
-		case 'H':
-		  CommandLineEntry->ErrorRecoveryStrategy[TargetID] =
-		    BusLogic_ErrorRecovery_HardReset;
-		  break;
-		case 'B':
-		  CommandLineEntry->ErrorRecoveryStrategy[TargetID] =
-		    BusLogic_ErrorRecovery_BusDeviceReset;
-		  break;
-		case 'N':
-		  CommandLineEntry->ErrorRecoveryStrategy[TargetID] =
-		    BusLogic_ErrorRecovery_None;
-		  break;
-		default:
-		  Strings--;
-		  TargetID = BusLogic_MaxTargetDevices;
-		  break;
-		}
-	}
-      else if (strcmp(Strings, "NoProbe") == 0 ||
-	       strcmp(Strings, "noprobe") == 0)
-	{
+	    CommandLineEntry->TaggedQueuingPermitted = 0x0000;
+	    CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF;
+	  }
+	else
+	  for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++)
+	    switch (*Strings++)
+	      {
+	      case 'Y':
+		CommandLineEntry->TaggedQueuingPermitted |= 1 << TargetID;
+		CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID;
+		break;
+	      case 'N':
+		CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID;
+		break;
+	      case 'X':
+		break;
+	      default:
+		Strings--;
+		TargetID = BusLogic_MaxTargetDevices;
+		break;
+	      }
+      }
+    else if (strncmp(Strings, "ER:", 3) == 0)
+      {
+	Strings += 3;
+	if (strncmp(Strings, "Default", 7) == 0)
 	  Strings += 7;
-	  BusLogic_ProbeOptions |= BusLogic_NoProbe;
-	}
-      else if (strncmp(Strings, "NoProbeISA", 10) == 0)
-	{
-	  Strings += 10;
-	  BusLogic_ProbeOptions |= BusLogic_NoProbeISA;
-	}
-      else if (strncmp(Strings, "NoSortPCI", 9) == 0)
-	{
-	  Strings += 9;
-	  BusLogic_ProbeOptions |= BusLogic_NoSortPCI;
-	}
-      else
-	{
-	  printk("BusLogic: Unexpected Command Line String '%s' ignored\n",
-		 Strings);
-	  break;
-	}
-      if (*Strings == ',') Strings++;
-    }
+	else if (strncmp(Strings, "HardReset", 9) == 0)
+	  {
+	    Strings += 9;
+	    memset(CommandLineEntry->ErrorRecoveryStrategy,
+		   BusLogic_ErrorRecovery_HardReset,
+		   sizeof(CommandLineEntry->ErrorRecoveryStrategy));
+	  }
+	else if (strncmp(Strings, "BusDeviceReset", 14) == 0)
+	  {
+	    Strings += 14;
+	    memset(CommandLineEntry->ErrorRecoveryStrategy,
+		   BusLogic_ErrorRecovery_BusDeviceReset,
+		   sizeof(CommandLineEntry->ErrorRecoveryStrategy));
+	  }
+	else if (strncmp(Strings, "None", 4) == 0)
+	  {
+	    Strings += 4;
+	    memset(CommandLineEntry->ErrorRecoveryStrategy,
+		   BusLogic_ErrorRecovery_None,
+		   sizeof(CommandLineEntry->ErrorRecoveryStrategy));
+	  }
+	else
+	  for (TargetID = 0; TargetID < BusLogic_MaxTargetDevices; TargetID++)
+	    switch (*Strings++)
+	      {
+	      case 'D':
+		CommandLineEntry->ErrorRecoveryStrategy[TargetID] =
+		  BusLogic_ErrorRecovery_Default;
+		break;
+	      case 'H':
+		CommandLineEntry->ErrorRecoveryStrategy[TargetID] =
+		  BusLogic_ErrorRecovery_HardReset;
+		break;
+	      case 'B':
+		CommandLineEntry->ErrorRecoveryStrategy[TargetID] =
+		  BusLogic_ErrorRecovery_BusDeviceReset;
+		break;
+	      case 'N':
+		CommandLineEntry->ErrorRecoveryStrategy[TargetID] =
+		  BusLogic_ErrorRecovery_None;
+		break;
+	      default:
+		Strings--;
+		TargetID = BusLogic_MaxTargetDevices;
+		break;
+	      }
+      }
+    else if (strcmp(Strings, "NoProbe") == 0 ||
+	     strcmp(Strings, "noprobe") == 0)
+      {
+	Strings += 7;
+	BusLogic_ProbeOptions.Bits.NoProbe = true;
+      }
+    else if (strncmp(Strings, "NoProbeISA", 10) == 0)
+      {
+	Strings += 10;
+	BusLogic_ProbeOptions.Bits.NoProbeISA = true;
+      }
+    else if (strncmp(Strings, "NoProbePCI", 10) == 0)
+      {
+	Strings += 10;
+	BusLogic_ProbeOptions.Bits.NoProbePCI = true;
+      }
+    else if (strncmp(Strings, "NoSortPCI", 9) == 0)
+      {
+	Strings += 9;
+	BusLogic_ProbeOptions.Bits.NoSortPCI = true;
+      }
+    else if (strncmp(Strings, "MultiMasterFirst", 16) == 0)
+      {
+	Strings += 16;
+	BusLogic_ProbeOptions.Bits.ProbeMultiMasterFirst = true;
+      }
+    else if (strncmp(Strings, "FlashPointFirst", 15) == 0)
+      {
+	Strings += 15;
+	BusLogic_ProbeOptions.Bits.ProbeFlashPointFirst = true;
+      }
+    else if (strncmp(Strings, "Debug", 5) == 0)
+      {
+	Strings += 5;
+	BusLogic_GlobalOptions.Bits.TraceProbe = true;
+	BusLogic_GlobalOptions.Bits.TraceHardReset = true;
+	BusLogic_GlobalOptions.Bits.TraceConfiguration = true;
+	BusLogic_GlobalOptions.Bits.TraceErrors = true;
+      }
+    else if (*Strings == ',')
+      Strings++;
+    else
+      {
+	BusLogic_Error("BusLogic: Unexpected Command Line String '%s' "
+		       "ignored\n", NULL, Strings);
+	break;
+      }
 }
 
 
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/BusLogic.h linux/drivers/scsi/BusLogic.h
--- v2.1.27/linux/drivers/scsi/BusLogic.h	Mon Oct 28 03:39:05 1996
+++ linux/drivers/scsi/BusLogic.h	Sun Feb 23 16:14:00 1997
@@ -1,6 +1,6 @@
 /*
 
-  Linux Driver for BusLogic MultiMaster SCSI Host Adapters
+  Linux Driver for BusLogic MultiMaster and FlashPoint SCSI Host Adapters
 
   Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
 
@@ -17,9 +17,12 @@
   The author respectfully requests that any modifications to this software be
   sent directly to him for evaluation and testing.
 
-  Special thanks to Wayne Yen and Alex Win of BusLogic, whose advice has been
-  invaluable, to David Gentzel, for writing the original Linux BusLogic driver,
-  and to Paul Gortmaker, for being such a dedicated test site.
+  Special thanks to Wayne Yen, Jin-Lon Hon, and Alex Win of BusLogic, whose
+  advice has been invaluable, to David Gentzel, for writing the original Linux
+  BusLogic driver, and to Paul Gortmaker, for being such a dedicated test site.
+
+  Finally, special thanks to Mylex/BusLogic for making the FlashPoint SCCB
+  Manager available as freely redistributable source code.
 
 */
 
@@ -29,6 +32,8 @@
   of the Linux Kernel and SCSI Subsystem.
 */
 
+typedef kdev_t KernelDevice_T;
+typedef struct proc_dir_entry PROC_DirectoryEntry_T;
 typedef struct pt_regs Registers_T;
 typedef Scsi_Host_Template SCSI_Host_Template_T;
 typedef struct Scsi_Host SCSI_Host_T;
@@ -36,21 +41,22 @@
 typedef struct scsi_disk SCSI_Disk_T;
 typedef struct scsi_cmnd SCSI_Command_T;
 typedef struct scatterlist SCSI_ScatterList_T;
-typedef kdev_t KernelDevice_T;
 
 
 /*
   Define prototypes for the BusLogic Driver Interface Functions.
 */
 
-const char *BusLogic_DriverInfo(SCSI_Host_T *);
-int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *);
-int BusLogic_ReleaseHostAdapter(SCSI_Host_T *);
-int BusLogic_QueueCommand(SCSI_Command_T *,
-			  void (*CompletionRoutine)(SCSI_Command_T *));
-int BusLogic_AbortCommand(SCSI_Command_T *);
-int BusLogic_ResetCommand(SCSI_Command_T *, unsigned int);
-int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *);
+extern PROC_DirectoryEntry_T BusLogic_ProcDirectoryEntry;
+extern const char *BusLogic_DriverInfo(SCSI_Host_T *);
+extern int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *);
+extern int BusLogic_ReleaseHostAdapter(SCSI_Host_T *);
+extern int BusLogic_QueueCommand(SCSI_Command_T *,
+				 void (*CompletionRoutine)(SCSI_Command_T *));
+extern int BusLogic_AbortCommand(SCSI_Command_T *);
+extern int BusLogic_ResetCommand(SCSI_Command_T *, unsigned int);
+extern int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *);
+extern int BusLogic_ProcDirectoryInfo(char *, char **, off_t, int, int, int);
 
 
 /*
@@ -58,26 +64,26 @@
 */
 
 #define BUSLOGIC							 \
-  { NULL,				/* Next			     */  \
-    NULL,				/* Usage Count Pointer	     */  \
-    NULL,				/* /proc Directory Entry     */	 \
-    NULL,				/* /proc Info Function	     */	 \
-    "BusLogic",				/* Driver Name		     */  \
-    BusLogic_DetectHostAdapter,		/* Detect Host Adapter	     */  \
-    BusLogic_ReleaseHostAdapter,	/* Release Host Adapter	     */  \
-    BusLogic_DriverInfo,		/* Driver Info Function	     */  \
-    NULL,				/* Command Function	     */  \
-    BusLogic_QueueCommand,		/* Queue Command Function    */  \
-    BusLogic_AbortCommand,		/* Abort Command Function    */  \
-    BusLogic_ResetCommand,		/* Reset Command Function    */  \
-    NULL,				/* Slave Attach Function     */  \
-    BusLogic_BIOSDiskParameters,	/* Disk BIOS Parameters	     */  \
-    0,					/* Can Queue		     */  \
-    0,					/* This ID		     */  \
-    0,					/* Scatter/Gather Table Size */  \
-    0,					/* SCSI Commands per LUN     */  \
-    0,					/* Present		     */  \
-    1,					/* Default Unchecked ISA DMA */  \
+  { NULL,				/* Next			     */	 \
+    NULL,				/* Usage Count Pointer	     */	 \
+    &BusLogic_ProcDirectoryEntry,	/* /proc Directory Entry     */	 \
+    BusLogic_ProcDirectoryInfo,		/* /proc Info Function	     */	 \
+    "BusLogic",				/* Driver Name		     */	 \
+    BusLogic_DetectHostAdapter,		/* Detect Host Adapter	     */	 \
+    BusLogic_ReleaseHostAdapter,	/* Release Host Adapter	     */	 \
+    BusLogic_DriverInfo,		/* Driver Info Function	     */	 \
+    NULL,				/* Command Function	     */	 \
+    BusLogic_QueueCommand,		/* Queue Command Function    */	 \
+    BusLogic_AbortCommand,		/* Abort Command Function    */	 \
+    BusLogic_ResetCommand,		/* Reset Command Function    */	 \
+    NULL,				/* Slave Attach Function     */	 \
+    BusLogic_BIOSDiskParameters,	/* BIOS Disk Parameters	     */	 \
+    0,					/* Can Queue		     */	 \
+    0,					/* This ID		     */	 \
+    0,					/* Scatter/Gather Table Size */	 \
+    0,					/* SCSI Commands per LUN     */	 \
+    0,					/* Present		     */	 \
+    1,					/* Default Unchecked ISA DMA */	 \
     ENABLE_CLUSTERING }			/* Enable Clustering	     */
 
 
@@ -89,17 +95,10 @@
 
 
 /*
-  Define the maximum number of BusLogic Host Adapters that are supported.
-*/
-
-#define BusLogic_MaxHostAdapters		10
-
-
-/*
-  Define the maximum number of I/O Addresses that may be probed.
+  Define the maximum number of BusLogic Host Adapters supported by this driver.
 */
 
-#define BusLogic_IO_MaxProbeAddresses		16
+#define BusLogic_MaxHostAdapters		16
 
 
 /*
@@ -112,7 +111,7 @@
 /*
   Define the maximum number of Scatter/Gather Segments used by this driver.
   For optimal performance, it is important that this limit be at least as
-  large as the maximum single request generated by the I/O Subsystem.
+  large as the largest single request generated by the I/O Subsystem.
 */
 
 #define BusLogic_ScatterGatherLimit		128
@@ -126,7 +125,8 @@
 
 #define BusLogic_MaxTaggedQueueDepth		63
 #define BusLogic_PreferredTaggedQueueDepth	28
-#define BusLogic_TaggedQueueDepth_BB		2
+#define BusLogic_TaggedQueueDepthBounceBuffers	2
+#define BusLogic_TaggedQueueDepthAutomatic	0
 #define BusLogic_UntaggedQueueDepth		3
 
 
@@ -141,122 +141,334 @@
 
 
 /*
-  Define the possible Probe Options.
+  Define the Host Adapter Line and Message Buffer Sizes.
 */
 
-#define BusLogic_NoProbe			1
-#define BusLogic_NoProbeISA			2
-#define BusLogic_NoSortPCI			4
+#define BusLogic_LineBufferSize			100
+#define BusLogic_MessageBufferSize		8900
 
 
 /*
-  Define the possible Local Options.
+  Define the Driver Message Levels.
 */
 
-#define BusLogic_InhibitTargetInquiry		1
+typedef enum BusLogic_MessageLevel
+{
+  BusLogic_AnnounceLevel =			0,
+  BusLogic_InfoLevel =				1,
+  BusLogic_NoticeLevel =			2,
+  BusLogic_WarningLevel =			3,
+  BusLogic_ErrorLevel =				4
+}
+BusLogic_MessageLevel_T;
+
+static char
+  *BusLogic_MessageLevelMap[] =
+    { KERN_INFO, KERN_INFO, KERN_NOTICE, KERN_WARNING, KERN_ERR };
 
 
 /*
-  Define the possible Global Options.
+  Define the types of BusLogic Host Adapters that are supported and the number
+  of I/O Addresses required by each type.
 */
 
-#define BusLogic_TraceProbe			1
-#define BusLogic_TraceHardReset			2
-#define BusLogic_TraceConfiguration		4
-#define BusLogic_TraceErrors			8
-#define BusLogic_TraceQueueDepths		16
+typedef enum
+{
+  BusLogic_MultiMaster =			1,
+  BusLogic_FlashPoint =				2
+}
+__attribute__ ((packed))
+BusLogic_HostAdapterType_T;
+
+#define BusLogic_MultiMasterAddressCount	4
+#define BusLogic_FlashPointAddressCount		256
+
+static int
+  BusLogic_HostAdapter_AddressCount[3] =
+    { 0, BusLogic_MultiMasterAddressCount, BusLogic_FlashPointAddressCount };
 
 
 /*
-  Define the possible Error Recovery Strategy Options.
+  Define the possible Host Adapter Bus Types.
 */
 
-#define BusLogic_ErrorRecovery_Default		0
-#define BusLogic_ErrorRecovery_HardReset	1
-#define BusLogic_ErrorRecovery_BusDeviceReset	2
-#define BusLogic_ErrorRecovery_None		3
+typedef enum
+{
+  BusLogic_Unknown_Bus =			0,
+  BusLogic_ISA_Bus =				1,
+  BusLogic_EISA_Bus =				2,
+  BusLogic_PCI_Bus =				3,
+  BusLogic_VESA_Bus =				4,
+  BusLogic_MCA_Bus =				5
+}
+BusLogic_HostAdapterBusType_T;
 
 static char
-  *BusLogic_ErrorRecoveryStrategyNames[] =
-    { "Default", "Hard Reset", "Bus Device Reset", "None" },
-  *BusLogic_ErrorRecoveryStrategyLetters[] =
-    { "D", "H", "B", "N" };
+  *BusLogic_HostAdapterBusNames[] =
+    { "Unknown", "ISA", "EISA", "PCI", "VESA", "MCA" };
+
+static BusLogic_HostAdapterBusType_T
+  BusLogic_HostAdapterBusTypes[] =
+    { BusLogic_VESA_Bus,				/* BT-4xx */
+      BusLogic_ISA_Bus,					/* BT-5xx */
+      BusLogic_MCA_Bus,					/* BT-6xx */
+      BusLogic_EISA_Bus,				/* BT-7xx */
+      BusLogic_Unknown_Bus,				/* BT-8xx */
+      BusLogic_PCI_Bus };				/* BT-9xx */
 
 
 /*
-  Define a boolean data type.
+  Define the possible Host Adapter BIOS Disk Geometry Translations.
 */
 
-#define false 0
-#define true  1
-typedef unsigned char boolean;
+typedef enum BusLogic_BIOS_DiskGeometryTranslation
+{
+  BusLogic_BIOS_Disk_Not_Installed =		0,
+  BusLogic_BIOS_Disk_Installed_64x32 =		1,
+  BusLogic_BIOS_Disk_Installed_128x32 =		2,
+  BusLogic_BIOS_Disk_Installed_255x63 =		3
+}
+__attribute__ ((packed))
+BusLogic_BIOS_DiskGeometryTranslation_T;
 
 
 /*
-  Define a 32 bit bus address data type.
+  Define a Boolean data type.
 */
 
-typedef unsigned int bus_address_t;
+typedef enum { false, true } __attribute__ ((packed)) boolean;
+
+
+/*
+  Define a 32 bit I/O Address data type.
+*/
+
+typedef unsigned int BusLogic_IO_Address_T;
+
+
+/*
+  Define a 32 bit PCI Bus Address data type.
+*/
+
+typedef unsigned int BusLogic_PCI_Address_T;
+
+
+/*
+  Define a 32 bit Bus Address data type.
+*/
+
+typedef unsigned int BusLogic_BusAddress_T;
+
+
+/*
+  Define a 32 bit Byte Count data type.
+*/
+
+typedef unsigned int BusLogic_ByteCount_T;
+
+
+/*
+  Define a 10^18 Statistics Byte Counter data type.
+*/
+
+typedef struct BusLogic_ByteCounter
+{
+  unsigned int Units;
+  unsigned int Billions;
+}
+BusLogic_ByteCounter_T;
+
+
+/*
+  Define the structure for I/O Address and Bus Probing Information.
+*/
+
+typedef struct BusLogic_ProbeInfo
+{
+  BusLogic_IO_Address_T IO_Address;
+  BusLogic_PCI_Address_T PCI_Address;
+  BusLogic_HostAdapterType_T HostAdapterType:2;
+  BusLogic_HostAdapterBusType_T HostAdapterBusType:3;
+  unsigned char :3;
+  unsigned char Bus;
+  unsigned char Device;
+  unsigned char IRQ_Channel;
+}
+BusLogic_ProbeInfo_T;
+
+
+/*
+  BusLogic_ISA_StandardAddresses is the list of standard ISA I/O Addresses at
+  which BusLogic MultiMaster Host Adapters may potentially be found.  The first
+  I/O Address 0x330 is known as the "Primary" I/O Address.  A Host Adapter
+  configured to use the Primary I/O Address will always be the preferred boot
+  device.
+*/
+
+#define BusLogic_ISA_StandardAddressesCount	6
+
+static BusLogic_IO_Address_T
+  BusLogic_ISA_StandardAddresses[BusLogic_ISA_StandardAddressesCount] =
+    { 0x330, 0x334, 0x230, 0x234, 0x130, 0x134 };
+
+
+/*
+  Define the Probe Options.
+*/
+
+typedef union BusLogic_ProbeOptions
+{
+  unsigned short All;
+  struct {
+    boolean NoProbe:1;					/* Bit 0 */
+    boolean NoProbeISA:1;				/* Bit 1 */
+    boolean NoProbePCI:1;				/* Bit 2 */
+    boolean NoSortPCI:1;				/* Bit 3 */
+    boolean ProbeMultiMasterFirst:1;			/* Bit 4 */
+    boolean ProbeFlashPointFirst:1;			/* Bit 5 */
+  } Bits;
+}
+BusLogic_ProbeOptions_T;
+
+
+/*
+  Define the Global Options.
+*/
+
+typedef union BusLogic_GlobalOptions
+{
+  unsigned short All;
+  struct {
+    boolean TraceProbe:1;				/* Bit 0 */
+    boolean TraceHardReset:1;				/* Bit 1 */
+    boolean TraceConfiguration:1;			/* Bit 2 */
+    boolean TraceErrors:1;				/* Bit 3 */
+  } Bits;
+}
+BusLogic_GlobalOptions_T;
+
+
+/*
+  Define the Local Options.
+*/
+
+typedef union BusLogic_LocalOptions
+{
+  unsigned short All;
+  struct {
+    boolean InhibitTargetInquiry:1;			/* Bit 0 */
+    boolean InhibitInterruptTest:1;			/* Bit 1 */
+  } Bits;
+}
+BusLogic_LocalOptions_T;
+
+
+/*
+  Define the Error Recovery Strategy Options.
+*/
+
+typedef enum
+{
+  BusLogic_ErrorRecovery_Default =		0,
+  BusLogic_ErrorRecovery_BusDeviceReset =	1,
+  BusLogic_ErrorRecovery_HardReset =		2,
+  BusLogic_ErrorRecovery_None =			3
+}
+__attribute__ ((packed))
+BusLogic_ErrorRecoveryStrategy_T;
+
+static char
+  *BusLogic_ErrorRecoveryStrategyNames[] =
+    { "Default", "Bus Device Reset", "Hard Reset", "None" },
+  BusLogic_ErrorRecoveryStrategyLetters[] =
+    { 'D', 'B', 'H', 'N' };
 
 
 /*
   Define the BusLogic SCSI Host Adapter I/O Register Offsets.
 */
 
-#define BusLogic_IO_PortCount			4	/* I/O Registers */
-#define BusLogic_ControlRegister		0	/* WO register */
-#define BusLogic_StatusRegister			0	/* RO register */
-#define BusLogic_CommandParameterRegister	1	/* WO register */
-#define BusLogic_DataInRegister			1	/* RO register */
-#define BusLogic_InterruptRegister		2	/* RO register */
-#define BusLogic_GeometryRegister		3	/* RO register */
+#define BusLogic_ControlRegisterOffset		0	/* WO register */
+#define BusLogic_StatusRegisterOffset		0	/* RO register */
+#define BusLogic_CommandParameterRegisterOffset	1	/* WO register */
+#define BusLogic_DataInRegisterOffset		1	/* RO register */
+#define BusLogic_InterruptRegisterOffset	2	/* RO register */
+#define BusLogic_GeometryRegisterOffset		3	/* RO register */
 
 
 /*
-  Define the bits in the write-only Control Register.
+  Define the structure of the write-only Control Register.
 */
 
-#define BusLogic_ReservedCR			0x0F
-#define BusLogic_SCSIBusReset			0x10
-#define BusLogic_InterruptReset			0x20
-#define BusLogic_SoftReset			0x40
-#define BusLogic_HardReset			0x80
+typedef union BusLogic_ControlRegister
+{
+  unsigned char All;
+  struct {
+    unsigned char :4;					/* Bits 0-3 */
+    boolean SCSIBusReset:1;				/* Bit 4 */
+    boolean InterruptReset:1;				/* Bit 5 */
+    boolean SoftReset:1;				/* Bit 6 */
+    boolean HardReset:1;				/* Bit 7 */
+  } Bits;
+}
+BusLogic_ControlRegister_T;
 
 
 /*
-  Define the bits in the read-only Status Register.
+  Define the structure of the read-only Status Register.
 */
 
-#define BusLogic_CommandInvalid			0x01
-#define BusLogic_ReservedSR			0x02
-#define BusLogic_DataInRegisterReady		0x04
-#define BusLogic_CommandParameterRegisterBusy	0x08
-#define BusLogic_HostAdapterReady		0x10
-#define BusLogic_InitializationRequired		0x20
-#define BusLogic_DiagnosticFailure		0x40
-#define BusLogic_DiagnosticActive		0x80
+typedef union BusLogic_StatusRegister
+{
+  unsigned char All;
+  struct {
+    boolean CommandInvalid:1;				/* Bit 0 */
+    boolean Reserved:1;					/* Bit 1 */
+    boolean DataInRegisterReady:1;			/* Bit 2 */
+    boolean CommandParameterRegisterBusy:1;		/* Bit 3 */
+    boolean HostAdapterReady:1;				/* Bit 4 */
+    boolean InitializationRequired:1;			/* Bit 5 */
+    boolean DiagnosticFailure:1;			/* Bit 6 */
+    boolean DiagnosticActive:1;				/* Bit 7 */
+  } Bits;
+}
+BusLogic_StatusRegister_T;
 
 
 /*
-  Define the bits in the read-only Interrupt Register.
+  Define the structure of the read-only Interrupt Register.
 */
 
-#define BusLogic_IncomingMailboxLoaded		0x01
-#define BusLogic_OutgoingMailboxAvailable	0x02
-#define BusLogic_CommandComplete		0x04
-#define BusLogic_SCSIResetState			0x08
-#define BusLogic_ReservedIR			0x70
-#define BusLogic_InterruptValid			0x80
+typedef union BusLogic_InterruptRegister
+{
+  unsigned char All;
+  struct {
+    boolean IncomingMailboxLoaded:1;			/* Bit 0 */
+    boolean OutgoingMailboxAvailable:1;			/* Bit 1 */
+    boolean CommandComplete:1;				/* Bit 2 */
+    boolean ExternalBusReset:1;				/* Bit 3 */
+    unsigned char Reserved:3;				/* Bits 4-6 */
+    boolean InterruptValid:1;				/* Bit 7 */
+  } Bits;
+}
+BusLogic_InterruptRegister_T;
 
 
 /*
-  Define the bits in the read-only Geometry Register.
+  Define the structure of the read-only Geometry Register.
 */
 
-#define BusLogic_Drive0Geometry			0x03
-#define BusLogic_Drive1Geometry			0x0C
-#define BusLogic_ReservedGR			0x70
-#define BusLogic_ExtendedTranslationEnabled	0x80
+typedef union BusLogic_GeometryRegister
+{
+  unsigned char All;
+  struct {
+    BusLogic_BIOS_DiskGeometryTranslation_T Drive0Geometry:2; /* Bits 0-1 */
+    BusLogic_BIOS_DiskGeometryTranslation_T Drive1Geometry:2; /* Bits 2-3 */
+    unsigned char :3;					/* Bits 4-6 */
+    boolean ExtendedTranslationEnabled:1;		/* Bit 7 */
+  } Bits;
+}
+BusLogic_GeometryRegister_T;
 
 
 /*
@@ -293,8 +505,8 @@
   BusLogic_ExecuteSCSICommand =			0x83,
   BusLogic_InquireFirmwareVersion3rdDigit =	0x84,
   BusLogic_InquireFirmwareVersionLetter =	0x85,
-  BusLogic_InquireGenericIOPortInformation =	0x86,
-  BusLogic_InquireControllerModelNumber =	0x8B,
+  BusLogic_InquirePCIHostAdapterInformation =	0x86,
+  BusLogic_InquireHostAdapterModelNumber =	0x8B,
   BusLogic_InquireSynchronousPeriod =		0x8C,
   BusLogic_InquireExtendedSetupInformation =	0x8D,
   BusLogic_EnableStrictRoundRobinMode =		0x8F,
@@ -341,7 +553,7 @@
   Define the Inquire Target Devices reply type.  Inquire Target Devices only
   tests Logical Unit 0 of each Target Device unlike the Inquire Installed
   Devices commands which test Logical Units 0 - 7.  Two bytes are returned,
-  where bit 0 set indicates that Target Device 0 exists, and so on.
+  where byte 0 bit 0 set indicates that Target Device 0 exists, and so on.
 */
 
 typedef unsigned short BusLogic_InstalledDevices_T;
@@ -392,7 +604,7 @@
 typedef struct BusLogic_SetupInformation
 {
   boolean SynchronousInitiationEnabled:1;		/* Byte 0 Bit 0 */
-  boolean ParityCheckEnabled:1;				/* Byte 0 Bit 1 */
+  boolean ParityCheckingEnabled:1;			/* Byte 0 Bit 1 */
   unsigned char :6;					/* Byte 0 Bits 2-7 */
   unsigned char BusTransferRate;			/* Byte 1 */
   unsigned char PreemptTimeOnBus;			/* Byte 2 */
@@ -419,8 +631,9 @@
 typedef struct BusLogic_ExtendedMailboxRequest
 {
   unsigned char MailboxCount;				/* Byte 0 */
-  bus_address_t BaseMailboxAddress __attribute__ ((packed)); /* Bytes 1-4 */
+  BusLogic_BusAddress_T BaseMailboxAddress;		/* Bytes 1-4 */
 }
+__attribute__ ((packed))
 BusLogic_ExtendedMailboxRequest_T;
 
 
@@ -439,12 +652,28 @@
 
 
 /*
-  Define the Inquire Generic I/O Port Information reply type.
+  Define the Inquire PCI Host Adapter Information reply type.  The ISA
+  Compatible I/O Port values are defined here and are also used with
+  the Modify I/O Address command.
 */
 
-typedef struct BusLogic_GenericIOPortInformation
+typedef enum BusLogic_ISACompatibleIOPort
 {
-  unsigned char ISACompatibleIOPort;			/* Byte 0 */
+  BusLogic_IO_330 =				0,
+  BusLogic_IO_334 =				1,
+  BusLogic_IO_230 =				2,
+  BusLogic_IO_234 =				3,
+  BusLogic_IO_130 =				4,
+  BusLogic_IO_134 =				5,
+  BusLogic_IO_Disable =				6,
+  BusLogic_IO_Disable2 =			7
+}
+__attribute__ ((packed))
+BusLogic_ISACompatibleIOPort_T;
+
+typedef struct BusLogic_PCIHostAdapterInformation
+{
+  BusLogic_ISACompatibleIOPort_T ISACompatibleIOPort;	/* Byte 0 */
   unsigned char PCIAssignedIRQChannel;			/* Byte 1 */
   boolean LowByteTerminated:1;				/* Byte 2 Bit 0 */
   boolean HighByteTerminated:1;				/* Byte 2 Bit 1 */
@@ -452,17 +681,17 @@
   boolean JP1:1;					/* Byte 2 Bit 4 */
   boolean JP2:1;					/* Byte 2 Bit 5 */
   boolean JP3:1;					/* Byte 2 Bit 6 */
-  boolean Valid:1;					/* Byte 2 Bit 7 */
+  boolean GenericInfoValid:1;				/* Byte 2 Bit 7 */
   unsigned char :8;					/* Byte 3 */
 }
-BusLogic_GenericIOPortInformation_T;
+BusLogic_PCIHostAdapterInformation_T;
 
 
 /*
-  Define the Inquire Controller Model Number reply type.
+  Define the Inquire Host Adapter Model Number reply type.
 */
 
-typedef unsigned char BusLogic_ControllerModelNumber_T[5];
+typedef unsigned char BusLogic_HostAdapterModelNumber_T[5];
 
 
 /*
@@ -484,18 +713,21 @@
   unsigned char BIOS_Address;				/* Byte 1 */
   unsigned short ScatterGatherLimit;			/* Bytes 2-3 */
   unsigned char MailboxCount;				/* Byte 4 */
-  bus_address_t BaseMailboxAddress __attribute__ ((packed)); /* Bytes 5-8 */
-  struct { unsigned char :6;				/* Byte 9 Bits 0-5 */
-	   boolean LevelSensitiveInterrupts:1;		/* Byte 9 Bit 6 */
+  BusLogic_BusAddress_T BaseMailboxAddress;		/* Bytes 5-8 */
+  struct { unsigned char :2;				/* Byte 9 Bits 0-1 */
+	   boolean FastOnEISA:1;			/* Byte 9 Bit 2 */
+	   unsigned char :3;				/* Byte 9 Bits 3-5 */
+	   boolean LevelSensitiveInterrupt:1;		/* Byte 9 Bit 6 */
 	   unsigned char :1; } Misc;			/* Byte 9 Bit 7 */
   unsigned char FirmwareRevision[3];			/* Bytes 10-12 */
   boolean HostWideSCSI:1;				/* Byte 13 Bit 0 */
   boolean HostDifferentialSCSI:1;			/* Byte 13 Bit 1 */
-  boolean HostAutomaticConfiguration:1;			/* Byte 13 Bit 2 */
+  boolean HostSupportsSCAM:1;				/* Byte 13 Bit 2 */
   boolean HostUltraSCSI:1;				/* Byte 13 Bit 3 */
   boolean HostSmartTermination:1;			/* Byte 13 Bit 4 */
   unsigned char :3;					/* Byte 13 Bits 5-7 */
 }
+__attribute__ ((packed))
 BusLogic_ExtendedSetupInformation_T;
 
 
@@ -503,10 +735,13 @@
   Define the Enable Strict Round Robin Mode request type.
 */
 
-#define BusLogic_AggressiveRoundRobinMode	0x00
-#define BusLogic_StrictRoundRobinMode		0x01
-
-typedef unsigned char BusLogic_RoundRobinModeRequest_T;
+typedef enum BusLogic_RoundRobinModeRequest
+{
+  BusLogic_AggressiveRoundRobinMode =		0,
+  BusLogic_StrictRoundRobinMode =		1
+}
+__attribute__ ((packed))
+BusLogic_RoundRobinModeRequest_T;
 
 
 /*
@@ -525,21 +760,86 @@
 
 
 /*
-  Define the Host Adapter Local RAM Auto SCSI Byte 15 reply structure.
+  Define the Host Adapter Local RAM AutoSCSI structure.
 */
 
-typedef struct BusLogic_AutoSCSIByte15
+typedef struct BusLogic_AutoSCSIData
 {
-  unsigned char LowByteTerminated:1;			/* Bit 0 */
-  unsigned char :1;					/* Bit 1 */
-  unsigned char HighByteTerminated:1;			/* Bit 2 */
-  unsigned char :5;					/* Bits 3-7 */
+  unsigned char InternalFactorySignature[2];		/* Bytes 0-1 */
+  unsigned char InformationByteCount;			/* Byte 2 */
+  unsigned char HostAdapterType[6];			/* Bytes 3-8 */
+  unsigned char :8;					/* Byte 9 */
+  boolean FloppyEnabled:1;				/* Byte 10 Bit 0 */
+  boolean FloppySecondary:1;				/* Byte 10 Bit 1 */
+  boolean LevelSensitiveInterrupt:1;			/* Byte 10 Bit 2 */
+  unsigned char :2;					/* Byte 10 Bits 3-4 */
+  unsigned char SystemRAMAreaForBIOS:3;			/* Byte 10 Bits 5-7 */
+  unsigned char DMA_Channel:7;				/* Byte 11 Bits 0-6 */
+  boolean DMA_AutoConfiguration:1;			/* Byte 11 Bit 7 */
+  unsigned char IRQ_Channel:7;				/* Byte 12 Bits 0-6 */
+  boolean IRQ_AutoConfiguration:1;			/* Byte 12 Bit 7 */
+  unsigned char DMA_TransferRate;			/* Byte 13 */
+  unsigned char SCSI_ID;				/* Byte 14 */
+  boolean LowByteTerminated:1;				/* Byte 15 Bit 0 */
+  boolean ParityCheckingEnabled:1;			/* Byte 15 Bit 1 */
+  boolean HighByteTerminated:1;				/* Byte 15 Bit 2 */
+  boolean NoisyCablingEnvironment:1;			/* Byte 15 Bit 3 */
+  boolean FastSynchronousNegotiation:1;			/* Byte 15 Bit 4 */
+  boolean BusResetEnabled:1;				/* Byte 15 Bit 5 */
+  boolean :1;						/* Byte 15 Bit 6 */
+  boolean ActiveNegationEnabled:1;			/* Byte 15 Bit 7 */
+  unsigned char BusOnDelay;				/* Byte 16 */
+  unsigned char BusOffDelay;				/* Byte 17 */
+  boolean HostAdapterBIOSEnabled:1;			/* Byte 18 Bit 0 */
+  boolean BIOSRedirectionOfINT19Enabled:1;		/* Byte 18 Bit 1 */
+  boolean ExtendedTranslationEnabled:1;			/* Byte 18 Bit 2 */
+  boolean MapRemovableAsFixedEnabled:1;			/* Byte 18 Bit 3 */
+  boolean :1;						/* Byte 18 Bit 4 */
+  boolean BIOSSupportsMoreThan2DrivesEnabled:1;		/* Byte 18 Bit 5 */
+  boolean BIOSInterruptModeEnabled:1;			/* Byte 18 Bit 6 */
+  boolean FlopticalSupportEnabled:1;			/* Byte 19 Bit 7 */
+  unsigned short DeviceEnabled;				/* Bytes 19-20 */
+  unsigned short WidePermitted;				/* Bytes 21-22 */
+  unsigned short FastPermitted;				/* Bytes 23-24 */
+  unsigned short SynchronousPermitted;			/* Bytes 25-26 */
+  unsigned short DisconnectPermitted;			/* Bytes 27-28 */
+  unsigned short SendStartUnitCommand;			/* Bytes 29-30 */
+  unsigned short IgnoreInBIOSScan;			/* Bytes 31-32 */
+  unsigned char PCIInterruptPin:2;			/* Byte 33 Bits 0-1 */
+  unsigned char HostAdapterIOPortAddress:2;		/* Byte 33 Bits 2-3 */
+  boolean StrictRoundRobinModeEnabled:1;		/* Byte 33 Bit 4 */
+  boolean VESABusSpeedGreaterThan33MHz:1;		/* Byte 33 Bit 5 */
+  boolean VESABurstWriteEnabled:1;			/* Byte 33 Bit 6 */
+  boolean VESABurstReadEnabled:1;			/* Byte 33 Bit 7 */
+  unsigned short UltraPermitted;			/* Bytes 34-35 */
+  unsigned int :32;					/* Bytes 36-39 */
+  unsigned char :8;					/* Byte 40 */
+  unsigned char AutoSCSIMaximumLUN;			/* Byte 41 */
+  boolean :1;						/* Byte 42 Bit 0 */
+  boolean SCAM_Dominant:1;				/* Byte 42 Bit 1 */
+  boolean SCAM_Enabled:1;				/* Byte 42 Bit 2 */
+  boolean SCAM_Level2:1;				/* Byte 42 Bit 3 */
+  unsigned char :4;					/* Byte 42 Bits 4-7 */
+  boolean INT13ExtensionEnabled:1;			/* Byte 43 Bit 0 */
+  boolean :1;						/* Byte 43 Bit 1 */
+  boolean CDROMBootEnabled:1;				/* Byte 43 Bit 2 */
+  unsigned char :5;					/* Byte 43 Bits 3-7 */
+  unsigned char BootTargetID:4;				/* Byte 44 Bits 0-3 */
+  unsigned char BootChannel:4;				/* Byte 44 Bits 4-7 */
+  unsigned char ForceBusDeviceScanningOrder:1;		/* Byte 45 Bit 0 */
+  unsigned char :7;					/* Byte 45 Bits 1-7 */
+  unsigned short NonTaggedToAlternateLUNPermitted;	/* Bytes 46-47 */
+  unsigned short RenegotiateSyncAfterCheckCondition;	/* Bytes 48-49 */
+  unsigned char Reserved[10];				/* Bytes 50-59 */
+  unsigned char ManufacturingDiagnostic[2];		/* Bytes 60-61 */
+  unsigned short Checksum;				/* Bytes 62-63 */
 }
-BusLogic_AutoSCSIByte15_T;
+__attribute__ ((packed))
+BusLogic_AutoSCSIData_T;
 
 
 /*
-  Define the Host Adapter Local RAM Auto SCSI Byte 45 reply structure.
+  Define the Host Adapter Local RAM Auto SCSI Byte 45 structure.
 */
 
 typedef struct BusLogic_AutoSCSIByte45
@@ -551,39 +851,48 @@
 
 
 /*
+  Define the Host Adapter Local RAM BIOS Drive Map Byte structure.
+*/
+
+#define BusLogic_BIOS_DriveMapOffset		17
+
+typedef struct BusLogic_BIOSDriveMapByte
+{
+  unsigned char TargetIDBit3:1;				/* Bit 0 */
+  unsigned char :2;					/* Bits 1-2 */
+  BusLogic_BIOS_DiskGeometryTranslation_T DiskGeometry:2; /* Bits 3-4 */
+  unsigned char TargetID:3;				/* Bits 5-7 */
+}
+BusLogic_BIOSDriveMapByte_T;
+
+
+/*
   Define the Modify I/O Address request type.  On PCI Host Adapters, the
   Modify I/O Address command allows modification of the ISA compatible I/O
   Address that the Host Adapter responds to; it does not affect the PCI
   compliant I/O Address assigned at system initialization.
 */
 
-#define BusLogic_ModifyIO_330			0x00
-#define BusLogic_ModifyIO_334			0x01
-#define BusLogic_ModifyIO_230			0x02
-#define BusLogic_ModifyIO_234			0x03
-#define BusLogic_ModifyIO_130			0x04
-#define BusLogic_ModifyIO_134			0x05
-#define BusLogic_ModifyIO_Disable		0x06
-#define BusLogic_ModifyIO_Disable2		0x07
-
-typedef unsigned char BusLogic_ModifyIOAddressRequest_T;
+typedef BusLogic_ISACompatibleIOPort_T BusLogic_ModifyIOAddressRequest_T;
 
 
 /*
-  Define the Set CCB Format request type.  64 LUN Format CCBs are necessary to
-  support 64 Logical Units per Target Device.  8 LUN Format CCBs only support 8
-  Logical Units per Target Device.
+  Define the Set CCB Format request type.  Extended LUN Format CCBs are
+  necessary to support more than 8 Logical Units per Target Device.
 */
 
-#define BusLogic_8LUNFormatCCB			0x00
-#define BusLogic_64LUNFormatCCB			0x01
-
-typedef unsigned char BusLogic_SetCCBFormatRequest_T;
+typedef enum BusLogic_SetCCBFormatRequest
+{
+  BusLogic_LegacyLUNFormatCCB =			0,
+  BusLogic_ExtendedLUNFormatCCB =		1
+}
+__attribute__ ((packed))
+BusLogic_SetCCBFormatRequest_T;
 
 
 /*
   Define the Requested Reply Length type used by the Inquire Setup Information,
-  Inquire Controller Model Number, Inquire Synchronous Period, and Inquire
+  Inquire Host Adapter Model Number, Inquire Synchronous Period, and Inquire
   Extended Setup Information commands.
 */
 
@@ -591,10 +900,10 @@
 
 
 /*
-  Define a Lock data structure.  Until a true symmetric multiprocessing kernel
-  with fine grained locking is available, acquiring the lock is implemented as
-  saving the processor flags and disabling interrupts, and releasing the lock
-  restores the saved processor flags.
+  Define the Lock data structure.  Until a true symmetric multiprocessing
+  kernel with fine grained locking is available, acquiring the lock is
+  implemented as saving the processor flags and disabling interrupts, and
+  releasing the lock restores the saved processor flags.
 */
 
 typedef unsigned long BusLogic_Lock_T;
@@ -606,25 +915,30 @@
 
 typedef enum
 {
-  BusLogic_OutgoingMailboxFree =		0,
-  BusLogic_MailboxStartCommand =		1,
-  BusLogic_MailboxAbortCommand =		2
+  BusLogic_OutgoingMailboxFree =		0x00,
+  BusLogic_MailboxStartCommand =		0x01,
+  BusLogic_MailboxAbortCommand =		0x02
 }
+__attribute__ ((packed))
 BusLogic_ActionCode_T;
 
 
 /*
-  Define the Incoming Mailbox Completion Codes.
+  Define the Incoming Mailbox Completion Codes.  The MultiMaster Firmware
+  only uses codes 0 - 4.  The FlashPoint SCCB Manager has no mailboxes, so
+  completion codes are stored in the CCB; it only uses codes 1, 2, 4, and 5.
 */
 
 typedef enum
 {
-  BusLogic_IncomingMailboxFree =		0,
-  BusLogic_CommandCompletedWithoutError =	1,
-  BusLogic_CommandAbortedAtHostRequest =	2,
-  BusLogic_AbortedCommandNotFound =		3,
-  BusLogic_CommandCompletedWithError =		4
+  BusLogic_IncomingMailboxFree =		0x00,
+  BusLogic_CommandCompletedWithoutError =	0x01,
+  BusLogic_CommandAbortedAtHostRequest =	0x02,
+  BusLogic_AbortedCommandNotFound =		0x03,
+  BusLogic_CommandCompletedWithError =		0x04,
+  BusLogic_InvalidCCB =				0x05
 }
+__attribute__ ((packed))
 BusLogic_CompletionCode_T;
 
 
@@ -641,6 +955,7 @@
   BusLogic_InitiatorCCB_ScatterGatherResidual =	0x04,
   BusLogic_BusDeviceReset =			0x81
 }
+__attribute__ ((packed))
 BusLogic_CCB_Opcode_T;
 
 
@@ -650,16 +965,17 @@
 
 typedef enum
 {
-  BusLogic_UncheckedDataTransfer =		0x00,
-  BusLogic_DataInLengthChecked =		0x01,
-  BusLogic_DataOutLengthChecked =		0x02,
-  BusLogic_NoDataTransfer =			0x03
+  BusLogic_UncheckedDataTransfer =		0,
+  BusLogic_DataInLengthChecked =		1,
+  BusLogic_DataOutLengthChecked =		2,
+  BusLogic_NoDataTransfer =			3
 }
 BusLogic_DataDirection_T;
 
 
 /*
-  Define the Host Adapter Status Codes.
+  Define the Host Adapter Status Codes.  The MultiMaster Firmware does not
+  return status code 0x0C; it uses 0x12 for both overruns and underruns.
 */
 
 typedef enum
@@ -667,8 +983,9 @@
   BusLogic_CommandCompletedNormally =		0x00,
   BusLogic_LinkedCommandCompleted =		0x0A,
   BusLogic_LinkedCommandCompletedWithFlag =	0x0B,
+  BusLogic_DataUnderRun =			0x0C,
   BusLogic_SCSISelectionTimeout =		0x11,
-  BusLogic_DataOverUnderRun =			0x12,
+  BusLogic_DataOverRun =			0x12,
   BusLogic_UnexpectedBusFree =			0x13,
   BusLogic_InvalidBusPhaseRequested =		0x14,
   BusLogic_InvalidOutgoingMailboxActionCode =	0x15,
@@ -689,6 +1006,7 @@
   BusLogic_HostAdapterHardwareTimeoutError =	0x30,
   BusLogic_SCSIParityErrorDetected =		0x34
 }
+__attribute__ ((packed))
 BusLogic_HostAdapterStatus_T;
 
 
@@ -702,6 +1020,7 @@
   BusLogic_CheckCondition =			0x02,
   BusLogic_DeviceBusy =				0x08
 }
+__attribute__ ((packed))
 BusLogic_TargetDeviceStatus_T;
 
 
@@ -711,10 +1030,10 @@
 
 typedef enum
 {
-  BusLogic_SimpleQueueTag =			0x00,
-  BusLogic_HeadOfQueueTag =			0x01,
-  BusLogic_OrderedQueueTag =			0x02,
-  BusLogic_ReservedQT =				0x03
+  BusLogic_SimpleQueueTag =			0,
+  BusLogic_HeadOfQueueTag =			1,
+  BusLogic_OrderedQueueTag =			2,
+  BusLogic_ReservedQT =				3
 }
 BusLogic_QueueTag_T;
 
@@ -729,59 +1048,75 @@
 
 
 /*
-  Define the Scatter/Gather Segment structure required by the Host Adapter
-  Firmware Interface.
+  Define the Scatter/Gather Segment structure required by the MultiMaster
+  Firmware Interface and the FlashPoint SCCB Manager.
 */
 
 typedef struct BusLogic_ScatterGatherSegment
 {
-  unsigned int SegmentByteCount;			/* Bytes 0-3 */
-  bus_address_t SegmentDataPointer;			/* Bytes 4-7 */
+  BusLogic_ByteCount_T SegmentByteCount;		/* Bytes 0-3 */
+  BusLogic_BusAddress_T SegmentDataPointer;		/* Bytes 4-7 */
 }
 BusLogic_ScatterGatherSegment_T;
 
 
 /*
   Define the 32 Bit Mode Command Control Block (CCB) structure.  The first 40
-  bytes are defined by the Host Adapter Firmware Interface.  The remaining
-  components are defined by the Linux BusLogic Driver.  64 LUN Format CCBs
-  differ from standard 8 LUN Format 32 Bit Mode CCBs only in having the
-  TagEnable and QueueTag fields moved from byte 17 to byte 1, and the Logical
-  Unit field in byte 17 expanded to 6 bits; unfortunately, using a union of
-  structs containing enumeration type bitfields to provide both definitions
-  leads to packing problems, so the following definition is used which requires
-  setting TagEnable to Logical Unit bit 5 in 64 LUN Format CCBs.
+  bytes are defined by and common to both the MultiMaster Firmware and the
+  FlashPoint SCCB Manager.  The next 60 bytes are defined by the FlashPoint
+  SCCB Manager.  The remaining components are defined by the Linux BusLogic
+  Driver.  Extended LUN Format CCBs differ from Legacy LUN Format 32 Bit Mode
+  CCBs only in having the TagEnable and QueueTag fields moved from byte 17 to
+  byte 1, and the Logical Unit field in byte 17 expanded to 6 bits.  In theory,
+  Extended LUN Format CCBs can support up to 64 Logical Units, but in practice
+  many devices will respond improperly to Logical Units between 32 and 63, and
+  the SCSI-2 specification defines Bit 5 as LUNTAR.  Extended LUN Format CCBs
+  are used by recent versions of the MultiMaster Firmware, as well as by the
+  FlashPoint SCCB Manager; the FlashPoint SCCB Manager only supports 32 Logical
+  Units.  Since 64 Logical Units are unlikely to be needed in practice, and
+  since they are problematic for the above reasons, and since limiting them to
+  5 bits simplifies the CCB structure definition, this driver only supports
+  32 Logical Units per Target Device.
 */
 
 typedef struct BusLogic_CCB
 {
   /*
-    BusLogic Host Adapter Firmware Portion.
+    MultiMaster Firmware and FlashPoint SCCB Manager Common Portion.
   */
-  BusLogic_CCB_Opcode_T Opcode:8;			/* Byte 0 */
+  BusLogic_CCB_Opcode_T Opcode;				/* Byte 0 */
   unsigned char :3;					/* Byte 1 Bits 0-2 */
   BusLogic_DataDirection_T DataDirection:2;		/* Byte 1 Bits 3-4 */
-  boolean TagEnable64LUN:1;				/* Byte 1 Bit 5 */
-  BusLogic_QueueTag_T QueueTag64LUN:2;			/* Byte 1 Bits 6-7 */
+  boolean TagEnable:1;					/* Byte 1 Bit 5 */
+  BusLogic_QueueTag_T QueueTag:2;			/* Byte 1 Bits 6-7 */
   unsigned char CDB_Length;				/* Byte 2 */
   unsigned char SenseDataLength;			/* Byte 3 */
-  unsigned int DataLength;				/* Bytes 4-7 */
-  bus_address_t DataPointer;				/* Bytes 8-11 */
+  BusLogic_ByteCount_T DataLength;			/* Bytes 4-7 */
+  BusLogic_BusAddress_T DataPointer;			/* Bytes 8-11 */
   unsigned char :8;					/* Byte 12 */
   unsigned char :8;					/* Byte 13 */
-  BusLogic_HostAdapterStatus_T HostAdapterStatus:8;	/* Byte 14 */
-  BusLogic_TargetDeviceStatus_T TargetDeviceStatus:8;	/* Byte 15 */
+  BusLogic_HostAdapterStatus_T HostAdapterStatus;	/* Byte 14 */
+  BusLogic_TargetDeviceStatus_T TargetDeviceStatus;	/* Byte 15 */
   unsigned char TargetID;				/* Byte 16 */
   unsigned char LogicalUnit:5;				/* Byte 17 Bits 0-4 */
-  boolean TagEnable:1;					/* Byte 17 Bit 5 */
-  BusLogic_QueueTag_T QueueTag:2;			/* Byte 17 Bits 6-7 */
+  boolean LegacyTagEnable:1;				/* Byte 17 Bit 5 */
+  BusLogic_QueueTag_T LegacyQueueTag:2;			/* Byte 17 Bits 6-7 */
   SCSI_CDB_T CDB;					/* Bytes 18-29 */
   unsigned char :8;					/* Byte 30 */
   unsigned char :8;					/* Byte 31 */
   unsigned int :32;					/* Bytes 32-35 */
-  bus_address_t SenseDataPointer;			/* Bytes 36-39 */
+  BusLogic_BusAddress_T SenseDataPointer;		/* Bytes 36-39 */
+  /*
+    FlashPoint SCCB Manager Defined Portion.
+  */
+  void (*CallbackFunction)(struct BusLogic_CCB *);	/* Bytes 40-43 */
+  BusLogic_IO_Address_T BaseAddress;			/* Bytes 44-47 */
+  BusLogic_CompletionCode_T CompletionCode;		/* Byte 48 */
+  unsigned char :8;					/* Byte 49 */
+  unsigned short OS_Flags;				/* Bytes 50-51 */
+  unsigned char Private[48];				/* Bytes 52-99 */
   /*
-    BusLogic Linux Driver Portion.
+    BusLogic Linux Driver Defined Portion.
   */
   struct BusLogic_HostAdapter *HostAdapter;
   SCSI_Command_T *Command;
@@ -789,13 +1124,13 @@
 	 BusLogic_CCB_Active =	    1,
 	 BusLogic_CCB_Completed =   2,
 	 BusLogic_CCB_Reset =	    3 } Status;
-  BusLogic_CompletionCode_T MailboxCompletionCode;
   unsigned long SerialNumber;
   struct BusLogic_CCB *Next;
   struct BusLogic_CCB *NextAll;
   BusLogic_ScatterGatherSegment_T
     ScatterGatherList[BusLogic_ScatterGatherLimit];
 }
+__attribute__ ((packed))
 BusLogic_CCB_T;
 
 
@@ -805,9 +1140,9 @@
 
 typedef struct BusLogic_OutgoingMailbox
 {
-  bus_address_t CCB;					/* Bytes 0-3 */
-  unsigned int :24;					/* Byte 4 */
-  BusLogic_ActionCode_T ActionCode:8;			/* Bytes 5-7 */
+  BusLogic_BusAddress_T CCB;				/* Bytes 0-3 */
+  unsigned int :24;					/* Bytes 4-6 */
+  BusLogic_ActionCode_T ActionCode;			/* Byte 7 */
 }
 BusLogic_OutgoingMailbox_T;
 
@@ -818,85 +1153,155 @@
 
 typedef struct BusLogic_IncomingMailbox
 {
-  bus_address_t CCB;					/* Bytes 0-3 */
-  BusLogic_HostAdapterStatus_T HostAdapterStatus:8;	/* Byte 4 */
-  BusLogic_TargetDeviceStatus_T TargetDeviceStatus:8;	/* Byte 5 */
+  BusLogic_BusAddress_T CCB;				/* Bytes 0-3 */
+  BusLogic_HostAdapterStatus_T HostAdapterStatus;	/* Byte 4 */
+  BusLogic_TargetDeviceStatus_T TargetDeviceStatus;	/* Byte 5 */
   unsigned char :8;					/* Byte 6 */
-  BusLogic_CompletionCode_T CompletionCode:8;		/* Byte 7 */
+  BusLogic_CompletionCode_T CompletionCode;		/* Byte 7 */
 }
 BusLogic_IncomingMailbox_T;
 
 
 /*
-  Define the possible Bus Types.
-*/
-
-typedef enum
-{
-  BusLogic_Unknown_Bus =			0,
-  BusLogic_ISA_Bus =				1,
-  BusLogic_MCA_Bus =				2,
-  BusLogic_EISA_Bus =				3,
-  BusLogic_VESA_Bus =				4,
-  BusLogic_PCI_Bus =				5
-}
-BusLogic_BusType_T;
-
-static char
-  *BusLogic_BusNames[] =
-    { "Unknown", "ISA", "MCA", "EISA", "VESA", "PCI" };
-
-
-/*
   Define the Linux BusLogic Driver Command Line Entry structure.
 */
 
 typedef struct BusLogic_CommandLineEntry
 {
-  unsigned int IO_Address;
+  BusLogic_IO_Address_T IO_Address;
   unsigned short TaggedQueueDepth;
   unsigned short BusSettleTime;
-  unsigned short LocalOptions;
   unsigned short TaggedQueuingPermitted;
   unsigned short TaggedQueuingPermittedMask;
-  unsigned char ErrorRecoveryStrategy[BusLogic_MaxTargetDevices];
+  BusLogic_LocalOptions_T LocalOptions;
+  BusLogic_ErrorRecoveryStrategy_T
+    ErrorRecoveryStrategy[BusLogic_MaxTargetDevices];
 }
 BusLogic_CommandLineEntry_T;
 
 
 /*
+  Define the Host Adapter Target Device Statistics structure.
+*/
+
+#define BusLogic_SizeBuckets			10
+
+typedef unsigned int BusLogic_CommandSizeBuckets_T[BusLogic_SizeBuckets];
+
+typedef struct BusLogic_TargetDeviceStatistics
+{
+  unsigned int CommandsAttempted;
+  unsigned int CommandsCompleted;
+  unsigned int ReadCommands;
+  unsigned int WriteCommands;
+  BusLogic_ByteCounter_T TotalBytesRead;
+  BusLogic_ByteCounter_T TotalBytesWritten;
+  BusLogic_CommandSizeBuckets_T ReadCommandSizeBuckets;
+  BusLogic_CommandSizeBuckets_T WriteCommandSizeBuckets;
+  unsigned short CommandAbortsRequested;
+  unsigned short CommandAbortsAttempted;
+  unsigned short CommandAbortsCompleted;
+  unsigned short BusDeviceResetsRequested;
+  unsigned short BusDeviceResetsAttempted;
+  unsigned short BusDeviceResetsCompleted;
+  unsigned short HostAdapterResetsRequested;
+  unsigned short HostAdapterResetsAttempted;
+  unsigned short HostAdapterResetsCompleted;
+}
+BusLogic_TargetDeviceStatistics_T;
+
+
+/*
+  Define the FlashPoint Card Handle data type.
+*/
+
+#define FlashPoint_BadCardHandle		0xFFFFFFFF
+
+typedef unsigned int FlashPoint_CardHandle_T;
+
+
+/*
+  Define the FlashPoint Information structure.  This structure is defined
+  by the FlashPoint SCCB Manager.
+*/
+
+typedef struct FlashPoint_Info
+{
+  BusLogic_IO_Address_T BaseAddress;			/* Bytes 0-3 */
+  boolean Present;					/* Byte 4 */
+  unsigned char IRQ_Channel;				/* Byte 5 */
+  unsigned char SCSI_ID;				/* Byte 6 */
+  unsigned char SCSI_LUN;				/* Byte 7 */
+  unsigned short FirmwareRevision;			/* Bytes 8-9 */
+  unsigned short SynchronousPermitted;			/* Bytes 10-11 */
+  unsigned short FastPermitted;				/* Bytes 12-13 */
+  unsigned short UltraPermitted;			/* Bytes 14-15 */
+  unsigned short DisconnectPermitted;			/* Bytes 16-17 */
+  unsigned short WidePermitted;				/* Bytes 18-19 */
+  boolean ParityCheckingEnabled:1;			/* Byte 20 Bit 0 */
+  boolean HostWideSCSI:1;				/* Byte 20 Bit 1 */
+  boolean HostSoftReset:1;				/* Byte 20 Bit 2 */
+  boolean ExtendedTranslationEnabled:1;			/* Byte 20 Bit 3 */
+  boolean LowByteTerminated:1;				/* Byte 20 Bit 4 */
+  boolean HighByteTerminated:1;				/* Byte 20 Bit 5 */
+  boolean ReportDataUnderrun:1;				/* Byte 20 Bit 6 */
+  boolean SCAM_Enabled:1;				/* Byte 20 Bit 7 */
+  boolean SCAM_Level2:1;				/* Byte 21 Bit 0 */
+  unsigned char :7;					/* Byte 21 Bits 1-7 */
+  unsigned char Family;					/* Byte 22 */
+  unsigned char BusType;				/* Byte 23 */
+  unsigned char ModelNumber[3];				/* Bytes 24-26 */
+  unsigned char RelativeCardNumber;			/* Byte 27 */
+  unsigned char Reserved[4];				/* Bytes 28-31 */
+  unsigned int OS_Reserved;				/* Bytes 32-35 */
+  unsigned char TranslationInfo[4];			/* Bytes 36-39 */
+  unsigned int Reserved2[5];				/* Bytes 40-59 */
+  unsigned int SecondaryRange;				/* Bytes 60-63 */
+}
+FlashPoint_Info_T;
+
+
+/*
   Define the Linux BusLogic Driver Host Adapter structure.
 */
 
 typedef struct BusLogic_HostAdapter
 {
   SCSI_Host_T *SCSI_Host;
-  unsigned int IO_Address;
+  BusLogic_IO_Address_T IO_Address;
+  BusLogic_PCI_Address_T PCI_Address;
+  unsigned short AddressCount;
   unsigned char HostNumber;
   unsigned char ModelName[9];
   unsigned char FirmwareVersion[6];
-  unsigned char ControllerName[18];
-  unsigned char InterruptLabel[62];
+  unsigned char FullModelName[18];
+  unsigned char InterruptLabel[68];
   unsigned char IRQ_Channel;
   unsigned char DMA_Channel;
   unsigned char SCSI_ID;
-  BusLogic_BusType_T BusType:3;
+  unsigned char Bus;
+  unsigned char Device;
+  BusLogic_HostAdapterType_T HostAdapterType;
+  BusLogic_HostAdapterBusType_T HostAdapterBusType:3;
   boolean IRQ_ChannelAcquired:1;
   boolean DMA_ChannelAcquired:1;
-  boolean SynchronousInitiation:1;
-  boolean ParityChecking:1;
-  boolean ExtendedTranslation:1;
-  boolean LevelSensitiveInterrupts:1;
+  boolean ExtendedTranslationEnabled:1;
+  boolean ParityCheckingEnabled:1;
+  boolean BusResetEnabled;
+  boolean LevelSensitiveInterrupt:1;
   boolean HostWideSCSI:1;
   boolean HostDifferentialSCSI:1;
-  boolean HostAutomaticConfiguration:1;
+  boolean HostSupportsSCAM:1;
   boolean HostUltraSCSI:1;
+  boolean ExtendedLUNSupport:1;
   boolean TerminationInfoValid:1;
   boolean LowByteTerminated:1;
   boolean HighByteTerminated:1;
   boolean BounceBuffersRequired:1;
   boolean StrictRoundRobinModeSupport:1;
-  boolean Host64LUNSupport:1;
+  boolean SCAM_Enabled:1;
+  boolean SCAM_Level2:1;
+  boolean HostAdapterInitialized;
   boolean HostAdapterResetRequested:1;
   volatile boolean HostAdapterCommandCompleted:1;
   unsigned short HostAdapterScatterGatherLimit;
@@ -906,27 +1311,40 @@
   unsigned short MailboxCount;
   unsigned short InitialCCBs;
   unsigned short IncrementalCCBs;
-  unsigned short TotalQueueDepth;
+  unsigned short DriverQueueDepth;
+  unsigned short HostAdapterQueueDepth;
   unsigned short TaggedQueueDepth;
   unsigned short UntaggedQueueDepth;
   unsigned short BusSettleTime;
-  unsigned short LocalOptions;
+  unsigned short SynchronousPermitted;
+  unsigned short FastPermitted;
+  unsigned short UltraPermitted;
+  unsigned short WidePermitted;
   unsigned short DisconnectPermitted;
   unsigned short TaggedQueuingPermitted;
-  bus_address_t BIOS_Address;
+  unsigned short ExternalHostAdapterResets;
+  BusLogic_LocalOptions_T LocalOptions;
+  BusLogic_BusAddress_T BIOS_Address;
   BusLogic_InstalledDevices_T InstalledDevices;
   BusLogic_SynchronousValues_T SynchronousValues;
   BusLogic_SynchronousPeriod_T SynchronousPeriod;
   BusLogic_CommandLineEntry_T *CommandLineEntry;
+  FlashPoint_Info_T *FlashPointInfo;
+  FlashPoint_CardHandle_T CardHandle;
   struct BusLogic_HostAdapter *Next;
+  char *MessageBuffer;
+  int MessageBufferLength;
   BusLogic_CCB_T *All_CCBs;
   BusLogic_CCB_T *Free_CCBs;
   BusLogic_CCB_T *BusDeviceResetPendingCCB[BusLogic_MaxTargetDevices];
-  unsigned char ErrorRecoveryStrategy[BusLogic_MaxTargetDevices];
-  unsigned char TaggedQueuingActive[BusLogic_MaxTargetDevices];
-  unsigned char CommandSuccessfulFlag[BusLogic_MaxTargetDevices];
-  unsigned char ActiveCommandCount[BusLogic_MaxTargetDevices];
-  unsigned long TotalCommandCount[BusLogic_MaxTargetDevices];
+  BusLogic_ErrorRecoveryStrategy_T
+    ErrorRecoveryStrategy[BusLogic_MaxTargetDevices];
+  boolean TaggedQueuingSupported[BusLogic_MaxTargetDevices];
+  boolean TaggedQueuingActive[BusLogic_MaxTargetDevices];
+  boolean CommandSuccessfulFlag[BusLogic_MaxTargetDevices];
+  unsigned char QueueDepth[BusLogic_MaxTargetDevices];
+  unsigned char ActiveCommands[BusLogic_MaxTargetDevices];
+  unsigned int CommandsSinceReset[BusLogic_MaxTargetDevices];
   unsigned long LastSequencePoint[BusLogic_MaxTargetDevices];
   unsigned long LastResetTime[BusLogic_MaxTargetDevices];
   BusLogic_OutgoingMailbox_T *FirstOutgoingMailbox;
@@ -935,6 +1353,7 @@
   BusLogic_IncomingMailbox_T *FirstIncomingMailbox;
   BusLogic_IncomingMailbox_T *LastIncomingMailbox;
   BusLogic_IncomingMailbox_T *NextIncomingMailbox;
+  BusLogic_TargetDeviceStatistics_T *TargetDeviceStatistics;
 }
 BusLogic_HostAdapter_T;
 
@@ -1007,42 +1426,78 @@
 */
 
 static inline
-void BusLogic_WriteControlRegister(BusLogic_HostAdapter_T *HostAdapter,
-				   unsigned char Value)
+void BusLogic_SCSIBusReset(BusLogic_HostAdapter_T *HostAdapter)
+{
+  BusLogic_ControlRegister_T ControlRegister;
+  ControlRegister.All = 0;
+  ControlRegister.Bits.SCSIBusReset = true;
+  outb(ControlRegister.All,
+       HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+}
+
+static inline
+void BusLogic_InterruptReset(BusLogic_HostAdapter_T *HostAdapter)
+{
+  BusLogic_ControlRegister_T ControlRegister;
+  ControlRegister.All = 0;
+  ControlRegister.Bits.InterruptReset = true;
+  outb(ControlRegister.All,
+       HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+}
+
+static inline
+void BusLogic_SoftReset(BusLogic_HostAdapter_T *HostAdapter)
+{
+  BusLogic_ControlRegister_T ControlRegister;
+  ControlRegister.All = 0;
+  ControlRegister.Bits.SoftReset = true;
+  outb(ControlRegister.All,
+       HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
+}
+
+static inline
+void BusLogic_HardReset(BusLogic_HostAdapter_T *HostAdapter)
 {
-  outb(Value, HostAdapter->IO_Address + BusLogic_ControlRegister);
+  BusLogic_ControlRegister_T ControlRegister;
+  ControlRegister.All = 0;
+  ControlRegister.Bits.HardReset = true;
+  outb(ControlRegister.All,
+       HostAdapter->IO_Address + BusLogic_ControlRegisterOffset);
 }
 
 static inline
 unsigned char BusLogic_ReadStatusRegister(BusLogic_HostAdapter_T *HostAdapter)
 {
-  return inb(HostAdapter->IO_Address + BusLogic_StatusRegister);
+  return inb(HostAdapter->IO_Address + BusLogic_StatusRegisterOffset);
 }
 
 static inline
-void BusLogic_WriteCommandParameterRegister(BusLogic_HostAdapter_T *HostAdapter,
+void BusLogic_WriteCommandParameterRegister(BusLogic_HostAdapter_T
+					      *HostAdapter,
 					    unsigned char Value)
 {
-  outb(Value, HostAdapter->IO_Address + BusLogic_CommandParameterRegister);
+  outb(Value,
+       HostAdapter->IO_Address + BusLogic_CommandParameterRegisterOffset);
 }
 
 static inline
 unsigned char BusLogic_ReadDataInRegister(BusLogic_HostAdapter_T *HostAdapter)
 {
-  return inb(HostAdapter->IO_Address + BusLogic_DataInRegister);
+  return inb(HostAdapter->IO_Address + BusLogic_DataInRegisterOffset);
 }
 
 static inline
 unsigned char BusLogic_ReadInterruptRegister(BusLogic_HostAdapter_T
 					     *HostAdapter)
 {
-  return inb(HostAdapter->IO_Address + BusLogic_InterruptRegister);
+  return inb(HostAdapter->IO_Address + BusLogic_InterruptRegisterOffset);
 }
 
 static inline
-unsigned char BusLogic_ReadGeometryRegister(BusLogic_HostAdapter_T *HostAdapter)
+unsigned char BusLogic_ReadGeometryRegister(BusLogic_HostAdapter_T
+					    *HostAdapter)
 {
-  return inb(HostAdapter->IO_Address + BusLogic_GeometryRegister);
+  return inb(HostAdapter->IO_Address + BusLogic_GeometryRegisterOffset);
 }
 
 
@@ -1079,26 +1534,165 @@
   and PCI/VLB/EISA/ISA Bus Addresses.
 */
 
-static inline bus_address_t Virtual_to_Bus(void *VirtualAddress)
+static inline BusLogic_BusAddress_T Virtual_to_Bus(void *VirtualAddress)
 {
-  return (bus_address_t) virt_to_bus(VirtualAddress);
+  return (BusLogic_BusAddress_T) virt_to_bus(VirtualAddress);
 }
 
-static inline void *Bus_to_Virtual(bus_address_t BusAddress)
+static inline void *Bus_to_Virtual(BusLogic_BusAddress_T BusAddress)
 {
   return (void *) bus_to_virt(BusAddress);
 }
 
 
 /*
+  BusLogic_IncrementErrorCounter increments Error Counter by 1, stopping at
+  65535 rather than wrapping around to 0.
+*/
+
+static inline void BusLogic_IncrementErrorCounter(unsigned short *ErrorCounter)
+{
+  if (*ErrorCounter < 65535) (*ErrorCounter)++;
+}
+
+
+/*
+  BusLogic_IncrementByteCounter increments Byte Counter by Amount.
+*/
+
+static inline void BusLogic_IncrementByteCounter(BusLogic_ByteCounter_T
+						   *ByteCounter,
+						 unsigned int Amount)
+{
+  ByteCounter->Units += Amount;
+  if (ByteCounter->Units > 999999999)
+    {
+      ByteCounter->Units -= 1000000000;
+      ByteCounter->Billions++;
+    }
+}
+
+
+/*
+  BusLogic_IncrementSizeBucket increments the Bucket for Amount.
+*/
+
+static inline void BusLogic_IncrementSizeBucket(BusLogic_CommandSizeBuckets_T
+						  CommandSizeBuckets,
+						unsigned int Amount)
+{
+  int Index = 0;
+  if (Amount < 8*1024)
+    if (Amount < 2*1024)
+      Index = (Amount < 1*1024 ? 0 : 1);
+    else Index = (Amount < 4*1024 ? 2 : 3);
+  else if (Amount < 128*1024)
+    if (Amount < 32*1024)
+      Index = (Amount < 16*1024 ? 4 : 5);
+    else Index = (Amount < 64*1024 ? 6 : 7);
+  else Index = (Amount < 256*1024 ? 8 : 9);
+  CommandSizeBuckets[Index]++;
+}
+
+
+/*
+  If CONFIG_PCI is not set, force CONFIG_SCSI_OMIT_FLASHPOINT, and use the
+  ISA only probe function as the general one.
+*/
+
+#ifndef CONFIG_PCI
+
+#undef CONFIG_SCSI_OMIT_FLASHPOINT
+#define CONFIG_SCSI_OMIT_FLASHPOINT
+#define BusLogic_InitializeProbeInfoListISA BusLogic_InitializeProbeInfoList
+
+#endif
+
+
+/*
+  Define macros for testing the Host Adapter Type.
+*/
+
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+
+#define BusLogic_MultiMasterHostAdapterP(HostAdapter) \
+  (HostAdapter->HostAdapterType == BusLogic_MultiMaster)
+
+#define BusLogic_FlashPointHostAdapterP(HostAdapter) \
+  (HostAdapter->HostAdapterType == BusLogic_FlashPoint)
+
+#else
+
+#define BusLogic_MultiMasterHostAdapterP(HostAdapter) \
+  (true)
+
+#define BusLogic_FlashPointHostAdapterP(HostAdapter) \
+  (false)
+
+#endif
+
+
+/*
+  Define Driver Message Macros.
+*/
+
+#define BusLogic_Announce(Format, Arguments...) \
+  BusLogic_Message(BusLogic_AnnounceLevel, Format, ##Arguments)
+
+#define BusLogic_Info(Format, Arguments...) \
+  BusLogic_Message(BusLogic_InfoLevel, Format, ##Arguments)
+
+#define BusLogic_Notice(Format, Arguments...) \
+  BusLogic_Message(BusLogic_NoticeLevel, Format, ##Arguments)
+
+#define BusLogic_Warning(Format, Arguments...) \
+  BusLogic_Message(BusLogic_WarningLevel, Format, ##Arguments)
+
+#define BusLogic_Error(Format, Arguments...) \
+  BusLogic_Message(BusLogic_ErrorLevel, Format, ##Arguments)
+
+
+/*
+  Define the version number of the FlashPoint Firmware (SCCB Manager).
+*/
+
+#define FlashPoint_FirmwareVersion		"5.01"
+
+
+/*
+  Define the possible return values from FlashPoint_HandleInterrupt.
+*/
+
+#define FlashPoint_NormalInterrupt		0x00
+#define FlashPoint_ExternalBusReset		0xFF
+
+
+/*
+  Define prototypes for the FlashPoint SCCB Manager Functions.
+*/
+
+extern unsigned char FlashPoint_ProbeHostAdapter(FlashPoint_Info_T *);
+extern FlashPoint_CardHandle_T
+       FlashPoint_HardResetHostAdapter(FlashPoint_Info_T *);
+extern void FlashPoint_StartCCB(FlashPoint_CardHandle_T, BusLogic_CCB_T *);
+extern int FlashPoint_AbortCCB(FlashPoint_CardHandle_T, BusLogic_CCB_T *);
+extern boolean FlashPoint_InterruptPending(FlashPoint_CardHandle_T);
+extern int FlashPoint_HandleInterrupt(FlashPoint_CardHandle_T);
+extern void FlashPoint_ReleaseHostAdapter(FlashPoint_CardHandle_T);
+
+
+/*
   Define prototypes for the forward referenced BusLogic Driver
   Internal Functions.
 */
 
+static void BusLogic_QueueCompletedCCB(BusLogic_CCB_T *CCB);
 static void BusLogic_InterruptHandler(int, void *, Registers_T *);
 static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *,
 				     SCSI_Command_T *,
 				     unsigned int);
+static void BusLogic_Message(BusLogic_MessageLevel_T, char *Format,
+			     BusLogic_HostAdapter_T *, ...);
 
 
 #endif /* BusLogic_DriverVersion */
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/Config.in linux/drivers/scsi/Config.in
--- v2.1.27/linux/drivers/scsi/Config.in	Sun Feb  2 05:18:43 1997
+++ linux/drivers/scsi/Config.in	Fri Feb 28 15:15:42 1997
@@ -26,6 +26,9 @@
 dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
 dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI
 dep_tristate 'BusLogic SCSI support' CONFIG_SCSI_BUSLOGIC $CONFIG_SCSI
+if [ "$CONFIG_SCSI_BUSLOGIC" != "n" ]; then
+    bool '  Omit FlashPoint support' CONFIG_SCSI_OMIT_FLASHPOINT
+fi
 dep_tristate 'DTC3180/3280 SCSI support' CONFIG_SCSI_DTC3280 $CONFIG_SCSI
 dep_tristate 'EATA-DMA (DPT, NEC, AT&T, SNI, AST, Olivetti, Alphatronix) support' CONFIG_SCSI_EATA_DMA $CONFIG_SCSI
 dep_tristate 'EATA-PIO (old DPT PM2001, PM2012A) support' CONFIG_SCSI_EATA_PIO $CONFIG_SCSI
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/FlashPoint.c linux/drivers/scsi/FlashPoint.c
--- v2.1.27/linux/drivers/scsi/FlashPoint.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/FlashPoint.c	Sun Feb 23 16:14:00 1997
@@ -0,0 +1,11776 @@
+/*
+
+  FlashPoint.c -- FlashPoint SCCB Manager for Linux
+
+  This file contains the FlashPoint SCCB Manager from BusLogic's FlashPoint
+  Driver Developer's Kit, with minor modifications by Leonard N. Zubkoff for
+  Linux compatibility.  It was provided by BusLogic in the form of 16 separate
+  source files, which would have unnecessarily cluttered the scsi directory, so
+  the individual files have been combined into this single file.
+
+  Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+
+  This file is available under both the GNU General Public License
+  and a BSD-style copyright; see LICENSE.FlashPoint for details.
+
+*/
+
+
+#include <linux/config.h>
+
+
+/*
+  If CONFIG_PCI is not set, force CONFIG_SCSI_OMIT_FLASHPOINT.
+*/
+
+#ifndef CONFIG_PCI
+
+#undef CONFIG_SCSI_OMIT_FLASHPOINT
+#define CONFIG_SCSI_OMIT_FLASHPOINT
+
+#endif
+
+
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+
+
+#define UNIX
+#define FW_TYPE		_SCCB_MGR_
+#define MAX_CARDS	8
+
+
+#include <asm/io.h>
+
+#define OS_InPortByte(port)		inb(port)
+#define OS_InPortWord(port)		inw(port)
+#define OS_InPortLong(port)		inl(port)
+#define OS_OutPortByte(port, value)	outb(value, port)
+#define OS_OutPortWord(port, value)	outw(value, port)
+#define OS_OutPortLong(port, value)	outl(value, port)
+#define OS_Lock(x)
+#define OS_UnLock(x)
+
+
+/*
+  Define name replacements for compatibility with the Linux BusLogic Driver.
+*/
+
+#define SccbMgr_sense_adapter		FlashPoint_ProbeHostAdapter
+#define SccbMgr_config_adapter		FlashPoint_HardResetHostAdapter
+#define SccbMgr_unload_card		FlashPoint_ReleaseHostAdapter
+#define SccbMgr_start_sccb		FlashPoint_StartCCB
+#define SccbMgr_abort_sccb		FlashPoint_AbortCCB
+#define SccbMgr_my_int			FlashPoint_InterruptPending
+#define SccbMgr_isr			FlashPoint_HandleInterrupt
+
+
+/*
+  Define name replacements to avoid kernel namespace pollution.
+*/
+
+#define BL_Card				FPT_BL_Card
+#define BusMasterInit			FPT_BusMasterInit
+#define CalcCrc16			FPT_CalcCrc16
+#define CalcLrc				FPT_CalcLrc
+#define ChkIfChipInitialized		FPT_ChkIfChipInitialized
+#define DiagBusMaster			FPT_DiagBusMaster
+#define DiagEEPROM			FPT_DiagEEPROM
+#define DiagXbow			FPT_DiagXbow
+#define GetTarLun			FPT_GetTarLun
+#define RNVRamData			FPT_RNVRamData
+#define RdStack				FPT_RdStack
+#define SccbMgrTableInitAll		FPT_SccbMgrTableInitAll
+#define SccbMgrTableInitCard		FPT_SccbMgrTableInitCard
+#define SccbMgrTableInitTarget		FPT_SccbMgrTableInitTarget
+#define SccbMgr_bad_isr			FPT_SccbMgr_bad_isr
+#define SccbMgr_scsi_reset		FPT_SccbMgr_scsi_reset
+#define SccbMgr_timer_expired		FPT_SccbMgr_timer_expired
+#define SendMsg				FPT_SendMsg
+#define Wait				FPT_Wait
+#define Wait1Second			FPT_Wait1Second
+#define WrStack				FPT_WrStack
+#define XbowInit			FPT_XbowInit
+#define autoCmdCmplt			FPT_autoCmdCmplt
+#define autoLoadDefaultMap		FPT_autoLoadDefaultMap
+#define busMstrDataXferStart		FPT_busMstrDataXferStart
+#define busMstrSGDataXferStart		FPT_busMstrSGDataXferStart
+#define busMstrTimeOut			FPT_busMstrTimeOut
+#define dataXferProcessor		FPT_dataXferProcessor
+#define default_intena			FPT_default_intena
+#define hostDataXferAbort		FPT_hostDataXferAbort
+#define hostDataXferRestart		FPT_hostDataXferRestart
+#define inisci				FPT_inisci
+#define mbCards				FPT_mbCards
+#define nvRamInfo			FPT_nvRamInfo
+#define phaseBusFree			FPT_phaseBusFree
+#define phaseChkFifo			FPT_phaseChkFifo
+#define phaseCommand			FPT_phaseCommand
+#define phaseDataIn			FPT_phaseDataIn
+#define phaseDataOut			FPT_phaseDataOut
+#define phaseDecode			FPT_phaseDecode
+#define phaseIllegal			FPT_phaseIllegal
+#define phaseMsgIn			FPT_phaseMsgIn
+#define phaseMsgOut			FPT_phaseMsgOut
+#define phaseStatus			FPT_phaseStatus
+#define queueAddSccb			FPT_queueAddSccb
+#define queueCmdComplete		FPT_queueCmdComplete
+#define queueDisconnect			FPT_queueDisconnect
+#define queueFindSccb			FPT_queueFindSccb
+#define queueFlushSccb			FPT_queueFlushSccb
+#define queueFlushTargSccb		FPT_queueFlushTargSccb
+#define queueSearchSelect		FPT_queueSearchSelect
+#define queueSelectFail			FPT_queueSelectFail
+#define s_PhaseTbl			FPT_s_PhaseTbl
+#define scamHAString			FPT_scamHAString
+#define scamInfo			FPT_scamInfo
+#define scarb				FPT_scarb
+#define scasid				FPT_scasid
+#define scbusf				FPT_scbusf
+#define sccbMgrTbl			FPT_sccbMgrTbl
+#define schkdd				FPT_schkdd
+#define scini				FPT_scini
+#define sciso				FPT_sciso
+#define scmachid			FPT_scmachid
+#define scsavdi				FPT_scsavdi
+#define scsel				FPT_scsel
+#define scsell				FPT_scsell
+#define scsendi				FPT_scsendi
+#define scvalq				FPT_scvalq
+#define scwirod				FPT_scwirod
+#define scwiros				FPT_scwiros
+#define scwtsel				FPT_scwtsel
+#define scxferc				FPT_scxferc
+#define sdecm				FPT_sdecm
+#define sfm				FPT_sfm
+#define shandem				FPT_shandem
+#define sinits				FPT_sinits
+#define sisyncn				FPT_sisyncn
+#define sisyncr				FPT_sisyncr
+#define siwidn				FPT_siwidn
+#define siwidr				FPT_siwidr
+#define sres				FPT_sres
+#define sresb				FPT_sresb
+#define ssel				FPT_ssel
+#define ssenss				FPT_ssenss
+#define sssyncv				FPT_sssyncv
+#define stsyncn				FPT_stsyncn
+#define stwidn				FPT_stwidn
+#define sxfrp				FPT_sxfrp
+#define utilEERead			FPT_utilEERead
+#define utilEESendCmdAddr		FPT_utilEESendCmdAddr
+#define utilEEWrite			FPT_utilEEWrite
+#define utilEEWriteOnOff		FPT_utilEEWriteOnOff
+#define utilUpdateResidual		FPT_utilUpdateResidual
+
+
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   globals.h  $
+ *
+ *   Description:  Common shared global defines.
+ *
+ *   $Date: 1996/09/04 01:26:13 $
+ *
+ *   $Revision: 1.11 $
+ *
+ *----------------------------------------------------------------------*/
+#ifndef __GLOBALS_H__
+#define __GLOBALS_H__
+
+#define _UCB_MGR_  1
+#define _SCCB_MGR_ 2
+
+/*#include <osflags.h>*/
+
+#define MAX_CDBLEN  12
+
+#define SCAM_LEV_2	1
+
+#define CRCMASK	0xA001
+
+/*  In your osflags.h file, please ENSURE that only ONE OS FLAG 
+    is on at a time !!! Also, please make sure you turn set the 
+ 	 variable FW_TYPE to either _UCB_MGR_ or _SCCB_MGR_  !!! */
+
+#if defined(DOS) || defined(WIN95_16) || defined(OS2) || defined(OTHER_16)
+   #define       COMPILER_16_BIT 1
+#elif defined(NETWARE) || defined(NT) || defined(WIN95_32) || defined(UNIX) || defined(OTHER_32) || defined(SOLARIS_REAL_MODE)
+   #define       COMPILER_32_BIT 1
+#endif
+
+
+#define     BL_VENDOR_ID      0x104B
+#define     FP_DEVICE_ID      0x8130
+#define     MM_DEVICE_ID      0x1040
+
+
+#ifndef FALSE
+#define FALSE           0
+#endif
+#ifndef TRUE
+#define TRUE            (!(FALSE))
+#endif
+
+#ifndef NULL
+#define NULL            0
+#endif
+
+#define FAILURE         0xFFFFFFFFL
+
+
+typedef unsigned char   UCHAR;
+typedef unsigned short  USHORT;
+typedef unsigned int    UINT;
+typedef unsigned long   ULONG;
+typedef unsigned char * PUCHAR;
+typedef unsigned short* PUSHORT;
+typedef unsigned long * PULONG;
+typedef void *          PVOID;
+
+
+#if defined(COMPILER_16_BIT)
+typedef unsigned char far       * uchar_ptr;
+typedef unsigned short far      * ushort_ptr;
+typedef unsigned long far       * ulong_ptr;
+#endif  /* 16_BIT_COMPILER */
+
+#if defined(COMPILER_32_BIT)
+typedef unsigned char           * uchar_ptr;
+typedef unsigned short          * ushort_ptr;
+typedef unsigned long           * ulong_ptr;
+#endif  /* 32_BIT_COMPILER */
+
+
+/*	 			NEW TYPE DEFINITIONS (shared with Mylex North)
+
+**  Use following type defines to avoid confusion in 16 and 32-bit
+**  environments.  Avoid using 'int' as it denotes 16 bits in 16-bit
+**  environment and 32 in 32-bit environments.
+
+*/
+
+#define s08bits	char
+#define s16bits 	short
+#define s32bits	long
+
+#define u08bits	unsigned s08bits
+#define u16bits	unsigned s16bits
+#define u32bits	unsigned s32bits
+
+#if defined(COMPILER_16_BIT)
+
+typedef u08bits far 	* pu08bits;
+typedef u16bits far 	* pu16bits;
+typedef u32bits far	* pu32bits;
+
+#endif	/* COMPILER_16_BIT */
+
+#if defined(COMPILER_32_BIT)
+
+typedef u08bits 	* pu08bits;
+typedef u16bits 	* pu16bits;
+typedef u32bits 	* pu32bits;
+
+#endif	/* COMPILER_32_BIT */
+
+
+#define BIT(x)          ((UCHAR)(1<<(x)))    /* single-bit mask in bit position x */
+#define BITW(x)          ((USHORT)(1<<(x)))  /* single-bit mask in bit position x */
+
+
+
+#if defined(DOS)
+/*#include <dos.h>*/
+	#undef inportb          /* undefine for Borland Lib */
+	#undef inport           /* they may have define I/O function in LIB */
+	#undef outportb
+	#undef outport
+
+	#define OS_InPortByte(ioport) 		inportb(ioport)
+	#define OS_InPortWord(ioport) 		inport(ioport)
+	#define OS_InPortLong(ioport)			inportq(ioport, val)
+	#define OS_OutPortByte(ioport, val) outportb(ioport, val)
+	#define OS_OutPortWord(ioport, val)	outport(ioport, val)
+	#define OS_OutPortLong(ioport)		outportq(ioport, val)
+#endif	/* DOS */
+
+#if defined(NETWARE) || defined(OTHER_32) ||  defined(OTHER_16)
+	extern u08bits	OS_InPortByte(u32bits ioport);
+	extern u16bits	OS_InPortWord(u32bits ioport);
+	extern u32bits	OS_InPortLong(u32bits ioport);
+
+	extern OS_InPortByteBuffer(u32bits ioport, pu08bits buffer, u32bits count);
+	extern OS_InPortWordBuffer(u32bits ioport, pu16bits buffer, u32bits count);
+	extern OS_OutPortByte(u32bits ioport, u08bits val);
+	extern OS_OutPortWord(u32bits ioport, u16bits val);
+	extern OS_OutPortLong(u32bits ioport, u32bits val);
+	extern OS_OutPortByteBuffer(u32bits ioport, pu08bits buffer, u32bits count);
+	extern OS_OutPortWordBuffer(u32bits ioport, pu16bits buffer, u32bits count);
+#endif	/* NETWARE || OTHER_32 || OTHER_16 */
+
+#if defined (NT) || defined(WIN95_32) || defined(WIN95_16)
+	#if defined(NT)
+
+		extern __declspec(dllimport) u08bits ScsiPortReadPortUchar(pu08bits ioport);
+		extern __declspec(dllimport) u16bits ScsiPortReadPortUshort(pu16bits ioport);
+		extern __declspec(dllimport) u32bits ScsiPortReadPortUlong(pu32bits ioport);
+		extern __declspec(dllimport) void ScsiPortWritePortUchar(pu08bits ioport, u08bits val);
+		extern __declspec(dllimport) void ScsiPortWritePortUshort(pu16bits port, u16bits val);
+		extern __declspec(dllimport) void ScsiPortWritePortUlong(pu32bits port, u32bits val);
+
+	#else
+
+		extern u08bits ScsiPortReadPortUchar(pu08bits ioport);
+		extern u16bits ScsiPortReadPortUshort(pu16bits ioport);
+		extern u32bits ScsiPortReadPortUlong(pu32bits ioport);
+		extern void ScsiPortWritePortUchar(pu08bits ioport, u08bits val);
+		extern void ScsiPortWritePortUshort(pu16bits port, u16bits val);
+		extern void ScsiPortWritePortUlong(pu32bits port, u32bits val);
+	#endif
+
+
+	#define OS_InPortByte(ioport) ScsiPortReadPortUchar((pu08bits) ioport)
+	#define OS_InPortWord(ioport) ScsiPortReadPortUshort((pu16bits) ioport)
+	#define OS_InPortLong(ioport) ScsiPortReadPortUlong((pu32bits) ioport)
+
+	#define OS_OutPortByte(ioport, val) ScsiPortWritePortUchar((pu08bits) ioport, (u08bits) val)
+	#define OS_OutPortWord(ioport, val) ScsiPortWritePortUshort((pu16bits) ioport, (u16bits) val)
+	#define OS_OutPortLong(ioport, val) ScsiPortWritePortUlong((pu32bits) ioport, (u32bits) val)
+	#define OS_OutPortByteBuffer(ioport, buffer, count) \
+		ScsiPortWritePortBufferUchar((pu08bits)&port, (pu08bits) buffer, (u32bits) count)
+	#define OS_OutPortWordBuffer(ioport, buffer, count) \
+		ScsiPortWritePortBufferUshort((pu16bits)&port, (pu16bits) buffer, (u32bits) count)
+
+	#define OS_Lock(x)
+	#define OS_UnLock(x)
+#endif /* NT || WIN95_32 || WIN95_16 */
+
+#if defined (UNIX) && !defined(OS_InPortByte)
+	#define OS_InPortByte(ioport)    inb((u16bits)ioport)
+	#define OS_InPortWord(ioport)    inw((u16bits)ioport)
+	#define OS_InPortLong(ioport)    inl((u16bits)ioport)
+	#define OS_OutPortByte(ioport,val)  outb((u16bits)ioport, (u08bits)val)
+	#define OS_OutPortWord(ioport,val)  outw((u16bits)ioport, (u16bits)val)
+	#define OS_OutPortLong(ioport,val)  outl((u16bits)ioport, (u32bits)val)
+
+	#define OS_Lock(x)
+	#define OS_UnLock(x)
+#endif /* UNIX */
+
+
+#if defined(OS2)
+	extern u08bits	inb(u32bits ioport);
+	extern u16bits	inw(u32bits ioport);
+	extern void	outb(u32bits ioport, u08bits val);
+	extern void	outw(u32bits ioport, u16bits val);
+
+	#define OS_InPortByte(ioport)			inb(ioport)
+	#define OS_InPortWord(ioport)			inw(ioport)
+	#define OS_OutPortByte(ioport, val)	outb(ioport, val)
+	#define OS_OutPortWord(ioport, val)	outw(ioport, val)
+	extern u32bits	OS_InPortLong(u32bits ioport);
+	extern void	OS_OutPortLong(u32bits ioport, u32bits val);
+
+	#define OS_Lock(x)
+	#define OS_UnLock(x)
+#endif /* OS2 */
+
+#if defined(SOLARIS_REAL_MODE)
+
+extern unsigned char    inb(unsigned long ioport);
+extern unsigned short   inw(unsigned long ioport);
+
+#define OS_InPortByte(ioport)    inb(ioport)
+#define OS_InPortWord(ioport)    inw(ioport)
+
+extern void OS_OutPortByte(unsigned long ioport, unsigned char val);
+extern void OS_OutPortWord(unsigned long ioport, unsigned short val);
+extern unsigned long  OS_InPortLong(unsigned long ioport);
+extern void     OS_OutPortLong(unsigned long ioport, unsigned long val);
+
+#define OS_Lock(x)
+#define OS_UnLock(x)
+
+#endif  /* SOLARIS_REAL_MODE */
+
+#endif  /* __GLOBALS_H__ */
+
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   sccbmgr.h  $
+ *
+ *   Description:  Common shared SCCB Interface defines and SCCB 
+ *						 Manager specifics defines.
+ *
+ *   $Date: 1996/10/24 23:09:33 $
+ *
+ *   $Revision: 1.14 $
+ *
+ *----------------------------------------------------------------------*/
+
+#ifndef __SCCB_H__
+#define __SCCB_H__
+
+/*#include <osflags.h>*/
+/*#include <globals.h>*/
+
+#if defined(BUGBUG)
+#define debug_size 32
+#endif
+
+#if defined(DOS)
+
+   typedef struct _SCCB near *PSCCB;
+	#if (FW_TYPE == _SCCB_MGR_)
+   	typedef void (*CALL_BK_FN)(PSCCB);
+	#endif
+
+#elif defined(OS2)
+
+   typedef struct _SCCB far *PSCCB;
+	#if (FW_TYPE == _SCCB_MGR_)
+   	typedef void (far *CALL_BK_FN)(PSCCB);
+	#endif
+
+#else
+
+   typedef struct _SCCB *PSCCB;
+	#if (FW_TYPE == _SCCB_MGR_)
+   	typedef void (*CALL_BK_FN)(PSCCB);
+	#endif
+
+#endif
+
+
+typedef struct SCCBMgr_info {
+   ULONG    si_baseaddr;
+   UCHAR    si_present;
+   UCHAR    si_intvect;
+   UCHAR    si_id;
+   UCHAR    si_lun;
+   USHORT   si_fw_revision;
+   USHORT   si_per_targ_init_sync;
+   USHORT   si_per_targ_fast_nego;
+   USHORT   si_per_targ_ultra_nego;
+   USHORT   si_per_targ_no_disc;
+   USHORT   si_per_targ_wide_nego;
+   USHORT   si_flags;
+   UCHAR    si_card_family;
+   UCHAR    si_bustype;
+   UCHAR    si_card_model[3];
+   UCHAR    si_relative_cardnum;
+   UCHAR    si_reserved[4];
+   ULONG    si_OS_reserved;
+   UCHAR    si_XlatInfo[4];
+   ULONG    si_reserved2[5];
+   ULONG    si_secondary_range;
+} SCCBMGR_INFO;
+
+#if defined(DOS)
+   typedef SCCBMGR_INFO *      PSCCBMGR_INFO;
+#else
+   #if defined (COMPILER_16_BIT)
+   typedef SCCBMGR_INFO far *  PSCCBMGR_INFO;
+   #else
+   typedef SCCBMGR_INFO *      PSCCBMGR_INFO;
+   #endif
+#endif // defined(DOS)
+
+
+
+
+#if (FW_TYPE==_SCCB_MGR_)
+	#define SCSI_PARITY_ENA		  0x0001
+	#define LOW_BYTE_TERM		  0x0010
+	#define HIGH_BYTE_TERM		  0x0020
+	#define BUSTYPE_PCI	  0x3
+#endif
+
+#define SUPPORT_16TAR_32LUN	  0x0002
+#define SOFT_RESET		  0x0004
+#define EXTENDED_TRANSLATION	  0x0008
+#define POST_ALL_UNDERRRUNS	  0x0040
+#define FLAG_SCAM_ENABLED	  0x0080
+#define FLAG_SCAM_LEVEL2	  0x0100
+
+
+
+
+#define HARPOON_FAMILY        0x02
+
+
+#define ISA_BUS_CARD          0x01
+#define EISA_BUS_CARD         0x02
+#define PCI_BUS_CARD          0x03
+#define VESA_BUS_CARD         0x04
+
+/* SCCB struc used for both SCCB and UCB manager compiles! 
+ * The UCB Manager treats the SCCB as it's 'native hardware structure' 
+ */
+
+
+#pragma pack(1)
+typedef struct _SCCB {
+   UCHAR OperationCode;
+   UCHAR ControlByte;
+   UCHAR CdbLength;
+   UCHAR RequestSenseLength;
+   ULONG DataLength;
+   ULONG DataPointer;
+   UCHAR CcbRes[2];
+   UCHAR HostStatus;
+   UCHAR TargetStatus;
+   UCHAR TargID;
+   UCHAR Lun;
+   UCHAR Cdb[12];
+   UCHAR CcbRes1;
+   UCHAR Reserved1;
+   ULONG Reserved2;
+   ULONG SensePointer;
+
+
+   CALL_BK_FN SccbCallback;                  /* VOID (*SccbCallback)(); */
+   ULONG  SccbIOPort;                        /* Identifies board base port */
+   UCHAR  SccbStatus;
+   UCHAR  SCCBRes2;
+   USHORT SccbOSFlags;
+
+
+   ULONG   Sccb_XferCnt;            /* actual transfer count */
+   ULONG   Sccb_ATC;
+   ULONG   SccbVirtDataPtr;         /* virtual addr for OS/2 */
+   ULONG   Sccb_res1;
+   USHORT  Sccb_MGRFlags;
+   USHORT  Sccb_sgseg;
+   UCHAR   Sccb_scsimsg;            /* identify msg for selection */
+   UCHAR   Sccb_tag;
+   UCHAR   Sccb_scsistat;
+   UCHAR   Sccb_idmsg;              /* image of last msg in */
+   PSCCB   Sccb_forwardlink;
+   PSCCB   Sccb_backlink;
+   ULONG   Sccb_savedATC;
+   UCHAR   Save_Cdb[6];
+   UCHAR   Save_CdbLen;
+   UCHAR   Sccb_XferState;
+   ULONG   Sccb_SGoffset;
+#if (FW_TYPE == _UCB_MGR_)
+   PUCB    Sccb_ucb_ptr;
+#endif
+   } SCCB;
+
+#define SCCB_SIZE sizeof(SCCB)
+
+#pragma pack()
+
+
+
+#define SCSI_INITIATOR_COMMAND    0x00
+#define TARGET_MODE_COMMAND       0x01
+#define SCATTER_GATHER_COMMAND    0x02
+#define RESIDUAL_COMMAND          0x03
+#define RESIDUAL_SG_COMMAND       0x04
+#define RESET_COMMAND             0x81
+
+
+#define F_USE_CMD_Q              0x20     /*Inidcates TAGGED command. */
+#define TAG_TYPE_MASK            0xC0     /*Type of tag msg to send. */
+#define TAG_Q_MASK               0xE0
+#define SCCB_DATA_XFER_OUT       0x10     /* Write */
+#define SCCB_DATA_XFER_IN        0x08     /* Read */
+
+
+#define FOURTEEN_BYTES           0x00     /* Request Sense Buffer size */
+#define NO_AUTO_REQUEST_SENSE    0x01     /* No Request Sense Buffer */
+
+
+#define BUS_FREE_ST     0       
+#define SELECT_ST       1
+#define SELECT_BDR_ST   2     /* Select w\ Bus Device Reset */
+#define SELECT_SN_ST    3     /* Select w\ Sync Nego */
+#define SELECT_WN_ST    4     /* Select w\ Wide Data Nego */
+#define SELECT_Q_ST     5     /* Select w\ Tagged Q'ing */
+#define COMMAND_ST      6
+#define DATA_OUT_ST     7
+#define DATA_IN_ST      8
+#define DISCONNECT_ST   9
+#define STATUS_ST       10
+#define ABORT_ST        11
+#define MESSAGE_ST      12
+
+
+#define F_HOST_XFER_DIR                0x01
+#define F_ALL_XFERRED                  0x02
+#define F_SG_XFER                      0x04
+#define F_AUTO_SENSE                   0x08
+#define F_ODD_BALL_CNT                 0x10
+#define F_NO_DATA_YET                  0x80
+
+
+#define F_STATUSLOADED                 0x01
+#define F_MSGLOADED                    0x02
+#define F_DEV_SELECTED                 0x04
+
+
+#define SCCB_COMPLETE               0x00  /* SCCB completed without error */
+#define SCCB_DATA_UNDER_RUN         0x0C
+#define SCCB_SELECTION_TIMEOUT      0x11  /* Set SCSI selection timed out */
+#define SCCB_DATA_OVER_RUN          0x12
+#define SCCB_UNEXPECTED_BUS_FREE    0x13  /* Target dropped SCSI BSY */
+#define SCCB_PHASE_SEQUENCE_FAIL    0x14  /* Target bus phase sequence failure */
+
+#define SCCB_INVALID_OP_CODE        0x16  /* SCCB invalid operation code */
+#define SCCB_INVALID_SCCB           0x1A  /* Invalid SCCB - bad parameter */
+#define SCCB_GROSS_FW_ERR           0x27  /* Major problem! */
+#define SCCB_BM_ERR                 0x30  /* BusMaster error. */
+#define SCCB_PARITY_ERR             0x34  /* SCSI parity error */
+
+
+
+#if (FW_TYPE==_UCB_MGR_)  
+   #define  HBA_AUTO_SENSE_FAIL        0x1B  
+   #define  HBA_TQ_REJECTED            0x1C  
+   #define  HBA_UNSUPORTED_MSG         0x1D  
+   #define  HBA_HW_ERROR               0x20  
+   #define  HBA_ATN_NOT_RESPONDED      0x21  
+   #define  HBA_SCSI_RESET_BY_ADAPTER  0x22
+   #define  HBA_SCSI_RESET_BY_TARGET   0x23
+   #define  HBA_WRONG_CONNECTION       0x24
+   #define  HBA_BUS_DEVICE_RESET       0x25
+   #define  HBA_ABORT_QUEUE            0x26
+
+#else // these are not defined in BUDI/UCB
+
+   #define SCCB_INVALID_DIRECTION      0x18  /* Invalid target direction */
+   #define SCCB_DUPLICATE_SCCB         0x19  /* Duplicate SCCB */
+   #define SCCB_SCSI_RST               0x35  /* SCSI RESET detected. */
+
+#endif // (FW_TYPE==_UCB_MGR_)  
+
+
+#define SCCB_IN_PROCESS            0x00
+#define SCCB_SUCCESS               0x01
+#define SCCB_ABORT                 0x02
+#define SCCB_NOT_FOUND             0x03
+#define SCCB_ERROR                 0x04
+#define SCCB_INVALID               0x05
+
+#define SCCB_SIZE sizeof(SCCB)
+
+
+
+
+#if (FW_TYPE == _UCB_MGR_)
+	void SccbMgr_start_sccb(CARD_HANDLE pCurrCard, PUCB p_ucb);
+	s32bits SccbMgr_abort_sccb(CARD_HANDLE pCurrCard, PUCB p_ucb);
+	u08bits SccbMgr_my_int(CARD_HANDLE pCurrCard);
+	s32bits SccbMgr_isr(CARD_HANDLE pCurrCard);
+	void SccbMgr_scsi_reset(CARD_HANDLE pCurrCard);
+	void SccbMgr_timer_expired(CARD_HANDLE pCurrCard);
+	void SccbMgr_unload_card(CARD_HANDLE pCurrCard);
+	void SccbMgr_restore_foreign_state(CARD_HANDLE pCurrCard);
+	void SccbMgr_restore_native_state(CARD_HANDLE pCurrCard);
+	void SccbMgr_save_foreign_state(PADAPTER_INFO pAdapterInfo);
+
+#endif
+
+
+#if (FW_TYPE == _SCCB_MGR_)
+
+ #if defined (DOS)
+	int    SccbMgr_sense_adapter(PSCCBMGR_INFO pCardInfo);
+	USHORT SccbMgr_config_adapter(PSCCBMGR_INFO pCardInfo);
+	void  SccbMgr_start_sccb(USHORT pCurrCard, PSCCB p_SCCB);
+	int   SccbMgr_abort_sccb(USHORT pCurrCard, PSCCB p_SCCB);
+	UCHAR SccbMgr_my_int(USHORT pCurrCard);
+	int   SccbMgr_isr(USHORT pCurrCard);
+	void  SccbMgr_scsi_reset(USHORT pCurrCard);
+	void  SccbMgr_timer_expired(USHORT pCurrCard);
+	USHORT SccbMgr_status(USHORT pCurrCard);
+	void SccbMgr_unload_card(USHORT pCurrCard);
+
+ #else    //non-DOS
+
+	int   SccbMgr_sense_adapter(PSCCBMGR_INFO pCardInfo);
+	ULONG SccbMgr_config_adapter(PSCCBMGR_INFO pCardInfo);
+	void  SccbMgr_start_sccb(ULONG pCurrCard, PSCCB p_SCCB);
+	int   SccbMgr_abort_sccb(ULONG pCurrCard, PSCCB p_SCCB);
+	UCHAR SccbMgr_my_int(ULONG pCurrCard);
+	int   SccbMgr_isr(ULONG pCurrCard);
+	void  SccbMgr_scsi_reset(ULONG pCurrCard);
+	void  SccbMgr_enable_int(ULONG pCurrCard);
+	void  SccbMgr_disable_int(ULONG pCurrCard);
+	void  SccbMgr_timer_expired(ULONG pCurrCard);
+	void SccbMgr_unload_card(ULONG pCurrCard);
+
+  #endif
+#endif  // (FW_TYPE == _SCCB_MGR_)
+
+#endif  /* __SCCB_H__ */
+
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   blx30.h  $
+ *
+ *   Description: This module contains SCCB/UCB Manager implementation
+ *                specific stuff.
+ *
+ *   $Date: 1996/11/13 18:34:22 $
+ *
+ *   $Revision: 1.10 $
+ *
+ *----------------------------------------------------------------------*/
+
+
+#ifndef __blx30_H__
+#define __blx30_H__
+
+/*#include <globals.h>*/
+
+#define  ORION_FW_REV      3110
+
+
+
+
+#define HARP_REVD    1
+
+
+#if defined(DOS)
+#define QUEUE_DEPTH     8+1            /*1 for Normal disconnect 0 for Q'ing. */
+#else
+#define QUEUE_DEPTH     254+1            /*1 for Normal disconnect 32 for Q'ing. */
+#endif   // defined(DOS)
+
+#define	MAX_MB_CARDS	4					/* Max. no of cards suppoerted on Mother Board */
+
+#define WIDE_SCSI       1
+
+#if defined(WIDE_SCSI)
+   #if defined(DOS)
+      #define MAX_SCSI_TAR    16
+      #define MAX_LUN         8
+		#define LUN_MASK			0x07
+   #else
+      #define MAX_SCSI_TAR    16
+      #define MAX_LUN         32
+		#define LUN_MASK			0x1f
+	
+   #endif
+#else
+   #define MAX_SCSI_TAR    8
+   #define MAX_LUN         8
+	#define LUN_MASK			0x07
+#endif 
+
+#if defined(HARP_REVA)
+#define SG_BUF_CNT      15             /*Number of prefetched elements. */
+#else
+#define SG_BUF_CNT      16             /*Number of prefetched elements. */
+#endif
+
+#define SG_ELEMENT_SIZE 8              /*Eight byte per element. */
+#define SG_LOCAL_MASK   0x00000000L
+#define SG_ELEMENT_MASK 0xFFFFFFFFL
+
+
+#if (FW_TYPE == _UCB_MGR_)
+	#define OPC_DECODE_NORMAL       0x0f7f
+#endif   // _UCB_MGR_
+
+
+
+#if defined(DOS)
+
+/*#include <dos.h>*/
+	#define RD_HARPOON(ioport)          (OS_InPortByte(ioport))
+	#define RDW_HARPOON(ioport)         (OS_InPortWord(ioport))
+	#define WR_HARPOON(ioport,val)      (OS_OutPortByte(ioport,val))
+	#define WRW_HARPOON(ioport,val)     (OS_OutPortWord(ioport,val))
+
+	#define RD_HARP32(port,offset,data)  asm{db 66h;         \
+                                       push ax;             \
+                                       mov dx,port;         \
+                                       add dx, offset;      \
+                                       db 66h;              \
+                                       in ax,dx;            \
+                                       db 66h;              \
+                                       mov word ptr data,ax;\
+                                       db 66h;              \
+                                       pop ax}
+
+	#define WR_HARP32(port,offset,data) asm{db 66h;          \
+                                       push ax;             \
+                                       mov dx,port;         \
+                                       add dx, offset;      \
+                                       db 66h;              \
+                                       mov ax,word ptr data;\
+                                       db 66h;              \
+                                       out dx,ax;           \
+                                       db 66h;              \
+                                       pop ax}
+#endif	/* DOS */
+
+#if defined(NETWARE) || defined(OTHER_32) ||  defined(OTHER_16)
+	#define RD_HARPOON(ioport)     OS_InPortByte((unsigned long)ioport)
+	#define RDW_HARPOON(ioport)    OS_InPortWord((unsigned long)ioport)
+	#define RD_HARP32(ioport,offset,data) (data = OS_InPortLong(ioport + offset))
+	#define WR_HARPOON(ioport,val) OS_OutPortByte((ULONG)ioport,(UCHAR) val)
+	#define WRW_HARPOON(ioport,val)  OS_OutPortWord((ULONG)ioport,(USHORT)val)
+	#define WR_HARP32(ioport,offset,data)  OS_OutPortLong((ioport + offset), data)
+#endif	/* NETWARE || OTHER_32 || OTHER_16 */
+
+#if defined(NT) || defined(WIN95_32) || defined(WIN95_16)
+	#define RD_HARPOON(ioport)          OS_InPortByte((ULONG)ioport)
+	#define RDW_HARPOON(ioport)         OS_InPortWord((ULONG)ioport)
+	#define RD_HARP32(ioport,offset,data) (data = OS_InPortLong((ULONG)(ioport + offset)))
+	#define WR_HARPOON(ioport,val)      OS_OutPortByte((ULONG)ioport,(UCHAR) val)
+	#define WRW_HARPOON(ioport,val)     OS_OutPortWord((ULONG)ioport,(USHORT)val)
+	#define WR_HARP32(ioport,offset,data)  OS_OutPortLong((ULONG)(ioport + offset), data)
+#endif /* NT || WIN95_32 || WIN95_16 */
+
+#if defined (UNIX)
+	#define RD_HARPOON(ioport)          OS_InPortByte((u32bits)ioport)
+	#define RDW_HARPOON(ioport)         OS_InPortWord((u32bits)ioport)
+	#define RD_HARP32(ioport,offset,data) (data = OS_InPortLong((u32bits)(ioport + offset)))
+	#define WR_HARPOON(ioport,val)      OS_OutPortByte((u32bits)ioport,(u08bits) val)
+	#define WRW_HARPOON(ioport,val)       OS_OutPortWord((u32bits)ioport,(u16bits)val)
+	#define WR_HARP32(ioport,offset,data)  OS_OutPortLong((u32bits)(ioport + offset), data)
+#endif /* UNIX */
+
+#if defined(OS2)
+	#define RD_HARPOON(ioport)          OS_InPortByte((unsigned long)ioport)
+	#define RDW_HARPOON(ioport)         OS_InPortWord((unsigned long)ioport)
+	#define RD_HARP32(ioport,offset,data) (data = OS_InPortLong((ULONG)(ioport + offset)))
+	#define WR_HARPOON(ioport,val)      OS_OutPortByte((ULONG)ioport,(UCHAR) val)
+	#define WRW_HARPOON(ioport,val)       OS_OutPortWord((ULONG)ioport,(USHORT)val)
+	#define WR_HARP32(ioport,offset,data)  OS_OutPortLong(((ULONG)(ioport + offset)), data)
+#endif /* OS2 */
+
+#if defined(SOLARIS_REAL_MODE)
+
+	#define RD_HARPOON(ioport)          OS_InPortByte((unsigned long)ioport)
+	#define RDW_HARPOON(ioport)         OS_InPortWord((unsigned long)ioport)
+	#define RD_HARP32(ioport,offset,data) (data = OS_InPortLong((ULONG)(ioport + offset)))
+	#define WR_HARPOON(ioport,val)      OS_OutPortByte((ULONG)ioport,(UCHAR) val)
+	#define WRW_HARPOON(ioport,val)       OS_OutPortWord((ULONG)ioport,(USHORT)val)
+	#define WR_HARP32(ioport,offset,data)  OS_OutPortLong((ULONG)(ioport + offset), (ULONG)data)
+
+#endif  /* SOLARIS_REAL_MODE */
+
+#endif  /* __BLX30_H__ */
+
+
+/*----------------------------------------------------------------------
+ * 
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   target.h  $
+ *
+ *   Description:  Definitions for Target related structures
+ *
+ *   $Date: 1996/12/11 22:06:20 $
+ *
+ *   $Revision: 1.9 $
+ *
+ *----------------------------------------------------------------------*/
+
+#ifndef __TARGET__
+#define __TARGET__
+
+/*#include <globals.h>*/
+/*#include <blx30.h>*/
+
+
+#define  TAR_SYNC_MASK     (BIT(7)+BIT(6))
+#define  SYNC_UNKNOWN      0x00
+#define  SYNC_TRYING               BIT(6)
+#define  SYNC_SUPPORTED    (BIT(7)+BIT(6))
+
+#define  TAR_WIDE_MASK     (BIT(5)+BIT(4))
+#define  WIDE_DISABLED     0x00
+#define  WIDE_ENABLED              BIT(4)
+#define  WIDE_NEGOCIATED   BIT(5)
+
+#define  TAR_TAG_Q_MASK    (BIT(3)+BIT(2))
+#define  TAG_Q_UNKNOWN     0x00
+#define  TAG_Q_TRYING              BIT(2)
+#define  TAG_Q_REJECT      BIT(3)
+#define  TAG_Q_SUPPORTED   (BIT(3)+BIT(2))
+
+#define  TAR_ALLOW_DISC    BIT(0)
+
+
+#define  EE_SYNC_MASK      (BIT(0)+BIT(1))
+#define  EE_SYNC_ASYNC     0x00
+#define  EE_SYNC_5MB       BIT(0)
+#define  EE_SYNC_10MB      BIT(1)
+#define  EE_SYNC_20MB      (BIT(0)+BIT(1))
+
+#define  EE_ALLOW_DISC     BIT(6)
+#define  EE_WIDE_SCSI      BIT(7)
+
+
+#if defined(DOS)
+   typedef struct SCCBMgr_tar_info near *PSCCBMgr_tar_info;
+
+#elif defined(OS2)
+   typedef struct SCCBMgr_tar_info far *PSCCBMgr_tar_info;
+
+#else
+   typedef struct SCCBMgr_tar_info *PSCCBMgr_tar_info;
+
+#endif
+
+
+typedef struct SCCBMgr_tar_info {
+
+   PSCCB    TarSelQ_Head;
+   PSCCB    TarSelQ_Tail;
+   UCHAR    TarLUN_CA;        /*Contingent Allgiance */
+   UCHAR    TarTagQ_Cnt;
+   UCHAR    TarSelQ_Cnt;
+   UCHAR    TarStatus;
+   UCHAR    TarEEValue;
+   UCHAR 	TarSyncCtrl;
+   UCHAR 	TarReserved[2];			/* for alignment */ 
+   UCHAR 	LunDiscQ_Idx[MAX_LUN];
+   UCHAR    TarLUNBusy[MAX_LUN];
+} SCCBMGR_TAR_INFO;
+
+typedef struct NVRAMInfo {
+	UCHAR		niModel;								/* Model No. of card */
+	UCHAR		niCardNo;							/* Card no. */
+#if defined(DOS)
+	USHORT	niBaseAddr;							/* Port Address of card */
+#else
+	ULONG		niBaseAddr;							/* Port Address of card */
+#endif
+	UCHAR		niSysConf;							/* Adapter Configuration byte - Byte 16 of eeprom map */
+	UCHAR		niScsiConf;							/* SCSI Configuration byte - Byte 17 of eeprom map */
+	UCHAR		niScamConf;							/* SCAM Configuration byte - Byte 20 of eeprom map */
+	UCHAR		niAdapId;							/* Host Adapter ID - Byte 24 of eerpom map */
+	UCHAR		niSyncTbl[MAX_SCSI_TAR / 2];	/* Sync/Wide byte of targets */
+	UCHAR		niScamTbl[MAX_SCSI_TAR][4];	/* Compressed Scam name string of Targets */
+}NVRAMINFO;
+
+#if defined(DOS)
+typedef NVRAMINFO near *PNVRamInfo;
+#elif defined (OS2)
+typedef NVRAMINFO far *PNVRamInfo;
+#else
+typedef NVRAMINFO *PNVRamInfo;
+#endif
+
+#define	MODEL_LT		1
+#define	MODEL_DL		2
+#define	MODEL_LW		3
+#define	MODEL_DW		4
+
+
+typedef struct SCCBcard {
+   PSCCB currentSCCB;
+#if (FW_TYPE==_SCCB_MGR_)
+   PSCCBMGR_INFO cardInfo;
+#else
+   PADAPTER_INFO cardInfo;
+#endif
+
+#if defined(DOS)
+   USHORT ioPort;
+#else
+   ULONG ioPort;
+#endif
+
+   USHORT cmdCounter;
+   UCHAR  discQCount;
+   UCHAR  tagQ_Lst; 
+   UCHAR cardIndex;
+   UCHAR scanIndex;
+   UCHAR globalFlags;
+   UCHAR ourId;
+   PNVRamInfo pNvRamInfo;
+   PSCCB discQ_Tbl[QUEUE_DEPTH]; 
+      
+}SCCBCARD;
+
+#if defined(DOS)
+typedef struct SCCBcard near *PSCCBcard;
+#elif defined (OS2)
+typedef struct SCCBcard far *PSCCBcard;
+#else
+typedef struct SCCBcard *PSCCBcard;
+#endif
+
+
+#define F_TAG_STARTED		0x01
+#define F_CONLUN_IO			0x02
+#define F_DO_RENEGO			0x04
+#define F_NO_FILTER			0x08
+#define F_GREEN_PC			0x10
+#define F_HOST_XFER_ACT		0x20
+#define F_NEW_SCCB_CMD		0x40
+#define F_UPDATE_EEPROM		0x80
+
+
+#define  ID_STRING_LENGTH  32
+#define  TYPE_CODE0        0x63           /*Level2 Mstr (bits 7-6),  */
+
+#define  TYPE_CODE1        00             /*No ID yet */
+
+#define  SLV_TYPE_CODE0    0xA3           /*Priority Bit set (bits 7-6),  */
+
+#define  ASSIGN_ID   0x00
+#define  SET_P_FLAG  0x01
+#define  CFG_CMPLT   0x03
+#define  DOM_MSTR    0x0F
+#define  SYNC_PTRN   0x1F
+
+#define  ID_0_7      0x18
+#define  ID_8_F      0x11
+#define  ID_10_17    0x12
+#define  ID_18_1F    0x0B
+#define  MISC_CODE   0x14
+#define  CLR_P_FLAG  0x18
+#define  LOCATE_ON   0x12
+#define  LOCATE_OFF  0x0B
+
+#define  LVL_1_MST   0x00
+#define  LVL_2_MST   0x40
+#define  DOM_LVL_2   0xC0
+
+
+#define  INIT_SELTD  0x01
+#define  LEVEL2_TAR  0x02
+
+
+enum scam_id_st { ID0,ID1,ID2,ID3,ID4,ID5,ID6,ID7,ID8,ID9,ID10,ID11,ID12,
+                  ID13,ID14,ID15,ID_UNUSED,ID_UNASSIGNED,ID_ASSIGNED,LEGACY,
+                  CLR_PRIORITY,NO_ID_AVAIL };
+
+typedef struct SCCBscam_info {
+
+   UCHAR    id_string[ID_STRING_LENGTH];
+   enum scam_id_st state;
+    
+} SCCBSCAM_INFO, *PSCCBSCAM_INFO;
+
+#endif
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   scsi2.h  $
+ *
+ *   Description:  Register definitions for HARPOON ASIC.
+ *
+ *   $Date: 1996/11/13 18:32:57 $
+ *
+ *   $Revision: 1.4 $
+ *
+ *----------------------------------------------------------------------*/
+
+#ifndef __SCSI_H__
+#define __SCSI_H__
+
+
+
+#define  SCSI_TEST_UNIT_READY    0x00
+#define  SCSI_REZERO_UNIT        0x01
+#define  SCSI_REQUEST_SENSE      0x03
+#define  SCSI_FORMAT_UNIT        0x04
+#define  SCSI_REASSIGN           0x07
+#define  SCSI_READ               0x08
+#define  SCSI_WRITE              0x0A
+#define  SCSI_SEEK               0x0B
+#define  SCSI_INQUIRY            0x12
+#define  SCSI_MODE_SELECT        0x15
+#define  SCSI_RESERVE_UNIT       0x16
+#define  SCSI_RELEASE_UNIT       0x17
+#define  SCSI_MODE_SENSE         0x1A
+#define  SCSI_START_STOP_UNIT    0x1B
+#define  SCSI_SEND_DIAGNOSTIC    0x1D
+#define  SCSI_READ_CAPACITY      0x25
+#define  SCSI_READ_EXTENDED      0x28
+#define  SCSI_WRITE_EXTENDED     0x2A
+#define  SCSI_SEEK_EXTENDED      0x2B
+#define  SCSI_WRITE_AND_VERIFY   0x2E
+#define  SCSI_VERIFY             0x2F
+#define  SCSI_READ_DEFECT_DATA   0x37
+#define  SCSI_WRITE_BUFFER       0x3B
+#define  SCSI_READ_BUFFER        0x3C
+#define  SCSI_RECV_DIAGNOSTIC    0x1C
+#define  SCSI_READ_LONG          0x3E
+#define  SCSI_WRITE_LONG         0x3F
+#define  SCSI_LAST_SCSI_CMND     SCSI_WRITE_LONG
+#define  SCSI_INVALID_CMND       0xFF
+
+
+
+#define  SSGOOD                  0x00
+#define  SSCHECK                 0x02
+#define  SSCOND_MET              0x04
+#define  SSBUSY                  0x08
+#define  SSRESERVATION_CONFLICT  0x18
+#define  SSCMD_TERM              0x22
+#define  SSQ_FULL                0x28
+
+
+#define  SKNO_SEN                0x00
+#define  SKRECOV_ERR             0x01
+#define  SKNOT_RDY               0x02
+#define  SKMED_ERR               0x03
+#define  SKHW_ERR                0x04
+#define  SKILL_REQ               0x05
+#define  SKUNIT_ATTN             0x06
+#define  SKDATA_PROTECT          0x07
+#define  SKBLNK_CHK              0x08
+#define  SKCPY_ABORT             0x0A
+#define  SKABORT_CMD             0x0B
+#define  SKEQUAL                 0x0C
+#define  SKVOL_OVF               0x0D
+#define  SKMIS_CMP               0x0E
+
+
+#define  SMCMD_COMP              0x00
+#define  SMEXT                   0x01
+#define  SMSAVE_DATA_PTR         0x02
+#define  SMREST_DATA_PTR         0x03
+#define  SMDISC                  0x04
+#define  SMINIT_DETEC_ERR        0x05
+#define  SMABORT                 0x06
+#define  SMREJECT                0x07
+#define  SMNO_OP                 0x08
+#define  SMPARITY                0x09
+#define  SMDEV_RESET             0x0C
+#define	SMABORT_TAG					0x0D
+#define	SMINIT_RECOVERY			0x0F
+#define	SMREL_RECOVERY				0x10
+
+#define  SMIDENT                 0x80
+#define  DISC_PRIV               0x40
+
+
+#define  SMSYNC                  0x01
+#define  SM10MBS                 0x19     /* 100ns           */
+#define  SM5MBS                  0x32     /* 200ns           */
+#define  SMOFFSET                0x0F     /* Maxoffset value */
+#define  SMWDTR                  0x03
+#define  SM8BIT                  0x00
+#define  SM16BIT                 0x01
+#define  SM32BIT                 0x02
+#define  SMIGNORWR               0x23     /* Ignore Wide Residue */
+
+
+#define  ARBITRATION_DELAY       0x01     /* 2.4us using a 40Mhz clock */
+#define  BUS_SETTLE_DELAY        0x01     /* 400ns */
+#define  BUS_CLEAR_DELAY         0x01     /* 800ns */
+
+
+
+#define  SPHASE_TO               0x0A  /* 10 second timeout waiting for */
+#define  SCMD_TO                 0x0F  /* Overall command timeout */
+
+
+
+#define  SIX_BYTE_CMD            0x06
+#define  TEN_BYTE_CMD            0x0A
+#define  TWELVE_BYTE_CMD         0x0C
+
+#define  ASYNC                   0x00
+#define  PERI25NS                0x06  /* 25/4ns to next clock for xbow. */
+#define  SYNC10MBS               0x19
+#define  SYNC5MBS                0x32
+#define  MAX_OFFSET              0x0F  /* Maxbyteoffset for Sync Xfers */
+
+#endif
+/*----------------------------------------------------------------------
+ *  
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   eeprom.h  $
+ *
+ *   Description:  Definitions for EEPROM related structures
+ *
+ *   $Date: 1996/11/13 18:28:39 $
+ *
+ *   $Revision: 1.4 $
+ *
+ *----------------------------------------------------------------------*/
+
+#ifndef __EEPROM__
+#define __EEPROM__
+
+/*#include <globals.h>*/
+
+#define  EEPROM_WD_CNT     256
+
+#define  EEPROM_CHECK_SUM  0
+#define  FW_SIGNATURE      2
+#define  MODEL_NUMB_0      4
+#define  MODEL_NUMB_1      5
+#define  MODEL_NUMB_2      6
+#define  MODEL_NUMB_3      7
+#define  MODEL_NUMB_4      8
+#define  MODEL_NUMB_5      9
+#define  IO_BASE_ADDR      10
+#define  IRQ_NUMBER        12
+#define  PCI_INT_PIN       13
+#define  BUS_DELAY         14       /*On time in byte 14 off delay in 15 */
+#define  SYSTEM_CONFIG     16
+#define  SCSI_CONFIG       17
+#define  BIOS_CONFIG       18
+#define  SPIN_UP_DELAY     19
+#define  SCAM_CONFIG       20
+#define  ADAPTER_SCSI_ID   24
+
+
+#define  IGNORE_B_SCAN     32
+#define  SEND_START_ENA    34
+#define  DEVICE_ENABLE     36
+
+#define  SYNC_RATE_TBL     38
+#define  SYNC_RATE_TBL01   38
+#define  SYNC_RATE_TBL23   40
+#define  SYNC_RATE_TBL45   42
+#define  SYNC_RATE_TBL67   44
+#define  SYNC_RATE_TBL89   46
+#define  SYNC_RATE_TBLab   48
+#define  SYNC_RATE_TBLcd   50
+#define  SYNC_RATE_TBLef   52
+
+
+
+#define  EE_SCAMBASE      256 
+
+
+
+   #define  DOM_MASTER     (BIT(0) + BIT(1))
+   #define  SCAM_ENABLED   BIT(2)
+   #define  SCAM_LEVEL2    BIT(3)
+
+
+	#define	RENEGO_ENA		BITW(10)
+	#define	CONNIO_ENA		BITW(11)
+   #define  GREEN_PC_ENA   BITW(12)
+
+
+   #define  AUTO_RATE_00   00
+   #define  AUTO_RATE_05   01
+   #define  AUTO_RATE_10   02
+   #define  AUTO_RATE_20   03
+
+   #define  WIDE_NEGO_BIT     BIT(7)
+   #define  DISC_ENABLE_BIT   BIT(6)
+
+
+#endif
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   harpoon.h  $
+ *
+ *   Description:  Register definitions for HARPOON ASIC.
+ *
+ *   $Date: 1997/01/31 02:14:28 $
+ *
+ *   $Revision: 1.6 $
+ *
+ *----------------------------------------------------------------------*/
+
+
+/*#include <globals.h>*/
+
+#ifndef __HARPOON__
+#define __HARPOON__
+
+
+   #define  hp_vendor_id_0       0x00		/* LSB */
+      #define  ORION_VEND_0   0x4B
+ 
+   #define  hp_vendor_id_1       0x01		/* MSB */
+      #define  ORION_VEND_1   0x10
+
+   #define  hp_device_id_0       0x02		/* LSB */
+      #define  ORION_DEV_0    0x30 
+
+   #define  hp_device_id_1       0x03		/* MSB */
+      #define  ORION_DEV_1    0x81 
+
+	/* Sub Vendor ID and Sub Device ID only available in
+		Harpoon Version 2 and higher */
+
+   #define  hp_sub_vendor_id_0   0x04		/* LSB */
+   #define  hp_sub_vendor_id_1   0x05		/* MSB */
+   #define  hp_sub_device_id_0   0x06		/* LSB */
+   #define  hp_sub_device_id_1   0x07		/* MSB */
+
+
+   #define  hp_dual_addr_lo      0x08
+   #define  hp_dual_addr_lmi     0x09
+   #define  hp_dual_addr_hmi     0x0A
+   #define  hp_dual_addr_hi      0x0B
+
+   #define  hp_semaphore         0x0C
+      #define SCCB_MGR_ACTIVE    BIT(0)
+      #define TICKLE_ME          BIT(1)
+      #define SCCB_MGR_PRESENT   BIT(3)
+      #define BIOS_IN_USE        BIT(4)
+
+   #define  hp_user_defined_D    0x0D
+
+   #define  hp_reserved_E        0x0E
+
+   #define  hp_sys_ctrl          0x0F
+
+      #define  STOP_CLK          BIT(0)      /*Turn off BusMaster Clock */
+      #define  DRVR_RST          BIT(1)      /*Firmware Reset to 80C15 chip */
+      #define  HALT_MACH         BIT(3)      /*Halt State Machine      */
+      #define  HARD_ABORT        BIT(4)      /*Hard Abort              */
+      #define  DIAG_MODE         BIT(5)      /*Diagnostic Mode         */
+
+      #define  BM_ABORT_TMOUT    0x50        /*Halt State machine time out */
+
+   #define  hp_sys_cfg           0x10
+
+      #define  DONT_RST_FIFO     BIT(7)      /*Don't reset FIFO      */
+
+
+   #define  hp_host_ctrl0        0x11
+
+      #define  DUAL_ADDR_MODE    BIT(0)   /*Enable 64-bit addresses */
+      #define  IO_MEM_SPACE      BIT(1)   /*I/O Memory Space    */
+      #define  RESOURCE_LOCK     BIT(2)   /*Enable Resource Lock */
+      #define  IGNOR_ACCESS_ERR  BIT(3)   /*Ignore Access Error */
+      #define  HOST_INT_EDGE     BIT(4)   /*Host interrupt level/edge mode sel */
+      #define  SIX_CLOCKS        BIT(5)   /*6 Clocks between Strobe   */
+      #define  DMA_EVEN_PARITY   BIT(6)   /*Enable DMA Enen Parity */
+
+/*
+      #define  BURST_MODE        BIT(0)
+*/
+
+   #define  hp_reserved_12       0x12
+
+   #define  hp_host_blk_cnt      0x13
+
+      #define  XFER_BLK1         0x00     /*     0 0 0  1 byte per block*/
+      #define  XFER_BLK2         0x01     /*     0 0 1  2 byte per block*/
+      #define  XFER_BLK4         0x02     /*     0 1 0  4 byte per block*/
+      #define  XFER_BLK8         0x03     /*     0 1 1  8 byte per block*/
+      #define  XFER_BLK16        0x04     /*     1 0 0 16 byte per block*/
+      #define  XFER_BLK32        0x05     /*     1 0 1 32 byte per block*/
+      #define  XFER_BLK64        0x06     /*     1 1 0 64 byte per block*/
+   
+      #define  BM_THRESHOLD      0x40     /* PCI mode can only xfer 16 bytes*/
+
+
+   #define  hp_reserved_14       0x14
+   #define  hp_reserved_15       0x15
+   #define  hp_reserved_16       0x16
+
+   #define  hp_int_mask          0x17
+
+      #define  INT_CMD_COMPL     BIT(0)   /* DMA command complete   */
+      #define  INT_EXT_STATUS    BIT(1)   /* Extended Status Set    */
+      #define  INT_SCSI          BIT(2)   /* Scsi block interrupt   */
+      #define  INT_FIFO_RDY      BIT(4)   /* FIFO data ready        */
+
+
+   #define  hp_xfer_cnt_lo       0x18
+   #define  hp_xfer_cnt_mi       0x19
+   #define  hp_xfer_cnt_hi       0x1A
+   #define  hp_xfer_cmd          0x1B
+
+      #define  XFER_HOST_DMA     0x00     /*     0 0 0 Transfer Host -> DMA */
+      #define  XFER_DMA_HOST     0x01     /*     0 0 1 Transfer DMA  -> Host */
+      #define  XFER_HOST_MPU     0x02     /*     0 1 0 Transfer Host -> MPU  */
+      #define  XFER_MPU_HOST     0x03     /*     0 1 1 Transfer MPU  -> Host */
+      #define  XFER_DMA_MPU      0x04     /*     1 0 0 Transfer DMA  -> MPU  */
+      #define  XFER_MPU_DMA      0x05     /*     1 0 1 Transfer MPU  -> DMA  */
+      #define  SET_SEMAPHORE     0x06     /*     1 1 0 Set Semaphore         */
+      #define  XFER_NOP          0x07     /*     1 1 1 Transfer NOP          */
+      #define  XFER_MB_MPU       0x06     /*     1 1 0 Transfer MB -> MPU */
+      #define  XFER_MB_DMA       0x07     /*     1 1 1 Transfer MB -> DMA */
+
+
+      #define  XFER_HOST_AUTO    0x00     /*     0 0 Auto Transfer Size   */
+      #define  XFER_HOST_8BIT    0x08     /*     0 1 8 BIT Transfer Size  */
+      #define  XFER_HOST_16BIT   0x10     /*     1 0 16 BIT Transfer Size */
+      #define  XFER_HOST_32BIT   0x18     /*     1 1 32 BIT Transfer Size */
+
+      #define  XFER_DMA_8BIT     0x20     /*     0 1 8 BIT  Transfer Size */
+      #define  XFER_DMA_16BIT    0x40     /*     1 0 16 BIT Transfer Size */
+
+      #define  DISABLE_INT       BIT(7)   /*Do not interrupt at end of cmd. */
+
+      #define  HOST_WRT_CMD      ((DISABLE_INT + XFER_HOST_DMA + XFER_HOST_AUTO + XFER_DMA_8BIT))
+      #define  HOST_RD_CMD       ((DISABLE_INT + XFER_DMA_HOST + XFER_HOST_AUTO + XFER_DMA_8BIT))
+      #define  WIDE_HOST_WRT_CMD ((DISABLE_INT + XFER_HOST_DMA + XFER_HOST_AUTO + XFER_DMA_16BIT))
+      #define  WIDE_HOST_RD_CMD  ((DISABLE_INT + XFER_DMA_HOST + XFER_HOST_AUTO + XFER_DMA_16BIT))
+
+   #define  hp_host_addr_lo      0x1C
+   #define  hp_host_addr_lmi     0x1D
+   #define  hp_host_addr_hmi     0x1E
+   #define  hp_host_addr_hi      0x1F
+
+   #define  hp_pio_data          0x20
+   #define  hp_reserved_21       0x21
+   #define  hp_ee_ctrl           0x22
+
+      #define  EXT_ARB_ACK       BIT(7)
+      #define  SCSI_TERM_ENA_H   BIT(6)   /* SCSI high byte terminator */
+      #define  SEE_MS            BIT(5)
+      #define  SEE_CS            BIT(3)
+      #define  SEE_CLK           BIT(2)
+      #define  SEE_DO            BIT(1)
+      #define  SEE_DI            BIT(0)
+
+      #define  EE_READ           0x06
+      #define  EE_WRITE          0x05
+      #define  EWEN              0x04
+      #define  EWEN_ADDR         0x03C0
+      #define  EWDS              0x04
+      #define  EWDS_ADDR         0x0000
+
+   #define  hp_brdctl            0x23
+
+      #define  DAT_7             BIT(7)
+      #define  DAT_6             BIT(6)
+      #define  DAT_5             BIT(5)
+      #define  BRD_STB           BIT(4)
+      #define  BRD_CS            BIT(3)
+      #define  BRD_WR            BIT(2)
+
+   #define  hp_reserved_24       0x24
+   #define  hp_reserved_25       0x25
+
+
+
+
+   #define  hp_bm_ctrl           0x26
+
+      #define  SCSI_TERM_ENA_L   BIT(0)   /*Enable/Disable external terminators */
+      #define  FLUSH_XFER_CNTR   BIT(1)   /*Flush transfer counter */
+      #define  BM_XFER_MIN_8     BIT(2)   /*Enable bus master transfer of 9 */
+      #define  BIOS_ENA          BIT(3)   /*Enable BIOS/FLASH Enable */
+      #define  FORCE1_XFER       BIT(5)   /*Always xfer one byte in byte mode */
+      #define  FAST_SINGLE       BIT(6)   /*?? */
+
+      #define  BMCTRL_DEFAULT    (FORCE1_XFER|FAST_SINGLE|SCSI_TERM_ENA_L)
+
+   #define  hp_reserved_27       0x27
+
+   #define  hp_sg_addr           0x28
+   #define  hp_page_ctrl         0x29
+
+      #define  SCATTER_EN        BIT(0)   
+      #define  SGRAM_ARAM        BIT(1)   
+      #define  BIOS_SHADOW       BIT(2)   
+      #define  G_INT_DISABLE     BIT(3)   /* Enable/Disable all Interrupts */
+      #define  NARROW_SCSI_CARD  BIT(4)   /* NARROW/WIDE SCSI config pin */
+
+   #define  hp_reserved_2A       0x2A
+   #define  hp_pci_cmd_cfg       0x2B
+
+      #define  IO_SPACE_ENA      BIT(0)   /*enable I/O space */
+      #define  MEM_SPACE_ENA     BIT(1)   /*enable memory space */
+      #define  BUS_MSTR_ENA      BIT(2)   /*enable bus master operation */
+      #define  MEM_WI_ENA        BIT(4)   /*enable Write and Invalidate */
+      #define  PAR_ERR_RESP      BIT(6)   /*enable parity error responce. */
+
+   #define  hp_reserved_2C       0x2C
+
+   #define  hp_pci_stat_cfg      0x2D
+
+      #define  DATA_PARITY_ERR   BIT(0)   
+      #define  REC_TARGET_ABORT  BIT(4)   /*received Target abort */
+      #define  REC_MASTER_ABORT  BIT(5)   /*received Master abort */
+      #define  SIG_SYSTEM_ERR    BIT(6)   
+      #define  DETECTED_PAR_ERR  BIT(7)   
+
+   #define  hp_reserved_2E       0x2E
+
+   #define  hp_sys_status        0x2F
+
+      #define  SLV_DATA_RDY      BIT(0)   /*Slave data ready */
+      #define  XFER_CNT_ZERO     BIT(1)   /*Transfer counter = 0 */
+      #define  BM_FIFO_EMPTY     BIT(2)   /*FIFO empty */
+      #define  BM_FIFO_FULL      BIT(3)   /*FIFO full */
+      #define  HOST_OP_DONE      BIT(4)   /*host operation done */
+      #define  DMA_OP_DONE       BIT(5)   /*DMA operation done */
+      #define  SLV_OP_DONE       BIT(6)   /*Slave operation done */
+      #define  PWR_ON_FLAG       BIT(7)   /*Power on flag */
+
+   #define  hp_reserved_30       0x30
+
+   #define  hp_host_status0      0x31
+
+      #define  HOST_TERM         BIT(5)   /*Host Terminal Count */
+      #define  HOST_TRSHLD       BIT(6)   /*Host Threshold      */
+      #define  CONNECTED_2_HOST  BIT(7)   /*Connected to Host   */
+
+   #define  hp_reserved_32       0x32
+
+   #define  hp_rev_num           0x33
+
+      #define  REV_A_CONST       0x0E
+      #define  REV_B_CONST       0x0E
+
+   #define  hp_stack_data        0x34
+   #define  hp_stack_addr        0x35
+
+   #define  hp_ext_status        0x36
+
+      #define  BM_FORCE_OFF      BIT(0)   /*Bus Master is forced to get off */
+      #define  PCI_TGT_ABORT     BIT(0)   /*PCI bus master transaction aborted */
+      #define  PCI_DEV_TMOUT     BIT(1)   /*PCI Device Time out */
+      #define  FIFO_TC_NOT_ZERO  BIT(2)   /*FIFO or transfer counter not zero */
+      #define  CHIP_RST_OCCUR    BIT(3)   /*Chip reset occurs */
+      #define  CMD_ABORTED       BIT(4)   /*Command aborted */
+      #define  BM_PARITY_ERR     BIT(5)   /*parity error on data received   */
+      #define  PIO_OVERRUN       BIT(6)   /*Slave data overrun */
+      #define  BM_CMD_BUSY       BIT(7)   /*Bus master transfer command busy */
+      #define  BAD_EXT_STATUS    (BM_FORCE_OFF | PCI_DEV_TMOUT | CMD_ABORTED | \
+                                  BM_PARITY_ERR | PIO_OVERRUN)
+
+   #define  hp_int_status        0x37
+      
+      #define  BM_CMD_CMPL       BIT(0)   /*Bus Master command complete */
+      #define  EXT_STATUS_ON     BIT(1)   /*Extended status is valid */
+      #define  SCSI_INTERRUPT    BIT(2)   /*Global indication of a SCSI int. */
+      #define  BM_FIFO_RDY       BIT(4)   
+      #define  INT_ASSERTED      BIT(5)   /* */
+      #define  SRAM_BUSY         BIT(6)   /*Scatter/Gather RAM busy */
+      #define  CMD_REG_BUSY      BIT(7)                                       
+
+
+   #define  hp_fifo_cnt          0x38
+   #define  hp_curr_host_cnt     0x39
+   #define  hp_reserved_3A       0x3A
+   #define  hp_fifo_in_addr      0x3B
+
+   #define  hp_fifo_out_addr     0x3C
+   #define  hp_reserved_3D       0x3D
+   #define  hp_reserved_3E       0x3E
+   #define  hp_reserved_3F       0x3F
+
+
+
+   extern USHORT default_intena;
+
+   #define  hp_intena		 0x40
+
+      #define  RESET		 BITW(7)
+      #define  PROG_HLT		 BITW(6)  
+      #define  PARITY		 BITW(5)
+      #define  FIFO		 BITW(4)
+      #define  SEL		 BITW(3)
+      #define  SCAM_SEL		 BITW(2) 
+      #define  RSEL		 BITW(1)
+      #define  TIMEOUT		 BITW(0)
+      #define  BUS_FREE		 BITW(15)
+      #define  XFER_CNT_0	 BITW(14)
+      #define  PHASE		 BITW(13)
+      #define  IUNKWN		 BITW(12)
+      #define  ICMD_COMP	 BITW(11)
+      #define  ITICKLE		 BITW(10)
+      #define  IDO_STRT		 BITW(9)
+      #define  ITAR_DISC	 BITW(8)
+      #define  AUTO_INT		 (BITW(12)+BITW(11)+BITW(10)+BITW(9)+BITW(8))
+      #define  CLR_ALL_INT	 0xFFFF
+      #define  CLR_ALL_INT_1	 0xFF00
+
+   #define  hp_intstat		 0x42
+
+   #define  hp_scsisig           0x44
+
+      #define  SCSI_SEL          BIT(7)
+      #define  SCSI_BSY          BIT(6)
+      #define  SCSI_REQ          BIT(5)
+      #define  SCSI_ACK          BIT(4)
+      #define  SCSI_ATN          BIT(3)
+      #define  SCSI_CD           BIT(2)
+      #define  SCSI_MSG          BIT(1)
+      #define  SCSI_IOBIT        BIT(0)
+
+      #define  S_SCSI_PHZ        (BIT(2)+BIT(1)+BIT(0))
+      #define  S_CMD_PH          (BIT(2)              )
+      #define  S_MSGO_PH         (BIT(2)+BIT(1)       )
+      #define  S_STAT_PH         (BIT(2)       +BIT(0))
+      #define  S_MSGI_PH         (BIT(2)+BIT(1)+BIT(0))
+      #define  S_DATAI_PH        (              BIT(0))
+      #define  S_DATAO_PH        0x00
+      #define  S_ILL_PH          (       BIT(1)       )
+
+   #define  hp_scsictrl_0        0x45
+
+      #define  NO_ARB            BIT(7)
+      #define  SEL_TAR           BIT(6)
+      #define  ENA_ATN           BIT(4)
+      #define  ENA_RESEL         BIT(2)
+      #define  SCSI_RST          BIT(1)
+      #define  ENA_SCAM_SEL      BIT(0)
+
+
+
+   #define  hp_portctrl_0        0x46
+
+      #define  SCSI_PORT         BIT(7)
+      #define  SCSI_INBIT        BIT(6)
+      #define  DMA_PORT          BIT(5)
+      #define  DMA_RD            BIT(4)
+      #define  HOST_PORT         BIT(3)
+      #define  HOST_WRT          BIT(2)
+      #define  SCSI_BUS_EN       BIT(1)
+      #define  START_TO          BIT(0)
+
+   #define  hp_scsireset         0x47
+
+      #define  SCSI_TAR          BIT(7)
+      #define  SCSI_INI          BIT(6)
+      #define  SCAM_EN           BIT(5)
+      #define  ACK_HOLD          BIT(4)
+      #define  DMA_RESET         BIT(3)
+      #define  HPSCSI_RESET      BIT(2)
+      #define  PROG_RESET        BIT(1)
+      #define  FIFO_CLR          BIT(0)
+
+   #define  hp_xfercnt_0         0x48
+   #define  hp_xfercnt_1         0x49
+   #define  hp_xfercnt_2         0x4A
+   #define  hp_xfercnt_3         0x4B
+
+   #define  hp_fifodata_0        0x4C
+   #define  hp_fifodata_1        0x4D
+   #define  hp_addstat           0x4E
+
+      #define  SCAM_TIMER        BIT(7)
+      #define  AUTO_RUNNING      BIT(6)
+      #define  FAST_SYNC         BIT(5)
+      #define  SCSI_MODE8        BIT(3)
+      #define  SCSI_PAR_ERR      BIT(0)
+
+   #define  hp_prgmcnt_0         0x4F
+
+      #define  AUTO_PC_MASK      0x3F
+
+   #define  hp_selfid_0          0x50
+   #define  hp_selfid_1          0x51
+   #define  hp_arb_id            0x52
+
+      #define  ARB_ID            (BIT(3) + BIT(2) + BIT(1) + BIT(0))
+
+   #define  hp_select_id         0x53
+
+      #define  RESEL_ID          (BIT(7) + BIT(6) + BIT(5) + BIT(4))
+      #define  SELECT_ID         (BIT(3) + BIT(2) + BIT(1) + BIT(0))
+
+   #define  hp_synctarg_base     0x54
+   #define  hp_synctarg_12       0x54
+   #define  hp_synctarg_13       0x55
+   #define  hp_synctarg_14       0x56
+   #define  hp_synctarg_15       0x57
+
+   #define  hp_synctarg_8        0x58
+   #define  hp_synctarg_9        0x59
+   #define  hp_synctarg_10       0x5A
+   #define  hp_synctarg_11       0x5B
+
+   #define  hp_synctarg_4        0x5C
+   #define  hp_synctarg_5        0x5D
+   #define  hp_synctarg_6        0x5E
+   #define  hp_synctarg_7        0x5F
+
+   #define  hp_synctarg_0        0x60
+   #define  hp_synctarg_1        0x61
+   #define  hp_synctarg_2        0x62
+   #define  hp_synctarg_3        0x63
+
+      #define  RATE_20MB         0x00
+      #define  RATE_10MB         (              BIT(5))
+      #define  RATE_6_6MB        (       BIT(6)       )   
+      #define  RATE_5MB          (       BIT(6)+BIT(5))
+      #define  RATE_4MB          (BIT(7)              )
+      #define  RATE_3_33MB       (BIT(7)       +BIT(5))
+      #define  RATE_2_85MB       (BIT(7)+BIT(6)       )
+      #define  RATE_2_5MB        (BIT(7)+BIT(5)+BIT(6))
+      #define  NEXT_CLK          BIT(5)
+      #define  SLOWEST_SYNC      (BIT(7)+BIT(6)+BIT(5))
+      #define  NARROW_SCSI       BIT(4)
+      #define  SYNC_OFFSET       (BIT(3) + BIT(2) + BIT(1) + BIT(0))
+      #define  DEFAULT_ASYNC     0x00
+      #define  DEFAULT_OFFSET    0x0F
+
+   #define  hp_autostart_0       0x64
+   #define  hp_autostart_1       0x65
+   #define  hp_autostart_2       0x66
+   #define  hp_autostart_3       0x67
+
+
+
+      #define  DISABLE  0x00
+      #define  AUTO_IMMED    BIT(5)
+      #define  SELECT   BIT(6)
+      #define  RESELECT (BIT(6)+BIT(5))
+      #define  BUSFREE  BIT(7)
+      #define  XFER_0   (BIT(7)+BIT(5))
+      #define  END_DATA (BIT(7)+BIT(6))
+      #define  MSG_PHZ  (BIT(7)+BIT(6)+BIT(5))
+
+   #define  hp_gp_reg_0          0x68
+   #define  hp_gp_reg_1          0x69
+   #define  hp_gp_reg_2          0x6A
+   #define  hp_gp_reg_3          0x6B
+
+   #define  hp_seltimeout        0x6C
+
+
+      #define  TO_2ms            0x54      /* 2.0503ms */
+      #define  TO_4ms            0x67      /* 3.9959ms */
+
+      #define  TO_5ms            0x03      /* 4.9152ms */
+      #define  TO_10ms           0x07      /* 11.xxxms */
+      #define  TO_250ms          0x99      /* 250.68ms */
+      #define  TO_290ms          0xB1      /* 289.99ms */
+      #define  TO_350ms          0xD6      /* 350.62ms */
+      #define  TO_417ms          0xFF      /* 417.79ms */
+
+   #define  hp_clkctrl_0         0x6D
+
+      #define  PWR_DWN           BIT(6)
+      #define  ACTdeassert       BIT(4)
+      #define  ATNonErr          BIT(3)
+      #define  CLK_30MHZ         BIT(1)
+      #define  CLK_40MHZ         (BIT(1) + BIT(0))
+      #define  CLK_50MHZ         BIT(2)
+
+      #define  CLKCTRL_DEFAULT   (ACTdeassert | CLK_40MHZ)
+
+   #define  hp_fiforead          0x6E
+   #define  hp_fifowrite         0x6F
+
+   #define  hp_offsetctr         0x70
+   #define  hp_xferstat          0x71
+
+      #define  FIFO_FULL         BIT(7)
+      #define  FIFO_EMPTY        BIT(6)
+      #define  FIFO_MASK         0x3F   /* Mask for the FIFO count value. */
+      #define  FIFO_LEN          0x20
+
+   #define  hp_portctrl_1        0x72
+
+      #define  EVEN_HOST_P       BIT(5)
+      #define  INVT_SCSI         BIT(4)
+      #define  CHK_SCSI_P        BIT(3)
+      #define  HOST_MODE8        BIT(0)
+      #define  HOST_MODE16       0x00
+
+   #define  hp_xfer_pad          0x73
+
+      #define  ID_UNLOCK         BIT(3)
+      #define  XFER_PAD          BIT(2)
+
+   #define  hp_scsidata_0        0x74
+   #define  hp_scsidata_1        0x75
+   #define  hp_timer_0           0x76
+   #define  hp_timer_1           0x77
+
+   #define  hp_reserved_78       0x78
+   #define  hp_reserved_79       0x79
+   #define  hp_reserved_7A       0x7A
+   #define  hp_reserved_7B       0x7B
+
+   #define  hp_reserved_7C       0x7C
+   #define  hp_reserved_7D       0x7D
+   #define  hp_reserved_7E       0x7E
+   #define  hp_reserved_7F       0x7F
+
+   #define  hp_aramBase          0x80
+   #define  BIOS_DATA_OFFSET     0x60
+   #define  BIOS_RELATIVE_CARD   0x64
+
+
+
+
+      #define  AUTO_LEN 0x80
+      #define  AR0      0x00
+      #define  AR1      BITW(8)
+      #define  AR2      BITW(9)
+      #define  AR3      (BITW(9) + BITW(8))
+      #define  SDATA    BITW(10)
+
+      #define  NOP_OP   0x00        /* Nop command */
+
+      #define  CRD_OP   BITW(11)     /* Cmp Reg. w/ Data */
+
+      #define  CRR_OP   BITW(12)     /* Cmp Reg. w. Reg. */
+
+      #define  CBE_OP   (BITW(14)+BITW(12)+BITW(11)) /* Cmp SCSI cmd class & Branch EQ */
+      
+      #define  CBN_OP   (BITW(14)+BITW(13))  /* Cmp SCSI cmd class & Branch NOT EQ */
+      
+      #define  CPE_OP   (BITW(14)+BITW(11))  /* Cmp SCSI phs & Branch EQ */
+
+      #define  CPN_OP   (BITW(14)+BITW(12))  /* Cmp SCSI phs & Branch NOT EQ */
+
+
+      #define  ADATA_OUT   0x00     
+      #define  ADATA_IN    BITW(8)
+      #define  ACOMMAND    BITW(10)
+      #define  ASTATUS     (BITW(10)+BITW(8))
+      #define  AMSG_OUT    (BITW(10)+BITW(9))
+      #define  AMSG_IN     (BITW(10)+BITW(9)+BITW(8))
+      #define  AILLEGAL    (BITW(9)+BITW(8))
+
+
+      #define  BRH_OP   BITW(13)   /* Branch */
+
+      
+      #define  ALWAYS   0x00
+      #define  EQUAL    BITW(8)
+      #define  NOT_EQ   BITW(9)
+
+      #define  TCB_OP   (BITW(13)+BITW(11))    /* Test condition & branch */
+
+      
+      #define  ATN_SET     BITW(8)
+      #define  ATN_RESET   BITW(9)
+      #define  XFER_CNT    (BITW(9)+BITW(8))
+      #define  FIFO_0      BITW(10)
+      #define  FIFO_NOT0   (BITW(10)+BITW(8))
+      #define  T_USE_SYNC0 (BITW(10)+BITW(9))
+
+
+      #define  MPM_OP   BITW(15)        /* Match phase and move data */
+
+      #define  MDR_OP   (BITW(12)+BITW(11)) /* Move data to Reg. */
+
+      #define  MRR_OP   BITW(14)        /* Move DReg. to Reg. */
+
+
+      #define  S_IDREG  (BIT(2)+BIT(1)+BIT(0))
+
+
+      #define  D_AR0    0x00
+      #define  D_AR1    BIT(0)
+      #define  D_AR2    BIT(1)
+      #define  D_AR3    (BIT(1) + BIT(0))
+      #define  D_SDATA  BIT(2)
+      #define  D_BUCKET (BIT(2) + BIT(1) + BIT(0))
+
+
+      #define  ADR_OP   (BITW(13)+BITW(12)) /* Logical AND Reg. w. Data */
+
+      #define  ADS_OP   (BITW(14)+BITW(13)+BITW(12)) 
+
+      #define  ODR_OP   (BITW(13)+BITW(12)+BITW(11))  
+
+      #define  ODS_OP   (BITW(14)+BITW(13)+BITW(12)+BITW(11))  
+
+      #define  STR_OP   (BITW(15)+BITW(14)) /* Store to A_Reg. */
+
+      #define  AINT_ENA1   0x00
+      #define  AINT_STAT1  BITW(8)
+      #define  ASCSI_SIG   BITW(9)
+      #define  ASCSI_CNTL  (BITW(9)+BITW(8))
+      #define  APORT_CNTL  BITW(10)
+      #define  ARST_CNTL   (BITW(10)+BITW(8))
+      #define  AXFERCNT0   (BITW(10)+BITW(9))
+      #define  AXFERCNT1   (BITW(10)+BITW(9)+BITW(8))
+      #define  AXFERCNT2   BITW(11)
+      #define  AFIFO_DATA  (BITW(11)+BITW(8))
+      #define  ASCSISELID  (BITW(11)+BITW(9))
+      #define  ASCSISYNC0  (BITW(11)+BITW(9)+BITW(8))
+
+
+      #define  RAT_OP      (BITW(14)+BITW(13)+BITW(11))
+
+      #define  SSI_OP      (BITW(15)+BITW(11))
+
+
+      #define  SSI_ITAR_DISC	(ITAR_DISC >> 8)
+      #define  SSI_IDO_STRT	(IDO_STRT >> 8)
+      #define  SSI_IDI_STRT	(IDO_STRT >> 8)
+
+      #define  SSI_ICMD_COMP	(ICMD_COMP >> 8)
+      #define  SSI_ITICKLE	(ITICKLE >> 8)
+
+      #define  SSI_IUNKWN	(IUNKWN >> 8)
+      #define  SSI_INO_CC	(IUNKWN >> 8)
+      #define  SSI_IRFAIL	(IUNKWN >> 8)
+
+
+      #define  NP    0x10     /*Next Phase */
+      #define  NTCMD 0x02     /*Non- Tagged Command start */
+      #define  CMDPZ 0x04     /*Command phase */
+      #define  DINT  0x12     /*Data Out/In interrupt */
+      #define  DI    0x13     /*Data Out */
+      #define  MI    0x14     /*Message In */
+      #define  DC    0x19     /*Disconnect Message */
+      #define  ST    0x1D     /*Status Phase */
+      #define  UNKNWN 0x24    /*Unknown bus action */
+      #define  CC    0x25     /*Command Completion failure */
+      #define  TICK  0x26     /*New target reselected us. */
+      #define  RFAIL 0x27     /*Reselection failed */
+      #define  SELCHK 0x28     /*Select & Check SCSI ID latch reg */
+
+
+      #define  ID_MSG_STRT    hp_aramBase + 0x00
+      #define  NON_TAG_ID_MSG hp_aramBase + 0x06
+      #define  CMD_STRT       hp_aramBase + 0x08
+      #define  SYNC_MSGS      hp_aramBase + 0x08
+
+
+
+
+
+      #define  TAG_STRT          0x00
+      #define  SELECTION_START   0x00
+      #define  DISCONNECT_START  0x10/2
+      #define  END_DATA_START    0x14/2
+      #define  NONTAG_STRT       0x02/2
+      #define  CMD_ONLY_STRT     CMDPZ/2
+      #define  TICKLE_STRT     TICK/2
+      #define  SELCHK_STRT     SELCHK/2
+
+
+
+
+#define mEEPROM_CLK_DELAY(port) (RD_HARPOON(port+hp_intstat_1))
+
+#define mWAIT_10MS(port) (RD_HARPOON(port+hp_intstat_1))
+
+
+#define CLR_XFER_CNT(port) (WR_HARPOON(port+hp_xfercnt_0, 0x00))
+
+#define SET_XFER_CNT(port, data) (WR_HARP32(port,hp_xfercnt_0,data))
+
+#define GET_XFER_CNT(port, xfercnt) {RD_HARP32(port,hp_xfercnt_0,xfercnt); xfercnt &= 0xFFFFFF;}
+/* #define GET_XFER_CNT(port, xfercnt) (xfercnt = RD_HARPOON(port+hp_xfercnt_2), \
+                                 xfercnt <<= 16,\
+                                 xfercnt |= RDW_HARPOON((USHORT)(port+hp_xfercnt_0)))
+ */
+#if defined(DOS)
+#define HP_SETUP_ADDR_CNT(port,addr,count) (WRW_HARPOON((USHORT)(port+hp_host_addr_lo), (USHORT)(addr & 0x0000FFFFL)),\
+         addr >>= 16,\
+         WRW_HARPOON((USHORT)(port+hp_host_addr_hmi), (USHORT)(addr & 0x0000FFFFL)),\
+         WR_HARP32(port,hp_xfercnt_0,count),\
+         WRW_HARPOON((USHORT)(port+hp_xfer_cnt_lo), (USHORT)(count & 0x0000FFFFL)),\
+         count >>= 16,\
+         WR_HARPOON(port+hp_xfer_cnt_hi, (count & 0xFF)))
+#else
+#define HP_SETUP_ADDR_CNT(port,addr,count) (WRW_HARPOON((port+hp_host_addr_lo), (USHORT)(addr & 0x0000FFFFL)),\
+         addr >>= 16,\
+         WRW_HARPOON((port+hp_host_addr_hmi), (USHORT)(addr & 0x0000FFFFL)),\
+         WR_HARP32(port,hp_xfercnt_0,count),\
+         WRW_HARPOON((port+hp_xfer_cnt_lo), (USHORT)(count & 0x0000FFFFL)),\
+         count >>= 16,\
+         WR_HARPOON(port+hp_xfer_cnt_hi, (count & 0xFF)))
+#endif
+
+#define ACCEPT_MSG(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
+                          WR_HARPOON(port+hp_scsisig, S_ILL_PH);}
+
+
+#define ACCEPT_MSG_ATN(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
+                          WR_HARPOON(port+hp_scsisig, (S_ILL_PH|SCSI_ATN));}
+
+#define ACCEPT_STAT(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
+                          WR_HARPOON(port+hp_scsisig, S_ILL_PH);}
+
+#define ACCEPT_STAT_ATN(port) {while(RD_HARPOON(port+hp_scsisig) & SCSI_REQ){}\
+                          WR_HARPOON(port+hp_scsisig, (S_ILL_PH|SCSI_ATN));}
+
+#define DISABLE_AUTO(port) (WR_HARPOON(port+hp_scsireset, PROG_RESET),\
+                        WR_HARPOON(port+hp_scsireset, 0x00))
+
+#define ARAM_ACCESS(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
+                             (RD_HARPOON(p_port+hp_page_ctrl) | SGRAM_ARAM)))
+
+#define SGRAM_ACCESS(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
+                             (RD_HARPOON(p_port+hp_page_ctrl) & ~SGRAM_ARAM)))
+
+#define MDISABLE_INT(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
+                             (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE)))
+
+#define MENABLE_INT(p_port) (WR_HARPOON(p_port+hp_page_ctrl, \
+                             (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE)))
+
+
+
+#endif
+
+
+#if (FW_TYPE==_UCB_MGR_)
+void ReadNVRam(PSCCBcard pCurrCard,PUCB p_ucb);
+void WriteNVRam(PSCCBcard pCurrCard,PUCB p_ucb);
+void UpdateCheckSum(u32bits baseport);
+#endif // (FW_TYPE==_UCB_MGR_)
+
+#if defined(DOS)
+UCHAR sfm(USHORT port, PSCCB pcurrSCCB);
+void  scsiStartAuto(USHORT port);
+UCHAR sisyncn(USHORT port, UCHAR p_card, UCHAR syncFlag);
+void  ssel(USHORT port, UCHAR p_card);
+void  sres(USHORT port, UCHAR p_card, PSCCBcard pCurrCard);
+void  sdecm(UCHAR message, USHORT port, UCHAR p_card);
+void  shandem(USHORT port, UCHAR p_card,PSCCB pCurrSCCB);
+void  stsyncn(USHORT port, UCHAR p_card);
+void  sisyncr(USHORT port,UCHAR sync_pulse, UCHAR offset);
+void  sssyncv(USHORT p_port, UCHAR p_id, UCHAR p_sync_value, PSCCBMgr_tar_info currTar_Info);
+void  sresb(USHORT port, UCHAR p_card);
+void  sxfrp(USHORT p_port, UCHAR p_card);
+void  schkdd(USHORT port, UCHAR p_card);
+UCHAR RdStack(USHORT port, UCHAR index);
+void  WrStack(USHORT portBase, UCHAR index, UCHAR data);
+UCHAR ChkIfChipInitialized(USHORT ioPort);
+UCHAR GetTarLun(USHORT port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tag, PUCHAR lun);
+void SendMsg(USHORT port, UCHAR message);
+void  queueFlushTargSccb(UCHAR p_card, UCHAR thisTarg, UCHAR error_code);
+#else
+UCHAR sfm(ULONG port, PSCCB pcurrSCCB);
+void  scsiStartAuto(ULONG port);
+UCHAR sisyncn(ULONG port, UCHAR p_card, UCHAR syncFlag);
+void  ssel(ULONG port, UCHAR p_card);
+void  sres(ULONG port, UCHAR p_card, PSCCBcard pCurrCard);
+void  sdecm(UCHAR message, ULONG port, UCHAR p_card);
+void  shandem(ULONG port, UCHAR p_card,PSCCB pCurrSCCB);
+void  stsyncn(ULONG port, UCHAR p_card);
+void  sisyncr(ULONG port,UCHAR sync_pulse, UCHAR offset);
+void  sssyncv(ULONG p_port, UCHAR p_id, UCHAR p_sync_value, PSCCBMgr_tar_info currTar_Info);
+void  sresb(ULONG port, UCHAR p_card);
+void  sxfrp(ULONG p_port, UCHAR p_card);
+void  schkdd(ULONG port, UCHAR p_card);
+UCHAR RdStack(ULONG port, UCHAR index);
+void  WrStack(ULONG portBase, UCHAR index, UCHAR data);
+UCHAR ChkIfChipInitialized(ULONG ioPort);
+UCHAR GetTarLun(ULONG port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tar, PUCHAR lun);
+void SendMsg(ULONG port, UCHAR message);
+void  queueFlushTargSccb(UCHAR p_card, UCHAR thisTarg, UCHAR error_code);
+#endif
+
+void  ssenss(PSCCBcard pCurrCard);
+void  sinits(PSCCB p_sccb, UCHAR p_card);
+void  RNVRamData(PNVRamInfo pNvRamInfo);
+
+#if defined(WIDE_SCSI)
+   #if defined(DOS)
+   UCHAR siwidn(USHORT port, UCHAR p_card);
+   void  stwidn(USHORT port, UCHAR p_card);
+   void  siwidr(USHORT port, UCHAR width);
+   #else
+   UCHAR siwidn(ULONG port, UCHAR p_card);
+   void  stwidn(ULONG port, UCHAR p_card);
+   void  siwidr(ULONG port, UCHAR width);
+   #endif
+#endif
+
+
+void  queueSelectFail(PSCCBcard pCurrCard, UCHAR p_card);
+void  queueDisconnect(PSCCB p_SCCB, UCHAR p_card);
+void  queueCmdComplete(PSCCBcard pCurrCard, PSCCB p_SCCB, UCHAR p_card);
+void  queueSearchSelect(PSCCBcard pCurrCard, UCHAR p_card);
+void  queueFlushSccb(UCHAR p_card, UCHAR error_code);
+void  queueAddSccb(PSCCB p_SCCB, UCHAR card);
+UCHAR queueFindSccb(PSCCB p_SCCB, UCHAR p_card);
+void  utilUpdateResidual(PSCCB p_SCCB);
+USHORT CalcCrc16(UCHAR buffer[]);
+UCHAR  CalcLrc(UCHAR buffer[]);
+
+
+#if defined(DOS)
+void  Wait1Second(USHORT p_port);
+void  Wait(USHORT p_port, UCHAR p_delay);
+void  utilEEWriteOnOff(USHORT p_port,UCHAR p_mode);
+void  utilEEWrite(USHORT p_port, USHORT ee_data, USHORT ee_addr);
+USHORT utilEERead(USHORT p_port, USHORT ee_addr);
+void  utilEESendCmdAddr(USHORT p_port, UCHAR ee_cmd, USHORT ee_addr);
+#else
+void  Wait1Second(ULONG p_port);
+void  Wait(ULONG p_port, UCHAR p_delay);
+void  utilEEWriteOnOff(ULONG p_port,UCHAR p_mode);
+void  utilEEWrite(ULONG p_port, USHORT ee_data, USHORT ee_addr);
+USHORT utilEERead(ULONG p_port, USHORT ee_addr);
+void  utilEESendCmdAddr(ULONG p_port, UCHAR ee_cmd, USHORT ee_addr);
+#endif
+
+
+
+#if defined(OS2)
+   void  far phaseDataOut(ULONG port, UCHAR p_card);
+   void  far phaseDataIn(ULONG port, UCHAR p_card);
+   void  far phaseCommand(ULONG port, UCHAR p_card);
+   void  far phaseStatus(ULONG port, UCHAR p_card);
+   void  far phaseMsgOut(ULONG port, UCHAR p_card);
+   void  far phaseMsgIn(ULONG port, UCHAR p_card);
+   void  far phaseIllegal(ULONG port, UCHAR p_card);
+#else
+   #if defined(DOS)
+      void  phaseDataOut(USHORT port, UCHAR p_card);
+      void  phaseDataIn(USHORT port, UCHAR p_card);
+      void  phaseCommand(USHORT port, UCHAR p_card);
+      void  phaseStatus(USHORT port, UCHAR p_card);
+      void  phaseMsgOut(USHORT port, UCHAR p_card);
+      void  phaseMsgIn(USHORT port, UCHAR p_card);
+      void  phaseIllegal(USHORT port, UCHAR p_card);
+   #else
+      void  phaseDataOut(ULONG port, UCHAR p_card);
+      void  phaseDataIn(ULONG port, UCHAR p_card);
+      void  phaseCommand(ULONG port, UCHAR p_card);
+      void  phaseStatus(ULONG port, UCHAR p_card);
+      void  phaseMsgOut(ULONG port, UCHAR p_card);
+      void  phaseMsgIn(ULONG port, UCHAR p_card);
+      void  phaseIllegal(ULONG port, UCHAR p_card);
+   #endif
+#endif
+
+#if defined(DOS)
+void  phaseDecode(USHORT port, UCHAR p_card);
+void  phaseChkFifo(USHORT port, UCHAR p_card);
+void  phaseBusFree(USHORT p_port, UCHAR p_card);
+#else
+void  phaseDecode(ULONG port, UCHAR p_card);
+void  phaseChkFifo(ULONG port, UCHAR p_card);
+void  phaseBusFree(ULONG p_port, UCHAR p_card);
+#endif
+
+
+
+
+#if defined(DOS)
+void  XbowInit(USHORT port, UCHAR scamFlg);
+void  BusMasterInit(USHORT p_port);
+int   DiagXbow(USHORT port);
+int   DiagBusMaster(USHORT port);
+void  DiagEEPROM(USHORT p_port);
+#else
+void  XbowInit(ULONG port, UCHAR scamFlg);
+void  BusMasterInit(ULONG p_port);
+int   DiagXbow(ULONG port);
+int   DiagBusMaster(ULONG port);
+void  DiagEEPROM(ULONG p_port);
+#endif
+
+
+
+
+#if defined(DOS)
+void  busMstrAbort(USHORT port);
+UCHAR busMstrTimeOut(USHORT port);
+void  dataXferProcessor(USHORT port, PSCCBcard pCurrCard);
+void  busMstrSGDataXferStart(USHORT port, PSCCB pCurrSCCB);
+void  busMstrDataXferStart(USHORT port, PSCCB pCurrSCCB);
+void  hostDataXferAbort(USHORT port, UCHAR p_card, PSCCB pCurrSCCB);
+#else
+void  busMstrAbort(ULONG port);
+UCHAR busMstrTimeOut(ULONG port);
+void  dataXferProcessor(ULONG port, PSCCBcard pCurrCard);
+void  busMstrSGDataXferStart(ULONG port, PSCCB pCurrSCCB);
+void  busMstrDataXferStart(ULONG port, PSCCB pCurrSCCB);
+void  hostDataXferAbort(ULONG port, UCHAR p_card, PSCCB pCurrSCCB);
+#endif
+void  hostDataXferRestart(PSCCB currSCCB);
+
+
+#if defined (DOS)
+UCHAR SccbMgr_bad_isr(USHORT p_port, UCHAR p_card, PSCCBcard pCurrCard, USHORT p_int);
+#else
+UCHAR SccbMgr_bad_isr(ULONG p_port, UCHAR p_card, PSCCBcard pCurrCard, USHORT p_int);
+
+#endif
+
+void  SccbMgrTableInitAll(void);
+void  SccbMgrTableInitCard(PSCCBcard pCurrCard, UCHAR p_card);
+void  SccbMgrTableInitTarget(UCHAR p_card, UCHAR target);
+
+
+
+void  scini(UCHAR p_card, UCHAR p_our_id, UCHAR p_power_up);
+
+#if defined(DOS)
+int   scarb(USHORT p_port, UCHAR p_sel_type);
+void  scbusf(USHORT p_port);
+void  scsel(USHORT p_port);
+void  scasid(UCHAR p_card, USHORT p_port);
+UCHAR scxferc(USHORT p_port, UCHAR p_data);
+UCHAR scsendi(USHORT p_port, UCHAR p_id_string[]);
+UCHAR sciso(USHORT p_port, UCHAR p_id_string[]);
+void  scwirod(USHORT p_port, UCHAR p_data_bit);
+void  scwiros(USHORT p_port, UCHAR p_data_bit);
+UCHAR scvalq(UCHAR p_quintet);
+UCHAR scsell(USHORT p_port, UCHAR targ_id);
+void  scwtsel(USHORT p_port);
+void  inisci(UCHAR p_card, USHORT p_port, UCHAR p_our_id);
+void  scsavdi(UCHAR p_card, USHORT p_port);
+#else
+int   scarb(ULONG p_port, UCHAR p_sel_type);
+void  scbusf(ULONG p_port);
+void  scsel(ULONG p_port);
+void  scasid(UCHAR p_card, ULONG p_port);
+UCHAR scxferc(ULONG p_port, UCHAR p_data);
+UCHAR scsendi(ULONG p_port, UCHAR p_id_string[]);
+UCHAR sciso(ULONG p_port, UCHAR p_id_string[]);
+void  scwirod(ULONG p_port, UCHAR p_data_bit);
+void  scwiros(ULONG p_port, UCHAR p_data_bit);
+UCHAR scvalq(UCHAR p_quintet);
+UCHAR scsell(ULONG p_port, UCHAR targ_id);
+void  scwtsel(ULONG p_port);
+void  inisci(UCHAR p_card, ULONG p_port, UCHAR p_our_id);
+void  scsavdi(UCHAR p_card, ULONG p_port);
+#endif
+UCHAR scmachid(UCHAR p_card, UCHAR p_id_string[]);
+
+
+#if defined(DOS)
+void  autoCmdCmplt(USHORT p_port, UCHAR p_card);
+void  autoLoadDefaultMap(USHORT p_port);
+#else
+void  autoCmdCmplt(ULONG p_port, UCHAR p_card);
+void  autoLoadDefaultMap(ULONG p_port);
+#endif
+
+
+
+#if (FW_TYPE==_SCCB_MGR_)
+	void  OS_start_timer(unsigned long ioport, unsigned long timeout);
+	void  OS_stop_timer(unsigned long ioport, unsigned long timeout);
+	void  OS_disable_int(unsigned char intvec);
+	void  OS_enable_int(unsigned char intvec);
+	void  OS_delay(unsigned long count);
+	int   OS_VirtToPhys(u32bits CardHandle, u32bits *physaddr, u32bits *virtaddr);
+	#if !(defined(UNIX) || defined(OS2) || defined(SOLARIS_REAL_MODE)) 
+	void  OS_Lock(PSCCBMGR_INFO pCardInfo);
+	void  OS_UnLock(PSCCBMGR_INFO pCardInfo);
+#endif // if FW_TYPE == ...
+
+#endif
+
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+
+
+#if defined(OS2)
+   extern void (far *s_PhaseTbl[8]) (ULONG, UCHAR);
+#else
+   #if defined(DOS)
+      extern void (*s_PhaseTbl[8]) (USHORT, UCHAR);
+   #else
+      extern void (*s_PhaseTbl[8]) (ULONG, UCHAR);
+   #endif
+#endif
+
+extern SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR];
+extern NVRAMINFO nvRamInfo[MAX_MB_CARDS];
+#if defined(DOS) || defined(OS2)
+extern UCHAR temp_id_string[ID_STRING_LENGTH];
+#endif
+extern UCHAR scamHAString[];
+
+
+extern UCHAR mbCards;
+#if defined(BUGBUG)
+extern UCHAR debug_int[MAX_CARDS][debug_size];
+extern UCHAR debug_index[MAX_CARDS];
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+
+#if (FW_TYPE==_SCCB_MGR_)
+#if defined(DOS)
+   extern UCHAR first_time;
+#endif
+#endif /* (FW_TYPE==_SCCB_MGR_) */
+
+#if (FW_TYPE==_UCB_MGR_)
+#if defined(DOS)
+   extern u08bits first_time;
+#endif
+#endif /* (FW_TYPE==_UCB_MGR_) */
+
+#if defined(BUGBUG)
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+
+extern unsigned int SccbGlobalFlags;
+
+
+#ident "$Id: sccb.c 1.17 1997/02/11 21:06:41 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   sccb.c  $
+ *
+ *   Description:  Functions relating to handling of the SCCB interface
+ *                 between the device driver and the HARPOON.
+ *
+ *   $Date: 1997/02/11 21:06:41 $
+ *
+ *   $Revision: 1.17 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+	/*#include <budi.h>*/
+	/*#include <budioctl.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <eeprom.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+
+
+#if (FW_TYPE==_SCCB_MGR_)
+#define mOS_Lock(card)    OS_Lock((PSCCBMGR_INFO)(((PSCCBcard)card)->cardInfo))
+#define mOS_UnLock(card)  OS_UnLock((PSCCBMGR_INFO)(((PSCCBcard)card)->cardInfo))
+#else /* FW_TYPE==_UCB_MGR_ */
+#define mOS_Lock(card)    OS_Lock((u32bits)(((PSCCBcard)card)->ioPort))
+#define mOS_UnLock(card)  OS_UnLock((u32bits)(((PSCCBcard)card)->ioPort))
+#endif
+
+
+/*
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+extern SCCBCARD BL_Card[MAX_CARDS];
+
+extern NVRAMINFO nvRamInfo[MAX_MB_CARDS];
+extern UCHAR mbCards;
+
+#if defined (OS2)
+   extern void (far *s_PhaseTbl[8]) (ULONG, UCHAR);
+#else
+   #if defined(DOS)
+      extern void (*s_PhaseTbl[8]) (USHORT, UCHAR);
+   #else
+      extern void (*s_PhaseTbl[8]) (ULONG, UCHAR);
+   #endif
+#endif
+
+
+#if defined(BUGBUG)
+extern UCHAR debug_int[MAX_CARDS][debug_size];
+extern UCHAR debug_index[MAX_CARDS];
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+*/
+
+#if (FW_TYPE==_SCCB_MGR_)
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_sense_adapter
+ *
+ * Description: Setup and/or Search for cards and return info to caller.
+ *
+ *---------------------------------------------------------------------*/
+
+int SccbMgr_sense_adapter(PSCCBMGR_INFO pCardInfo)
+{
+#if defined(DOS)
+#else
+   static UCHAR first_time = 1;
+#endif
+
+   UCHAR i,j,id,ScamFlg;
+   USHORT temp,temp2,temp3,temp4,temp5,temp6;
+#if defined(DOS)
+   USHORT ioport;
+#else
+   ULONG ioport;
+#endif
+	PNVRamInfo pCurrNvRam;
+
+#if defined(DOS)
+   ioport = (USHORT)pCardInfo->si_baseaddr;
+#else
+   ioport = pCardInfo->si_baseaddr;
+#endif
+
+
+   if (RD_HARPOON(ioport+hp_vendor_id_0) != ORION_VEND_0)
+      return((int)FAILURE);
+
+   if ((RD_HARPOON(ioport+hp_vendor_id_1) != ORION_VEND_1))
+      return((int)FAILURE);
+
+   if ((RD_HARPOON(ioport+hp_device_id_0) != ORION_DEV_0))
+      return((int)FAILURE);
+
+   if ((RD_HARPOON(ioport+hp_device_id_1) != ORION_DEV_1))
+      return((int)FAILURE);
+
+
+   if (RD_HARPOON(ioport+hp_rev_num) != 0x0f){
+
+/* For new Harpoon then check for sub_device ID LSB
+   the bits(0-3) must be all ZERO for compatible with
+   current version of SCCBMgr, else skip this Harpoon
+	device. */
+
+	   if (RD_HARPOON(ioport+hp_sub_device_id_0) & 0x0f)
+	      return((int)FAILURE);
+	}
+
+   if (first_time)
+      {
+      SccbMgrTableInitAll();
+      first_time = 0;
+		mbCards = 0;
+      }
+
+	if(RdStack(ioport, 0) != 0x00) {
+		if(ChkIfChipInitialized(ioport) == FALSE)
+		{
+			pCurrNvRam = NULL;
+			XbowInit(ioport, 0);             /*Must Init the SCSI before attempting */
+			DiagEEPROM(ioport);
+		}
+		else
+		{
+			if(mbCards < MAX_MB_CARDS) {
+				pCurrNvRam = &nvRamInfo[mbCards];
+				mbCards++;
+				pCurrNvRam->niBaseAddr = ioport;
+				RNVRamData(pCurrNvRam);
+			}else
+				return((int) FAILURE);
+		}
+	}else
+		pCurrNvRam = NULL;
+#if defined (NO_BIOS_OPTION)
+	pCurrNvRam = NULL;
+   XbowInit(ioport, 0);                /*Must Init the SCSI before attempting */
+   DiagEEPROM(ioport);
+#endif  /* No BIOS Option */
+
+   WR_HARPOON(ioport+hp_clkctrl_0, CLKCTRL_DEFAULT);
+   WR_HARPOON(ioport+hp_sys_ctrl, 0x00);
+
+	if(pCurrNvRam)
+		pCardInfo->si_id = pCurrNvRam->niAdapId;
+	else
+	   pCardInfo->si_id = (UCHAR)(utilEERead(ioport, (ADAPTER_SCSI_ID/2)) &
+   	   (UCHAR)0x0FF);
+
+   pCardInfo->si_lun = 0x00;
+   pCardInfo->si_fw_revision = ORION_FW_REV;
+   temp2 = 0x0000;
+   temp3 = 0x0000;
+   temp4 = 0x0000;
+   temp5 = 0x0000;
+   temp6 = 0x0000;
+
+   for (id = 0; id < (16/2); id++) {
+
+		if(pCurrNvRam){
+			temp = (USHORT) pCurrNvRam->niSyncTbl[id];
+			temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
+					 (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
+		}else
+	      temp = utilEERead(ioport, (USHORT)((SYNC_RATE_TBL/2)+id));
+
+      for (i = 0; i < 2; temp >>=8,i++) {
+
+         temp2 >>= 1;
+         temp3 >>= 1;
+         temp4 >>= 1;
+         temp5 >>= 1;
+         temp6 >>= 1;
+	 switch (temp & 0x3)
+	   {
+	   case AUTO_RATE_20:	/* Synchronous, 20 mega-transfers/second */
+	     temp6 |= 0x8000;	/* Fall through */
+	   case AUTO_RATE_10:	/* Synchronous, 10 mega-transfers/second */
+	     temp5 |= 0x8000;	/* Fall through */
+	   case AUTO_RATE_05:	/* Synchronous, 5 mega-transfers/second */
+	     temp2 |= 0x8000;	/* Fall through */
+	   case AUTO_RATE_00:	/* Asynchronous */
+	     break;
+	   }
+
+         if (temp & DISC_ENABLE_BIT)
+	   temp3 |= 0x8000;
+
+         if (temp & WIDE_NEGO_BIT)
+	   temp4 |= 0x8000;
+
+         }
+      }
+
+   pCardInfo->si_per_targ_init_sync = temp2;
+   pCardInfo->si_per_targ_no_disc = temp3;
+   pCardInfo->si_per_targ_wide_nego = temp4;
+   pCardInfo->si_per_targ_fast_nego = temp5;
+   pCardInfo->si_per_targ_ultra_nego = temp6;
+
+	if(pCurrNvRam)
+		i = pCurrNvRam->niSysConf;
+	else
+	   i = (UCHAR)(utilEERead(ioport, (SYSTEM_CONFIG/2)));
+
+	if(pCurrNvRam)
+		ScamFlg = pCurrNvRam->niScamConf;
+	else
+	   ScamFlg = (UCHAR) utilEERead(ioport, SCAM_CONFIG/2);
+
+   pCardInfo->si_flags = 0x0000;
+
+   if (i & 0x01)
+      pCardInfo->si_flags |= SCSI_PARITY_ENA;
+
+   if (!(i & 0x02))
+      pCardInfo->si_flags |= SOFT_RESET;
+
+   if (i & 0x10)
+      pCardInfo->si_flags |= EXTENDED_TRANSLATION;
+
+   if (ScamFlg & SCAM_ENABLED)
+     pCardInfo->si_flags |= FLAG_SCAM_ENABLED;
+
+   if (ScamFlg & SCAM_LEVEL2)
+     pCardInfo->si_flags |= FLAG_SCAM_LEVEL2;
+
+   j = (RD_HARPOON(ioport+hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
+   if (i & 0x04) {
+      j |= SCSI_TERM_ENA_L;
+      }
+   WR_HARPOON(ioport+hp_bm_ctrl, j );
+
+   j = (RD_HARPOON(ioport+hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
+   if (i & 0x08) {
+      j |= SCSI_TERM_ENA_H;
+      }
+   WR_HARPOON(ioport+hp_ee_ctrl, j );
+
+   if (!(RD_HARPOON(ioport+hp_page_ctrl) & NARROW_SCSI_CARD))
+
+      pCardInfo->si_flags |= SUPPORT_16TAR_32LUN;
+
+   pCardInfo->si_card_family = HARPOON_FAMILY;
+   pCardInfo->si_bustype = BUSTYPE_PCI;
+
+	if(pCurrNvRam){
+   	pCardInfo->si_card_model[0] = '9';
+		switch(pCurrNvRam->niModel & 0x0f){
+			case MODEL_LT:
+		   	pCardInfo->si_card_model[1] = '3';
+		   	pCardInfo->si_card_model[2] = '0';
+				break;
+			case MODEL_LW:
+		   	pCardInfo->si_card_model[1] = '5';
+		   	pCardInfo->si_card_model[2] = '0';
+				break;
+			case MODEL_DL:
+		   	pCardInfo->si_card_model[1] = '3';
+		   	pCardInfo->si_card_model[2] = '2';
+				break;
+			case MODEL_DW:
+		   	pCardInfo->si_card_model[1] = '5';
+		   	pCardInfo->si_card_model[2] = '2';
+				break;
+		}
+	}else{
+	   temp = utilEERead(ioport, (MODEL_NUMB_0/2));
+   	pCardInfo->si_card_model[0] = (UCHAR)(temp >> 8);
+	   temp = utilEERead(ioport, (MODEL_NUMB_2/2));
+
+   	pCardInfo->si_card_model[1] = (UCHAR)(temp & 0x00FF);
+	   pCardInfo->si_card_model[2] = (UCHAR)(temp >> 8);
+	}
+
+   if (pCardInfo->si_card_model[1] == '3')
+     {
+       if (RD_HARPOON(ioport+hp_ee_ctrl) & BIT(7))
+	 pCardInfo->si_flags |= LOW_BYTE_TERM;
+     }
+   else if (pCardInfo->si_card_model[2] == '0')
+     {
+       temp = RD_HARPOON(ioport+hp_xfer_pad);
+       WR_HARPOON(ioport+hp_xfer_pad, (temp & ~BIT(4)));
+       if (RD_HARPOON(ioport+hp_ee_ctrl) & BIT(7))
+	 pCardInfo->si_flags |= LOW_BYTE_TERM;
+       WR_HARPOON(ioport+hp_xfer_pad, (temp | BIT(4)));
+       if (RD_HARPOON(ioport+hp_ee_ctrl) & BIT(7))
+	 pCardInfo->si_flags |= HIGH_BYTE_TERM;
+       WR_HARPOON(ioport+hp_xfer_pad, temp);
+     }
+   else
+     {
+       temp = RD_HARPOON(ioport+hp_ee_ctrl);
+       temp2 = RD_HARPOON(ioport+hp_xfer_pad);
+       WR_HARPOON(ioport+hp_ee_ctrl, (temp | SEE_CS));
+       WR_HARPOON(ioport+hp_xfer_pad, (temp2 | BIT(4)));
+       temp3 = 0;
+       for (i = 0; i < 8; i++)
+	 {
+	   temp3 <<= 1;
+	   if (!(RD_HARPOON(ioport+hp_ee_ctrl) & BIT(7)))
+	     temp3 |= 1;
+	   WR_HARPOON(ioport+hp_xfer_pad, (temp2 & ~BIT(4)));
+	   WR_HARPOON(ioport+hp_xfer_pad, (temp2 | BIT(4)));
+	 }
+       WR_HARPOON(ioport+hp_ee_ctrl, temp);
+       WR_HARPOON(ioport+hp_xfer_pad, temp2);
+       if (!(temp3 & BIT(7)))
+	 pCardInfo->si_flags |= LOW_BYTE_TERM;
+       if (!(temp3 & BIT(6)))
+	 pCardInfo->si_flags |= HIGH_BYTE_TERM;
+     }
+
+
+   ARAM_ACCESS(ioport);
+
+   for ( i = 0; i < 4; i++ ) {
+
+      pCardInfo->si_XlatInfo[i] =
+         RD_HARPOON(ioport+hp_aramBase+BIOS_DATA_OFFSET+i);
+      }
+
+	/* return with -1 if no sort, else return with
+	   logical card number sorted by BIOS (zero-based) */
+
+	pCardInfo->si_relative_cardnum =
+	(UCHAR)(RD_HARPOON(ioport+hp_aramBase+BIOS_RELATIVE_CARD)-1);
+
+   SGRAM_ACCESS(ioport);
+
+   s_PhaseTbl[0] = phaseDataOut;
+   s_PhaseTbl[1] = phaseDataIn;
+   s_PhaseTbl[2] = phaseIllegal;
+   s_PhaseTbl[3] = phaseIllegal;
+   s_PhaseTbl[4] = phaseCommand;
+   s_PhaseTbl[5] = phaseStatus;
+   s_PhaseTbl[6] = phaseMsgOut;
+   s_PhaseTbl[7] = phaseMsgIn;
+
+   pCardInfo->si_present = 0x01;
+
+#if defined(BUGBUG)
+
+
+   for (i = 0; i < MAX_CARDS; i++) {
+
+      for (id=0; id<debug_size; id++)
+         debug_int[i][id] =  (UCHAR)0x00;
+      debug_index[i] = 0;
+      }
+
+#endif
+
+   return(0);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_config_adapter
+ *
+ * Description: Setup adapter for normal operation (hard reset).
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+USHORT SccbMgr_config_adapter(PSCCBMGR_INFO pCardInfo)
+#else
+ULONG SccbMgr_config_adapter(PSCCBMGR_INFO pCardInfo)
+#endif
+{
+   PSCCBcard CurrCard = NULL;
+	PNVRamInfo pCurrNvRam;
+   UCHAR i,j,thisCard, ScamFlg;
+   USHORT temp,sync_bit_map,id;
+#if defined(DOS)
+   USHORT ioport;
+#else
+   ULONG ioport;
+#endif
+
+#if defined(DOS)
+   ioport = (USHORT)pCardInfo->si_baseaddr;
+#else
+   ioport = pCardInfo->si_baseaddr;
+#endif
+
+   for(thisCard =0; thisCard <= MAX_CARDS; thisCard++) {
+
+      if (thisCard == MAX_CARDS) {
+
+	 return(FAILURE);
+         }
+
+      if (BL_Card[thisCard].ioPort == ioport) {
+
+         CurrCard = &BL_Card[thisCard];
+         SccbMgrTableInitCard(CurrCard,thisCard);
+         break;
+         }
+
+      else if (BL_Card[thisCard].ioPort == 0x00) {
+
+         BL_Card[thisCard].ioPort = ioport;
+         CurrCard = &BL_Card[thisCard];
+
+			if(mbCards)
+				for(i = 0; i < mbCards; i++){
+					if(CurrCard->ioPort == nvRamInfo[i].niBaseAddr)
+						CurrCard->pNvRamInfo = &nvRamInfo[i];
+				}
+         SccbMgrTableInitCard(CurrCard,thisCard);
+         CurrCard->cardIndex = thisCard;
+         CurrCard->cardInfo = pCardInfo;
+
+	 break;
+         }
+      }
+
+	pCurrNvRam = CurrCard->pNvRamInfo;
+
+	if(pCurrNvRam){
+		ScamFlg = pCurrNvRam->niScamConf;
+	}
+	else{
+	   ScamFlg = (UCHAR) utilEERead(ioport, SCAM_CONFIG/2);
+	}
+
+
+   BusMasterInit(ioport);
+   XbowInit(ioport, ScamFlg);
+
+#if defined (NO_BIOS_OPTION)
+
+
+   if (DiagXbow(ioport)) return(FAILURE);
+   if (DiagBusMaster(ioport)) return(FAILURE);
+
+#endif  /* No BIOS Option */
+
+   autoLoadDefaultMap(ioport);
+
+
+   for (i = 0,id = 0x01; i != pCardInfo->si_id; i++,id <<= 1){}
+
+   WR_HARPOON(ioport+hp_selfid_0, id);
+   WR_HARPOON(ioport+hp_selfid_1, 0x00);
+   WR_HARPOON(ioport+hp_arb_id, pCardInfo->si_id);
+   CurrCard->ourId = pCardInfo->si_id;
+
+   i = (UCHAR) pCardInfo->si_flags;
+   if (i & SCSI_PARITY_ENA)
+       WR_HARPOON(ioport+hp_portctrl_1,(HOST_MODE8 | CHK_SCSI_P));
+
+   j = (RD_HARPOON(ioport+hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
+   if (i & LOW_BYTE_TERM)
+      j |= SCSI_TERM_ENA_L;
+   WR_HARPOON(ioport+hp_bm_ctrl, j);
+
+   j = (RD_HARPOON(ioport+hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
+   if (i & HIGH_BYTE_TERM)
+      j |= SCSI_TERM_ENA_H;
+   WR_HARPOON(ioport+hp_ee_ctrl, j );
+
+
+   if (!(pCardInfo->si_flags & SOFT_RESET)) {
+
+      sresb(ioport,thisCard);
+
+         scini(thisCard, pCardInfo->si_id, 0);
+      }
+
+
+
+   if (pCardInfo->si_flags & POST_ALL_UNDERRRUNS)
+      CurrCard->globalFlags |= F_NO_FILTER;
+
+	if(pCurrNvRam){
+		if(pCurrNvRam->niSysConf & 0x10)
+			CurrCard->globalFlags |= F_GREEN_PC;
+	}
+	else{
+	   if (utilEERead(ioport, (SYSTEM_CONFIG/2)) & GREEN_PC_ENA)
+   	   CurrCard->globalFlags |= F_GREEN_PC;
+	}
+
+	/* Set global flag to indicate Re-Negotiation to be done on all
+		ckeck condition */
+	if(pCurrNvRam){
+		if(pCurrNvRam->niScsiConf & 0x04)
+			CurrCard->globalFlags |= F_DO_RENEGO;
+	}
+	else{
+	   if (utilEERead(ioport, (SCSI_CONFIG/2)) & RENEGO_ENA)
+   	   CurrCard->globalFlags |= F_DO_RENEGO;
+	}
+
+	if(pCurrNvRam){
+		if(pCurrNvRam->niScsiConf & 0x08)
+			CurrCard->globalFlags |= F_CONLUN_IO;
+	}
+	else{
+	   if (utilEERead(ioport, (SCSI_CONFIG/2)) & CONNIO_ENA)
+   	   CurrCard->globalFlags |= F_CONLUN_IO;
+	}
+
+
+   temp = pCardInfo->si_per_targ_no_disc;
+
+   for (i = 0,id = 1; i < MAX_SCSI_TAR; i++, id <<= 1) {
+
+      if (temp & id)
+	 sccbMgrTbl[thisCard][i].TarStatus |= TAR_ALLOW_DISC;
+      }
+
+   sync_bit_map = 0x0001;
+
+   for (id = 0; id < (MAX_SCSI_TAR/2); id++) {
+
+		if(pCurrNvRam){
+			temp = (USHORT) pCurrNvRam->niSyncTbl[id];
+			temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
+					 (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
+		}else
+	      temp = utilEERead(ioport, (USHORT)((SYNC_RATE_TBL/2)+id));
+
+      for (i = 0; i < 2; temp >>=8,i++) {
+
+         if (pCardInfo->si_per_targ_init_sync & sync_bit_map) {
+
+            sccbMgrTbl[thisCard][id*2+i].TarEEValue = (UCHAR)temp;
+            }
+
+         else {
+	    sccbMgrTbl[thisCard][id*2+i].TarStatus |= SYNC_SUPPORTED;
+            sccbMgrTbl[thisCard][id*2+i].TarEEValue =
+               (UCHAR)(temp & ~EE_SYNC_MASK);
+            }
+
+#if defined(WIDE_SCSI)
+/*         if ((pCardInfo->si_per_targ_wide_nego & sync_bit_map) ||
+            (id*2+i >= 8)){
+*/
+         if (pCardInfo->si_per_targ_wide_nego & sync_bit_map){
+
+            sccbMgrTbl[thisCard][id*2+i].TarEEValue |= EE_WIDE_SCSI;
+
+            }
+
+         else { /* NARROW SCSI */
+            sccbMgrTbl[thisCard][id*2+i].TarStatus |= WIDE_NEGOCIATED;
+            }
+
+#else
+         sccbMgrTbl[thisCard][id*2+i].TarStatus |= WIDE_NEGOCIATED;
+#endif
+
+
+	 sync_bit_map <<= 1;
+
+
+
+         }
+      }
+
+   WR_HARPOON((ioport+hp_semaphore),
+      (UCHAR)(RD_HARPOON((ioport+hp_semaphore)) | SCCB_MGR_PRESENT));
+
+#if defined(DOS)
+   return((USHORT)CurrCard);
+#else
+   return((ULONG)CurrCard);
+#endif
+}
+
+#else  			/* end (FW_TYPE==_SCCB_MGR_)  */
+
+
+
+STATIC s16bits FP_PresenceCheck(PMGR_INFO pMgrInfo)
+{
+	PMGR_ENTRYPNTS	pMgr_EntryPnts = &pMgrInfo->mi_Functions;
+
+      pMgr_EntryPnts->UCBMgr_probe_adapter = probe_adapter;
+      pMgr_EntryPnts->UCBMgr_init_adapter = init_adapter;
+      pMgr_EntryPnts->UCBMgr_start_UCB = SccbMgr_start_sccb;
+      pMgr_EntryPnts->UCBMgr_build_UCB = build_UCB;
+      pMgr_EntryPnts->UCBMgr_abort_UCB = SccbMgr_abort_sccb;
+      pMgr_EntryPnts->UCBMgr_my_int = SccbMgr_my_int;
+      pMgr_EntryPnts->UCBMgr_isr = SccbMgr_isr;
+      pMgr_EntryPnts->UCBMgr_scsi_reset = SccbMgr_scsi_reset;
+      pMgr_EntryPnts->UCBMgr_timer_expired = SccbMgr_timer_expired;
+#ifndef NO_IOCTLS
+	  pMgr_EntryPnts->UCBMgr_unload_card = SccbMgr_unload_card;
+	  pMgr_EntryPnts->UCBMgr_save_foreign_state =
+	  									SccbMgr_save_foreign_state;
+	  pMgr_EntryPnts->UCBMgr_restore_foreign_state =
+	  									SccbMgr_restore_foreign_state;
+	  pMgr_EntryPnts->UCBMgr_restore_native_state =
+	  									SccbMgr_restore_native_state;
+#endif /*NO_IOCTLS*/
+
+      pMgrInfo->mi_SGListFormat=0x01;
+      pMgrInfo->mi_DataPtrFormat=0x01;
+      pMgrInfo->mi_MaxSGElements= (u16bits) 0xffffffff;
+      pMgrInfo->mi_MgrPrivateLen=sizeof(SCCB);
+      pMgrInfo->mi_PCIVendorID=BL_VENDOR_ID;
+      pMgrInfo->mi_PCIDeviceID=FP_DEVICE_ID;
+      pMgrInfo->mi_MgrAttributes= ATTR_IO_MAPPED +
+											 ATTR_PHYSICAL_ADDRESS +
+											 ATTR_VIRTUAL_ADDRESS +
+											 ATTR_OVERLAPPED_IO_IOCTLS_OK;
+      pMgrInfo->mi_IoRangeLen = 256;
+      return(0);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: probe_adapter
+ *
+ * Description: Setup and/or Search for cards and return info to caller.
+ *
+ *---------------------------------------------------------------------*/
+STATIC s32bits probe_adapter(PADAPTER_INFO pAdapterInfo)
+{
+   u16bits temp,temp2,temp3,temp4;
+   u08bits i,j,id;
+
+#if defined(DOS)
+#else
+   static u08bits first_time = 1;
+#endif
+   BASE_PORT ioport;
+	PNVRamInfo pCurrNvRam;
+
+   ioport = (BASE_PORT)pAdapterInfo->ai_baseaddr;
+
+
+
+   if (RD_HARPOON(ioport+hp_vendor_id_0) != ORION_VEND_0)
+      return(1);
+
+   if ((RD_HARPOON(ioport+hp_vendor_id_1) != ORION_VEND_1))
+      return(2);
+
+   if ((RD_HARPOON(ioport+hp_device_id_0) != ORION_DEV_0))
+      return(3);
+
+   if ((RD_HARPOON(ioport+hp_device_id_1) != ORION_DEV_1))
+      return(4);
+
+
+   if (RD_HARPOON(ioport+hp_rev_num) != 0x0f){
+
+
+/* For new Harpoon then check for sub_device ID LSB
+   the bits(0-3) must be all ZERO for compatible with
+   current version of SCCBMgr, else skip this Harpoon
+	device. */
+
+	   if (RD_HARPOON(ioport+hp_sub_device_id_0) & 0x0f)
+	      return(5);
+	}
+
+   if (first_time) {
+
+      SccbMgrTableInitAll();
+      first_time = 0;
+		mbCards = 0;
+      }
+
+	if(RdStack(ioport, 0) != 0x00) {
+		if(ChkIfChipInitialized(ioport) == FALSE)
+		{
+			pCurrNvRam = NULL;
+			XbowInit(ioport, 0);                /*Must Init the SCSI before attempting */
+			DiagEEPROM(ioport);
+		}
+		else
+		{
+			if(mbCards < MAX_MB_CARDS) {
+				pCurrNvRam = &nvRamInfo[mbCards];
+				mbCards++;
+				pCurrNvRam->niBaseAddr = ioport;
+				RNVRamData(pCurrNvRam);
+			}else
+				return((int) FAILURE);
+		}
+	}else
+		pCurrNvRam = NULL;
+
+#if defined (NO_BIOS_OPTION)
+	pCurrNvRam = NULL;
+   XbowInit(ioport, 0);                /*Must Init the SCSI before attempting */
+   DiagEEPROM(ioport);
+#endif  /* No BIOS Option */
+
+   WR_HARPOON(ioport+hp_clkctrl_0, CLKCTRL_DEFAULT);
+   WR_HARPOON(ioport+hp_sys_ctrl, 0x00);
+
+	if(pCurrNvRam)
+		pAdapterInfo->ai_id = pCurrNvRam->niAdapId;
+	else
+   	pAdapterInfo->ai_id = (u08bits)(utilEERead(ioport, (ADAPTER_SCSI_ID/2)) &
+      	(u08bits)0x0FF);
+
+   pAdapterInfo->ai_lun = 0x00;
+   pAdapterInfo->ai_fw_revision[0] = '3';
+   pAdapterInfo->ai_fw_revision[1] = '1';
+   pAdapterInfo->ai_fw_revision[2] = '1';
+   pAdapterInfo->ai_fw_revision[3] = ' ';
+   pAdapterInfo->ai_NumChannels = 1;
+
+   temp2 = 0x0000;
+   temp3 = 0x0000;
+   temp4 = 0x0000;
+
+   for (id = 0; id < (16/2); id++) {
+
+		if(pCurrNvRam){
+			temp = (USHORT) pCurrNvRam->niSyncTbl[id];
+			temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
+					 (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
+		}else
+	      temp = utilEERead(ioport, (u16bits)((SYNC_RATE_TBL/2)+id));
+
+      for (i = 0; i < 2; temp >>=8,i++) {
+
+         if ((temp & 0x03) != AUTO_RATE_00) {
+
+            temp2 >>= 0x01;
+            temp2 |= 0x8000;
+            }
+
+         else {
+            temp2 >>= 0x01;
+            }
+
+         if (temp & DISC_ENABLE_BIT) {
+
+            temp3 >>= 0x01;
+            temp3 |= 0x8000;
+            }
+
+         else {
+            temp3 >>= 0x01;
+            }
+
+         if (temp & WIDE_NEGO_BIT) {
+
+            temp4 >>= 0x01;
+            temp4 |= 0x8000;
+            }
+
+         else {
+            temp4 >>= 0x01;
+            }
+
+         }
+      }
+
+   pAdapterInfo->ai_per_targ_init_sync = temp2;
+   pAdapterInfo->ai_per_targ_no_disc = temp3;
+   pAdapterInfo->ai_per_targ_wide_nego = temp4;
+	if(pCurrNvRam)
+		i = pCurrNvRam->niSysConf;
+	else
+   	i = (u08bits)(utilEERead(ioport, (SYSTEM_CONFIG/2)));
+
+   /*
+   ** interrupts always level-triggered for FlashPoint
+   */
+   pAdapterInfo->ai_stateinfo |= LEVEL_TRIG;
+
+   if (i & 0x01)
+      pAdapterInfo->ai_stateinfo |= SCSI_PARITY_ENA;
+
+	if (i & 0x02)	/* SCSI Bus reset in AutoSCSI Set ? */
+	{
+		if(pCurrNvRam)
+		{
+			j = pCurrNvRam->niScamConf;
+		}
+		else
+		{
+		j = (u08bits) utilEERead(ioport, SCAM_CONFIG/2);
+		}
+		if(j & SCAM_ENABLED)
+		{
+			if(j & SCAM_LEVEL2)
+			{
+				pAdapterInfo->ai_stateinfo |= SCAM2_ENA;
+			}
+			else
+			{
+				pAdapterInfo->ai_stateinfo |= SCAM1_ENA;
+			}
+		}
+	}
+   j = (RD_HARPOON(ioport+hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
+   if (i & 0x04) {
+      j |= SCSI_TERM_ENA_L;
+      pAdapterInfo->ai_stateinfo |= LOW_BYTE_TERM_ENA;
+      }
+   WR_HARPOON(ioport+hp_bm_ctrl, j );
+
+   j = (RD_HARPOON(ioport+hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
+   if (i & 0x08) {
+      j |= SCSI_TERM_ENA_H;
+      pAdapterInfo->ai_stateinfo |= HIGH_BYTE_TERM_ENA;
+      }
+   WR_HARPOON(ioport+hp_ee_ctrl, j );
+
+	if(RD_HARPOON(ioport + hp_page_ctrl) & BIOS_SHADOW)
+	{
+		pAdapterInfo->ai_FlashRomSize = 64 * 1024;	/* 64k Rom */
+	}
+	else
+	{
+		pAdapterInfo->ai_FlashRomSize = 32 * 1024;	/* 32k Rom */
+	}
+
+   pAdapterInfo->ai_stateinfo |= (FAST20_ENA | TAG_QUEUE_ENA);
+   if (!(RD_HARPOON(ioport+hp_page_ctrl) & NARROW_SCSI_CARD))
+	{
+      pAdapterInfo->ai_attributes |= (WIDE_CAPABLE | FAST20_CAPABLE
+													| SCAM2_CAPABLE
+													| TAG_QUEUE_CAPABLE
+													| SUPRESS_UNDERRRUNS_CAPABLE
+													| SCSI_PARITY_CAPABLE);
+		pAdapterInfo->ai_MaxTarg = 16;
+		pAdapterInfo->ai_MaxLun  = 32;
+	}
+	else
+	{
+      pAdapterInfo->ai_attributes |= (FAST20_CAPABLE | SCAM2_CAPABLE
+													| TAG_QUEUE_CAPABLE
+													| SUPRESS_UNDERRRUNS_CAPABLE
+													| SCSI_PARITY_CAPABLE);
+		pAdapterInfo->ai_MaxTarg = 8;
+		pAdapterInfo->ai_MaxLun  = 8;
+	}
+
+   pAdapterInfo->ai_product_family = HARPOON_FAMILY;
+   pAdapterInfo->ai_HBAbustype = BUSTYPE_PCI;
+
+   for (i=0;i<CARD_MODEL_NAMELEN;i++)
+   {
+      pAdapterInfo->ai_card_model[i]=' '; /* initialize the ai_card_model */
+   }
+
+	if(pCurrNvRam){
+	pAdapterInfo->ai_card_model[0] = '9';
+		switch(pCurrNvRam->niModel & 0x0f){
+			case MODEL_LT:
+			pAdapterInfo->ai_card_model[1] = '3';
+			pAdapterInfo->ai_card_model[2] = '0';
+				break;
+			case MODEL_LW:
+			pAdapterInfo->ai_card_model[1] = '5';
+			pAdapterInfo->ai_card_model[2] = '0';
+				break;
+			case MODEL_DL:
+			pAdapterInfo->ai_card_model[1] = '3';
+			pAdapterInfo->ai_card_model[2] = '2';
+				break;
+			case MODEL_DW:
+			pAdapterInfo->ai_card_model[1] = '5';
+			pAdapterInfo->ai_card_model[2] = '2';
+				break;
+		}
+	}else{
+	   temp = utilEERead(ioport, (MODEL_NUMB_0/2));
+		pAdapterInfo->ai_card_model[0] = (u08bits)(temp >> 8);
+	   temp = utilEERead(ioport, (MODEL_NUMB_2/2));
+
+		pAdapterInfo->ai_card_model[1] = (u08bits)(temp & 0x00FF);
+	   pAdapterInfo->ai_card_model[2] = (u08bits)(temp >> 8);
+	}
+
+
+
+   pAdapterInfo->ai_FiberProductType = 0;
+
+   pAdapterInfo->ai_secondary_range = 0;
+
+   for (i=0;i<WORLD_WIDE_NAMELEN;i++)
+   {
+      pAdapterInfo->ai_worldwidename[i]='\0';
+   }
+
+   for (i=0;i<VENDOR_NAMELEN;i++)
+   {
+      pAdapterInfo->ai_vendorstring[i]='\0';
+   }
+   	pAdapterInfo->ai_vendorstring[0]='B';
+   	pAdapterInfo->ai_vendorstring[1]='U';
+   	pAdapterInfo->ai_vendorstring[2]='S';
+   	pAdapterInfo->ai_vendorstring[3]='L';
+   	pAdapterInfo->ai_vendorstring[4]='O';
+   	pAdapterInfo->ai_vendorstring[5]='G';
+   	pAdapterInfo->ai_vendorstring[6]='I';
+   	pAdapterInfo->ai_vendorstring[7]='C';
+
+	for (i=0;i<FAMILY_NAMELEN;i++)
+	{
+	   pAdapterInfo->ai_AdapterFamilyString[i]='\0';
+	}
+   	pAdapterInfo->ai_AdapterFamilyString[0]='F';
+   	pAdapterInfo->ai_AdapterFamilyString[1]='L';
+   	pAdapterInfo->ai_AdapterFamilyString[2]='A';
+   	pAdapterInfo->ai_AdapterFamilyString[3]='S';
+   	pAdapterInfo->ai_AdapterFamilyString[4]='H';
+   	pAdapterInfo->ai_AdapterFamilyString[5]='P';
+   	pAdapterInfo->ai_AdapterFamilyString[6]='O';
+   	pAdapterInfo->ai_AdapterFamilyString[7]='I';
+   	pAdapterInfo->ai_AdapterFamilyString[8]='N';
+   	pAdapterInfo->ai_AdapterFamilyString[9]='T';
+
+   ARAM_ACCESS(ioport);
+
+   for ( i = 0; i < 4; i++ ) {
+
+      pAdapterInfo->ai_XlatInfo[i] =
+         RD_HARPOON(ioport+hp_aramBase+BIOS_DATA_OFFSET+i);
+      }
+
+	/* return with -1 if no sort, else return with
+	   logical card number sorted by BIOS (zero-based) */
+
+
+	pAdapterInfo->ai_relative_cardnum = 
+      (u08bits)(RD_HARPOON(ioport+hp_aramBase+BIOS_RELATIVE_CARD)-1); 
+
+   SGRAM_ACCESS(ioport);
+
+   s_PhaseTbl[0] = phaseDataOut;
+   s_PhaseTbl[1] = phaseDataIn;
+   s_PhaseTbl[2] = phaseIllegal;
+   s_PhaseTbl[3] = phaseIllegal;
+   s_PhaseTbl[4] = phaseCommand;
+   s_PhaseTbl[5] = phaseStatus;
+   s_PhaseTbl[6] = phaseMsgOut;
+   s_PhaseTbl[7] = phaseMsgIn;
+
+   pAdapterInfo->ai_present = 0x01;
+
+#if defined(BUGBUG)
+
+
+   for (i = 0; i < MAX_CARDS; i++) {
+
+      for (id=0; id<debug_size; id++)
+         debug_int[i][id] =  (u08bits)0x00;
+      debug_index[i] = 0;
+      }
+
+#endif
+
+   return(0);
+}
+
+
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: init_adapter, exported to BUDI via UCBMgr_init_adapter entry
+ *
+ *
+ * Description: Setup adapter for normal operation (hard reset).
+ *
+ *---------------------------------------------------------------------*/
+STATIC CARD_HANDLE init_adapter(PADAPTER_INFO pCardInfo)
+{
+   PSCCBcard CurrCard;
+	PNVRamInfo pCurrNvRam;
+   u08bits i,j,thisCard, ScamFlg;
+   u16bits temp,sync_bit_map,id;
+   BASE_PORT ioport;
+
+   ioport = (BASE_PORT)pCardInfo->ai_baseaddr;
+
+   for(thisCard =0; thisCard <= MAX_CARDS; thisCard++) {
+
+      if (thisCard == MAX_CARDS) {
+
+         return(FAILURE);
+         }
+
+      if (BL_Card[thisCard].ioPort == ioport) {
+
+         CurrCard = &BL_Card[thisCard];
+         SccbMgrTableInitCard(CurrCard,thisCard);
+         break;
+         }
+
+      else if (BL_Card[thisCard].ioPort == 0x00) {
+
+         BL_Card[thisCard].ioPort = ioport;
+         CurrCard = &BL_Card[thisCard];
+
+			if(mbCards)
+				for(i = 0; i < mbCards; i++){
+					if(CurrCard->ioPort == nvRamInfo[i].niBaseAddr)
+						CurrCard->pNvRamInfo = &nvRamInfo[i];
+				}
+         SccbMgrTableInitCard(CurrCard,thisCard);
+         CurrCard->cardIndex = thisCard;
+         CurrCard->cardInfo = pCardInfo;
+
+         break;
+         }
+      }
+
+	pCurrNvRam = CurrCard->pNvRamInfo;
+
+   
+	if(pCurrNvRam){
+		ScamFlg = pCurrNvRam->niScamConf;
+	}
+	else{
+	   ScamFlg = (UCHAR) utilEERead(ioport, SCAM_CONFIG/2);
+	}
+	
+
+   BusMasterInit(ioport);
+   XbowInit(ioport, ScamFlg);
+
+#if defined (NO_BIOS_OPTION)
+
+
+   if (DiagXbow(ioport)) return(FAILURE);
+   if (DiagBusMaster(ioport)) return(FAILURE);
+
+#endif  /* No BIOS Option */
+
+   autoLoadDefaultMap(ioport);
+
+
+   for (i = 0,id = 0x01; i != pCardInfo->ai_id; i++,id <<= 1){}
+
+   WR_HARPOON(ioport+hp_selfid_0, id);
+   WR_HARPOON(ioport+hp_selfid_1, 0x00);
+   WR_HARPOON(ioport+hp_arb_id, pCardInfo->ai_id);
+   CurrCard->ourId = (unsigned char) pCardInfo->ai_id;
+
+   i = (u08bits) pCardInfo->ai_stateinfo;
+   if (i & SCSI_PARITY_ENA)
+       WR_HARPOON(ioport+hp_portctrl_1,(HOST_MODE8 | CHK_SCSI_P));
+
+   j = (RD_HARPOON(ioport+hp_bm_ctrl) & ~SCSI_TERM_ENA_L);
+   if (i & LOW_BYTE_TERM_ENA)
+      j |= SCSI_TERM_ENA_L;
+   WR_HARPOON(ioport+hp_bm_ctrl, j);
+
+   j = (RD_HARPOON(ioport+hp_ee_ctrl) & ~SCSI_TERM_ENA_H);
+   if (i & HIGH_BYTE_TERM_ENA)
+      j |= SCSI_TERM_ENA_H;
+   WR_HARPOON(ioport+hp_ee_ctrl, j );
+
+
+   if (!(pCardInfo->ai_stateinfo & NO_RESET_IN_INIT)) {
+
+      sresb(ioport,thisCard);
+
+         scini(thisCard, (u08bits) pCardInfo->ai_id, 0);
+      }
+
+
+
+   if (pCardInfo->ai_stateinfo & SUPRESS_UNDERRRUNS_ENA)
+      CurrCard->globalFlags |= F_NO_FILTER;
+
+	if(pCurrNvRam){
+		if(pCurrNvRam->niSysConf & 0x10)
+			CurrCard->globalFlags |= F_GREEN_PC;
+	}
+	else{
+	   if (utilEERead(ioport, (SYSTEM_CONFIG/2)) & GREEN_PC_ENA)
+   	   CurrCard->globalFlags |= F_GREEN_PC;
+	}
+
+	/* Set global flag to indicate Re-Negotiation to be done on all
+		ckeck condition */
+	if(pCurrNvRam){
+		if(pCurrNvRam->niScsiConf & 0x04)
+			CurrCard->globalFlags |= F_DO_RENEGO;
+	}
+	else{
+	   if (utilEERead(ioport, (SCSI_CONFIG/2)) & RENEGO_ENA)
+   	   CurrCard->globalFlags |= F_DO_RENEGO;
+	}
+
+	if(pCurrNvRam){
+		if(pCurrNvRam->niScsiConf & 0x08)
+			CurrCard->globalFlags |= F_CONLUN_IO;
+	}
+	else{
+	   if (utilEERead(ioport, (SCSI_CONFIG/2)) & CONNIO_ENA)
+   	   CurrCard->globalFlags |= F_CONLUN_IO;
+	}
+
+   temp = pCardInfo->ai_per_targ_no_disc;
+
+   for (i = 0,id = 1; i < MAX_SCSI_TAR; i++, id <<= 1) {
+
+      if (temp & id)
+         sccbMgrTbl[thisCard][i].TarStatus |= TAR_ALLOW_DISC;
+      }
+
+   sync_bit_map = 0x0001;
+
+   for (id = 0; id < (MAX_SCSI_TAR/2); id++){
+
+		if(pCurrNvRam){
+			temp = (USHORT) pCurrNvRam->niSyncTbl[id];
+			temp = ((temp & 0x03) + ((temp << 4) & 0xc0)) +
+					 (((temp << 4) & 0x0300) + ((temp << 8) & 0xc000));
+		}else
+	      temp = utilEERead(ioport, (u16bits)((SYNC_RATE_TBL/2)+id));
+
+      for (i = 0; i < 2; temp >>=8,i++){
+
+         if (pCardInfo->ai_per_targ_init_sync & sync_bit_map){
+
+            sccbMgrTbl[thisCard][id*2+i].TarEEValue = (u08bits)temp;
+            }
+
+         else {
+            sccbMgrTbl[thisCard][id*2+i].TarStatus |= SYNC_SUPPORTED;
+            sccbMgrTbl[thisCard][id*2+i].TarEEValue =
+               (u08bits)(temp & ~EE_SYNC_MASK);
+            }
+
+#if defined(WIDE_SCSI)
+/*         if ((pCardInfo->ai_per_targ_wide_nego & sync_bit_map) ||
+            (id*2+i >= 8)){
+*/
+         if (pCardInfo->ai_per_targ_wide_nego & sync_bit_map){
+
+            sccbMgrTbl[thisCard][id*2+i].TarEEValue |= EE_WIDE_SCSI;
+
+            }
+
+         else { /* NARROW SCSI */
+            sccbMgrTbl[thisCard][id*2+i].TarStatus |= WIDE_NEGOCIATED;
+            }
+
+#else
+         sccbMgrTbl[thisCard][id*2+i].TarStatus |= WIDE_NEGOCIATED;
+#endif
+
+
+         sync_bit_map <<= 1;
+         }
+      }
+
+
+   pCardInfo->ai_SGListFormat=0x01;
+   pCardInfo->ai_DataPtrFormat=0x01;
+   pCardInfo->ai_AEN_mask &= SCSI_RESET_COMPLETE;
+
+   WR_HARPOON((ioport+hp_semaphore),
+      (u08bits)(RD_HARPOON((ioport+hp_semaphore)) | SCCB_MGR_PRESENT));
+
+   return((u32bits)CurrCard);
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: build_ucb, exported to BUDI via UCBMgr_build_ucb entry
+ *
+ * Description: prepare fw portion of ucb. do not start, resource not guaranteed
+ *             so don't manipulate anything that's derived from states which
+ *             may change
+ *
+ *---------------------------------------------------------------------*/
+void build_UCB(CARD_HANDLE pCurrCard, PUCB p_ucb)
+{
+
+   u08bits thisCard;
+   u08bits i,j;
+
+   PSCCB p_sccb;
+
+
+   thisCard = ((PSCCBcard) pCurrCard)->cardIndex;
+
+
+   p_sccb=(PSCCB)p_ucb->UCB_MgrPrivatePtr;
+
+
+   p_sccb->Sccb_ucb_ptr=p_ucb;
+
+   switch (p_ucb->UCB_opcode & (OPC_DEVICE_RESET+OPC_XFER_SG+OPC_CHK_RESIDUAL))
+   {
+      case OPC_DEVICE_RESET:
+         p_sccb->OperationCode=RESET_COMMAND;
+         break;
+      case OPC_XFER_SG:
+         p_sccb->OperationCode=SCATTER_GATHER_COMMAND;
+         break;
+      case OPC_XFER_SG+OPC_CHK_RESIDUAL:
+         p_sccb->OperationCode=RESIDUAL_SG_COMMAND;
+         break;
+      case OPC_CHK_RESIDUAL:
+
+	      p_sccb->OperationCode=RESIDUAL_COMMAND;
+	      break;
+      default:
+	      p_sccb->OperationCode=SCSI_INITIATOR_COMMAND;
+	      break;
+   }
+
+   if (p_ucb->UCB_opcode & OPC_TQ_ENABLE)
+   {
+      p_sccb->ControlByte = (u08bits)((p_ucb->UCB_opcode & OPC_TQ_MASK)>>2) | F_USE_CMD_Q;
+   }
+   else
+   {
+      p_sccb->ControlByte = 0;
+   }
+
+
+   p_sccb->CdbLength = (u08bits)p_ucb->UCB_cdblen;
+
+   if (p_ucb->UCB_opcode & OPC_NO_AUTO_SENSE)
+   {
+      p_sccb->RequestSenseLength = 0;
+   }
+   else
+   {
+      p_sccb->RequestSenseLength = (unsigned char) p_ucb->UCB_senselen;
+   }
+
+
+   if (p_ucb->UCB_opcode & OPC_XFER_SG)
+   {
+      p_sccb->DataPointer=p_ucb->UCB_virt_dataptr;
+      p_sccb->DataLength = (((u32bits)p_ucb->UCB_NumSgElements)<<3);
+   }
+   else
+   {
+      p_sccb->DataPointer=p_ucb->UCB_phys_dataptr;
+      p_sccb->DataLength=p_ucb->UCB_datalen;
+   };
+
+   p_sccb->HostStatus=0;
+   p_sccb->TargetStatus=0;
+   p_sccb->TargID=(unsigned char)p_ucb->UCB_targid;
+   p_sccb->Lun=(unsigned char) p_ucb->UCB_lun;
+   p_sccb->SccbIOPort=((PSCCBcard)pCurrCard)->ioPort;
+
+   j=p_ucb->UCB_cdblen;
+   for (i=0;i<j;i++)
+   {
+      p_sccb->Cdb[i] = p_ucb->UCB_cdb[i];
+   }
+
+   p_sccb->SensePointer=p_ucb->UCB_phys_senseptr;
+
+   sinits(p_sccb,thisCard);
+
+}
+#ifndef NO_IOCTLS
+
+/*---------------------------------------------------------------------
+ *
+ * Function: GetDevSyncRate
+ *
+ *---------------------------------------------------------------------*/
+STATIC  int GetDevSyncRate(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+	struct _SYNC_RATE_INFO * pSyncStr;
+   PSCCBMgr_tar_info currTar_Info;
+	BASE_PORT ioport;
+	u08bits scsiID, j;
+
+#if (FW_TYPE != _SCCB_MGR_)
+	if( p_ucb->UCB_targid >= pCurrCard->cardInfo->ai_MaxTarg )
+	{
+		return(1);
+	}
+#endif
+
+	ioport  = pCurrCard->ioPort;
+	pSyncStr	= (struct _SYNC_RATE_INFO *) p_ucb->UCB_virt_dataptr;
+	scsiID = (u08bits) p_ucb->UCB_targid;
+   currTar_Info = &sccbMgrTbl[pCurrCard->cardIndex][scsiID];
+	j = currTar_Info->TarSyncCtrl;
+
+	switch (currTar_Info->TarEEValue & EE_SYNC_MASK)
+	{
+		case EE_SYNC_ASYNC:
+			pSyncStr->RequestMegaXferRate = 0x00;
+			break;
+		case EE_SYNC_5MB:
+			pSyncStr->RequestMegaXferRate = (j & NARROW_SCSI) ? 50 : 100;
+			break;
+		case EE_SYNC_10MB:
+			pSyncStr->RequestMegaXferRate = (j & NARROW_SCSI) ? 100 : 200;
+			break;
+		case EE_SYNC_20MB:
+			pSyncStr->RequestMegaXferRate = (j & NARROW_SCSI) ? 200 : 400;
+			break;
+	}
+
+	switch ((j >> 5) & 0x07)
+	{
+		case 0x00:
+			if((j & 0x07) == 0x00)
+			{
+				pSyncStr->ActualMegaXferRate = 0x00;	/* Async Mode */
+			}
+			else
+			{
+				pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 200 : 400;
+			}
+			break;
+		case 0x01:
+			pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 100 : 200;
+			break;
+		case 0x02:
+			pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 66 : 122;
+			break;
+		case 0x03:
+			pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 50 : 100;
+			break;
+		case 0x04:
+			pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 40 : 80;
+			break;
+		case 0x05:
+			pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 33 : 66;
+			break;
+		case 0x06:
+			pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 28 : 56;
+			break;
+		case 0x07:
+			pSyncStr->ActualMegaXferRate = (j & NARROW_SCSI) ? 25 : 50;
+			break;
+	}
+	pSyncStr->NegotiatedOffset = j & 0x0f;
+
+	return(0);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SetDevSyncRate
+ *
+ *---------------------------------------------------------------------*/
+STATIC int SetDevSyncRate(PSCCBcard pCurrCard, PUCB p_ucb)
+{
+	struct _SYNC_RATE_INFO * pSyncStr;
+   PSCCBMgr_tar_info currTar_Info;
+	BASE_PORT ioPort;
+	u08bits scsiID, i, j, syncVal;
+	u16bits syncOffset, actualXferRate;
+	union {
+		u08bits tempb[2];
+		u16bits tempw;
+	}temp2;
+
+#if (FW_TYPE != _SCCB_MGR_)
+	if( p_ucb->UCB_targid >= pCurrCard->cardInfo->ai_MaxTarg )
+	{
+		return(1);
+	}
+#endif
+
+	ioPort  = pCurrCard->ioPort;
+	pSyncStr	= (struct _SYNC_RATE_INFO *) p_ucb->UCB_virt_dataptr;
+	scsiID = (u08bits) p_ucb->UCB_targid;
+   currTar_Info = &sccbMgrTbl[pCurrCard->cardIndex][scsiID];
+	i = RD_HARPOON(ioPort+hp_xfer_pad);		/* Save current value */
+	WR_HARPOON(ioPort+hp_xfer_pad, (i | ID_UNLOCK));
+	WR_HARPOON(ioPort+hp_select_id, ((scsiID << 4) | scsiID));
+	j = RD_HARPOON(ioPort+hp_synctarg_0);
+	WR_HARPOON(ioPort+hp_xfer_pad, i);		/* restore value */
+
+	actualXferRate = pSyncStr->ActualMegaXferRate;
+	if(!(j & NARROW_SCSI))
+	{
+		actualXferRate <<= 1;
+	}
+	if(actualXferRate == 0x00)
+	{
+		syncVal = EE_SYNC_ASYNC;			/* Async Mode */
+	}
+	if(actualXferRate == 0x0200)
+	{
+		syncVal = EE_SYNC_20MB;				/* 20/40 MB Mode */
+	}
+	if(actualXferRate > 0x0050 && actualXferRate < 0x0200 )
+	{
+		syncVal = EE_SYNC_10MB;				/* 10/20 MB Mode */
+	}
+	else
+	{
+		syncVal = EE_SYNC_5MB;				/* 5/10 MB Mode */
+	}
+	if(currTar_Info->TarEEValue && EE_SYNC_MASK == syncVal)
+		return(0);
+	currTar_Info->TarEEValue = (currTar_Info->TarEEValue & !EE_SYNC_MASK)
+											| syncVal;
+	syncOffset = (SYNC_RATE_TBL + scsiID) / 2;
+	temp2.tempw = utilEERead(ioPort, syncOffset);
+	if(scsiID & 0x01)
+	{
+		temp2.tempb[0] = (temp2.tempb[0] & !EE_SYNC_MASK) | syncVal;
+	}
+	else
+	{
+		temp2.tempb[1] = (temp2.tempb[1] & !EE_SYNC_MASK) | syncVal;
+	}
+	utilEEWriteOnOff(ioPort, 1);
+	utilEEWrite(ioPort, temp2.tempw, syncOffset);
+	utilEEWriteOnOff(ioPort, 0);
+	UpdateCheckSum(ioPort);
+
+	return(0);
+}
+/*---------------------------------------------------------------------
+ *
+ * Function: GetDevWideMode
+ *
+ *---------------------------------------------------------------------*/
+int GetDevWideMode(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+	u08bits *pData;
+
+	pData = (u08bits *)p_ucb->UCB_virt_dataptr;
+	if(sccbMgrTbl[pCurrCard->cardIndex][p_ucb->UCB_targid].TarEEValue
+				& EE_WIDE_SCSI)
+	{
+		*pData = 1;
+	}
+	else
+	{
+		*pData = 0;
+	}
+
+	return(0);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SetDevWideMode
+ *
+ *---------------------------------------------------------------------*/
+int SetDevWideMode(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+	u08bits *pData;
+   PSCCBMgr_tar_info currTar_Info;
+	BASE_PORT ioPort;
+	u08bits scsiID, scsiWideMode;
+	u16bits syncOffset;
+	union {
+		u08bits tempb[2];
+		u16bits tempw;
+	}temp2;
+
+#if (FW_TYPE != _SCCB_MGR_)
+	if( !(pCurrCard->cardInfo->ai_attributes & WIDE_CAPABLE) )
+	{
+		return(1);
+	}
+
+	if( p_ucb->UCB_targid >= pCurrCard->cardInfo->ai_MaxTarg )
+	{
+		return(1);
+	}
+#endif
+
+	ioPort  = pCurrCard->ioPort;
+	pData = (u08bits *)p_ucb->UCB_virt_dataptr;
+	scsiID = (u08bits) p_ucb->UCB_targid;
+	currTar_Info = &sccbMgrTbl[pCurrCard->cardIndex][scsiID];
+
+	if(*pData)
+	{
+		if(currTar_Info->TarEEValue & EE_WIDE_SCSI)
+		{
+			return(0);
+		}
+		else
+		{
+			scsiWideMode = EE_WIDE_SCSI;
+		}
+	}
+	else
+	{
+		if(!currTar_Info->TarEEValue & EE_WIDE_SCSI)
+		{
+			return(0);
+		}
+		else
+		{
+			scsiWideMode = 0;
+		}
+	}
+	currTar_Info->TarEEValue = (currTar_Info->TarEEValue & !EE_WIDE_SCSI)
+											| scsiWideMode;
+
+	syncOffset = (SYNC_RATE_TBL + scsiID) / 2;
+	temp2.tempw = utilEERead(ioPort, syncOffset);
+	if(scsiID & 0x01)
+	{
+		temp2.tempb[0] = (temp2.tempb[0] & !EE_WIDE_SCSI) | scsiWideMode;
+	}
+	else
+	{
+		temp2.tempb[1] = (temp2.tempb[1] & !EE_WIDE_SCSI) | scsiWideMode;
+	}
+	utilEEWriteOnOff(ioPort, 1);
+	utilEEWrite(ioPort, temp2.tempw, syncOffset);
+	utilEEWriteOnOff(ioPort, 0);
+	UpdateCheckSum(ioPort);
+
+	return(0);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: ReadNVRam
+ *
+ *---------------------------------------------------------------------*/
+void ReadNVRam(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+	u08bits *pdata;
+	u16bits i,numwrds,numbytes,offset,temp;
+	u08bits OneMore = FALSE;
+#if defined(DOS)
+	u16bits ioport;
+#else
+	u32bits ioport;
+#endif
+
+	numbytes = (u16bits) p_ucb->UCB_datalen;
+	ioport  = pCurrCard->ioPort;
+   pdata   = (u08bits *) p_ucb->UCB_virt_dataptr;
+	offset  = (u16bits) (p_ucb->UCB_IOCTLParams[0]);
+
+
+
+   if (offset & 0x1)
+	{
+	    *((u16bits*) pdata) = utilEERead(ioport,(u16bits)((offset - 1) / 2)); /* 16 bit read */
+		 *pdata = *(pdata + 1);
+		 ++offset;
+   	 ++pdata;
+		 --numbytes;
+	}
+
+	numwrds = numbytes / 2;
+	if (numbytes & 1)
+	 	OneMore = TRUE;
+
+	for (i = 0; i < numwrds; i++)
+	{
+   	*((u16bits*) pdata) = utilEERead(ioport,(u16bits)(offset / 2));
+		pdata += 2;
+		offset += 2;
+   }
+	if (OneMore)
+	{
+		--pdata;
+		-- offset;
+   	temp = utilEERead(ioport,(u16bits)(offset / 2));
+		*pdata = (u08bits) (temp);
+	}
+
+} /* end proc ReadNVRam */
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: WriteNVRam
+ *
+ *---------------------------------------------------------------------*/
+void WriteNVRam(PSCCBcard pCurrCard,PUCB p_ucb)
+{
+	u08bits *pdata;
+	u16bits i,numwrds,numbytes,offset, eeprom_end;
+	u08bits OneMore = FALSE;
+	union {
+		u08bits  tempb[2];
+		u16bits  tempw;
+	} temp2;
+
+#if defined(DOS)
+	u16bits ioport;
+#else
+	u32bits ioport;
+#endif
+
+	numbytes = (u16bits) p_ucb->UCB_datalen;
+	ioport  = pCurrCard->ioPort;
+   pdata   = (u08bits *) p_ucb->UCB_virt_dataptr;
+	offset  = (u16bits) (p_ucb->UCB_IOCTLParams[0]);
+
+   if (RD_HARPOON(ioport+hp_page_ctrl) & NARROW_SCSI_CARD)
+      eeprom_end = 512;
+   else
+      eeprom_end = 768;
+	
+	if(offset > eeprom_end)
+		return;
+
+	if((offset + numbytes) > eeprom_end)
+		numbytes = eeprom_end - offset;
+
+    utilEEWriteOnOff(ioport,1);   /* Enable write access to the EEPROM */
+
+
+
+   if (offset & 0x1)
+	{
+	    temp2.tempw = utilEERead(ioport,(u16bits)((offset - 1) / 2)); /* 16 bit read */
+		 temp2.tempb[1] = *pdata;
+	    utilEEWrite(ioport, temp2.tempw, (u16bits)((offset -1) / 2));
+		 *pdata = *(pdata + 1);
+		 ++offset;
+   	 ++pdata;
+		 --numbytes;
+	}
+
+	numwrds = numbytes / 2;
+	if (numbytes & 1)
+	 	OneMore = TRUE;
+
+	for (i = 0; i < numwrds; i++)
+	{
+   	utilEEWrite(ioport, *((pu16bits)pdata),(u16bits)(offset / 2));
+		pdata += 2;
+		offset += 2;
+   }
+	if (OneMore)
+	{
+
+   	temp2.tempw = utilEERead(ioport,(u16bits)(offset / 2));
+		temp2.tempb[0] = *pdata;
+   	utilEEWrite(ioport, temp2.tempw, (u16bits)(offset / 2));
+	}
+   utilEEWriteOnOff(ioport,0);   /* Turn off write access */
+   UpdateCheckSum((u32bits)ioport);
+
+} /* end proc WriteNVRam */
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: UpdateCheckSum
+ *
+ * Description: Update Check Sum in EEPROM
+ *
+ *---------------------------------------------------------------------*/
+
+
+void UpdateCheckSum(u32bits baseport)
+{
+	USHORT i,sum_data, eeprom_end;
+
+	sum_data = 0x0000;
+
+
+   if (RD_HARPOON(baseport+hp_page_ctrl) & NARROW_SCSI_CARD)
+      eeprom_end = 512;
+   else
+      eeprom_end = 768;
+
+	for (i = 1; i < eeprom_end/2; i++)
+	{
+		sum_data += utilEERead(baseport, i);
+	}
+
+   utilEEWriteOnOff(baseport,1);   /* Enable write access to the EEPROM */
+
+   utilEEWrite(baseport, sum_data, EEPROM_CHECK_SUM/2);
+   utilEEWriteOnOff(baseport,0);   /* Turn off write access */
+}
+
+void SccbMgr_save_foreign_state(PADAPTER_INFO pAdapterInfo)
+{
+}
+
+
+void SccbMgr_restore_foreign_state(CARD_HANDLE pCurrCard)
+{
+}
+
+void SccbMgr_restore_native_state(CARD_HANDLE pCurrCard)
+{
+}
+
+#endif /* NO_IOCTLS */
+
+#endif /* (FW_TYPE==_UCB_MGR_)   */
+
+#ifndef NO_IOCTLS
+#if (FW_TYPE==_UCB_MGR_)
+void SccbMgr_unload_card(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+void SccbMgr_unload_card(USHORT pCurrCard)
+#else
+void SccbMgr_unload_card(ULONG pCurrCard)
+#endif
+#endif
+{
+	UCHAR i;
+#if defined(DOS)
+	USHORT portBase;
+	USHORT regOffset;
+#else
+	ULONG portBase;
+	ULONG regOffset;
+#endif
+	ULONG scamData;
+#if defined(OS2)
+	ULONG far *pScamTbl;
+#else
+	ULONG *pScamTbl;
+#endif
+	PNVRamInfo pCurrNvRam;
+
+	pCurrNvRam = ((PSCCBcard)pCurrCard)->pNvRamInfo;
+
+	if(pCurrNvRam){
+		WrStack(pCurrNvRam->niBaseAddr, 0, pCurrNvRam->niModel);
+		WrStack(pCurrNvRam->niBaseAddr, 1, pCurrNvRam->niSysConf);
+		WrStack(pCurrNvRam->niBaseAddr, 2, pCurrNvRam->niScsiConf);
+		WrStack(pCurrNvRam->niBaseAddr, 3, pCurrNvRam->niScamConf);
+		WrStack(pCurrNvRam->niBaseAddr, 4, pCurrNvRam->niAdapId);
+
+		for(i = 0; i < MAX_SCSI_TAR / 2; i++)
+			WrStack(pCurrNvRam->niBaseAddr, (UCHAR)(i+5), pCurrNvRam->niSyncTbl[i]);
+
+		portBase = pCurrNvRam->niBaseAddr;
+
+		for(i = 0; i < MAX_SCSI_TAR; i++){
+			regOffset = hp_aramBase + 64 + i*4;
+#if defined(OS2)
+			pScamTbl = (ULONG far *) &pCurrNvRam->niScamTbl[i];
+#else
+			pScamTbl = (ULONG *) &pCurrNvRam->niScamTbl[i];
+#endif
+			scamData = *pScamTbl;
+			WR_HARP32(portBase, regOffset, scamData);
+		}
+
+	}else{
+		WrStack(((PSCCBcard)pCurrCard)->ioPort, 0, 0);
+	}
+}
+#endif /* NO_IOCTLS */
+
+
+void RNVRamData(PNVRamInfo pNvRamInfo)
+{
+	UCHAR i;
+#if defined(DOS)
+	USHORT portBase;
+	USHORT regOffset;
+#else
+	ULONG portBase;
+	ULONG regOffset;
+#endif
+	ULONG scamData;
+#if defined (OS2)
+	ULONG far *pScamTbl;
+#else
+	ULONG *pScamTbl;
+#endif
+
+	pNvRamInfo->niModel    = RdStack(pNvRamInfo->niBaseAddr, 0);
+	pNvRamInfo->niSysConf  = RdStack(pNvRamInfo->niBaseAddr, 1);
+	pNvRamInfo->niScsiConf = RdStack(pNvRamInfo->niBaseAddr, 2);
+	pNvRamInfo->niScamConf = RdStack(pNvRamInfo->niBaseAddr, 3);
+	pNvRamInfo->niAdapId   = RdStack(pNvRamInfo->niBaseAddr, 4);
+
+	for(i = 0; i < MAX_SCSI_TAR / 2; i++)
+		pNvRamInfo->niSyncTbl[i] = RdStack(pNvRamInfo->niBaseAddr, (UCHAR)(i+5));
+
+	portBase = pNvRamInfo->niBaseAddr;
+
+	for(i = 0; i < MAX_SCSI_TAR; i++){
+		regOffset = hp_aramBase + 64 + i*4;
+		RD_HARP32(portBase, regOffset, scamData);
+#if defined(OS2)
+		pScamTbl = (ULONG far *) &pNvRamInfo->niScamTbl[i];
+#else
+		pScamTbl = (ULONG *) &pNvRamInfo->niScamTbl[i];
+#endif
+		*pScamTbl = scamData;
+	}
+
+}
+
+#if defined(DOS)
+UCHAR RdStack(USHORT portBase, UCHAR index)
+#else
+UCHAR RdStack(ULONG portBase, UCHAR index)
+#endif
+{
+	WR_HARPOON(portBase + hp_stack_addr, index);
+	return(RD_HARPOON(portBase + hp_stack_data));
+}
+
+#if defined(DOS)
+void WrStack(USHORT portBase, UCHAR index, UCHAR data)
+#else
+void WrStack(ULONG portBase, UCHAR index, UCHAR data)
+#endif
+{
+	WR_HARPOON(portBase + hp_stack_addr, index);
+	WR_HARPOON(portBase + hp_stack_data, data);
+}
+
+
+#if (FW_TYPE==_UCB_MGR_)
+u08bits ChkIfChipInitialized(BASE_PORT ioPort)
+#else
+#if defined(DOS)
+UCHAR ChkIfChipInitialized(USHORT ioPort)
+#else
+UCHAR ChkIfChipInitialized(ULONG ioPort)
+#endif
+#endif
+{
+	if((RD_HARPOON(ioPort + hp_arb_id) & 0x0f) != RdStack(ioPort, 4))
+		return(FALSE);
+	if((RD_HARPOON(ioPort + hp_clkctrl_0) & CLKCTRL_DEFAULT)
+								!= CLKCTRL_DEFAULT)
+		return(FALSE);
+	if((RD_HARPOON(ioPort + hp_seltimeout) == TO_250ms) ||
+		(RD_HARPOON(ioPort + hp_seltimeout) == TO_290ms))
+		return(TRUE);
+	return(FALSE);
+
+}
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_start_sccb
+ *
+ * Description: Start a command pointed to by p_Sccb. When the
+ *              command is completed it will be returned via the
+ *              callback function.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+void SccbMgr_start_sccb(CARD_HANDLE pCurrCard, PUCB p_ucb)
+#else
+#if defined(DOS)
+void SccbMgr_start_sccb(USHORT pCurrCard, PSCCB p_Sccb)
+#else
+void SccbMgr_start_sccb(ULONG pCurrCard, PSCCB p_Sccb)
+#endif
+#endif
+{
+#if defined(DOS)
+   USHORT ioport;
+#else
+   ULONG ioport;
+#endif
+   UCHAR thisCard, lun;
+	PSCCB pSaveSccb;
+   CALL_BK_FN callback;
+
+#if (FW_TYPE==_UCB_MGR_)
+   PSCCB p_Sccb;
+#endif
+
+   mOS_Lock((PSCCBcard)pCurrCard);
+   thisCard = ((PSCCBcard) pCurrCard)->cardIndex;
+   ioport = ((PSCCBcard) pCurrCard)->ioPort;
+
+#if (FW_TYPE==_UCB_MGR_)
+   p_Sccb = (PSCCB)p_ucb->UCB_MgrPrivatePtr;
+#endif
+
+	if((p_Sccb->TargID > MAX_SCSI_TAR) || (p_Sccb->Lun > MAX_LUN))
+	{
+
+#if (FW_TYPE==_UCB_MGR_)
+		p_ucb->UCB_hbastat = SCCB_COMPLETE;
+		p_ucb->UCB_status=SCCB_ERROR;
+		callback = (CALL_BK_FN)p_ucb->UCB_callback;
+		if (callback)
+			callback(p_ucb);
+#endif
+
+#if (FW_TYPE==_SCCB_MGR_)
+		p_Sccb->HostStatus = SCCB_COMPLETE;
+		p_Sccb->SccbStatus = SCCB_ERROR;
+		callback = (CALL_BK_FN)p_Sccb->SccbCallback;
+		if (callback)
+			callback(p_Sccb);
+#endif
+
+		mOS_UnLock((PSCCBcard)pCurrCard);
+		return;
+	}
+
+#if (FW_TYPE==_SCCB_MGR_)
+   sinits(p_Sccb,thisCard);
+#endif
+
+
+#if (FW_TYPE==_UCB_MGR_)
+#ifndef NO_IOCTLS
+
+   if (p_ucb->UCB_opcode & OPC_IOCTL)
+	{
+
+		switch (p_ucb->UCB_IOCTLCommand) 
+		{
+			case READ_NVRAM:
+				ReadNVRam((PSCCBcard)pCurrCard,p_ucb);
+				p_ucb->UCB_status=UCB_SUCCESS;
+				callback = (CALL_BK_FN)p_ucb->UCB_callback;
+				if (callback)
+					callback(p_ucb);
+				mOS_UnLock((PSCCBcard)pCurrCard);
+				return;
+
+			case WRITE_NVRAM:
+				WriteNVRam((PSCCBcard)pCurrCard,p_ucb);
+				p_ucb->UCB_status=UCB_SUCCESS;
+				callback = (CALL_BK_FN)p_ucb->UCB_callback;
+				if (callback)
+					callback(p_ucb);
+				mOS_UnLock((PSCCBcard)pCurrCard);
+				return;
+
+			case SEND_SCSI_PASSTHRU:
+#if (FW_TYPE != _SCCB_MGR_)
+				if( p_ucb->UCB_targid >=
+				    ((PSCCBcard)pCurrCard)->cardInfo->ai_MaxTarg )
+				{
+					p_ucb->UCB_status = UCB_ERROR;
+					p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+					callback = (CALL_BK_FN)p_ucb->UCB_callback;
+					if (callback)
+						callback(p_ucb);
+					mOS_UnLock((PSCCBcard)pCurrCard);
+					return;
+				}
+#endif
+				break;
+
+			case HARD_RESET:
+				p_ucb->UCB_status = UCB_INVALID;
+				callback = (CALL_BK_FN)p_ucb->UCB_callback;
+				if (callback)
+					callback(p_ucb);
+				mOS_UnLock((PSCCBcard)pCurrCard);
+				return;
+			case GET_DEVICE_SYNCRATE:
+				if( !GetDevSyncRate((PSCCBcard)pCurrCard,p_ucb) )
+				{
+					p_ucb->UCB_status = UCB_SUCCESS;
+				}
+				else
+				{
+					p_ucb->UCB_status = UCB_ERROR;
+					p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+				}
+				callback = (CALL_BK_FN)p_ucb->UCB_callback;
+				if (callback)
+					callback(p_ucb);
+				mOS_UnLock((PSCCBcard)pCurrCard);
+				return;
+			case SET_DEVICE_SYNCRATE:
+				if( !SetDevSyncRate((PSCCBcard)pCurrCard,p_ucb) )
+				{
+					p_ucb->UCB_status = UCB_SUCCESS;
+				}
+				else
+				{
+					p_ucb->UCB_status = UCB_ERROR;
+					p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+				}
+				callback = (CALL_BK_FN)p_ucb->UCB_callback;
+				if (callback)
+					callback(p_ucb);
+				mOS_UnLock((PSCCBcard)pCurrCard);
+				return;
+			case GET_WIDE_MODE:
+				if( !GetDevWideMode((PSCCBcard)pCurrCard,p_ucb) )
+				{
+					p_ucb->UCB_status = UCB_SUCCESS;
+				}
+				else
+				{
+					p_ucb->UCB_status = UCB_ERROR;
+					p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+				}
+				callback = (CALL_BK_FN)p_ucb->UCB_callback;
+				if (callback)
+					callback(p_ucb);
+				mOS_UnLock((PSCCBcard)pCurrCard);
+				return;
+			case SET_WIDE_MODE:
+				if( !SetDevWideMode((PSCCBcard)pCurrCard,p_ucb) )
+				{
+					p_ucb->UCB_status = UCB_SUCCESS;
+				}
+				else
+				{
+					p_ucb->UCB_status = UCB_ERROR;
+					p_ucb->UCB_hbastat = HASTAT_HW_ERROR;
+				}
+				callback = (CALL_BK_FN)p_ucb->UCB_callback;
+				if (callback)
+					callback(p_ucb);
+				mOS_UnLock((PSCCBcard)pCurrCard);
+				return;
+			default:
+				p_ucb->UCB_status=UCB_INVALID;
+				callback = (CALL_BK_FN)p_ucb->UCB_callback;
+				if (callback)
+					callback(p_ucb);
+				mOS_UnLock((PSCCBcard)pCurrCard);
+				return;
+		}
+	}
+#endif /* NO_IOCTLS */
+#endif /* (FW_TYPE==_UCB_MGR_) */
+
+
+   if (!((PSCCBcard) pCurrCard)->cmdCounter)
+      {
+      WR_HARPOON(ioport+hp_semaphore, (RD_HARPOON(ioport+hp_semaphore)
+         | SCCB_MGR_ACTIVE));
+
+      if (((PSCCBcard) pCurrCard)->globalFlags & F_GREEN_PC)
+         {
+		 WR_HARPOON(ioport+hp_clkctrl_0, CLKCTRL_DEFAULT);
+		 WR_HARPOON(ioport+hp_sys_ctrl, 0x00);
+         }
+      }
+
+   ((PSCCBcard)pCurrCard)->cmdCounter++;
+
+   if (RD_HARPOON(ioport+hp_semaphore) & BIOS_IN_USE) {
+
+      WR_HARPOON(ioport+hp_semaphore, (RD_HARPOON(ioport+hp_semaphore)
+         | TICKLE_ME));
+		if(p_Sccb->OperationCode == RESET_COMMAND)
+			{
+				pSaveSccb = ((PSCCBcard) pCurrCard)->currentSCCB;
+				((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+				queueSelectFail(&BL_Card[thisCard], thisCard);
+				((PSCCBcard) pCurrCard)->currentSCCB = pSaveSccb;
+			}
+		else
+			{
+	      queueAddSccb(p_Sccb,thisCard);
+			}
+      }
+
+   else if ((RD_HARPOON(ioport+hp_page_ctrl) & G_INT_DISABLE)) {
+
+			if(p_Sccb->OperationCode == RESET_COMMAND)
+				{
+					pSaveSccb = ((PSCCBcard) pCurrCard)->currentSCCB;
+					((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+					queueSelectFail(&BL_Card[thisCard], thisCard);
+					((PSCCBcard) pCurrCard)->currentSCCB = pSaveSccb;
+				}
+			else
+				{
+		      queueAddSccb(p_Sccb,thisCard);
+				}
+      }
+
+   else {
+
+      MDISABLE_INT(ioport);
+
+		if((((PSCCBcard) pCurrCard)->globalFlags & F_CONLUN_IO) && 
+			((sccbMgrTbl[thisCard][p_Sccb->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+			lun = p_Sccb->Lun;
+		else
+			lun = 0;
+      if ((((PSCCBcard) pCurrCard)->currentSCCB == NULL) &&
+         (sccbMgrTbl[thisCard][p_Sccb->TargID].TarSelQ_Cnt == 0) &&
+         (sccbMgrTbl[thisCard][p_Sccb->TargID].TarLUNBusy[lun]
+         == FALSE)) {
+
+            ((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+			   mOS_UnLock((PSCCBcard)pCurrCard);
+#if defined(DOS)
+            ssel((USHORT)p_Sccb->SccbIOPort,thisCard);
+#else
+	    ssel(p_Sccb->SccbIOPort,thisCard);
+#endif
+			   mOS_Lock((PSCCBcard)pCurrCard);
+         }
+
+      else {
+
+			if(p_Sccb->OperationCode == RESET_COMMAND)
+				{
+					pSaveSccb = ((PSCCBcard) pCurrCard)->currentSCCB;
+					((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+					queueSelectFail(&BL_Card[thisCard], thisCard);
+					((PSCCBcard) pCurrCard)->currentSCCB = pSaveSccb;
+				}
+			else
+				{
+	         	queueAddSccb(p_Sccb,thisCard);
+				}
+         }
+
+
+      MENABLE_INT(ioport);
+      }
+
+   mOS_UnLock((PSCCBcard)pCurrCard);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_abort_sccb
+ *
+ * Description: Abort the command pointed to by p_Sccb.  When the
+ *              command is completed it will be returned via the
+ *              callback function.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+s32bits SccbMgr_abort_sccb(CARD_HANDLE pCurrCard, PUCB p_ucb)
+#else
+#if defined(DOS)
+int SccbMgr_abort_sccb(USHORT pCurrCard, PSCCB p_Sccb)
+#else
+int SccbMgr_abort_sccb(ULONG pCurrCard, PSCCB p_Sccb)
+#endif
+#endif
+
+{
+#if defined(DOS)
+	USHORT ioport;
+#else
+	ULONG ioport;
+#endif
+
+	UCHAR thisCard;
+	CALL_BK_FN callback;
+	UCHAR TID;
+	PSCCB pSaveSCCB;
+	PSCCBMgr_tar_info currTar_Info;
+
+
+#if (FW_TYPE==_UCB_MGR_)
+	PSCCB    p_Sccb;
+	p_Sccb=(PSCCB)p_ucb->UCB_MgrPrivatePtr;
+#endif
+
+	ioport = ((PSCCBcard) pCurrCard)->ioPort;
+
+	thisCard = ((PSCCBcard)pCurrCard)->cardIndex;
+
+	mOS_Lock((PSCCBcard)pCurrCard);
+
+	if (RD_HARPOON(ioport+hp_page_ctrl) & G_INT_DISABLE)
+	{
+		mOS_UnLock((PSCCBcard)pCurrCard);
+	}
+
+	else
+	{
+
+		if (queueFindSccb(p_Sccb,thisCard))
+		{
+
+			mOS_UnLock((PSCCBcard)pCurrCard);
+
+			((PSCCBcard)pCurrCard)->cmdCounter--;
+
+			if (!((PSCCBcard)pCurrCard)->cmdCounter)
+				WR_HARPOON(ioport+hp_semaphore,(RD_HARPOON(ioport+hp_semaphore)
+					& (UCHAR)(~(SCCB_MGR_ACTIVE | TICKLE_ME)) ));
+
+#if (FW_TYPE==_SCCB_MGR_)
+			p_Sccb->SccbStatus = SCCB_ABORT;
+			callback = p_Sccb->SccbCallback;
+			callback(p_Sccb);
+#else
+			p_ucb->UCB_status=SCCB_ABORT;
+			callback = (CALL_BK_FN)p_ucb->UCB_callback;
+			callback(p_ucb);
+#endif
+
+			return(0);
+		}
+
+		else
+		{
+			mOS_UnLock((PSCCBcard)pCurrCard);
+
+			if (((PSCCBcard)pCurrCard)->currentSCCB == p_Sccb)
+			{
+				p_Sccb->SccbStatus = SCCB_ABORT;
+				return(0);
+
+			}
+
+			else
+			{
+
+				TID = p_Sccb->TargID;
+
+
+				if(p_Sccb->Sccb_tag)
+				{
+					MDISABLE_INT(ioport);
+					if (((PSCCBcard) pCurrCard)->discQ_Tbl[p_Sccb->Sccb_tag]==p_Sccb)
+					{
+						p_Sccb->SccbStatus = SCCB_ABORT;
+						p_Sccb->Sccb_scsistat = ABORT_ST;
+#if (FW_TYPE==_UCB_MGR_)
+						p_ucb->UCB_status=SCCB_ABORT;
+#endif
+						p_Sccb->Sccb_scsimsg = SMABORT_TAG;
+
+						if(((PSCCBcard) pCurrCard)->currentSCCB == NULL)
+						{
+							((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+							ssel(ioport, thisCard);
+						}
+						else
+						{
+							pSaveSCCB = ((PSCCBcard) pCurrCard)->currentSCCB;
+							((PSCCBcard) pCurrCard)->currentSCCB = p_Sccb;
+							queueSelectFail((PSCCBcard) pCurrCard, thisCard);
+							((PSCCBcard) pCurrCard)->currentSCCB = pSaveSCCB;
+						}
+					}
+					MENABLE_INT(ioport);
+					return(0);
+				}
+				else
+				{
+					currTar_Info = &sccbMgrTbl[thisCard][p_Sccb->TargID];
+
+					if(BL_Card[thisCard].discQ_Tbl[currTar_Info->LunDiscQ_Idx[p_Sccb->Lun]] 
+							== p_Sccb)
+					{
+						p_Sccb->SccbStatus = SCCB_ABORT;
+						return(0);
+					}
+				}
+			}
+		}
+	}
+	return(-1);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_my_int
+ *
+ * Description: Do a quick check to determine if there is a pending
+ *              interrupt for this card and disable the IRQ Pin if so.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+u08bits SccbMgr_my_int(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+UCHAR SccbMgr_my_int(USHORT pCurrCard)
+#else
+UCHAR SccbMgr_my_int(ULONG pCurrCard)
+#endif
+#endif
+{
+#if defined(DOS)
+   USHORT ioport;
+#else
+   ULONG ioport;
+#endif
+
+   ioport = ((PSCCBcard)pCurrCard)->ioPort;
+
+   if (RD_HARPOON(ioport+hp_int_status) & INT_ASSERTED)
+   {
+
+#if defined(DOS)
+      MDISABLE_INT(ioport);
+#endif
+
+      return(TRUE);
+   }
+
+   else
+
+      return(FALSE);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_isr
+ *
+ * Description: This is our entry point when an interrupt is generated
+ *              by the card and the upper level driver passes it on to
+ *              us.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+s32bits SccbMgr_isr(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+int SccbMgr_isr(USHORT pCurrCard)
+#else
+int SccbMgr_isr(ULONG pCurrCard)
+#endif
+#endif
+{
+   PSCCB currSCCB;
+   UCHAR thisCard,result,bm_status, bm_int_st;
+   USHORT hp_int;
+   UCHAR i, target;
+#if defined(DOS)
+   USHORT ioport;
+#else
+   ULONG ioport;
+#endif
+
+   mOS_Lock((PSCCBcard)pCurrCard);
+
+   thisCard = ((PSCCBcard)pCurrCard)->cardIndex;
+   ioport = ((PSCCBcard)pCurrCard)->ioPort;
+
+   MDISABLE_INT(ioport);
+
+#if defined(BUGBUG)
+   WR_HARPOON(ioport+hp_user_defined_D, RD_HARPOON(ioport+hp_int_status));
+#endif
+
+   if ((bm_int_st=RD_HARPOON(ioport+hp_int_status)) & EXT_STATUS_ON)
+		bm_status = RD_HARPOON(ioport+hp_ext_status) & (UCHAR)BAD_EXT_STATUS;
+   else
+      bm_status = 0;
+
+   WR_HARPOON(ioport+hp_int_mask, (INT_CMD_COMPL | SCSI_INTERRUPT));
+
+   mOS_UnLock((PSCCBcard)pCurrCard);
+
+   while ((hp_int = RDW_HARPOON((ioport+hp_intstat)) & default_intena) |
+	  bm_status)
+     {
+
+       currSCCB = ((PSCCBcard)pCurrCard)->currentSCCB;
+
+#if defined(BUGBUG)
+   Debug_Load(thisCard,(UCHAR) 0XFF);
+   Debug_Load(thisCard,bm_int_st);
+
+   Debug_Load(thisCard,hp_int_0);
+   Debug_Load(thisCard,hp_int_1);
+#endif
+
+
+      if (hp_int & (FIFO | TIMEOUT | RESET | SCAM_SEL) || bm_status) {
+         result = SccbMgr_bad_isr(ioport,thisCard,((PSCCBcard)pCurrCard),hp_int);
+         WRW_HARPOON((ioport+hp_intstat), (FIFO | TIMEOUT | RESET | SCAM_SEL));
+         bm_status = 0;
+
+         if (result) {
+
+			   mOS_Lock((PSCCBcard)pCurrCard);
+            MENABLE_INT(ioport);
+			   mOS_UnLock((PSCCBcard)pCurrCard);
+            return(result);
+            }
+         }
+
+
+      else if (hp_int & ICMD_COMP) {
+
+         if ( !(hp_int & BUS_FREE) ) {
+            /* Wait for the BusFree before starting a new command.  We
+               must also check for being reselected since the BusFree
+               may not show up if another device reselects us in 1.5us or
+               less.  SRR Wednesday, 3/8/1995.
+	         */
+	   while (!(RDW_HARPOON((ioport+hp_intstat)) & (BUS_FREE | RSEL))) ;
+	 }
+
+         if (((PSCCBcard)pCurrCard)->globalFlags & F_HOST_XFER_ACT)
+
+            phaseChkFifo(ioport, thisCard);
+
+/*         WRW_HARPOON((ioport+hp_intstat),
+            (BUS_FREE | ICMD_COMP | ITAR_DISC | XFER_CNT_0));
+         */
+
+		 WRW_HARPOON((ioport+hp_intstat), CLR_ALL_INT_1);
+
+         autoCmdCmplt(ioport,thisCard);
+
+         }
+
+
+      else if (hp_int & ITAR_DISC)
+         {
+
+         if (((PSCCBcard)pCurrCard)->globalFlags & F_HOST_XFER_ACT) {
+
+            phaseChkFifo(ioport, thisCard);
+
+            }
+
+         if (RD_HARPOON(ioport+hp_gp_reg_1) == SMSAVE_DATA_PTR) {
+
+            WR_HARPOON(ioport+hp_gp_reg_1, 0x00);
+            currSCCB->Sccb_XferState |= F_NO_DATA_YET;
+
+            currSCCB->Sccb_savedATC = currSCCB->Sccb_ATC;
+            }
+
+         currSCCB->Sccb_scsistat = DISCONNECT_ST;
+         queueDisconnect(currSCCB,thisCard);
+
+            /* Wait for the BusFree before starting a new command.  We
+               must also check for being reselected since the BusFree
+               may not show up if another device reselects us in 1.5us or
+               less.  SRR Wednesday, 3/8/1995.
+             */
+	   while (!(RDW_HARPOON((ioport+hp_intstat)) & (BUS_FREE | RSEL))) ;
+
+         WRW_HARPOON((ioport+hp_intstat), (BUS_FREE | ITAR_DISC));
+
+
+         ((PSCCBcard)pCurrCard)->globalFlags |= F_NEW_SCCB_CMD;
+
+      	}
+
+
+      else if (hp_int & RSEL) {
+
+         WRW_HARPOON((ioport+hp_intstat), (PROG_HLT | RSEL | PHASE | BUS_FREE));
+
+         if (RDW_HARPOON((ioport+hp_intstat)) & ITAR_DISC)
+		      {
+            if (((PSCCBcard)pCurrCard)->globalFlags & F_HOST_XFER_ACT)
+			      {
+               phaseChkFifo(ioport, thisCard);
+               }
+
+            if (RD_HARPOON(ioport+hp_gp_reg_1) == SMSAVE_DATA_PTR)
+			      {
+               WR_HARPOON(ioport+hp_gp_reg_1, 0x00);
+               currSCCB->Sccb_XferState |= F_NO_DATA_YET;
+               currSCCB->Sccb_savedATC = currSCCB->Sccb_ATC;
+               }
+
+            WRW_HARPOON((ioport+hp_intstat), (BUS_FREE | ITAR_DISC));
+            currSCCB->Sccb_scsistat = DISCONNECT_ST;
+            queueDisconnect(currSCCB,thisCard);
+            }
+
+         sres(ioport,thisCard,((PSCCBcard)pCurrCard));
+         phaseDecode(ioport,thisCard);
+
+         }
+
+
+      else if ((hp_int & IDO_STRT) && (!(hp_int & BUS_FREE)))
+         {
+
+            WRW_HARPOON((ioport+hp_intstat), (IDO_STRT | XFER_CNT_0));
+            phaseDecode(ioport,thisCard);
+
+         }
+
+
+      else if ( (hp_int & IUNKWN) || (hp_int & PROG_HLT) )
+		   {
+	 	   WRW_HARPOON((ioport+hp_intstat), (PHASE | IUNKWN | PROG_HLT));
+	 	   if ((RD_HARPOON(ioport+hp_prgmcnt_0) & (UCHAR)0x3f)< (UCHAR)SELCHK)
+	    		{
+	    		phaseDecode(ioport,thisCard);
+	    		}
+	 	   else
+	    		{
+   /* Harpoon problem some SCSI target device respond to selection
+   with short BUSY pulse (<400ns) this will make the Harpoon is not able
+   to latch the correct Target ID into reg. x53.
+   The work around require to correct this reg. But when write to this
+   reg. (0x53) also increment the FIFO write addr reg (0x6f), thus we
+   need to read this reg first then restore it later. After update to 0x53 */
+
+	    		i = (UCHAR)(RD_HARPOON(ioport+hp_fifowrite));
+	    		target = (UCHAR)(RD_HARPOON(ioport+hp_gp_reg_3));
+	    		WR_HARPOON(ioport+hp_xfer_pad, (UCHAR) ID_UNLOCK);
+	    		WR_HARPOON(ioport+hp_select_id, (UCHAR)(target | target<<4));
+	    		WR_HARPOON(ioport+hp_xfer_pad, (UCHAR) 0x00);
+	    		WR_HARPOON(ioport+hp_fifowrite, i);
+	    		WR_HARPOON(ioport+hp_autostart_3, (AUTO_IMMED+TAG_STRT));
+	    		}
+	 	   }
+
+      else if (hp_int & XFER_CNT_0) {
+
+         WRW_HARPOON((ioport+hp_intstat), XFER_CNT_0);
+
+         schkdd(ioport,thisCard);
+
+         }
+
+
+      else if (hp_int & BUS_FREE) {
+
+         WRW_HARPOON((ioport+hp_intstat), BUS_FREE);
+
+        	if (((PSCCBcard)pCurrCard)->globalFlags & F_HOST_XFER_ACT) {
+
+           	hostDataXferAbort(ioport,thisCard,currSCCB);
+				}
+
+         phaseBusFree(ioport,thisCard);
+			}
+
+
+      else if (hp_int & ITICKLE) {
+
+         WRW_HARPOON((ioport+hp_intstat), ITICKLE);
+         ((PSCCBcard)pCurrCard)->globalFlags |= F_NEW_SCCB_CMD;
+         }
+
+
+
+      if (((PSCCBcard)pCurrCard)->globalFlags & F_NEW_SCCB_CMD) {
+
+
+         ((PSCCBcard)pCurrCard)->globalFlags &= ~F_NEW_SCCB_CMD;
+
+
+         if (((PSCCBcard)pCurrCard)->currentSCCB == NULL) {
+
+            queueSearchSelect(((PSCCBcard)pCurrCard),thisCard);
+            }
+
+         if (((PSCCBcard)pCurrCard)->currentSCCB != NULL) {
+            ((PSCCBcard)pCurrCard)->globalFlags &= ~F_NEW_SCCB_CMD;
+            ssel(ioport,thisCard);
+            }
+
+         break;
+
+         }
+
+      }  /*end while */
+
+   mOS_Lock((PSCCBcard)pCurrCard);
+   MENABLE_INT(ioport);
+   mOS_UnLock((PSCCBcard)pCurrCard);
+
+   return(0);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Sccb_bad_isr
+ *
+ * Description: Some type of interrupt has occured which is slightly
+ *              out of the ordinary.  We will now decode it fully, in
+ *              this routine.  This is broken up in an attempt to save
+ *              processing time.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+UCHAR SccbMgr_bad_isr(USHORT p_port, UCHAR p_card, PSCCBcard pCurrCard, USHORT p_int)
+#else
+UCHAR SccbMgr_bad_isr(ULONG p_port, UCHAR p_card, PSCCBcard pCurrCard, USHORT p_int)
+#endif
+{
+#if defined(HARP_REVX)
+   ULONG timer;
+#endif
+UCHAR temp, ScamFlg;
+PSCCBMgr_tar_info currTar_Info;
+PNVRamInfo pCurrNvRam;
+
+
+   if (RD_HARPOON(p_port+hp_ext_status) &
+         (BM_FORCE_OFF | PCI_DEV_TMOUT | BM_PARITY_ERR | PIO_OVERRUN) )
+      {
+
+      if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
+         {
+
+         hostDataXferAbort(p_port,p_card, pCurrCard->currentSCCB);
+         }
+
+      if (RD_HARPOON(p_port+hp_pci_stat_cfg) & REC_MASTER_ABORT)
+
+         {
+         WR_HARPOON(p_port+hp_pci_stat_cfg,
+            (RD_HARPOON(p_port+hp_pci_stat_cfg) & ~REC_MASTER_ABORT));
+
+         WR_HARPOON(p_port+hp_host_blk_cnt, 0x00);
+
+         }
+
+      if (pCurrCard->currentSCCB != NULL)
+         {
+
+         if (!pCurrCard->currentSCCB->HostStatus)
+            pCurrCard->currentSCCB->HostStatus = SCCB_BM_ERR;
+
+         sxfrp(p_port,p_card);
+
+	     temp = (UCHAR)(RD_HARPOON(p_port+hp_ee_ctrl) &
+							(EXT_ARB_ACK | SCSI_TERM_ENA_H));
+      	WR_HARPOON(p_port+hp_ee_ctrl, ((UCHAR)temp | SEE_MS | SEE_CS));
+         WR_HARPOON(p_port+hp_ee_ctrl, temp);
+
+         if (!(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RESET)))
+            {
+            phaseDecode(p_port,p_card);
+            }
+         }
+      }
+
+
+   else if (p_int & RESET)
+         {
+
+				WR_HARPOON(p_port+hp_clkctrl_0, CLKCTRL_DEFAULT);
+				WR_HARPOON(p_port+hp_sys_ctrl, 0x00);
+           if (pCurrCard->currentSCCB != NULL) {
+
+               if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
+
+               hostDataXferAbort(p_port,p_card, pCurrCard->currentSCCB);
+               }
+
+
+           DISABLE_AUTO(p_port);
+
+           sresb(p_port,p_card);
+
+           while(RD_HARPOON(p_port+hp_scsictrl_0) & SCSI_RST) {}
+
+				pCurrNvRam = pCurrCard->pNvRamInfo;
+				if(pCurrNvRam){
+					ScamFlg = pCurrNvRam->niScamConf;
+				}
+				else{
+				   ScamFlg = (UCHAR) utilEERead(p_port, SCAM_CONFIG/2);
+				}
+
+           XbowInit(p_port, ScamFlg);
+
+               scini(p_card, pCurrCard->ourId, 0);
+
+           return(0xFF);
+         }
+
+
+   else if (p_int & FIFO) {
+
+      WRW_HARPOON((p_port+hp_intstat), FIFO);
+
+#if defined(HARP_REVX)
+
+      for (timer=0x00FFFFFFL; timer != 0x00000000L; timer--) {
+
+         if (RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY)
+            break;
+
+         if (RDW_HARPOON((p_port+hp_intstat)) & BUS_FREE)
+            break;
+         }
+
+
+      if ( (RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY) &&
+           (RD_HARPOON(p_port+hp_fiforead) !=
+            RD_HARPOON(p_port+hp_fifowrite)) &&
+           (RD_HARPOON(p_port+hp_xfercnt_0))
+         )
+
+            WR_HARPOON((p_port+hp_xferstat), 0x01);
+
+/*      else
+ */
+/*         sxfrp(p_port,p_card);
+ */
+#else
+      if (pCurrCard->currentSCCB != NULL)
+         sxfrp(p_port,p_card);
+#endif
+      }
+
+   else if (p_int & TIMEOUT)
+      {
+
+      DISABLE_AUTO(p_port);
+
+      WRW_HARPOON((p_port+hp_intstat),
+		  (PROG_HLT | TIMEOUT | SEL |BUS_FREE | PHASE | IUNKWN));
+
+      pCurrCard->currentSCCB->HostStatus = SCCB_SELECTION_TIMEOUT;
+
+
+		currTar_Info = &sccbMgrTbl[p_card][pCurrCard->currentSCCB->TargID];
+		if((pCurrCard->globalFlags & F_CONLUN_IO) &&
+			((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+	      currTar_Info->TarLUNBusy[pCurrCard->currentSCCB->Lun] = FALSE;
+		else
+	      currTar_Info->TarLUNBusy[0] = FALSE;
+
+
+      if (currTar_Info->TarEEValue & EE_SYNC_MASK)
+         {
+	       currTar_Info->TarSyncCtrl = 0;
+         currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
+         }
+
+      if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
+         {
+         currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
+         }
+
+      sssyncv(p_port, pCurrCard->currentSCCB->TargID, NARROW_SCSI,currTar_Info);
+
+      queueCmdComplete(pCurrCard, pCurrCard->currentSCCB, p_card);
+
+      }
+
+#if defined(SCAM_LEV_2)
+
+   else if (p_int & SCAM_SEL)
+      {
+
+      scarb(p_port,LEVEL2_TAR);
+      scsel(p_port);
+      scasid(p_card, p_port);
+
+      scbusf(p_port);
+
+      WRW_HARPOON((p_port+hp_intstat), SCAM_SEL);
+      }
+#endif
+
+   return(0x00);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_scsi_reset
+ *
+ * Description: A SCSI bus reset will be generated and all outstanding
+ *              Sccbs will be returned via the callback.
+ *
+ *---------------------------------------------------------------------*/
+#if (FW_TYPE==_UCB_MGR_)
+void SccbMgr_scsi_reset(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+void SccbMgr_scsi_reset(USHORT pCurrCard)
+#else
+void SccbMgr_scsi_reset(ULONG pCurrCard)
+#endif
+#endif
+{
+   UCHAR thisCard;
+
+   thisCard = ((PSCCBcard)pCurrCard)->cardIndex;
+
+   mOS_Lock((PSCCBcard)pCurrCard);
+
+   if (((PSCCBcard) pCurrCard)->globalFlags & F_GREEN_PC)
+      {
+      WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_clkctrl_0, CLKCTRL_DEFAULT);
+      WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_sys_ctrl, 0x00);
+      }
+
+   sresb(((PSCCBcard)pCurrCard)->ioPort,thisCard);
+
+   if (RD_HARPOON(((PSCCBcard)pCurrCard)->ioPort+hp_ext_status) & BM_CMD_BUSY)
+      {
+      WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_page_ctrl,
+         (RD_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_page_ctrl)
+         & ~SCATTER_EN));
+
+      WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_sg_addr,0x00);
+
+      ((PSCCBcard) pCurrCard)->globalFlags &= ~F_HOST_XFER_ACT;
+      busMstrTimeOut(((PSCCBcard) pCurrCard)->ioPort);
+
+      WR_HARPOON(((PSCCBcard) pCurrCard)->ioPort+hp_int_mask,
+         (INT_CMD_COMPL | SCSI_INTERRUPT));
+      }
+
+/*
+      if (utilEERead(((PSCCBcard)pCurrCard)->ioPort, (SCAM_CONFIG/2))
+            & SCAM_ENABLED)
+*/
+         scini(thisCard, ((PSCCBcard)pCurrCard)->ourId, 0);
+
+#if (FW_TYPE==_UCB_MGR_)
+   ((PSCCBcard)pCurrCard)->cardInfo->ai_AEN_routine(0x01,pCurrCard,0,0,0,0);
+#endif
+
+   mOS_UnLock((PSCCBcard)pCurrCard);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_timer_expired
+ *
+ * Description: This function allow me to kill my own job that has not
+ *              yet completed, and has cause a timeout to occur.  This
+ *              timeout has caused the upper level driver to call this
+ *              function.
+ *
+ *---------------------------------------------------------------------*/
+
+#if (FW_TYPE==_UCB_MGR_)
+void SccbMgr_timer_expired(CARD_HANDLE pCurrCard)
+#else
+#if defined(DOS)
+void SccbMgr_timer_expired(USHORT pCurrCard)
+#else
+void SccbMgr_timer_expired(ULONG pCurrCard)
+#endif
+#endif
+{
+}
+
+#if defined(DOS)
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgr_status
+ *
+ * Description: This function returns the number of outstanding SCCB's.
+ *              This is specific to the DOS enviroment, which needs this
+ *              to help them keep protected and real mode commands staight.
+ *
+ *---------------------------------------------------------------------*/
+
+USHORT SccbMgr_status(USHORT pCurrCard)
+{
+   return(BL_Card[pCurrCard].cmdCounter);
+}
+#endif
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgrTableInit
+ *
+ * Description: Initialize all Sccb manager data structures.
+ *
+ *---------------------------------------------------------------------*/
+
+void SccbMgrTableInitAll()
+{
+   UCHAR thisCard;
+
+   for (thisCard = 0; thisCard < MAX_CARDS; thisCard++)
+      {
+      SccbMgrTableInitCard(&BL_Card[thisCard],thisCard);
+
+      BL_Card[thisCard].ioPort      = 0x00;
+      BL_Card[thisCard].cardInfo    = NULL;
+      BL_Card[thisCard].cardIndex   = 0xFF;
+      BL_Card[thisCard].ourId       = 0x00;
+		BL_Card[thisCard].pNvRamInfo	= NULL;
+      }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgrTableInit
+ *
+ * Description: Initialize all Sccb manager data structures.
+ *
+ *---------------------------------------------------------------------*/
+
+void SccbMgrTableInitCard(PSCCBcard pCurrCard, UCHAR p_card)
+{
+   UCHAR scsiID, qtag;
+
+	for (qtag = 0; qtag < QUEUE_DEPTH; qtag++)
+	{
+		BL_Card[p_card].discQ_Tbl[qtag] = NULL;
+	}
+
+   for (scsiID = 0; scsiID < MAX_SCSI_TAR; scsiID++)
+      {
+      sccbMgrTbl[p_card][scsiID].TarStatus = 0;
+      sccbMgrTbl[p_card][scsiID].TarEEValue = 0;
+      SccbMgrTableInitTarget(p_card, scsiID);
+      }
+
+   pCurrCard->scanIndex = 0x00;
+   pCurrCard->currentSCCB = NULL;
+   pCurrCard->globalFlags = 0x00;
+   pCurrCard->cmdCounter  = 0x00;
+	pCurrCard->tagQ_Lst = 0x01;
+	pCurrCard->discQCount = 0; 
+
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: SccbMgrTableInit
+ *
+ * Description: Initialize all Sccb manager data structures.
+ *
+ *---------------------------------------------------------------------*/
+
+void SccbMgrTableInitTarget(UCHAR p_card, UCHAR target)
+{
+
+	UCHAR lun, qtag;
+	PSCCBMgr_tar_info currTar_Info;
+
+	currTar_Info = &sccbMgrTbl[p_card][target];
+
+	currTar_Info->TarSelQ_Cnt = 0;
+	currTar_Info->TarSyncCtrl = 0;
+
+	currTar_Info->TarSelQ_Head = NULL;
+	currTar_Info->TarSelQ_Tail = NULL;
+	currTar_Info->TarTagQ_Cnt = 0;
+	currTar_Info->TarLUN_CA = FALSE;
+
+
+	for (lun = 0; lun < MAX_LUN; lun++)
+	{
+		currTar_Info->TarLUNBusy[lun] = FALSE;
+		currTar_Info->LunDiscQ_Idx[lun] = 0;
+	}
+
+	for (qtag = 0; qtag < QUEUE_DEPTH; qtag++)
+	{
+		if(BL_Card[p_card].discQ_Tbl[qtag] != NULL)
+		{
+			if(BL_Card[p_card].discQ_Tbl[qtag]->TargID == target)
+			{
+				BL_Card[p_card].discQ_Tbl[qtag] = NULL;
+				BL_Card[p_card].discQCount--;
+			}
+		}
+	}
+}
+
+#if defined(BUGBUG)
+
+/*****************************************************************
+ * Save the current byte in the debug array
+ *****************************************************************/
+
+
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data)
+{
+   debug_int[p_card][debug_index[p_card]] = p_bug_data;
+   debug_index[p_card]++;
+
+   if (debug_index[p_card] == debug_size)
+
+      debug_index[p_card] = 0;
+}
+
+#endif
+#ident "$Id: sccb_dat.c 1.9 1997/01/31 02:12:58 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   sccb_dat.c  $
+ *
+ *   Description:  Functions relating to handling of the SCCB interface 
+ *                 between the device driver and the HARPOON.
+ *
+ *   $Date: 1997/01/31 02:12:58 $
+ *
+ *   $Revision: 1.9 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+	/*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <harpoon.h>*/
+
+
+#if defined(OS2) || defined (SOLARIS_REAL_MODE)
+SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR] = { 0 };
+SCCBCARD BL_Card[MAX_CARDS] = { 0 };
+SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR] = { 0 };
+NVRAMINFO nvRamInfo[MAX_MB_CARDS] = { 0 };
+#else
+SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+SCCBCARD BL_Card[MAX_CARDS];
+SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR];
+NVRAMINFO nvRamInfo[MAX_MB_CARDS];
+#endif
+
+
+#if defined(OS2)
+void (far *s_PhaseTbl[8]) (ULONG, UCHAR) = { 0 };
+UCHAR temp_id_string[ID_STRING_LENGTH] = { 0 };
+#elif defined(SOLARIS_REAL_MODE) || defined(__STDC__)
+void (*s_PhaseTbl[8]) (ULONG, UCHAR) = { 0 };
+#else
+void (*s_PhaseTbl[8]) ();
+#endif
+
+#if defined(DOS)
+UCHAR first_time;
+#endif
+
+UCHAR mbCards;
+UCHAR scamHAString[] = {0x63, 0x07, 'B', 'U', 'S', 'L', 'O', 'G', 'I', 'C', \
+								' ', 'B', 'T', '-', '9', '3', '0', \
+								0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, \
+								0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
+
+USHORT default_intena;
+
+#if defined(BUGBUG)
+UCHAR    debug_int[MAX_CARDS][debug_size];
+UCHAR    debug_index[MAX_CARDS];
+UCHAR    reserved_1[3];
+#endif
+#ident "$Id: scsi.c 1.19 1997/01/31 02:08:14 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   scsi.c  $
+ *
+ *   Description:  Functions for handling SCSI bus functions such as
+ *                 selection/reselection, sync negotiation, message-in
+ *                 decoding.
+ *
+ *   $Date: 1997/01/31 02:08:14 $
+ *
+ *   $Revision: 1.19 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+	/*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <eeprom.h>*/
+/*#include <harpoon.h>*/
+
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+#if defined(BUGBUG)
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sfetm
+ *
+ * Description: Read in a message byte from the SCSI bus, and check
+ *              for a parity error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR sfm(USHORT port, PSCCB pCurrSCCB)
+#else
+UCHAR sfm(ULONG port, PSCCB pCurrSCCB)
+#endif
+{
+	UCHAR message;
+	USHORT TimeOutLoop;
+
+	TimeOutLoop = 0;
+	while( (!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) &&
+			(TimeOutLoop++ < 20000) ){}
+
+	WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+	message = RD_HARPOON(port+hp_scsidata_0);
+
+	WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+
+	if (TimeOutLoop > 20000)
+		message = 0x00;   /* force message byte = 0 if Time Out on Req */
+
+	if ((RDW_HARPOON((port+hp_intstat)) & PARITY) &&
+		(RD_HARPOON(port+hp_addstat) & SCSI_PAR_ERR))
+	{
+		if (pCurrSCCB != NULL)
+		{
+			pCurrSCCB->Sccb_scsimsg = SMPARITY;
+		}
+		message = 0x00;
+		do
+		{
+			ACCEPT_MSG_ATN(port);
+			while( (!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) &&
+				(TimeOutLoop++ < 20000) ){}
+			if (TimeOutLoop > 20000)
+			{
+				WRW_HARPOON((port+hp_intstat), PARITY);
+				return(message);
+			}
+			if ((RD_HARPOON(port+hp_scsisig) & S_SCSI_PHZ) != S_MSGI_PH)
+			{
+				WRW_HARPOON((port+hp_intstat), PARITY);
+				return(message);
+			}
+			WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+			RD_HARPOON(port+hp_scsidata_0);
+
+			WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+
+		}while(1);
+
+	}
+	return(message);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: ssel
+ *
+ * Description: Load up automation and select target device.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void ssel(USHORT port, UCHAR p_card)
+#else
+void ssel(ULONG port, UCHAR p_card)
+#endif
+{
+
+#if defined(DOS)
+   UCHAR auto_loaded, i, target, *theCCB;
+#elif defined(OS2)
+   UCHAR auto_loaded, i, target;
+   UCHAR far *theCCB;
+#else
+   UCHAR auto_loaded, i, target, *theCCB;
+#endif
+
+#if defined(DOS)
+   USHORT cdb_reg;
+#else
+   ULONG cdb_reg;
+#endif
+   PSCCBcard CurrCard;
+   PSCCB currSCCB;
+   PSCCBMgr_tar_info currTar_Info;
+   UCHAR lastTag, lun;
+
+   CurrCard = &BL_Card[p_card];
+   currSCCB = CurrCard->currentSCCB;
+   target = currSCCB->TargID;
+   currTar_Info = &sccbMgrTbl[p_card][target];
+   lastTag = CurrCard->tagQ_Lst;
+
+   ARAM_ACCESS(port);
+
+
+	if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_REJECT)
+		currSCCB->ControlByte &= ~F_USE_CMD_Q;
+
+	if(((CurrCard->globalFlags & F_CONLUN_IO) && 
+		((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+
+	   lun = currSCCB->Lun;
+	else
+		lun = 0;
+
+
+#if defined(DOS)
+   currTar_Info->TarLUNBusy[lun] = TRUE;
+
+#else
+
+   if (CurrCard->globalFlags & F_TAG_STARTED)
+      {
+      if (!(currSCCB->ControlByte & F_USE_CMD_Q))
+         {
+      	if ((currTar_Info->TarLUN_CA == FALSE)
+      	    && ((currTar_Info->TarStatus & TAR_TAG_Q_MASK)
+      	    == TAG_Q_TRYING))
+            {
+
+	         if (currTar_Info->TarTagQ_Cnt !=0)
+                  {
+         		   currTar_Info->TarLUNBusy[lun] = TRUE;
+            		queueSelectFail(CurrCard,p_card);
+					   SGRAM_ACCESS(port);
+         		   return;
+         		   }
+
+            else {
+         		  currTar_Info->TarLUNBusy[lun] = TRUE;
+         		  }
+
+   	      }  /*End non-tagged */
+
+	      else {
+	         currTar_Info->TarLUNBusy[lun] = TRUE;
+	         }
+
+	      }  /*!Use cmd Q Tagged */
+
+	   else {
+   	     if (currTar_Info->TarLUN_CA == TRUE)
+               {
+      	      queueSelectFail(CurrCard,p_card);
+				   SGRAM_ACCESS(port);
+      	      return;
+	            }
+
+	        currTar_Info->TarLUNBusy[lun] = TRUE;
+
+   	     }  /*else use cmd Q tagged */
+
+      }  /*if glob tagged started */
+
+   else {
+        currTar_Info->TarLUNBusy[lun] = TRUE;
+        }
+
+#endif /* DOS */
+
+
+
+	if((((CurrCard->globalFlags & F_CONLUN_IO) && 
+		((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)) 
+		|| (!(currSCCB->ControlByte & F_USE_CMD_Q))))
+	{
+		if(CurrCard->discQCount >= QUEUE_DEPTH)
+		{
+			currTar_Info->TarLUNBusy[lun] = TRUE;
+			queueSelectFail(CurrCard,p_card);
+			SGRAM_ACCESS(port);
+			return;
+		}
+		for (i = 1; i < QUEUE_DEPTH; i++)
+		{
+			if (++lastTag >= QUEUE_DEPTH) lastTag = 1;
+			if (CurrCard->discQ_Tbl[lastTag] == NULL)
+			{
+				CurrCard->tagQ_Lst = lastTag;
+				currTar_Info->LunDiscQ_Idx[lun] = lastTag;
+				CurrCard->discQ_Tbl[lastTag] = currSCCB;
+				CurrCard->discQCount++;
+				break;
+			}
+		}
+		if(i == QUEUE_DEPTH)
+		{
+			currTar_Info->TarLUNBusy[lun] = TRUE;
+			queueSelectFail(CurrCard,p_card);
+			SGRAM_ACCESS(port);
+			return;
+		}
+	}
+
+
+
+   auto_loaded = FALSE;
+
+   WR_HARPOON(port+hp_select_id, target);
+   WR_HARPOON(port+hp_gp_reg_3, target);  /* Use by new automation logic */
+
+   if (currSCCB->OperationCode == RESET_COMMAND) {
+      WRW_HARPOON((port+ID_MSG_STRT), (MPM_OP+AMSG_OUT+
+               	 (currSCCB->Sccb_idmsg & ~DISC_PRIV)));
+
+      WRW_HARPOON((port+ID_MSG_STRT+2),BRH_OP+ALWAYS+NP);
+
+      currSCCB->Sccb_scsimsg = SMDEV_RESET;
+
+      WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+      auto_loaded = TRUE;
+      currSCCB->Sccb_scsistat = SELECT_BDR_ST;
+
+      if (currTar_Info->TarEEValue & EE_SYNC_MASK)
+         {
+	       currTar_Info->TarSyncCtrl = 0;
+	      currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
+	      }
+
+#if defined(WIDE_SCSI)
+
+      if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
+         {
+      	currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
+      	}
+#endif
+
+      sssyncv(port, target, NARROW_SCSI,currTar_Info);
+      SccbMgrTableInitTarget(p_card, target);
+
+      }
+
+		else if(currSCCB->Sccb_scsistat == ABORT_ST)
+		{
+			WRW_HARPOON((port+ID_MSG_STRT), (MPM_OP+AMSG_OUT+
+								(currSCCB->Sccb_idmsg & ~DISC_PRIV)));
+
+      WRW_HARPOON((port+ID_MSG_STRT+2),BRH_OP+ALWAYS+CMDPZ);
+
+			WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+
+								(((UCHAR)(currSCCB->ControlByte & TAG_TYPE_MASK)
+								>> 6) | (UCHAR)0x20)));
+			WRW_HARPOON((port+SYNC_MSGS+2),
+							(MPM_OP+AMSG_OUT+currSCCB->Sccb_tag));
+			WRW_HARPOON((port+SYNC_MSGS+4), (BRH_OP+ALWAYS+NP ));
+
+			WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+			auto_loaded = TRUE;
+		
+		}
+
+#if defined(WIDE_SCSI)
+
+
+   else if (!(currTar_Info->TarStatus & WIDE_NEGOCIATED))  {
+      auto_loaded = siwidn(port,p_card);
+      currSCCB->Sccb_scsistat = SELECT_WN_ST;
+      }
+
+#endif
+
+
+   else if (!((currTar_Info->TarStatus & TAR_SYNC_MASK)
+      == SYNC_SUPPORTED))  {
+      auto_loaded = sisyncn(port,p_card, FALSE);
+      currSCCB->Sccb_scsistat = SELECT_SN_ST;
+      }
+
+
+   if (!auto_loaded)
+      {
+
+#if !defined(DOS)
+      if (currSCCB->ControlByte & F_USE_CMD_Q)
+         {
+
+         CurrCard->globalFlags |= F_TAG_STARTED;
+
+         if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK)
+            == TAG_Q_REJECT)
+            {
+            currSCCB->ControlByte &= ~F_USE_CMD_Q;
+
+            /* Fix up the start instruction with a jump to
+               Non-Tag-CMD handling */
+            WRW_HARPOON((port+ID_MSG_STRT),BRH_OP+ALWAYS+NTCMD);
+
+            WRW_HARPOON((port+NON_TAG_ID_MSG),
+	                     (MPM_OP+AMSG_OUT+currSCCB->Sccb_idmsg));
+
+	         WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+
+	         /* Setup our STATE so we know what happend when
+               the wheels fall off. */
+            currSCCB->Sccb_scsistat = SELECT_ST;
+
+	         currTar_Info->TarLUNBusy[lun] = TRUE;
+            }
+
+         else
+            {
+            WRW_HARPOON((port+ID_MSG_STRT), (MPM_OP+AMSG_OUT+currSCCB->Sccb_idmsg));
+
+            WRW_HARPOON((port+ID_MSG_STRT+2), (MPM_OP+AMSG_OUT+
+                        (((UCHAR)(currSCCB->ControlByte & TAG_TYPE_MASK)
+                        >> 6) | (UCHAR)0x20)));
+
+				for (i = 1; i < QUEUE_DEPTH; i++)
+				{
+					if (++lastTag >= QUEUE_DEPTH) lastTag = 1;
+					if (CurrCard->discQ_Tbl[lastTag] == NULL)
+					{
+						WRW_HARPOON((port+ID_MSG_STRT+6),
+							(MPM_OP+AMSG_OUT+lastTag));
+						CurrCard->tagQ_Lst = lastTag;
+						currSCCB->Sccb_tag = lastTag;
+						CurrCard->discQ_Tbl[lastTag] = currSCCB;
+						CurrCard->discQCount++;
+						break;
+					}
+				}
+
+
+            if ( i == QUEUE_DEPTH )
+               {
+   	         currTar_Info->TarLUNBusy[lun] = TRUE;
+               queueSelectFail(CurrCard,p_card);
+				   SGRAM_ACCESS(port);
+   	         return;
+   	         }
+
+            currSCCB->Sccb_scsistat = SELECT_Q_ST;
+
+   	      WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+            }
+         }
+
+      else
+         {
+#endif   /* !DOS */
+
+         WRW_HARPOON((port+ID_MSG_STRT),BRH_OP+ALWAYS+NTCMD);
+
+      	WRW_HARPOON((port+NON_TAG_ID_MSG),
+            (MPM_OP+AMSG_OUT+currSCCB->Sccb_idmsg));
+
+         currSCCB->Sccb_scsistat = SELECT_ST;
+
+         WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+#if !defined(DOS)
+         }
+#endif
+
+
+#if defined(OS2)
+      theCCB = (UCHAR far *)&currSCCB->Cdb[0];
+#else
+      theCCB = (UCHAR *)&currSCCB->Cdb[0];
+#endif
+
+      cdb_reg = port + CMD_STRT;
+
+      for (i=0; i < currSCCB->CdbLength; i++)
+         {
+         WRW_HARPOON(cdb_reg, (MPM_OP + ACOMMAND + *theCCB));
+         cdb_reg +=2;
+         theCCB++;
+         }
+
+      if (currSCCB->CdbLength != TWELVE_BYTE_CMD)
+         WRW_HARPOON(cdb_reg, (BRH_OP+ALWAYS+    NP));
+
+      }  /* auto_loaded */
+
+#if defined(WIDE_SCSI)
+   WRW_HARPOON((port+hp_fiforead), (USHORT) 0x00);
+   WR_HARPOON(port+hp_xferstat, 0x00);
+#endif
+
+   WRW_HARPOON((port+hp_intstat), (PROG_HLT | TIMEOUT | SEL | BUS_FREE));
+
+   WR_HARPOON(port+hp_portctrl_0,(SCSI_PORT));
+
+
+   if (!(currSCCB->Sccb_MGRFlags & F_DEV_SELECTED))
+      {
+      WR_HARPOON(port+hp_scsictrl_0, (SEL_TAR | ENA_ATN | ENA_RESEL | ENA_SCAM_SEL));
+      }
+   else
+      {
+
+/*      auto_loaded =  (RD_HARPOON(port+hp_autostart_3) & (UCHAR)0x1F);
+      auto_loaded |= AUTO_IMMED; */
+      auto_loaded = AUTO_IMMED;
+
+      DISABLE_AUTO(port);
+
+      WR_HARPOON(port+hp_autostart_3, auto_loaded);
+      }
+
+   SGRAM_ACCESS(port);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sres
+ *
+ * Description: Hookup the correct CCB and handle the incoming messages.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void sres(USHORT port, UCHAR p_card, PSCCBcard pCurrCard)
+#else
+void sres(ULONG port, UCHAR p_card, PSCCBcard pCurrCard)
+#endif
+{
+#ifdef DOS
+   UCHAR our_target,message, msgRetryCount;
+   extern UCHAR lun, tag;
+#else
+   UCHAR our_target,message,lun,tag, msgRetryCount;
+#endif
+   PSCCBMgr_tar_info currTar_Info;
+	PSCCB currSCCB;
+
+
+
+
+	if(pCurrCard->currentSCCB != NULL)
+	{
+		currTar_Info = &sccbMgrTbl[p_card][pCurrCard->currentSCCB->TargID];
+		DISABLE_AUTO(port);
+
+
+		WR_HARPOON((port+hp_scsictrl_0),(ENA_RESEL | ENA_SCAM_SEL));
+
+
+		currSCCB = pCurrCard->currentSCCB;
+		if(currSCCB->Sccb_scsistat == SELECT_WN_ST)
+		{
+			currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
+			currSCCB->Sccb_scsistat = BUS_FREE_ST;
+		}
+		if(currSCCB->Sccb_scsistat == SELECT_SN_ST)
+		{
+			currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
+			currSCCB->Sccb_scsistat = BUS_FREE_ST;
+		}
+		if(((pCurrCard->globalFlags & F_CONLUN_IO) &&
+			((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+		{
+      	currTar_Info->TarLUNBusy[currSCCB->Lun] = FALSE;
+			if(currSCCB->Sccb_scsistat != ABORT_ST)
+			{
+				pCurrCard->discQCount--;
+				pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[currSCCB->Lun]] 
+													= NULL;
+			}
+		}
+		else
+		{
+	      currTar_Info->TarLUNBusy[0] = FALSE;
+			if(currSCCB->Sccb_tag)
+			{
+				if(currSCCB->Sccb_scsistat != ABORT_ST)
+				{
+					pCurrCard->discQCount--;
+					pCurrCard->discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+				}
+			}else
+			{
+				if(currSCCB->Sccb_scsistat != ABORT_ST)
+				{
+					pCurrCard->discQCount--;
+					pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]] = NULL;
+				}
+			}
+		}
+
+      queueSelectFail(&BL_Card[p_card],p_card);
+	}
+
+#if defined(WIDE_SCSI)
+	WRW_HARPOON((port+hp_fiforead), (USHORT) 0x00);
+#endif
+
+
+	our_target = (UCHAR)(RD_HARPOON(port+hp_select_id) >> 4);
+	currTar_Info = &sccbMgrTbl[p_card][our_target];
+
+
+	msgRetryCount = 0;
+	do
+	{
+		message = GetTarLun(port, p_card, our_target, pCurrCard, &tag, &lun);
+		if(message == FALSE)
+		{
+			msgRetryCount++;
+			if(msgRetryCount == 1)
+			{
+				SendMsg(port, SMPARITY);
+			}
+			else
+			{
+				SendMsg(port, SMDEV_RESET);
+
+				sssyncv(port, our_target, NARROW_SCSI,currTar_Info);
+
+				if (sccbMgrTbl[p_card][our_target].TarEEValue & EE_SYNC_MASK) 
+				{
+			
+					sccbMgrTbl[p_card][our_target].TarStatus &= ~TAR_SYNC_MASK;
+
+				}
+
+				if (sccbMgrTbl[p_card][our_target].TarEEValue & EE_WIDE_SCSI) 
+				{
+
+					sccbMgrTbl[p_card][our_target].TarStatus &= ~TAR_WIDE_MASK;
+				}
+
+
+				queueFlushTargSccb(p_card, our_target, SCCB_COMPLETE);
+				SccbMgrTableInitTarget(p_card,our_target);
+				return;
+			}
+		}
+	}while(message == FALSE);
+
+
+
+	if(((pCurrCard->globalFlags & F_CONLUN_IO) &&
+		((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+	{
+		currTar_Info->TarLUNBusy[lun] = TRUE;
+		pCurrCard->currentSCCB = pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[lun]];
+		if(pCurrCard->currentSCCB != NULL)
+		{
+			ACCEPT_MSG(port);
+		}
+		else 
+		{
+			ACCEPT_MSG_ATN(port);
+		}
+	}
+	else
+	{
+		currTar_Info->TarLUNBusy[0] = TRUE;
+
+
+		if (tag)
+		{
+			if (pCurrCard->discQ_Tbl[tag] != NULL)
+			{
+				pCurrCard->currentSCCB = pCurrCard->discQ_Tbl[tag];
+		 		currTar_Info->TarTagQ_Cnt--;
+				ACCEPT_MSG(port);
+			}
+			else
+			{
+			ACCEPT_MSG_ATN(port);
+			}
+		}else
+		{
+			pCurrCard->currentSCCB = pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]];
+			if(pCurrCard->currentSCCB != NULL)
+			{
+				ACCEPT_MSG(port);
+			}
+			else 
+			{
+				ACCEPT_MSG_ATN(port);
+			}
+		}
+	}
+
+	if(pCurrCard->currentSCCB != NULL)
+	{
+		if(pCurrCard->currentSCCB->Sccb_scsistat == ABORT_ST)
+		{
+		/* During Abort Tag command, the target could have got re-selected
+			and completed the command. Check the select Q and remove the CCB
+			if it is in the Select Q */
+			queueFindSccb(pCurrCard->currentSCCB, p_card);
+		}
+	}
+
+
+   while (!(RDW_HARPOON((port+hp_intstat)) & (PHASE | RESET)) &&
+	  !(RD_HARPOON(port+hp_scsisig) & SCSI_REQ) &&
+	  (RD_HARPOON(port+hp_scsisig) & SCSI_BSY)) ;
+}
+
+#if defined(DOS)
+UCHAR GetTarLun(USHORT port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tag, PUCHAR lun)
+#else
+UCHAR GetTarLun(ULONG port, UCHAR p_card, UCHAR our_target, PSCCBcard pCurrCard, PUCHAR tag, PUCHAR lun)
+#endif
+{
+   UCHAR message;
+   PSCCBMgr_tar_info currTar_Info;
+
+
+	currTar_Info = &sccbMgrTbl[p_card][our_target];
+	*tag = 0;
+
+
+	while(!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ))
+	{
+		if (! (RD_HARPOON(port+hp_scsisig) & SCSI_BSY))
+		{
+
+			WRW_HARPOON((port+hp_intstat), PHASE);
+			return(TRUE);
+		}
+	}
+
+	WRW_HARPOON((port+hp_intstat), PHASE);
+	if ((RD_HARPOON(port+hp_scsisig) & S_SCSI_PHZ) == S_MSGI_PH)
+	{
+
+		message = sfm(port,pCurrCard->currentSCCB);
+		if (message)
+		{
+
+			if (message <= (0x80 | LUN_MASK))
+			{
+				*lun = message & (UCHAR)LUN_MASK;
+
+#if !defined(DOS)
+				if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_TRYING)
+				{
+					if (currTar_Info->TarTagQ_Cnt != 0)
+					{
+
+						if (!(currTar_Info->TarLUN_CA))
+						{
+							ACCEPT_MSG(port);    /*Release the ACK for ID msg. */
+
+
+							message = sfm(port,pCurrCard->currentSCCB);
+							if (message)
+							{
+								ACCEPT_MSG(port);
+							}
+
+							else
+   							return(FALSE);
+
+							*tag = sfm(port,pCurrCard->currentSCCB);
+
+							if (!(*tag)) return(FALSE);
+
+						} /*C.A. exists! */
+
+					} /*End Q cnt != 0 */
+
+				} /*End Tag cmds supported! */
+#endif /* !DOS */
+
+			} /*End valid ID message.  */
+
+			else
+			{
+
+				ACCEPT_MSG_ATN(port);
+			}
+
+		} /* End good id message. */
+
+		else
+		{
+
+			return(FALSE);
+		}
+	}
+	else
+	{
+		ACCEPT_MSG_ATN(port);
+		return(TRUE);
+	}
+	return(TRUE);
+}
+
+
+#if defined(DOS)
+void SendMsg(USHORT port, UCHAR message)
+#else
+void SendMsg(ULONG port, UCHAR message)
+#endif
+{
+	while(!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ))
+	{
+		if (! (RD_HARPOON(port+hp_scsisig) & SCSI_BSY))
+		{
+
+			WRW_HARPOON((port+hp_intstat), PHASE);
+			return;
+		}
+	}
+
+	WRW_HARPOON((port+hp_intstat), PHASE);
+	if ((RD_HARPOON(port+hp_scsisig) & S_SCSI_PHZ) == S_MSGO_PH)
+	{
+		WRW_HARPOON((port+hp_intstat), (BUS_FREE | PHASE | XFER_CNT_0));
+
+
+		WR_HARPOON(port+hp_portctrl_0, SCSI_BUS_EN);
+
+		WR_HARPOON(port+hp_scsidata_0,message);
+
+		WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+
+		ACCEPT_MSG(port);
+
+		WR_HARPOON(port+hp_portctrl_0, 0x00);
+
+		if ((message == SMABORT) || (message == SMDEV_RESET) ||
+				(message == SMABORT_TAG) )
+		{
+			while(!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | PHASE))) {}
+
+			if (RDW_HARPOON((port+hp_intstat)) & BUS_FREE)
+			{
+			WRW_HARPOON((port+hp_intstat), BUS_FREE);
+			}
+		}
+	}
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sdecm
+ *
+ * Description: Determine the proper responce to the message from the
+ *              target device.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void sdecm(UCHAR message, USHORT port, UCHAR p_card)
+#else
+void sdecm(UCHAR message, ULONG port, UCHAR p_card)
+#endif
+{
+	PSCCB currSCCB;
+	PSCCBcard CurrCard;
+	PSCCBMgr_tar_info currTar_Info;
+
+	CurrCard = &BL_Card[p_card];
+	currSCCB = CurrCard->currentSCCB;
+
+	currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+	if (message == SMREST_DATA_PTR)
+	{
+		if (!(currSCCB->Sccb_XferState & F_NO_DATA_YET))
+		{
+			currSCCB->Sccb_ATC = currSCCB->Sccb_savedATC;
+
+			hostDataXferRestart(currSCCB);
+		}
+
+		ACCEPT_MSG(port);
+		WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+	}
+
+	else if (message == SMCMD_COMP)
+	{
+
+
+		if (currSCCB->Sccb_scsistat == SELECT_Q_ST)
+		{
+			currTar_Info->TarStatus &= ~(UCHAR)TAR_TAG_Q_MASK;
+			currTar_Info->TarStatus |= (UCHAR)TAG_Q_REJECT;
+		}
+
+		ACCEPT_MSG(port);
+
+	}
+
+	else if ((message == SMNO_OP) || (message >= SMIDENT) 
+			|| (message == SMINIT_RECOVERY) || (message == SMREL_RECOVERY))
+	{
+
+		ACCEPT_MSG(port);
+		WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+	}
+
+	else if (message == SMREJECT)
+	{
+
+		if ((currSCCB->Sccb_scsistat == SELECT_SN_ST) ||
+				(currSCCB->Sccb_scsistat == SELECT_WN_ST) ||
+				((currTar_Info->TarStatus & TAR_SYNC_MASK) == SYNC_TRYING ) ||
+				((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_TRYING ) )
+
+		{
+			WRW_HARPOON((port+hp_intstat), BUS_FREE);
+
+			ACCEPT_MSG(port);
+
+
+			while ((!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) &&
+				(!(RDW_HARPOON((port+hp_intstat)) & BUS_FREE))) {}
+
+			if(currSCCB->Lun == 0x00)
+			{
+				if ((currSCCB->Sccb_scsistat == SELECT_SN_ST))
+				{
+
+					currTar_Info->TarStatus |= (UCHAR)SYNC_SUPPORTED;
+
+					currTar_Info->TarEEValue &= ~EE_SYNC_MASK;
+				}
+
+#if defined(WIDE_SCSI)
+				else if ((currSCCB->Sccb_scsistat == SELECT_WN_ST))
+				{
+
+
+					currTar_Info->TarStatus = (currTar_Info->TarStatus &
+													~WIDE_ENABLED) | WIDE_NEGOCIATED;
+
+					currTar_Info->TarEEValue &= ~EE_WIDE_SCSI;
+
+				}
+#endif
+
+				else if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_TRYING )
+				{
+					currTar_Info->TarStatus = (currTar_Info->TarStatus &
+													~(UCHAR)TAR_TAG_Q_MASK) | TAG_Q_REJECT;
+
+
+					currSCCB->ControlByte &= ~F_USE_CMD_Q;
+					CurrCard->discQCount--;
+					CurrCard->discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+					currSCCB->Sccb_tag = 0x00;
+
+				}
+			}
+
+			if (RDW_HARPOON((port+hp_intstat)) & BUS_FREE)
+			{
+
+
+				if(currSCCB->Lun == 0x00)
+				{
+					WRW_HARPOON((port+hp_intstat), BUS_FREE);
+					CurrCard->globalFlags |= F_NEW_SCCB_CMD;
+				}
+			}
+
+			else 
+			{
+
+				if((CurrCard->globalFlags & F_CONLUN_IO) &&
+					((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+					currTar_Info->TarLUNBusy[currSCCB->Lun] = TRUE;
+				else
+					currTar_Info->TarLUNBusy[0] = TRUE;
+
+
+				currSCCB->ControlByte &= ~(UCHAR)F_USE_CMD_Q;
+
+				WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+
+			}
+		}
+
+		else
+		{
+			ACCEPT_MSG(port);
+
+			while ((!(RD_HARPOON(port+hp_scsisig) & SCSI_REQ)) &&
+				(!(RDW_HARPOON((port+hp_intstat)) & BUS_FREE))) {}
+	
+			if (!(RDW_HARPOON((port+hp_intstat)) & BUS_FREE))
+			{
+				WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+			}
+		}
+	}
+
+	else if (message == SMEXT)
+	{
+
+		ACCEPT_MSG(port);
+		shandem(port,p_card,currSCCB);
+	}
+
+	else if (message == SMIGNORWR)
+	{
+
+		ACCEPT_MSG(port);          /* ACK the RESIDUE MSG */
+
+		message = sfm(port,currSCCB);
+
+		if(currSCCB->Sccb_scsimsg != SMPARITY)
+			ACCEPT_MSG(port);
+		WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+	}
+
+
+	else
+	{
+
+		currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
+		currSCCB->Sccb_scsimsg = SMREJECT;
+
+		ACCEPT_MSG_ATN(port);
+		WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+	}
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: shandem
+ *
+ * Description: Decide what to do with the extended message.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void shandem(USHORT port, UCHAR p_card, PSCCB pCurrSCCB)
+#else
+void shandem(ULONG port, UCHAR p_card, PSCCB pCurrSCCB)
+#endif
+{
+	UCHAR length,message;
+
+	length = sfm(port,pCurrSCCB);
+	if (length) 
+	{
+
+		ACCEPT_MSG(port);
+		message = sfm(port,pCurrSCCB);
+		if (message) 
+		{
+
+			if (message == SMSYNC) 
+			{
+
+				if (length == 0x03)
+				{
+
+					ACCEPT_MSG(port);
+					stsyncn(port,p_card);
+				}
+				else 
+				{
+
+					pCurrSCCB->Sccb_scsimsg = SMREJECT;
+					ACCEPT_MSG_ATN(port);
+				}
+			}
+#if defined(WIDE_SCSI)
+			else if (message == SMWDTR) 
+			{
+
+				if (length == 0x02)
+				{
+
+					ACCEPT_MSG(port);
+					stwidn(port,p_card);
+				}
+				else 
+				{
+
+					pCurrSCCB->Sccb_scsimsg = SMREJECT;
+					ACCEPT_MSG_ATN(port);
+
+					WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+				}
+			}
+#endif
+			else 
+			{
+
+				pCurrSCCB->Sccb_scsimsg = SMREJECT;
+				ACCEPT_MSG_ATN(port);
+
+				WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+			}
+		}
+		else
+		{
+			if(pCurrSCCB->Sccb_scsimsg != SMPARITY)
+				ACCEPT_MSG(port);
+			WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+		}
+	}
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sisyncn
+ *
+ * Description: Read in a message byte from the SCSI bus, and check
+ *              for a parity error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR sisyncn(USHORT port, UCHAR p_card, UCHAR syncFlag)
+#else
+UCHAR sisyncn(ULONG port, UCHAR p_card, UCHAR syncFlag)
+#endif
+{
+   PSCCB currSCCB;
+   PSCCBMgr_tar_info currTar_Info;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+   currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+   if (!((currTar_Info->TarStatus & TAR_SYNC_MASK) == SYNC_TRYING)) {
+
+
+      WRW_HARPOON((port+ID_MSG_STRT),
+                 (MPM_OP+AMSG_OUT+(currSCCB->Sccb_idmsg & ~(UCHAR)DISC_PRIV)));
+
+      WRW_HARPOON((port+ID_MSG_STRT+2),BRH_OP+ALWAYS+CMDPZ);
+
+      WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+SMEXT ));
+      WRW_HARPOON((port+SYNC_MSGS+2), (MPM_OP+AMSG_OUT+0x03  ));
+      WRW_HARPOON((port+SYNC_MSGS+4), (MPM_OP+AMSG_OUT+SMSYNC));
+
+
+      if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_20MB)
+
+	 WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+ 12));
+
+      else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_10MB)
+
+	 WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+ 25));
+
+      else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_5MB)
+
+	 WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+ 50));
+
+      else
+	 WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+ 00));
+
+
+      WRW_HARPOON((port+SYNC_MSGS+8), (RAT_OP                ));
+      WRW_HARPOON((port+SYNC_MSGS+10),(MPM_OP+AMSG_OUT+DEFAULT_OFFSET));
+      WRW_HARPOON((port+SYNC_MSGS+12),(BRH_OP+ALWAYS+NP      ));
+
+
+		if(syncFlag == FALSE)
+		{
+		   WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+	      currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+   	      ~(UCHAR)TAR_SYNC_MASK) | (UCHAR)SYNC_TRYING);
+		}
+		else
+		{
+		   WR_HARPOON(port+hp_autostart_3, (AUTO_IMMED + CMD_ONLY_STRT));
+		}
+
+
+      return(TRUE);
+      }
+
+   else {
+
+      currTar_Info->TarStatus |=	 (UCHAR)SYNC_SUPPORTED;
+      currTar_Info->TarEEValue &= ~EE_SYNC_MASK;
+      return(FALSE);
+      }
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: stsyncn
+ *
+ * Description: The has sent us a Sync Nego message so handle it as
+ *              necessary.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void stsyncn(USHORT port, UCHAR p_card)
+#else
+void stsyncn(ULONG port, UCHAR p_card)
+#endif
+{
+   UCHAR sync_msg,offset,sync_reg,our_sync_msg;
+   PSCCB currSCCB;
+   PSCCBMgr_tar_info currTar_Info;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+   currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+   sync_msg = sfm(port,currSCCB);
+
+	if((sync_msg == 0x00) && (currSCCB->Sccb_scsimsg == SMPARITY))
+	{
+		WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+		return;
+	}
+
+   ACCEPT_MSG(port);
+
+
+   offset = sfm(port,currSCCB);
+
+	if((offset == 0x00) && (currSCCB->Sccb_scsimsg == SMPARITY))
+	{
+		WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+		return;
+	}
+
+   if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_20MB)
+
+      our_sync_msg = 12;              /* Setup our Message to 20mb/s */
+
+   else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_10MB)
+
+      our_sync_msg = 25;              /* Setup our Message to 10mb/s */
+
+   else if ((currTar_Info->TarEEValue & EE_SYNC_MASK) == EE_SYNC_5MB)
+
+      our_sync_msg = 50;              /* Setup our Message to 5mb/s */
+   else
+
+      our_sync_msg = 0;               /* Message = Async */
+
+   if (sync_msg < our_sync_msg) {
+      sync_msg = our_sync_msg;    /*if faster, then set to max. */
+      }
+
+   if (offset == ASYNC)
+      sync_msg = ASYNC;
+
+   if (offset > MAX_OFFSET)
+      offset = MAX_OFFSET;
+
+   sync_reg = 0x00;
+
+   if (sync_msg > 12)
+
+      sync_reg = 0x20;        /* Use 10MB/s */
+
+   if (sync_msg > 25)
+
+      sync_reg = 0x40;        /* Use 6.6MB/s */
+
+   if (sync_msg > 38)
+
+      sync_reg = 0x60;        /* Use 5MB/s */
+
+   if (sync_msg > 50)
+
+      sync_reg = 0x80;        /* Use 4MB/s */
+
+   if (sync_msg > 62)
+
+      sync_reg = 0xA0;        /* Use 3.33MB/s */
+
+   if (sync_msg > 75)
+
+      sync_reg = 0xC0;        /* Use 2.85MB/s */
+
+   if (sync_msg > 87)
+
+      sync_reg = 0xE0;        /* Use 2.5MB/s */
+
+   if (sync_msg > 100) {
+
+      sync_reg = 0x00;        /* Use ASYNC */
+      offset = 0x00;
+      }
+
+
+#if defined(WIDE_SCSI)
+   if (currTar_Info->TarStatus & WIDE_ENABLED)
+
+      sync_reg |= offset;
+
+   else
+
+      sync_reg |= (offset | NARROW_SCSI);
+
+#else
+   sync_reg |= (offset | NARROW_SCSI);
+#endif
+
+   sssyncv(port,currSCCB->TargID,sync_reg,currTar_Info);
+
+
+   if (currSCCB->Sccb_scsistat == SELECT_SN_ST) {
+
+
+      ACCEPT_MSG(port);
+
+      currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+         ~(UCHAR)TAR_SYNC_MASK) | (UCHAR)SYNC_SUPPORTED);
+
+      WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+      }
+
+   else {
+
+
+      ACCEPT_MSG_ATN(port);
+
+      sisyncr(port,sync_msg,offset);
+
+      currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+         ~(UCHAR)TAR_SYNC_MASK) | (UCHAR)SYNC_SUPPORTED);
+      }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sisyncr
+ *
+ * Description: Answer the targets sync message.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void sisyncr(USHORT port,UCHAR sync_pulse, UCHAR offset)
+#else
+void sisyncr(ULONG port,UCHAR sync_pulse, UCHAR offset)
+#endif
+{
+   ARAM_ACCESS(port);
+   WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+SMEXT ));
+   WRW_HARPOON((port+SYNC_MSGS+2), (MPM_OP+AMSG_OUT+0x03  ));
+   WRW_HARPOON((port+SYNC_MSGS+4), (MPM_OP+AMSG_OUT+SMSYNC));
+   WRW_HARPOON((port+SYNC_MSGS+6), (MPM_OP+AMSG_OUT+sync_pulse));
+   WRW_HARPOON((port+SYNC_MSGS+8), (RAT_OP                ));
+   WRW_HARPOON((port+SYNC_MSGS+10),(MPM_OP+AMSG_OUT+offset));
+   WRW_HARPOON((port+SYNC_MSGS+12),(BRH_OP+ALWAYS+NP      ));
+   SGRAM_ACCESS(port);
+
+   WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+   WRW_HARPOON((port+hp_intstat), CLR_ALL_INT_1);
+
+   WR_HARPOON(port+hp_autostart_3, (AUTO_IMMED+CMD_ONLY_STRT));
+
+   while (!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | AUTO_INT))) {}
+}
+
+
+
+#if defined(WIDE_SCSI)
+
+/*---------------------------------------------------------------------
+ *
+ * Function: siwidn
+ *
+ * Description: Read in a message byte from the SCSI bus, and check
+ *              for a parity error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR siwidn(USHORT port, UCHAR p_card)
+#else
+UCHAR siwidn(ULONG port, UCHAR p_card)
+#endif
+{
+   PSCCB currSCCB;
+   PSCCBMgr_tar_info currTar_Info;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+   currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+   if (!((currTar_Info->TarStatus & TAR_WIDE_MASK) == WIDE_NEGOCIATED)) {
+
+
+      WRW_HARPOON((port+ID_MSG_STRT),
+	              (MPM_OP+AMSG_OUT+(currSCCB->Sccb_idmsg & ~(UCHAR)DISC_PRIV)));
+
+      WRW_HARPOON((port+ID_MSG_STRT+2),BRH_OP+ALWAYS+CMDPZ);
+
+      WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+SMEXT ));
+      WRW_HARPOON((port+SYNC_MSGS+2), (MPM_OP+AMSG_OUT+0x02  ));
+      WRW_HARPOON((port+SYNC_MSGS+4), (MPM_OP+AMSG_OUT+SMWDTR));
+      WRW_HARPOON((port+SYNC_MSGS+6), (RAT_OP                ));
+      WRW_HARPOON((port+SYNC_MSGS+8), (MPM_OP+AMSG_OUT+ SM16BIT));
+      WRW_HARPOON((port+SYNC_MSGS+10),(BRH_OP+ALWAYS+NP      ));
+
+      WR_HARPOON(port+hp_autostart_3, (SELECT+SELCHK_STRT));
+
+
+      currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+         ~(UCHAR)TAR_WIDE_MASK) | (UCHAR)WIDE_ENABLED);
+
+      return(TRUE);
+      }
+
+   else {
+
+      currTar_Info->TarStatus = ((currTar_Info->TarStatus &
+               ~(UCHAR)TAR_WIDE_MASK) | WIDE_NEGOCIATED);
+
+      currTar_Info->TarEEValue &= ~EE_WIDE_SCSI;
+      return(FALSE);
+      }
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: stwidn
+ *
+ * Description: The has sent us a Wide Nego message so handle it as
+ *              necessary.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void stwidn(USHORT port, UCHAR p_card)
+#else
+void stwidn(ULONG port, UCHAR p_card)
+#endif
+{
+   UCHAR width;
+   PSCCB currSCCB;
+   PSCCBMgr_tar_info currTar_Info;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+   currTar_Info = &sccbMgrTbl[p_card][currSCCB->TargID];
+
+   width = sfm(port,currSCCB);
+
+	if((width == 0x00) && (currSCCB->Sccb_scsimsg == SMPARITY))
+	{
+		WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+		return;
+	}
+
+
+   if (!(currTar_Info->TarEEValue & EE_WIDE_SCSI))
+      width = 0;
+
+   if (width) {
+      currTar_Info->TarStatus |= WIDE_ENABLED;
+      width = 0;
+      }
+   else {
+      width = NARROW_SCSI;
+      currTar_Info->TarStatus &= ~WIDE_ENABLED;
+      }
+
+
+   sssyncv(port,currSCCB->TargID,width,currTar_Info);
+
+
+   if (currSCCB->Sccb_scsistat == SELECT_WN_ST)
+	{
+
+
+
+      currTar_Info->TarStatus |=	 WIDE_NEGOCIATED;
+
+	   if (!((currTar_Info->TarStatus & TAR_SYNC_MASK) == SYNC_SUPPORTED))
+		{
+	      ACCEPT_MSG_ATN(port);
+		   ARAM_ACCESS(port);
+	     	sisyncn(port,p_card, TRUE);
+	      currSCCB->Sccb_scsistat = SELECT_SN_ST;
+		   SGRAM_ACCESS(port);
+		}
+		else
+		{
+	      ACCEPT_MSG(port);
+  		   WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+		}
+   }
+
+   else {
+
+
+      ACCEPT_MSG_ATN(port);
+
+      if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
+      	 width = SM16BIT;
+      else
+      	 width = SM8BIT;
+
+      siwidr(port,width);
+
+      currTar_Info->TarStatus |= (WIDE_NEGOCIATED | WIDE_ENABLED);
+      }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: siwidr
+ *
+ * Description: Answer the targets Wide nego message.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void siwidr(USHORT port, UCHAR width)
+#else
+void siwidr(ULONG port, UCHAR width)
+#endif
+{
+   ARAM_ACCESS(port);
+   WRW_HARPOON((port+SYNC_MSGS+0), (MPM_OP+AMSG_OUT+SMEXT ));
+   WRW_HARPOON((port+SYNC_MSGS+2), (MPM_OP+AMSG_OUT+0x02  ));
+   WRW_HARPOON((port+SYNC_MSGS+4), (MPM_OP+AMSG_OUT+SMWDTR));
+   WRW_HARPOON((port+SYNC_MSGS+6), (RAT_OP                ));
+   WRW_HARPOON((port+SYNC_MSGS+8),(MPM_OP+AMSG_OUT+width));
+   WRW_HARPOON((port+SYNC_MSGS+10),(BRH_OP+ALWAYS+NP      ));
+   SGRAM_ACCESS(port);
+
+   WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+   WRW_HARPOON((port+hp_intstat), CLR_ALL_INT_1);
+
+   WR_HARPOON(port+hp_autostart_3, (AUTO_IMMED+CMD_ONLY_STRT));
+
+   while (!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | AUTO_INT))) {}
+}
+
+#endif
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sssyncv
+ *
+ * Description: Write the desired value to the Sync Regisiter for the
+ *              ID specified.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void sssyncv(USHORT p_port, UCHAR p_id, UCHAR p_sync_value,PSCCBMgr_tar_info currTar_Info)
+#else
+void sssyncv(ULONG p_port, UCHAR p_id, UCHAR p_sync_value,PSCCBMgr_tar_info currTar_Info)
+#endif
+{
+   UCHAR index;
+
+   index = p_id;
+
+   switch (index) {
+
+      case 0:
+	 index = 12;             /* hp_synctarg_0 */
+	 break;
+      case 1:
+	 index = 13;             /* hp_synctarg_1 */
+	 break;
+      case 2:
+	 index = 14;             /* hp_synctarg_2 */
+	 break;
+      case 3:
+	 index = 15;             /* hp_synctarg_3 */
+	 break;
+      case 4:
+	 index = 8;              /* hp_synctarg_4 */
+	 break;
+      case 5:
+	 index = 9;              /* hp_synctarg_5 */
+	 break;
+      case 6:
+	 index = 10;             /* hp_synctarg_6 */
+	 break;
+      case 7:
+	 index = 11;             /* hp_synctarg_7 */
+	 break;
+      case 8:
+	 index = 4;              /* hp_synctarg_8 */
+	 break;
+      case 9:
+	 index = 5;              /* hp_synctarg_9 */
+	 break;
+      case 10:
+	 index = 6;              /* hp_synctarg_10 */
+	 break;
+      case 11:
+	 index = 7;              /* hp_synctarg_11 */
+	 break;
+      case 12:
+	 index = 0;              /* hp_synctarg_12 */
+	 break;
+      case 13:
+	 index = 1;              /* hp_synctarg_13 */
+	 break;
+      case 14:
+	 index = 2;              /* hp_synctarg_14 */
+	 break;
+      case 15:
+	 index = 3;              /* hp_synctarg_15 */
+
+      }
+
+   WR_HARPOON(p_port+hp_synctarg_base+index, p_sync_value);
+
+	currTar_Info->TarSyncCtrl = p_sync_value;
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sresb
+ *
+ * Description: Reset the desired card's SCSI bus.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void sresb(USHORT port, UCHAR p_card)
+#else
+void sresb(ULONG port, UCHAR p_card)
+#endif
+{
+   UCHAR scsiID, i;
+
+   PSCCBMgr_tar_info currTar_Info;
+
+   WR_HARPOON(port+hp_page_ctrl,
+      (RD_HARPOON(port+hp_page_ctrl) | G_INT_DISABLE));
+   WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+
+   WR_HARPOON(port+hp_scsictrl_0, SCSI_RST);
+
+   scsiID = RD_HARPOON(port+hp_seltimeout);
+   WR_HARPOON(port+hp_seltimeout,TO_5ms);
+   WRW_HARPOON((port+hp_intstat), TIMEOUT);
+
+   WR_HARPOON(port+hp_portctrl_0,(SCSI_PORT | START_TO));
+
+   while (!(RDW_HARPOON((port+hp_intstat)) & TIMEOUT)) {}
+
+   WR_HARPOON(port+hp_seltimeout,scsiID);
+
+   WR_HARPOON(port+hp_scsictrl_0, ENA_SCAM_SEL);
+
+   Wait(port, TO_5ms);
+
+   WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+
+   WR_HARPOON(port+hp_int_mask, (RD_HARPOON(port+hp_int_mask) | 0x00));
+
+   for (scsiID = 0; scsiID < MAX_SCSI_TAR; scsiID++)
+      {
+      currTar_Info = &sccbMgrTbl[p_card][scsiID];
+
+      if (currTar_Info->TarEEValue & EE_SYNC_MASK)
+         {
+	      	currTar_Info->TarSyncCtrl = 0;
+	      	currTar_Info->TarStatus &= ~TAR_SYNC_MASK;
+	      }
+
+      if (currTar_Info->TarEEValue & EE_WIDE_SCSI)
+         {
+      	currTar_Info->TarStatus &= ~TAR_WIDE_MASK;
+      	}
+
+      sssyncv(port, scsiID, NARROW_SCSI,currTar_Info);
+
+      SccbMgrTableInitTarget(p_card, scsiID);
+      }
+
+   BL_Card[p_card].scanIndex = 0x00;
+   BL_Card[p_card].currentSCCB = NULL;
+   BL_Card[p_card].globalFlags &= ~(F_TAG_STARTED | F_HOST_XFER_ACT 
+													| F_NEW_SCCB_CMD);
+   BL_Card[p_card].cmdCounter  = 0x00;
+	BL_Card[p_card].discQCount = 0x00;
+   BL_Card[p_card].tagQ_Lst = 0x01; 
+
+	for(i = 0; i < QUEUE_DEPTH; i++)
+		BL_Card[p_card].discQ_Tbl[i] = NULL;
+
+   WR_HARPOON(port+hp_page_ctrl,
+      (RD_HARPOON(port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: ssenss
+ *
+ * Description: Setup for the Auto Sense command.
+ *
+ *---------------------------------------------------------------------*/
+void ssenss(PSCCBcard pCurrCard)
+{
+   UCHAR i;
+   PSCCB currSCCB;
+
+   currSCCB = pCurrCard->currentSCCB;
+
+
+   currSCCB->Save_CdbLen = currSCCB->CdbLength;
+
+   for (i = 0; i < 6; i++) {
+
+      currSCCB->Save_Cdb[i] = currSCCB->Cdb[i];
+      }
+
+   currSCCB->CdbLength = SIX_BYTE_CMD;
+   currSCCB->Cdb[0]    = SCSI_REQUEST_SENSE;
+   currSCCB->Cdb[1]    = currSCCB->Cdb[1] & (UCHAR)0xE0; /*Keep LUN. */
+   currSCCB->Cdb[2]    = 0x00;
+   currSCCB->Cdb[3]    = 0x00;
+   currSCCB->Cdb[4]    = currSCCB->RequestSenseLength;
+   currSCCB->Cdb[5]    = 0x00;
+
+   currSCCB->Sccb_XferCnt = (unsigned long)currSCCB->RequestSenseLength;
+
+   currSCCB->Sccb_ATC = 0x00;
+
+   currSCCB->Sccb_XferState |= F_AUTO_SENSE;
+
+   currSCCB->Sccb_XferState &= ~F_SG_XFER;
+
+   currSCCB->Sccb_idmsg = currSCCB->Sccb_idmsg & ~(UCHAR)DISC_PRIV;
+
+   currSCCB->ControlByte = 0x00;
+
+   currSCCB->Sccb_MGRFlags &= F_STATUSLOADED;
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sxfrp
+ *
+ * Description: Transfer data into the bit bucket until the device
+ *              decides to switch phase.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void sxfrp(USHORT p_port, UCHAR p_card)
+#else
+void sxfrp(ULONG p_port, UCHAR p_card)
+#endif
+{
+   UCHAR curr_phz;
+
+
+   DISABLE_AUTO(p_port);
+
+   if (BL_Card[p_card].globalFlags & F_HOST_XFER_ACT) {
+
+      hostDataXferAbort(p_port,p_card,BL_Card[p_card].currentSCCB);
+
+      }
+
+   /* If the Automation handled the end of the transfer then do not
+      match the phase or we will get out of sync with the ISR.       */
+
+   if (RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | XFER_CNT_0 | AUTO_INT))
+      return;
+
+   WR_HARPOON(p_port+hp_xfercnt_0, 0x00);
+
+   curr_phz = RD_HARPOON(p_port+hp_scsisig) & (UCHAR)S_SCSI_PHZ;
+
+   WRW_HARPOON((p_port+hp_intstat), XFER_CNT_0);
+
+
+   WR_HARPOON(p_port+hp_scsisig, curr_phz);
+
+   while ( !(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RESET)) &&
+      (curr_phz == (RD_HARPOON(p_port+hp_scsisig) & (UCHAR)S_SCSI_PHZ)) )
+      {
+      if (curr_phz & (UCHAR)SCSI_IOBIT)
+         {
+      	WR_HARPOON(p_port+hp_portctrl_0, (SCSI_PORT | HOST_PORT | SCSI_INBIT));
+
+	      if (!(RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY))
+            {
+	         RD_HARPOON(p_port+hp_fifodata_0);
+	         }
+	      }
+      else
+         {
+      	WR_HARPOON(p_port+hp_portctrl_0, (SCSI_PORT | HOST_PORT | HOST_WRT));
+   	   if (RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY)
+            {
+	         WR_HARPOON(p_port+hp_fifodata_0,0xFA);
+	         }
+	      }
+      } /* End of While loop for padding data I/O phase */
+
+      while ( !(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RESET)))
+         {
+         if (RD_HARPOON(p_port+hp_scsisig) & SCSI_REQ)
+      	   break;
+         }
+
+      WR_HARPOON(p_port+hp_portctrl_0, (SCSI_PORT | HOST_PORT | SCSI_INBIT));
+      while (!(RD_HARPOON(p_port+hp_xferstat) & FIFO_EMPTY))
+         {
+         RD_HARPOON(p_port+hp_fifodata_0);
+         }
+
+      if ( !(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RESET)))
+         {
+         WR_HARPOON(p_port+hp_autostart_0, (AUTO_IMMED+DISCONNECT_START));
+         while (!(RDW_HARPOON((p_port+hp_intstat)) & AUTO_INT)) {}
+
+         if (RDW_HARPOON((p_port+hp_intstat)) & (ICMD_COMP | ITAR_DISC))
+   	   while (!(RDW_HARPOON((p_port+hp_intstat)) & (BUS_FREE | RSEL))) ;
+         }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: schkdd
+ *
+ * Description: Make sure data has been flushed from both FIFOs and abort
+ *              the operations if necessary.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void schkdd(USHORT port, UCHAR p_card)
+#else
+void schkdd(ULONG port, UCHAR p_card)
+#endif
+{
+   USHORT TimeOutLoop;
+	UCHAR sPhase;
+
+   PSCCB currSCCB;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+
+
+   if ((currSCCB->Sccb_scsistat != DATA_OUT_ST) &&
+       (currSCCB->Sccb_scsistat != DATA_IN_ST)) {
+      return;
+      }
+
+
+
+   if (currSCCB->Sccb_XferState & F_ODD_BALL_CNT)
+      {
+
+      currSCCB->Sccb_ATC += (currSCCB->Sccb_XferCnt-1);
+
+      currSCCB->Sccb_XferCnt = 1;
+
+      currSCCB->Sccb_XferState &= ~F_ODD_BALL_CNT;
+      WRW_HARPOON((port+hp_fiforead), (USHORT) 0x00);
+      WR_HARPOON(port+hp_xferstat, 0x00);
+      }
+
+   else
+      {
+
+      currSCCB->Sccb_ATC += currSCCB->Sccb_XferCnt;
+
+      currSCCB->Sccb_XferCnt = 0;
+      }
+
+   if ((RDW_HARPOON((port+hp_intstat)) & PARITY) &&
+      (currSCCB->HostStatus == SCCB_COMPLETE)) {
+
+      currSCCB->HostStatus = SCCB_PARITY_ERR;
+      WRW_HARPOON((port+hp_intstat), PARITY);
+      }
+
+
+   hostDataXferAbort(port,p_card,currSCCB);
+
+
+   while (RD_HARPOON(port+hp_scsisig) & SCSI_ACK) {}
+
+   TimeOutLoop = 0;
+
+   while(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY)
+      {
+      if (RDW_HARPOON((port+hp_intstat)) & BUS_FREE) {
+	      return;
+   	   }
+      if (RD_HARPOON(port+hp_offsetctr) & (UCHAR)0x1F) {
+	      break;
+   	   }
+      if (RDW_HARPOON((port+hp_intstat)) & RESET) {
+	      return;
+   	   }
+      if ((RD_HARPOON(port+hp_scsisig) & SCSI_REQ) || (TimeOutLoop++>0x3000) )
+   	   break;
+      }
+
+	sPhase = RD_HARPOON(port+hp_scsisig) & (SCSI_BSY | S_SCSI_PHZ);
+   if ((!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY))                     ||
+      (RD_HARPOON(port+hp_offsetctr) & (UCHAR)0x1F)                       ||
+      (sPhase == (SCSI_BSY | S_DATAO_PH)) ||
+      (sPhase == (SCSI_BSY | S_DATAI_PH)))
+      {
+
+	   WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+	   if (!(currSCCB->Sccb_XferState & F_ALL_XFERRED))
+         {
+	      if (currSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
+	         phaseDataIn(port,p_card);
+	      	}
+
+	   	else {
+	       phaseDataOut(port,p_card);
+	       	}
+	   	}
+		else
+      	{
+	   	sxfrp(port,p_card);
+	   	if (!(RDW_HARPOON((port+hp_intstat)) &
+		      (BUS_FREE | ICMD_COMP | ITAR_DISC | RESET)))
+         {
+   		WRW_HARPOON((port+hp_intstat), AUTO_INT);
+		   phaseDecode(port,p_card);
+		   }
+	   }
+
+   }
+
+   else {
+      WR_HARPOON(port+hp_portctrl_0, 0x00);
+      }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sinits
+ *
+ * Description: Setup SCCB manager fields in this SCCB.
+ *
+ *---------------------------------------------------------------------*/
+
+void sinits(PSCCB p_sccb, UCHAR p_card)
+{
+   PSCCBMgr_tar_info currTar_Info;
+
+	if((p_sccb->TargID > MAX_SCSI_TAR) || (p_sccb->Lun > MAX_LUN))
+	{
+		return;
+	}
+   currTar_Info = &sccbMgrTbl[p_card][p_sccb->TargID];
+
+   p_sccb->Sccb_XferState     = 0x00;
+   p_sccb->Sccb_XferCnt       = p_sccb->DataLength;
+
+   if ((p_sccb->OperationCode == SCATTER_GATHER_COMMAND) ||
+      (p_sccb->OperationCode == RESIDUAL_SG_COMMAND)) {
+
+      p_sccb->Sccb_SGoffset   = 0;
+      p_sccb->Sccb_XferState  = F_SG_XFER;
+      p_sccb->Sccb_XferCnt    = 0x00;
+      }
+
+   if (p_sccb->DataLength == 0x00)
+
+      p_sccb->Sccb_XferState |= F_ALL_XFERRED;
+
+   if (p_sccb->ControlByte & F_USE_CMD_Q)
+      {
+      if ((currTar_Info->TarStatus & TAR_TAG_Q_MASK) == TAG_Q_REJECT)
+         p_sccb->ControlByte &= ~F_USE_CMD_Q;
+
+      else
+	      currTar_Info->TarStatus |= TAG_Q_TRYING;
+      }
+
+/*      For !single SCSI device in system  & device allow Disconnect
+	or command is tag_q type then send Cmd with Disconnect Enable
+	else send Cmd with Disconnect Disable */
+
+/*
+   if (((!(BL_Card[p_card].globalFlags & F_SINGLE_DEVICE)) &&
+      (currTar_Info->TarStatus & TAR_ALLOW_DISC)) ||
+      (currTar_Info->TarStatus & TAG_Q_TRYING)) {
+*/
+   if ((currTar_Info->TarStatus & TAR_ALLOW_DISC) ||
+      (currTar_Info->TarStatus & TAG_Q_TRYING)) {
+      p_sccb->Sccb_idmsg      = (UCHAR)(SMIDENT | DISC_PRIV) | p_sccb->Lun;
+      }
+
+   else {
+
+      p_sccb->Sccb_idmsg      = (UCHAR)SMIDENT | p_sccb->Lun;
+      }
+
+   p_sccb->HostStatus         = 0x00;
+   p_sccb->TargetStatus       = 0x00;
+   p_sccb->Sccb_tag           = 0x00;
+   p_sccb->Sccb_MGRFlags      = 0x00;
+   p_sccb->Sccb_sgseg         = 0x00;
+   p_sccb->Sccb_ATC           = 0x00;
+   p_sccb->Sccb_savedATC      = 0x00;
+/*
+   p_sccb->SccbVirtDataPtr    = 0x00;
+   p_sccb->Sccb_forwardlink   = NULL;
+   p_sccb->Sccb_backlink      = NULL;
+ */
+   p_sccb->Sccb_scsistat      = BUS_FREE_ST;
+   p_sccb->SccbStatus         = SCCB_IN_PROCESS;
+   p_sccb->Sccb_scsimsg       = SMNO_OP;
+}
+
+
+#ident "$Id: phase.c 1.11 1997/01/31 02:08:49 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   phase.c  $
+ *
+ *   Description:  Functions to intially handle the SCSI bus phase when
+ *                 the target asserts request (and the automation is not
+ *                 enabled to handle the situation).
+ *
+ *   $Date: 1997/01/31 02:08:49 $
+ *
+ *   $Revision: 1.11 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+	/*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+
+#if defined(OS2)
+   extern void (far *s_PhaseTbl[8]) (ULONG, UCHAR);
+#else
+   #if defined(DOS)
+      extern void (*s_PhaseTbl[8]) (USHORT, UCHAR);
+   #else
+      extern void (*s_PhaseTbl[8]) (ULONG, UCHAR);
+   #endif
+#endif
+*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Phase Decode
+ *
+ * Description: Determine the phase and call the appropriate function.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void phaseDecode(USHORT p_port, UCHAR p_card)
+#else
+void phaseDecode(ULONG p_port, UCHAR p_card)
+#endif
+{
+   unsigned char phase_ref;
+#if defined(OS2)
+   void (far *phase) (ULONG, UCHAR);
+#else
+   #if defined(DOS)
+      void (*phase) (USHORT, UCHAR);
+   #else
+      void (*phase) (ULONG, UCHAR);
+   #endif
+#endif
+
+
+   DISABLE_AUTO(p_port);
+
+   phase_ref = (UCHAR) (RD_HARPOON(p_port+hp_scsisig) & S_SCSI_PHZ);
+
+   phase = s_PhaseTbl[phase_ref];
+
+   (*phase)(p_port, p_card);           /* Call the correct phase func */
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Data Out Phase
+ *
+ * Description: Start up both the BusMaster and Xbow.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseDataOut(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseDataOut(USHORT port, UCHAR p_card)
+#else
+void phaseDataOut(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+
+   PSCCB currSCCB;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+   if (currSCCB == NULL)
+      {
+      return;  /* Exit if No SCCB record */
+      }
+
+   currSCCB->Sccb_scsistat = DATA_OUT_ST;
+   currSCCB->Sccb_XferState &= ~(F_HOST_XFER_DIR | F_NO_DATA_YET);
+
+   WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+   WRW_HARPOON((port+hp_intstat), XFER_CNT_0);
+
+   WR_HARPOON(port+hp_autostart_0, (END_DATA+END_DATA_START));
+
+   dataXferProcessor(port, &BL_Card[p_card]);
+
+#if defined(NOBUGBUG)
+   if (RDW_HARPOON((port+hp_intstat)) & XFER_CNT_0)
+      WRW_HARPOON((port+hp_intstat), XFER_CNT_0);
+
+#endif
+
+
+   if (currSCCB->Sccb_XferCnt == 0) {
+
+
+      if ((currSCCB->ControlByte & SCCB_DATA_XFER_OUT) &&
+	 (currSCCB->HostStatus == SCCB_COMPLETE))
+	 currSCCB->HostStatus = SCCB_DATA_OVER_RUN;
+
+      sxfrp(port,p_card);
+      if (!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | RESET)))
+	    phaseDecode(port,p_card);
+      }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Data In Phase
+ *
+ * Description: Startup the BusMaster and the XBOW.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseDataIn(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseDataIn(USHORT port, UCHAR p_card)
+#else
+void phaseDataIn(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+
+   PSCCB currSCCB;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+
+   if (currSCCB == NULL)
+      {
+      return;  /* Exit if No SCCB record */
+      }
+
+
+   currSCCB->Sccb_scsistat = DATA_IN_ST;
+   currSCCB->Sccb_XferState |= F_HOST_XFER_DIR;
+   currSCCB->Sccb_XferState &= ~F_NO_DATA_YET;
+
+   WR_HARPOON(port+hp_portctrl_0, SCSI_PORT);
+
+   WRW_HARPOON((port+hp_intstat), XFER_CNT_0);
+
+   WR_HARPOON(port+hp_autostart_0, (END_DATA+END_DATA_START));
+
+   dataXferProcessor(port, &BL_Card[p_card]);
+
+   if (currSCCB->Sccb_XferCnt == 0) {
+
+
+      if ((currSCCB->ControlByte & SCCB_DATA_XFER_IN) &&
+	 (currSCCB->HostStatus == SCCB_COMPLETE))
+	 currSCCB->HostStatus = SCCB_DATA_OVER_RUN;
+
+      sxfrp(port,p_card);
+      if (!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | RESET)))
+	    phaseDecode(port,p_card);
+
+      }
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Command Phase
+ *
+ * Description: Load the CDB into the automation and start it up.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseCommand(ULONG p_port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseCommand(USHORT p_port, UCHAR p_card)
+#else
+void phaseCommand(ULONG p_port, UCHAR p_card)
+#endif
+#endif
+{
+   PSCCB currSCCB;
+#if defined(DOS)
+   USHORT cdb_reg;
+#else
+   ULONG cdb_reg;
+#endif
+   UCHAR i;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+
+   if (currSCCB->OperationCode == RESET_COMMAND) {
+
+      currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
+      currSCCB->CdbLength = SIX_BYTE_CMD;
+      }
+
+   WR_HARPOON(p_port+hp_scsisig, 0x00);
+
+   ARAM_ACCESS(p_port);
+
+
+   cdb_reg = p_port + CMD_STRT;
+
+   for (i=0; i < currSCCB->CdbLength; i++) {
+
+      if (currSCCB->OperationCode == RESET_COMMAND)
+
+	 WRW_HARPOON(cdb_reg, (MPM_OP + ACOMMAND + 0x00));
+
+      else
+	 WRW_HARPOON(cdb_reg, (MPM_OP + ACOMMAND + currSCCB->Cdb[i]));
+      cdb_reg +=2;
+      }
+
+   if (currSCCB->CdbLength != TWELVE_BYTE_CMD)
+      WRW_HARPOON(cdb_reg, (BRH_OP+ALWAYS+    NP));
+
+   WR_HARPOON(p_port+hp_portctrl_0,(SCSI_PORT));
+
+   currSCCB->Sccb_scsistat = COMMAND_ST;
+
+   WR_HARPOON(p_port+hp_autostart_3, (AUTO_IMMED | CMD_ONLY_STRT));
+   SGRAM_ACCESS(p_port);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Status phase
+ *
+ * Description: Bring in the status and command complete message bytes
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseStatus(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseStatus(USHORT port, UCHAR p_card)
+#else
+void phaseStatus(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+   /* Start-up the automation to finish off this command and let the
+      isr handle the interrupt for command complete when it comes in.
+      We could wait here for the interrupt to be generated?
+    */
+
+   WR_HARPOON(port+hp_scsisig, 0x00);
+
+   WR_HARPOON(port+hp_autostart_0, (AUTO_IMMED+END_DATA_START));
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Phase Message Out
+ *
+ * Description: Send out our message (if we have one) and handle whatever
+ *              else is involed.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseMsgOut(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseMsgOut(USHORT port, UCHAR p_card)
+#else
+void phaseMsgOut(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+	UCHAR message,scsiID;
+	PSCCB currSCCB;
+	PSCCBMgr_tar_info currTar_Info;
+
+	currSCCB = BL_Card[p_card].currentSCCB;
+
+	if (currSCCB != NULL) {
+
+		message = currSCCB->Sccb_scsimsg;
+		scsiID = currSCCB->TargID;
+
+		if (message == SMDEV_RESET) 
+		{
+
+
+			currTar_Info = &sccbMgrTbl[p_card][scsiID];
+			currTar_Info->TarSyncCtrl = 0;
+			sssyncv(port, scsiID, NARROW_SCSI,currTar_Info);
+
+			if (sccbMgrTbl[p_card][scsiID].TarEEValue & EE_SYNC_MASK) 
+			{
+
+				sccbMgrTbl[p_card][scsiID].TarStatus &= ~TAR_SYNC_MASK;
+
+			}
+
+			if (sccbMgrTbl[p_card][scsiID].TarEEValue & EE_WIDE_SCSI) 
+			{
+
+				sccbMgrTbl[p_card][scsiID].TarStatus &= ~TAR_WIDE_MASK;
+			}
+
+
+			queueFlushSccb(p_card,SCCB_COMPLETE);
+			SccbMgrTableInitTarget(p_card,scsiID);
+		}
+		else if (currSCCB->Sccb_scsistat == ABORT_ST)
+		{
+			currSCCB->HostStatus = SCCB_COMPLETE;
+			if(BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] != NULL)
+			{
+				BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+				sccbMgrTbl[p_card][scsiID].TarTagQ_Cnt--;
+			}
+					
+		}
+
+		else if (currSCCB->Sccb_scsistat < COMMAND_ST) 
+		{
+
+
+			if(message == SMNO_OP)
+			{
+				currSCCB->Sccb_MGRFlags |= F_DEV_SELECTED;
+		
+				ssel(port,p_card);
+				return;
+			}
+		}
+		else 
+		{
+
+
+			if (message == SMABORT)
+
+				queueFlushSccb(p_card,SCCB_COMPLETE);
+		}
+
+	}
+	else 
+	{
+		message = SMABORT;
+	}
+
+	WRW_HARPOON((port+hp_intstat), (BUS_FREE | PHASE | XFER_CNT_0));
+
+
+	WR_HARPOON(port+hp_portctrl_0, SCSI_BUS_EN);
+
+	WR_HARPOON(port+hp_scsidata_0,message);
+
+	WR_HARPOON(port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+
+	ACCEPT_MSG(port);
+
+	WR_HARPOON(port+hp_portctrl_0, 0x00);
+
+	if ((message == SMABORT) || (message == SMDEV_RESET) || 
+				(message == SMABORT_TAG) ) 
+	{
+
+		while(!(RDW_HARPOON((port+hp_intstat)) & (BUS_FREE | PHASE))) {}
+
+		if (RDW_HARPOON((port+hp_intstat)) & BUS_FREE) 
+		{
+			WRW_HARPOON((port+hp_intstat), BUS_FREE);
+
+			if (currSCCB != NULL) 
+			{
+
+				if((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+					((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+					sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = FALSE;
+				else
+					sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = FALSE;
+
+				queueCmdComplete(&BL_Card[p_card],currSCCB, p_card);
+			}
+
+			else 
+			{
+				BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+			}
+		}
+
+		else 
+		{
+
+			sxfrp(port,p_card);
+		}
+	}
+
+	else 
+	{
+
+		if(message == SMPARITY)
+		{
+			currSCCB->Sccb_scsimsg = SMNO_OP;
+			WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+		}
+		else
+		{
+			sxfrp(port,p_card);
+		}
+	}
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Message In phase
+ *
+ * Description: Bring in the message and determine what to do with it.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseMsgIn(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseMsgIn(USHORT port, UCHAR p_card)
+#else
+void phaseMsgIn(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+	UCHAR message;
+	PSCCB currSCCB;
+
+	currSCCB = BL_Card[p_card].currentSCCB;
+
+	if (BL_Card[p_card].globalFlags & F_HOST_XFER_ACT) 
+	{
+
+		phaseChkFifo(port, p_card);
+	}
+
+	message = RD_HARPOON(port+hp_scsidata_0);
+	if ((message == SMDISC) || (message == SMSAVE_DATA_PTR)) 
+	{
+
+		WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+END_DATA_START));
+
+	}
+
+	else 
+	{
+
+		message = sfm(port,currSCCB);
+		if (message) 
+		{
+
+
+			sdecm(message,port,p_card);
+
+		}
+		else
+		{
+			if(currSCCB->Sccb_scsimsg != SMPARITY)
+				ACCEPT_MSG(port);
+			WR_HARPOON(port+hp_autostart_1, (AUTO_IMMED+DISCONNECT_START));
+		}
+	}
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Illegal phase
+ *
+ * Description: Target switched to some illegal phase, so all we can do
+ *              is report an error back to the host (if that is possible)
+ *              and send an ABORT message to the misbehaving target.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(OS2)
+void far phaseIllegal(ULONG port, UCHAR p_card)
+#else
+#if defined(DOS)
+void phaseIllegal(USHORT port, UCHAR p_card)
+#else
+void phaseIllegal(ULONG port, UCHAR p_card)
+#endif
+#endif
+{
+   PSCCB currSCCB;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+
+   WR_HARPOON(port+hp_scsisig, RD_HARPOON(port+hp_scsisig));
+   if (currSCCB != NULL) {
+
+      currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
+      currSCCB->Sccb_scsistat = ABORT_ST;
+      currSCCB->Sccb_scsimsg = SMABORT;
+      }
+
+   ACCEPT_MSG_ATN(port);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Phase Check FIFO
+ *
+ * Description: Make sure data has been flushed from both FIFOs and abort
+ *              the operations if necessary.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void phaseChkFifo(USHORT port, UCHAR p_card)
+#else
+void phaseChkFifo(ULONG port, UCHAR p_card)
+#endif
+{
+   ULONG xfercnt;
+   PSCCB currSCCB;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+
+   if (currSCCB->Sccb_scsistat == DATA_IN_ST)
+      {
+
+      while((!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY)) &&
+	      (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY)) {}
+
+
+      if (!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY))
+         {
+	      currSCCB->Sccb_ATC += currSCCB->Sccb_XferCnt;
+
+	      currSCCB->Sccb_XferCnt = 0;
+
+	      if ((RDW_HARPOON((port+hp_intstat)) & PARITY) &&
+	            (currSCCB->HostStatus == SCCB_COMPLETE))
+            {
+	         currSCCB->HostStatus = SCCB_PARITY_ERR;
+	         WRW_HARPOON((port+hp_intstat), PARITY);
+	         }
+
+	      hostDataXferAbort(port,p_card,currSCCB);
+
+	      dataXferProcessor(port, &BL_Card[p_card]);
+
+	      while((!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY)) &&
+	         (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY)) {}
+
+	      }
+      }  /*End Data In specific code. */
+
+
+
+#if defined(DOS)
+   asm { mov dx,port;
+      add dx,hp_xfercnt_2;
+      in  al,dx;
+      dec dx;
+      xor ah,ah;
+      mov word ptr xfercnt+2,ax;
+      in  al,dx;
+      dec dx;
+      mov ah,al;
+      in  al,dx;
+      mov word ptr xfercnt,ax;
+      }
+#else
+   GET_XFER_CNT(port,xfercnt);
+#endif
+
+
+   WR_HARPOON(port+hp_xfercnt_0, 0x00);
+
+
+   WR_HARPOON(port+hp_portctrl_0, 0x00);
+
+   currSCCB->Sccb_ATC += (currSCCB->Sccb_XferCnt - xfercnt);
+
+   currSCCB->Sccb_XferCnt = xfercnt;
+
+   if ((RDW_HARPOON((port+hp_intstat)) & PARITY) &&
+      (currSCCB->HostStatus == SCCB_COMPLETE)) {
+
+      currSCCB->HostStatus = SCCB_PARITY_ERR;
+      WRW_HARPOON((port+hp_intstat), PARITY);
+      }
+
+
+   hostDataXferAbort(port,p_card,currSCCB);
+
+
+   WR_HARPOON(port+hp_fifowrite, 0x00);
+   WR_HARPOON(port+hp_fiforead, 0x00);
+   WR_HARPOON(port+hp_xferstat, 0x00);
+
+   WRW_HARPOON((port+hp_intstat), XFER_CNT_0);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Phase Bus Free
+ *
+ * Description: We just went bus free so figure out if it was
+ *              because of command complete or from a disconnect.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void phaseBusFree(USHORT port, UCHAR p_card)
+#else
+void phaseBusFree(ULONG port, UCHAR p_card)
+#endif
+{
+   PSCCB currSCCB;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+
+   if (currSCCB != NULL)
+      {
+
+      DISABLE_AUTO(port);
+
+
+      if (currSCCB->OperationCode == RESET_COMMAND)
+         {
+
+			if((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+				((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+	   		 sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = FALSE;
+			else
+		   	 sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = FALSE;
+
+	      queueCmdComplete(&BL_Card[p_card], currSCCB, p_card);
+
+	      queueSearchSelect(&BL_Card[p_card],p_card);
+
+	      }
+
+      else if(currSCCB->Sccb_scsistat == SELECT_SN_ST)
+	      {
+	      sccbMgrTbl[p_card][currSCCB->TargID].TarStatus |=
+			         (UCHAR)SYNC_SUPPORTED;
+	      sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &= ~EE_SYNC_MASK;
+	      }
+
+      else if(currSCCB->Sccb_scsistat == SELECT_WN_ST)
+	      {
+	      sccbMgrTbl[p_card][currSCCB->TargID].TarStatus =
+		            (sccbMgrTbl[p_card][currSCCB->TargID].
+		   TarStatus & ~WIDE_ENABLED) | WIDE_NEGOCIATED;
+
+	      sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &= ~EE_WIDE_SCSI;
+	      }
+
+#if !defined(DOS)
+      else if(currSCCB->Sccb_scsistat == SELECT_Q_ST)
+	      {
+	      /* Make sure this is not a phony BUS_FREE.  If we were
+	      reselected or if BUSY is NOT on then this is a
+	      valid BUS FREE.  SRR Wednesday, 5/10/1995.     */
+
+	      if ((!(RD_HARPOON(port+hp_scsisig) & SCSI_BSY)) ||
+	         (RDW_HARPOON((port+hp_intstat)) & RSEL))
+	         {
+	         sccbMgrTbl[p_card][currSCCB->TargID].TarStatus &= ~TAR_TAG_Q_MASK;
+	         sccbMgrTbl[p_card][currSCCB->TargID].TarStatus |= TAG_Q_REJECT;
+	         }
+
+	      else
+            {
+	         return;
+	         }
+         }
+#endif
+
+      else
+	      {
+
+	      currSCCB->Sccb_scsistat = BUS_FREE_ST;
+
+         if (!currSCCB->HostStatus)
+	         {
+	         currSCCB->HostStatus = SCCB_PHASE_SEQUENCE_FAIL;
+	         }
+
+			if((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+				((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+	   		 sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = FALSE;
+			else
+		   	 sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = FALSE;
+
+	      queueCmdComplete(&BL_Card[p_card], currSCCB, p_card);
+	      return;
+	      }
+
+
+      BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+      } /*end if !=null */
+}
+
+
+
+
+#ident "$Id: automate.c 1.14 1997/01/31 02:11:46 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   automate.c  $
+ *
+ *   Description:  Functions relating to programming the automation of
+ *                 the HARPOON.
+ *
+ *   $Date: 1997/01/31 02:11:46 $
+ *
+ *   $Revision: 1.14 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+	/*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+extern SCCBCARD BL_Card[MAX_CARDS];
+*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Auto Load Default Map
+ *
+ * Description: Load the Automation RAM with the defualt map values.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void autoLoadDefaultMap(USHORT p_port)
+#else
+void autoLoadDefaultMap(ULONG p_port)
+#endif
+{
+#if defined(DOS)
+   USHORT map_addr;
+#else
+   ULONG map_addr;
+#endif
+
+   ARAM_ACCESS(p_port);
+   map_addr = p_port + hp_aramBase;
+
+   WRW_HARPOON(map_addr, (MPM_OP+AMSG_OUT+ 0xC0));  /*ID MESSAGE */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+AMSG_OUT+ 0x20));  /*SIMPLE TAG QUEUEING MSG */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, RAT_OP);                   /*RESET ATTENTION */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+AMSG_OUT+ 0x00));  /*TAG ID MSG */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00));  /*CDB BYTE 0 */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00));  /*CDB BYTE 1 */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00));  /*CDB BYTE 2 */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00));  /*CDB BYTE 3 */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00));  /*CDB BYTE 4 */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00));  /*CDB BYTE 5 */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00));  /*CDB BYTE 6 */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00));  /*CDB BYTE 7 */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00));  /*CDB BYTE 8 */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00));  /*CDB BYTE 9 */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00));  /*CDB BYTE 10 */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MPM_OP+ACOMMAND+ 0x00));  /*CDB BYTE 11 */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (CPE_OP+ADATA_OUT+ DINT)); /*JUMP IF DATA OUT */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (TCB_OP+FIFO_0+ DI));     /*JUMP IF NO DATA IN FIFO */
+   map_addr +=2;                                   /*This means AYNC DATA IN */
+   WRW_HARPOON(map_addr, (SSI_OP+   SSI_IDO_STRT)); /*STOP AND INTERRUPT */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (CPE_OP+ADATA_IN+DINT));   /*JUMP IF NOT DATA IN PHZ */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (CPN_OP+AMSG_IN+  ST));    /*IF NOT MSG IN CHECK 4 DATA IN */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (CRD_OP+SDATA+    0x02));  /*SAVE DATA PTR MSG? */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (BRH_OP+NOT_EQ+   DC));    /*GO CHECK FOR DISCONNECT MSG */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MRR_OP+SDATA+    D_AR1)); /*SAVE DATA PTRS MSG */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (CPN_OP+AMSG_IN+  ST));    /*IF NOT MSG IN CHECK DATA IN */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (CRD_OP+SDATA+    0x04));  /*DISCONNECT MSG? */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (BRH_OP+NOT_EQ+   UNKNWN));/*UKNKNOWN MSG */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MRR_OP+SDATA+    D_BUCKET));/*XFER DISCONNECT MSG */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (SSI_OP+          SSI_ITAR_DISC));/*STOP AND INTERRUPT */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (CPN_OP+ASTATUS+  UNKNWN));/*JUMP IF NOT STATUS PHZ. */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MRR_OP+SDATA+  D_AR0));   /*GET STATUS BYTE */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (CPN_OP+AMSG_IN+  CC));    /*ERROR IF NOT MSG IN PHZ */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (CRD_OP+SDATA+    0x00));  /*CHECK FOR CMD COMPLETE MSG. */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (BRH_OP+NOT_EQ+   CC));    /*ERROR IF NOT CMD COMPLETE MSG. */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (MRR_OP+SDATA+  D_BUCKET));/*GET CMD COMPLETE MSG */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (SSI_OP+       SSI_ICMD_COMP));/*END OF COMMAND */
+   map_addr +=2;
+
+   WRW_HARPOON(map_addr, (SSI_OP+ SSI_IUNKWN));  /*RECEIVED UNKNOWN MSG BYTE */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (SSI_OP+ SSI_INO_CC));  /*NO COMMAND COMPLETE AFTER STATUS */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (SSI_OP+ SSI_ITICKLE)); /*BIOS Tickled the Mgr */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (SSI_OP+ SSI_IRFAIL));  /*EXPECTED ID/TAG MESSAGES AND */
+   map_addr +=2;                             /* DIDN'T GET ONE */
+   WRW_HARPOON(map_addr, (CRR_OP+AR3+  S_IDREG)); /* comp SCSI SEL ID & AR3*/
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (BRH_OP+EQUAL+   0x00));    /*SEL ID OK then Conti. */
+   map_addr +=2;
+   WRW_HARPOON(map_addr, (SSI_OP+ SSI_INO_CC));  /*NO COMMAND COMPLETE AFTER STATUS */
+
+
+
+   SGRAM_ACCESS(p_port);
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Auto Command Complete
+ *
+ * Description: Post command back to host and find another command
+ *              to execute.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void autoCmdCmplt(USHORT p_port, UCHAR p_card)
+#else
+void autoCmdCmplt(ULONG p_port, UCHAR p_card)
+#endif
+{
+   PSCCB currSCCB;
+   UCHAR status_byte;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+
+   status_byte = RD_HARPOON(p_port+hp_gp_reg_0);
+
+   sccbMgrTbl[p_card][currSCCB->TargID].TarLUN_CA = FALSE;
+
+   if (status_byte != SSGOOD) {
+
+      if (status_byte == SSQ_FULL) {
+
+
+			if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+				((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+			{
+	         sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+				if(BL_Card[p_card].discQCount != 0)
+					BL_Card[p_card].discQCount--;
+				BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+			}
+			else
+			{
+	         sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+				if(currSCCB->Sccb_tag)
+				{
+					if(BL_Card[p_card].discQCount != 0)
+						BL_Card[p_card].discQCount--;
+					BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+				}else
+				{
+					if(BL_Card[p_card].discQCount != 0)
+						BL_Card[p_card].discQCount--;
+					BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+				}
+			}
+
+         currSCCB->Sccb_MGRFlags |= F_STATUSLOADED;
+
+         queueSelectFail(&BL_Card[p_card],p_card);
+
+         return;
+         }
+
+      if(currSCCB->Sccb_scsistat == SELECT_SN_ST)
+         {
+         sccbMgrTbl[p_card][currSCCB->TargID].TarStatus |=
+            (UCHAR)SYNC_SUPPORTED;
+
+	      sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &= ~EE_SYNC_MASK;
+         BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+			if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+				((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+			{
+	         sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+				if(BL_Card[p_card].discQCount != 0)
+					BL_Card[p_card].discQCount--;
+				BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+			}
+			else
+			{
+	         sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+				if(currSCCB->Sccb_tag)
+				{
+					if(BL_Card[p_card].discQCount != 0)
+						BL_Card[p_card].discQCount--;
+					BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+				}else
+				{
+					if(BL_Card[p_card].discQCount != 0)
+						BL_Card[p_card].discQCount--;
+					BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+				}
+			}
+         return;
+
+         }
+
+      if(currSCCB->Sccb_scsistat == SELECT_WN_ST)
+         {
+
+	      sccbMgrTbl[p_card][currSCCB->TargID].TarStatus =
+	         (sccbMgrTbl[p_card][currSCCB->TargID].
+	         TarStatus & ~WIDE_ENABLED) | WIDE_NEGOCIATED;
+
+	      sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue &= ~EE_WIDE_SCSI;
+         BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+			if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+				((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+			{
+	         sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+				if(BL_Card[p_card].discQCount != 0)
+					BL_Card[p_card].discQCount--;
+				BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+			}
+			else
+			{
+	         sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+				if(currSCCB->Sccb_tag)
+				{
+					if(BL_Card[p_card].discQCount != 0)
+						BL_Card[p_card].discQCount--;
+					BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+				}else
+				{
+					if(BL_Card[p_card].discQCount != 0)
+						BL_Card[p_card].discQCount--;
+					BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+				}
+			}
+         return;
+      
+         }
+     
+	   if (status_byte == SSCHECK) 
+		{
+			if(BL_Card[p_card].globalFlags & F_DO_RENEGO)
+			{
+				if (sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue & EE_SYNC_MASK)
+				{
+					sccbMgrTbl[p_card][currSCCB->TargID].TarStatus &= ~TAR_SYNC_MASK;
+				}
+				if (sccbMgrTbl[p_card][currSCCB->TargID].TarEEValue & EE_WIDE_SCSI)
+				{
+					sccbMgrTbl[p_card][currSCCB->TargID].TarStatus &= ~TAR_WIDE_MASK;
+				}
+			}
+		}
+
+      if (!(currSCCB->Sccb_XferState & F_AUTO_SENSE)) {
+
+         currSCCB->SccbStatus = SCCB_ERROR;
+         currSCCB->TargetStatus = status_byte;
+
+         if (status_byte == SSCHECK) {
+
+            sccbMgrTbl[p_card][currSCCB->TargID].TarLUN_CA
+               = TRUE;
+     
+
+#if (FW_TYPE==_SCCB_MGR_)
+            if (currSCCB->RequestSenseLength != NO_AUTO_REQUEST_SENSE) {
+
+               if (currSCCB->RequestSenseLength == 0)
+                  currSCCB->RequestSenseLength = 14;
+
+               ssenss(&BL_Card[p_card]);
+               BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+ 					if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+						((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+					{
+			         sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+						if(BL_Card[p_card].discQCount != 0)
+							BL_Card[p_card].discQCount--;
+						BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+					}
+					else
+					{
+	   		      sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+						if(currSCCB->Sccb_tag)
+						{
+							if(BL_Card[p_card].discQCount != 0)
+								BL_Card[p_card].discQCount--;
+							BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+						}else
+						{
+							if(BL_Card[p_card].discQCount != 0)
+								BL_Card[p_card].discQCount--;
+							BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+						}
+					}
+               return;
+               }
+#else
+				   if ((!(currSCCB->Sccb_ucb_ptr->UCB_opcode & OPC_NO_AUTO_SENSE)) &&
+			   		(currSCCB->RequestSenseLength))
+				   {
+				   	ssenss(&BL_Card[p_card]);
+				      BL_Card[p_card].globalFlags |= F_NEW_SCCB_CMD;
+
+						if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+							((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+						{
+	      			   sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = TRUE;
+							if(BL_Card[p_card].discQCount != 0)
+								BL_Card[p_card].discQCount--;
+							BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[currSCCB->Lun]] = NULL;
+						}
+						else
+						{
+	      			   sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = TRUE;
+							if(currSCCB->Sccb_tag)
+							{
+								if(BL_Card[p_card].discQCount != 0)
+									BL_Card[p_card].discQCount--;
+								BL_Card[p_card].discQ_Tbl[currSCCB->Sccb_tag] = NULL;
+							}else
+							{
+								if(BL_Card[p_card].discQCount != 0)
+									BL_Card[p_card].discQCount--;
+								BL_Card[p_card].discQ_Tbl[sccbMgrTbl[p_card][currSCCB->TargID].LunDiscQ_Idx[0]] = NULL;
+							}
+						}
+				      return;
+				   }
+
+#endif
+            }
+         }
+      }
+
+
+	if((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+		((sccbMgrTbl[p_card][currSCCB->TargID].TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+	   sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[currSCCB->Lun] = FALSE;
+	else
+	   sccbMgrTbl[p_card][currSCCB->TargID].TarLUNBusy[0] = FALSE;
+
+
+   queueCmdComplete(&BL_Card[p_card], currSCCB, p_card);
+}
+#ident "$Id: busmstr.c 1.8 1997/01/31 02:10:27 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   busmstr.c  $
+ *
+ *   Description:  Functions to start, stop, and abort BusMaster operations.
+ *
+ *   $Date: 1997/01/31 02:10:27 $
+ *
+ *   $Revision: 1.8 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+	/*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+*/
+
+#define SHORT_WAIT   0x0000000F
+#define LONG_WAIT    0x0000FFFFL
+
+#if defined(BUGBUG)
+void Debug_Load(UCHAR p_card, UCHAR p_bug_data);
+#endif
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Data Transfer Processor
+ *
+ * Description: This routine performs two tasks.
+ *              (1) Start data transfer by calling HOST_DATA_XFER_START
+ *              function.  Once data transfer is started, (2) Depends
+ *              on the type of data transfer mode Scatter/Gather mode
+ *              or NON Scatter/Gather mode.  In NON Scatter/Gather mode,
+ *              this routine checks Sccb_MGRFlag (F_HOST_XFER_ACT bit) for
+ *              data transfer done.  In Scatter/Gather mode, this routine
+ *              checks bus master command complete and dual rank busy
+ *              bit to keep chaining SC transfer command.  Similarly,
+ *              in Scatter/Gather mode, it checks Sccb_MGRFlag
+ *              (F_HOST_XFER_ACT bit) for data transfer done.
+ *              
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void dataXferProcessor(USHORT port, PSCCBcard pCurrCard)
+#else
+void dataXferProcessor(ULONG port, PSCCBcard pCurrCard)
+#endif
+{
+   PSCCB currSCCB;
+
+   currSCCB = pCurrCard->currentSCCB;
+
+      if (currSCCB->Sccb_XferState & F_SG_XFER)
+			{
+			if (pCurrCard->globalFlags & F_HOST_XFER_ACT)
+
+				{
+		   	currSCCB->Sccb_sgseg += (UCHAR)SG_BUF_CNT;
+         	currSCCB->Sccb_SGoffset = 0x00; 
+ 				}
+			pCurrCard->globalFlags |= F_HOST_XFER_ACT;
+         
+         busMstrSGDataXferStart(port, currSCCB);
+			}
+
+      else
+			{
+			if (!(pCurrCard->globalFlags & F_HOST_XFER_ACT))
+				{
+				pCurrCard->globalFlags |= F_HOST_XFER_ACT;
+         
+         	busMstrDataXferStart(port, currSCCB);
+         	}
+			}
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: BusMaster Scatter Gather Data Transfer Start
+ *
+ * Description:
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void busMstrSGDataXferStart(USHORT p_port, PSCCB pcurrSCCB)
+#else
+void busMstrSGDataXferStart(ULONG p_port, PSCCB pcurrSCCB)
+#endif
+{
+   ULONG count,addr,tmpSGCnt;
+   UINT sg_index;
+   UCHAR sg_count, i;
+#if defined(DOS)
+   USHORT reg_offset;
+#else
+   ULONG reg_offset;
+#endif
+
+
+   if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
+
+      count =  ((ULONG) HOST_RD_CMD)<<24;
+      }
+
+   else {
+      count =  ((ULONG) HOST_WRT_CMD)<<24;
+      }
+
+   sg_count = 0;
+   tmpSGCnt = 0;
+   sg_index = pcurrSCCB->Sccb_sgseg;
+   reg_offset = hp_aramBase;
+
+
+	i = (UCHAR) (RD_HARPOON(p_port+hp_page_ctrl) & ~(SGRAM_ARAM|SCATTER_EN));
+
+
+	WR_HARPOON(p_port+hp_page_ctrl, i);
+
+   while ((sg_count < (UCHAR)SG_BUF_CNT) &&
+      ((ULONG)(sg_index * (UINT)SG_ELEMENT_SIZE) < pcurrSCCB->DataLength) ) {
+
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+      tmpSGCnt += *(((ULONG far *)pcurrSCCB->DataPointer)+
+         (sg_index * 2));
+
+      count |= *(((ULONG far *)pcurrSCCB->DataPointer)+
+         (sg_index * 2));
+
+      addr = *(((ULONG far *)pcurrSCCB->DataPointer)+
+         ((sg_index * 2) + 1));
+
+#else
+      tmpSGCnt += *(((ULONG *)pcurrSCCB->DataPointer)+
+         (sg_index * 2));
+
+      count |= *(((ULONG *)pcurrSCCB->DataPointer)+
+         (sg_index * 2));
+
+      addr = *(((ULONG *)pcurrSCCB->DataPointer)+
+         ((sg_index * 2) + 1));
+#endif
+
+
+      if ((!sg_count) && (pcurrSCCB->Sccb_SGoffset)) {
+
+         addr += ((count & 0x00FFFFFFL) - pcurrSCCB->Sccb_SGoffset);
+         count = (count & 0xFF000000L) | pcurrSCCB->Sccb_SGoffset;
+
+         tmpSGCnt = count & 0x00FFFFFFL;
+         }
+
+      WR_HARP32(p_port,reg_offset,addr);
+      reg_offset +=4;
+
+      WR_HARP32(p_port,reg_offset,count);
+      reg_offset +=4;
+
+      count &= 0xFF000000L;
+      sg_index++;
+      sg_count++;
+
+      } /*End While */
+
+   pcurrSCCB->Sccb_XferCnt = tmpSGCnt;
+
+   WR_HARPOON(p_port+hp_sg_addr,(sg_count<<4));
+
+   if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
+
+      WR_HARP32(p_port,hp_xfercnt_0,tmpSGCnt);
+
+
+      WR_HARPOON(p_port+hp_portctrl_0,(DMA_PORT | SCSI_PORT | SCSI_INBIT));
+      WR_HARPOON(p_port+hp_scsisig, S_DATAI_PH);
+      }
+
+   else {
+
+
+      if ((!(RD_HARPOON(p_port+hp_synctarg_0) & NARROW_SCSI)) &&
+         (tmpSGCnt & 0x000000001))
+         {
+
+         pcurrSCCB->Sccb_XferState |= F_ODD_BALL_CNT;
+         tmpSGCnt--;
+         }
+
+
+      WR_HARP32(p_port,hp_xfercnt_0,tmpSGCnt);
+
+      WR_HARPOON(p_port+hp_portctrl_0,(SCSI_PORT | DMA_PORT | DMA_RD));
+      WR_HARPOON(p_port+hp_scsisig, S_DATAO_PH);
+      }
+
+
+   WR_HARPOON(p_port+hp_page_ctrl, (UCHAR) (i | SCATTER_EN));
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: BusMaster Data Transfer Start
+ *
+ * Description: 
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void busMstrDataXferStart(USHORT p_port, PSCCB pcurrSCCB)
+#else
+void busMstrDataXferStart(ULONG p_port, PSCCB pcurrSCCB)
+#endif
+{
+   ULONG addr,count;
+
+   if (!(pcurrSCCB->Sccb_XferState & F_AUTO_SENSE)) {
+
+      count = pcurrSCCB->Sccb_XferCnt;
+
+      addr = (ULONG) pcurrSCCB->DataPointer + pcurrSCCB->Sccb_ATC;
+      }
+
+   else {
+      addr = pcurrSCCB->SensePointer;
+      count = pcurrSCCB->RequestSenseLength;
+
+      }
+
+#if defined(DOS)
+   asm { mov dx,p_port;
+         mov ax,word ptr count;
+         add dx,hp_xfer_cnt_lo;
+         out dx,al;
+         inc dx;
+         xchg ah,al
+         out dx,al;
+         inc dx;
+         mov ax,word ptr count+2;
+         out dx,al;
+         inc dx;
+         inc dx;
+         mov ax,word ptr addr;
+         out dx,al;
+         inc dx;
+         xchg ah,al
+         out dx,al;
+         inc dx;
+         mov ax,word ptr addr+2;
+         out dx,al;
+         inc dx;
+         xchg ah,al
+         out dx,al;
+         }
+
+   WR_HARP32(p_port,hp_xfercnt_0,count);
+
+#else
+   HP_SETUP_ADDR_CNT(p_port,addr,count);
+#endif
+
+
+   if (pcurrSCCB->Sccb_XferState & F_HOST_XFER_DIR) {
+
+      WR_HARPOON(p_port+hp_portctrl_0,(DMA_PORT | SCSI_PORT | SCSI_INBIT));
+      WR_HARPOON(p_port+hp_scsisig, S_DATAI_PH);
+
+      WR_HARPOON(p_port+hp_xfer_cmd,
+         (XFER_DMA_HOST | XFER_HOST_AUTO | XFER_DMA_8BIT));
+      }
+
+   else {
+
+      WR_HARPOON(p_port+hp_portctrl_0,(SCSI_PORT | DMA_PORT | DMA_RD));
+      WR_HARPOON(p_port+hp_scsisig, S_DATAO_PH);
+
+      WR_HARPOON(p_port+hp_xfer_cmd,
+         (XFER_HOST_DMA | XFER_HOST_AUTO | XFER_DMA_8BIT));
+
+      }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: BusMaster Timeout Handler
+ *
+ * Description: This function is called after a bus master command busy time
+ *               out is detected.  This routines issue halt state machine
+ *               with a software time out for command busy.  If command busy
+ *               is still asserted at the end of the time out, it issues
+ *               hard abort with another software time out.  It hard abort
+ *               command busy is also time out, it'll just give up.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+UCHAR busMstrTimeOut(USHORT p_port)
+#else
+UCHAR busMstrTimeOut(ULONG p_port)
+#endif
+{
+   ULONG timeout;
+
+   timeout = LONG_WAIT;
+
+   WR_HARPOON(p_port+hp_sys_ctrl, HALT_MACH);
+
+   while ((!(RD_HARPOON(p_port+hp_ext_status) & CMD_ABORTED)) && timeout--) {}
+
+   
+   
+   if (RD_HARPOON(p_port+hp_ext_status) & BM_CMD_BUSY) {
+      WR_HARPOON(p_port+hp_sys_ctrl, HARD_ABORT);
+
+      timeout = LONG_WAIT;
+      while ((RD_HARPOON(p_port+hp_ext_status) & BM_CMD_BUSY) && timeout--) {}
+      }
+
+   RD_HARPOON(p_port+hp_int_status);           /*Clear command complete */
+
+   if (RD_HARPOON(p_port+hp_ext_status) & BM_CMD_BUSY) {
+      return(TRUE);
+      }
+
+   else {
+      return(FALSE);
+      }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Host Data Transfer Abort
+ *
+ * Description: Abort any in progress transfer.
+ *
+ *---------------------------------------------------------------------*/
+#if defined(DOS)
+void hostDataXferAbort(USHORT port, UCHAR p_card, PSCCB pCurrSCCB)
+#else
+void hostDataXferAbort(ULONG port, UCHAR p_card, PSCCB pCurrSCCB)
+#endif
+{
+
+   ULONG timeout;
+   ULONG remain_cnt;
+   UINT sg_ptr;
+
+   BL_Card[p_card].globalFlags &= ~F_HOST_XFER_ACT;
+
+   if (pCurrSCCB->Sccb_XferState & F_AUTO_SENSE) {
+
+
+      if (!(RD_HARPOON(port+hp_int_status) & INT_CMD_COMPL)) {
+
+         WR_HARPOON(port+hp_bm_ctrl, (RD_HARPOON(port+hp_bm_ctrl) | FLUSH_XFER_CNTR));
+         timeout = LONG_WAIT;
+
+         while ((RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) && timeout--) {}
+
+         WR_HARPOON(port+hp_bm_ctrl, (RD_HARPOON(port+hp_bm_ctrl) & ~FLUSH_XFER_CNTR));
+
+         if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+            if (busMstrTimeOut(port)) {
+
+               if (pCurrSCCB->HostStatus == 0x00)
+
+                  pCurrSCCB->HostStatus = SCCB_BM_ERR;
+
+               }
+
+            if (RD_HARPOON(port+hp_int_status) & INT_EXT_STATUS) 
+
+               if (RD_HARPOON(port+hp_ext_status) & BAD_EXT_STATUS) 
+
+                  if (pCurrSCCB->HostStatus == 0x00)
+
+                     {
+                     pCurrSCCB->HostStatus = SCCB_BM_ERR;
+#if defined(BUGBUG)
+                     WR_HARPOON(port+hp_dual_addr_lo,
+                        RD_HARPOON(port+hp_ext_status));
+#endif
+                     }
+            }
+         }
+      }
+
+   else if (pCurrSCCB->Sccb_XferCnt) {
+
+      if (pCurrSCCB->Sccb_XferState & F_SG_XFER) {
+
+
+              WR_HARPOON(port+hp_page_ctrl, (RD_HARPOON(port+hp_page_ctrl) &
+            ~SCATTER_EN));
+
+         WR_HARPOON(port+hp_sg_addr,0x00);
+
+         sg_ptr = pCurrSCCB->Sccb_sgseg + SG_BUF_CNT;
+
+         if (sg_ptr > (UINT)(pCurrSCCB->DataLength / SG_ELEMENT_SIZE)) {
+
+            sg_ptr = (UINT)(pCurrSCCB->DataLength / SG_ELEMENT_SIZE);
+            }
+
+         remain_cnt = pCurrSCCB->Sccb_XferCnt;
+
+         while (remain_cnt < 0x01000000L) {
+
+            sg_ptr--;
+
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+            if (remain_cnt > (ULONG)(*(((ULONG far *)pCurrSCCB->
+               DataPointer) + (sg_ptr * 2)))) {
+
+               remain_cnt -= (ULONG)(*(((ULONG far *)pCurrSCCB->
+                  DataPointer) + (sg_ptr * 2)));
+               }
+
+#else
+            if (remain_cnt > (ULONG)(*(((ULONG *)pCurrSCCB->
+               DataPointer) + (sg_ptr * 2)))) {
+
+               remain_cnt -= (ULONG)(*(((ULONG *)pCurrSCCB->
+                  DataPointer) + (sg_ptr * 2)));
+               }
+#endif
+
+            else {
+
+               break;
+               }
+            }
+
+
+
+         if (remain_cnt < 0x01000000L) {
+
+
+            pCurrSCCB->Sccb_SGoffset = remain_cnt;
+
+            pCurrSCCB->Sccb_sgseg = (USHORT)sg_ptr;
+
+
+            if ((ULONG)(sg_ptr * SG_ELEMENT_SIZE) == pCurrSCCB->DataLength 
+                && (remain_cnt == 0))
+
+               pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
+            }
+
+         else {
+
+
+            if (pCurrSCCB->HostStatus == 0x00) {
+
+               pCurrSCCB->HostStatus = SCCB_GROSS_FW_ERR;
+               }
+            }
+         }
+
+
+      if (!(pCurrSCCB->Sccb_XferState & F_HOST_XFER_DIR)) {
+
+
+         if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+            busMstrTimeOut(port);
+            }
+
+         else {
+
+            if (RD_HARPOON(port+hp_int_status) & INT_EXT_STATUS) {
+
+               if (RD_HARPOON(port+hp_ext_status) & BAD_EXT_STATUS) {
+
+                  if (pCurrSCCB->HostStatus == 0x00) {
+
+                     pCurrSCCB->HostStatus = SCCB_BM_ERR;
+#if defined(BUGBUG)
+                     WR_HARPOON(port+hp_dual_addr_lo,
+                        RD_HARPOON(port+hp_ext_status));
+#endif
+                     }
+                  }
+               }
+
+            }
+         }
+
+      else {
+
+
+         if ((RD_HARPOON(port+hp_fifo_cnt)) >= BM_THRESHOLD) {
+
+            timeout = SHORT_WAIT;
+
+            while ((RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) &&
+               ((RD_HARPOON(port+hp_fifo_cnt)) >= BM_THRESHOLD) &&
+               timeout--) {}
+            }
+
+         if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+            WR_HARPOON(port+hp_bm_ctrl, (RD_HARPOON(port+hp_bm_ctrl) |
+               FLUSH_XFER_CNTR));
+
+            timeout = LONG_WAIT;
+
+            while ((RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) &&
+               timeout--) {}
+
+            WR_HARPOON(port+hp_bm_ctrl, (RD_HARPOON(port+hp_bm_ctrl) &
+               ~FLUSH_XFER_CNTR));
+
+
+            if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+               if (pCurrSCCB->HostStatus == 0x00) {
+
+                  pCurrSCCB->HostStatus = SCCB_BM_ERR;
+                  }
+
+               busMstrTimeOut(port);
+               }
+            }
+
+         if (RD_HARPOON(port+hp_int_status) & INT_EXT_STATUS) {
+
+            if (RD_HARPOON(port+hp_ext_status) & BAD_EXT_STATUS) {
+
+               if (pCurrSCCB->HostStatus == 0x00) {
+
+                  pCurrSCCB->HostStatus = SCCB_BM_ERR;
+#if defined(BUGBUG)
+                  WR_HARPOON(port+hp_dual_addr_lo,
+                     RD_HARPOON(port+hp_ext_status));
+#endif
+                  }
+               }
+            }
+         }
+
+      }
+
+   else {
+
+
+      if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+         timeout = LONG_WAIT;
+
+         while ((RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) && timeout--) {}
+
+         if (RD_HARPOON(port+hp_ext_status) & BM_CMD_BUSY) {
+
+            if (pCurrSCCB->HostStatus == 0x00) {
+
+               pCurrSCCB->HostStatus = SCCB_BM_ERR;
+               }
+
+            busMstrTimeOut(port);
+            }
+         }
+
+
+      if (RD_HARPOON(port+hp_int_status) & INT_EXT_STATUS) {
+
+         if (RD_HARPOON(port+hp_ext_status) & BAD_EXT_STATUS) {
+
+            if (pCurrSCCB->HostStatus == 0x00) {
+
+               pCurrSCCB->HostStatus = SCCB_BM_ERR;
+#if defined(BUGBUG)
+               WR_HARPOON(port+hp_dual_addr_lo,
+                  RD_HARPOON(port+hp_ext_status));
+#endif
+               }
+            }
+
+         }
+
+      if (pCurrSCCB->Sccb_XferState & F_SG_XFER) {
+
+         WR_HARPOON(port+hp_page_ctrl, (RD_HARPOON(port+hp_page_ctrl) &
+                 ~SCATTER_EN));
+
+         WR_HARPOON(port+hp_sg_addr,0x00);
+
+         pCurrSCCB->Sccb_sgseg += SG_BUF_CNT;
+
+         pCurrSCCB->Sccb_SGoffset = 0x00; 
+
+
+         if ((ULONG)(pCurrSCCB->Sccb_sgseg * SG_ELEMENT_SIZE) >=
+            pCurrSCCB->DataLength) {
+
+            pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
+
+            pCurrSCCB->Sccb_sgseg = (USHORT)(pCurrSCCB->DataLength / SG_ELEMENT_SIZE);
+
+            }
+         }
+
+      else {
+
+         if (!(pCurrSCCB->Sccb_XferState & F_AUTO_SENSE))
+
+            pCurrSCCB->Sccb_XferState |= F_ALL_XFERRED;
+         }
+      }
+
+   WR_HARPOON(port+hp_int_mask,(INT_CMD_COMPL | SCSI_INTERRUPT));
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Host Data Transfer Restart
+ *
+ * Description: Reset the available count due to a restore data
+ *              pointers message.
+ *
+ *---------------------------------------------------------------------*/
+void hostDataXferRestart(PSCCB currSCCB)
+{
+   ULONG data_count;
+   UINT  sg_index;
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+   ULONG far *sg_ptr;
+#else
+   ULONG *sg_ptr;
+#endif
+
+   if (currSCCB->Sccb_XferState & F_SG_XFER) {
+
+      currSCCB->Sccb_XferCnt = 0;
+
+      sg_index = 0xffff;         /*Index by long words into sg list. */
+      data_count = 0;            /*Running count of SG xfer counts. */
+
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+      sg_ptr = (ULONG far *)currSCCB->DataPointer;
+#else
+      sg_ptr = (ULONG *)currSCCB->DataPointer;
+#endif
+
+      while (data_count < currSCCB->Sccb_ATC) {
+
+         sg_index++;
+         data_count += *(sg_ptr+(sg_index * 2));
+         }
+
+      if (data_count == currSCCB->Sccb_ATC) {
+
+         currSCCB->Sccb_SGoffset = 0;
+         sg_index++;
+         }
+
+      else {
+         currSCCB->Sccb_SGoffset = data_count - currSCCB->Sccb_ATC;
+         }
+
+      currSCCB->Sccb_sgseg = (USHORT)sg_index;
+      }
+
+   else {
+      currSCCB->Sccb_XferCnt = currSCCB->DataLength - currSCCB->Sccb_ATC;
+      }
+}
+#ident "$Id: scam.c 1.16 1997/01/31 02:11:12 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   scam.c  $
+ *
+ *   Description:  Functions relating to handling of the SCAM selection
+ *                 and the determination of the SCSI IDs to be assigned
+ *                 to all perspective SCSI targets.
+ *
+ *   $Date: 1997/01/31 02:11:12 $
+ *
+ *   $Revision: 1.16 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+	/*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <eeprom.h>*/
+/*#include <harpoon.h>*/
+
+
+
+/*
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBSCAM_INFO scamInfo[MAX_SCSI_TAR];
+extern NVRAMINFO nvRamInfo[MAX_MB_CARDS];
+#if defined(DOS) || defined(OS2)
+extern UCHAR temp_id_string[ID_STRING_LENGTH];
+#endif
+extern UCHAR scamHAString[];
+*/
+/*---------------------------------------------------------------------
+ *
+ * Function: scini
+ *
+ * Description: Setup all data structures necessary for SCAM selection.
+ *
+ *---------------------------------------------------------------------*/
+
+void scini(UCHAR p_card, UCHAR p_our_id, UCHAR p_power_up)
+{
+
+#if defined(SCAM_LEV_2)
+   UCHAR loser,assigned_id;
+#endif
+#if defined(DOS)
+
+   USHORT p_port;
+#else
+   ULONG p_port;
+#endif
+
+   UCHAR i,k,ScamFlg ;
+   PSCCBcard currCard;
+	PNVRamInfo pCurrNvRam;
+
+   currCard = &BL_Card[p_card];
+   p_port = currCard->ioPort;
+	pCurrNvRam = currCard->pNvRamInfo;
+
+
+	if(pCurrNvRam){
+		ScamFlg = pCurrNvRam->niScamConf;
+		i = pCurrNvRam->niSysConf;
+	}
+	else{
+	   ScamFlg = (UCHAR) utilEERead(p_port, SCAM_CONFIG/2);
+	   i = (UCHAR)(utilEERead(p_port, (SYSTEM_CONFIG/2)));
+	}
+	if(!(i & 0x02))	/* check if reset bus in AutoSCSI parameter set */
+		return;
+
+   inisci(p_card,p_port, p_our_id);
+
+   /* Force to wait 1 sec after SCSI bus reset. Some SCAM device FW
+      too slow to return to SCAM selection */
+
+   /* if (p_power_up)
+         Wait1Second(p_port);
+      else
+         Wait(p_port, TO_250ms); */
+
+   Wait1Second(p_port);
+
+#if defined(SCAM_LEV_2)
+
+   if ((ScamFlg & SCAM_ENABLED) && (ScamFlg & SCAM_LEVEL2))
+      {
+      while (!(scarb(p_port,INIT_SELTD))) {}
+
+      scsel(p_port);
+
+      do {
+         scxferc(p_port,SYNC_PTRN);
+         scxferc(p_port,DOM_MSTR);
+         loser = scsendi(p_port,&scamInfo[p_our_id].id_string[0]);
+         } while ( loser == 0xFF );
+
+      scbusf(p_port);
+
+      if ((p_power_up) && (!loser))
+         {
+         sresb(p_port,p_card);
+         Wait(p_port, TO_250ms);
+
+         while (!(scarb(p_port,INIT_SELTD))) {}
+
+         scsel(p_port);
+
+         do {
+            scxferc(p_port, SYNC_PTRN);
+            scxferc(p_port, DOM_MSTR);
+            loser = scsendi(p_port,&scamInfo[p_our_id].
+               id_string[0]);
+            } while ( loser == 0xFF );
+
+         scbusf(p_port);
+         }
+      }
+
+   else
+      {
+      loser = FALSE;
+      }
+
+
+   if (!loser)
+      {
+
+#endif  /* SCAM_LEV_2 */
+
+      scamInfo[p_our_id].state = ID_ASSIGNED;
+
+
+		if (ScamFlg & SCAM_ENABLED)
+		{
+
+	      for (i=0; i < MAX_SCSI_TAR; i++)
+  		   {
+      	   if ((scamInfo[i].state == ID_UNASSIGNED) ||
+  	      	   (scamInfo[i].state == ID_UNUSED))
+	  	      {
+   	     	   if (scsell(p_port,i))
+      	  	   {
+            	   scamInfo[i].state = LEGACY;
+  	            	if ((scamInfo[i].id_string[0] != 0xFF) ||
+     	            	(scamInfo[i].id_string[1] != 0xFA))
+	     	         {
+
+   	        	      scamInfo[i].id_string[0] = 0xFF;
+      	        	   scamInfo[i].id_string[1] = 0xFA;
+							if(pCurrNvRam == NULL)
+	         	         currCard->globalFlags |= F_UPDATE_EEPROM;
+               	}
+	  	         }
+   	  	   }
+      	}
+
+	      sresb(p_port,p_card);
+      	Wait1Second(p_port);
+         while (!(scarb(p_port,INIT_SELTD))) {}
+         scsel(p_port);
+         scasid(p_card, p_port);
+         }
+
+#if defined(SCAM_LEV_2)
+
+      }
+
+   else if ((loser) && (ScamFlg & SCAM_ENABLED))
+      {
+      scamInfo[p_our_id].id_string[0] = SLV_TYPE_CODE0;
+      assigned_id = FALSE;
+      scwtsel(p_port);
+
+      do {
+         while (scxferc(p_port,0x00) != SYNC_PTRN) {}
+
+         i = scxferc(p_port,0x00);
+         if (i == ASSIGN_ID)
+            {
+            if (!(scsendi(p_port,&scamInfo[p_our_id].id_string[0])))
+                  {
+                  i = scxferc(p_port,0x00);
+                  if (scvalq(i))
+                     {
+                     k = scxferc(p_port,0x00);
+
+                     if (scvalq(k))
+                        {
+                        currCard->ourId =
+                           ((UCHAR)(i<<3)+(k & (UCHAR)7)) & (UCHAR) 0x3F;
+                        inisci(p_card, p_port, p_our_id);
+                        scamInfo[currCard->ourId].state = ID_ASSIGNED;
+                        scamInfo[currCard->ourId].id_string[0]
+                           = SLV_TYPE_CODE0;
+                        assigned_id = TRUE;
+                        }
+                     }
+                  }
+            }
+
+         else if (i == SET_P_FLAG)
+            {
+               if (!(scsendi(p_port,
+                        &scamInfo[p_our_id].id_string[0])))
+                        scamInfo[p_our_id].id_string[0] |= 0x80;
+            }
+         }while (!assigned_id);
+
+      while (scxferc(p_port,0x00) != CFG_CMPLT) {}
+      }
+
+#endif   /* SCAM_LEV_2 */
+   if (ScamFlg & SCAM_ENABLED)
+      {
+      scbusf(p_port);
+      if (currCard->globalFlags & F_UPDATE_EEPROM)
+         {
+         scsavdi(p_card, p_port);
+         currCard->globalFlags &= ~F_UPDATE_EEPROM;
+         }
+      }
+
+
+#if defined(DOS)
+   for (i=0; i < MAX_SCSI_TAR; i++)
+   {
+     	if (((ScamFlg & SCAM_ENABLED) && (scamInfo[i].state == LEGACY))
+			|| (i != p_our_id))
+        	{
+         scsell(p_port,i);
+  	      }
+  	}
+#endif
+
+/*
+   for (i=0,k=0; i < MAX_SCSI_TAR; i++)
+      {
+      if ((scamInfo[i].state == ID_ASSIGNED) ||
+         (scamInfo[i].state == LEGACY))
+         k++;
+      }
+
+   if (k==2)
+      currCard->globalFlags |= F_SINGLE_DEVICE;
+   else
+      currCard->globalFlags &= ~F_SINGLE_DEVICE;
+*/
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scarb
+ *
+ * Description: Gain control of the bus and wait SCAM select time (250ms)
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+int scarb(USHORT p_port, UCHAR p_sel_type)
+#else
+int scarb(ULONG p_port, UCHAR p_sel_type)
+#endif
+{
+   if (p_sel_type == INIT_SELTD)
+      {
+
+      while (RD_HARPOON(p_port+hp_scsisig) & (SCSI_SEL | SCSI_BSY)) {}
+
+
+      if (RD_HARPOON(p_port+hp_scsisig) & SCSI_SEL)
+         return(FALSE);
+
+      if (RD_HARPOON(p_port+hp_scsidata_0) != 00)
+         return(FALSE);
+
+      WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) | SCSI_BSY));
+
+      if (RD_HARPOON(p_port+hp_scsisig) & SCSI_SEL) {
+
+         WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) &
+            ~SCSI_BSY));
+         return(FALSE);
+         }
+
+
+      WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) | SCSI_SEL));
+
+      if (RD_HARPOON(p_port+hp_scsidata_0) != 00) {
+
+         WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) &
+            ~(SCSI_BSY | SCSI_SEL)));
+         return(FALSE);
+         }
+      }
+
+
+   WR_HARPOON(p_port+hp_clkctrl_0, (RD_HARPOON(p_port+hp_clkctrl_0)
+      & ~ACTdeassert));
+   WR_HARPOON(p_port+hp_scsireset, SCAM_EN);
+   WR_HARPOON(p_port+hp_scsidata_0, 0x00);
+#if defined(WIDE_SCSI)
+   WR_HARPOON(p_port+hp_scsidata_1, 0x00);
+#endif
+   WR_HARPOON(p_port+hp_portctrl_0, SCSI_BUS_EN);
+
+   WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig) | SCSI_MSG));
+
+   WR_HARPOON(p_port+hp_scsisig, (RD_HARPOON(p_port+hp_scsisig)
+      & ~SCSI_BSY));
+
+   Wait(p_port,TO_250ms);
+
+   return(TRUE);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scbusf
+ *
+ * Description: Release the SCSI bus and disable SCAM selection.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scbusf(USHORT p_port)
+#else
+void scbusf(ULONG p_port)
+#endif
+{
+   WR_HARPOON(p_port+hp_page_ctrl,
+      (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE));
+
+
+   WR_HARPOON(p_port+hp_scsidata_0, 0x00);
+
+   WR_HARPOON(p_port+hp_portctrl_0, (RD_HARPOON(p_port+hp_portctrl_0)
+      & ~SCSI_BUS_EN));
+
+   WR_HARPOON(p_port+hp_scsisig, 0x00);
+
+
+   WR_HARPOON(p_port+hp_scsireset,  (RD_HARPOON(p_port+hp_scsireset)
+      & ~SCAM_EN));
+
+   WR_HARPOON(p_port+hp_clkctrl_0, (RD_HARPOON(p_port+hp_clkctrl_0)
+      | ACTdeassert));
+
+#if defined(SCAM_LEV_2)
+   WRW_HARPOON((p_port+hp_intstat), (BUS_FREE | AUTO_INT | SCAM_SEL));
+#else
+   WRW_HARPOON((p_port+hp_intstat), (BUS_FREE | AUTO_INT));
+#endif
+
+   WR_HARPOON(p_port+hp_page_ctrl,
+      (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scasid
+ *
+ * Description: Assign an ID to all the SCAM devices.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scasid(UCHAR p_card, USHORT p_port)
+#else
+void scasid(UCHAR p_card, ULONG p_port)
+#endif
+{
+#if defined(DOS) || defined(OS2)
+   /* Use external defined in global space area, instead of Stack
+      space. WIN/95 DOS doesnot work TINY mode. The OS doesnot intialize
+      SS equal to DS. Thus the array allocated on stack doesnot get
+      access correctly */
+#else
+   UCHAR temp_id_string[ID_STRING_LENGTH];
+#endif
+
+   UCHAR i,k,scam_id;
+	UCHAR crcBytes[3];
+	PNVRamInfo pCurrNvRam;
+	ushort_ptr pCrcBytes;
+
+	pCurrNvRam = BL_Card[p_card].pNvRamInfo;
+
+   i=FALSE;
+
+   while (!i)
+      {
+
+      for (k=0; k < ID_STRING_LENGTH; k++)
+         {
+         temp_id_string[k] = (UCHAR) 0x00;
+         }
+
+      scxferc(p_port,SYNC_PTRN);
+      scxferc(p_port,ASSIGN_ID);
+
+      if (!(sciso(p_port,&temp_id_string[0])))
+         {
+			if(pCurrNvRam){
+				pCrcBytes = (ushort_ptr)&crcBytes[0];
+				*pCrcBytes = CalcCrc16(&temp_id_string[0]);
+				crcBytes[2] = CalcLrc(&temp_id_string[0]);
+				temp_id_string[1] = crcBytes[2];
+				temp_id_string[2] = crcBytes[0];
+				temp_id_string[3] = crcBytes[1];
+				for(k = 4; k < ID_STRING_LENGTH; k++)
+					temp_id_string[k] = (UCHAR) 0x00;
+			}
+         i = scmachid(p_card,temp_id_string);
+
+         if (i == CLR_PRIORITY)
+            {
+            scxferc(p_port,MISC_CODE);
+            scxferc(p_port,CLR_P_FLAG);
+            i = FALSE;  /*Not the last ID yet. */
+            }
+
+         else if (i != NO_ID_AVAIL)
+            {
+            if (i < 8 )
+               scxferc(p_port,ID_0_7);
+            else
+               scxferc(p_port,ID_8_F);
+
+            scam_id = (i & (UCHAR) 0x07);
+
+
+            for (k=1; k < 0x08; k <<= 1)
+               if (!( k & i ))
+                  scam_id += 0x08;        /*Count number of zeros in DB0-3. */
+
+            scxferc(p_port,scam_id);
+
+            i = FALSE;  /*Not the last ID yet. */
+            }
+         }
+
+      else
+         {
+         i = TRUE;
+         }
+
+      }  /*End while */
+
+   scxferc(p_port,SYNC_PTRN);
+   scxferc(p_port,CFG_CMPLT);
+}
+
+
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scsel
+ *
+ * Description: Select all the SCAM devices.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scsel(USHORT p_port)
+#else
+void scsel(ULONG p_port)
+#endif
+{
+
+   WR_HARPOON(p_port+hp_scsisig, SCSI_SEL);
+   scwiros(p_port, SCSI_MSG);
+
+   WR_HARPOON(p_port+hp_scsisig, (SCSI_SEL | SCSI_BSY));
+
+
+   WR_HARPOON(p_port+hp_scsisig, (SCSI_SEL | SCSI_BSY | SCSI_IOBIT | SCSI_CD));
+   WR_HARPOON(p_port+hp_scsidata_0, (UCHAR)(RD_HARPOON(p_port+hp_scsidata_0) |
+      (UCHAR)(BIT(7)+BIT(6))));
+
+
+   WR_HARPOON(p_port+hp_scsisig, (SCSI_BSY | SCSI_IOBIT | SCSI_CD));
+   scwiros(p_port, SCSI_SEL);
+
+   WR_HARPOON(p_port+hp_scsidata_0, (UCHAR)(RD_HARPOON(p_port+hp_scsidata_0) &
+      ~(UCHAR)BIT(6)));
+   scwirod(p_port, BIT(6));
+
+   WR_HARPOON(p_port+hp_scsisig, (SCSI_SEL | SCSI_BSY | SCSI_IOBIT | SCSI_CD));
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scxferc
+ *
+ * Description: Handshake the p_data (DB4-0) across the bus.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR scxferc(USHORT p_port, UCHAR p_data)
+#else
+UCHAR scxferc(ULONG p_port, UCHAR p_data)
+#endif
+{
+   UCHAR curr_data, ret_data;
+
+   curr_data = p_data | BIT(7) | BIT(5);   /*Start with DB7 & DB5 asserted. */
+
+   WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+   curr_data &= ~BIT(7);
+
+   WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+   scwirod(p_port,BIT(7));              /*Wait for DB7 to be released. */
+	while (!(RD_HARPOON(p_port+hp_scsidata_0) & BIT(5)));
+
+   ret_data = (RD_HARPOON(p_port+hp_scsidata_0) & (UCHAR) 0x1F);
+
+   curr_data |= BIT(6);
+
+   WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+   curr_data &= ~BIT(5);
+
+   WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+   scwirod(p_port,BIT(5));              /*Wait for DB5 to be released. */
+
+   curr_data &= ~(BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0)); /*Release data bits */
+   curr_data |= BIT(7);
+
+   WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+   curr_data &= ~BIT(6);
+
+   WR_HARPOON(p_port+hp_scsidata_0, curr_data);
+
+   scwirod(p_port,BIT(6));              /*Wait for DB6 to be released. */
+
+   return(ret_data);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scsendi
+ *
+ * Description: Transfer our Identification string to determine if we
+ *              will be the dominant master.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR scsendi(USHORT p_port, UCHAR p_id_string[])
+#else
+UCHAR scsendi(ULONG p_port, UCHAR p_id_string[])
+#endif
+{
+   UCHAR ret_data,byte_cnt,bit_cnt,defer;
+
+   defer = FALSE;
+
+   for (byte_cnt = 0; byte_cnt < ID_STRING_LENGTH; byte_cnt++) {
+
+      for (bit_cnt = 0x80; bit_cnt != 0 ; bit_cnt >>= 1) {
+
+         if (defer)
+            ret_data = scxferc(p_port,00);
+
+         else if (p_id_string[byte_cnt] & bit_cnt)
+
+               ret_data = scxferc(p_port,02);
+
+            else {
+
+               ret_data = scxferc(p_port,01);
+               if (ret_data & 02)
+                  defer = TRUE;
+               }
+
+         if ((ret_data & 0x1C) == 0x10)
+            return(0x00);  /*End of isolation stage, we won! */
+
+         if (ret_data & 0x1C)
+            return(0xFF);
+
+         if ((defer) && (!(ret_data & 0x1F)))
+            return(0x01);  /*End of isolation stage, we lost. */
+
+         } /*bit loop */
+
+      } /*byte loop */
+
+   if (defer)
+      return(0x01);  /*We lost */
+   else
+      return(0);  /*We WON! Yeeessss! */
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: sciso
+ *
+ * Description: Transfer the Identification string.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR sciso(USHORT p_port, UCHAR p_id_string[])
+#else
+UCHAR sciso(ULONG p_port, UCHAR p_id_string[])
+#endif
+{
+   UCHAR ret_data,the_data,byte_cnt,bit_cnt;
+
+   the_data = 0;
+
+   for (byte_cnt = 0; byte_cnt < ID_STRING_LENGTH; byte_cnt++) {
+
+      for (bit_cnt = 0; bit_cnt < 8; bit_cnt++) {
+
+         ret_data = scxferc(p_port,0);
+
+         if (ret_data & 0xFC)
+            return(0xFF);
+
+         else {
+
+            the_data <<= 1;
+            if (ret_data & BIT(1)) {
+               the_data |= 1;
+               }
+            }
+
+         if ((ret_data & 0x1F) == 0)
+/*
+				if(bit_cnt != 0 || bit_cnt != 8)
+				{
+					byte_cnt = 0;
+					bit_cnt = 0;
+					scxferc(p_port, SYNC_PTRN);
+					scxferc(p_port, ASSIGN_ID);
+					continue;
+				}
+*/
+            if (byte_cnt)
+               return(0x00);
+            else
+               return(0xFF);
+
+         } /*bit loop */
+
+      p_id_string[byte_cnt] = the_data;
+
+      } /*byte loop */
+
+   return(0);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scwirod
+ *
+ * Description: Sample the SCSI data bus making sure the signal has been
+ *              deasserted for the correct number of consecutive samples.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scwirod(USHORT p_port, UCHAR p_data_bit)
+#else
+void scwirod(ULONG p_port, UCHAR p_data_bit)
+#endif
+{
+   UCHAR i;
+
+   i = 0;
+   while ( i < MAX_SCSI_TAR ) {
+
+      if (RD_HARPOON(p_port+hp_scsidata_0) & p_data_bit)
+
+         i = 0;
+
+      else
+
+         i++;
+
+      }
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scwiros
+ *
+ * Description: Sample the SCSI Signal lines making sure the signal has been
+ *              deasserted for the correct number of consecutive samples.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scwiros(USHORT p_port, UCHAR p_data_bit)
+#else
+void scwiros(ULONG p_port, UCHAR p_data_bit)
+#endif
+{
+   UCHAR i;
+
+   i = 0;
+   while ( i < MAX_SCSI_TAR ) {
+
+      if (RD_HARPOON(p_port+hp_scsisig) & p_data_bit)
+
+         i = 0;
+
+      else
+
+         i++;
+
+      }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scvalq
+ *
+ * Description: Make sure we received a valid data byte.
+ *
+ *---------------------------------------------------------------------*/
+
+UCHAR scvalq(UCHAR p_quintet)
+{
+   UCHAR count;
+
+   for (count=1; count < 0x08; count<<=1) {
+      if (!(p_quintet & count))
+         p_quintet -= 0x80;
+      }
+
+   if (p_quintet & 0x18)
+      return(FALSE);
+
+   else
+      return(TRUE);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scsell
+ *
+ * Description: Select the specified device ID using a selection timeout
+ *              less than 4ms.  If somebody responds then it is a legacy
+ *              drive and this ID must be marked as such.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+UCHAR scsell(USHORT p_port, UCHAR targ_id)
+#else
+UCHAR scsell(ULONG p_port, UCHAR targ_id)
+#endif
+{
+#if defined(DOS)
+   USHORT i;
+#else
+   ULONG i;
+#endif
+
+   WR_HARPOON(p_port+hp_page_ctrl,
+      (RD_HARPOON(p_port+hp_page_ctrl) | G_INT_DISABLE));
+
+   ARAM_ACCESS(p_port);
+
+   WR_HARPOON(p_port+hp_addstat,(RD_HARPOON(p_port+hp_addstat) | SCAM_TIMER));
+   WR_HARPOON(p_port+hp_seltimeout,TO_4ms);
+
+
+   for (i = p_port+CMD_STRT; i < p_port+CMD_STRT+12; i+=2) {
+      WRW_HARPOON(i, (MPM_OP+ACOMMAND));
+      }
+   WRW_HARPOON(i, (BRH_OP+ALWAYS+    NP));
+
+   WRW_HARPOON((p_port+hp_intstat),
+	       (RESET | TIMEOUT | SEL | BUS_FREE | AUTO_INT));
+
+   WR_HARPOON(p_port+hp_select_id, targ_id);
+
+   WR_HARPOON(p_port+hp_portctrl_0, SCSI_PORT);
+   WR_HARPOON(p_port+hp_autostart_3, (SELECT | CMD_ONLY_STRT));
+   WR_HARPOON(p_port+hp_scsictrl_0, (SEL_TAR | ENA_RESEL));
+
+
+   while (!(RDW_HARPOON((p_port+hp_intstat)) &
+	    (RESET | PROG_HLT | TIMEOUT | AUTO_INT))) {}
+
+   if (RDW_HARPOON((p_port+hp_intstat)) & RESET)
+         Wait(p_port, TO_250ms);
+
+   DISABLE_AUTO(p_port);
+
+   WR_HARPOON(p_port+hp_addstat,(RD_HARPOON(p_port+hp_addstat) & ~SCAM_TIMER));
+   WR_HARPOON(p_port+hp_seltimeout,TO_290ms);
+
+   SGRAM_ACCESS(p_port);
+
+   if (RDW_HARPOON((p_port+hp_intstat)) & (RESET | TIMEOUT) ) {
+
+      WRW_HARPOON((p_port+hp_intstat),
+		  (RESET | TIMEOUT | SEL | BUS_FREE | PHASE));
+
+      WR_HARPOON(p_port+hp_page_ctrl,
+         (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+      return(FALSE);  /*No legacy device */
+      }
+
+   else {
+
+      while(!(RDW_HARPOON((p_port+hp_intstat)) & BUS_FREE)) {
+				if (RD_HARPOON(p_port+hp_scsisig) & SCSI_REQ)
+					{
+					WR_HARPOON(p_port+hp_scsisig, (SCSI_ACK + S_ILL_PH));
+      			ACCEPT_MSG(p_port);
+					}
+		}
+
+      WRW_HARPOON((p_port+hp_intstat), CLR_ALL_INT_1);
+
+      WR_HARPOON(p_port+hp_page_ctrl,
+         (RD_HARPOON(p_port+hp_page_ctrl) & ~G_INT_DISABLE));
+
+      return(TRUE);  /*Found one of them oldies! */
+      }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scwtsel
+ *
+ * Description: Wait to be selected by another SCAM initiator.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scwtsel(USHORT p_port)
+#else
+void scwtsel(ULONG p_port)
+#endif
+{
+   while(!(RDW_HARPOON((p_port+hp_intstat)) & SCAM_SEL)) {}
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: inisci
+ *
+ * Description: Setup the data Structure with the info from the EEPROM.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void inisci(UCHAR p_card, USHORT p_port, UCHAR p_our_id)
+#else
+void inisci(UCHAR p_card, ULONG p_port, UCHAR p_our_id)
+#endif
+{
+   UCHAR i,k,max_id;
+   USHORT ee_data;
+	PNVRamInfo pCurrNvRam;
+
+	pCurrNvRam = BL_Card[p_card].pNvRamInfo;
+
+   if (RD_HARPOON(p_port+hp_page_ctrl) & NARROW_SCSI_CARD)
+      max_id = 0x08;
+
+   else
+      max_id = 0x10;
+
+	if(pCurrNvRam){
+		for(i = 0; i < max_id; i++){
+
+			for(k = 0; k < 4; k++)
+				scamInfo[i].id_string[k] = pCurrNvRam->niScamTbl[i][k];
+			for(k = 4; k < ID_STRING_LENGTH; k++)
+				scamInfo[i].id_string[k] = (UCHAR) 0x00;
+
+	      if(scamInfo[i].id_string[0] == 0x00)
+      	   scamInfo[i].state = ID_UNUSED;  /*Default to unused ID. */
+	      else
+   	      scamInfo[i].state = ID_UNASSIGNED;  /*Default to unassigned ID. */
+
+		}
+	}else {
+	   for (i=0; i < max_id; i++)
+   	   {
+      	for (k=0; k < ID_STRING_LENGTH; k+=2)
+	         {
+   	      ee_data = utilEERead(p_port, (USHORT)((EE_SCAMBASE/2) +
+      	     (USHORT) (i*((USHORT)ID_STRING_LENGTH/2)) + (USHORT)(k/2)));
+         	scamInfo[i].id_string[k] = (UCHAR) ee_data;
+	         ee_data >>= 8;
+   	      scamInfo[i].id_string[k+1] = (UCHAR) ee_data;
+      	   }
+
+	      if ((scamInfo[i].id_string[0] == 0x00) ||
+   	       (scamInfo[i].id_string[0] == 0xFF))
+
+      	   scamInfo[i].state = ID_UNUSED;  /*Default to unused ID. */
+
+	      else
+   	      scamInfo[i].state = ID_UNASSIGNED;  /*Default to unassigned ID. */
+
+      	}
+	}
+	for(k = 0; k < ID_STRING_LENGTH; k++)
+		scamInfo[p_our_id].id_string[k] = scamHAString[k];
+
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scmachid
+ *
+ * Description: Match the Device ID string with our values stored in
+ *              the EEPROM.
+ *
+ *---------------------------------------------------------------------*/
+
+UCHAR scmachid(UCHAR p_card, UCHAR p_id_string[])
+{
+
+   UCHAR i,k,match;
+
+
+   for (i=0; i < MAX_SCSI_TAR; i++) {
+
+#if !defined(SCAM_LEV_2)
+      if (scamInfo[i].state == ID_UNASSIGNED)
+         {
+#endif
+         match = TRUE;
+
+         for (k=0; k < ID_STRING_LENGTH; k++)
+            {
+            if (p_id_string[k] != scamInfo[i].id_string[k])
+               match = FALSE;
+            }
+
+         if (match)
+            {
+            scamInfo[i].state = ID_ASSIGNED;
+            return(i);
+            }
+
+#if !defined(SCAM_LEV_2)
+         }
+#endif
+
+      }
+
+
+
+   if (p_id_string[0] & BIT(5))
+      i = 8;
+   else
+      i = MAX_SCSI_TAR;
+
+   if (((p_id_string[0] & 0x06) == 0x02) || ((p_id_string[0] & 0x06) == 0x04))
+      match = p_id_string[1] & (UCHAR) 0x1F;
+   else
+      match = 7;
+
+   while (i > 0)
+      {
+      i--;
+
+      if (scamInfo[match].state == ID_UNUSED)
+         {
+         for (k=0; k < ID_STRING_LENGTH; k++)
+            {
+            scamInfo[match].id_string[k] = p_id_string[k];
+            }
+
+         scamInfo[match].state = ID_ASSIGNED;
+
+			if(BL_Card[p_card].pNvRamInfo == NULL)
+	         BL_Card[p_card].globalFlags |= F_UPDATE_EEPROM;
+         return(match);
+
+         }
+
+
+      match--;
+
+      if (match == 0xFF)
+         if (p_id_string[0] & BIT(5))
+            match = 7;
+         else
+            match = MAX_SCSI_TAR-1;
+      }
+
+
+
+   if (p_id_string[0] & BIT(7))
+      {
+      return(CLR_PRIORITY);
+      }
+
+
+   if (p_id_string[0] & BIT(5))
+      i = 8;
+   else
+      i = MAX_SCSI_TAR;
+
+   if (((p_id_string[0] & 0x06) == 0x02) || ((p_id_string[0] & 0x06) == 0x04))
+      match = p_id_string[1] & (UCHAR) 0x1F;
+   else
+      match = 7;
+
+   while (i > 0)
+      {
+
+      i--;
+
+      if (scamInfo[match].state == ID_UNASSIGNED)
+         {
+         for (k=0; k < ID_STRING_LENGTH; k++)
+            {
+            scamInfo[match].id_string[k] = p_id_string[k];
+            }
+
+         scamInfo[match].id_string[0] |= BIT(7);
+         scamInfo[match].state = ID_ASSIGNED;
+			if(BL_Card[p_card].pNvRamInfo == NULL)
+	         BL_Card[p_card].globalFlags |= F_UPDATE_EEPROM;
+         return(match);
+
+         }
+
+
+      match--;
+
+      if (match == 0xFF)
+         if (p_id_string[0] & BIT(5))
+            match = 7;
+         else
+            match = MAX_SCSI_TAR-1;
+      }
+
+   return(NO_ID_AVAIL);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: scsavdi
+ *
+ * Description: Save off the device SCAM ID strings.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void scsavdi(UCHAR p_card, USHORT p_port)
+#else
+void scsavdi(UCHAR p_card, ULONG p_port)
+#endif
+{
+   UCHAR i,k,max_id;
+   USHORT ee_data,sum_data;
+
+
+   sum_data = 0x0000;
+
+   for (i = 1; i < EE_SCAMBASE/2; i++)
+      {
+      sum_data += utilEERead(p_port, i);
+      }
+
+
+   utilEEWriteOnOff(p_port,1);   /* Enable write access to the EEPROM */
+
+   if (RD_HARPOON(p_port+hp_page_ctrl) & NARROW_SCSI_CARD)
+      max_id = 0x08;
+
+   else
+      max_id = 0x10;
+
+   for (i=0; i < max_id; i++)
+      {
+
+      for (k=0; k < ID_STRING_LENGTH; k+=2)
+         {
+         ee_data = scamInfo[i].id_string[k+1];
+         ee_data <<= 8;
+         ee_data |= scamInfo[i].id_string[k];
+         sum_data += ee_data;
+         utilEEWrite(p_port, ee_data, (USHORT)((EE_SCAMBASE/2) +
+            (USHORT)(i*((USHORT)ID_STRING_LENGTH/2)) + (USHORT)(k/2)));
+         }
+      }
+
+
+   utilEEWrite(p_port, sum_data, EEPROM_CHECK_SUM/2);
+   utilEEWriteOnOff(p_port,0);   /* Turn off write access */
+}
+#ident "$Id: diagnose.c 1.9 1997/01/31 02:09:48 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   diagnose.c  $
+ *
+ *   Description:  Diagnostic funtions for testing the integrity of
+ *                 the HARPOON.
+ *
+ *   $Date: 1997/01/31 02:09:48 $
+ *
+ *   $Revision: 1.9 $
+ *
+ *----------------------------------------------------------------------*/
+
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+	/*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <eeprom.h>*/
+/*#include <harpoon.h>*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: XbowInit
+ *
+ * Description: Setup the Xbow for normal operation.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void XbowInit(USHORT port, UCHAR ScamFlg)
+#else
+void XbowInit(ULONG port, UCHAR ScamFlg)
+#endif
+{
+UCHAR i;
+
+	i = RD_HARPOON(port+hp_page_ctrl);
+	WR_HARPOON(port+hp_page_ctrl, (UCHAR) (i | G_INT_DISABLE));
+
+   WR_HARPOON(port+hp_scsireset,0x00);
+   WR_HARPOON(port+hp_portctrl_1,HOST_MODE8);
+
+   WR_HARPOON(port+hp_scsireset,(DMA_RESET | HPSCSI_RESET | PROG_RESET | \
+				 FIFO_CLR));
+
+   WR_HARPOON(port+hp_scsireset,0x00);
+
+   WR_HARPOON(port+hp_clkctrl_0,CLKCTRL_DEFAULT);
+
+   WR_HARPOON(port+hp_scsisig,0x00);         /*  Clear any signals we might */
+   WR_HARPOON(port+hp_scsictrl_0,ENA_SCAM_SEL);
+
+   WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+
+#if defined(SCAM_LEV_2)
+   default_intena = RESET | RSEL | PROG_HLT | TIMEOUT |
+		    BUS_FREE | XFER_CNT_0 | AUTO_INT;
+
+   if ((ScamFlg & SCAM_ENABLED) && (ScamFlg & SCAM_LEVEL2))
+		default_intena |= SCAM_SEL;
+
+#else
+   default_intena = RESET | RSEL | PROG_HLT | TIMEOUT |
+		    BUS_FREE | XFER_CNT_0 | AUTO_INT;
+#endif
+   WRW_HARPOON((port+hp_intena), default_intena);
+
+   WR_HARPOON(port+hp_seltimeout,TO_290ms);
+
+   /* Turn on SCSI_MODE8 for narrow cards to fix the
+      strapping issue with the DUAL CHANNEL card */
+   if (RD_HARPOON(port+hp_page_ctrl) & NARROW_SCSI_CARD)
+      WR_HARPOON(port+hp_addstat,SCSI_MODE8);
+
+#if defined(NO_BIOS_OPTION)
+
+   WR_HARPOON(port+hp_synctarg_0,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_1,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_2,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_3,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_4,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_5,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_6,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_7,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_8,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_9,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_10,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_11,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_12,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_13,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_14,NARROW_SCSI);
+   WR_HARPOON(port+hp_synctarg_15,NARROW_SCSI);
+
+#endif
+	WR_HARPOON(port+hp_page_ctrl, i);
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: BusMasterInit
+ *
+ * Description: Initialize the BusMaster for normal operations.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void BusMasterInit(USHORT p_port)
+#else
+void BusMasterInit(ULONG p_port)
+#endif
+{
+
+
+   WR_HARPOON(p_port+hp_sys_ctrl, DRVR_RST);
+   WR_HARPOON(p_port+hp_sys_ctrl, 0x00);
+
+   WR_HARPOON(p_port+hp_host_blk_cnt, XFER_BLK64);
+
+
+   WR_HARPOON(p_port+hp_bm_ctrl, (BMCTRL_DEFAULT));
+
+   WR_HARPOON(p_port+hp_ee_ctrl, (SCSI_TERM_ENA_H));
+
+
+#if defined(NT)
+
+   WR_HARPOON(p_port+hp_pci_cmd_cfg, (RD_HARPOON(p_port+hp_pci_cmd_cfg)
+      & ~MEM_SPACE_ENA));
+
+#endif
+
+   RD_HARPOON(p_port+hp_int_status);        /*Clear interrupts. */
+   WR_HARPOON(p_port+hp_int_mask, (INT_CMD_COMPL | SCSI_INTERRUPT));
+   WR_HARPOON(p_port+hp_page_ctrl, (RD_HARPOON(p_port+hp_page_ctrl) &
+      ~SCATTER_EN));
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: DiagXbow
+ *
+ * Description: Test Xbow integrity.  Non-zero return indicates an error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+int DiagXbow(USHORT port)
+#else
+int DiagXbow(ULONG port)
+#endif
+{
+   unsigned char fifo_cnt,loop_cnt;
+
+   unsigned char fifodata[5];
+   fifodata[0] = 0x00;
+   fifodata[1] = 0xFF;
+   fifodata[2] = 0x55;
+   fifodata[3] = 0xAA;
+   fifodata[4] = 0x00;
+
+
+   WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+   WRW_HARPOON((port+hp_intena), 0x0000);
+
+   WR_HARPOON(port+hp_seltimeout,TO_5ms);
+
+   WR_HARPOON(port+hp_portctrl_0,START_TO);
+
+
+   for(fifodata[4] = 0x01; fifodata[4] != (UCHAR) 0; fifodata[4] = fifodata[4] << 1) {
+
+      WR_HARPOON(port+hp_selfid_0,fifodata[4]);
+      WR_HARPOON(port+hp_selfid_1,fifodata[4]);
+
+      if ((RD_HARPOON(port+hp_selfid_0) != fifodata[4]) ||
+          (RD_HARPOON(port+hp_selfid_1) != fifodata[4]))
+         return(1);
+      }
+
+
+   for(loop_cnt = 0; loop_cnt < 4; loop_cnt++) {
+
+      WR_HARPOON(port+hp_portctrl_0,(HOST_PORT | HOST_WRT | START_TO));
+
+
+      for (fifo_cnt = 0; fifo_cnt < FIFO_LEN; fifo_cnt++) {
+
+         WR_HARPOON(port+hp_fifodata_0, fifodata[loop_cnt]);
+         }
+
+
+      if (!(RD_HARPOON(port+hp_xferstat) & FIFO_FULL))
+         return(1);
+
+
+      WR_HARPOON(port+hp_portctrl_0,(HOST_PORT | START_TO));
+
+      for (fifo_cnt = 0; fifo_cnt < FIFO_LEN; fifo_cnt++) {
+
+         if (RD_HARPOON(port+hp_fifodata_0) != fifodata[loop_cnt])
+            return(1);
+         }
+
+
+      if (!(RD_HARPOON(port+hp_xferstat) & FIFO_EMPTY))
+         return(1);
+      }
+
+
+   while(!(RDW_HARPOON((port+hp_intstat)) & TIMEOUT)) {}
+
+
+   WR_HARPOON(port+hp_seltimeout,TO_290ms);
+
+   WRW_HARPOON((port+hp_intstat), CLR_ALL_INT);
+
+   WRW_HARPOON((port+hp_intena), default_intena);
+
+   return(0);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: DiagBusMaster
+ *
+ * Description: Test BusMaster integrity.  Non-zero return indicates an
+ *              error.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+int DiagBusMaster(USHORT port)
+#else
+int DiagBusMaster(ULONG port)
+#endif
+{
+   UCHAR testdata;
+
+   for(testdata = (UCHAR) 1; testdata != (UCHAR)0; testdata = testdata << 1) {
+
+      WR_HARPOON(port+hp_xfer_cnt_lo,testdata);
+      WR_HARPOON(port+hp_xfer_cnt_mi,testdata);
+      WR_HARPOON(port+hp_xfer_cnt_hi,testdata);
+      WR_HARPOON(port+hp_host_addr_lo,testdata);
+      WR_HARPOON(port+hp_host_addr_lmi,testdata);
+      WR_HARPOON(port+hp_host_addr_hmi,testdata);
+      WR_HARPOON(port+hp_host_addr_hi,testdata);
+
+      if ((RD_HARPOON(port+hp_xfer_cnt_lo) != testdata)   ||
+          (RD_HARPOON(port+hp_xfer_cnt_mi) != testdata)   ||
+          (RD_HARPOON(port+hp_xfer_cnt_hi) != testdata)   ||
+          (RD_HARPOON(port+hp_host_addr_lo) != testdata)  ||
+          (RD_HARPOON(port+hp_host_addr_lmi) != testdata) ||
+          (RD_HARPOON(port+hp_host_addr_hmi) != testdata) ||
+          (RD_HARPOON(port+hp_host_addr_hi) != testdata))
+
+         return(1);
+      }
+   RD_HARPOON(port+hp_int_status);        /*Clear interrupts. */
+   return(0);
+}
+
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: DiagEEPROM
+ *
+ * Description: Verfiy checksum and 'Key' and initialize the EEPROM if
+ *              neccessary.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void DiagEEPROM(USHORT p_port)
+#else
+void DiagEEPROM(ULONG p_port)
+#endif
+
+{
+   USHORT index,temp,max_wd_cnt;
+
+   if (RD_HARPOON(p_port+hp_page_ctrl) & NARROW_SCSI_CARD)
+      max_wd_cnt = EEPROM_WD_CNT;
+   else
+      max_wd_cnt = EEPROM_WD_CNT * 2;
+
+   temp = utilEERead(p_port, FW_SIGNATURE/2);
+
+   if (temp == 0x4641) {
+
+      for (index = 2; index < max_wd_cnt; index++) {
+
+         temp += utilEERead(p_port, index);
+
+         }
+
+      if (temp == utilEERead(p_port, EEPROM_CHECK_SUM/2)) {
+
+         return;          /*EEPROM is Okay so return now! */
+         }
+      }
+
+
+   utilEEWriteOnOff(p_port,(UCHAR)1);
+
+   for (index = 0; index < max_wd_cnt; index++) {
+
+      utilEEWrite(p_port, 0x0000, index);
+      }
+
+   temp = 0;
+
+   utilEEWrite(p_port, 0x4641, FW_SIGNATURE/2);
+   temp += 0x4641;
+   utilEEWrite(p_port, 0x3920, MODEL_NUMB_0/2);
+   temp += 0x3920;
+   utilEEWrite(p_port, 0x3033, MODEL_NUMB_2/2);
+   temp += 0x3033;
+   utilEEWrite(p_port, 0x2020, MODEL_NUMB_4/2);
+   temp += 0x2020;
+   utilEEWrite(p_port, 0x70D3, SYSTEM_CONFIG/2);
+   temp += 0x70D3;
+   utilEEWrite(p_port, 0x0010, BIOS_CONFIG/2);
+   temp += 0x0010;
+   utilEEWrite(p_port, 0x0007, SCAM_CONFIG/2);
+   temp += 0x0007;
+   utilEEWrite(p_port, 0x0007, ADAPTER_SCSI_ID/2);
+   temp += 0x0007;
+
+   utilEEWrite(p_port, 0x0000, IGNORE_B_SCAN/2);
+   temp += 0x0000;
+   utilEEWrite(p_port, 0x0000, SEND_START_ENA/2);
+   temp += 0x0000;
+   utilEEWrite(p_port, 0x0000, DEVICE_ENABLE/2);
+   temp += 0x0000;
+
+   utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL01/2);
+   temp += 0x4242;
+   utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL23/2);
+   temp += 0x4242;
+   utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL45/2);
+   temp += 0x4242;
+   utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL67/2);
+   temp += 0x4242;
+   utilEEWrite(p_port, 0x4242, SYNC_RATE_TBL89/2);
+   temp += 0x4242;
+   utilEEWrite(p_port, 0x4242, SYNC_RATE_TBLab/2);
+   temp += 0x4242;
+   utilEEWrite(p_port, 0x4242, SYNC_RATE_TBLcd/2);
+   temp += 0x4242;
+   utilEEWrite(p_port, 0x4242, SYNC_RATE_TBLef/2);
+   temp += 0x4242;
+
+
+   utilEEWrite(p_port, 0x6C46, 64/2);  /*PRODUCT ID */
+   temp += 0x6C46;
+   utilEEWrite(p_port, 0x7361, 66/2);  /* FlashPoint LT   */
+   temp += 0x7361;
+   utilEEWrite(p_port, 0x5068, 68/2);
+   temp += 0x5068;
+   utilEEWrite(p_port, 0x696F, 70/2);
+   temp += 0x696F;
+   utilEEWrite(p_port, 0x746E, 72/2);
+   temp += 0x746E;
+   utilEEWrite(p_port, 0x4C20, 74/2);
+   temp += 0x4C20;
+   utilEEWrite(p_port, 0x2054, 76/2);
+   temp += 0x2054;
+   utilEEWrite(p_port, 0x2020, 78/2);
+   temp += 0x2020;
+
+   index = ((EE_SCAMBASE/2)+(7*16));
+   utilEEWrite(p_port, (0x0700+TYPE_CODE0), index);
+   temp += (0x0700+TYPE_CODE0);
+   index++;
+   utilEEWrite(p_port, 0x5542, index);            /*Vendor ID code */
+   temp += 0x5542;                                /* BUSLOGIC      */
+   index++;
+   utilEEWrite(p_port, 0x4C53, index);
+   temp += 0x4C53;
+   index++;
+   utilEEWrite(p_port, 0x474F, index);
+   temp += 0x474F;
+   index++;
+   utilEEWrite(p_port, 0x4349, index);
+   temp += 0x4349;
+   index++;
+   utilEEWrite(p_port, 0x5442, index);            /*Vendor unique code */
+   temp += 0x5442;                         /* BT- 930           */
+   index++;
+   utilEEWrite(p_port, 0x202D, index);
+   temp += 0x202D;
+   index++;
+   utilEEWrite(p_port, 0x3339, index);
+   temp += 0x3339;
+   index++;                                 /*Serial #          */
+   utilEEWrite(p_port, 0x2030, index);             /* 01234567         */
+   temp += 0x2030;
+   index++;
+   utilEEWrite(p_port, 0x5453, index);
+   temp += 0x5453;
+   index++;
+   utilEEWrite(p_port, 0x5645, index);
+   temp += 0x5645;
+   index++;
+   utilEEWrite(p_port, 0x2045, index);
+   temp += 0x2045;
+   index++;
+   utilEEWrite(p_port, 0x202F, index);
+   temp += 0x202F;
+   index++;
+   utilEEWrite(p_port, 0x4F4A, index);
+   temp += 0x4F4A;
+   index++;
+   utilEEWrite(p_port, 0x204E, index);
+   temp += 0x204E;
+   index++;
+   utilEEWrite(p_port, 0x3539, index);
+   temp += 0x3539;
+
+
+
+   utilEEWrite(p_port, temp, EEPROM_CHECK_SUM/2);
+
+   utilEEWriteOnOff(p_port,(UCHAR)0);
+
+}
+
+#ident "$Id: utility.c 1.22 1997/01/31 02:12:23 mohan Exp $"
+/*----------------------------------------------------------------------
+ *
+ *
+ *   Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+ *
+ *   This file is available under both the GNU General Public License
+ *   and a BSD-style copyright; see LICENSE.FlashPoint for details.
+ *
+ *   $Workfile:   utility.c  $
+ *
+ *   Description:  Utility functions relating to queueing and EEPROM
+ *                 manipulation and any other garbage functions.
+ *
+ *   $Date: 1997/01/31 02:12:23 $
+ *
+ *   $Revision: 1.22 $
+ *
+ *----------------------------------------------------------------------*/
+/*#include <globals.h>*/
+
+#if (FW_TYPE==_UCB_MGR_)
+	/*#include <budi.h>*/
+#endif
+
+/*#include <sccbmgr.h>*/
+/*#include <blx30.h>*/
+/*#include <target.h>*/
+/*#include <scsi2.h>*/
+/*#include <harpoon.h>*/
+
+
+/*
+extern SCCBCARD BL_Card[MAX_CARDS];
+extern SCCBMGR_TAR_INFO sccbMgrTbl[MAX_CARDS][MAX_SCSI_TAR];
+extern unsigned int SccbGlobalFlags;
+*/
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Search Select
+ *
+ * Description: Try to find a new command to execute.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueSearchSelect(PSCCBcard pCurrCard, UCHAR p_card)
+{
+   UCHAR scan_ptr, lun;
+   PSCCBMgr_tar_info currTar_Info;
+	PSCCB pOldSccb;
+
+   scan_ptr = pCurrCard->scanIndex;
+	do 
+	{
+		currTar_Info = &sccbMgrTbl[p_card][scan_ptr];
+		if((pCurrCard->globalFlags & F_CONLUN_IO) && 
+			((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING))
+		{
+			if (currTar_Info->TarSelQ_Cnt != 0)
+			{
+
+				scan_ptr++;
+				if (scan_ptr == MAX_SCSI_TAR)
+					scan_ptr = 0;
+				
+				for(lun=0; lun < MAX_LUN; lun++)
+				{
+					if(currTar_Info->TarLUNBusy[lun] == FALSE)
+					{
+
+						pCurrCard->currentSCCB = currTar_Info->TarSelQ_Head;
+						pOldSccb = NULL;
+
+						while((pCurrCard->currentSCCB != NULL) &&
+								 (lun != pCurrCard->currentSCCB->Lun))
+						{
+							pOldSccb = pCurrCard->currentSCCB;
+							pCurrCard->currentSCCB = (PSCCB)(pCurrCard->currentSCCB)->
+																	Sccb_forwardlink;
+						}
+						if(pCurrCard->currentSCCB == NULL)
+							continue;
+						if(pOldSccb != NULL)
+						{
+							pOldSccb->Sccb_forwardlink = (PSCCB)(pCurrCard->currentSCCB)->
+																	Sccb_forwardlink;
+							pOldSccb->Sccb_backlink = (PSCCB)(pCurrCard->currentSCCB)->
+																	Sccb_backlink;
+							currTar_Info->TarSelQ_Cnt--;
+						}
+						else
+						{
+							currTar_Info->TarSelQ_Head = (PSCCB)(pCurrCard->currentSCCB)->Sccb_forwardlink;
+					
+							if (currTar_Info->TarSelQ_Head == NULL)
+							{
+								currTar_Info->TarSelQ_Tail = NULL;
+								currTar_Info->TarSelQ_Cnt = 0;
+							}
+							else
+							{
+								currTar_Info->TarSelQ_Cnt--;
+								currTar_Info->TarSelQ_Head->Sccb_backlink = (PSCCB)NULL;
+							}
+						}
+					pCurrCard->scanIndex = scan_ptr;
+
+					pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
+
+					break;
+					}
+				}
+			}
+
+			else 
+			{
+				scan_ptr++;
+				if (scan_ptr == MAX_SCSI_TAR) {
+					scan_ptr = 0;
+				}
+			}
+
+		}
+		else
+		{
+			if ((currTar_Info->TarSelQ_Cnt != 0) &&
+				(currTar_Info->TarLUNBusy[0] == FALSE))
+			{
+
+				pCurrCard->currentSCCB = currTar_Info->TarSelQ_Head;
+
+				currTar_Info->TarSelQ_Head = (PSCCB)(pCurrCard->currentSCCB)->Sccb_forwardlink;
+
+				if (currTar_Info->TarSelQ_Head == NULL)
+				{
+					currTar_Info->TarSelQ_Tail = NULL;
+					currTar_Info->TarSelQ_Cnt = 0;
+				}
+				else
+				{
+					currTar_Info->TarSelQ_Cnt--;
+					currTar_Info->TarSelQ_Head->Sccb_backlink = (PSCCB)NULL;
+				}
+
+				scan_ptr++;
+				if (scan_ptr == MAX_SCSI_TAR)
+					scan_ptr = 0;
+
+				pCurrCard->scanIndex = scan_ptr;
+
+				pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
+
+				break;
+			}
+
+			else 
+			{
+				scan_ptr++;
+				if (scan_ptr == MAX_SCSI_TAR) 
+				{
+					scan_ptr = 0;
+				}
+			}
+		}
+	} while (scan_ptr != pCurrCard->scanIndex);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Select Fail
+ *
+ * Description: Add the current SCCB to the head of the Queue.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueSelectFail(PSCCBcard pCurrCard, UCHAR p_card)
+{
+   UCHAR thisTarg;
+   PSCCBMgr_tar_info currTar_Info;
+
+   if (pCurrCard->currentSCCB != NULL)
+	  {
+	  thisTarg = (UCHAR)(((PSCCB)(pCurrCard->currentSCCB))->TargID);
+      currTar_Info = &sccbMgrTbl[p_card][thisTarg];
+
+      pCurrCard->currentSCCB->Sccb_backlink = (PSCCB)NULL;
+
+      pCurrCard->currentSCCB->Sccb_forwardlink = currTar_Info->TarSelQ_Head;
+
+	  if (currTar_Info->TarSelQ_Cnt == 0)
+		 {
+		 currTar_Info->TarSelQ_Tail = pCurrCard->currentSCCB;
+		 }
+
+	  else
+		 {
+		 currTar_Info->TarSelQ_Head->Sccb_backlink = pCurrCard->currentSCCB;
+		 }
+
+
+	  currTar_Info->TarSelQ_Head = pCurrCard->currentSCCB;
+
+	  pCurrCard->currentSCCB = NULL;
+	  currTar_Info->TarSelQ_Cnt++;
+	  }
+}
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Command Complete
+ *
+ * Description: Call the callback function with the current SCCB.
+ *
+ *---------------------------------------------------------------------*/
+
+void queueCmdComplete(PSCCBcard pCurrCard, PSCCB p_sccb, UCHAR p_card)
+{
+
+#if (FW_TYPE==_UCB_MGR_)
+
+   u08bits SCSIcmd;
+   CALL_BK_FN callback;
+   PSCCBMgr_tar_info currTar_Info;
+
+   PUCB p_ucb;
+   p_ucb=p_sccb->Sccb_ucb_ptr;
+
+   SCSIcmd = p_sccb->Cdb[0];
+
+
+   if (!(p_sccb->Sccb_XferState & F_ALL_XFERRED))
+   {
+
+      if ((p_ucb->UCB_opcode & OPC_CHK_UNDER_OVER_RUN)                     &&
+         (p_sccb->HostStatus == SCCB_COMPLETE)                             &&
+         (p_sccb->TargetStatus != SSCHECK))
+
+         if ((SCSIcmd == SCSI_READ)             ||
+             (SCSIcmd == SCSI_WRITE)            ||
+             (SCSIcmd == SCSI_READ_EXTENDED)    ||
+             (SCSIcmd == SCSI_WRITE_EXTENDED)   ||
+             (SCSIcmd == SCSI_WRITE_AND_VERIFY) ||
+             (SCSIcmd == SCSI_START_STOP_UNIT)  ||
+             (pCurrCard->globalFlags & F_NO_FILTER)
+            )
+               p_sccb->HostStatus = SCCB_DATA_UNDER_RUN;
+   }
+
+   p_ucb->UCB_status=SCCB_SUCCESS;
+
+   if ((p_ucb->UCB_hbastat=p_sccb->HostStatus) || (p_ucb->UCB_scsistat=p_sccb->TargetStatus))
+   {
+      p_ucb->UCB_status=SCCB_ERROR;
+   }
+
+   if ((p_sccb->OperationCode == RESIDUAL_SG_COMMAND) ||
+      (p_sccb->OperationCode == RESIDUAL_COMMAND))
+   {
+
+         utilUpdateResidual(p_sccb);
+
+         p_ucb->UCB_datalen=p_sccb->DataLength;
+   }
+
+   pCurrCard->cmdCounter--;
+   if (!pCurrCard->cmdCounter)
+   {
+
+      if (pCurrCard->globalFlags & F_GREEN_PC)
+      {
+         WR_HARPOON(pCurrCard->ioPort+hp_clkctrl_0,(PWR_DWN | CLKCTRL_DEFAULT));
+         WR_HARPOON(pCurrCard->ioPort+hp_sys_ctrl, STOP_CLK);
+      }
+
+      WR_HARPOON(pCurrCard->ioPort+hp_semaphore,
+      (RD_HARPOON(pCurrCard->ioPort+hp_semaphore) & ~SCCB_MGR_ACTIVE));
+   }
+
+	if(pCurrCard->discQCount != 0)
+	{
+      currTar_Info = &sccbMgrTbl[p_card][p_sccb->TargID];
+		if(((pCurrCard->globalFlags & F_CONLUN_IO) &&
+			((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+		{
+			pCurrCard->discQCount--;
+			pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[p_sccb->Lun]] = NULL;
+		}
+		else
+		{
+			if(p_sccb->Sccb_tag)
+			{
+				pCurrCard->discQCount--;
+				pCurrCard->discQ_Tbl[p_sccb->Sccb_tag] = NULL;
+			}else
+			{
+				pCurrCard->discQCount--;
+				pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]] = NULL;
+			}
+		}
+
+	}
+   callback = (CALL_BK_FN)p_ucb->UCB_callback;
+   callback(p_ucb);
+   pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
+   pCurrCard->currentSCCB = NULL;
+}
+
+
+
+
+#else
+
+   UCHAR i, SCSIcmd;
+   CALL_BK_FN callback;
+   PSCCBMgr_tar_info currTar_Info;
+
+   SCSIcmd = p_sccb->Cdb[0];
+
+
+   if (!(p_sccb->Sccb_XferState & F_ALL_XFERRED)) {
+
+	  if ((p_sccb->ControlByte & (SCCB_DATA_XFER_OUT | SCCB_DATA_XFER_IN)) &&
+		 (p_sccb->HostStatus == SCCB_COMPLETE)                             &&
+		 (p_sccb->TargetStatus != SSCHECK))
+
+		 if ((SCSIcmd == SCSI_READ)             ||
+			 (SCSIcmd == SCSI_WRITE)            ||
+			 (SCSIcmd == SCSI_READ_EXTENDED)    ||
+			 (SCSIcmd == SCSI_WRITE_EXTENDED)   ||
+			 (SCSIcmd == SCSI_WRITE_AND_VERIFY) ||
+			 (SCSIcmd == SCSI_START_STOP_UNIT)  ||
+			 (pCurrCard->globalFlags & F_NO_FILTER)
+			)
+			   p_sccb->HostStatus = SCCB_DATA_UNDER_RUN;
+	  }
+
+
+	if(p_sccb->SccbStatus == SCCB_IN_PROCESS)
+	{
+	   if (p_sccb->HostStatus || p_sccb->TargetStatus)
+		  p_sccb->SccbStatus = SCCB_ERROR;
+	   else
+		  p_sccb->SccbStatus = SCCB_SUCCESS;
+	}
+
+   if (p_sccb->Sccb_XferState & F_AUTO_SENSE) {
+
+	  p_sccb->CdbLength = p_sccb->Save_CdbLen;
+	  for (i=0; i < 6; i++) {
+		 p_sccb->Cdb[i] = p_sccb->Save_Cdb[i];
+		 }
+	  }
+
+   if ((p_sccb->OperationCode == RESIDUAL_SG_COMMAND) ||
+	  (p_sccb->OperationCode == RESIDUAL_COMMAND)) {
+
+		 utilUpdateResidual(p_sccb);
+		 }
+
+   pCurrCard->cmdCounter--;
+   if (!pCurrCard->cmdCounter) {
+
+	  if (pCurrCard->globalFlags & F_GREEN_PC) {
+		 WR_HARPOON(pCurrCard->ioPort+hp_clkctrl_0,(PWR_DWN | CLKCTRL_DEFAULT));
+		 WR_HARPOON(pCurrCard->ioPort+hp_sys_ctrl, STOP_CLK);
+		 }
+
+	  WR_HARPOON(pCurrCard->ioPort+hp_semaphore,
+	  (RD_HARPOON(pCurrCard->ioPort+hp_semaphore) & ~SCCB_MGR_ACTIVE));
+
+	  }
+
+	if(pCurrCard->discQCount != 0)
+	{
+      currTar_Info = &sccbMgrTbl[p_card][p_sccb->TargID];
+		if(((pCurrCard->globalFlags & F_CONLUN_IO) &&
+			((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+		{
+			pCurrCard->discQCount--;
+			pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[p_sccb->Lun]] = NULL;
+		}
+		else
+		{
+			if(p_sccb->Sccb_tag)
+			{
+				pCurrCard->discQCount--;
+				pCurrCard->discQ_Tbl[p_sccb->Sccb_tag] = NULL;
+			}else
+			{
+				pCurrCard->discQCount--;
+				pCurrCard->discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]] = NULL;
+			}
+		}
+
+	}
+
+	callback = (CALL_BK_FN)p_sccb->SccbCallback;
+   callback(p_sccb);
+   pCurrCard->globalFlags |= F_NEW_SCCB_CMD;
+   pCurrCard->currentSCCB = NULL;
+}
+#endif /* ( if FW_TYPE==...) */
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Disconnect
+ *
+ * Description: Add SCCB to our disconnect array.
+ *
+ *---------------------------------------------------------------------*/
+void queueDisconnect(PSCCB p_sccb, UCHAR p_card)
+{
+   PSCCBMgr_tar_info currTar_Info;
+
+	currTar_Info = &sccbMgrTbl[p_card][p_sccb->TargID];
+
+	if(((BL_Card[p_card].globalFlags & F_CONLUN_IO) &&
+		((currTar_Info->TarStatus & TAR_TAG_Q_MASK) != TAG_Q_TRYING)))
+	{
+		BL_Card[p_card].discQ_Tbl[currTar_Info->LunDiscQ_Idx[p_sccb->Lun]] = p_sccb;
+	}
+	else
+	{
+		if (p_sccb->Sccb_tag)
+		{
+			BL_Card[p_card].discQ_Tbl[p_sccb->Sccb_tag] = p_sccb;
+			sccbMgrTbl[p_card][p_sccb->TargID].TarLUNBusy[0] = FALSE;
+			sccbMgrTbl[p_card][p_sccb->TargID].TarTagQ_Cnt++;
+		}else
+		{
+			BL_Card[p_card].discQ_Tbl[currTar_Info->LunDiscQ_Idx[0]] = p_sccb;
+		}
+	}
+	BL_Card[p_card].currentSCCB = NULL;
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Flush SCCB
+ *
+ * Description: Flush all SCCB's back to the host driver for this target.
+ *
+ *---------------------------------------------------------------------*/
+
+void  queueFlushSccb(UCHAR p_card, UCHAR error_code)
+{
+   UCHAR qtag,thisTarg;
+   PSCCB currSCCB;
+   PSCCBMgr_tar_info currTar_Info;
+
+   currSCCB = BL_Card[p_card].currentSCCB;
+	if(currSCCB != NULL)
+	{
+	   thisTarg = (UCHAR)currSCCB->TargID;
+   	currTar_Info = &sccbMgrTbl[p_card][thisTarg];
+
+	   for (qtag=0; qtag<QUEUE_DEPTH; qtag++) {
+
+		  if (BL_Card[p_card].discQ_Tbl[qtag] && 
+					(BL_Card[p_card].discQ_Tbl[qtag]->TargID == thisTarg))
+			 {
+
+			 BL_Card[p_card].discQ_Tbl[qtag]->HostStatus = (UCHAR)error_code;
+			
+			 queueCmdComplete(&BL_Card[p_card],BL_Card[p_card].discQ_Tbl[qtag], p_card);
+
+			 BL_Card[p_card].discQ_Tbl[qtag] = NULL;
+			 currTar_Info->TarTagQ_Cnt--;
+
+			 }
+		  }
+	}
+
+}
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Flush Target SCCB
+ *
+ * Description: Flush all SCCB's back to the host driver for this target.
+ *
+ *---------------------------------------------------------------------*/
+
+void  queueFlushTargSccb(UCHAR p_card, UCHAR thisTarg, UCHAR error_code)
+{
+   UCHAR qtag;
+   PSCCBMgr_tar_info currTar_Info;
+
+   currTar_Info = &sccbMgrTbl[p_card][thisTarg];
+
+   for (qtag=0; qtag<QUEUE_DEPTH; qtag++) {
+
+	  if (BL_Card[p_card].discQ_Tbl[qtag] && 
+				(BL_Card[p_card].discQ_Tbl[qtag]->TargID == thisTarg))
+		 {
+
+		 BL_Card[p_card].discQ_Tbl[qtag]->HostStatus = (UCHAR)error_code;
+
+		 queueCmdComplete(&BL_Card[p_card],BL_Card[p_card].discQ_Tbl[qtag], p_card);
+
+		 BL_Card[p_card].discQ_Tbl[qtag] = NULL;
+		 currTar_Info->TarTagQ_Cnt--;
+
+		 }
+	  }
+
+}
+
+
+
+
+
+void queueAddSccb(PSCCB p_SCCB, UCHAR p_card)
+{
+   PSCCBMgr_tar_info currTar_Info;
+   currTar_Info = &sccbMgrTbl[p_card][p_SCCB->TargID];
+
+   p_SCCB->Sccb_forwardlink = NULL;
+
+   p_SCCB->Sccb_backlink = currTar_Info->TarSelQ_Tail;
+
+   if (currTar_Info->TarSelQ_Cnt == 0) {
+
+	  currTar_Info->TarSelQ_Head = p_SCCB;
+	  }
+
+   else {
+
+	  currTar_Info->TarSelQ_Tail->Sccb_forwardlink = p_SCCB;
+	  }
+
+
+   currTar_Info->TarSelQ_Tail = p_SCCB;
+   currTar_Info->TarSelQ_Cnt++;
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Queue Find SCCB
+ *
+ * Description: Search the target select Queue for this SCCB, and
+ *              remove it if found.
+ *
+ *---------------------------------------------------------------------*/
+
+UCHAR queueFindSccb(PSCCB p_SCCB, UCHAR p_card)
+{
+   PSCCB q_ptr;
+   PSCCBMgr_tar_info currTar_Info;
+
+   currTar_Info = &sccbMgrTbl[p_card][p_SCCB->TargID];
+
+   q_ptr = currTar_Info->TarSelQ_Head;
+
+   while(q_ptr != NULL) {
+
+	  if (q_ptr == p_SCCB) {
+
+
+		 if (currTar_Info->TarSelQ_Head == q_ptr) {
+
+			currTar_Info->TarSelQ_Head = q_ptr->Sccb_forwardlink;
+			}
+
+		 if (currTar_Info->TarSelQ_Tail == q_ptr) {
+
+			currTar_Info->TarSelQ_Tail = q_ptr->Sccb_backlink;
+			}
+
+		 if (q_ptr->Sccb_forwardlink != NULL) {
+			q_ptr->Sccb_forwardlink->Sccb_backlink = q_ptr->Sccb_backlink;
+			}
+
+		 if (q_ptr->Sccb_backlink != NULL) {
+			q_ptr->Sccb_backlink->Sccb_forwardlink = q_ptr->Sccb_forwardlink;
+			}
+
+		 currTar_Info->TarSelQ_Cnt--;
+
+		 return(TRUE);
+		 }
+
+	  else {
+		 q_ptr = q_ptr->Sccb_forwardlink;
+		 }
+	  }
+
+
+   return(FALSE);
+
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Utility Update Residual Count
+ *
+ * Description: Update the XferCnt to the remaining byte count.
+ *              If we transferred all the data then just write zero.
+ *              If Non-SG transfer then report Total Cnt - Actual Transfer
+ *              Cnt.  For SG transfers add the count fields of all
+ *              remaining SG elements, as well as any partial remaining
+ *              element.
+ *
+ *---------------------------------------------------------------------*/
+
+void  utilUpdateResidual(PSCCB p_SCCB)
+{
+   ULONG partial_cnt;
+   UINT  sg_index;
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+   ULONG far *sg_ptr;
+#else
+   ULONG *sg_ptr;
+#endif
+
+   if (p_SCCB->Sccb_XferState & F_ALL_XFERRED) {
+
+	  p_SCCB->DataLength = 0x0000;
+	  }
+
+   else if (p_SCCB->Sccb_XferState & F_SG_XFER) {
+
+		 partial_cnt = 0x0000;
+
+		 sg_index = p_SCCB->Sccb_sgseg;
+
+#if defined(COMPILER_16_BIT) && !defined(DOS)
+		 sg_ptr = (ULONG far *)p_SCCB->DataPointer;
+#else
+		 sg_ptr = (ULONG *)p_SCCB->DataPointer;
+#endif
+
+		 if (p_SCCB->Sccb_SGoffset) {
+
+			partial_cnt = p_SCCB->Sccb_SGoffset;
+			sg_index++;
+			}
+
+		 while ( ((ULONG)sg_index * (ULONG)SG_ELEMENT_SIZE) <
+			p_SCCB->DataLength ) {
+
+			partial_cnt += *(sg_ptr+(sg_index * 2));
+			sg_index++;
+			}
+
+		 p_SCCB->DataLength = partial_cnt;
+		 }
+
+	  else {
+
+		 p_SCCB->DataLength -= p_SCCB->Sccb_ATC;
+		 }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Wait 1 Second
+ *
+ * Description: Wait for 1 second.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void Wait1Second(USHORT p_port)
+#else
+void Wait1Second(ULONG p_port)
+#endif
+{
+   UCHAR i;
+
+   for(i=0; i < 4; i++) {
+
+	  Wait(p_port, TO_250ms);
+
+	  if ((RD_HARPOON(p_port+hp_scsictrl_0) & SCSI_RST))
+		 break;
+
+	  if((RDW_HARPOON((p_port+hp_intstat)) & SCAM_SEL))
+		 break;
+	  }
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Wait
+ *
+ * Description: Wait the desired delay.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void Wait(USHORT p_port, UCHAR p_delay)
+#else
+void Wait(ULONG p_port, UCHAR p_delay)
+#endif
+{
+   UCHAR old_timer;
+   UCHAR green_flag;
+
+   old_timer = RD_HARPOON(p_port+hp_seltimeout);
+
+   green_flag=RD_HARPOON(p_port+hp_clkctrl_0);
+   WR_HARPOON(p_port+hp_clkctrl_0, CLKCTRL_DEFAULT);
+
+   WR_HARPOON(p_port+hp_seltimeout,p_delay);
+   WRW_HARPOON((p_port+hp_intstat), TIMEOUT);
+   WRW_HARPOON((p_port+hp_intena), (default_intena & ~TIMEOUT));
+
+
+   WR_HARPOON(p_port+hp_portctrl_0,
+	  (RD_HARPOON(p_port+hp_portctrl_0) | START_TO));
+
+   while (!(RDW_HARPOON((p_port+hp_intstat)) & TIMEOUT)) {
+
+	  if ((RD_HARPOON(p_port+hp_scsictrl_0) & SCSI_RST))
+		 break;
+
+	  if ((RDW_HARPOON((p_port+hp_intstat)) & SCAM_SEL))
+		 break;
+	  }
+
+   WR_HARPOON(p_port+hp_portctrl_0,
+	  (RD_HARPOON(p_port+hp_portctrl_0) & ~START_TO));
+
+   WRW_HARPOON((p_port+hp_intstat), TIMEOUT);
+   WRW_HARPOON((p_port+hp_intena), default_intena);
+
+   WR_HARPOON(p_port+hp_clkctrl_0,green_flag);
+
+   WR_HARPOON(p_port+hp_seltimeout,old_timer);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Enable/Disable Write to EEPROM
+ *
+ * Description: The EEPROM must first be enabled for writes
+ *              A total of 9 clocks are needed.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void utilEEWriteOnOff(USHORT p_port,UCHAR p_mode)
+#else
+void utilEEWriteOnOff(ULONG p_port,UCHAR p_mode)
+#endif
+{
+   UCHAR ee_value;
+
+   ee_value = (UCHAR)(RD_HARPOON(p_port+hp_ee_ctrl) & (EXT_ARB_ACK | SCSI_TERM_ENA_H));
+
+   if (p_mode)
+
+	  utilEESendCmdAddr(p_port, EWEN, EWEN_ADDR);
+
+   else
+
+
+	  utilEESendCmdAddr(p_port, EWDS, EWDS_ADDR);
+
+   WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS)); /*Turn off CS */
+   WR_HARPOON(p_port+hp_ee_ctrl, ee_value);       /*Turn off Master Select */
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Write EEPROM
+ *
+ * Description: Write a word to the EEPROM at the specified
+ *              address.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void utilEEWrite(USHORT p_port, USHORT ee_data, USHORT ee_addr)
+#else
+void utilEEWrite(ULONG p_port, USHORT ee_data, USHORT ee_addr)
+#endif
+{
+
+   UCHAR ee_value;
+   USHORT i;
+
+   ee_value = (UCHAR)((RD_HARPOON(p_port+hp_ee_ctrl) & (EXT_ARB_ACK | SCSI_TERM_ENA_H))|
+		   (SEE_MS | SEE_CS));
+
+
+
+   utilEESendCmdAddr(p_port, EE_WRITE, ee_addr);
+
+
+   ee_value |= (SEE_MS + SEE_CS);
+
+   for(i = 0x8000; i != 0; i>>=1) {
+
+	  if (i & ee_data)
+	 ee_value |= SEE_DO;
+	  else
+	 ee_value &= ~SEE_DO;
+
+	  WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+	  ee_value |= SEE_CLK;          /* Clock  data! */
+	  WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+	  ee_value &= ~SEE_CLK;
+	  WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+	  }
+   ee_value &= (EXT_ARB_ACK | SCSI_TERM_ENA_H);
+   WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS));
+
+   Wait(p_port, TO_10ms);
+
+   WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS | SEE_CS)); /* Set CS to EEPROM */
+   WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS));       /* Turn off CS */
+   WR_HARPOON(p_port+hp_ee_ctrl, ee_value);       /* Turn off Master Select */
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Read EEPROM
+ *
+ * Description: Read a word from the EEPROM at the desired
+ *              address.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+USHORT utilEERead(USHORT p_port, USHORT ee_addr)
+#else
+USHORT utilEERead(ULONG p_port, USHORT ee_addr)
+#endif
+{
+
+   UCHAR ee_value;
+   USHORT i, ee_data;
+
+   ee_value = (UCHAR)((RD_HARPOON(p_port+hp_ee_ctrl) & (EXT_ARB_ACK | SCSI_TERM_ENA_H))|
+		   (SEE_MS | SEE_CS));
+
+
+   utilEESendCmdAddr(p_port, EE_READ, ee_addr);
+
+
+   ee_value |= (SEE_MS + SEE_CS);
+   ee_data = 0;
+
+   for(i = 1; i <= 16; i++) {
+
+	  ee_value |= SEE_CLK;          /* Clock  data! */
+	  WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+	  ee_value &= ~SEE_CLK;
+	  WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+
+	  ee_data <<= 1;
+
+	  if (RD_HARPOON(p_port+hp_ee_ctrl) & SEE_DI)
+		 ee_data |= 1;
+	  }
+
+   ee_value &= ~(SEE_MS + SEE_CS);
+   WR_HARPOON(p_port+hp_ee_ctrl, (ee_value | SEE_MS)); /*Turn off CS */
+   WR_HARPOON(p_port+hp_ee_ctrl, ee_value);   /*Turn off Master Select */
+
+   return(ee_data);
+}
+
+
+/*---------------------------------------------------------------------
+ *
+ * Function: Send EE command and Address to the EEPROM
+ *
+ * Description: Transfers the correct command and sends the address
+ *              to the eeprom.
+ *
+ *---------------------------------------------------------------------*/
+
+#if defined(DOS)
+void utilEESendCmdAddr(USHORT p_port, UCHAR ee_cmd, USHORT ee_addr)
+#else
+void utilEESendCmdAddr(ULONG p_port, UCHAR ee_cmd, USHORT ee_addr)
+#endif
+{
+   UCHAR ee_value;
+   UCHAR narrow_flg;
+
+   USHORT i;
+
+
+   narrow_flg= (UCHAR)(RD_HARPOON(p_port+hp_page_ctrl) & NARROW_SCSI_CARD);
+
+
+   ee_value = SEE_MS;
+   WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+
+   ee_value |= SEE_CS;                             /* Set CS to EEPROM */
+   WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+
+
+   for(i = 0x04; i != 0; i>>=1) {
+
+	  if (i & ee_cmd)
+		 ee_value |= SEE_DO;
+	  else
+		 ee_value &= ~SEE_DO;
+
+	  WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+	  ee_value |= SEE_CLK;                         /* Clock  data! */
+	  WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+	  ee_value &= ~SEE_CLK;
+	  WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+	  }
+
+
+   if (narrow_flg)
+	  i = 0x0080;
+
+   else
+	  i = 0x0200;
+
+
+   while (i != 0) {
+
+	  if (i & ee_addr)
+		 ee_value |= SEE_DO;
+	  else
+		 ee_value &= ~SEE_DO;
+
+	  WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+	  ee_value |= SEE_CLK;                         /* Clock  data! */
+	  WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+	  ee_value &= ~SEE_CLK;
+	  WR_HARPOON(p_port+hp_ee_ctrl, ee_value);
+
+	  i >>= 1;
+	  }
+}
+
+USHORT CalcCrc16(UCHAR buffer[])
+{
+   USHORT crc=0;
+	int i,j;
+   USHORT ch;
+   for (i=0; i < ID_STRING_LENGTH; i++)
+   {
+      ch = (USHORT) buffer[i];
+	   for(j=0; j < 8; j++)
+	   {
+		   if ((crc ^ ch) & 1)
+            crc = (crc >> 1) ^ CRCMASK;
+		   else
+            crc >>= 1;
+		   ch >>= 1;
+	   }
+   }
+	return(crc);
+}
+
+UCHAR CalcLrc(UCHAR buffer[])
+{
+	int i;
+	UCHAR lrc;
+	lrc = 0;
+	for(i = 0; i < ID_STRING_LENGTH; i++)
+		lrc ^= buffer[i];
+	return(lrc);
+}
+
+
+
+#endif /* CONFIG_SCSI_OMIT_FLASHPOINT */
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/LICENSE.FlashPoint linux/drivers/scsi/LICENSE.FlashPoint
--- v2.1.27/linux/drivers/scsi/LICENSE.FlashPoint	Wed Dec 31 16:00:00 1969
+++ linux/drivers/scsi/LICENSE.FlashPoint	Sun Feb 23 16:14:00 1997
@@ -0,0 +1,60 @@
+                     FlashPoint Driver Developer's Kit
+                                Version 1.0
+
+                   Copyright 1995-1996 by Mylex Corporation
+                            All Rights Reserved
+
+This program is free software; you may redistribute and/or modify it under
+the terms of either:
+
+    a) the GNU General Public License as published by the Free Software
+    Foundation; either version 2, or (at your option) any later version,
+
+    or
+
+    b) the "BSD-style License" included below.
+
+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 either the GNU General Public
+License or the BSD-style License below 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.,
+675 Mass Ave, Cambridge, MA 02139, USA.
+
+The BSD-style License is as follows:
+
+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 this LICENSE.FlashPoint
+   file, without modification, this list of conditions, and the following
+   disclaimer.  The following copyright notice must appear immediately at
+   the beginning of all source files:
+
+        Copyright 1995-1996 by Mylex Corporation.  All Rights Reserved
+
+        This file is available under both the GNU General Public License
+        and a BSD-style copyright; see LICENSE.FlashPoint for details.
+
+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 Mylex Corporation may not be used to endorse or promote
+   products derived from this software without specific prior written
+   permission.
+
+THIS SOFTWARE IS PROVIDED BY MYLEX CORP. ``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.
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/Makefile linux/drivers/scsi/Makefile
--- v2.1.27/linux/drivers/scsi/Makefile	Fri Jan 24 09:35:50 1997
+++ linux/drivers/scsi/Makefile	Fri Feb 28 15:15:42 1997
@@ -381,6 +381,12 @@
 
 include $(TOPDIR)/Rules.make
 
+BusLogic.o: BusLogic.c FlashPoint.c
+	$(CC) $(CFLAGS) -c BusLogic.c -o BusLogic.O
+	$(CC) $(CFLAGS) -c FlashPoint.c -o FlashPoint.O
+	$(LD) -r -o BusLogic.o BusLogic.O FlashPoint.O
+	rm -f BusLogic.O FlashPoint.O
+
 aha152x.o: aha152x.c
 	$(CC) $(CFLAGS) $(AHA152X) -c aha152x.c 
 
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/README.BusLogic linux/drivers/scsi/README.BusLogic
--- v2.1.27/linux/drivers/scsi/README.BusLogic	Wed Jul 17 11:22:13 1996
+++ linux/drivers/scsi/README.BusLogic	Sun Feb 23 16:14:00 1997
@@ -1,9 +1,10 @@
-		  BusLogic MultiMaster SCSI Driver for Linux
+	   BusLogic MultiMaster and FlashPoint SCSI Driver for Linux
 
-		       Version 1.2.6 for Linux 1.2.13
-		       Version 2.0.6 for Linux 2.0.4
+			  Version 2.0.7 for Linux 2.0
 
-				 17 July 1996
+			      PRODUCTION RELEASE
+
+			       23 February 1997
 
 			       Leonard N. Zubkoff
 			       Dandelion Digital
@@ -18,10 +19,15 @@
 adapters which share a common programming interface across a diverse collection
 of bus architectures by virtue of their MultiMaster ASIC technology.  This
 driver supports all present BusLogic MultiMaster Host Adapters, and should
-support any future MultiMaster designs with little or no modification.  Host
-adapters based on the new FlashPoint architecture are not supported by this
-driver; consult the README.FlashPoint file for information about a program to
-upgrade Linux users from the unsupported FlashPoint LT to the supported BT-948.
+support any future MultiMaster designs with little or no modification.  More
+recently, BusLogic has introduced the FlashPoint Host Adapters, which are less
+costly and rely on the host CPU, rather than including an onboard processor.
+Mylex/BusLogic has recently provided me with the FlashPoint Driver Developer's
+Kit, which comprises documentation and freely redistributable source code for
+the FlashPoint SCCB Manager.  The SCCB Manager is the library of code that runs
+on the host CPU and performs functions analogous to the firmware on the
+MultiMaster Host Adapters.  Thanks to their having provided the SCCB Manager,
+this driver now supports the FlashPoint Host Adapters as well.
 
 My primary goals in writing this completely new BusLogic driver for Linux are
 to achieve the full performance that BusLogic SCSI Host Adapters and modern
@@ -112,26 +118,23 @@
 o Robustness Features
 
   The driver implements extensive error recovery procedures.  When the higher
-  level parts of the SCSI subsystem request that a command be reset, action is
-  taken to restore proper operation of the host adapter and SCSI bus.  On Linux
-  1.2.13, by default a full host adapter hard reset and SCSI bus reset is
-  performed.  On Linux 2.0.x, by default a selection is made between a full
-  host adapter hard reset and SCSI bus reset versus sending a bus device reset
-  message to the individual target device based on the recommendation of the
-  SCSI subsystem.  Error recovery strategies are selectable from the kernel
-  command line individually for each target device, and also include sending a
-  bus device reset to the specific target device associated with the command
-  being reset, as well as suppressing error recovery entirely to avoid
-  perturbing an improperly functioning device.  If the bus device reset error
-  recovery strategy is selected and sending a bus device reset does not restore
-  correct operation, the next command that is reset will force a full host
-  adapter hard reset and SCSI bus reset.  SCSI bus resets caused by other
-  devices and detected by the host adapter are also handled by issuing a hard
-  reset to the host adapter and full re-initialization.  Finally, if tagged
-  queuing is active and more than one command reset occurs in a 10 minute
-  interval, or if a command reset occurs within the first 10 minutes of
-  operation, then tagged queuing will be disabled for that target device.
-  These error recovery options should improve overall system robustness by
+  level parts of the SCSI subsystem request that a timed out command be reset,
+  a selection is made between a full host adapter hard reset and SCSI bus reset
+  versus sending a bus device reset message to the individual target device
+  based on the recommendation of the SCSI subsystem.  Error recovery strategies
+  are selectable from the kernel command line individually for each target
+  device, and also include sending a bus device reset to the specific target
+  device associated with the command being reset, as well as suppressing error
+  recovery entirely to avoid perturbing an improperly functioning device.  If
+  the bus device reset error recovery strategy is selected and sending a bus
+  device reset does not restore correct operation, the next command that is
+  reset will force a full host adapter hard reset and SCSI bus reset.  SCSI bus
+  resets caused by other devices and detected by the host adapter are also
+  handled by issuing a hard reset to the host adapter and re-initialization.
+  Finally, if tagged queuing is active and more than one command reset occurs
+  in a 10 minute interval, or if a command reset occurs within the first 10
+  minutes of operation, then tagged queuing will be disabled for that target
+  device.  These error recovery options improve overall system robustness by
   preventing individual errant devices from causing the system as a whole to
   lock up or crash, and thereby allowing a clean shutdown and restart after the
   offending component is removed.
@@ -155,13 +158,16 @@
   used to disable the ISA compatible I/O port entirely as it is not necessary.
   The ISA compatible I/O port is disabled by default on the BT-948/958/958D.
 
+o /proc File System Support
+
+  Copies of the host adapter configuration information together with data
+  transfer and error recovery statistics are now available through the
+  /proc/scsi/BusLogic/<N> interface.
+
 o Shared Interrupts Support
 
   On systems that support shared interrupts, any number of BusLogic Host
-  Adapters may share the same interrupt request channel, and in fact it is more
-  efficient if they do so.  The driver scans all known BusLogic Host Adapters
-  whenever an interrupt is handled on an interrupt channel assigned to any
-  BusLogic Host Adapter.
+  Adapters may share the same interrupt request channel.
 
 o Wide SCSI Support
 
@@ -181,48 +187,50 @@
 Host Adapter not in the following table contact the author beforehand to verify
 that it is or will be supported.
 
-"W" Series Host Adapters:
+FlashPoint Series PCI Host Adapters:
+
+FlashPoint LT (BT-930)	Ultra Fast Single-ended SCSI-2
+FlashPoint DL (BT-932)	Dual Channel Ultra Fast Single-ended SCSI-2
+FlashPoint LW (BT-950)	Ultra Wide Single-ended SCSI-2
+FlashPoint DW (BT-952)	Dual Channel Ultra Wide Single-ended SCSI-2
+
+MultiMaster "W" Series Host Adapters:
+
+BT-948	    PCI		Ultra Fast Single-ended SCSI-2
+BT-958	    PCI		Ultra Wide Single-ended SCSI-2
+BT-958D	    PCI		Ultra Wide Differential SCSI-2
+
+MultiMaster "C" Series Host Adapters:
+
+BT-946C	    PCI		Fast Single-ended SCSI-2
+BT-956C	    PCI		Fast Wide Single-ended SCSI-2
+BT-956CD    PCI		Fast Wide Differential SCSI-2
+BT-445C	    VLB		Fast Single-ended SCSI-2
+BT-747C	    EISA	Fast Single-ended SCSI-2
+BT-757C	    EISA	Fast Wide Single-ended SCSI-2
+BT-757CD    EISA	Fast Wide Differential SCSI-2
+BT-545C	    ISA		Fast Single-ended SCSI-2
+BT-540CF    ISA		Fast Single-ended SCSI-2
+
+MultiMaster "S" Series Host Adapters:
+
+BT-445S	    VLB		Fast Single-ended SCSI-2
+BT-747S	    EISA	Fast Single-ended SCSI-2
+BT-747D	    EISA	Fast Differential SCSI-2
+BT-757S	    EISA	Fast Wide Single-ended SCSI-2
+BT-757D	    EISA	Fast Wide Differential SCSI-2
+BT-545S	    ISA		Fast Single-ended SCSI-2
+BT-542D	    ISA		Fast Differential SCSI-2
+BT-742A	    EISA	Single-ended SCSI-2 (742A revision H)
+BT-542B	    ISA		Single-ended SCSI-2 (542B revision H)
+
+MultiMaster "A" Series Host Adapters:
 
-BT-948	    PCI	    Ultra Fast Single-ended SCSI-2
-BT-958	    PCI	    Ultra Wide Single-ended SCSI-2
-BT-958D	    PCI	    Ultra Wide Differential SCSI-2
-
-"C" Series Host Adapters:
-
-BT-946C	    PCI	    Fast Single-ended SCSI-2
-BT-956C	    PCI	    Fast Wide Single-ended SCSI-2
-BT-956CD    PCI	    Fast Wide Differential SCSI-2
-BT-445C	    VLB	    Fast Single-ended SCSI-2
-BT-747C	    EISA    Fast Single-ended SCSI-2
-BT-757C	    EISA    Fast Wide Single-ended SCSI-2
-BT-757CD    EISA    Fast Wide Differential SCSI-2
-BT-545C	    ISA	    Fast Single-ended SCSI-2
-BT-540CF    ISA	    Fast Single-ended SCSI-2
-
-"S" Series Host Adapters:
-
-BT-445S	    VLB	    Fast Single-ended SCSI-2
-BT-747S	    EISA    Fast Single-ended SCSI-2
-BT-747D	    EISA    Fast Differential SCSI-2
-BT-757S	    EISA    Fast Wide Single-ended SCSI-2
-BT-757D	    EISA    Fast Wide Differential SCSI-2
-BT-545S	    ISA	    Fast Single-ended SCSI-2
-BT-542D	    ISA	    Fast Differential SCSI-2
-BT-742A	    EISA    Single-ended SCSI-2 (742A revision H)
-BT-542B	    ISA	    Single-ended SCSI-2 (542B revision H)
-
-"A" Series Host Adapters:
-
-BT-742A	    EISA    Single-ended SCSI-2 (742A revisions A - G)
-BT-542B	    ISA	    Single-ended SCSI-2 (542B revisions A - G)
-
-The FlashPoint LT, also known as the BT-930 Ultra, implements a different host
-interface and is not supported by this driver.  Consult the README.FlashPoint
-file for information about a program to upgrade Linux users from the
-unsupported FlashPoint LT to the supported BT-948.
+BT-742A	    EISA	Single-ended SCSI-2 (742A revisions A - G)
+BT-542B	    ISA		Single-ended SCSI-2 (542B revisions A - G)
 
-AMI FastDisk Host Adapters that are true BusLogic clones are supported by this
-driver.
+AMI FastDisk Host Adapters that are true BusLogic MultiMaster clones are also
+supported by this driver.
 
 
 		      BT-948/958/958D INSTALLATION NOTES
@@ -285,6 +293,45 @@
 may be found in the comments before BusLogic_Setup in the kernel source code
 file "BusLogic.c".  The following examples may be useful as a starting point:
 
+  "BusLogic=NoProbe"
+
+    No probing of any kind is to be performed, and hence no BusLogic Host
+    Adapters will be detected.
+
+  "BusLogic=NoProbeISA"
+
+    No probing of the standard ISA I/O Addresses will be done, and hence only
+    PCI Host Adapters will be detected.
+
+  "BusLogic=NoProbePCI"
+
+    No interrogation of PCI Configuration Space will be made, and hence only
+    ISA Multimaster Host Adapters will be detected, as well as PCI Multimaster
+    Host Adapters that have their ISA Compatible I/O Port set to "Primary" or
+    "Alternate".
+
+  "BusLogic=NoSortPCI"
+
+    PCI MultiMaster Host Adapters will be enumerated in the order provided by
+    the PCI BIOS, ignoring any setting of the AutoSCSI "Use Bus And Device #
+    For PCI Scanning Seq." option.
+
+  "BusLogic=MultiMasterFirst"
+
+    By default, if both FlashPoint and PCI MultiMaster Host Adapters are
+    present, this driver will probe for PCI MultiMaster Host Adapters first
+    unless the BIOS primary disk is not controlled by the first PCI MultiMaster
+    Host Adapter, in which case FlashPoint Host Adapters will be probed first.
+    This option forces MultiMaster Host Adapters to be probed first.
+
+  "BusLogic=FlashPointFirst"
+
+    By default, if both FlashPoint and PCI MultiMaster Host Adapters are
+    present, this driver will probe for PCI MultiMaster Host Adapters first
+    unless the BIOS primary disk is not controlled by the first PCI MultiMaster
+    Host Adapter, in which case FlashPoint Host Adapters will be probed first.
+    This option forces FlashPoint Host Adapters to be probed first.
+
   "BusLogic=0x330"
 
     This command line limits probing to the single I/O port at 0x330.
@@ -295,10 +342,11 @@
     which also disables tagged queuing.  It may be useful if problems arise
     during installation on a system with a flaky SCSI configuration.  In cases
     of a marginal SCSI configuration it may also be beneficial to disable fast
-    transfers and/or synchronous negotiation using AutoSCSI on "W" and "C"
-    series controllers.  Disconnect/reconnect may also be disabled for fast
-    devices such as disk drives, but should not be disabled for tape drives or
-    other devices where a single command may take over a second to execute.
+    transfers and/or synchronous negotiation using AutoSCSI on FlashPoint and
+    "W" and "C" series MultiMaster host adapters.  Disconnect/reconnect may
+    also be disabled for fast devices such as disk drives, but should not be
+    disabled for tape drives or other devices where a single command may take
+    over a second to execute.
 
   "BusLogic=0,0,30"
 
@@ -325,21 +373,16 @@
 
 				 INSTALLATION
 
-This distribution was prepared for Linux kernel version 1.2.13
-(BusLogic-1.2.6.tar.gz) or Linux kernel version 2.0.4 (BusLogic-2.0.6.tar.gz).
-Installation in later versions will probably be successful as well, though
-BusLogic.patch may not be required.  Installation in earlier versions is not
-recommended.
-
-To install the BusLogic SCSI driver, you may use the following commands,
-replacing "/usr/src" with wherever you keep your Linux kernel source tree
-(substitute "1.2" or "2.0" for "x.y" in the tar command as appropriate):
+This distribution was prepared for Linux kernel version 2.0.29, but should be
+compatible with 2.0.4 or any later 2.0 series kernel.
+
+To install the new BusLogic SCSI driver, you may use the following commands,
+replacing "/usr/src" with wherever you keep your Linux kernel source tree:
 
   cd /usr/src
-  tar -xvzf BusLogic-x.y.6.tar.gz
-  mv README.* BusLogic.[ch] linux/drivers/scsi
-  patch -p < BusLogic.patch	    (on Linux 1.2.13 only)
-  patch -p < BusLogic.elf_patch	    (on Linux 1.2.13 ELF systems only)
+  tar -xvzf BusLogic-2.0.7.tar.gz
+  mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi
+  patch -p < BusLogic.patch
   cd linux
   make config
   make depend
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/README.FlashPoint linux/drivers/scsi/README.FlashPoint
--- v2.1.27/linux/drivers/scsi/README.FlashPoint	Tue Oct 29 04:55:15 1996
+++ linux/drivers/scsi/README.FlashPoint	Sun Feb 23 16:14:00 1997
@@ -1,7 +1,5 @@
-The BusLogic FlashPoint SCSI host adapters are now supported on Linux 2.0.x.
-See http://www.dandelion.com/Linux/ for the beta test BusLogic driver which
-includes FlashPoint support.  The upgrade program described below will remain
-available through the end of 1996.
+The BusLogic FlashPoint SCSI Host Adapters are now supported on Linux.  The
+upgrade program described below will remain available through March 1997.
 
 
 
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/aha1542.c linux/drivers/scsi/aha1542.c
--- v2.1.27/linux/drivers/scsi/aha1542.c	Thu Nov  7 01:25:56 1996
+++ linux/drivers/scsi/aha1542.c	Fri Feb 28 15:15:42 1997
@@ -13,6 +13,9 @@
  *        controller).
  *  Modified by Matti Aarnio
  *        Accept parameters from LILO cmd-line. -- 1-Oct-94
+ *  Modified by Mike McLagan <mike.mclagan@linux.org>
+ *        Recognise extended mode on AHA1542CP, different bit than 1542CF
+ *        1-Jan-97
  */
 
 #include <linux/module.h>
@@ -813,7 +816,9 @@
      mbenable_cmd[0]=CMD_MBENABLE;
      mbenable_cmd[1]=0;
      mbenable_cmd[2]=mbenable_result[1];
-     if(mbenable_result[1] & 1) retval = BIOS_TRANSLATION_25563;
+
+     if(mbenable_result[1] & 0x03) retval = BIOS_TRANSLATION_25563;
+
      aha1542_out(base,mbenable_cmd,3);
      WAIT(INTRFLAGS(base),INTRMASK,HACC,0);
   };
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/scsi_ioctl.c linux/drivers/scsi/scsi_ioctl.c
--- v2.1.27/linux/drivers/scsi/scsi_ioctl.c	Fri Feb  7 04:34:19 1997
+++ linux/drivers/scsi/scsi_ioctl.c	Fri Feb 28 15:15:42 1997
@@ -247,8 +247,12 @@
 	retries = 1;
 	break;
       case START_STOP:
+	timeout =  2 * 60 * HZ;	/* 2 minutes */
+	retries = 1;
+	break;
       case MOVE_MEDIUM:
-	timeout =  60 * HZ;	/* 60 seconds */
+      case READ_ELEMENT_STATUS:
+	timeout =  5 * 60 * HZ;	/* 5 minutes */
 	retries = 1;
 	break;
       default:
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/sd.c linux/drivers/scsi/sd.c
--- v2.1.27/linux/drivers/scsi/sd.c	Sun Jan 26 02:07:20 1997
+++ linux/drivers/scsi/sd.c	Fri Feb 28 15:15:42 1997
@@ -1210,6 +1210,7 @@
 		printk ("scsi : deleting disk entry.\n");
 		rscsi_disks[i].device = NULL;
 		sd_template.nr_dev--;
+		sd_gendisk.nr_real--;
 		return i;
 	    }
 	}
diff -u --recursive --new-file v2.1.27/linux/drivers/scsi/st.c linux/drivers/scsi/st.c
--- v2.1.27/linux/drivers/scsi/st.c	Sun Jan 26 02:07:23 1997
+++ linux/drivers/scsi/st.c	Fri Feb 28 15:15:42 1997
@@ -629,7 +629,6 @@
     if ((STp->buffer)->last_result_fatal != 0) {
       if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
 	  (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) {
-	printk(KERN_NOTICE "st%d: No tape.\n", dev);
 	STp->ready = ST_NO_TAPE;
       } else
 	STp->ready = ST_NOT_READY;
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/.objects linux/drivers/sound/.objects
--- v2.1.27/linux/drivers/sound/.objects	Fri Nov 15 00:14:57 1996
+++ linux/drivers/sound/.objects	Wed Feb 26 02:34:50 1997
@@ -8,10 +8,6 @@
 	OBJS := $(OBJS)  adlib_card.o
 endif
 
-ifdef CONFIG_AEDSP16
-	OBJS := $(OBJS)  aedsp16.o
-endif
-
 ifdef CONFIG_AUDIO
 	OBJS := $(OBJS)  audio.o
 endif
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/.version linux/drivers/sound/.version
--- v2.1.27/linux/drivers/sound/.version	Fri Nov 15 00:14:57 1996
+++ linux/drivers/sound/.version	Wed Feb 26 02:34:51 1997
@@ -1,2 +1,2 @@
-3.7-beta12
-0x030707
+3.8-beta9
+0x030803
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/CHANGELOG linux/drivers/sound/CHANGELOG
--- v2.1.27/linux/drivers/sound/CHANGELOG	Fri Nov 15 00:15:00 1996
+++ linux/drivers/sound/CHANGELOG	Wed Feb 26 02:34:54 1997
@@ -1,7 +1,25 @@
-Changelog for version 3.7-beta10
---------------------------------
+Changelog for version 3.8-beta8
+-------------------------------
 
-Since 3.5-beta5
+Since 3.8-beta6
+- Fixed the famous Quake delay bug.
+
+Since 3.8-beta5
+- Fixed many bugs in audio playback.
+
+Since 3.8-beta4
+- Just minor changes.
+
+Since 3.8-beta1
+- Major rewrite of audio playback handling.
+- Added AWE32 support by Takashi Iwai (in ./lowlevel/).
+
+Since 3.7-beta#
+- Passing of ioctl() parameters between soundcard.c and other modules has been
+changed so that arg always points to kernel space.
+- Some bugfixes.
+
+Since 3.7-beta5
 - Disabled MIDI input with GUS PnP (Interwave). There seems to be constant
 stream of received 0x00 bytes when the MIDI receiver is enabled.
 
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
--- v2.1.27/linux/drivers/sound/Config.in	Fri Nov 15 00:15:48 1996
+++ linux/drivers/sound/Config.in	Wed Feb 26 02:35:40 1997
@@ -15,4 +15,55 @@
 if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
    bool 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER
    bool 'AWE32 synth' CONFIG_AWE32_SYNTH
+   bool 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16
+
+   if [ "$CONFIG_AEDSP16" = "y" ]; then
+      comment 'SC-6600 Audio Cards have no jumper switches at all'
+      bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600
+
+      if [ "$CONFIG_SB" = "y" -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+         bool 'Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO
+         if [ "$CONFIG_AEDSP16_SBPRO" = "y" ]; then
+	    comment 'Audio Excel DSP 16 [Sound Blaster Pro]'
+            hex 'I/O base for Audio Excel DSP 16 220 or 240' \
+	                                           AEDSP16_BASE $SBC_BASE
+            int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \
+	                                           AEDSP16_SBC_IRQ $SBC_IRQ
+            int 'Audio Excel DSP 16 DMA 0, 1 or 3' AEDSP16_SBC_DMA $SBC_DMA
+         fi
+      fi
+   
+      if [ "$CONFIG_MSS" = "y" -a "$CONFIG_AEDSP16_SBPRO" != "y" ]; then
+         bool 'Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS
+         if [ "$CONFIG_AEDSP16_MSS" = "y" ]; then
+	    comment 'Audio Excel DSP 16 [Microsoft Sound System]'
+	    hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+            int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \
+	                                           AEDSP16_MSS_IRQ $MSS_IRQ
+            int 'Audio Excel DSP 16 DMA 0, 1 or 3' AEDSP16_MSS_DMA $MSS_DMA
+         fi
+      fi
+
+      if [ "$CONFIG_MIDI" = "y" ]; then
+         bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
+         if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then
+	    comment 'Audio Excel DSP 16 [MPU-401]'
+	    if [ "$CONFIG_AEDSP16_SBPRO" != "y" \
+	         -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+	       hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+	    fi
+            int 'MPU401 IRQ for Audio Excel DSP 16 5, 7, 9, 10 or 0 (disable)' \
+	    						AEDSP16_MPU_IRQ $MPU_IRQ
+         fi
+      fi
+
+      if [ "$CONFIG_SC6600" = "y" ]; then
+         comment 'SC-6600 specific configuration'
+         bool 'Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY
+         int 'SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' \
+	 					CONFIG_SC6600_CDROM 4
+         hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0
+      fi
+
+   fi
 fi
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/Config.std linux/drivers/sound/Config.std
--- v2.1.27/linux/drivers/sound/Config.std	Fri Nov 15 00:15:48 1996
+++ linux/drivers/sound/Config.std	Wed Feb 26 02:35:40 1997
@@ -15,4 +15,55 @@
 if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
    bool 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER
    bool 'AWE32 synth' CONFIG_AWE32_SYNTH
+   bool 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16
+
+   if [ "$CONFIG_AEDSP16" = "y" ]; then
+      comment 'SC-6600 Audio Cards have no jumper switches at all'
+      bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600
+
+      if [ "$CONFIG_SB" = "y" -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+         bool 'Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO
+         if [ "$CONFIG_AEDSP16_SBPRO" = "y" ]; then
+	    comment 'Audio Excel DSP 16 [Sound Blaster Pro]'
+            hex 'I/O base for Audio Excel DSP 16 220 or 240' \
+	                                           AEDSP16_BASE $SBC_BASE
+            int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \
+	                                           AEDSP16_SBC_IRQ $SBC_IRQ
+            int 'Audio Excel DSP 16 DMA 0, 1 or 3' AEDSP16_SBC_DMA $SBC_DMA
+         fi
+      fi
+   
+      if [ "$CONFIG_MSS" = "y" -a "$CONFIG_AEDSP16_SBPRO" != "y" ]; then
+         bool 'Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS
+         if [ "$CONFIG_AEDSP16_MSS" = "y" ]; then
+	    comment 'Audio Excel DSP 16 [Microsoft Sound System]'
+	    hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+            int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \
+	                                           AEDSP16_MSS_IRQ $MSS_IRQ
+            int 'Audio Excel DSP 16 DMA 0, 1 or 3' AEDSP16_MSS_DMA $MSS_DMA
+         fi
+      fi
+
+      if [ "$CONFIG_MIDI" = "y" ]; then
+         bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
+         if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then
+	    comment 'Audio Excel DSP 16 [MPU-401]'
+	    if [ "$CONFIG_AEDSP16_SBPRO" != "y" \
+	         -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+	       hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+	    fi
+            int 'MPU401 IRQ for Audio Excel DSP 16 5, 7, 9, 10 or 0 (disable)' \
+	    						AEDSP16_MPU_IRQ $MPU_IRQ
+         fi
+      fi
+
+      if [ "$CONFIG_SC6600" = "y" ]; then
+         comment 'SC-6600 specific configuration'
+         bool 'Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY
+         int 'SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' \
+	 					CONFIG_SC6600_CDROM 4
+         hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0
+      fi
+
+   fi
 fi
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/Makefile.old linux/drivers/sound/Makefile.old
--- v2.1.27/linux/drivers/sound/Makefile.old	Sun Nov 17 14:33:18 1996
+++ linux/drivers/sound/Makefile.old	Wed Dec 31 16:00:00 1969
@@ -1,164 +0,0 @@
-# Makefile for the Linux sound card driver
-#
-# Note 2! The CFLAGS definitions are now inherited from the
-# parent makes. (hopefully)
-#
-#
-
-.PHONY: dummy
-SUB_DIRS	= lowlevel
-VERSION		= `head -1 .version`
-TARGET_OS	= linux
-USRINCDIR	= /usr/include
-MODULEDIR	= /lib/modules/misc
-
-FIXEDOBJS	= soundcard.o dev_table.o sound_switch.o
-
-ifndef NO_LOWLEVEL
-	FIXEDOBJS := $(FIXEDOBJS) lowlevel/lowlevel.o
-endif
-
-ifeq (.defines,$(wildcard .defines))
-include .defines
-include .objects
-endif
-
-ifndef TOPDIR
-TOPDIR=/usr/src/linux
-endif
-
-
-ifndef HOSTCC
-build:
-	@echo Compiling modularized sound driver
-	@make sound.o
-	@echo Sound module compiled.
-
-install:	sound.o
-	cp sound.o $(MODULEDIR)
-endif
-
-.c.o:
-	$(CC) $(CFLAGS) -c $<
-
-ifeq ($(CONFIG_SOUND),y)
-
-all:	local.h sound.a
-
-OBJS += $(FIXEDOBJS)
-
-else
-all:
-endif
-
-ifndef HOSTCC
-#
-#	Running outside the kernel build.
-#
-CC	= gcc
-HOSTCC	= gcc
-CFLAGS	= -O2 -D__KERNEL__ -DMODULE -I/usr/src/linux/include -Wall -Wstrict-prototypes -fomit-frame-pointer -pipe -m486
-USE_DEPEND=y
-else
-include $(TOPDIR)/Rules.make
-endif
-
-sound.a: $(OBJS) 
-	-rm -f sound.a
-	$(AR) rcs sound.a $(OBJS)
-	sync
-
-clean:
-	rm -f core core.* *.o *.a tmp_make *~ x y z *%
-	rm -f configure
-	cd lowlevel;make clean
-	cd vivo;make clean
-
-indent:
-	for n in *.c;do echo indent $$n;indent $$n;done
-
-local.h: 
-	$(MAKE) clean
-	$(MAKE) setup
-	$(MAKE) oldconfig
-	$(MAKE) dep
-	@echo
-	@echo
-	@echo
-	@echo NOTE! Object file dependencies may not be up to date. Run
-	@echo make again if kernel/driver doesn''t link properly. Restarting
-	@echo it now may save some time.
-	@echo
-	@echo
-
-config: configure
-	@$(MAKE) setup
-	@./configure > local.h
-	@echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h
-	@echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h
-#	@echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h 2>/dev/null
-#	@echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null
-	@echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h
-
-oldconfig: setup configure
-	@./configure -o > local.h
-	@echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h
-	@echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h
-#	@echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h 2>/dev/null
-#	@echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null
-	@echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h
-
-kernelconfig: setup
-	rm -f configure
-	$(HOSTCC) -o configure configure.c
-	./configure fixedlocal > local.h
-	./configure fixeddefines > .defines
-	@echo \#define SOUND_CONFIG_DATE \"`date`\" >> local.h
-	@echo \#define SOUND_CONFIG_BY \"`whoami`\" >> local.h
-#	@echo \#define SOUND_CONFIG_HOST \"`hostname`\" >> local.h 2>/dev/null
-#	@echo \#define SOUND_CONFIG_DOMAIN \"`hostname -d`\" >> local.h 2>/dev/null
-	@echo \#define SOUND_UNAME_A \"`uname -a`\" >> local.h
-
-mkscript: setup
-	rm -f configure
-	$(HOSTCC) -o configure configure.c
-	./configure script > Config.in
-	cat lowlevel/Config.tmpl >> Config.in
-	./configure fixedlocal > local.h
-	./configure fixeddefines > .defines
-
-clrconf:
-	rm -f local.h .depend synth-ld.h trix_boot.h smw-midi0001.h maui_boot.h .defines
-
-configure: configure.c
-	$(HOSTCC) -o configure configure.c
-	@cat .blurb
-
-dep:
-	$(CPP) -M $(CFLAGS) -I. *.c > .depend
-
-setup:
-	@echo Compiling Sound Driver v $(VERSION) for Linux
-
-sound.o: local.h $(FIXEDOBJS) sound.a
-	-rm -f sound.o
-	$(LD) -r -o sound.o $(FIXEDOBJS) sound.a
-
-modules: local.h sound.o
-	ln -fs `pwd`/sound.o $(TOPDIR)/modules/sound.o
-
-
-lowlevel/lowlevel.o: dummy
-		cd lowlevel;make CC="$(CC)" CFLAGS="$(CFLAGS)"
-
-contrib:
-		cd lowlevel;make clean;make module "CC=$(CC)" CFLAGS="$(CFLAGS)"
-
-ifdef USE_DEPEND
-#
-# include a dependency file if one exists
-#
-ifeq (.depend,$(wildcard .depend))
-include .depend
-endif
-endif
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/Readme linux/drivers/sound/Readme
--- v2.1.27/linux/drivers/sound/Readme	Fri Nov 15 00:14:58 1996
+++ linux/drivers/sound/Readme	Wed Feb 26 02:34:52 1997
@@ -1,4 +1,4 @@
-OSS Lite version 3.7-beta release notes
+OSS Lite version 3.8-beta release notes
 ---------------------------------------
 
 Most up to date information about this driver is available from 
@@ -12,11 +12,11 @@
 date but still very useful. Information about bug fixes and such things
 is available from the web page (see below).
 
-New Programmer's Guide is currently under work (June/July 96). Please
-check http://www.4front-tech.com/pguide for more info.
+Please check http://www.4front-tech.com/pguide for more info about programming
+with OSS.
 
    ====================================================
--  THIS VERSION ____REQUIRES____ Linux 2.1.5 OR LATER.
+-  THIS VERSION ____REQUIRES____ Linux 2.1.26 OR LATER.
    ====================================================
 
 Packages "snd-util-3.7.tar.gz" and "snd-data-0.1.tar.Z"
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/Readme.aedsp16 linux/drivers/sound/Readme.aedsp16
--- v2.1.27/linux/drivers/sound/Readme.aedsp16	Fri Nov 15 00:14:58 1996
+++ linux/drivers/sound/Readme.aedsp16	Wed Dec 31 16:00:00 1969
@@ -1,6 +0,0 @@
-Informations about Audio Excel DSP 16 can be found in the source
-file aedsp16.c
-Please, read the head of the source before using it. It contain useful
-informations.
-
-    Riccardo
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/Readme.cards linux/drivers/sound/Readme.cards
--- v2.1.27/linux/drivers/sound/Readme.cards	Fri Nov 15 00:14:59 1996
+++ linux/drivers/sound/Readme.cards	Wed Feb 26 02:34:53 1997
@@ -1,4 +1,4 @@
-Configuring version 3.7 (for Linux) with some most common soundcards
+Configuring version 3.8 (for Linux) with some most common soundcards
 ====================================================================
 
 This document describes configuring soundcards with freeware version of
@@ -19,14 +19,95 @@
 		Cards having some kind of loadable "microcode" such as
 		PSS, SM Wave, AudioTrix Pro and Maui/Tropez must be
 		configured using the old method. The new one will not 
-		work with them.
+		work with them. The "old" method is used by default by
+		"make config". "make xconfig" will always use the "new"
+		method. "make menuconfig" will allow you to select which
+		method to use. After you have used the "new" method once
+		it will always be used when you use any of the config
+		programs. To return back to the "old" method you should
+		reinstall the kernel sources.
+
+		The /etc/soundconf file (forget it if you don't know what
+		this file does) contains settings that are used only by
+		the "old" method. Don't ever think the "active" settings
+		are stored there (they really are _NOT_ stored
+		there). Don't try to edit /etc/soundconf or any other
+		kernel or sound driver config files manually. The _only_
+		proper ways to change the settings are make config,
+		make menuconfig or make xconfig.
 
 		When using make xconfig and/or make menuconfig, you should
 		carefully check each sound configuration option (particularly
-		"Support for /dev/dsp and /dev/audio").
+		"Support for /dev/dsp and /dev/audio"). The default values
+		offered by these programs are not necessarily valid.
+
+
+
+THE BIGGEST MISTAKE YOU CAN DO
+==============================
+
+1. Assuming that the card is Sound Blaster compatible when it's not.
+--------------------------------------------------------------------
+
+The number one mistake is to assume that your card is compatible with
+Sound Blaster. Only the cards made by Creative Technology or which have
+one or more chips labeled by Creative are SB compatible. In addition there
+are few sound chipsets which are SB compatible in Linux such as ESS1688 or
+Jazz16. Note that SB compatibility in DOS/Windows does _NOT_ mean anything
+in Linux. 
+
+IF YOU REALLY ARE 150% SURE YOU REALLY HAVE A SOUND BLASTER YOU CAN SKIP THE
+REST OF THIS CHAPTER.
+
+For most other "supposed to be SB compatible" cards you have use other
+than SB drivers (see below). It is possible to get most soundcards to work
+in SB mode but in general it's complete waste of time. There are several
+problems which you will encounter by using SB mode with cards that are not
+truly SB compatible:
+
+- The SB emulation is at most SB Pro (DSP version 3.x) which means that 
+you get only 8 bit audio (there is always an another ("native") mode which
+gives the 16 bit capability). The 8 bit only operation is the reason why
+many users claim that sound quality in Linux is much worse than in DOS.
+In addition some applications require 16 bit mode and they produce just
+noise with a 8 bit only device.
+- The card may work only in some cases but refuse to work most of the
+time. The SB compatible mode always requires special intialization which is 
+done by the DOS/Windows drivers. This kind of cards work in Linux after
+you have warm booted it after DOS but they don't work after cold boot
+(power on or reset).
+- You get the famous "DMA timed out" messages. Usually all SB clones have
+software selectable IRQ and DMA settings. If the (power on default) values
+currently used by the card don't match configuration of the driver you will
+get the above error message whenever you try to record or play. There are
+few other reasons to the DMA timeout message but using the SB mode seems
+to be the most common cause.
+
+2. Trying to use a PnP (Plug & Play) card just like an ordinary soundcard
+-------------------------------------------------------------------------
+
+Plug & Play is a protocol defined by Intel and Microsoft. It let's operating
+systems to easily identify and reconfigure I/O ports, IRQs and DMAs of ISA
+cards. The problem with PnP cards is that the standard Linux doesn't currently
+(versions 2.1.x and earlier) don't support PnP. This means that you will have
+to use some special tricks (see later) to get a PnP card alive. Many PnP cards
+work after they have been initialized but this is not always the case.
+
+There are usually both a non PnP and PnP versions of the same soundcard.
+The non PnP version is the original model which usually has been discontinued
+more than an year ago. The PnP version has the same name but with a PnP
+appended to it (sometimes not). This causes major confusion since even the
+non PnP model works with Linux the PnP one doesn't.
+
+You should carefully check if "Plug & Play" or "PnP" is mentioned in the name
+of the card or in the documentation or package that came with the card. 
+Everything described in the rest of this document is not necessarily valid
+for PnP models of soudcards even you have managed to wake up the card properly.
+Many PnP cards are simply too much different than their original non PnP
+ancestors which are covered by this document.
 
 Cards that are not (fully) supported by this driver
----------------------------------------------------
+===================================================
 
 See http://www.4front-tech.com/ossfree for information about soundcards 
 to be supported in future. 
@@ -45,14 +126,14 @@
 soft configuring their I/O, IRQ, DMA and shared memory resources.
 Currently at least cards made by Creative Technology (SB32 and SB32AWE
 PnP), Gravis (GUS PnP and GUS PnP Pro), Ensoniq (Soundscape PnP) and
-Aztech (some Sound Galaxy models) use PnP technology. The CS4232 audio
+Aztech (some Sound Galaxy models) use PnP technology. The CS4232/4236 audio
 chip by Crystal Semiconductor (Intel Atlantis, HP Pavilion and many other
 motherboards) is also based on PnP technology but there is a "native" driver
 available for it (see information about CS4232 later in this document).
 
 PnP soundcards (as well as most other PnP ISA cards) are not supported
 by this version of the driver . Proper
-support for them should be released during spring 97 once kernel level
+support for them should be released during 97 once the kernel level
 PnP support is available.
 
 There is a method to get most of the PnP cards to work. The basic method
@@ -86,7 +167,7 @@
 
 Yet another way to use PnP cards is to use (commercial) OSS/Linux drivers.
 See http://www.4front-tech.com/linux.html for more info. This is the way
-you propably like to do it if you don't waste hours of time in recompiling
+you probably like to do it if you don't waste hours of time in recompiling
 kernel and the required tools.
 
 Read this before trying to configure the driver
@@ -125,18 +206,31 @@
 		compatible 16 bit mode. Usually it's MSS/WSS but it could also
 		be a proprietary one like MV Jazz16 or ESS ES688. OPTi
 		MAD16 chips are very common in so called "SB 16 bit cards".
+
+	======================================================================
 	"Supposed to be SB compatible" cards.
 		Forget the SB compatibility and check for other alternatives
 		first. The only cards that work with the SB driver in
 		Linux have been made by Creative Technology (there is at least
 		one chip on the card with "CREATIVE" printed on it). The
 		only other SB compatible chips are ESS and Jazz16 chips
-		(maybe ALSxxx chips too but they propably don't work).
+		(maybe ALSxxx chips too but they probably don't work).
+		Most other "16 bit SB compatible" cards such as "OPTi/MAD16" or
+		"Crystal" are _NOT_ SB compatible in Linux.
 
 		Practically all soundcards have some kind of SB emulation mode
 		in addition to their native (16 bit) mode. In most cases this
-		(8 bit only) SB compatible mode doesn't work with Linux. However
-		in most cases the native 16 bit mode is supported by Linux.
+		(8 bit only) SB compatible mode doesn't work with Linux. If
+		you get it working it may cause problems with games and
+		applications which require 16 bit audio. Some 16 bit only
+		applications don't check if the card actually supports 16 bits.
+		They just dump 16 bit data to a 8 bit card which produces just
+		noise.
+
+		In most cases the 16 bit native mode is supported by Linux.
+		Use the SB mode with "clones" only if you don't find anything
+		better from the rest of this doc.
+	======================================================================
 
 Gravis Ultrasound (GUS)
 	GUS
@@ -177,7 +271,7 @@
 	This UART chip is used in the MIDI interface of some (rare)
 	soundcards. It's supported by the driver in case you need it.
 
-Yamaha FM synthesizers (OPL2, OPL3 and OPL4)
+Yamaha FM synthesizers (OPL2, OPL3 (not OPL3-SA) and OPL4)
 	Most soundcards have a FM synthesizer chip. The OPL2 is a 2
 	operator chip used in the original AdLib card. Currently it's used
 	only in the cheapest (8 bit mono) cards. The OPL3 is a 4 operator 
@@ -193,6 +287,13 @@
 	card has a FM chip made by Yamaha. Don't enable it if your card
 	has a software (TRS) based FM emulator.
 
+	----------------------------------------------------------------
+	NOTE! OPL3-SA is different chip than the ordinary OPL3. In addition
+	to the FM synth this chip has also digital audio (WSS) and
+	MIDI (MPU401) capabilities. OPL3-SA is not supported by OSS/Free.
+	Support for it is included in OSS/Linux v3.8 and later.
+	----------------------------------------------------------------
+
 PSS based cards (AD1848 + ADSP-2115 + Echo ESC614 ASIC)
 	Analog Devices and Echo Speech have together defined a soundcard
 	architecture based on the above chips. The DSP chip is used
@@ -203,11 +304,17 @@
 
 	The driver supports downloading DSP algorithms to these cards.
 
+	NOTE! You will have to use the "old" config script when configuring
+	PSS cards.
+
 MediaTrix AudioTrix Pro
 	The ATP card is built around a CS4231 codec and an OPL4 synthesizer
 	chips. The OPL4 mode is supported by a microcontroller running a
 	General MIDI emulator. There is also a SB 1.5 compatible playback mode.
 
+	NOTE! You will have to use the "old" config script when configuring
+	AudioTrix Pro.
+
 Ensoniq SoundScape and compatibles
 	Ensoniq has designed a soundcard architecture based on the
 	OTTO synthesizer chip used in their professional MIDI synthesizers.
@@ -218,31 +325,35 @@
 
 MAD16 and Mozart based cards
 	The Mozart (OAK OTI-601), MAD16 (OPTi 82C928), MAD16 Pro (OPTi 82C929),
-	OPTi 82C924 (non PnP mode) and OPTi 82C930 interface
+	OPTi 82C924/82C925 (in _non_ PnP mode) and OPTi 82C930 interface
 	chips are used in many different soundcards, including some
 	cards by Reveal miro and Turtle Beach (Tropez). The purpose of these
 	chips is to connect other audio components to the PC bus. The
 	interface chip performs address decoding for the other chips.
 	NOTE! Tropez Plus is not MAD16 but CS4232 based.
+	NOTE! MAD16 PnP cards (82C924, 82C925, 82C931) are not MAD16 compatible
+	in the PnP mode. You will have to use them in MAD16 mode after having
+	initialized them using isapnptools or DOS.
 
 Audio Excel DSP16 
 	Support for this card was written by Riccardo Faccetti
 	(riccardo@cdc8g5.cdc.polimi.it). The AEDSP16 driver included in
-	this source distribution is not fully functional. A patch is
-	available from sunsite.unc.edu/pub/Linux/kernel/sound.
-	
-Crystal CS4232 based cards such as AcerMagic S23, TB Tropez _Plus_ and 
+	the lowlevel/ directory. To use it you should use the "new" config
+	script and to enable the "Additional low level drivers" option.
+Crystal CS4232 and 4236 based cards such as AcerMagic S23, TB Tropez _Plus_ and 
 	many PC motherboards (Compaq, HP, Intel, ...)
 	CS4232 is a PnP multimedia chip which contains a CS3231A codec,
 	SB and MPU401 emulations. There is support for OPL3 too.
 	Unfortunately the MPU401 mode doesn't work (I don't know how to
-	initialize it). 
+	initialize it). CS4236 is an enhanced (compatible) version of 4232.
 
-Turtle Beach Maui and Tropez
+Turtle Beach Maui and Tropez "classic"
 	This driver version supports sample, patch and program loading commands
 	described in the Maui/Tropez User's manual. 
 	There is now full initialization support too. The audio side of
 	the Tropez is based on the MAD16 chip (see above).
+	NOTE! You will have to use the "old" config script when configuring
+	Maui or Tropez.
 
 Jumpers and software configuration
 ----------------------------------
@@ -430,21 +541,32 @@
 	  in this file. For cards having native support in the driver, consult
 	  the card specific instructions later in this file. Some drivers
 	  have their own MSS support and enabling this option will cause a
-	  conflict.
+	  conflict. 
+	  Note! The MSS driver permits configuring two DMA channels. This is a
+	  "nonstandard" feature and works only with very few cards (if any).
+	  In most cases the second DMA channel should be disabled or set to
+	  the same channel than the first one. Trying to configure two separate
+	  channels with cards that don't support this feature will prevent
+	  audio (at least recording) from working.
   "Ensoniq Soundscape support",
 	- Answer 'y' if you have a soundcard based on the Ensoniq SoundScape
 	  chipset. Such cards are being manufactured at least by Ensoniq,
-	  Spea and Reveal (note that Reveal makes other cards also).
+	  Spea and Reveal (note that Reveal makes other cards also). Oldest
+	  cards made by Spea don't work properly with Linux. 
+	  Soundscape PnP as well as Ensoniq VIVO work only with the commercial
+	  OSS/Linux version.
   "MediaTrix AudioTrix Pro support",
 	- Answer 'y' if you have the AudioTrix Pro.
   "Support for MAD16 and/or Mozart based cards",
 	- Answer y if your card has a Mozart (OAK OTI-601) or MAD16
-	  (OPTi 82C928, 82C929, 82C924 or 82C930) audio interface chip. 
+	  (OPTi 82C928, 82C929, 82C924/82C925 or 82C930) audio interface chip. 
 	  These chips are
 	  currently quite common so it's possible that many no-name cards
 	  have one of them. In addition the MAD16 chip is used in some
 	  cards made by known manufacturers such as Turtle Beach (Tropez),
 	  Reveal (some models) and Diamond (some recent models).
+	  Note OPTi 82C924 and 82C925 are MAD16 compatible only in non PnP
+	  mode (jumper selectable on many cards).
   "Support for TB Maui"
 	- This enables TB Maui specific initialization. Works with TB Maui
 	and TB Tropez (may not work with Tropez Plus).
@@ -773,9 +895,8 @@
 The one connected to the MAD16 chip is the second one (there is a second
 MIDI connector/pins somewhere??). If you have not connected the second MIDI
 port, just disable the MIDI port of MAD16. The 'Maui' compatible synth of
-Tropez is jumper configurable and not connected to the MAD16 chip.
-It can be used by enabling the stand alone MPU401 support but you have
-to initialize it by using the MS-DOS SNDSETUP program.
+Tropez is jumper configurable and not connected to the MAD16 chip (the
+Maui driver can be used with it).
 
 Some MAD16 based cards may cause feedback, whistle or terrible noise if the
 line3 mixer channel is turned too high. This happens at least with Shuttle
@@ -790,7 +911,7 @@
 can be enabled by configuring the card to use two DMA channels. Possible
 DMA channel pairs are: 0&1, 1&0 and 3&0.
 
-NOTE! Cards having an OPTi 82C924 chip work with OSS/Free only in
+NOTE! Cards having an OPTi 82C924/82C925 chip work with OSS/Free only in
 non-PnP mode (usually jumper selectable). The PnP mode is supported only
 by OSS/Linux.
 
@@ -812,6 +933,9 @@
 Logitech Soundman Wave
 ----------------------
 
+NOTE! You will have to use the "old" config script when configuring
+      SM Wave.
+
 Read the above MV Jazz specific instructions first.
 
 The Logitech SoundMan Wave (don't confuse with the SM16 or SM Games) is
@@ -860,7 +984,20 @@
 so you don't need to enable it (the driver uses normal SB MIDI automatically
 with ES688).
 
-NOTE! ESS cards are not compatible with MSS/WSS.
+NOTE! ESS cards are not compatible with MSS/WSS so don't worry if MSS support
+of OSS doesn't work with it.
+
+There are some ES1688/688 based soundcards and (particularily) motherboards
+which use software configurable I/O port relocation feature of the chip.
+This ESS proprietary feature is supported only by OSS/Linux.
+
+There are ES1688 based cards which use different interrupt pin assignment than
+recommended by ESS (5, 7, 9/2 and 10). In this case all IRQ's don't work.
+At least a card called (Pearl?) Hypersound 16 supports IRQ15 but it doesn't
+work.
+
+ES1868 is a PnP chip which is (supposed to be) compatible with ESS1688
+brobably works with OSS/Free after initialization using isapnptools.
 
 Reveal cards
 ------------
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/Readme.linux linux/drivers/sound/Readme.linux
--- v2.1.27/linux/drivers/sound/Readme.linux	Fri Nov 15 00:14:58 1996
+++ linux/drivers/sound/Readme.linux	Wed Feb 26 02:34:51 1997
@@ -72,6 +72,15 @@
 Hannu Savolainen
 hannu@voxware.pp.fi
 
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+NOTE!
+
+Running the script enclosed below is usually not required. All known Linux
+distributions build them automaticly during installation. You need to run
+this script only if "ls /dev/sndstat" displays "No such file or directory".
+In case of any other error message you should start looking for the reason
+from somewhere else (see above).
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 ----------------- cut here ------------------------------
 #!/bin/sh
 # *****************************************
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/ad1848.c linux/drivers/sound/ad1848.c
--- v2.1.27/linux/drivers/sound/ad1848.c	Fri Nov 15 00:15:08 1996
+++ linux/drivers/sound/ad1848.c	Wed Feb 26 02:35:04 1997
@@ -15,7 +15,7 @@
  */
 
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -42,17 +42,13 @@
     unsigned char   saved_regs[16];
     int             debug_flag;
 
-    int             speed;
-    unsigned char   speed_bits;
-    int             channels;
-    int             audio_format;
-    unsigned char   format_bits;
     int             audio_flags;
+    int             record_dev, playback_dev;
 
     int             xfer_count;
     int             audio_mode;
+    int             open_mode;
     int             intr_active;
-    int             opened;
     char           *chip_name;
     int             model;
 #define MD_1848		1
@@ -76,16 +72,29 @@
     mixer_ents     *mix_devices;
     int             mixer_output_port;
   }
-
 ad1848_info;
 
+typedef struct ad1848_port_info
+  {
+    int             open_mode;
+    int             speed;
+    unsigned char   speed_bits;
+    int             channels;
+    int             audio_format;
+    unsigned char   format_bits;
+  }
+ad1848_port_info;
+
 static int      nr_ad1848_devs = 0;
 static volatile char irq2dev[17] =
 {-1, -1, -1, -1, -1, -1, -1, -1,
  -1, -1, -1, -1, -1, -1, -1, -1, -1};
 
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 static int      timer_installed = -1;
 
+#endif
+
 static int      ad_format_mask[8 /*devc->model */ ] =
 {
   0,
@@ -107,19 +116,22 @@
 
 static int      ad1848_open (int dev, int mode);
 static void     ad1848_close (int dev);
-static int      ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg, int local);
-static void     ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
-static void     ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart);
+static int      ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg);
+static void     ad1848_output_block (int dev, unsigned long buf, int count, int intrflag);
+static void     ad1848_start_input (int dev, unsigned long buf, int count, int intrflag);
 static int      ad1848_prepare_for_output (int dev, int bsize, int bcount);
-static int      ad1848_prepare_for_IO (int dev, int bsize, int bcount);
-static void     ad1848_reset (int dev);
+static int      ad1848_prepare_for_input (int dev, int bsize, int bcount);
 static void     ad1848_halt (int dev);
 static void     ad1848_halt_input (int dev);
 static void     ad1848_halt_output (int dev);
 static void     ad1848_trigger (int dev, int bits);
+
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 static int      ad1848_tmr_install (int dev);
 static void     ad1848_tmr_reprogram (int dev);
 
+#endif
+
 static int
 ad_read (ad1848_info * devc, int reg)
 {
@@ -134,7 +146,7 @@
   cli ();
   outb (((unsigned char) (reg & 0xff) | devc->MCE_bit), io_Index_Addr (devc));
   x = inb (io_Indexed_Data (devc));
-  /*  printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */
+/* printk("(%02x<-%02x) ", reg|devc->MCE_bit, x); */
   restore_flags (flags);
 
   return x;
@@ -188,6 +200,7 @@
   if (ad_read (devc, 11) & 0x20)
     if (devc->model != MD_1845)
       printk ("ad1848: Auto calibration timed out(3).\n");
+  ad_write (devc, 9, ad_read (devc, 9) & ~0x18);	/* Disable autocalibration */
 }
 
 static void
@@ -205,24 +218,18 @@
       ad_write (devc, i, prev | 0x80);
     }
 
-/*
- * Let's have some delay
- */
-
-  for (i = 0; i < 1000; i++)
-    inb (devc->base);
 }
 
 static void
 ad_unmute (ad1848_info * devc)
 {
-  int             i;
+  int             i, dummy;
 
 /*
  * Let's have some delay
  */
   for (i = 0; i < 1000; i++)
-    inb (devc->base);
+    dummy = inb (devc->base);
 
   /*
    * Restore back old volume registers (unmute)
@@ -262,7 +269,7 @@
 ad_leave_MCE (ad1848_info * devc)
 {
   unsigned long   flags;
-  unsigned char   prev;
+  unsigned char   prev, acal;
   int             timeout = 1000;
 
   while (timeout > 0 && inb (devc->base) == 0x80)	/*Are we initializing */
@@ -271,6 +278,8 @@
   save_flags (flags);
   cli ();
 
+  acal = ad_read (devc, 9);
+
   devc->MCE_bit = 0x00;
   prev = inb (io_Index_Addr (devc));
   outb ((0x00), io_Index_Addr (devc));	/* Clear the MCE bit */
@@ -282,7 +291,8 @@
     }
 
   outb ((0x00), io_Index_Addr (devc));	/* Clear the MCE bit */
-  wait_for_calibration (devc);
+  if (acal & 0x08)		/* Auto calibration is enabled */
+    wait_for_calibration (devc);
   restore_flags (flags);
 }
 
@@ -526,10 +536,10 @@
     {
       int             val;
 
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
 
       if (val == 0xffff)
-	return ioctl_out (arg, devc->mixer_output_port);
+	return (*(int *) arg = devc->mixer_output_port);
 
       val &= (AUDIO_SPEAKER | AUDIO_HEADPHONE | AUDIO_LINE_OUT);
 
@@ -542,24 +552,24 @@
       else
 	ad_write (devc, 26, ad_read (devc, 26) | 0x40);		/* Mute mono out */
 
-      return ioctl_out (arg, devc->mixer_output_port);
+      return (*(int *) arg = devc->mixer_output_port);
     }
 
   if (((cmd >> 8) & 0xff) == 'M')
     {
       int             val;
 
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
 	switch (cmd & 0xff)
 	  {
 	  case SOUND_MIXER_RECSRC:
-	    get_user (val, (int *) arg);
-	    return ioctl_out (arg, ad1848_set_recmask (devc, val));
+	    val = *(int *) arg;
+	    return (*(int *) arg = ad1848_set_recmask (devc, val));
 	    break;
 
 	  default:
-	    get_user (val, (int *) arg);
-	    return ioctl_out (arg, ad1848_mixer_set (devc, cmd & 0xff, val));
+	    val = *(int *) arg;
+	    return (*(int *) arg = ad1848_mixer_set (devc, cmd & 0xff, val));
 	  }
       else
 	switch (cmd & 0xff)	/*
@@ -568,30 +578,30 @@
 	  {
 
 	  case SOUND_MIXER_RECSRC:
-	    return ioctl_out (arg, devc->recmask);
+	    return (*(int *) arg = devc->recmask);
 	    break;
 
 	  case SOUND_MIXER_DEVMASK:
-	    return ioctl_out (arg, devc->supported_devices);
+	    return (*(int *) arg = devc->supported_devices);
 	    break;
 
 	  case SOUND_MIXER_STEREODEVS:
 	    if (devc->model == MD_C930)
-	      return ioctl_out (arg, devc->supported_devices);
+	      return (*(int *) arg = devc->supported_devices);
 	    else
-	      return ioctl_out (arg, devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
+	      return (*(int *) arg = devc->supported_devices & ~(SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
 	    break;
 
 	  case SOUND_MIXER_RECMASK:
-	    return ioctl_out (arg, devc->supported_rec_devices);
+	    return (*(int *) arg = devc->supported_rec_devices);
 	    break;
 
 	  case SOUND_MIXER_CAPS:
-	    return ioctl_out (arg, SOUND_CAP_EXCL_INPUT);
+	    return (*(int *) arg = SOUND_CAP_EXCL_INPUT);
 	    break;
 
 	  default:
-	    return ioctl_out (arg, ad1848_mixer_get (devc, cmd & 0xff));
+	    return (*(int *) arg = ad1848_mixer_get (devc, cmd & 0xff));
 	  }
     }
   else
@@ -602,6 +612,7 @@
 ad1848_set_speed (int dev, int arg)
 {
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   /*
      * The sampling speed is encoded in the least significant nibble of I8. The
@@ -642,7 +653,7 @@
   n = sizeof (speed_table) / sizeof (speed_struct);
 
   if (arg <= 0)
-    return devc->speed;
+    return portc->speed;
 
   if (devc->model == MD_1845)	/* AD1845 has different timer than others */
     {
@@ -651,9 +662,9 @@
       if (arg > 50000)
 	arg = 50000;
 
-      devc->speed = arg;
-      devc->speed_bits = speed_table[3].bits;
-      return devc->speed;
+      portc->speed = arg;
+      portc->speed_bits = speed_table[3].bits;
+      return portc->speed;
     }
 
   if (arg < speed_table[0].speed)
@@ -683,20 +694,20 @@
       selected = 3;
     }
 
-  devc->speed = speed_table[selected].speed;
-  devc->speed_bits = speed_table[selected].bits;
-  return devc->speed;
+  portc->speed = speed_table[selected].speed;
+  portc->speed_bits = speed_table[selected].bits;
+  return portc->speed;
 }
 
 static short
 ad1848_set_channels (int dev, short arg)
 {
-  ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   if (arg != 1 && arg != 2)
-    return devc->channels;
+    return portc->channels;
 
-  devc->channels = arg;
+  portc->channels = arg;
   return arg;
 }
 
@@ -704,6 +715,7 @@
 ad1848_set_bits (int dev, unsigned int arg)
 {
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   static struct format_tbl
     {
@@ -755,25 +767,25 @@
   int             i, n = sizeof (format2bits) / sizeof (struct format_tbl);
 
   if (arg == 0)
-    return devc->audio_format;
+    return portc->audio_format;
 
   if (!(arg & ad_format_mask[devc->model]))
     arg = AFMT_U8;
 
-  devc->audio_format = arg;
+  portc->audio_format = arg;
 
   for (i = 0; i < n; i++)
     if (format2bits[i].format == arg)
       {
-	if ((devc->format_bits = format2bits[i].bits) == 0)
-	  return devc->audio_format = AFMT_U8;	/* Was not supported */
+	if ((portc->format_bits = format2bits[i].bits) == 0)
+	  return portc->audio_format = AFMT_U8;		/* Was not supported */
 
 	return arg;
       }
 
   /* Still hanging here. Something must be terribly wrong */
-  devc->format_bits = 0;
-  return devc->audio_format = AFMT_U8;
+  portc->format_bits = 0;
+  return portc->audio_format = AFMT_U8;
 }
 
 static struct audio_driver ad1848_audio_driver =
@@ -783,9 +795,8 @@
   ad1848_output_block,
   ad1848_start_input,
   ad1848_ioctl,
-  ad1848_prepare_for_IO,
+  ad1848_prepare_for_input,
   ad1848_prepare_for_output,
-  ad1848_reset,
   ad1848_halt,
   NULL,
   NULL,
@@ -808,17 +819,18 @@
 ad1848_open (int dev, int mode)
 {
   ad1848_info    *devc = NULL;
+  ad1848_port_info *portc;
   unsigned long   flags;
 
   if (dev < 0 || dev >= num_audiodevs)
     return -ENXIO;
 
   devc = (ad1848_info *) audio_devs[dev]->devc;
-
+  portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   save_flags (flags);
   cli ();
-  if (devc->opened)
+  if (portc->open_mode || (devc->open_mode & mode))
     {
       restore_flags (flags);
       return -EBUSY;
@@ -832,9 +844,15 @@
     }
 
   devc->intr_active = 0;
-  devc->opened = 1;
   devc->audio_mode = 0;
+  devc->open_mode |= mode;
+  portc->open_mode = mode;
   ad1848_trigger (dev, 0);
+
+  if (mode & OPEN_READ)
+    devc->record_dev = dev;
+  if (mode & OPEN_WRITE)
+    devc->playback_dev = dev;
   restore_flags (flags);
 /*
  * Mute output until the playback really starts. This decreases clicking (hope so).
@@ -849,6 +867,7 @@
 {
   unsigned long   flags;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   DEB (printk ("ad1848_close(void)\n"));
 
@@ -856,43 +875,41 @@
   cli ();
 
   devc->intr_active = 0;
-  ad1848_reset (dev);
-
+  ad1848_halt (dev);
 
-  devc->opened = 0;
   devc->audio_mode = 0;
+  devc->open_mode &= ~portc->open_mode;
+  portc->open_mode = 0;
 
   ad_unmute (devc);
   restore_flags (flags);
 }
 
 static int
-ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
+ad1848_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
   return -EINVAL;
 }
 
 static void
-ad1848_output_block (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+ad1848_output_block (int dev, unsigned long buf, int count, int intrflag)
 {
   unsigned long   flags, cnt;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
-
-  if (!dma_restart)
-    return;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   cnt = count;
 
-  if (devc->audio_format == AFMT_IMA_ADPCM)
+  if (portc->audio_format == AFMT_IMA_ADPCM)
     {
       cnt /= 4;
     }
   else
     {
-      if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
+      if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
 	cnt >>= 1;
     }
-  if (devc->channels > 1)
+  if (portc->channels > 1)
     cnt >>= 1;
   cnt--;
 
@@ -909,13 +926,9 @@
   save_flags (flags);
   cli ();
 
-  ad1848_halt_output (dev);
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
-
   ad_write (devc, 15, (unsigned char) (cnt & 0xff));
   ad_write (devc, 14, (unsigned char) ((cnt >> 8) & 0xff));
 
-
   devc->xfer_count = cnt;
   devc->audio_mode |= PCM_ENABLE_OUTPUT;
   devc->intr_active = 1;
@@ -923,25 +936,23 @@
 }
 
 static void
-ad1848_start_input (int dev, unsigned long buf, int count, int intrflag, int dma_restart)
+ad1848_start_input (int dev, unsigned long buf, int count, int intrflag)
 {
   unsigned long   flags, cnt;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
-
-  if (!dma_restart)
-    return;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   cnt = count;
-  if (devc->audio_format == AFMT_IMA_ADPCM)
+  if (portc->audio_format == AFMT_IMA_ADPCM)
     {
       cnt /= 4;
     }
   else
     {
-      if (devc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
+      if (portc->audio_format & (AFMT_S16_LE | AFMT_S16_BE))	/* 16 bit data */
 	cnt >>= 1;
     }
-  if (devc->channels > 1)
+  if (portc->channels > 1)
     cnt >>= 1;
   cnt--;
 
@@ -958,12 +969,6 @@
   save_flags (flags);
   cli ();
 
-  if (dma_restart)
-    {
-      ad1848_halt_input (dev);
-      DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
-    }
-
   if (devc->model == MD_1848 || !devc->dual_dma)	/* Single DMA channel mode */
     {
       ad_write (devc, 15, (unsigned char) (cnt & 0xff));
@@ -987,62 +992,99 @@
 static int
 ad1848_prepare_for_output (int dev, int bsize, int bcount)
 {
+  int             timeout;
+  unsigned char   fs, old_fs, tmp = 0;
+  unsigned long   flags;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   ad_mute (devc);
-  if (devc->model != MD_4232)
+
+  save_flags (flags);
+  cli ();
+  fs = portc->speed_bits | (portc->format_bits << 5);
+
+  if (portc->channels > 1)
+    fs |= 0x10;
+
+  if (devc->model == MD_1845)	/* Use alternate speed select registers */
     {
-/*
- * This code fragment ensures that the playback FIFO is empty before
- * setting the codec for playback. Enabling playback for a moment should
- * be enough to do that.
- */
-      int             tmout;
+      fs &= 0xf0;		/* Mask off the rate select bits */
 
-      ad_write (devc, 9, ad_read (devc, 9) | 0x01);	/* Enable playback */
-      disable_dma (audio_devs[dev]->dmachan1);
-      for (tmout = 0; tmout < 1000000; tmout++)
-	if (ad_read (devc, 11) & 0x10)	/* DRQ active */
-	  if (tmout > 10000)
-	    break;
-      ad_write (devc, 9, ad_read (devc, 9) & ~0x01);	/* Stop playback */
+      ad_write (devc, 22, (portc->speed >> 8) & 0xff);	/* Speed MSB */
+      ad_write (devc, 23, portc->speed & 0xff);		/* Speed LSB */
+    }
+
+  old_fs = ad_read (devc, 8);
 
-      enable_dma (audio_devs[dev]->dmachan1);
-      devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
+  ad_enter_MCE (devc);		/* Enables changes to the format select reg */
+
+  if (devc->model == MD_4232)
+    {
+      tmp = ad_read (devc, 16);
+      ad_write (devc, 16, tmp | 0x30);
     }
 
-  return ad1848_prepare_for_IO (dev, bsize, bcount);
+  ad_write (devc, 8, fs);
+  /*
+   * Write to I8 starts resynchronization. Wait until it completes.
+   */
+  timeout = 0;
+  while (timeout < 100 && inb (devc->base) != 0x80)
+    timeout++;
+  timeout = 0;
+  while (timeout < 10000 && inb (devc->base) == 0x80)
+    timeout++;
+
+  if (devc->model == MD_4232)
+    ad_write (devc, 16, tmp & ~0x30);
+
+  ad_leave_MCE (devc);		/*
+				 * Starts the calibration process.
+				 */
+  restore_flags (flags);
+  devc->xfer_count = 0;
+
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
+  if (dev == timer_installed && devc->timer_running)
+    if ((fs & 0x01) != (old_fs & 0x01))
+      {
+	ad1848_tmr_reprogram (dev);
+      }
+#endif
+  ad1848_halt_output (dev);
+  return 0;
 }
 
 static int
-ad1848_prepare_for_IO (int dev, int bsize, int bcount)
+ad1848_prepare_for_input (int dev, int bsize, int bcount)
 {
   int             timeout;
   unsigned char   fs, old_fs, tmp = 0;
   unsigned long   flags;
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   if (devc->audio_mode)
     return 0;
 
   save_flags (flags);
   cli ();
-  fs = devc->speed_bits | (devc->format_bits << 5);
+  fs = portc->speed_bits | (portc->format_bits << 5);
 
-  if (devc->channels > 1)
+  if (portc->channels > 1)
     fs |= 0x10;
 
   if (devc->model == MD_1845)	/* Use alternate speed select registers */
     {
       fs &= 0xf0;		/* Mask off the rate select bits */
 
-      ad_write (devc, 22, (devc->speed >> 8) & 0xff);	/* Speed MSB */
-      ad_write (devc, 23, devc->speed & 0xff);	/* Speed LSB */
+      ad_write (devc, 22, (portc->speed >> 8) & 0xff);	/* Speed MSB */
+      ad_write (devc, 23, portc->speed & 0xff);		/* Speed LSB */
     }
 
   old_fs = ad_read (devc, 8);
 
-
   ad_enter_MCE (devc);		/* Enables changes to the format select reg */
 
   if (devc->model == MD_4232)
@@ -1090,33 +1132,29 @@
   restore_flags (flags);
   devc->xfer_count = 0;
 
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
   if (dev == timer_installed && devc->timer_running)
     if ((fs & 0x01) != (old_fs & 0x01))
       {
 	ad1848_tmr_reprogram (dev);
       }
 #endif
+  ad1848_halt_input (dev);
   return 0;
 }
 
 static void
-ad1848_reset (int dev)
-{
-  ad1848_halt (dev);
-}
-
-static void
 ad1848_halt (int dev)
 {
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
 
   unsigned char   bits = ad_read (devc, 9);
 
-  if (bits & 0x01)
+  if (bits & 0x01 && portc->open_mode & OPEN_WRITE)
     ad1848_halt_output (dev);
 
-  if (bits & 0x02)
+  if (bits & 0x02 && portc->open_mode & OPEN_READ)
     ad1848_halt_input (dev);
   devc->audio_mode = 0;
 }
@@ -1138,18 +1176,17 @@
   {
     int             tmout;
 
-    disable_dma (audio_devs[dev]->dmachan1);
+    disable_dma (audio_devs[dev]->dmap_out->dma);
 
     for (tmout = 0; tmout < 100000; tmout++)
       if (ad_read (devc, 11) & 0x10)
 	break;
     ad_write (devc, 9, ad_read (devc, 9) & ~0x02);	/* Stop capture */
 
-    enable_dma (audio_devs[dev]->dmachan1);
+    enable_dma (audio_devs[dev]->dmap_out->dma);
     devc->audio_mode &= ~PCM_ENABLE_INPUT;
   }
 
-
   outb ((0), io_Status (devc));	/* Clear interrupt status */
   outb ((0), io_Status (devc));	/* Clear interrupt status */
 
@@ -1174,18 +1211,17 @@
   {
     int             tmout;
 
-    disable_dma (audio_devs[dev]->dmachan1);
+    disable_dma (audio_devs[dev]->dmap_out->dma);
 
     for (tmout = 0; tmout < 100000; tmout++)
       if (ad_read (devc, 11) & 0x10)
 	break;
     ad_write (devc, 9, ad_read (devc, 9) & ~0x01);	/* Stop playback */
 
-    enable_dma (audio_devs[dev]->dmachan1);
+    enable_dma (audio_devs[dev]->dmap_out->dma);
     devc->audio_mode &= ~PCM_ENABLE_OUTPUT;
   }
 
-
   outb ((0), io_Status (devc));	/* Clear interrupt status */
   outb ((0), io_Status (devc));	/* Clear interrupt status */
 
@@ -1198,6 +1234,7 @@
 ad1848_trigger (int dev, int state)
 {
   ad1848_info    *devc = (ad1848_info *) audio_devs[dev]->devc;
+  ad1848_port_info *portc = (ad1848_port_info *) audio_devs[dev]->portc;
   unsigned long   flags;
   unsigned char   tmp, old;
 
@@ -1205,22 +1242,35 @@
   cli ();
   state &= devc->audio_mode;
 
-  tmp = (old = ad_read (devc, 9)) & ~0x03;
-  if (state & PCM_ENABLE_INPUT)
-    tmp |= 0x02;
-  if (state & PCM_ENABLE_OUTPUT)
-    tmp |= 0x01;
+  tmp = old = ad_read (devc, 9);
 
-  ad_mute (devc);
+  if (portc->open_mode & OPEN_READ)
+    {
+      if (state & PCM_ENABLE_INPUT)
+	tmp |= 0x02;
+      else
+	tmp &= ~0x02;
+    }
 
-  ad_write (devc, 9, tmp);
+  if (portc->open_mode & OPEN_WRITE)
+    {
+      if (state & PCM_ENABLE_OUTPUT)
+	tmp |= 0x01;
+      else
+	tmp &= ~0x01;
+    }
 
-  ad_unmute (devc);
+  /* ad_mute(devc); */
+  if (tmp != old)
+    {
+      ad_write (devc, 9, tmp);
+      ad_unmute (devc);
+    }
 
   restore_flags (flags);
 }
 
-void
+static void
 ad1848_init_hw (ad1848_info * devc)
 {
   int             i;
@@ -1332,7 +1382,7 @@
   devc->timer_running = 0;
   devc->MCE_bit = 0x40;
   devc->irq = 0;
-  devc->opened = 0;
+  devc->open_mode = 0;
   devc->chip_name = "AD1848";
   devc->model = MD_1848;	/* AD1848 or CS4248 */
   devc->levels = NULL;
@@ -1348,12 +1398,10 @@
      * If the I/O address is unused, it typically returns 0xff.
    */
 
-  DDB (printk ("ad1848_detect() - step A\n"));
-
 /*
  * Wait for the device to stop initialization
  */
-  /* outb(( 0x0b),  devc->base); */
+  DDB (printk ("ad1848_detect() - step 0\n"));
 
   for (i = 0; i < 10000000; i++)
     {
@@ -1363,10 +1411,12 @@
 	break;
     }
 
+  DDB (printk ("ad1848_detect() - step A\n"));
+
   if ((inb (devc->base) & 0x80) != 0x00)	/* Not a AD1848 */
     {
       DDB (printk ("ad1848 detect error - step A (%02x)\n",
-		   inb (devc->base)));
+		   (int) inb (devc->base)));
       return 0;
     }
 
@@ -1391,7 +1441,7 @@
     else
       {
 	DDB (printk ("ad1848 detect error - step B (%x/%x)\n", tmp1, tmp2));
-	return 0;
+	/* return 0; */
       }
 
   DDB (printk ("ad1848_detect() - step C\n"));
@@ -1404,7 +1454,7 @@
     else
       {
 	DDB (printk ("ad1848 detect error - step C (%x/%x)\n", tmp1, tmp2));
-	return 0;
+	/* return 0; */
       }
 
   /*
@@ -1505,7 +1555,7 @@
 	  ad_write (devc, 25, ~tmp1);	/* Invert all bits */
 	  if ((ad_read (devc, 25) & 0xe7) == (tmp1 & 0xe7))
 	    {
-	      int             id;
+	      int             id, full_id;
 
 	      /*
 	       *      It's at least CS4231
@@ -1520,8 +1570,14 @@
 	       * while the CS4231A reports different.
 	       */
 
-	      DDB (printk ("ad1848_detect() - step I\n"));
 	      id = ad_read (devc, 25) & 0xe7;
+	      full_id = ad_read (devc, 25);
+	      if (id == 0x80)	/* Device busy??? */
+		id = ad_read (devc, 25) & 0xe7;
+	      if (id == 0x80)	/* Device still busy??? */
+		id = ad_read (devc, 25) & 0xe7;
+	      DDB (printk ("ad1848_detect() - step J (%02x/%02x)\n", id,
+			   ad_read (devc, 25)));
 
 	      switch (id)
 		{
@@ -1547,6 +1603,11 @@
 		  devc->model = MD_4232;
 		  break;
 
+		case 0x41:
+		  devc->chip_name = "CS4236B";
+		  devc->model = MD_4232;
+		  break;
+
 		case 0x80:
 		  {
 		    /* 
@@ -1559,23 +1620,25 @@
 		    unsigned char   tmp = ad_read (devc, 23);
 
 		    ad_write (devc, 23, ~tmp);
-		    if (ad_read (devc, 23) != tmp)	/* AD1845 ? */
-		      {
-			devc->chip_name = "AD1845";
-			devc->model = MD_1845;
-		      }
-		    else if (interwave)
+		    if (interwave)
 		      {
 			devc->model = MD_IWAVE;
 			devc->chip_name = "IWave";
 		      }
+		    else if (ad_read (devc, 23) != tmp)		/* AD1845 ? */
+		      {
+			devc->chip_name = "AD1845";
+			devc->model = MD_1845;
+		      }
 
 		    ad_write (devc, 23, tmp);	/* Restore */
 		  }
 		  break;
 
 		default:	/* Assume CS4231 or OPTi 82C930 */
-		  DDB (printk ("ad1848: I25 = %02x\n", ad_read (devc, 25)));
+		  DDB (printk ("ad1848: I25 = %02x/%02x\n",
+			       ad_read (devc, 25),
+			       ad_read (devc, 25) & 0xe7));
 		  if (optiC930)
 		    {
 		      devc->chip_name = "82C930";
@@ -1625,16 +1688,17 @@
 
   ad1848_info    *devc = &dev_info[nr_ad1848_devs];
 
-
+  ad1848_port_info *portc = NULL;
 
   request_region (devc->base, 4, devc->chip_name);
 
   devc->irq = (irq > 0) ? irq : 0;
-  devc->opened = 0;
+  devc->open_mode = 0;
   devc->timer_ticks = 0;
   devc->dma1 = dma_playback;
   devc->dma2 = dma_capture;
   devc->audio_flags = DMA_AUTOMODE;
+  devc->playback_dev = devc->record_dev = 0;
 
   if (name != NULL && name[0] != 0)
     sprintf (dev_name,
@@ -1670,6 +1734,14 @@
       return;
     }
 
+
+  portc = (ad1848_port_info *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (ad1848_port_info)));
+  sound_mem_sizes[sound_nblocks] = sizeof (ad1848_port_info);
+  if (sound_nblocks < 1024)
+    sound_nblocks++;;
+  audio_devs[my_dev]->portc = portc;
+  memset ((char *) portc, 0, sizeof (*portc));
+
   nr_ad1848_devs++;
 
   ad1848_init_hw (devc);
@@ -1712,7 +1784,7 @@
   else if (irq < 0)
     irq2dev[-irq] = devc->dev_no = my_dev;
 
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
   if (devc->model != MD_1848 &&
       devc->model != MD_C930 && devc->irq_ok)
     ad1848_tmr_install (my_dev);
@@ -1736,6 +1808,7 @@
     {
       audio_devs[my_dev]->mixer_dev = num_mixers - 1;
     }
+
 }
 
 void
@@ -1813,10 +1886,10 @@
 	  if (irq > 0)
 	    snd_release_irq (devc->irq);
 
-	  sound_free_dma (audio_devs[dev]->dmachan1);
+	  sound_free_dma (audio_devs[dev]->dmap_out->dma);
 
-	  if (audio_devs[dev]->dmachan2 != audio_devs[dev]->dmachan1)
-	    sound_free_dma (audio_devs[dev]->dmachan2);
+	  if (audio_devs[dev]->dmap_in->dma != audio_devs[dev]->dmap_out->dma)
+	    sound_free_dma (audio_devs[dev]->dmap_in->dma);
 	}
     }
   else
@@ -1832,6 +1905,7 @@
   int             dev;
   int             alt_stat = 0xff;
   unsigned char   c930_stat = 0;
+  int             cnt = 0;
 
   if (irq < 0 || irq > 15)
     {
@@ -1864,6 +1938,8 @@
 
   if (status == 0x80)
     printk ("ad1848_interrupt: Why?\n");
+  if (devc->model == MD_1848)
+    outb ((0), io_Status (devc));	/* Clear interrupt status */
 
   if (status & 0x01)
     {
@@ -1888,74 +1964,52 @@
       else if (devc->model != MD_1848)
 	alt_stat = ad_read (devc, 24);
 
-      if (devc->opened && devc->audio_mode & PCM_ENABLE_INPUT && alt_stat & 0x20)
+      /* Acknowledge the intr before proceeding */
+      if (devc->model == MD_C930)
+	{			/* 82C930 has interrupt status register in MAD16 register MC11 */
+	  unsigned long   flags;
+
+	  save_flags (flags);
+	  cli ();
+
+	  outb ((11), 0xe0e);
+	  outb ((~c930_stat), 0xe0f);
+	  restore_flags (flags);
+	}
+      else if (devc->model != MD_1848)
+	ad_write (devc, 24, ad_read (devc, 24) & ~alt_stat);	/* Selective ack */
+
+      if (devc->open_mode & OPEN_READ && devc->audio_mode & PCM_ENABLE_INPUT && alt_stat & 0x20)
 	{
-	  DMAbuf_inputintr (dev);
+	  DMAbuf_inputintr (devc->record_dev);
 	}
 
-      if (devc->opened && devc->audio_mode & PCM_ENABLE_OUTPUT &&
+      if (devc->open_mode & OPEN_WRITE && devc->audio_mode & PCM_ENABLE_OUTPUT &&
 	  alt_stat & 0x10)
 	{
-	  DMAbuf_outputintr (dev, 1);
+	  DMAbuf_outputintr (devc->playback_dev, 1);
 	}
 
       if (devc->model != MD_1848 && alt_stat & 0x40)	/* Timer interrupt */
 	{
 	  devc->timer_ticks++;
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 	  if (timer_installed == dev && devc->timer_running)
 	    sound_timer_interrupt ();
 #endif
 	}
     }
 
-  if (devc->model == MD_C930)
-    {				/* 82C930 has interrupt status register in MAD16 register MC11 */
-      unsigned long   flags;
-
-      save_flags (flags);
-      cli ();
-
-      outb ((11), 0xe0e);
-      outb ((~c930_stat), 0xe0f);
-      restore_flags (flags);
-    }
-  else if (devc->model != MD_1848)
-    ad_write (devc, 24, ad_read (devc, 24) & ~alt_stat);	/* Selective ack */
-  else
-    outb ((0), io_Status (devc));	/* Clear interrupt status */
-
 /*
  * Sometimes playback or capture interrupts occur while a timer interrupt
  * is being handled. The interrupt will not be retriggered if we don't
  * handle it now. Check if an interrupt is still pending and restart
  * the handler in this case.
  */
-  if (inb (io_Status (devc)) & 0x01)
-    goto interrupt_again;
-}
-
-/*
- * Some extra code for the MS Sound System
- */
-
-void
-check_opl3 (int base, struct address_info *hw_config)
-{
-
-#ifdef CONFIG_YM3812
-  if (check_region (base, 4))
+  if (inb (io_Status (devc)) & 0x01 && cnt++ < 4)
     {
-      printk ("\n\nopl3.c: I/O port %x already in use\n\n", base);
-      return;
+      goto interrupt_again;
     }
-
-  if (!opl3_detect (base, hw_config->osp))
-    return;
-
-  opl3_init (base, hw_config->osp);
-  request_region (base, 4, "OPL3/OPL2");
-#endif
 }
 
 #ifdef DESKPROXL
@@ -2217,11 +2271,12 @@
       int             ret;
 
       DDB (printk ("No MSS signature detected on port 0x%x (0x%x)\n",
-		   hw_config->io_base, inb (hw_config->io_base + 3)));
+		   hw_config->io_base, (int) inb (hw_config->io_base + 3)));
       DDB (printk ("Trying to detect codec anyway but IRQ/DMA may not work\n"));
       if (!(ret = ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp)))
 	return 0;
 
+      hw_config->card_subtype = 1;
       return 1;
     }
 
@@ -2276,7 +2331,6 @@
   int             dma = hw_config->dma;
   int             dma2 = hw_config->dma2;
 
-
   if (hw_config->card_subtype == 1)	/* Has no IRQ/DMA registers */
     {
       ad1848_init ("MS Sound System", hw_config->io_base + 4,
@@ -2350,37 +2404,7 @@
   release_region (hw_config->io_base, 4);
 }
 
-/*
- * WSS compatible PnP codec support
- */
-
-int
-probe_pnp_ad1848 (struct address_info *hw_config)
-{
-  return ad1848_detect (hw_config->io_base, NULL, hw_config->osp);
-}
-
-void
-attach_pnp_ad1848 (struct address_info *hw_config)
-{
-
-  ad1848_init (hw_config->name, hw_config->io_base,
-	       hw_config->irq,
-	       hw_config->dma,
-	       hw_config->dma2, 0, hw_config->osp);
-}
-
-void
-unload_pnp_ad1848 (struct address_info *hw_config)
-{
-  ad1848_unload (hw_config->io_base,
-		 hw_config->irq,
-		 hw_config->dma,
-		 hw_config->dma2, 0);
-  release_region (hw_config->io_base, 4);
-}
-
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 /*
  * Timer stuff (for /dev/music).
  */
@@ -2477,6 +2501,7 @@
 static struct sound_lowlev_timer ad1848_tmr =
 {
   0,
+  2,
   ad1848_tmr_start,
   ad1848_tmr_disable,
   ad1848_tmr_restart
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/ad1848_mixer.h linux/drivers/sound/ad1848_mixer.h
--- v2.1.27/linux/drivers/sound/ad1848_mixer.h	Fri Nov 15 00:14:52 1996
+++ linux/drivers/sound/ad1848_mixer.h	Wed Feb 26 02:34:44 1997
@@ -5,7 +5,7 @@
  */
 
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -78,7 +78,7 @@
 #define MIX_ENT(name, reg_l, pola_l, pos_l, len_l, reg_r, pola_r, pos_r, len_r)	\
 	{{reg_l, pola_l, pos_l, len_l}, {reg_r, pola_r, pos_r, len_r}}
 
-mixer_ents ad1848_mix_devices[32] = {
+static mixer_ents ad1848_mix_devices[32] = {
 MIX_ENT(SOUND_MIXER_VOLUME,	27, 1, 0, 4,	29, 1, 0, 4),
 MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0),
@@ -98,7 +98,7 @@
 MIX_ENT(SOUND_MIXER_LINE3,	18, 1, 0, 5,	19, 1, 0, 5)
 };
 
-mixer_ents iwave_mix_devices[32] = {
+static mixer_ents iwave_mix_devices[32] = {
 MIX_ENT(SOUND_MIXER_VOLUME,	25, 1, 0, 5,	27, 1, 0, 5),
 MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0),
@@ -108,7 +108,7 @@
 MIX_ENT(SOUND_MIXER_LINE,	18, 1, 0, 5,	19, 1, 0, 5),
 MIX_ENT(SOUND_MIXER_MIC,	 0, 0, 5, 1,	 1, 0, 5, 1),
 MIX_ENT(SOUND_MIXER_CD,	 	 2, 1, 0, 5,	 3, 1, 0, 5),
-MIX_ENT(SOUND_MIXER_IMIX,	13, 1, 2, 6,	 0, 0, 0, 0),
+MIX_ENT(SOUND_MIXER_IMIX,	16, 1, 0, 5,	17, 1, 0, 5),
 MIX_ENT(SOUND_MIXER_ALTPCM,	 0, 0, 0, 0,	 0, 0, 0, 0),
 MIX_ENT(SOUND_MIXER_RECLEV,	 0, 0, 0, 0,	 0, 0, 0, 0),
 MIX_ENT(SOUND_MIXER_IGAIN,	 0, 0, 0, 4,	 1, 0, 0, 4),
@@ -123,7 +123,7 @@
  * VOLUME, SYNTH, LINE, CD are not enabled above.
  * MIC is level of mic monitoring direct to output. Same for CD, LINE, etc.
  */
-mixer_ents c930_mix_devices[32] = {
+static mixer_ents c930_mix_devices[32] = {
 MIX_ENT(SOUND_MIXER_VOLUME,	22, 1, 0, 5,	23, 1, 0, 5),
 MIX_ENT(SOUND_MIXER_BASS,	 0, 0, 0, 0,	 0, 0, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,	 0, 0, 0, 0,	 0, 0, 0, 0),
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/adlib_card.c linux/drivers/sound/adlib_card.c
--- v2.1.27/linux/drivers/sound/adlib_card.c	Fri Nov 15 00:15:08 1996
+++ linux/drivers/sound/adlib_card.c	Wed Feb 26 02:35:04 1997
@@ -5,7 +5,7 @@
  */
 
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/aedsp16.c linux/drivers/sound/aedsp16.c
--- v2.1.27/linux/drivers/sound/aedsp16.c	Fri Nov 15 00:15:10 1996
+++ linux/drivers/sound/aedsp16.c	Wed Dec 31 16:00:00 1969
@@ -1,869 +0,0 @@
-/*
-   sound/aedsp16.c
-
-   Audio Excel DSP 16 software configuration routines
-
-   Copyright (C) 1995  Riccardo Facchetti (riccardo@cdc8g5.cdc.polimi.it)
-
-   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 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.
- */
-/*
- * Include the main voxware header file. It include all the os/voxware/etc
- * headers needed by this source.
- */
-#include <linux/config.h>
-#include "sound_config.h"
-
-#ifndef AEDSP16_BASE
-#undef CONFIG_AEDSP16
-#endif
-
-#if defined(CONFIG_AEDSP16)
-/*
-
-   READ THIS
-
-   This module is intended for Audio Excel DSP 16 Sound Card.
-
-   Audio Excel DSP 16 is an SB pro II, Microsoft Sound System
-   and MPU-401 compatible card.
-   It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq),
-   so before this module, the only way to configure the DSP under linux was
-   boot the MS-BAU loading the sound.sys device driver (this driver soft-
-   configure the sound board hardware by massaging someone of its registers),
-   and then ctrl-alt-del to boot linux with the DSP configured by the DOG
-   driver.
-
-   This module works configuring your Audio Excel DSP 16's
-   irq, dma and mpu-401-irq. The voxware probe routines rely on the
-   fact that if the hardware is there, they can detect it. The problem
-   with AEDSP16 is that no hardware can be found by the probe routines
-   if the sound card is not well configured. Sometimes the kernel probe
-   routines can find an SBPRO even when the card is not configured (this
-   is the standard setup of the card), but the SBPRO emulation don't work
-   well if the card is not properly initialized. For this reason
-
-   InitAEDSP16_...()
-
-   routines are called before the voxware probe routines try to detect the
-   hardware.
-
-   NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS)
-
-   The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS;
-   the voxware sound driver can be configured for SBPRO and MSS cards
-   at the same time, but the AEDSP16 can't be two cards!!
-   When we configure it, we have to choose the SBPRO or the MSS emulation
-   for AEDSP16. We also can install a *REAL* card of the other type
-   (see [1], not tested but I can't see any reason for it to fail).
-
-   NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO
-   please let me know if it works.
-
-   The MPU-401 support can be compiled in together with one of the other
-   two operating modes.
-
-   The board configuration calls, are in the probe_...() routines because
-   we have to configure the board before probing it for a particular
-   hardware. After card configuration, we can probe the hardware.
-
-   NOTE: This is something like plug-and-play: we have only to plug
-   the AEDSP16 board in the socket, and then configure and compile
-   a kernel that uses the AEDSP16 software configuration capability.
-   No jumper setting is needed!
-
-   For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3
-   you have just to make config the voxware package, configuring
-   the SBPro sound card with that parameters, then when configure
-   asks if you have an AEDSP16, answer yes. That's it.
-   Compile the kernel and run it.
-
-   NOTE: This means that you can choose irq and dma, but not the
-   I/O addresses. To change I/O addresses you have to set them
-   with jumpers.
-
-   NOTE: InitAEDSP16_...() routines get as parameter the hw_config,
-   the hardware configuration of the - to be configured - board.
-   The InitAEDSP16() routine, configure the board following our
-   wishes, that are in the hw_config structure.
-
-   You can change the irq/dma/mirq settings WITHOUT THE NEED to open
-   your computer and massage the jumpers (there are no irq/dma/mirq
-   jumpers to be configured anyway, only I/O port ones have to be
-   configured with jumpers)
-
-   For some ununderstandable reason, the card default of irq 7, dma 1,
-   don't work for me. Seems to be an IRQ or DMA conflict. Under heavy
-   HDD work, the kernel start to erupt out a lot of messages like:
-
-   'Sound: DMA timed out - IRQ/DRQ config error?'
-
-   For what I can say, I have NOT any conflict at irq 7 (under linux I'm
-   using the lp polling driver), and dma line 1 is unused as stated by
-   /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so
-   I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows!
-   Anyway a setting of irq 10, dma 3 works really fine.
-
-   NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know
-   the emulation mode, all the installed hardware and the hardware
-   configuration (irq and dma settings of all the hardware).
-
-   This init module should work with SBPRO+MSS, when one of the two is
-   the AEDSP16 emulation and the other the real card. (see [1])
-   For example:
-
-   AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other
-   AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other
-
-   MPU401 should work. (see [1])
-
-   [1] Not tested by me for lack of hardware.
-
-   TODO, WISHES AND TECH
-
-   May be there's lot of redundant delays, but for now I want to leave it
-   this way.
-
-   Should be interesting eventually write down a new ioctl for the
-   AEDSP16, to let the suser() change the irq/dma/mirq on the fly.
-   The thing is not trivial.
-   In the real world, there's no need to have such an ioctl because
-   when we configure the kernel for compile, we can choose the config
-   parameters. If we change our mind, we can easily re-config the kernel
-   and re-compile.
-   Why let the suser() change the config parameters on the fly ?
-   If anyone have a reasonable answer to this question, I will write down
-   the code to do it.
-
-   More integration with voxware, using voxware low level routines to
-   read-write DSP is not possible because you may want to have MSS
-   support and in that case we can not rely on the functions included
-   in sb_dsp.c to control 0x2yy I/O ports. I will continue to use my
-   own I/O functions.
-
-   - About I/O ports allocation -
-
-   The request_region should be done at device probe in every sound card
-   module. This module is not the best site for requesting regions.
-   When the request_region code will be added to the main modules such as
-   sb, adlib, gus, ad1848, etc, the requesting code in this module should
-   go away.
-
-   I think the request regions should be done this way:
-
-   if (check_region(...))
-   return ERR; // I/O region already reserved
-   device_probe(...);
-   device_attach(...);
-   request_region(...); // reserve only when we are sure all is okay
-
-   Request the 2x0h region in any case if we are using this card.
-
-   NOTE: the "(SBPro)" string with which we are requesting the AEDSP16 region
-   (see code) does not mean necessarily that we are emulating SBPro.
-   It mean that the region is the SBPro I/O ports region. We use this
-   region to access the control registers of the card, and if emulating
-   SBPro, I/O SBPro registers too. If we are emulating MSS, the SBPro
-   registers are not used, in no way, to emulate an SBPro: they are
-   used only for configuration purposes.
-
-   Someone pointed out that should be possible use both the SBPRO and MSS
-   modes because the sound card have all the two chipsets, supposing that
-   the card is really two cards. I have tried something to have the two
-   modes work together, but, for some reason unknown to me, without success.
-
-   I think all the soft-config only cards have an init sequence similar to
-   this. If you have a card that is not an AEDSP16, you can try to start
-   with this module changing it (mainly in the CMD? I think) to fit your
-   needs.
-
-   Started Fri Mar 17 16:13:18 MET 1995
-
-   v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c)
-   - Initial code.
-   v0.2 (ALPHA)
-   - Cleanups.
-   - Integrated with Linux voxware v 2.90-2 kernel sound driver.
-   - Sound Blaster Pro mode configuration.
-   - Microsoft Sound System mode configuration.
-   - MPU-401 mode configuration.
-   v0.3 (ALPHA)
-   - Cleanups.
-   - Rearranged the code to let InitAEDSP16 be more general.
-   - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h
-   inclusion too. We rely on os.h
-   - Used the  to get a variable
-   len string (we are not sure about the len of Copyright string).
-   This works with any SB and compatible.
-   - Added the code to request_region at device init (should go in
-   the main body of voxware).
-   v0.4 (BETA)
-   - Better configure.c patch for AEDSP16 configuration (better
-   logic of inclusion of AEDSP16 support)
-   - Modified the conditional compilation to better support more than
-   one sound card of the emulated type (read the NOTES above)
-   - Moved the sb init routine from the attach to the very first
-   probe in sb_card.c
-   - Rearrangements and cleanups
-   - Wiped out some unnecessary code and variables: this is kernel
-   code so it is better save some TEXT and DATA
-   - Fixed the request_region code. We must allocate the AEDSP16 (SBPro)
-   I/O ports in any case because they are used to access the DSP
-   configuration registers and we can not allow anyone to get them.
-   v0.5
-   - cleanups on comments
-   - prep for diffs against v3.0-proto-950402
-   v0.6
-   - removed the request_region()s when compiling the MODULE sound.o
-   because we are not allowed (by the actual voxware structure) to
-   release_region()
-
- */
-
-
-#define VERSION "0.6"		/* Version of Audio Excel DSP 16 driver */
-
-#undef AEDSP16_DEBUG		/* Define this to enable debug code     */
-/* Actually no debug code is activated  */
-
-/*
- * Hardware related defaults
- */
-#define IRQ  7			/* 5 7(default) 9 10 11                 */
-#define MIRQ 0			/* 5 7 9 10 0(default), 0 means disable */
-#define DMA  1			/* 0 1(default) 3                       */
-
-/*
- * Commands of AEDSP16's DSP (SBPRO+special).
- * For now they are CMDn, in the future they may change.
- */
-#define CMD1 0xe3		/* Get DSP Copyright                    */
-#define CMD2 0xe1		/* Get DSP Version                      */
-#define CMD3 0x88		/*                                      */
-#define CMD4 0x5c		/*                                      */
-#define CMD5 0x50		/* Set M&I&DRQ mask (the real config)   */
-#define CMD6 0x8c		/* Enable Microsoft Sound System mode   */
-
-/*
- * Offsets of AEDSP16 DSP I/O ports. The offset is added to portbase
- * to have the actual I/O port.
- * Register permissions are:
- * (wo) == Write Only
- * (ro) == Read  Only
- * (w-) == Write
- * (r-) == Read
- */
-#define DSP_RESET    0x06	/* offset of DSP RESET             (wo) */
-#define DSP_READ     0x0a	/* offset of DSP READ              (ro) */
-#define DSP_WRITE    0x0c	/* offset of DSP WRITE             (w-) */
-#define DSP_COMMAND  0x0c	/* offset of DSP COMMAND           (w-) */
-#define DSP_STATUS   0x0c	/* offset of DSP STATUS            (r-) */
-#define DSP_DATAVAIL 0x0e	/* offset of DSP DATA AVAILABLE    (ro) */
-
-
-#define RETRY           10	/* Various retry values on I/O opera-   */
-#define STATUSRETRY   1000	/* tions. Sometimes we have to          */
-#define HARDRETRY   500000	/* wait for previous cmd to complete    */
-
-/*
- * Size of character arrays that store name and version of sound card
- */
-#define CARDNAMELEN 15		/* Size of the card's name in chars     */
-#define CARDVERLEN  2		/* Size of the card's version in chars  */
-
-/*
- * Bit mapped flags for calling InitAEDSP16(), and saving the current
- * emulation mode.
- */
-#define INIT_NONE   (0   )
-#define INIT_SBPRO  (1<<0)
-#define INIT_MSS    (1<<1)
-#define INIT_MPU401 (1<<2)
-#define RESET_DSP16 (1<<3)
-
-/* Base HW Port for Audio Card          */
-static int      portbase = AEDSP16_BASE;
-static int      irq = IRQ;	/* irq for DSP I/O                      */
-static int      mirq = MIRQ;	/* irq for MPU-401 I/O                  */
-static int      dma = DMA;	/* dma for DSP I/O                      */
-
-/* Init status of the card              */
-static int      ae_init = INIT_NONE;	/* (bitmapped variable)                 */
-static int      oredparams = 0;	/* Will contain or'ed values of params  */
-static int      gc = 0;		/* generic counter (utility counter)    */
-struct orVals
-  {				/* Contain the values to be or'ed       */
-    int             val;	/* irq|mirq|dma                         */
-    int             or;		/* oredparams |= TheStruct.or           */
-  };
-
-/*
- * Magic values that the DSP will eat when configuring irq/mirq/dma
- */
-/* DSP IRQ conversion array             */
-static struct orVals orIRQ[] =
-{
-  {0x05, 0x28},
-  {0x07, 0x08},
-  {0x09, 0x10},
-  {0x0a, 0x18},
-  {0x0b, 0x20},
-  {0x00, 0x00}
-};
-
-/* MPU-401 IRQ conversion array         */
-static struct orVals orMIRQ[] =
-{
-  {0x05, 0x04},
-  {0x07, 0x44},
-  {0x09, 0x84},
-  {0x0a, 0xc4},
-  {0x00, 0x00}
-};
-
-/* DMA Channels conversion array        */
-static struct orVals orDMA[] =
-{
-  {0x00, 0x01},
-  {0x01, 0x02},
-  {0x03, 0x03},
-  {0x00, 0x00}
-};
-
-/*
- * Buffers to store audio card informations
- */
-static char     AudioExcelName[CARDNAMELEN + 1];
-static char     AudioExcelVersion[CARDVERLEN + 1];
-
-static void
-tenmillisec (void)
-{
-
-  for (gc = 0; gc < 1000; gc++)
-    tenmicrosec ();
-}
-
-static int
-WaitForDataAvail (int port)
-{
-  int             loop = STATUSRETRY;
-  unsigned char   ret = 0;
-
-  do
-    {
-      ret = inb (port + DSP_DATAVAIL);
-      /*
-         * Wait for data available (bit 7 of ret == 1)
-       */
-    }
-  while (!(ret & 0x80) && loop--);
-
-  if (ret & 0x80)
-    return 0;
-
-  return -1;
-}
-
-static int
-ReadData (int port)
-{
-  if (WaitForDataAvail (port))
-    return -1;
-  return inb (port + DSP_READ);
-}
-
-static int
-CheckDSPOkay (int port)
-{
-  return ((ReadData (port) == 0xaa) ? 0 : -1);
-}
-
-static int
-ResetBoard (int port)
-{
-  /*
-     * Reset DSP
-   */
-  outb ((1), (port + DSP_RESET));
-  tenmicrosec ();
-  outb ((0), (port + DSP_RESET));
-  tenmicrosec ();
-  tenmicrosec ();
-  return CheckDSPOkay (port);
-}
-
-static int
-WriteDSPCommand (int port, int cmd)
-{
-  unsigned char   ret;
-  int             loop = HARDRETRY;
-
-  do
-    {
-      ret = inb (port + DSP_STATUS);
-      /*
-         * DSP ready to receive data if bit 7 of ret == 0
-       */
-      if (!(ret & 0x80))
-	{
-	  outb ((cmd), port + DSP_COMMAND);
-	  return 0;
-	}
-    }
-  while (loop--);
-
-  printk ("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd);
-  return -1;
-}
-
-int
-InitMSS (int port)
-{
-
-  tenmillisec ();
-
-  if (WriteDSPCommand (port, CMD6))
-    {
-      printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD6);
-      return -1;
-    }
-
-  tenmillisec ();
-
-  return 0;
-}
-
-static int
-SetUpBoard (int port)
-{
-  int             loop = RETRY;
-
-  do
-    {
-      if (WriteDSPCommand (portbase, CMD3))
-	{
-	  printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD3);
-	  return -1;
-	}
-
-      tenmillisec ();
-
-    }
-  while (WaitForDataAvail (port) && loop--);
-
-#if defined(THIS_SHOULD_GO_AWAY)
-  if (CheckDSPOkay (port))
-    {
-      printk ("[AEDSP16]     CheckDSPOkay: failed\n");
-      return -1;
-    }
-#else
-  if (ReadData (port) == -1)
-    {
-      printk ("[AEDSP16] ReadData after CMD 0x%x: failed\n", CMD3);
-      return -1;
-    }
-#endif
-
-  if (WriteDSPCommand (portbase, CMD4))
-    {
-      printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD4);
-      return -1;
-    }
-
-  if (WriteDSPCommand (portbase, CMD5))
-    {
-      printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD5);
-      return -1;
-    }
-
-  if (WriteDSPCommand (portbase, oredparams))
-    {
-      printk ("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n");
-      return -1;
-    }
-  return 0;
-}
-
-static int
-GetCardVersion (int port)
-{
-  int             len = 0;
-  int             ret;
-  int             ver[3];
-
-  do
-    {
-      if ((ret = ReadData (port)) == -1)
-	return -1;
-      /*
-         * We already know how many int are stored (2), so we know when the
-         * string is finished.
-       */
-      ver[len++] = ret;
-    }
-  while (len < CARDVERLEN);
-  sprintf (AudioExcelVersion, "%d.%d", ver[0], ver[1]);
-  return 0;
-}
-
-static int
-GetCardName (int port)
-{
-  int             len = 0;
-  int             ret;
-
-  do
-    {
-      if ((ret = ReadData (port)) == -1)
-	/*
-	   * If no more data available, return to the caller, no error if len>0.
-	   * We have no other way to know when the string is finished.
-	 */
-	return (len ? 0 : -1);
-
-      AudioExcelName[len++] = ret;
-
-    }
-  while (len < CARDNAMELEN);
-  return 0;
-}
-
-static void
-InitializeHardParams (void)
-{
-
-  memset (AudioExcelName, 0, CARDNAMELEN + 1);
-  memset (AudioExcelVersion, 0, CARDVERLEN + 1);
-
-  for (gc = 0; orIRQ[gc].or; gc++)
-    if (orIRQ[gc].val == irq)
-      oredparams |= orIRQ[gc].or;
-
-  for (gc = 0; orMIRQ[gc].or; gc++)
-    if (orMIRQ[gc].or == mirq)
-      oredparams |= orMIRQ[gc].or;
-
-  for (gc = 0; orDMA[gc].or; gc++)
-    if (orDMA[gc].val == dma)
-      oredparams |= orDMA[gc].or;
-}
-
-static int
-InitAEDSP16 (int which)
-{
-  static char    *InitName = NULL;
-
-  InitializeHardParams ();
-
-  if (ResetBoard (portbase))
-    {
-      printk ("[AEDSP16] ResetBoard: failed!\n");
-      return -1;
-    }
-
-#if defined(THIS_SHOULD_GO_AWAY)
-  if (CheckDSPOkay (portbase))
-    {
-      printk ("[AEDSP16] CheckDSPOkay: failed!\n");
-      return -1;
-    }
-#endif
-
-  if (WriteDSPCommand (portbase, CMD1))
-    {
-      printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD1);
-      return -1;
-    }
-
-  if (GetCardName (portbase))
-    {
-      printk ("[AEDSP16] GetCardName: failed!\n");
-      return -1;
-    }
-
-  /*
-     * My AEDSP16 card return SC-6000 in AudioExcelName, so
-     * if we have something different, we have to be warned.
-   */
-  if (strcmp ("SC-6000", AudioExcelName))
-    printk ("[AEDSP16] Warning: non SC-6000 audio card!\n");
-
-  if (WriteDSPCommand (portbase, CMD2))
-    {
-      printk ("[AEDSP16] CMD 0x%x: failed!\n", CMD2);
-      return -1;
-    }
-
-  if (GetCardVersion (portbase))
-    {
-      printk ("[AEDSP16] GetCardVersion: failed!\n");
-      return -1;
-    }
-
-  if (SetUpBoard (portbase))
-    {
-      printk ("[AEDSP16] SetUpBoard: failed!\n");
-      return -1;
-    }
-
-  if (which == INIT_MSS)
-    {
-      if (InitMSS (portbase))
-	{
-	  printk ("[AEDSP16] Can't initialize Microsoft Sound System mode.\n");
-	  return -1;
-	}
-    }
-
-  /*
-     * If we are resetting, do not print any message because we may be
-     * in playing and we do not want lost too much time.
-   */
-  if (!(which & RESET_DSP16))
-    {
-      if (which & INIT_MPU401)
-	InitName = "MPU401";
-      else if (which & INIT_SBPRO)
-	InitName = "SBPro";
-      else if (which & INIT_MSS)
-	InitName = "MSS";
-      else
-	InitName = "None";
-
-      printk ("Audio Excel DSP 16 init v%s (%s %s) [%s]\n",
-	      VERSION, AudioExcelName,
-	      AudioExcelVersion, InitName);
-    }
-
-  tenmillisec ();
-
-  return 0;
-}
-
-#if defined(AEDSP16_SBPRO)
-
-int
-InitAEDSP16_SBPRO (struct address_info *hw_config)
-{
-  /*
-     * If the card is already init'ed MSS, we can not init it to SBPRO too
-     * because the board can not emulate simultaneously MSS and SBPRO.
-   */
-  if (ae_init & INIT_MSS)
-    return -1;
-  if (ae_init & INIT_SBPRO)
-    return 0;
-
-  /*
-     * For now we will leave this
-     * code included only when INCLUDE_AEDSP16 is configured in, but it should
-     * be better include it every time.
-   */
-  if (!(ae_init & INIT_MPU401))
-    {
-      if (check_region (hw_config->io_base, 0x0f))
-	{
-	  printk ("AEDSP16/SBPRO I/O port region is already in use.\n");
-	  return -1;
-	}
-    }
-
-  /*
-     * Set up the internal hardware parameters, to let the driver reach
-     * the Sound Card.
-   */
-  portbase = hw_config->io_base;
-  irq = hw_config->irq;
-  dma = hw_config->dma;
-
-  if (InitAEDSP16 (INIT_SBPRO))
-    return -1;
-
-#if !defined(MODULE)
-  /*
-     * If we are compiling sound.o (MODULAR version) we can not
-     * request any region because there is not a uninit routine that
-     * can allow me to release the requested region.
-   */
-  if (!(ae_init & INIT_MPU401))
-    request_region (hw_config->io_base, 0x0f, "aedsp16 (sbpro)");
-#endif
-
-  ae_init |= INIT_SBPRO;
-  return 0;
-}
-
-#endif /* AEDSP16_SBPRO */
-
-#if defined(AEDSP16_MSS)
-
-int
-InitAEDSP16_MSS (struct address_info *hw_config)
-{
-  /*
-     * If the card is already init'ed SBPRO, we can not init it to MSS too
-     * because the board can not emulate simultaneously MSS and SBPRO.
-   */
-  if (ae_init & INIT_SBPRO)
-    return -1;
-  if (ae_init & INIT_MSS)
-    return 0;
-
-  /*
-     * For now we will leave this
-     * code included only when INCLUDE_AEDSP16 is configured in, but it should
-     * be better include it every time.
-   */
-  if (check_region (hw_config->io_base, 0x08))
-    {
-      printk ("MSS I/O port region is already in use.\n");
-      return -1;
-    }
-
-  /*
-     * We must allocate the AEDSP16 region too because these are the I/O ports
-     * to access card's control registers.
-   */
-  if (!(ae_init & INIT_MPU401))
-    {
-      if (check_region (AEDSP16_BASE, 0x0f))
-	{
-	  printk ("AEDSP16 I/O port region is already in use.\n");
-	  return -1;
-	}
-    }
-
-
-  /*
-     * If we are configuring the card for MSS, the portbase for card
-     * configuration is the default one (0x220 unless you have changed the
-     * factory default with board switches), so no need to modify the
-     * portbase variable.
-     * The default is AEDSP16_BASE, that is the right value.
-   */
-  irq = hw_config->irq;
-  dma = hw_config->dma;
-
-  if (InitAEDSP16 (INIT_MSS))
-    return -1;
-
-#if !defined(MODULE)
-  /*
-     * If we are compiling sound.o (MODULAR version) we can not
-     * request any region because there is not a uninit routine that
-     * can allow me to release the requested region. So when unloading
-     * and then reloading it, we are going to have some nice Oops!
-   */
-  request_region (hw_config->io_base, 0x08, "aedsp16 (mss)");
-#endif
-
-  if (!(ae_init & INIT_MPU401))
-    request_region (AEDSP16_BASE, 0x0f, "aedsp16 (sbpro)");
-
-  ae_init |= INIT_MSS;
-  return 0;
-}
-
-#endif /* AEDSP16_MSS */
-
-#if defined(AEDSP16_MPU401)
-
-int
-InitAEDSP16_MPU401 (struct address_info *hw_config)
-{
-  if (ae_init & INIT_MPU401)
-    return 0;
-
-  /*
-     * For now we will leave this
-     * code included only when INCLUDE_AEDSP16 is configured in, but it should
-     * be better include it every time.
-   */
-  if (check_region (hw_config->io_base, 0x02))
-    {
-      printk ("SB I/O port region is already in use.\n");
-      return -1;
-    }
-
-  /*
-     * We must allocate the AEDSP16 region too because these are the I/O ports
-     * to access card's control registers.
-   */
-  if (!(ae_init & (INIT_MSS | INIT_SBPRO)))
-    {
-      if (check_region (AEDSP16_BASE, 0x0f))
-	{
-	  printk ("AEDSP16 I/O port region is already in use.\n");
-	  return -1;
-	}
-    }
-
-  /*
-     * If mpu401, the irq and dma are not important, do not touch it
-     * because we may use the default if SBPro is not yet configured,
-     * we may use the SBPro ones if configured, and nothing wrong
-     * should happen.
-     *
-     * The mirq default is 0, but once set it to non-0 value, we should
-     * not touch it anymore (unless I write an ioctl to do it, of course).
-   */
-  mirq = hw_config->irq;
-  if (InitAEDSP16 (INIT_MPU401))
-    return -1;
-
-#if !defined(MODULE)
-  /*
-     * If we are compiling sound.o (MODULAR version) we can not
-     * request any region because there is not a uninit routine that
-     * can allow me to release the requested region.
-   */
-  request_region (hw_config->io_base, 0x02, "aedsp16 (mpu401)");
-#endif
-
-  if (!(ae_init & (INIT_MSS | INIT_SBPRO)))
-    request_region (AEDSP16_BASE, 0x0f, "aedsp16 (sbpro)");
-
-  ae_init |= INIT_MPU401;
-  return 0;
-}
-
-#endif /* AEDSP16_MPU401 */
-
-#if 0				/* Leave it out for now. We are not using this portion of code. */
-
-/*
- * Entry point for a reset function.
- * May be I will write the infamous ioctl :)
- */
-int
-ResetAEDSP16 (void)
-{
-#if defined(AEDSP16_DEBUG)
-  printk ("[AEDSP16] ResetAEDSP16 called.\n");
-#endif
-  return InitAEDSP16 (RESET_DSP16);
-}
-
-#endif /* 0 */
-
-#endif /* CONFIG_AEDSP16 */
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/audio.c linux/drivers/sound/audio.c
--- v2.1.27/linux/drivers/sound/audio.c	Tue Jan 28 00:02:45 1997
+++ linux/drivers/sound/audio.c	Wed Feb 26 02:35:05 1997
@@ -5,14 +5,14 @@
  */
 
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
 #include <linux/config.h>
-#include <linux/poll.h>
+
 
 #include "sound_config.h"
 
@@ -24,6 +24,9 @@
 #define ON		1
 #define OFF		0
 
+#define NEUTRAL8	0x80
+#define NEUTRAL16	0x00
+
 static int      audio_mode[MAX_AUDIO_DEV];
 static int      dev_nblock[MAX_AUDIO_DEV];	/* 1 if in nonblocking mode */
 
@@ -95,9 +98,6 @@
 
   local_conversion[dev] = 0;
 
-  if (audio_devs[dev]->d->set_bits (dev, bits) != bits)
-    {
-    }
 
   if (dev_type == SND_DEV_AUDIO)
     {
@@ -113,23 +113,33 @@
   return ret;
 }
 
-void
+static void
 sync_output (int dev)
 {
-  int             buf_no, buf_ptr, buf_size, p, i;
-  char           *dma_buf;
+  int             p, i;
+  int             l;
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-  if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0)
+  if (dmap->fragment_size <= 0)
+    return;
+  dmap->flags |= DMA_POST;
+
+  /* Align the write pointer with fragment boundaries */
+  if ((l = dmap->user_counter % dmap->fragment_size) > 0)
     {
-      int             i, n = buf_size & 3;
+      char           *ptr;
+      int             err, dummylen, len = dmap->fragment_size - l;
 
-      if (n)			/* Not 4 byte aligned */
-	{
-	  for (i = 0; i < n; i++)
-	    dma_buf[buf_ptr++] = dmap->neutral_byte;
-	}
-      DMAbuf_start_output (dev, buf_no, buf_ptr);
+      if ((err = DMAbuf_getwrbuffer (dev, &ptr, &dummylen, 1)) >= 0)
+	if (dummylen >= len && ((long) ptr % dmap->fragment_size) == l)
+	  {
+	    if ((ptr + len) > (dmap->raw_buf + audio_devs[dev]->buffsize))
+	      printk ("audio: Buffer error 1\n");
+	    if (ptr < dmap->raw_buf)
+	      printk ("audio: Buffer error 11\n");
+	    memset (ptr, dmap->neutral_byte, len);
+	    DMAbuf_move_wrpointer (dev, len);
+	  }
     }
 
 /*
@@ -137,16 +147,21 @@
  */
 
   p = dmap->qtail;
+  dmap->flags |= DMA_POST;
 
   for (i = dmap->qlen + 1; i < dmap->nbufs; i++)
     {
       p = (p + 1) % dmap->nbufs;
+      if (((dmap->raw_buf + p * dmap->fragment_size) + dmap->fragment_size) >
+	  (dmap->raw_buf + audio_devs[dev]->buffsize))
+	printk ("audio: Buffer error 2\n");
+
       memset (dmap->raw_buf + p * dmap->fragment_size,
 	      dmap->neutral_byte,
 	      dmap->fragment_size);
     }
 
-  dmap->flags |= DMA_CLEAN;
+  dmap->flags |= DMA_DIRTY;
 }
 
 void
@@ -201,7 +216,7 @@
 int
 audio_write (int dev, struct fileinfo *file, const char *buf, int count)
 {
-  int             c, p, l, buf_no, buf_ptr, buf_size;
+  int             c, p, l, buf_size;
   int             err;
   char           *dma_buf;
 
@@ -210,9 +225,8 @@
   p = 0;
   c = count;
 
-  if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX))
-    {				/* Direction change */
-    }
+  if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+    return -EPERM;
 
   if (audio_devs[dev]->flags & DMA_DUPLEX)
     audio_mode[dev] |= AM_WRITE;
@@ -227,34 +241,33 @@
 
   while (c)
     {
-      if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) < 0)
+      if ((err = DMAbuf_getwrbuffer (dev, &dma_buf, &buf_size, dev_nblock[dev]) < 0))
 	{
-	  if ((buf_no = DMAbuf_getwrbuffer (dev, &dma_buf,
-					    &buf_size,
-					    dev_nblock[dev])) < 0)
-	    {
-	      /* Handle nonblocking mode */
-	      if (dev_nblock[dev] && buf_no == -EAGAIN)
-		return p;	/* No more space. Return # of accepted bytes */
-	      return buf_no;
-	    }
-	  buf_ptr = 0;
+	  /* Handle nonblocking mode */
+	  if (dev_nblock[dev] && err == -EAGAIN)
+	    return p;		/* No more space. Return # of accepted bytes */
+	  return err;
 	}
 
       l = c;
-      if (l > (buf_size - buf_ptr))
-	l = (buf_size - buf_ptr);
-
+      if (l > buf_size)
+	l = buf_size;
 
       if (!audio_devs[dev]->d->copy_user)
-	{			/*
-				 * No device specific copy routine
-				 */
-	  copy_from_user (&dma_buf[buf_ptr], &(buf)[p], l);
+	{
+	  if ((dma_buf + l) >
+	   (audio_devs[dev]->dmap_out->raw_buf + audio_devs[dev]->buffsize))
+	    printk ("audio: Buffer error 3 (%lx,%d), (%lx, %d)\n",
+		    (long) dma_buf, l,
+		    (long) audio_devs[dev]->dmap_out->raw_buf,
+		    (int) audio_devs[dev]->buffsize);
+	  if (dma_buf < audio_devs[dev]->dmap_out->raw_buf)
+	    printk ("audio: Buffer error 13\n");
+	  copy_from_user (dma_buf, &(buf)[p], l);
 	}
       else
 	audio_devs[dev]->d->copy_user (dev,
-				       dma_buf, buf_ptr, buf, p, l);
+				       dma_buf, 0, buf, p, l);
 
       if (local_conversion[dev] & CNV_MU_LAW)
 	{
@@ -262,23 +275,12 @@
 	   * This just allows interrupts while the conversion is running
 	   */
 	  sti ();
-	  translate_bytes (ulaw_dsp, (unsigned char *) &dma_buf[buf_ptr], l);
+	  translate_bytes (ulaw_dsp, (unsigned char *) dma_buf, l);
 	}
 
       c -= l;
       p += l;
-      buf_ptr += l;
-
-      if (buf_ptr >= buf_size)
-	{
-	  if ((err = DMAbuf_start_output (dev, buf_no, buf_ptr)) < 0)
-	    {
-	      return err;
-	    }
-
-	}
-      else
-	DMAbuf_set_count (dev, buf_no, buf_ptr);
+      DMAbuf_move_wrpointer (dev, l);
 
     }
 
@@ -296,6 +298,9 @@
   p = 0;
   c = count;
 
+  if (!(audio_devs[dev]->open_mode & OPEN_READ))
+    return -EPERM;
+
   if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX))
     {
       sync_output (dev);
@@ -352,12 +357,12 @@
 }
 
 int
-audio_ioctl (int dev, struct fileinfo *file,
+audio_ioctl (int dev, struct fileinfo *file_must_not_be_used,
 	     unsigned int cmd, caddr_t arg)
 {
   int             val;
 
-/* printk("audio_ioctl(%x, %x)\n", cmd, arg); */
+  /* printk("audio_ioctl(%x, %x)\n", (int)cmd, (int)arg); */
 
   dev = dev >> 4;
 
@@ -377,6 +382,8 @@
 	if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
 	  return 0;
 
+	if (audio_devs[dev]->dmap_out->fragment_size == 0)
+	  return 0;
 	sync_output (dev);
 	DMAbuf_sync (dev);
 	DMAbuf_reset (dev);
@@ -386,9 +393,11 @@
       case SNDCTL_DSP_POST:
 	if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
 	  return 0;
-	audio_devs[dev]->dmap_out->flags |= DMA_POST;
+	if (audio_devs[dev]->dmap_out->fragment_size == 0)
+	  return 0;
+	audio_devs[dev]->dmap_out->flags |= DMA_POST | DMA_DIRTY;
 	sync_output (dev);
-	DMAbuf_ioctl (dev, SNDCTL_DSP_POST, 0, 1);
+	dma_ioctl (dev, SNDCTL_DSP_POST, (caddr_t) 0);
 	return 0;
 	break;
 
@@ -399,12 +408,12 @@
 	break;
 
       case SNDCTL_DSP_GETFMTS:
-	return ioctl_out (arg, audio_devs[dev]->format_mask);
+	return (*(int *) arg = audio_devs[dev]->format_mask);
 	break;
 
       case SNDCTL_DSP_SETFMT:
-	get_user (val, (int *) arg);
-	return ioctl_out (arg, set_format (dev, val));
+	val = *(int *) arg;
+	return (*(int *) arg = set_format (dev, val));
 
       case SNDCTL_DSP_GETISPACE:
 	if (!(audio_devs[dev]->open_mode & OPEN_READ))
@@ -415,16 +424,12 @@
 	{
 	  audio_buf_info  info;
 
-	  int             err = DMAbuf_ioctl (dev, cmd, (caddr_t) & info, 1);
+	  int             err = dma_ioctl (dev, cmd, (caddr_t) & info);
 
 	  if (err < 0)
 	    return err;
 
-	  {
-	    char           *fixit = (char *) &info;
-
-	    copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-	  };
+	  memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 	  return 0;
 	}
 
@@ -436,22 +441,13 @@
 
 	{
 	  audio_buf_info  info;
-	  char           *dma_buf;
-	  int             buf_no, buf_ptr, buf_size;
 
-	  int             err = DMAbuf_ioctl (dev, cmd, (caddr_t) & info, 1);
+	  int             err = dma_ioctl (dev, cmd, (caddr_t) & info);
 
 	  if (err < 0)
 	    return err;
 
-	  if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0)
-	    info.bytes -= buf_ptr;
-
-	  {
-	    char           *fixit = (char *) &info;
-
-	    copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-	  };
+	  memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 	  return 0;
 	}
 
@@ -464,7 +460,8 @@
 	{
 	  int             info = 1;	/* Revision level of this ioctl() */
 
-	  if (audio_devs[dev]->flags & DMA_DUPLEX)
+	  if (audio_devs[dev]->flags & DMA_DUPLEX &&
+	      audio_devs[dev]->open_mode == OPEN_READWRITE)
 	    info |= DSP_CAP_DUPLEX;
 
 	  if (audio_devs[dev]->coproc)
@@ -478,59 +475,65 @@
 
 	  info |= DSP_CAP_MMAP;
 
-	  {
-	    char           *fixit = (char *) &info;
-
-	    copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-	  };
+	  memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
 	  return 0;
 	}
 	break;
 
       case SOUND_PCM_WRITE_RATE:
-	get_user (val, (int *) arg);
-	return ioctl_out (arg, audio_devs[dev]->d->set_speed (dev, val));
+	val = *(int *) arg;
+	return (*(int *) arg = audio_devs[dev]->d->set_speed (dev, val));
 
       case SOUND_PCM_READ_RATE:
-	return ioctl_out (arg, audio_devs[dev]->d->set_speed (dev, 0));
+	return (*(int *) arg = audio_devs[dev]->d->set_speed (dev, 0));
 
       case SNDCTL_DSP_STEREO:
 	{
 	  int             n;
 
-	  get_user (n, (int *) arg);
+	  n = *(int *) arg;
 	  if (n > 1)
 	    {
 	      printk ("sound: SNDCTL_DSP_STEREO called with invalid argument %d\n",
 		      n);
-	      return ioctl_out (arg, audio_devs[dev]->d->set_channels (dev, n));
+	      return -EINVAL;
 	    }
 
 	  if (n < 0)
 	    return -EINVAL;
 
-	  return ioctl_out (arg, audio_devs[dev]->d->set_channels (dev, n + 1) - 1);
+	  return (*(int *) arg = audio_devs[dev]->d->set_channels (dev, n + 1) - 1);
 	}
 
       case SOUND_PCM_WRITE_CHANNELS:
-	get_user (val, (int *) arg);
-	return ioctl_out (arg, audio_devs[dev]->d->set_channels (dev, val));
+	val = *(int *) arg;
+	return (*(int *) arg = audio_devs[dev]->d->set_channels (dev, val));
 
       case SOUND_PCM_READ_CHANNELS:
-	return ioctl_out (arg, audio_devs[dev]->d->set_channels (dev, 0));
+	return (*(int *) arg = audio_devs[dev]->d->set_channels (dev, 0));
 
       case SOUND_PCM_READ_BITS:
-	return ioctl_out (arg, audio_devs[dev]->d->set_bits (dev, 0));
+	return (*(int *) arg = audio_devs[dev]->d->set_bits (dev, 0));
 
       case SNDCTL_DSP_SETDUPLEX:
+	if (audio_devs[dev]->open_mode != OPEN_READWRITE)
+	  return -EPERM;
 	if (audio_devs[dev]->flags & DMA_DUPLEX)
 	  return 0;
 	else
 	  return -EIO;
 	break;
 
+      case SNDCTL_DSP_PROFILE:
+	if (audio_devs[dev]->open_mode & OPEN_WRITE)
+	  audio_devs[dev]->dmap_out->applic_profile = *(int *) arg;
+	if (audio_devs[dev]->open_mode & OPEN_READ)
+	  audio_devs[dev]->dmap_in->applic_profile = *(int *) arg;
+	return 0;
+	break;
+
       default:
-	return DMAbuf_ioctl (dev, cmd, arg, 0);
+	return dma_ioctl (dev, cmd, arg);
       }
 }
 
@@ -542,32 +545,499 @@
    */
 }
 
-unsigned int
-audio_poll (kdev_t dev, struct fileinfo *file, poll_table * wait)
+int
+audio_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
 {
-  char           *dma_buf;
-  unsigned int	  mask = 0;
-  int             buf_no, buf_ptr, buf_size;
-
   dev = dev >> 4;
 
-  mask = DMAbuf_poll (dev, file, wait);
+  switch (sel_type)
+    {
+    case SEL_IN:
+      if (!(audio_devs[dev]->open_mode & OPEN_READ))
+	return 0;
+      if (audio_mode[dev] & AM_WRITE && !(audio_devs[dev]->flags & DMA_DUPLEX))
+	{
+	  return 0;		/* Not recording */
+	}
+
+      return DMAbuf_select (dev, file, sel_type, wait);
+      break;
+
+    case SEL_OUT:
+      if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+	return 0;
+      if (audio_mode[dev] & AM_READ && !(audio_devs[dev]->flags & DMA_DUPLEX))
+	{
+	  return 0;		/* Wrong direction */
+	}
+
+      return DMAbuf_select (dev, file, sel_type, wait);
+      break;
+
+    case SEL_EX:
+      return 0;
+    }
+
+  return 0;
+}
+
+
+#endif
+
+void
+reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording)
+{
+  /*
+   * This routine breaks the physical device buffers to logical ones.
+   */
+
+  struct audio_operations *dsp_dev = audio_devs[dev];
+
+  unsigned        i, n;
+  unsigned        sr, nc, sz, bsz;
+
+  if (!dmap->needs_reorg)
+    return;
+
+  sr = dsp_dev->d->set_speed (dev, 0);
+  nc = dsp_dev->d->set_channels (dev, 0);
+  sz = dsp_dev->d->set_bits (dev, 0);
+  dmap->needs_reorg = 0;
+
+  if (sz == 8)
+    dmap->neutral_byte = NEUTRAL8;
+  else
+    dmap->neutral_byte = NEUTRAL16;
+
+  if (sr < 1 || nc < 1 || sz < 1)
+    {
+      printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n",
+	      dev, sr, nc, sz);
+      sr = DSP_DEFAULT_SPEED;
+      nc = 1;
+      sz = 8;
+    }
+
+  sz = sr * nc * sz;
+
+  sz /= 8;			/* #bits -> #bytes */
+  dmap->data_rate = sz;
+
+  if (dmap->fragment_size == 0)
+    {				/* Compute the fragment size using the default algorithm */
+
+      /*
+         * Compute a buffer size for time not exceeding 1 second.
+         * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
+         * of sound (using the current speed, sample size and #channels).
+       */
+
+      bsz = dsp_dev->buffsize;
+      while (bsz > sz)
+	bsz /= 2;
+
+      if (bsz == dsp_dev->buffsize)
+	bsz /= 2;		/* Needs at least 2 buffers */
+
+/*
+ *    Split the computed fragment to smaller parts. After 3.5a9
+ *      the default subdivision is 4 which should give better
+ *      results when recording.
+ */
+
+      if (dmap->subdivision == 0)	/* Not already set */
+	{
+	  dmap->subdivision = 4;	/* Init to the default value */
+
+	  if ((bsz / dmap->subdivision) > 4096)
+	    dmap->subdivision *= 2;
+	  if ((bsz / dmap->subdivision) < 4096)
+	    dmap->subdivision = 1;
+	}
+
+      bsz /= dmap->subdivision;
+
+      if (bsz < 16)
+	bsz = 16;		/* Just a sanity check */
+
+      dmap->fragment_size = bsz;
+    }
+  else
+    {
+      /*
+         * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
+         * the buffer size computation has already been done.
+       */
+      if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2))
+	dmap->fragment_size = (audio_devs[dev]->buffsize / 2);
+      bsz = dmap->fragment_size;
+    }
+
+  bsz &= ~0x03;			/* Force size which is multiple of 4 bytes */
+#ifdef OS_DMA_ALIGN_CHECK
+  OS_DMA_ALIGN_CHECK (bsz);
+#endif
+
+  n = dsp_dev->buffsize / bsz;
+  if (n > MAX_SUB_BUFFERS)
+    n = MAX_SUB_BUFFERS;
+  if (n > dmap->max_fragments)
+    n = dmap->max_fragments;
+
+  if (n < 2)
+    {
+      n = 2;
+      bsz /= 2;
+    }
+
+  dmap->nbufs = n;
+  dmap->bytes_in_use = n * bsz;
+  dmap->fragment_size = bsz;
+
+  if (dmap->raw_buf)
+    {
+      memset (dmap->raw_buf,
+	      dmap->neutral_byte,
+	      dmap->bytes_in_use);
+    }
+
+  for (i = 0; i < dmap->nbufs; i++)
+    {
+      dmap->counts[i] = 0;
+    }
+
+  dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
+}
+
+static int
+dma_subdivide (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
+{
+  if (fact == 0)
+    {
+      fact = dmap->subdivision;
+      if (fact == 0)
+	fact = 1;
+      return (*(int *) arg = fact);
+    }
+
+  if (dmap->subdivision != 0 ||
+      dmap->fragment_size)	/* Too late to change */
+    return -EINVAL;
 
-/* sel_in */
-  if (audio_mode[dev] & AM_WRITE && !(audio_devs[dev]->flags & DMA_DUPLEX))
-    mask &= ~(POLLIN | POLLRDNORM); /* Wrong direction */
-
-/* sel_out */
-  if (audio_mode[dev] & AM_READ && !(audio_devs[dev]->flags & DMA_DUPLEX)) {
-    mask &= ~(POLLOUT | POLLWRNORM); /* Wrong direction */
-    goto sel_ex;
-  }
-  if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0)
-    mask |= POLLOUT | POLLWRNORM;
+  if (fact > MAX_REALTIME_FACTOR)
+    return -EINVAL;
 
- sel_ex:
-  return mask;
+  if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
+    return -EINVAL;
+
+  dmap->subdivision = fact;
+  return (*(int *) arg = fact);
 }
 
+static int
+dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
+{
+  int             bytes, count;
+
+  if (fact == 0)
+    return -EIO;
+
+  if (dmap->subdivision != 0 ||
+      dmap->fragment_size)	/* Too late to change */
+    return -EINVAL;
+
+  bytes = fact & 0xffff;
+  count = (fact >> 16) & 0x7fff;
+
+  if (count == 0)
+    count = MAX_SUB_BUFFERS;
 
+  if (bytes < 4 || bytes > 17)	/* <16 || > 512k */
+    return -EINVAL;
+
+  if (count < 2)
+    return -EINVAL;
+
+  if (audio_devs[dev]->min_fragment > 0)
+    if (bytes < audio_devs[dev]->min_fragment)
+      bytes = audio_devs[dev]->min_fragment;
+
+#ifdef OS_DMA_MINBITS
+  if (bytes < OS_DMA_MINBITS)
+    bytes = OS_DMA_MINBITS;
 #endif
+
+  dmap->fragment_size = (1 << bytes);
+  dmap->max_fragments = count;
+
+  if (dmap->fragment_size > audio_devs[dev]->buffsize)
+    dmap->fragment_size = audio_devs[dev]->buffsize;
+
+  if (dmap->fragment_size == audio_devs[dev]->buffsize &&
+      audio_devs[dev]->flags & DMA_AUTOMODE)
+    dmap->fragment_size /= 2;	/* Needs at least 2 buffers */
+
+  dmap->subdivision = 1;	/* Disable SNDCTL_DSP_SUBDIVIDE */
+  if (arg)
+    return (*(int *) arg = bytes | (count << 16));
+  else
+    return 0;
+}
+
+static int
+dma_ioctl (int dev, unsigned int cmd, caddr_t arg)
+{
+
+  struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
+  struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
+
+  switch (cmd)
+    {
+
+    case SNDCTL_DSP_SUBDIVIDE:
+      {
+	int             fact;
+	int             ret;
+
+	fact = *(int *) arg;
+
+	ret = dma_subdivide (dev, dmap_out, arg, fact);
+	if (ret < 0)
+	  return ret;
+
+	if (audio_devs[dev]->flags & DMA_DUPLEX &&
+	    audio_devs[dev]->open_mode & OPEN_READ)
+	  ret = dma_subdivide (dev, dmap_in, arg, fact);
+
+	return ret;
+      }
+      break;
+
+    case SNDCTL_DSP_GETISPACE:
+    case SNDCTL_DSP_GETOSPACE:
+      {
+	struct dma_buffparms *dmap = dmap_out;
+
+	audio_buf_info *info = (audio_buf_info *) arg;
+
+	if (cmd == SNDCTL_DSP_GETISPACE &&
+	    !(audio_devs[dev]->open_mode & OPEN_READ))
+	  return -EINVAL;
+
+	if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
+	  dmap = dmap_in;
+
+	if (dmap->mapping_flags & DMA_MAP_MAPPED)
+	  return -EINVAL;
+
+	if (!(dmap->flags & DMA_ALLOC_DONE))
+	  reorganize_buffers (dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
+
+	info->fragstotal = dmap->nbufs;
+
+	if (cmd == SNDCTL_DSP_GETISPACE)
+	  info->fragments = dmap->qlen;
+	else
+	  {
+	    if (!DMAbuf_space_in_queue (dev))
+	      info->fragments = 0;
+	    else
+	      {
+		info->fragments = dmap->nbufs - dmap->qlen;
+		if (audio_devs[dev]->d->local_qlen)
+		  {
+		    int             tmp = audio_devs[dev]->d->local_qlen (dev);
+
+		    if (tmp && info->fragments)
+		      tmp--;	/*
+				   * This buffer has been counted twice
+				 */
+		    info->fragments -= tmp;
+		  }
+	      }
+	  }
+
+	if (info->fragments < 0)
+	  info->fragments = 0;
+	else if (info->fragments > dmap->nbufs)
+	  info->fragments = dmap->nbufs;
+
+	info->fragsize = dmap->fragment_size;
+	info->bytes = info->fragments * dmap->fragment_size;
+
+	if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
+	  info->bytes -= dmap->counts[dmap->qhead];
+      }
+      return 0;
+
+    case SNDCTL_DSP_SETTRIGGER:
+      {
+	unsigned long   flags;
+
+	int             bits;
+	int             changed;
+
+	bits = *(int *) arg;
+	bits &= audio_devs[dev]->open_mode;
+
+	if (audio_devs[dev]->d->trigger == NULL)
+	  return -EINVAL;
+
+	if (!(audio_devs[dev]->flags & DMA_DUPLEX))
+	  if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT))
+	    {
+	      printk ("Sound: Device doesn't have full duplex capability\n");
+	      return -EINVAL;
+	    }
+
+	save_flags (flags);
+	cli ();
+	changed = audio_devs[dev]->enable_bits ^ bits;
+
+	if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go)
+	  {
+	    int             err;
+
+	    reorganize_buffers (dev, dmap_in, 1);
+
+	    if ((err = audio_devs[dev]->d->prepare_for_input (dev,
+			       dmap_in->fragment_size, dmap_in->nbufs)) < 0)
+	      return -err;
+
+	    audio_devs[dev]->enable_bits = bits;
+	    DMAbuf_activate_recording (dev, dmap_in);
+	  }
+
+
+	if ((changed & bits) & PCM_ENABLE_OUTPUT &&
+	 (dmap_out->mapping_flags & DMA_MAP_MAPPED || dmap_out->qlen > 0) &&
+	    audio_devs[dev]->go)
+	  {
+
+	    if (!(dmap_out->flags & DMA_ALLOC_DONE))
+	      {
+		reorganize_buffers (dev, dmap_out, 0);
+	      }
+
+	    ;
+	    dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
+	    DMAbuf_launch_output (dev, dmap_out);
+	    ;
+	  }
+
+	audio_devs[dev]->enable_bits = bits;
+	if (changed && audio_devs[dev]->d->trigger)
+	  {
+	    audio_devs[dev]->d->trigger (dev, bits * audio_devs[dev]->go);
+	  }
+	restore_flags (flags);
+      }
+    case SNDCTL_DSP_GETTRIGGER:
+      return (*(int *) arg = audio_devs[dev]->enable_bits);
+      break;
+
+    case SNDCTL_DSP_SETSYNCRO:
+
+      if (!audio_devs[dev]->d->trigger)
+	return -EINVAL;
+
+      audio_devs[dev]->d->trigger (dev, 0);
+      audio_devs[dev]->go = 0;
+      return 0;
+      break;
+
+    case SNDCTL_DSP_GETIPTR:
+      {
+	count_info      info;
+	unsigned long   flags;
+
+	if (!(audio_devs[dev]->open_mode & OPEN_READ))
+	  return -EINVAL;
+
+	save_flags (flags);
+	cli ();
+	info.bytes = audio_devs[dev]->dmap_in->byte_counter;
+	info.ptr = DMAbuf_get_buffer_pointer (dev, audio_devs[dev]->dmap_in) & ~3;
+	info.blocks = audio_devs[dev]->dmap_in->qlen;
+	info.bytes += info.ptr;
+	memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
+
+	if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
+	  audio_devs[dev]->dmap_in->qlen = 0;	/* Reset interrupt counter */
+	restore_flags (flags);
+	return 0;
+      }
+      break;
+
+    case SNDCTL_DSP_GETOPTR:
+      {
+	count_info      info;
+	unsigned long   flags;
+
+	if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
+	  return -EINVAL;
+
+	save_flags (flags);
+	cli ();
+	info.bytes = audio_devs[dev]->dmap_out->byte_counter;
+	info.ptr = DMAbuf_get_buffer_pointer (dev, audio_devs[dev]->dmap_out) & ~3;
+	info.blocks = audio_devs[dev]->dmap_out->qlen;
+	info.bytes += info.ptr;
+	memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
+
+	if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
+	  audio_devs[dev]->dmap_out->qlen = 0;	/* Reset interrupt counter */
+
+	restore_flags (flags);
+	return 0;
+      }
+      break;
+
+
+    case SNDCTL_DSP_POST:
+      ;
+      if (audio_devs[dev]->dmap_out->qlen > 0)
+	if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
+	  DMAbuf_launch_output (dev, audio_devs[dev]->dmap_out);
+      ;
+      return 0;
+      break;
+
+    case SNDCTL_DSP_GETBLKSIZE:
+      if (!(dmap_out->flags & DMA_ALLOC_DONE))
+	{
+	  if (audio_devs[dev]->open_mode & OPEN_WRITE)
+	    reorganize_buffers (dev, dmap_out,
+				(audio_devs[dev]->open_mode == OPEN_READ));
+	  if (audio_devs[dev]->flags & DMA_DUPLEX &&
+	      audio_devs[dev]->open_mode & OPEN_READ)
+	    reorganize_buffers (dev, dmap_in,
+				(audio_devs[dev]->open_mode == OPEN_READ));
+	}
+
+      return (*(int *) arg = dmap_out->fragment_size);
+      break;
+
+    case SNDCTL_DSP_SETFRAGMENT:
+      {
+	int             fact;
+	int             ret;
+
+	fact = *(int *) arg;
+	ret = dma_set_fragment (dev, dmap_out, arg, fact);
+	if (ret < 0)
+	  return ret;
+
+	if (audio_devs[dev]->flags & DMA_DUPLEX &&
+	    audio_devs[dev]->open_mode & OPEN_READ)
+	  ret = dma_set_fragment (dev, dmap_in, arg, fact);
+
+	return ret;
+      }
+      break;
+
+    default:
+      return audio_devs[dev]->d->ioctl (dev, cmd, arg);
+    }
+
+}
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/configure.c linux/drivers/sound/configure.c
--- v2.1.27/linux/drivers/sound/configure.c	Fri Nov 15 00:15:12 1996
+++ linux/drivers/sound/configure.c	Wed Feb 26 02:35:07 1997
@@ -1,14 +1,14 @@
 /*
  *     PnP soundcard support is not included in this version.
  *
- *     There is a separately distributed patch available for AEDSP16.
+ *       AESDP16 driver is now included in the lowlevel directory.
  */
-#define DISABLED_OPTIONS 	(B(OPT_SPNP)|B(OPT_AEDSP16)|B(OPT_UNUSED1)|B(OPT_UNUSED2)|B(OPT_UNUSED3)|B(OPT_UNUSED4)|B(OPT_UNUSED5))
+#define DISABLED_OPTIONS 	(B(OPT_SPNP)|B(OPT_AEDSP16)|B(OPT_UNUSED1)|B(OPT_UNUSED2)|B(OPT_UNUSED3)|B(OPT_UNUSED4)|B(OPT_UNUSED5)|B(OPT_UART6850))
 /*
  * sound/configure.c  - Configuration program for the Linux Sound Driver
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -75,7 +75,7 @@
 		     B(OPT_SSCAPE)|B(OPT_TRIX)|B(OPT_MAD16)|B(OPT_CS4232)|\
 		     B(OPT_SPNP))
 #define SBDSP_DEVS (B(OPT_SB)|B(OPT_SPNP)|B(OPT_MAD16)|B(OPT_TRIX))
-#define SEQUENCER_DEVS (MIDI_CARDS|B(OPT_YM3812)|B(OPT_ADLIB)|B(OPT_GUS)|B(OPT_MAUI))
+#define SEQUENCER_DEVS 0x7fffffff
 /*
  * Options that have been disabled for some reason (incompletely implemented
  * and/or tested). Don't remove from this list before looking at file
@@ -145,7 +145,7 @@
 char           *questions[] =
 {
   "ProAudioSpectrum 16 support",
-  "Sound Blaster (SB, SBPro, SB16, clones) support",
+  "_TRUE_ Sound Blaster (SB, SBPro, SB16/32/64, ESS, Jazz16) support",
   "Generic OPL2/OPL3 FM synthesizer support",
   "Gravis Ultrasound support",
   "MPU-401 support (NOT for SB16)",
@@ -991,6 +991,12 @@
 		  3,
 		  "0, 1 or 3");
 
+  ask_int_choice (B (OPT_MSS), "MSS_DMA2",
+		  "MSS/WSS second DMA (if possible)",
+		  FMT_INT,
+		  -1,
+		  "0, 1 or 3");
+
   ask_int_choice (B (OPT_SSCAPE), "SSCAPE_BASE",
 		  "SoundScape MIDI I/O base",
 		  FMT_HEX,
@@ -1037,7 +1043,7 @@
     }
 
   if (dump_only)
-    show_comment (B (OPT_MAUI),
+    show_comment (B (OPT_TRIX),
     "ERROR! You have to use old sound configuration method with AudioTrix.");
 
   ask_int_choice (B (OPT_TRIX), "TRIX_BASE",
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/cs4232.c linux/drivers/sound/cs4232.c
--- v2.1.27/linux/drivers/sound/cs4232.c	Fri Nov 15 00:15:13 1996
+++ linux/drivers/sound/cs4232.c	Wed Feb 26 02:35:08 1997
@@ -7,9 +7,14 @@
  * interfaces. This is just a temporary driver until full PnP support
  * gets implemented. Just the WSS codec, FM synth and the MIDI ports are
  * supported. Other interfaces are left uninitialized.
+ *
+ * Supported chips are:
+ *      CS4232
+ *      CS4236
+ *      CS4236B
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -25,8 +30,6 @@
 #define KEY_PORT	0x279	/* Same as LPT1 status port */
 #define CSN_NUM		0x99	/* Just a random number */
 
-static int     *osp;
-
 static void 
 CS_OUT (unsigned char a)
 {
@@ -48,7 +51,7 @@
   mpu_base = hw_config->io_base;
   mpu_irq = hw_config->irq;
 
-  return 0;
+  return 1;
 }
 
 void
@@ -181,7 +184,7 @@
  * Initialize logical device 3 (MPU)
  */
 
-#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
       if (mpu_base != 0 && mpu_irq != 0)
 	{
 	  CS_OUT2 (0x15, 0x03);	/* Select logical device 3 (MPU) */
@@ -253,7 +256,7 @@
   if (dma2 == -1)
     dma2 = dma1;
 
-  ad1848_init ("CS4232", base,
+  ad1848_init ("Crystal audio controller", base,
 	       irq,
 	       dma1,		/* Playback DMA */
 	       dma2,		/* Capture DMA */
@@ -267,7 +270,7 @@
       AD1848_REROUTE (SOUND_MIXER_LINE3, SOUND_MIXER_SYNTH);	/* FM synth */
     }
 
-#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
   if (mpu_base != 0 && mpu_irq != 0)
     {
       static struct address_info hw_config2 =
@@ -283,10 +286,10 @@
       hw_config2.driver_use_2 = 0;
       hw_config2.card_subtype = 0;
 
-      if (probe_mpu401 (&hw_config2))
+      if (probe_uart401 (&hw_config2))
 	{
 	  mpu_detected = 1;
-	  attach_mpu401 (&hw_config2);
+	  attach_uart401 (&hw_config2);
 	}
       else
 	{
@@ -311,7 +314,7 @@
 		 dma2,		/* Capture DMA */
 		 0);
 
-#if (defined(CONFIG_MPU401) || defined(CONFIG_MPU_EMU)) && defined(CONFIG_MIDI)
+#if defined(CONFIG_UART401) && defined(CONFIG_MIDI)
   if (mpu_base != 0 && mpu_irq != 0 && mpu_detected)
     {
       static struct address_info hw_config2 =
@@ -327,7 +330,7 @@
       hw_config2.driver_use_2 = 0;
       hw_config2.card_subtype = 0;
 
-      unload_mpu401 (&hw_config2);
+      unload_uart401 (&hw_config2);
     }
 #endif
 }
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/dev_table.c linux/drivers/sound/dev_table.c
--- v2.1.27/linux/drivers/sound/dev_table.c	Fri Nov 15 00:15:14 1996
+++ linux/drivers/sound/dev_table.c	Wed Feb 26 02:35:09 1997
@@ -4,7 +4,7 @@
  * Device call tables.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -131,6 +131,7 @@
   start_cards ();
 }
 
+
 void
 sound_unload_drivers (void)
 {
@@ -143,14 +144,19 @@
   if (trace_init)
     printk ("Sound unload started\n");
 
+
   for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
     if (snd_installed_cards[i].enabled)
-      if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) != -1)
-	if (sound_drivers[drv].unload)
+      {
+	if ((drv = snd_find_driver (snd_installed_cards[i].card_type)) != -1)
 	  {
-	    sound_drivers[drv].unload (&snd_installed_cards[i].config);
-	    snd_installed_cards[i].enabled = 0;
+	    if (sound_drivers[drv].unload)
+	      {
+		sound_drivers[drv].unload (&snd_installed_cards[i].config);
+		snd_installed_cards[i].enabled = 0;
+	      }
 	  }
+      }
 
   if (trace_init)
     printk ("Sound unload complete\n");
@@ -163,6 +169,7 @@
 
   unsigned long   flags;
 
+
   DEB (printk ("unload driver %d: ", type));
 
   for (i = 0; i < n && snd_installed_cards[i].card_type; i++)
@@ -450,11 +457,13 @@
 
 
   d = (struct audio_driver *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct audio_driver)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct audio_driver);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
 
   op = (struct audio_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct audio_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct audio_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
@@ -480,8 +489,6 @@
   op->flags = flags;
   op->format_mask = format_mask;
   op->devc = devc;
-  op->dmachan1 = dma1;
-  op->dmachan2 = dma2;
 
 /*
  *    Hardcoded defaults
@@ -492,6 +499,11 @@
   num = num_audiodevs++;
 
   DMAbuf_init ();
+
+  op->dmap_out->dma = dma1;
+  op->dmap_in->dma = dma2;
+
+  DMAbuf_init ();
   audio_init_devices ();
   return num;
 #else
@@ -524,6 +536,7 @@
 
 
   op = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct mixer_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct mixer_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/dev_table.h linux/drivers/sound/dev_table.h
--- v2.1.27/linux/drivers/sound/dev_table.h	Sun Jan 26 02:07:24 1997
+++ linux/drivers/sound/dev_table.h	Wed Feb 26 02:34:46 1997
@@ -4,7 +4,7 @@
  *	Global definitions for device call tables
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -15,7 +15,6 @@
 #ifndef _DEV_TABLE_H_
 #define _DEV_TABLE_H_
 
-#include <linux/config.h>
 
 /*
  * Sound card numbers 27 to 999. (1 to 26 are defined in soundcard.h)
@@ -84,7 +83,7 @@
 #define DMA_EMPTY	0x00000010	
 #define DMA_ALLOC_DONE	0x00000020
 #define DMA_SYNCING	0x00000040
-#define DMA_CLEAN	0x00000080
+#define DMA_DIRTY	0x00000080
 #define DMA_POST	0x00000100
 
 	int      open_mode;
@@ -102,20 +101,25 @@
 	int      subdivision;
 
 	int      fragment_size;
+        int	 needs_reorg;
 	int	 max_fragments;
 
 	int	 bytes_in_use;
 
 	int	 underrun_count;
-	int	 byte_counter;
+	unsigned long	 byte_counter;
+	unsigned long	 user_counter;
 	int	 data_rate; /* Bytes/second */
 
 	int	 mapping_flags;
 #define			DMA_MAP_MAPPED		0x00000001
 	char	neutral_byte;
+	int	dma;		/* DMA channel */
+
 #ifdef OS_DMA_PARMS
 	OS_DMA_PARMS
 #endif
+	int     applic_profile;	/* Application profile (APF_*) */
 };
 
 /*
@@ -136,14 +140,13 @@
 	int (*open) (int dev, int mode);
 	void (*close) (int dev);
 	void (*output_block) (int dev, unsigned long buf, 
-			      int count, int intrflag, int dma_restart);
+			      int count, int intrflag);
 	void (*start_input) (int dev, unsigned long buf, 
-			     int count, int intrflag, int dma_restart);
-	int (*ioctl) (int dev, unsigned int cmd, caddr_t arg, int local);
+			     int count, int intrflag);
+	int (*ioctl) (int dev, unsigned int cmd, caddr_t arg);
 	int (*prepare_for_input) (int dev, int bufsize, int nbufs);
 	int (*prepare_for_output) (int dev, int bufsize, int nbufs);
-	void (*reset) (int dev);
-	void (*halt_xfer) (int dev);
+	void (*halt_io) (int dev);
 	int (*local_qlen)(int dev);
         void (*copy_user)(int dev, char *localbuf, int localoffs,
                                const char *userbuf, int useroffs, int len);
@@ -156,7 +159,7 @@
 };
 
 struct audio_operations {
-        char name[64];
+        char name[128];
 	int flags;
 #define NOTHING_SPECIAL 	0x00
 #define NEEDS_RESTART		0x01
@@ -165,11 +168,12 @@
 #define DMA_PSEUDO_AUTOMODE	0x08
 #define DMA_HARDSTOP		0x10
 #define DMA_NODMA		0x20
+#define DMA_EXACT		0x40
 	int  format_mask;	/* Bitmask for supported audio formats */
 	void *devc;		/* Driver specific info */
 	struct audio_driver *d;
+	void *portc;		/* Driver spesific info */
 	long buffsize;
-	int dmachan1, dmachan2;
 	struct dma_buffparms *dmap_in, *dmap_out;
 	struct coproc_operations *coproc;
 	int mixer_dev;
@@ -177,6 +181,7 @@
  	int open_mode;
 	int go;
 	int min_fragment;	/* 0 == unlimited */
+	int parent_dev;		/* 0 -> no parent, 1 to n -> parent=parent_dev+1 */
 };
 
 int *load_mixer_volumes(char *name, int *levels, int present);
@@ -191,6 +196,7 @@
 };
 
 struct synth_operations {
+	char *id;	/* Unique identifier (ASCII) max 29 char */
 	struct synth_info *info;
 	int midi_dev;
 	int synth_type;
@@ -217,6 +223,12 @@
 
  	struct voice_alloc_info alloc;
  	struct channel_info chn_info[16];
+	int emulation;
+#define	EMU_GM			1	/* General MIDI */
+#define	EMU_XG			2	/* Yamaha XG */
+#define MAX_SYSEX_BUF	64
+	unsigned char sysex_buf[MAX_SYSEX_BUF];
+	int sysex_ptr;
 };
 
 struct midi_input_info { /* MIDI input scanner variables */
@@ -255,6 +267,7 @@
 
 struct sound_lowlev_timer {
 		int dev;
+		int priority;
 		unsigned int (*tmr_start)(int dev, unsigned int usecs);
 		void (*tmr_disable)(int dev);
 		void (*tmr_restart)(int dev);
@@ -279,7 +292,7 @@
 	struct synth_operations *synth_devs[MAX_SYNTH_DEV+MAX_MIDI_DEV] = {NULL}; int num_synths = 0;
 	struct midi_operations *midi_devs[MAX_MIDI_DEV] = {NULL}; int num_midis = 0;
 
-#ifdef CONFIG_SEQUENCER
+#if defined(CONFIG_SEQUENCER) && !defined(EXCLUDE_TIMERS)
 	extern struct sound_timer_operations default_sound_timer;
 	struct sound_timer_operations *sound_timer_devs[MAX_TIMER_DEV] = 
 		{&default_sound_timer, NULL}; 
@@ -369,8 +382,6 @@
 
 	int num_sound_drivers =
 	    sizeof(sound_drivers) / sizeof (struct driver_info);
-	int max_sound_drivers =
-	    sizeof(sound_drivers) / sizeof (struct driver_info);
 
 
 #ifndef FULL_SOUND
@@ -429,12 +440,12 @@
 
 #ifdef CONFIG_MSS
 #	ifdef DESKPROXL
-		{SNDCARD_DESKPROXL, {MSS_BASE, MSS_IRQ, MSS_DMA, -1}, SND_DEFAULT_ENABLE},
+		{SNDCARD_DESKPROXL, {MSS_BASE, MSS_IRQ, MSS_DMA, MSS_DMA2}, SND_DEFAULT_ENABLE},
 #	else
-		{SNDCARD_MSS, {MSS_BASE, MSS_IRQ, MSS_DMA, -1}, SND_DEFAULT_ENABLE},
+		{SNDCARD_MSS, {MSS_BASE, MSS_IRQ, MSS_DMA, MSS_DMA2}, SND_DEFAULT_ENABLE},
 #	endif
 #	ifdef MSS2_BASE
-		{SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA, -1}, SND_DEFAULT_ENABLE},
+		{SNDCARD_MSS, {MSS2_BASE, MSS2_IRQ, MSS2_DMA, MSS2_DMA2}, SND_DEFAULT_ENABLE},
 #	endif
 #endif
 
@@ -497,23 +508,20 @@
 
 	int num_sound_cards =
 	    sizeof(snd_installed_cards) / sizeof (struct card_info);
-	int max_sound_cards =
+	static int max_sound_cards =
 	    sizeof(snd_installed_cards) / sizeof (struct card_info);
 
 #else
 	int num_sound_cards = 0;
 	struct card_info snd_installed_cards[20] = {{0}};
-	int max_sound_cards = 20;
+	static int max_sound_cards = 20;
 #endif
 
 #if defined(MODULE) || (!defined(linux) && !defined(_AIX))
 	int trace_init = 0;
-#else
+#   else
 	int trace_init = 1;
-#endif
-#ifdef MODULE_PARM
-MODULE_PARM(trace_init, "i");
-#endif
+#   endif
 
 #else
 	extern struct audio_operations * audio_devs[MAX_AUDIO_DEV]; extern int num_audiodevs;
@@ -524,10 +532,8 @@
 
 	extern struct driver_info sound_drivers[];
 	extern int num_sound_drivers;
-	extern int max_sound_drivers;
 	extern struct card_info snd_installed_cards[];
 	extern int num_sound_cards;
-	extern int max_sound_cards;
 
 	extern int trace_init;
 #endif	/* _DEV_TABLE_C_ */
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/dmabuf.c linux/drivers/sound/dmabuf.c
--- v2.1.27/linux/drivers/sound/dmabuf.c	Tue Jan 28 00:02:45 1997
+++ linux/drivers/sound/dmabuf.c	Wed Feb 26 02:35:10 1997
@@ -4,14 +4,15 @@
  * The DMA buffer manager for digitized voice applications
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
 #include <linux/config.h>
-#include <linux/poll.h>
+
+#undef  BE_CONSERVATIVE
 
 #include "sound_config.h"
 
@@ -28,9 +29,6 @@
 {
   {0}};
 
-#define NEUTRAL8	0x80
-#define NEUTRAL16	0x00
-
 static int      ndmaps = 0;
 
 #define MAX_DMAP (MAX_AUDIO_DEV*2)
@@ -39,130 +37,9 @@
 {
   {0}};
 
-static int      space_in_queue (int dev);
-
 static void     dma_reset_output (int dev);
 static void     dma_reset_input (int dev);
-static int      dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact);
-
-static void
-reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording)
-{
-  /*
-   * This routine breaks the physical device buffers to logical ones.
-   */
-
-  struct audio_operations *dsp_dev = audio_devs[dev];
-
-  unsigned        i, n;
-  unsigned        sr, nc, sz, bsz;
-
-  sr = dsp_dev->d->set_speed (dev, 0);
-  nc = dsp_dev->d->set_channels (dev, 0);
-  sz = dsp_dev->d->set_bits (dev, 0);
-
-  if (sz == 8)
-    dmap->neutral_byte = NEUTRAL8;
-  else
-    dmap->neutral_byte = NEUTRAL16;
-
-  if (sr < 1 || nc < 1 || sz < 1)
-    {
-      printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n",
-	      dev, sr, nc, sz);
-      sr = DSP_DEFAULT_SPEED;
-      nc = 1;
-      sz = 8;
-    }
-
-  sz = sr * nc * sz;
-
-  sz /= 8;			/* #bits -> #bytes */
-  dmap->data_rate = sz;
-
-  if (dmap->fragment_size == 0)
-    {				/* Compute the fragment size using the default algorithm */
-
-      /*
-         * Compute a buffer size for time not exceeding 1 second.
-         * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds
-         * of sound (using the current speed, sample size and #channels).
-       */
-
-      bsz = dsp_dev->buffsize;
-      while (bsz > sz)
-	bsz /= 2;
-
-      if (bsz == dsp_dev->buffsize)
-	bsz /= 2;		/* Needs at least 2 buffers */
-
-/*
- *    Split the computed fragment to smaller parts. After 3.5a9
- *      the default subdivision is 4 which should give better
- *      results when recording.
- */
-
-      if (dmap->subdivision == 0)	/* Not already set */
-	{
-	  dmap->subdivision = 4;	/* Init to the default value */
-
-	  if ((bsz / dmap->subdivision) > 4096)
-	    dmap->subdivision *= 2;
-	  if ((bsz / dmap->subdivision) < 4096)
-	    dmap->subdivision = 1;
-	}
-
-      bsz /= dmap->subdivision;
-
-      if (bsz < 16)
-	bsz = 16;		/* Just a sanity check */
-
-      dmap->fragment_size = bsz;
-    }
-  else
-    {
-      /*
-         * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or
-         * the buffer size computation has already been done.
-       */
-      if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2))
-	dmap->fragment_size = (audio_devs[dev]->buffsize / 2);
-      bsz = dmap->fragment_size;
-    }
-
-  bsz &= ~0x03;			/* Force size which is multiple of 4 bytes */
-#ifdef OS_DMA_ALIGN_CHECK
-  OS_DMA_ALIGN_CHECK (bsz);
-#endif
-
-  n = dsp_dev->buffsize / bsz;
-  if (n > MAX_SUB_BUFFERS)
-    n = MAX_SUB_BUFFERS;
-  if (n > dmap->max_fragments)
-    n = dmap->max_fragments;
-
-  if (n < 2)
-    {
-      n = 2;
-      bsz /= 2;
-    }
-
-  dmap->nbufs = n;
-  dmap->bytes_in_use = n * bsz;
-  dmap->fragment_size = bsz;
-
-  if (dmap->raw_buf)
-    memset (dmap->raw_buf,
-	    dmap->neutral_byte,
-	    dmap->bytes_in_use);
-
-  for (i = 0; i < dmap->nbufs; i++)
-    {
-      dmap->counts[i] = 0;
-    }
-
-  dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;
-}
+static int      local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
 
 static void
 dma_init_buffers (int dev, struct dma_buffparms *dmap)
@@ -176,17 +53,18 @@
       in_sleep_flag[dev].opts = WK_NONE;
     }
 
-  dmap->flags = DMA_BUSY;	/* Other flags off */
-  dmap->qlen = dmap->qhead = dmap->qtail = 0;
-  dmap->nbufs = 1;
+  dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+  dmap->byte_counter = 0;
   dmap->bytes_in_use = audio_devs[dev]->buffsize;
 
   dmap->dma_mode = DMODE_NONE;
   dmap->mapping_flags = 0;
-  dmap->neutral_byte = NEUTRAL8;
+  dmap->neutral_byte = 0x80;
   dmap->data_rate = 8000;
   dmap->cfrag = -1;
   dmap->closing = 0;
+  dmap->nbufs = 1;
+  dmap->flags = DMA_BUSY;	/* Other flags off */
 }
 
 static int
@@ -219,6 +97,8 @@
   dmap->fragment_size = 0;
   dmap->max_fragments = 65536;	/* Just a large value */
   dmap->byte_counter = 0;
+  dmap->applic_profile = APF_NORMAL;
+  dmap->needs_reorg = 1;
 
 
   dma_init_buffers (dev, dmap);
@@ -235,20 +115,20 @@
     dmap->dma_mode = DMODE_NONE;
   dmap->flags &= ~DMA_BUSY;
 
-  disable_dma (chan);
-  sound_free_dmap (dev, dmap, chan);
+  disable_dma (dmap->dma);
 }
 
+
 static unsigned int
 default_set_bits (int dev, unsigned int bits)
 {
-  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) bits, 1);
+  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) & bits);
 }
 
 static int
 default_set_speed (int dev, int speed)
 {
-  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) speed, 1);
+  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) & speed);
 }
 
 static short
@@ -256,7 +136,7 @@
 {
   int             c = channels;
 
-  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) c, 1);
+  return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) & c);
 }
 
 static void
@@ -279,50 +159,66 @@
 
   if (dev >= num_audiodevs)
     {
-      /*  printk ("PCM device %d not installed.\n", dev); */
       return -ENXIO;
     }
 
   if (!audio_devs[dev])
     {
-      /* printk ("PCM device %d not initialized\n", dev); */
       return -ENXIO;
     }
 
   if (!(audio_devs[dev]->flags & DMA_DUPLEX))
     {
       audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out;
-      audio_devs[dev]->dmachan2 = audio_devs[dev]->dmachan1;
+      audio_devs[dev]->dmap_in->dma = audio_devs[dev]->dmap_out->dma;
     }
 
+  check_driver (audio_devs[dev]->d);
+
   if ((retval = audio_devs[dev]->d->open (dev, mode)) < 0)
     return retval;
 
-  check_driver (audio_devs[dev]->d);
-
   dmap_out = audio_devs[dev]->dmap_out;
   dmap_in = audio_devs[dev]->dmap_in;
 
-  if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmachan1)) < 0)
+  if (dmap_in == dmap_out)
+    audio_devs[dev]->flags &= ~DMA_DUPLEX;
+
+  if (mode & OPEN_WRITE)
     {
-      audio_devs[dev]->d->close (dev);
-      return retval;
+      if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmap_out->dma)) < 0)
+	{
+	  audio_devs[dev]->d->close (dev);
+	  return retval;
+	}
     }
 
   audio_devs[dev]->enable_bits = mode;
-  if (mode & OPEN_READ &&
-      audio_devs[dev]->flags & DMA_DUPLEX &&
-      dmap_out != dmap_in)
-    if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmachan2)) < 0)
-      {
-	audio_devs[dev]->d->close (dev);
-	close_dmap (dev, dmap_out, audio_devs[dev]->dmachan1);
-	return retval;
-      }
+
+  if (mode == OPEN_READ || (mode != OPEN_WRITE &&
+			    audio_devs[dev]->flags & DMA_DUPLEX))
+    {
+      if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmap_in->dma)) < 0)
+	{
+	  audio_devs[dev]->d->close (dev);
+
+	  if (mode & OPEN_WRITE)
+	    {
+	      close_dmap (dev, dmap_out, audio_devs[dev]->dmap_out->dma);
+	    }
+
+	  return retval;
+	}
+    }
+
   audio_devs[dev]->open_mode = mode;
   audio_devs[dev]->go = 1;
-  in_sleep_flag[dev].opts = WK_NONE;
-  out_sleep_flag[dev].opts = WK_NONE;
+
+  if (mode & OPEN_READ)
+    in_sleep_flag[dev].opts = WK_NONE;
+
+  if (mode & OPEN_WRITE)
+    out_sleep_flag[dev].opts = WK_NONE;
 
   audio_devs[dev]->d->set_bits (dev, 8);
   audio_devs[dev]->d->set_channels (dev, 1);
@@ -334,17 +230,10 @@
 void
 DMAbuf_reset (int dev)
 {
-  unsigned long   flags;
-
-  save_flags (flags);
-  cli ();
-  audio_devs[dev]->d->reset (dev);
-  restore_flags (flags);
-
-  dma_reset_output (dev);
+  if (audio_devs[dev]->open_mode & OPEN_WRITE)
+    dma_reset_output (dev);
 
-  if (audio_devs[dev]->flags & DMA_DUPLEX &&
-      audio_devs[dev]->open_mode & OPEN_READ)
+  if (audio_devs[dev]->open_mode & OPEN_READ)
     dma_reset_input (dev);
 }
 
@@ -356,8 +245,6 @@
 
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-  if (dmap->dma_mode != DMODE_OUTPUT)
-    return;
 
   if (!(dmap->flags & DMA_STARTED))	/* DMA is not active */
     return;
@@ -404,7 +291,6 @@
       };
     }
   audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
-  audio_devs[dev]->dmap_out->flags |= DMA_RESTART;
 
 /*
  * Finally shut the device off
@@ -412,68 +298,74 @@
 
   if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
       !audio_devs[dev]->d->halt_output)
-    audio_devs[dev]->d->reset (dev);
+    audio_devs[dev]->d->halt_io (dev);
   else
     audio_devs[dev]->d->halt_output (dev);
   audio_devs[dev]->dmap_out->flags &= ~DMA_STARTED;
   restore_flags (flags);
 
-  dma_init_buffers (dev, audio_devs[dev]->dmap_out);
+  clear_dma_ff (dmap->dma);
+  disable_dma (dmap->dma);
+  dmap->byte_counter = 0;
   reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);
+  dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
 }
 
 static void
 dma_reset_input (int dev)
 {
   unsigned long   flags;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
 
   save_flags (flags);
   cli ();
   if (!(audio_devs[dev]->flags & DMA_DUPLEX) ||
       !audio_devs[dev]->d->halt_input)
-    audio_devs[dev]->d->reset (dev);
+    audio_devs[dev]->d->halt_io (dev);
   else
     audio_devs[dev]->d->halt_input (dev);
   audio_devs[dev]->dmap_in->flags &= ~DMA_STARTED;
   restore_flags (flags);
 
-  dma_init_buffers (dev, audio_devs[dev]->dmap_in);
+  dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;
+  dmap->byte_counter = 0;
   reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1);
 }
 
-static void
-launch_output (int dev, struct dma_buffparms *dmap)
+void
+DMAbuf_launch_output (int dev, struct dma_buffparms *dmap)
 {
-  dmap->flags |= DMA_ACTIVE;
-
-  if (dmap->dma_mode == DMODE_NONE || dmap->flags & DMA_RESTART)
+  if (!(dmap->flags & DMA_ACTIVE) || !(audio_devs[dev]->flags & DMA_AUTOMODE))
     {
-      reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);
-      audio_devs[dev]->d->prepare_for_output (dev,
-					  dmap->fragment_size, dmap->nbufs);
-    }
+      if (!(dmap->flags & DMA_STARTED))
+	{
+	  reorganize_buffers (dev, dmap, 0);
 
-  dmap->dma_mode |= DMODE_OUTPUT;
+	  if (audio_devs[dev]->d->prepare_for_output (dev,
+					  dmap->fragment_size, dmap->nbufs))
+	    return;
 
-  if (dmap->counts[dmap->qhead] == 0)
-    dmap->counts[dmap->qhead] = dmap->fragment_size;
+	  local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+			   DMA_MODE_WRITE);
+	}
+      if (dmap->counts[dmap->qhead] == 0)
+	dmap->counts[dmap->qhead] = dmap->fragment_size;
 
-  audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys +
-				    dmap->qhead * dmap->fragment_size,
-				    dmap->counts[dmap->qhead], 0,
-				 !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
-				    !(dmap->flags & DMA_STARTED));
-  dmap->flags |= DMA_STARTED;
-  if (audio_devs[dev]->d->trigger)
-    audio_devs[dev]->d->trigger (dev,
+      audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys +
+					dmap->qhead * dmap->fragment_size,
+					dmap->counts[dmap->qhead], 1);
+      if (audio_devs[dev]->d->trigger)
+	audio_devs[dev]->d->trigger (dev,
 			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+    }
+  dmap->flags |= DMA_ACTIVE;
 }
 
 int
 DMAbuf_sync (int dev)
 {
   unsigned long   flags;
-  int             tmout;
+  int             tmout, n = 0;
 
   if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
     return 0;
@@ -499,13 +391,14 @@
       ;
       if (dmap->qlen > 0)
 	if (!(dmap->flags & DMA_ACTIVE))
-	  launch_output (dev, dmap);
+	  DMAbuf_launch_output (dev, dmap);
       ;
 
       audio_devs[dev]->dmap_out->flags |= DMA_SYNCING;
 
       audio_devs[dev]->dmap_out->underrun_count = 0;
       while (!(current->signal & ~current->blocked)
+	     && n++ <= audio_devs[dev]->dmap_out->nbufs
 	     && audio_devs[dev]->dmap_out->qlen
 	     && audio_devs[dev]->dmap_out->underrun_count == 0)
 	{
@@ -534,7 +427,6 @@
 	    }
 	}
       audio_devs[dev]->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE);
-      audio_devs[dev]->dmap_out->flags |= DMA_RESTART;
       restore_flags (flags);
       /*
        * Some devices such as GUS have huge amount of on board RAM for the
@@ -578,32 +470,39 @@
 {
   unsigned long   flags;
 
-  audio_devs[dev]->dmap_out->closing = 1;
-  audio_devs[dev]->dmap_in->closing = 1;
+  if (audio_devs[dev]->open_mode & OPEN_WRITE)
+    audio_devs[dev]->dmap_out->closing = 1;
+  if (audio_devs[dev]->open_mode & OPEN_READ)
+    audio_devs[dev]->dmap_in->closing = 1;
+
+  if (audio_devs[dev]->open_mode & OPEN_WRITE)
+    if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED))
+      if (!((current->signal & ~current->blocked))
+	  && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT))
+	{
+	  DMAbuf_sync (dev);
+	}
 
-  if (!(audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED))
-  if (!((current->signal & ~current->blocked))
-      && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT))
+  if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
     {
-      DMAbuf_sync (dev);
+      memset (audio_devs[dev]->dmap_out->raw_buf,
+	      audio_devs[dev]->dmap_out->neutral_byte,
+	      audio_devs[dev]->dmap_out->bytes_in_use);
     }
 
-  if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)
-    memset (audio_devs[dev]->dmap_out->raw_buf,
-	    audio_devs[dev]->dmap_out->neutral_byte,
-	    audio_devs[dev]->dmap_out->bytes_in_use);
-
   save_flags (flags);
   cli ();
 
-  audio_devs[dev]->d->halt_xfer (dev);
+  DMAbuf_reset (dev);
   audio_devs[dev]->d->close (dev);
 
-  close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
+  if (audio_devs[dev]->open_mode & OPEN_WRITE)
+    close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
 
-  if (audio_devs[dev]->open_mode & OPEN_READ &&
-      audio_devs[dev]->flags & DMA_DUPLEX)
-    close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2);
+  if (audio_devs[dev]->open_mode == OPEN_READ ||
+      (audio_devs[dev]->open_mode != OPEN_WRITE &&
+       audio_devs[dev]->flags & DMA_DUPLEX))
+    close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
   audio_devs[dev]->open_mode = 0;
 
   restore_flags (flags);
@@ -611,21 +510,15 @@
   return 0;
 }
 
-static int
-activate_recording (int dev, struct dma_buffparms *dmap)
+int
+DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap)
 {
-  int             prepare = 0;
+  if (!(audio_devs[dev]->open_mode & OPEN_READ))
+    return 0;
 
   if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT))
     return 0;
 
-  if (dmap->flags & DMA_RESTART)
-    {
-      dma_reset_input (dev);
-      dmap->flags &= ~DMA_RESTART;
-      prepare = 1;
-    }
-
   if (dmap->dma_mode == DMODE_OUTPUT)	/* Direction change */
     {
       DMAbuf_sync (dev);
@@ -633,8 +526,7 @@
       dmap->dma_mode = DMODE_NONE;
     }
 
-
-  if (prepare || !dmap->dma_mode)
+  if (!dmap->dma_mode)
     {
       int             err;
 
@@ -649,12 +541,14 @@
 
   if (!(dmap->flags & DMA_ACTIVE))
     {
+      if (dmap->needs_reorg)
+	reorganize_buffers (dev, dmap, 0);
+      local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+		       DMA_MODE_READ);
       audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
 				       dmap->qtail * dmap->fragment_size,
-				       dmap->fragment_size, 0,
-				 !(audio_devs[dev]->flags & DMA_AUTOMODE) ||
-				       !(dmap->flags & DMA_STARTED));
-      dmap->flags |= DMA_ACTIVE | DMA_STARTED;
+				       dmap->fragment_size, 0);
+      dmap->flags |= DMA_ACTIVE;
       if (audio_devs[dev]->d->trigger)
 	audio_devs[dev]->d->trigger (dev,
 			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
@@ -669,6 +563,11 @@
   int             err = EIO;
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
 
+  if (!(audio_devs[dev]->open_mode & OPEN_READ))
+    return -EIO;
+  if (dmap->needs_reorg)
+    reorganize_buffers (dev, dmap, 0);
+
   save_flags (flags);
   cli ();
   if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
@@ -687,7 +586,7 @@
 	  return -EAGAIN;
 	}
 
-      if ((err = activate_recording (dev, dmap)) < 0)
+      if ((err = DMAbuf_activate_recording (dev, dmap)) < 0)
 	{
 	  restore_flags (flags);
 	  return err;
@@ -737,7 +636,7 @@
 	{
 	  printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n");
 	  err = EIO;
-	  audio_devs[dev]->d->reset (dev);
+	  dma_reset_input (dev);
 	  ;
 	}
       else
@@ -781,83 +680,8 @@
   return 0;
 }
 
-static int
-dma_subdivide (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
-{
-  if (fact == 0)
-    {
-      fact = dmap->subdivision;
-      if (fact == 0)
-	fact = 1;
-      return ioctl_out (arg, fact);
-    }
-
-  if (dmap->subdivision != 0 ||
-      dmap->fragment_size)	/* Too late to change */
-    return -EINVAL;
-
-  if (fact > MAX_REALTIME_FACTOR)
-    return -EINVAL;
-
-  if (fact != 1 && fact != 2 && fact != 4 && fact != 8 && fact != 16)
-    return -EINVAL;
-
-  dmap->subdivision = fact;
-  return ioctl_out (arg, fact);
-}
-
-static int
-dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact)
-{
-  int             bytes, count;
-
-  if (fact == 0)
-    return -EIO;
-
-  if (dmap->subdivision != 0 ||
-      dmap->fragment_size)	/* Too late to change */
-    return -EINVAL;
-
-  bytes = fact & 0xffff;
-  count = (fact >> 16) & 0x7fff;
-
-  if (count == 0)
-    count = MAX_SUB_BUFFERS;
-
-  if (bytes < 4 || bytes > 17)	/* <16 || > 512k */
-    return -EINVAL;
-
-  if (count < 2)
-    return -EINVAL;
-
-  if (audio_devs[dev]->min_fragment > 0)
-    if (bytes < audio_devs[dev]->min_fragment)
-      bytes = audio_devs[dev]->min_fragment;
-
-#ifdef OS_DMA_MINBITS
-  if (bytes < OS_DMA_MINBITS)
-    bytes = OS_DMA_MINBITS;
-#endif
-
-  dmap->fragment_size = (1 << bytes);
-  dmap->max_fragments = count;
-
-  if (dmap->fragment_size > audio_devs[dev]->buffsize)
-    dmap->fragment_size = audio_devs[dev]->buffsize;
-
-  if (dmap->fragment_size == audio_devs[dev]->buffsize &&
-      audio_devs[dev]->flags & DMA_AUTOMODE)
-    dmap->fragment_size /= 2;	/* Needs at least 2 buffers */
-
-  dmap->subdivision = 1;	/* Disable SNDCTL_DSP_SUBDIVIDE */
-  if (arg)
-    return ioctl_out (arg, bytes | (count << 16));
-  else
-    return 0;
-}
-
-static int
-get_buffer_pointer (int dev, int chan, struct dma_buffparms *dmap)
+int
+DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap)
 {
 /*
  * Try to approximate the active byte position of the DMA pointer within the
@@ -865,6 +689,7 @@
  */
   int             pos;
   unsigned long   flags;
+  int             chan = dmap->dma;
 
   save_flags (flags);
   cli ();
@@ -873,16 +698,26 @@
   else
     {
       clear_dma_ff (chan);
-      disable_dma (chan);
+      disable_dma (dmap->dma);
       pos = get_dma_residue (chan);
-      if (chan > 3)		/* Word count */
-	pos *= 2;
-      pos = dmap->bytes_in_use - pos - 1;
+      pos = dmap->bytes_in_use - pos;
+
+      if (dmap->flags & DMODE_OUTPUT)
+	{
+	  if (dmap->qhead == 0)
+	    pos %= dmap->bytes_in_use;
+	}
+      else
+	{
+	  if (dmap->qtail == 0)
+	    pos %= dmap->bytes_in_use;
+	}
+
       if (pos < 0)
 	pos = 0;
       if (pos > dmap->bytes_in_use)
 	pos = dmap->bytes_in_use;
-      enable_dma (chan);
+      enable_dma (dmap->dma);
     }
   restore_flags (flags);
   /* printk ("%04x ", pos); */
@@ -890,353 +725,200 @@
   return pos;
 }
 
-
-int
-DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
+/*
+ * DMAbuf_start_devices() is called by the /dev/music driver to start
+ * one or more audio devices at desired moment.
+ */
+static void
+DMAbuf_start_device (int dev)
 {
-  struct dma_buffparms *dmap_out = audio_devs[dev]->dmap_out;
-  struct dma_buffparms *dmap_in = audio_devs[dev]->dmap_in;
-
-  switch (cmd)
-    {
-    case SNDCTL_DSP_GETBLKSIZE:
-      if (!(dmap_out->flags & DMA_ALLOC_DONE))
-	{
-	  reorganize_buffers (dev, dmap_out,
-			      (audio_devs[dev]->open_mode == OPEN_READ));
-	  if (audio_devs[dev]->flags & DMA_DUPLEX &&
-	      audio_devs[dev]->open_mode & OPEN_READ)
-	    reorganize_buffers (dev, dmap_in,
-				(audio_devs[dev]->open_mode == OPEN_READ));
-	}
+  if (audio_devs[dev]->open_mode != 0)
+    if (!audio_devs[dev]->go)
+      {
+	/* OK to start the device */
+	audio_devs[dev]->go = 1;
 
-      if (local)
-	return dmap_out->fragment_size;
-      else
-	return ioctl_out (arg, dmap_out->fragment_size);
-      break;
+	if (audio_devs[dev]->d->trigger)
+	  audio_devs[dev]->d->trigger (dev,
+			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
+      }
+}
 
-    case SNDCTL_DSP_SUBDIVIDE:
-      {
-	int             fact;
-	int             ret;
+void
+DMAbuf_start_devices (unsigned int devmask)
+{
+  int             dev;
 
-	get_user (fact, (int *) arg);
+  for (dev = 0; dev < num_audiodevs; dev++)
+    if (devmask & (1 << dev))
+      DMAbuf_start_device (dev);
+}
 
-	ret = dma_subdivide (dev, dmap_out, arg, fact);
-	if (ret < 0)
-	  return ret;
+int
+DMAbuf_space_in_queue (int dev)
+{
+  int             len, max, tmp;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-	if (audio_devs[dev]->flags & DMA_DUPLEX &&
-	    audio_devs[dev]->open_mode & OPEN_READ)
-	  ret = dma_subdivide (dev, dmap_in, arg, fact);
+  /* Don't allow touching pages too close to the playing ones */
+  int             lim = dmap->nbufs - 1;
 
-	return ret;
-      }
-      break;
+  if (lim < 2)
+    lim = 2;
 
-    case SNDCTL_DSP_SETFRAGMENT:
-      {
-	int             fact;
-	int             ret;
+  if (dmap->qlen >= lim)	/* No space at all */
+    return 0;
 
-	get_user (fact, (int *) arg);
-	ret = dma_set_fragment (dev, dmap_out, arg, fact);
-	if (ret < 0)
-	  return ret;
-
-	if (audio_devs[dev]->flags & DMA_DUPLEX &&
-	    audio_devs[dev]->open_mode & OPEN_READ)
-	  ret = dma_set_fragment (dev, dmap_in, arg, fact);
+  /*
+   * Verify that there are no more pending buffers than the limit
+   * defined by the process.
+   */
 
-	return ret;
-      }
-      break;
+  max = dmap->max_fragments;
+  len = dmap->qlen;
 
-    case SNDCTL_DSP_GETISPACE:
-    case SNDCTL_DSP_GETOSPACE:
-      if (!local)
-	return -EINVAL;
-      else
-	{
-	  struct dma_buffparms *dmap = dmap_out;
+  if (audio_devs[dev]->d->local_qlen)
+    {
+      tmp = audio_devs[dev]->d->local_qlen (dev);
+      if (tmp && len)
+	tmp--;			/*
+				 * This buffer has been counted twice
+				 */
+      len += tmp;
+    }
 
-	  audio_buf_info *info = (audio_buf_info *) arg;
+  if (len >= max)
+    return 0;
+  return 1;
+}
 
-	  if (cmd == SNDCTL_DSP_GETISPACE &&
-	      !(audio_devs[dev]->open_mode & OPEN_READ))
-	    return -EINVAL;
+static int
+output_sleep (int dev, int dontblock)
+{
+  int             tmout;
+  int             err = 0;
+  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-	  if (cmd == SNDCTL_DSP_GETISPACE && audio_devs[dev]->flags & DMA_DUPLEX)
-	    dmap = dmap_in;
+  if (dontblock)
+    {
+      return -EAGAIN;
+    }
 
-	  if (dmap->mapping_flags & DMA_MAP_MAPPED)
-	    return -EINVAL;
+  if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
+    {
+      return -EAGAIN;
+    }
 
-	  if (!(dmap->flags & DMA_ALLOC_DONE))
-	    reorganize_buffers (dev, dmap, (cmd == SNDCTL_DSP_GETISPACE));
+  /*
+   * Wait for free space
+   */
+  if (!audio_devs[dev]->go)
+    tmout = 0;
+  else
+    {
+      tmout =
+	(dmap->fragment_size * HZ) / dmap->data_rate;
 
-	  info->fragstotal = dmap->nbufs;
+      tmout += HZ / 10;		/* Some safety distance */
 
-	  if (cmd == SNDCTL_DSP_GETISPACE)
-	    info->fragments = dmap->qlen;
-	  else
-	    {
-	      if (!space_in_queue (dev))
-		info->fragments = 0;
-	      else
-		{
-		  info->fragments = dmap->nbufs - dmap->qlen;
-		  if (audio_devs[dev]->d->local_qlen)
-		    {
-		      int             tmp = audio_devs[dev]->d->local_qlen (dev);
-
-		      if (tmp && info->fragments)
-			tmp--;	/*
-				   * This buffer has been counted twice
-				 */
-		      info->fragments -= tmp;
-		    }
-		}
-	    }
+      if (tmout < (HZ / 2))
+	tmout = HZ / 2;
+      if (tmout > 20 * HZ)
+	tmout = 20 * HZ;
+    }
 
-	  if (info->fragments < 0)
-	    info->fragments = 0;
-	  else if (info->fragments > dmap->nbufs)
-	    info->fragments = dmap->nbufs;
+  if ((current->signal & ~current->blocked))
+    return -EIO;
 
-	  info->fragsize = dmap->fragment_size;
-	  info->bytes = info->fragments * dmap->fragment_size;
 
-	  if (cmd == SNDCTL_DSP_GETISPACE && dmap->qlen)
-	    info->bytes -= dmap->counts[dmap->qhead];
-	}
-      return 0;
+  {
+    unsigned long   tlimit;
 
-    case SNDCTL_DSP_SETTRIGGER:
+    if (tmout)
+      current->timeout = tlimit = jiffies + (tmout);
+    else
+      tlimit = (unsigned long) -1;
+    out_sleep_flag[dev].opts = WK_SLEEP;
+    interruptible_sleep_on (&out_sleeper[dev]);
+    if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
       {
-	unsigned long   flags;
-
-	int             bits;
-	int             changed;
-
-	get_user (bits, (int *) arg);
-	bits &= audio_devs[dev]->open_mode;
-
-	if (audio_devs[dev]->d->trigger == NULL)
-	  return -EINVAL;
-
-	if (!(audio_devs[dev]->flags & DMA_DUPLEX))
-	  if ((bits & PCM_ENABLE_INPUT) && (bits & PCM_ENABLE_OUTPUT))
-	    {
-	      printk ("Sound: Device doesn't have full duplex capability\n");
-	      return -EINVAL;
-	    }
-
-	save_flags (flags);
-	cli ();
-	changed = audio_devs[dev]->enable_bits ^ bits;
-
-	if ((changed & bits) & PCM_ENABLE_INPUT && audio_devs[dev]->go)
-	  {
-	    int             err;
-
-	    reorganize_buffers (dev, dmap_in, 1);
-
-	    if ((err = audio_devs[dev]->d->prepare_for_input (dev,
-			       dmap_in->fragment_size, dmap_in->nbufs)) < 0)
-	      return -err;
-
-	    audio_devs[dev]->enable_bits = bits;
-	    activate_recording (dev, dmap_in);
-	  }
-
-
-	if ((changed & bits) & PCM_ENABLE_OUTPUT &&
-	    dmap_out->mapping_flags & DMA_MAP_MAPPED &&
-	    audio_devs[dev]->go)
-	  {
-
-	    if (!(dmap_out->flags & DMA_ALLOC_DONE))
-	      {
-		reorganize_buffers (dev, dmap_out, 0);
-	      }
-
-	    ;
-	    dmap_out->counts[dmap_out->qhead] = dmap_out->fragment_size;
-	    launch_output (dev, dmap_out);
-	    ;
-	  }
-
-	audio_devs[dev]->enable_bits = bits;
-	if (changed && audio_devs[dev]->d->trigger)
-	  {
-	    audio_devs[dev]->d->trigger (dev, bits * audio_devs[dev]->go);
-	  }
-	restore_flags (flags);
-      }
-    case SNDCTL_DSP_GETTRIGGER:
-      return ioctl_out (arg, audio_devs[dev]->enable_bits);
-      break;
-
-    case SNDCTL_DSP_SETSYNCRO:
-
-      if (!audio_devs[dev]->d->trigger)
-	return -EINVAL;
-
-      audio_devs[dev]->d->trigger (dev, 0);
-      audio_devs[dev]->go = 0;
-      return 0;
-      break;
-
-    case SNDCTL_DSP_GETIPTR:
-      {
-	count_info      info;
-	unsigned long   flags;
-
-	if (!(audio_devs[dev]->open_mode & OPEN_READ))
-	  return -EINVAL;
-
-	save_flags (flags);
-	cli ();
-	info.bytes = audio_devs[dev]->dmap_in->byte_counter;
-	info.ptr = get_buffer_pointer (dev, audio_devs[dev]->dmachan2, audio_devs[dev]->dmap_in);
-	info.blocks = audio_devs[dev]->dmap_in->qlen;
-	info.bytes += info.ptr;
-	{
-	  char           *fixit = (char *) &info;
-
-	  copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-	};
-
-	if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED)
-	  audio_devs[dev]->dmap_in->qlen = 0;	/* Acknowledge interrupts */
-	restore_flags (flags);
-	return 0;
+	if (jiffies >= tlimit)
+	  out_sleep_flag[dev].opts |= WK_TIMEOUT;
       }
-      break;
-
-    case SNDCTL_DSP_GETOPTR:
-      {
-	count_info      info;
-	unsigned long   flags;
-
-	if (!(audio_devs[dev]->open_mode & OPEN_WRITE))
-	  return -EINVAL;
-
-	save_flags (flags);
-	cli ();
-	info.bytes = audio_devs[dev]->dmap_out->byte_counter;
-	info.ptr = get_buffer_pointer (dev, audio_devs[dev]->dmachan1, audio_devs[dev]->dmap_out);
-	info.blocks = audio_devs[dev]->dmap_out->qlen;
-	info.bytes += info.ptr;
-	{
-	  char           *fixit = (char *) &info;
-
-	  copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-	};
-
-	if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
-	  audio_devs[dev]->dmap_out->qlen = 0;	/* Acknowledge interrupts */
-	restore_flags (flags);
-	return 0;
-      }
-      break;
-
-
-    case SNDCTL_DSP_POST:
-      ;
-      if (audio_devs[dev]->dmap_out->qlen > 0)
-	if (!(audio_devs[dev]->dmap_out->flags & DMA_ACTIVE))
-	  launch_output (dev, audio_devs[dev]->dmap_out);
+    out_sleep_flag[dev].opts &= ~WK_SLEEP;
+  };
+  if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
+    {
+      printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
+      err = EIO;
       ;
-      return 0;
-      break;
-
-    default:
-      return audio_devs[dev]->d->ioctl (dev, cmd, arg, local);
+      dma_reset_output (dev);
+    }
+  else if ((current->signal & ~current->blocked))
+    {
+      err = EINTR;
     }
 
-}
-
-/*
- * DMAbuf_start_devices() is called by the /dev/music driver to start
- * one or more audio devices at desired moment.
- */
-
-void
-DMAbuf_start_device (int dev)
-{
-  if (audio_devs[dev]->open_mode != 0)
-    if (!audio_devs[dev]->go)
-      {
-	/* OK to start the device */
-	audio_devs[dev]->go = 1;
-
-	if (audio_devs[dev]->d->trigger)
-	  audio_devs[dev]->d->trigger (dev,
-			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
-      }
-}
-
-void
-DMAbuf_start_devices (unsigned int devmask)
-{
-  int             dev;
-
-  for (dev = 0; dev < num_audiodevs; dev++)
-    if (devmask & (1 << dev))
-      DMAbuf_start_device (dev);
+  return err;
 }
 
 static int
-space_in_queue (int dev)
+find_output_space (int dev, char **buf, int *size)
 {
-  int             len, max, tmp;
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
+  unsigned long   flags;
+  unsigned long   offs, active_offs;
+  long            len;
 
-  /* Don't allow touching pages too close to the playing ones */
-  int             lim = dmap->nbufs - 1;
+  if (!DMAbuf_space_in_queue (dev))
+    return 0;
 
-  if (lim < 2)
-    lim = 2;
+  save_flags (flags);
+  cli ();
 
-  if (dmap->qlen >= lim)	/* No space at all */
-    return 0;
+#ifdef BE_CONSERVATIVE
+  active_offs = dmap->byte_counter + (dmap->qhead + 1) * dmap->fragment_size;
+#else
+  active_offs = ((dmap->byte_counter + DMAbuf_get_buffer_pointer (dev, dmap)));
+  /* / dmap->fragment_size) * dmap->fragment_size; */
 
-  /*
-   * Verify that there are no more pending buffers than the limit
-   * defined by the process.
-   */
+#endif
 
-  max = dmap->max_fragments;
-  len = dmap->qlen;
+  offs = (dmap->user_counter % dmap->bytes_in_use) & ~3;
+  *buf = dmap->raw_buf + offs;
 
-  if (audio_devs[dev]->d->local_qlen)
+  len = active_offs + dmap->bytes_in_use - dmap->user_counter;	/* Number of unused bytes in buffer */
+
+  if ((offs + len) > dmap->bytes_in_use)
+    len = dmap->bytes_in_use - offs;
+
+  if (len < 0)
     {
-      tmp = audio_devs[dev]->d->local_qlen (dev);
-      if (tmp && len)
-	tmp--;			/*
-				 * This buffer has been counted twice
-				 */
-      len += tmp;
+      restore_flags (flags);
+      return 0;
     }
 
-  if (len >= max)
-    return 0;
-  return 1;
+  if ((offs + len) > dmap->bytes_in_use)
+    len = dmap->bytes_in_use - offs;
+
+  *size = len & ~3;
+
+  restore_flags (flags);
+
+  return (len > 0);
 }
 
 int
 DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
 {
   unsigned long   flags;
-  int             abort, err = EIO;
+  int             err = EIO;
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
 
-  dmap->flags &= ~DMA_CLEAN;
+  if (dmap->needs_reorg)
+    reorganize_buffers (dev, dmap, 0);
 
-  if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
+  if (dmap->mapping_flags & DMA_MAP_MAPPED)
     {
       printk ("Sound: Can't write to mmapped device (3)\n");
       return -EINVAL;
@@ -1247,204 +929,77 @@
       DMAbuf_reset (dev);
       dmap->dma_mode = DMODE_NONE;
     }
-  else if (dmap->flags & DMA_RESTART)	/* Restart buffering */
-    {
-      DMAbuf_sync (dev);
-      dma_reset_output (dev);
-      dmap->dma_mode = DMODE_NONE;
-    }
-
-  dmap->flags &= ~(DMA_RESTART | DMA_EMPTY);
 
-  if (!dmap->dma_mode)
-    {
-      int             err;
-
-      reorganize_buffers (dev, dmap, 0);
-      dmap->dma_mode = DMODE_OUTPUT;
-      if ((err = audio_devs[dev]->d->prepare_for_output (dev,
-				     dmap->fragment_size, dmap->nbufs)) < 0)
-	return err;
-    }
+  dmap->dma_mode = DMODE_OUTPUT;
 
   save_flags (flags);
   cli ();
 
-  abort = 0;
-  while (!space_in_queue (dev) &&
-	 !abort)
+  while (!find_output_space (dev, buf, size))
     {
-      int             tmout;
-
-      if (dontblock)
+      if ((err = output_sleep (dev, dontblock)) < 0)
 	{
 	  restore_flags (flags);
-	  return -EAGAIN;
-	}
-
-      if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT))
-	{
-	  restore_flags (flags);
-	  return -EAGAIN;
-	}
-
-      /*
-       * Wait for free space
-       */
-      if (!audio_devs[dev]->go)
-	tmout = 0;
-      else
-	{
-	  tmout =
-	    (dmap->fragment_size * HZ) / dmap->data_rate;
-
-	  tmout += HZ / 10;	/* Some safety distance */
-
-	  if (tmout < (HZ / 2))
-	    tmout = HZ / 2;
-	  if (tmout > 20 * HZ)
-	    tmout = 20 * HZ;
-	}
-
-
-      {
-	unsigned long   tlimit;
-
-	if (tmout)
-	  current->timeout = tlimit = jiffies + (tmout);
-	else
-	  tlimit = (unsigned long) -1;
-	out_sleep_flag[dev].opts = WK_SLEEP;
-	interruptible_sleep_on (&out_sleeper[dev]);
-	if (!(out_sleep_flag[dev].opts & WK_WAKEUP))
-	  {
-	    if (jiffies >= tlimit)
-	      out_sleep_flag[dev].opts |= WK_TIMEOUT;
-	  }
-	out_sleep_flag[dev].opts &= ~WK_SLEEP;
-      };
-      if ((out_sleep_flag[dev].opts & WK_TIMEOUT))
-	{
-	  printk ("Sound: DMA (output) timed out - IRQ/DRQ config error?\n");
-	  err = EIO;
-	  abort = 1;
-	  ;
-	  if (audio_devs[dev]->flags & DMA_AUTOMODE)
-	    dmap->flags |= DMA_RESTART;
-	  else
-	    dmap->flags &= ~DMA_RESTART;
-	  audio_devs[dev]->d->reset (dev);
-	}
-      else if ((current->signal & ~current->blocked))
-	{
-	  err = EINTR;
-	  abort = 1;
+	  return err;
 	}
     }
-  restore_flags (flags);
 
-  if (!space_in_queue (dev))
+  if (!find_output_space (dev, buf, size))
     {
-      return -err;		/* Caught a signal ? */
+      restore_flags (flags);
+      return -EIO;		/* Caught a signal ? */
     }
+  restore_flags (flags);
 
-  *buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size;
-  *size = dmap->fragment_size;
-  dmap->counts[dmap->qtail] = 0;
-
-  return dmap->qtail;
-}
-
-int
-DMAbuf_get_curr_buffer (int dev, int *buf_no, char **dma_buf, int *buf_ptr, int *buf_size)
-{
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
-  if (dmap->cfrag < 0)
-    return -1;
-
-  *dma_buf = dmap->raw_buf + dmap->qtail * dmap->fragment_size;
-  *buf_ptr = dmap->counts[dmap->qtail];
-  *buf_size = dmap->fragment_size;
-  return *buf_no = dmap->cfrag;
-}
-
-int
-DMAbuf_set_count (int dev, int buff_no, int l)
-{
-  struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-
-  if (buff_no == dmap->qtail)
-    {
-      dmap->cfrag = buff_no;
-      dmap->counts[buff_no] = l;
-    }
-  else
-    dmap->cfrag = -1;
+  dmap->flags |= DMA_DIRTY;
   return 0;
 }
 
 int
-DMAbuf_start_output (int dev, int buff_no, int l)
+DMAbuf_move_wrpointer (int dev, int l)
 {
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_out;
-  int             restart = 0;
-  int             post = dmap->flags & DMA_POST;
+  unsigned long   ptr = (dmap->user_counter / dmap->fragment_size)
+  * dmap->fragment_size;
+
+  unsigned long   end_ptr, p, prev_count;
+  int             post = (dmap->flags & DMA_POST);
 
   ;
 
   dmap->flags &= ~DMA_POST;
 
   dmap->cfrag = -1;
-  if (dmap->flags & DMA_RESTART)
-    restart = 1;
 
-/*
- * Bypass buffering if using mmapped access
- */
+  prev_count = dmap->user_counter;
+  dmap->user_counter += l;
 
-  if (audio_devs[dev]->dmap_out->mapping_flags & DMA_MAP_MAPPED)
-    {
-      l = dmap->fragment_size;
-      dmap->counts[dmap->qtail] = l;
-      dmap->flags &= ~DMA_RESTART;
-      dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+  if (dmap->user_counter < prev_count)	/* Wrap? */
+    {				/* Wrap the device counter too */
+      dmap->byte_counter %= dmap->bytes_in_use;
     }
-  else
-    {
 
-      dmap->qlen++;
+  end_ptr = (dmap->user_counter / dmap->fragment_size) * dmap->fragment_size;
 
-      dmap->counts[dmap->qtail] = l;
-      if (l < dmap->fragment_size)
-	{
-	  int             p = dmap->fragment_size * dmap->qtail;
-
-#if defined(PPC) || defined(sparc) || defined(HPPA)
-	  dmap->neutral_byte = dmap->raw_buf[p + l - 2];
-#else
-	  dmap->neutral_byte = dmap->raw_buf[p + l - 1];
-#endif
-
-	  memset (dmap->raw_buf + p + l,
-		  dmap->neutral_byte,
-		  dmap->fragment_size - l);
-	}
-      else
-	dmap->neutral_byte =
-	  dmap->raw_buf[dmap->fragment_size * dmap->qtail - 1];
+  p = (dmap->user_counter - 1) % dmap->bytes_in_use;
+  dmap->neutral_byte = dmap->raw_buf[p];
 
+  /* Update the fragment based bookkeeping too */
+  while (ptr < end_ptr)
+    {
+      dmap->counts[dmap->qtail] = dmap->fragment_size;
       dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
+      dmap->qlen++;
+      ptr += dmap->fragment_size;
     }
 
-  /* Delay playback until there are at least two fragments (to prevent hiccup) */
-  if (dmap->qlen > 1 || post ||
-      (dmap->qlen > 0 && dmap->nbufs <= 2) ||
-      (dmap->qlen > 0 && dmap->flags & DMA_SYNCING) ||
-      restart || l != dmap->fragment_size)
-    if (!(dmap->flags & DMA_ACTIVE))
+  dmap->counts[dmap->qtail] = dmap->user_counter - ptr;
+
+  if (!(dmap->flags & DMA_ACTIVE))
+    if (dmap->qlen > 1 ||
+	(dmap->qlen > 0 && (post || dmap->qlen >= dmap->nbufs - 1)))
       {
-	launch_output (dev, dmap);
+	DMAbuf_launch_output (dev, dmap);
       }
 
   ;
@@ -1459,26 +1014,23 @@
 
   if (dma_mode == DMA_MODE_WRITE)
     {
-      chan = audio_devs[dev]->dmachan1;
+      chan = audio_devs[dev]->dmap_out->dma;
       dmap = audio_devs[dev]->dmap_out;
     }
   else
     {
-      chan = audio_devs[dev]->dmachan2;
+      chan = audio_devs[dev]->dmap_in->dma;
       dmap = audio_devs[dev]->dmap_in;
     }
 
   if (dmap->raw_buf == NULL)
     {
-      printk ("sound: DMA buffer == NULL\n");
+      printk ("sound: DMA buffer(1) == NULL\n");
+      printk ("Device %d, chn=%s\n", dev,
+	      (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
       return 0;
     }
 
-  /* Handle cards with non automode DMA in new way */
-  if (physaddr != dmap->raw_buf_phys)	/* Not fragment 0 */
-    return count;
-  count = dmap->bytes_in_use;
-
   if (chan < 0)
     return 0;
 
@@ -1487,79 +1039,55 @@
    * set_dma_addr()
    */
 
-  if (audio_devs[dev]->flags & DMA_AUTOMODE)
-    {				/*
-				 * Auto restart mode. Transfer the whole *
-				 * buffer
-				 */
-      unsigned long   flags;
+  sound_start_dma (dev, dmap, chan, physaddr, count, dma_mode, 0);
 
-      save_flags (flags);
-      cli ();
-      disable_dma (chan);
-      clear_dma_ff (chan);
-      set_dma_mode (chan, dma_mode | DMA_AUTOINIT);
-      set_dma_addr (chan, dmap->raw_buf_phys);
-      set_dma_count (chan, dmap->bytes_in_use);
-      enable_dma (chan);
-      restore_flags (flags);
+  return count;
+}
+
+static int
+local_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
+{
+  int             chan;
+  struct dma_buffparms *dmap;
+
+  if (dma_mode == DMA_MODE_WRITE)
+    {
+      chan = audio_devs[dev]->dmap_out->dma;
+      dmap = audio_devs[dev]->dmap_out;
     }
   else
     {
-      unsigned long   flags;
-
-      save_flags (flags);
-      cli ();
-      disable_dma (chan);
-      clear_dma_ff (chan);
-      set_dma_mode (chan, dma_mode);
-      set_dma_addr (chan, physaddr);
-      set_dma_count (chan, count);
-      enable_dma (chan);
-      restore_flags (flags);
+      chan = audio_devs[dev]->dmap_in->dma;
+      dmap = audio_devs[dev]->dmap_in;
     }
 
-  return count;
-}
-
-void
-DMAbuf_init (void)
-{
-  int             dev;
+  if (dmap->raw_buf == NULL)
+    {
+      printk ("sound: DMA buffer(2) == NULL\n");
+      printk ("Device %d, chn=%s\n", dev,
+	      (dmap == audio_devs[dev]->dmap_out) ? "out" : "in");
+      return 0;
+    }
 
+  if (chan < 0)
+    return 0;
 
   /*
-     * NOTE! This routine could be called several times.
+   * The count must be one less than the actual size. This is handled by
+   * set_dma_addr()
    */
 
-  for (dev = 0; dev < num_audiodevs; dev++)
-    if (audio_devs[dev]->dmap_out == NULL)
-      {
-	audio_devs[dev]->dmap_out =
-	  audio_devs[dev]->dmap_in =
-	  &dmaps[ndmaps++];
-
-	if (audio_devs[dev]->flags & DMA_DUPLEX)
-	  audio_devs[dev]->dmap_in =
-	    &dmaps[ndmaps++];
-      }
+  sound_start_dma (dev, dmap, chan, dmap->raw_buf_phys, dmap->bytes_in_use, dma_mode, 1);
+  dmap->flags |= DMA_STARTED;
+
+  return count;
 }
 
 static void
-force_restart (int dev, struct dma_buffparms *dmap)
+finish_output_interrupt (int dev, struct dma_buffparms *dmap)
 {
   unsigned long   flags;
 
-  if ((audio_devs[dev]->flags & DMA_DUPLEX) &&
-      audio_devs[dev]->d->halt_output)
-    audio_devs[dev]->d->halt_output (dev);
-  else
-    audio_devs[dev]->d->halt_xfer (dev);
-
-  dmap->flags &= ~(DMA_ACTIVE | DMA_STARTED);
-  dmap->flags |= DMA_RESTART;
-  dmap->qlen = dmap->qhead = dmap->qtail = 0;
-
   save_flags (flags);
   cli ();
   if ((out_sleep_flag[dev].opts & WK_SLEEP))
@@ -1590,111 +1118,98 @@
   int             this_fragment;
 
 #ifdef OS_DMA_INTR
-  if (audio_devs[dev]->dmachan1 >= 0)
-    sound_dma_intr (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
+  if (audio_devs[dev]->dmap_out->dma >= 0)
+    sound_dma_intr (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
 #endif
 
   if (dmap->raw_buf == NULL)
     {
-      printk ("Sound: Fatal error. Audio interrupt after freeing buffers.\n");
+      printk ("Sound: Fatal error. Audio interrupt (%d) after freeing buffers.\n", dev);
       return;
     }
 
   if (dmap->mapping_flags & DMA_MAP_MAPPED)
     {
+      unsigned long   prev_counter = dmap->byte_counter;
+
       /* mmapped access */
       dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
       if (dmap->qhead == 0)	/* Wrapped */
-	dmap->byte_counter += dmap->bytes_in_use;
-      dmap->qlen++;		/* Yes increment it (don't decrement) */
-      dmap->flags &= ~DMA_ACTIVE;
-      dmap->counts[dmap->qhead] = dmap->fragment_size;
+	{
+	  dmap->byte_counter += dmap->bytes_in_use;
+	  if (dmap->byte_counter < prev_counter)	/* Overflow */
+	    {
+	      dmap->user_counter %= dmap->bytes_in_use;
+	    }
+	}
 
+      dmap->qlen++;		/* Yes increment it (don't decrement) */
       if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
-	{
-	  if (dmap->counts[dmap->qhead] == 0)
-	    dmap->counts[dmap->qhead] = dmap->fragment_size;
+	dmap->flags &= ~DMA_ACTIVE;
+      dmap->counts[dmap->qhead] = dmap->fragment_size;
 
-	  audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys +
-					  dmap->qhead * dmap->fragment_size,
-					    dmap->counts[dmap->qhead], 1,
-				  !(audio_devs[dev]->flags & DMA_AUTOMODE));
-	  if (audio_devs[dev]->d->trigger)
-	    audio_devs[dev]->d->trigger (dev,
-			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
-	}
-      dmap->flags |= DMA_ACTIVE;
+      DMAbuf_launch_output (dev, dmap);
+      finish_output_interrupt (dev, dmap);
+      return;
     }
-  else if (event_type != 2)
+
+  if (event_type == 2)
     {
-      if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs)
-	{
-	  printk ("\nSound: Audio queue3 corrupted for dev%d (%d/%d)\n",
-		  dev, dmap->qlen, dmap->nbufs);
-	  force_restart (dev, dmap);
-	  return;
-	}
+      finish_output_interrupt (dev, dmap);
+      return;
+    }
 
-      save_flags (flags);
-      cli ();
+  if (dmap->qlen > dmap->nbufs)
+    dmap->qlen = dmap->nbufs;
 
-      dmap->qlen--;
-      this_fragment = dmap->qhead;
-      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+  if (dmap->qlen <= 0)
+    {
+      finish_output_interrupt (dev, dmap);
+      return;
+    }
 
-      if (dmap->qhead == 0)	/* Wrapped */
-	dmap->byte_counter += dmap->bytes_in_use;
-      dmap->flags &= ~DMA_ACTIVE;
+  save_flags (flags);
+  cli ();
 
-      if (event_type == 1 && dmap->qlen < 1)
-	{
-	  dmap->underrun_count++;
+  dmap->qlen--;
+  this_fragment = dmap->qhead;
+  dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
 
-	  dmap->qlen = 0;
-	  force_restart (dev, dmap);
-	}
+  if (dmap->qhead == 0)		/* Wrapped */
+    {
+      unsigned long   prev_counter = dmap->byte_counter;
 
-      if (dmap->qlen)
+      dmap->byte_counter += dmap->bytes_in_use;
+      if (dmap->byte_counter < prev_counter)	/* Overflow */
 	{
-	  if (dmap->flags & DMA_CLEAN)
-	    {
-	      int             p = dmap->fragment_size * this_fragment;
-
-	      memset (dmap->raw_buf + p,
-		      dmap->neutral_byte,
-		      dmap->fragment_size);
-	    }
+	  dmap->user_counter %= dmap->bytes_in_use;
+	}
+    }
 
-	  if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
-	    {
+  if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
+    dmap->flags &= ~DMA_ACTIVE;
 
-	      if (dmap->counts[dmap->qhead] == 0)
-		dmap->counts[dmap->qhead] = dmap->fragment_size;
+  if (event_type == 1 && dmap->qlen < 1)
+    {
+      dmap->underrun_count++;
 
-	      audio_devs[dev]->d->output_block (dev, dmap->raw_buf_phys +
-					  dmap->qhead * dmap->fragment_size,
-						dmap->counts[dmap->qhead], 1,
-				  !(audio_devs[dev]->flags & DMA_AUTOMODE));
-	      if (audio_devs[dev]->d->trigger)
-		audio_devs[dev]->d->trigger (dev,
-			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
-	    }
-	  dmap->flags |= DMA_ACTIVE;
+      dmap->qlen = 0;
+      if (dmap->flags & DMA_DIRTY && dmap->applic_profile != APF_CPUINTENS)
+	{
+	  dmap->flags &= ~DMA_DIRTY;
+	  memset (audio_devs[dev]->dmap_out->raw_buf,
+		  audio_devs[dev]->dmap_out->neutral_byte,
+		  audio_devs[dev]->dmap_out->bytes_in_use);
 	}
+      dmap->user_counter += dmap->fragment_size;
+      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
+    }
 
-      restore_flags (flags);
-    }				/* event_type != 2 */
+  if (dmap->qlen > 0)
+    DMAbuf_launch_output (dev, dmap);
 
-  save_flags (flags);
-  cli ();
-  if ((out_sleep_flag[dev].opts & WK_SLEEP))
-    {
-      {
-	out_sleep_flag[dev].opts = WK_WAKEUP;
-	wake_up (&out_sleeper[dev]);
-      };
-    }
   restore_flags (flags);
+  finish_output_interrupt (dev, dmap);
 }
 
 void
@@ -1704,8 +1219,8 @@
   struct dma_buffparms *dmap = audio_devs[dev]->dmap_in;
 
 #ifdef OS_DMA_INTR
-  if (audio_devs[dev]->dmachan2 >= 0)
-    sound_dma_intr (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2);
+  if (audio_devs[dev]->dmap_in->dma >= 0)
+    sound_dma_intr (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmap_in->dma);
 #endif
 
   if (dmap->raw_buf == NULL)
@@ -1718,15 +1233,26 @@
     {
       dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
       if (dmap->qtail == 0)	/* Wrapped */
-	dmap->byte_counter += dmap->bytes_in_use;
+	{
+	  unsigned long   prev_counter = dmap->byte_counter;
+
+	  dmap->byte_counter += dmap->bytes_in_use;
+	  if (dmap->byte_counter < prev_counter)	/* Overflow */
+	    {
+	      dmap->user_counter %= dmap->bytes_in_use;
+	    }
+	}
       dmap->qlen++;
 
       if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
 	{
+	  if (dmap->needs_reorg)
+	    reorganize_buffers (dev, dmap, 0);
+	  local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+			   DMA_MODE_READ);
 	  audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
 					   dmap->qtail * dmap->fragment_size,
-					   dmap->fragment_size, 1,
-				  !(audio_devs[dev]->flags & DMA_AUTOMODE));
+					   dmap->fragment_size, 1);
 	  if (audio_devs[dev]->d->trigger)
 	    audio_devs[dev]->d->trigger (dev,
 			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
@@ -1734,26 +1260,13 @@
 
       dmap->flags |= DMA_ACTIVE;
     }
-  else if (dmap->qlen == (dmap->nbufs - 1))
+  else if (dmap->qlen >= (dmap->nbufs - 1))
     {
       /* printk ("Sound: Recording overrun\n"); */
       dmap->underrun_count++;
 
-      if (audio_devs[dev]->flags & DMA_AUTOMODE)
-	{
-	  /* Force restart on next read */
-	  if ((audio_devs[dev]->flags & DMA_DUPLEX) &&
-	      audio_devs[dev]->d->halt_input)
-	    audio_devs[dev]->d->halt_input (dev);
-	  else
-	    audio_devs[dev]->d->halt_xfer (dev);
-
-	  dmap->flags &= ~DMA_ACTIVE;
-	  if (audio_devs[dev]->flags & DMA_AUTOMODE)
-	    dmap->flags |= DMA_RESTART;
-	  else
-	    dmap->flags &= ~DMA_RESTART;
-	}
+      /* Just throw away the oldest fragment but keep the engine running */
+      dmap->qhead = (dmap->qhead + 1) % dmap->nbufs;
     }
   else
     {
@@ -1763,15 +1276,26 @@
 		dev, dmap->qlen, dmap->nbufs);
       dmap->qtail = (dmap->qtail + 1) % dmap->nbufs;
       if (dmap->qtail == 0)	/* Wrapped */
-	dmap->byte_counter += dmap->bytes_in_use;
+	{
+	  unsigned long   prev_counter = dmap->byte_counter;
+
+	  dmap->byte_counter += dmap->bytes_in_use;
+	  if (dmap->byte_counter < prev_counter)	/* Overflow */
+	    {
+	      dmap->user_counter %= dmap->bytes_in_use;
+	    }
+	}
     }
 
   if (!(audio_devs[dev]->flags & DMA_AUTOMODE))
     {
+      if (dmap->needs_reorg)
+	reorganize_buffers (dev, dmap, 0);
+      local_start_dma (dev, dmap->raw_buf_phys, dmap->bytes_in_use,
+		       DMA_MODE_READ);
       audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys +
 				       dmap->qtail * dmap->fragment_size,
-				       dmap->fragment_size, 1,
-				  !(audio_devs[dev]->flags & DMA_AUTOMODE));
+				       dmap->fragment_size, 1);
       if (audio_devs[dev]->d->trigger)
 	audio_devs[dev]->d->trigger (dev,
 			audio_devs[dev]->enable_bits * audio_devs[dev]->go);
@@ -1798,9 +1322,8 @@
  *    NOTE!  This routine opens only the primary DMA channel (output).
  */
 
-  int             chan = audio_devs[dev]->dmachan1;
+  int             chan = audio_devs[dev]->dmap_out->dma;
   int             err;
-  unsigned long   flags;
 
   if ((err = open_dmap (dev, OPEN_READWRITE, audio_devs[dev]->dmap_out, chan)) < 0)
     {
@@ -1812,6 +1335,8 @@
 
   if (chan >= 0)
     {
+      unsigned long   flags;
+
       save_flags (flags);
       cli ();
       disable_dma (chan);
@@ -1825,167 +1350,147 @@
 void
 DMAbuf_close_dma (int dev)
 {
-  DMAbuf_reset_dma (dev);
-  close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1);
+  close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmap_out->dma);
 }
 
 void
-DMAbuf_reset_dma (int dev)
+DMAbuf_init (void)
 {
+  int             dev;
+
+  /*
+     * NOTE! This routine could be called several times.
+   */
+
+  for (dev = 0; dev < num_audiodevs; dev++)
+    if (audio_devs[dev]->dmap_out == NULL)
+      {
+	if (audio_devs[dev]->d == NULL)
+	  panic ("OSS: audio_devs[%d]->d == NULL\n", dev);
+	if (audio_devs[dev]->parent_dev)
+	  {			/* Use DMA map of the parent dev */
+	    int             parent = audio_devs[dev]->parent_dev - 1;
+
+	    audio_devs[dev]->dmap_out = audio_devs[parent]->dmap_out;
+	    audio_devs[dev]->dmap_in = audio_devs[parent]->dmap_in;
+	  }
+	else
+	  {
+	    audio_devs[dev]->dmap_out =
+	      audio_devs[dev]->dmap_in =
+	      &dmaps[ndmaps++];
+
+	    if (audio_devs[dev]->flags & DMA_DUPLEX)
+	      audio_devs[dev]->dmap_in =
+		&dmaps[ndmaps++];
+	  }
+      }
 }
 
-unsigned int
-DMAbuf_poll (kdev_t dev, struct fileinfo *file, poll_table * wait)
+int
+DMAbuf_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
 {
-  unsigned int mask = 0;
   struct dma_buffparms *dmap;
   unsigned long   flags;
 
-  save_flags (flags);
-  cli ();
-
-  in_sleep_flag[dev].opts = WK_SLEEP;
-  poll_wait (&in_sleeper[dev], wait);
-  out_sleep_flag[dev].opts = WK_SLEEP;
-  poll_wait (&out_sleeper[dev], wait);
-
-  restore_flags (flags);
-
-/* sel_in */
-  dmap = audio_devs[dev]->dmap_in;
-  if (!(audio_devs[dev]->open_mode))
-    goto sel_out;
-  if (dmap->mapping_flags & DMA_MAP_MAPPED) {
-    if (dmap->qlen)
-      mask |= POLLIN | POLLRDNORM;
-    goto sel_out;
-  }
-  if (dmap->dma_mode != DMODE_INPUT) {
-    if (dmap->dma_mode == DMODE_NONE &&
-	audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT &&
-	!dmap->qlen &&
-	audio_devs[dev]->go) {
-      unsigned long   flags;
+  switch (sel_type)
+    {
+    case SEL_IN:
+      if (!(audio_devs[dev]->open_mode))
+	return 0;
 
-      save_flags (flags);
-      cli ();
-      activate_recording (dev, dmap);
-      restore_flags (flags);
-    }
-    goto sel_out;
-  }
-  if (!dmap->qlen)
-    goto sel_out;
-  mask |= POLLIN | POLLRDNORM;
+      dmap = audio_devs[dev]->dmap_in;
 
- sel_out:
-  dmap = audio_devs[dev]->dmap_out;
+      if (dmap->mapping_flags & DMA_MAP_MAPPED)
+	{
+	  if (dmap->qlen)
+	    return 1;
 
-  if (dmap->mapping_flags & DMA_MAP_MAPPED) {
-    if (dmap->qlen)
-      mask |= POLLOUT | POLLWRNORM;
-    goto sel_ex;
-  }
-  if (dmap->dma_mode == DMODE_INPUT)
-    goto sel_ex;
-  if (dmap->dma_mode == DMODE_NONE) {
-    mask |= POLLOUT | POLLWRNORM;
-    goto sel_ex;
-  }
-  if (!space_in_queue (dev))
-    goto sel_ex;
-  mask |= POLLOUT | POLLWRNORM;
+	  save_flags (flags);
+	  cli ();
 
-sel_ex:
-  return mask;
-}
+	  in_sleep_flag[dev].opts = WK_SLEEP;
+	  poll_wait (&in_sleeper[dev], wait);
+	  restore_flags (flags);
+	  return 0;
+	}
 
+      if (dmap->dma_mode != DMODE_INPUT)
+	{
+	  if (dmap->dma_mode == DMODE_NONE &&
+	      audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT &&
+	      !dmap->qlen &&
+	      audio_devs[dev]->go)
+	    {
+	      unsigned long   flags;
 
-#else /* CONFIG_AUDIO */
-/*
- * Stub versions if audio services not included
- */
+	      save_flags (flags);
+	      cli ();
+	      DMAbuf_activate_recording (dev, dmap);
+	      restore_flags (flags);
+	    }
+	  return 0;
+	}
 
-int
-DMAbuf_open (int dev, int mode)
-{
-  return -ENXIO;
-}
+      if (!dmap->qlen)
+	{
+	  save_flags (flags);
+	  cli ();
 
-int
-DMAbuf_release (int dev, int mode)
-{
-  return 0;
-}
+	  in_sleep_flag[dev].opts = WK_SLEEP;
+	  poll_wait (&in_sleeper[dev], wait);
+	  restore_flags (flags);
+	  return 0;
+	}
+      return 1;
+      break;
 
-int
-DMAbuf_getwrbuffer (int dev, char **buf, int *size, int dontblock)
-{
-  return -EIO;
-}
+    case SEL_OUT:
+      dmap = audio_devs[dev]->dmap_out;
 
-int
-DMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock)
-{
-  return -EIO;
-}
+      if (dmap->mapping_flags & DMA_MAP_MAPPED)
+	{
+	  if (dmap->qlen)
+	    return 1;
 
-int
-DMAbuf_rmchars (int dev, int buff_no, int c)
-{
-  return -EIO;
-}
+	  save_flags (flags);
+	  cli ();
 
-int
-DMAbuf_start_output (int dev, int buff_no, int l)
-{
-  return -EIO;
-}
+	  out_sleep_flag[dev].opts = WK_SLEEP;
+	  poll_wait (&out_sleeper[dev], wait);
+	  restore_flags (flags);
+	  return 0;
+	}
 
-int
-DMAbuf_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
-{
-  return -EIO;
-}
+      if (dmap->dma_mode == DMODE_INPUT)
+	{
+	  return 0;
+	}
 
-void
-DMAbuf_init (void)
-{
-}
+      if (dmap->dma_mode == DMODE_NONE)
+	{
+	  return 1;
+	}
 
-int
-DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode)
-{
-  return -EIO;
-}
+      if (!DMAbuf_space_in_queue (dev))
+	{
+	  save_flags (flags);
+	  cli ();
 
-int
-DMAbuf_open_dma (int dev)
-{
-  return -ENXIO;
-}
+	  out_sleep_flag[dev].opts = WK_SLEEP;
+	  poll_wait (&out_sleeper[dev], wait);
+	  restore_flags (flags);
+	  return 0;
+	}
+      return 1;
+      break;
 
-void
-DMAbuf_close_dma (int dev)
-{
-  return;
-}
+    case SEL_EX:
+      return 0;
+    }
 
-void
-DMAbuf_reset_dma (int dev)
-{
-  return;
+  return 0;
 }
 
-void
-DMAbuf_inputintr (int dev)
-{
-  return;
-}
 
-void
-DMAbuf_outputintr (int dev, int underrun_flag)
-{
-  return;
-}
 #endif
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/finetune.h linux/drivers/sound/finetune.h
--- v2.1.27/linux/drivers/sound/finetune.h	Fri Nov 15 00:14:53 1996
+++ linux/drivers/sound/finetune.h	Wed Feb 26 02:34:46 1997
@@ -1,6 +1,6 @@
 #ifdef SEQUENCER_C
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/gus_card.c linux/drivers/sound/gus_card.c
--- v2.1.27/linux/drivers/sound/gus_card.c	Fri Nov 15 00:15:16 1996
+++ linux/drivers/sound/gus_card.c	Wed Feb 26 02:35:11 1997
@@ -4,7 +4,7 @@
  * Detection routine for the Gravis Ultrasound.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/gus_midi.c linux/drivers/sound/gus_midi.c
--- v2.1.27/linux/drivers/sound/gus_midi.c	Fri Nov 15 00:15:16 1996
+++ linux/drivers/sound/gus_midi.c	Wed Feb 26 02:35:11 1997
@@ -4,7 +4,7 @@
  * The low level driver for the GUS Midi Interface.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/gus_vol.c linux/drivers/sound/gus_vol.c
--- v2.1.27/linux/drivers/sound/gus_vol.c	Fri Nov 15 00:15:17 1996
+++ linux/drivers/sound/gus_vol.c	Wed Feb 26 02:35:11 1997
@@ -2,7 +2,7 @@
  * gus_vol.c - Compute volume for GUS.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/gus_wave.c linux/drivers/sound/gus_wave.c
--- v2.1.27/linux/drivers/sound/gus_wave.c	Fri Nov 15 00:15:21 1996
+++ linux/drivers/sound/gus_wave.c	Wed Feb 26 02:35:16 1997
@@ -4,7 +4,7 @@
  * Driver for the Gravis UltraSound wave table synth.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -33,6 +33,7 @@
     unsigned long   orig_freq;
     unsigned long   current_freq;
     unsigned long   mode;
+    int             fixed_pitch;
     int             bender;
     int             bender_range;
     int             panning;
@@ -128,7 +129,7 @@
 
 extern int     *gus_osp;
 
-struct voice_info voices[32];
+static struct voice_info voices[32];
 
 static int      freq_div_table[] =
 {
@@ -261,7 +262,7 @@
   restore_flags (flags);
 }
 
-unsigned char
+static unsigned char
 gus_read8 (int reg)
 {				/* Reads from an indirect register (8 bit). Offset 0x80. */
   unsigned long   flags;
@@ -276,7 +277,7 @@
   return val;
 }
 
-unsigned char
+static unsigned char
 gus_look8 (int reg)
 {				/* Reads from an indirect register (8 bit). No additional offset. */
   unsigned long   flags;
@@ -291,7 +292,7 @@
   return val;
 }
 
-void
+static void
 gus_write16 (int reg, unsigned int data)
 {				/* Writes to an indirect register (16 bit) */
   unsigned long   flags;
@@ -307,7 +308,7 @@
   restore_flags (flags);
 }
 
-unsigned short
+static unsigned short
 gus_read16 (int reg)
 {				/* Reads from an indirect register (16 bit). Offset 0x80. */
   unsigned long   flags;
@@ -326,7 +327,7 @@
   return ((hi << 8) & 0xff00) | lo;
 }
 
-unsigned short
+static unsigned short
 gus_look16 (int reg)
 {				/* Reads from an indirect register (16 bit). No additional offset. */
   unsigned long   flags;
@@ -345,7 +346,7 @@
   return ((hi << 8) & 0xff00) | lo;
 }
 
-void
+static void
 gus_write_addr (int reg, unsigned long address, int frac, int is16bit)
 {				/* Writes an 24 bit memory address */
   unsigned long   hold_address;
@@ -541,6 +542,7 @@
   voices[voice].patch_vol = 127;
   voices[voice].expression_vol = 127;
   voices[voice].sample_pending = -1;
+  voices[voice].fixed_pitch = 0;
 }
 
 static void
@@ -1107,11 +1109,7 @@
     {
     case SNDCTL_SYNTH_INFO:
       gus_info.nr_voices = nr_voices;
-      {
-	char           *fixit = (char *) &gus_info;
-
-	copy_to_user (&((char *) arg)[0], fixit, sizeof (gus_info));
-      };
+      memcpy ((&((char *) arg)[0]), (char *) &gus_info, sizeof (gus_info));
       return 0;
       break;
 
@@ -1362,7 +1360,7 @@
       if (voices[voice].volume_irq_mode != VMODE_START_NOTE)
 	{
 	  freq = compute_finetune (voices[voice].orig_freq, value,
-				   voices[voice].bender_range);
+				   voices[voice].bender_range, 0);
 	  voices[voice].current_freq = freq;
 
 	  save_flags (flags);
@@ -1494,10 +1492,17 @@
 
   sample_map[voice] = sample;
 
-  base_note = samples[sample].base_note / 100;	/* Try to avoid overflows */
-  note_freq /= 100;
+  if (voices[voice].fixed_pitch)	/* Fixed pitch */
+    {
+      freq = samples[sample].base_freq;
+    }
+  else
+    {
+      base_note = samples[sample].base_note / 100;
+      note_freq /= 100;
 
-  freq = samples[sample].base_freq * note_freq / base_note;
+      freq = samples[sample].base_freq * note_freq / base_note;
+    }
 
   voices[voice].orig_freq = freq;
 
@@ -1507,7 +1512,7 @@
    */
 
   freq = compute_finetune (voices[voice].orig_freq, voices[voice].bender,
-			   voices[voice].bender_range);
+			   voices[voice].bender_range, 0);
   voices[voice].current_freq = freq;
 
   pan = (samples[sample].panning + voices[voice].panning) / 32;
@@ -1894,7 +1899,6 @@
 	    {
 	      get_user (data, (unsigned char *) &((addr)[sizeof_patch + i]));
 	      if (patch.mode & WAVE_UNSIGNED)
-
 		if (!(patch.mode & WAVE_16_BITS) || (i & 0x01))
 		  data ^= 0x80;	/* Convert to signed */
 	      gus_poke (target + i, data);
@@ -1932,7 +1936,7 @@
 
 	  address = target;
 
-	  if (audio_devs[gus_devnum]->dmachan1 > 3)
+	  if (audio_devs[gus_devnum]->dmap_out->dma > 3)
 	    {
 	      hold_address = address;
 	      address = address >> 1;
@@ -1951,7 +1955,7 @@
 	    dma_command |= 0x80;	/* Invert MSB */
 	  if (patch.mode & WAVE_16_BITS)
 	    dma_command |= 0x40;	/* 16 bit _DATA_ */
-	  if (audio_devs[gus_devnum]->dmachan1 > 3)
+	  if (audio_devs[gus_devnum]->dmap_out->dma > 3)
 	    dma_command |= 0x04;	/* 16 bit DMA _channel_ */
 
 	  gus_write8 (0x41, dma_command);	/* Lets go luteet (=bugs) */
@@ -2217,63 +2221,49 @@
 }
 
 static int
-gus_audio_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
+gus_audio_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
   int             val;
 
   switch (cmd)
     {
     case SOUND_PCM_WRITE_RATE:
-      if (local)
-	return gus_audio_set_speed ((int) arg);
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, gus_audio_set_speed (val));
+      val = *(int *) arg;
+      return (*(int *) arg = gus_audio_set_speed (val));
       break;
 
     case SOUND_PCM_READ_RATE:
-      if (local)
-	return gus_audio_speed;
-      return ioctl_out (arg, gus_audio_speed);
+      return (*(int *) arg = gus_audio_speed);
       break;
 
     case SNDCTL_DSP_STEREO:
-      if (local)
-	return gus_audio_set_channels ((int) arg + 1) - 1;
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, gus_audio_set_channels (val + 1) - 1);
+      val = *(int *) arg;
+      return (*(int *) arg = gus_audio_set_channels (val + 1) - 1);
       break;
 
     case SOUND_PCM_WRITE_CHANNELS:
-      if (local)
-	return gus_audio_set_channels ((int) arg);
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, gus_audio_set_channels (val));
+      val = *(int *) arg;
+      return (*(int *) arg = gus_audio_set_channels (val));
       break;
 
     case SOUND_PCM_READ_CHANNELS:
-      if (local)
-	return gus_audio_channels;
-      return ioctl_out (arg, gus_audio_channels);
+      return (*(int *) arg = gus_audio_channels);
       break;
 
     case SNDCTL_DSP_SETFMT:
-      if (local)
-	return gus_audio_set_bits ((int) arg);
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, gus_audio_set_bits (val));
+      val = *(int *) arg;
+      return (*(int *) arg = gus_audio_set_bits (val));
       break;
 
     case SOUND_PCM_READ_BITS:
-      if (local)
-	return gus_audio_bits;
-      return ioctl_out (arg, gus_audio_bits);
+      return (*(int *) arg = gus_audio_bits);
 
     case SOUND_PCM_WRITE_FILTER:	/* NOT POSSIBLE */
-      return ioctl_out (arg, -EINVAL);
+      return (*(int *) arg = -EINVAL);
       break;
 
     case SOUND_PCM_READ_FILTER:
-      return ioctl_out (arg, -EINVAL);
+      return (*(int *) arg = -EINVAL);
       break;
 
     }
@@ -2530,7 +2520,7 @@
   address = this_one * pcm_bsize;
   address += chn * pcm_banksize;
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     {
       hold_address = address;
       address = address >> 1;
@@ -2547,7 +2537,7 @@
   else
     dma_command |= 0x80;	/* Invert MSB */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     dma_command |= 0x04;	/* 16 bit DMA channel */
 
   gus_write8 (0x41, dma_command);	/* Kick start */
@@ -2578,7 +2568,7 @@
 
 static void
 gus_audio_output_block (int dev, unsigned long buf, int total_count,
-			int intrflag, int restart_dma)
+			int intrflag)
 {
   pcm_current_buf = buf;
   pcm_current_count = total_count;
@@ -2589,7 +2579,7 @@
 
 static void
 gus_audio_start_input (int dev, unsigned long buf, int count,
-		       int intrflag, int restart_dma)
+		       int intrflag)
 {
   unsigned long   flags;
   unsigned char   mode;
@@ -2597,11 +2587,11 @@
   save_flags (flags);
   cli ();
 
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 
   mode = 0xa0;			/* DMA IRQ enabled, invert MSB */
 
-  if (audio_devs[dev]->dmachan2 > 3)
+  if (audio_devs[dev]->dmap_in->dma > 3)
     mode |= 0x04;		/* 16 bit DMA channel */
   if (gus_audio_channels > 1)
     mode |= 0x02;		/* Stereo */
@@ -2657,6 +2647,7 @@
 
   if (gus_audio_bits != 8 && pcm_banksize == (256 * 1024))
     pcm_nblk--;
+  gus_write8 (0x41, 0);		/* Disable GF1 DMA */
 
   return 0;
 }
@@ -2728,20 +2719,10 @@
   gus_audio_prepare_for_input,
   gus_audio_prepare_for_output,
   gus_audio_reset,
-  gus_audio_reset,
   gus_local_qlen,
   gus_copy_from_user
 };
 
-static struct audio_operations gus_audio_operations =
-{
-  "Gravis UltraSound",
-  NEEDS_RESTART,
-  AFMT_U8 | AFMT_S16_LE,
-  NULL,
-  &gus_audio_driver
-};
-
 static void
 guswave_setup_voice (int dev, int voice, int chn)
 {
@@ -2756,7 +2737,11 @@
     (info->controllers[CTL_MAIN_VOLUME] * 100) / (unsigned) 128;
   voices[voice].panning =
     (info->controllers[CTL_PAN] * 2) - 128;
-  voices[voice].bender = info->bender_value;
+  voices[voice].bender = 0;
+  voices[voice].bender_range = info->bender_range;
+
+  if (chn == 9)
+    voices[voice].fixed_pitch = 1;
 }
 
 static void
@@ -2767,7 +2752,7 @@
 
   voices[voice].bender = value - 8192;
   freq = compute_finetune (voices[voice].orig_freq, value - 8192,
-			   voices[voice].bender_range);
+			   voices[voice].bender_range, 0);
   voices[voice].current_freq = freq;
 
   save_flags (flags);
@@ -2825,6 +2810,7 @@
 
 static struct synth_operations guswave_operations =
 {
+  "GUS",
   &gus_info,
   0,
   SYNTH_TYPE_SAMPLE,
@@ -2894,23 +2880,23 @@
 			 SOUND_MASK_SYNTH|SOUND_MASK_PCM)
   if (((cmd >> 8) & 0xff) == 'M')
     {
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
 	switch (cmd & 0xff)
 	  {
 	  case SOUND_MIXER_RECSRC:
-	    get_user (gus_recmask, (int *) arg);
+	    gus_recmask = *(int *) arg;
 	    gus_recmask &= MIX_DEVS;
 	    if (!(gus_recmask & (SOUND_MASK_MIC | SOUND_MASK_LINE)))
 	      gus_recmask = SOUND_MASK_MIC;
 	    /* Note! Input volumes are updated during next open for recording */
-	    return ioctl_out (arg, gus_recmask);
+	    return (*(int *) arg = gus_recmask);
 	    break;
 
 	  case SOUND_MIXER_MIC:
 	    {
 	      int             vol;
 
-	      get_user (vol, (int *) arg);
+	      vol = *(int *) arg;
 	      vol &= 0xff;
 
 	      if (vol < 0)
@@ -2919,7 +2905,7 @@
 		vol = 100;
 	      gus_mic_vol = vol;
 	      set_input_volumes ();
-	      return ioctl_out (arg, vol | (vol << 8));
+	      return (*(int *) arg = vol | (vol << 8));
 	    }
 	    break;
 
@@ -2927,7 +2913,7 @@
 	    {
 	      int             vol;
 
-	      get_user (vol, (int *) arg);
+	      vol = *(int *) arg;
 	      vol &= 0xff;
 
 	      if (vol < 0)
@@ -2936,26 +2922,26 @@
 		vol = 100;
 	      gus_line_vol = vol;
 	      set_input_volumes ();
-	      return ioctl_out (arg, vol | (vol << 8));
+	      return (*(int *) arg = vol | (vol << 8));
 	    }
 	    break;
 
 	  case SOUND_MIXER_PCM:
-	    get_user (gus_pcm_volume, (int *) arg);
+	    gus_pcm_volume = *(int *) arg;
 	    gus_pcm_volume &= 0xff;
 	    if (gus_pcm_volume < 0)
 	      gus_pcm_volume = 0;
 	    if (gus_pcm_volume > 100)
 	      gus_pcm_volume = 100;
 	    gus_audio_update_volume ();
-	    return ioctl_out (arg, gus_pcm_volume | (gus_pcm_volume << 8));
+	    return (*(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8));
 	    break;
 
 	  case SOUND_MIXER_SYNTH:
 	    {
 	      int             voice;
 
-	      get_user (gus_wave_volume, (int *) arg);
+	      gus_wave_volume = *(int *) arg;
 	      gus_wave_volume &= 0xff;
 
 	      if (gus_wave_volume < 0)
@@ -2967,7 +2953,7 @@
 		for (voice = 0; voice < nr_voices; voice++)
 		  dynamic_volume_change (voice);	/* Apply the new vol */
 
-	      return ioctl_out (arg, gus_wave_volume | (gus_wave_volume << 8));
+	      return (*(int *) arg = gus_wave_volume | (gus_wave_volume << 8));
 	    }
 	    break;
 
@@ -2981,39 +2967,39 @@
 	  {
 
 	  case SOUND_MIXER_RECSRC:
-	    return ioctl_out (arg, gus_recmask);
+	    return (*(int *) arg = gus_recmask);
 	    break;
 
 	  case SOUND_MIXER_DEVMASK:
-	    return ioctl_out (arg, MIX_DEVS);
+	    return (*(int *) arg = MIX_DEVS);
 	    break;
 
 	  case SOUND_MIXER_STEREODEVS:
-	    return ioctl_out (arg, 0);
+	    return (*(int *) arg = 0);
 	    break;
 
 	  case SOUND_MIXER_RECMASK:
-	    return ioctl_out (arg, SOUND_MASK_MIC | SOUND_MASK_LINE);
+	    return (*(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE);
 	    break;
 
 	  case SOUND_MIXER_CAPS:
-	    return ioctl_out (arg, 0);
+	    return (*(int *) arg = 0);
 	    break;
 
 	  case SOUND_MIXER_MIC:
-	    return ioctl_out (arg, gus_mic_vol | (gus_mic_vol << 8));
+	    return (*(int *) arg = gus_mic_vol | (gus_mic_vol << 8));
 	    break;
 
 	  case SOUND_MIXER_LINE:
-	    return ioctl_out (arg, gus_line_vol | (gus_line_vol << 8));
+	    return (*(int *) arg = gus_line_vol | (gus_line_vol << 8));
 	    break;
 
 	  case SOUND_MIXER_PCM:
-	    return ioctl_out (arg, gus_pcm_volume | (gus_pcm_volume << 8));
+	    return (*(int *) arg = gus_pcm_volume | (gus_pcm_volume << 8));
 	    break;
 
 	  case SOUND_MIXER_SYNTH:
-	    return ioctl_out (arg, gus_wave_volume | (gus_wave_volume << 8));
+	    return (*(int *) arg = gus_wave_volume | (gus_wave_volume << 8));
 	    break;
 
 	  default:
@@ -3214,6 +3200,7 @@
 
 
   samples = (struct patch_info *) (sound_mem_blocks[sound_nblocks] = vmalloc ((MAX_SAMPLE + 1) * sizeof (*samples)));
+  sound_mem_sizes[sound_nblocks] = (MAX_SAMPLE + 1) * sizeof (*samples);
   if (sound_nblocks < 1024)
     sound_nblocks++;;
   if (samples == NULL)
@@ -3224,7 +3211,7 @@
 
   conf_printf (tmp2, hw_config);
   tmp2[sizeof (gus_info.name) - 1] = 0;
-  strncpy (gus_info.name, tmp2, strlen (tmp2));
+  strcpy (gus_info.name, tmp2);
 
   if (num_synths >= MAX_SYNTH_DEV)
     printk ("GUS Error: Too many synthesizers\n");
@@ -3244,15 +3231,23 @@
   if (gus_mem_size > 0)
     if (num_audiodevs < MAX_AUDIO_DEV)
       {
-	audio_devs[gus_devnum = num_audiodevs++] = &gus_audio_operations;
-	audio_devs[gus_devnum]->dmachan1 = dma;
-	audio_devs[gus_devnum]->dmachan2 = dma2;
-	audio_devs[gus_devnum]->buffsize = DSP_BUFFSIZE;
+
+	if ((gus_devnum = sound_install_audiodrv (AUDIO_DRIVER_VERSION,
+						  "Ultrasound",
+						  &gus_audio_driver,
+					       sizeof (struct audio_driver),
+						  NEEDS_RESTART |
+			                      ((dma2 != dma && dma2 != -1) ?
+					       DMA_DUPLEX : 0),
+						  AFMT_U8 | AFMT_S16_LE,
+						  NULL,
+						  dma,
+						  dma2)) < 0)
+	                  return;
+
 	audio_devs[gus_devnum]->min_fragment = 9;
 	audio_devs[gus_devnum]->mixer_dev = num_mixers;		/* Next mixer# */
 	audio_devs[gus_devnum]->flags |= DMA_HARDSTOP;
-	if (dma2 != dma && dma2 != -1)
-	  audio_devs[gus_devnum]->flags |= DMA_DUPLEX;
       }
     else
       printk ("GUS: Too many audio devices available\n");
@@ -3518,12 +3513,14 @@
 	break;
 
       case GUS_DEV_PCM_CONTINUE:	/* Left channel data transferred */
+	gus_write8 (0x41, 0);	/* Disable GF1 DMA */
 	gus_transfer_output_block (pcm_current_dev, pcm_current_buf,
 				   pcm_current_count,
 				   pcm_current_intrflag, 1);
 	break;
 
       case GUS_DEV_PCM_DONE:	/* Right or mono channel data transferred */
+	gus_write8 (0x41, 0);	/* Disable GF1 DMA */
 	if (pcm_qlen < pcm_nblk)
 	  {
 	    int             flag = (1 - dma_active) * 2;	/* 0 or 2 */
@@ -3641,6 +3638,7 @@
 static struct sound_lowlev_timer gus_tmr =
 {
   0,
+  1,
   gus_tmr_start,
   gus_tmr_disable,
   gus_tmr_restart
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/ics2101.c linux/drivers/sound/ics2101.c
--- v2.1.27/linux/drivers/sound/ics2101.c	Fri Nov 15 00:15:22 1996
+++ linux/drivers/sound/ics2101.c	Wed Feb 26 02:35:16 1997
@@ -4,7 +4,7 @@
  * Driver for the ICS2101 mixer of GUS v3.7.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -120,11 +120,11 @@
 {
   if (((cmd >> 8) & 0xff) == 'M')
     {
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
 	{
 	  int             val;
 
-	  get_user (val, (int *) arg);
+	  val = *(int *) arg;
 
 	  switch (cmd & 0xff)
 	    {
@@ -133,23 +133,23 @@
 	      break;
 
 	    case SOUND_MIXER_MIC:
-	      return ioctl_out (arg, set_volumes (DEV_MIC, val));
+	      return (*(int *) arg = set_volumes (DEV_MIC, val));
 	      break;
 
 	    case SOUND_MIXER_CD:
-	      return ioctl_out (arg, set_volumes (DEV_CD, val));
+	      return (*(int *) arg = set_volumes (DEV_CD, val));
 	      break;
 
 	    case SOUND_MIXER_LINE:
-	      return ioctl_out (arg, set_volumes (DEV_LINE, val));
+	      return (*(int *) arg = set_volumes (DEV_LINE, val));
 	      break;
 
 	    case SOUND_MIXER_SYNTH:
-	      return ioctl_out (arg, set_volumes (DEV_GF1, val));
+	      return (*(int *) arg = set_volumes (DEV_GF1, val));
 	      break;
 
 	    case SOUND_MIXER_VOLUME:
-	      return ioctl_out (arg, set_volumes (DEV_VOL, val));
+	      return (*(int *) arg = set_volumes (DEV_VOL, val));
 	      break;
 
 	    default:
@@ -167,39 +167,39 @@
 	    break;
 
 	  case SOUND_MIXER_DEVMASK:
-	    return ioctl_out (arg, MIX_DEVS);
+	    return (*(int *) arg = MIX_DEVS);
 	    break;
 
 	  case SOUND_MIXER_STEREODEVS:
-	    return ioctl_out (arg, SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC);
+	    return (*(int *) arg = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC);
 	    break;
 
 	  case SOUND_MIXER_RECMASK:
-	    return ioctl_out (arg, SOUND_MASK_MIC | SOUND_MASK_LINE);
+	    return (*(int *) arg = SOUND_MASK_MIC | SOUND_MASK_LINE);
 	    break;
 
 	  case SOUND_MIXER_CAPS:
-	    return ioctl_out (arg, 0);
+	    return (*(int *) arg = 0);
 	    break;
 
 	  case SOUND_MIXER_MIC:
-	    return ioctl_out (arg, volumes[DEV_MIC]);
+	    return (*(int *) arg = volumes[DEV_MIC]);
 	    break;
 
 	  case SOUND_MIXER_LINE:
-	    return ioctl_out (arg, volumes[DEV_LINE]);
+	    return (*(int *) arg = volumes[DEV_LINE]);
 	    break;
 
 	  case SOUND_MIXER_CD:
-	    return ioctl_out (arg, volumes[DEV_CD]);
+	    return (*(int *) arg = volumes[DEV_CD]);
 	    break;
 
 	  case SOUND_MIXER_VOLUME:
-	    return ioctl_out (arg, volumes[DEV_VOL]);
+	    return (*(int *) arg = volumes[DEV_VOL]);
 	    break;
 
 	  case SOUND_MIXER_SYNTH:
-	    return ioctl_out (arg, volumes[DEV_GF1]);
+	    return (*(int *) arg = volumes[DEV_GF1]);
 	    break;
 
 	  default:
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/iwmem.h linux/drivers/sound/iwmem.h
--- v2.1.27/linux/drivers/sound/iwmem.h	Fri Nov 15 00:14:53 1996
+++ linux/drivers/sound/iwmem.h	Wed Feb 26 02:34:47 1997
@@ -4,7 +4,7 @@
  * DRAM size encoding table for AMD Interwave chip.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/ChangeLog.awe linux/drivers/sound/lowlevel/ChangeLog.awe
--- v2.1.27/linux/drivers/sound/lowlevel/ChangeLog.awe	Fri Oct 25 03:06:34 1996
+++ linux/drivers/sound/lowlevel/ChangeLog.awe	Wed Jan 29 00:54:11 1997
@@ -1,3 +1,61 @@
+ver.0.3.1a
+	- Fix a bug to reset voice counter in awe_reset
+	- Fix voice balance on GUS mode
+	- Make symlink on /usr/include/asm in install script
+
+ver.0.3.1
+	- Remove zero size arrays from awe_voice.h
+	- Fix init_fm routine
+	- Remove all samples except primary samples in REMOVE_LAST_SAMPLES
+
+ver.0.3.0a
+	- Add AWE_NOTEOFF_ALL control
+	- Remove AWE_INIT_ATTEN control
+
+ver.0.3.0
+	- Fix decay time table
+	- Add exclusive sounds mode
+	- Add capability to get current status
+
+ver.0.2.99e
+	- Add #ifdef for all sounds/notes off controls.
+	- Fix bugs on searching the default drumset/preset.
+	- Fix usslite patch to modify the default Config.in.
+
+ver.0.2.99d
+	- Fix bugs of attack/hold parameters
+	- Fix attack & decay time table
+
+ver.0.2.99c
+	- Change volume control messages (main & expression volume)
+	  to accesspt normal MIDI parameters in channel mode.
+	- Use channel mode in SEQ2 controls.
+
+ver.0.2.99b
+	- #ifdef patch manager functions (for OSS-3.7)
+
+ver.0.2.99a
+	- Fix sustain bug
+
+ver.0.2.99 (0.3 beta)
+	- Support multiple instruments
+
+ver.0.2.0c
+	- Add copyright notice
+	- FreeBSD 2.2-ALPHA integration
+
+ver.0.2.0b
+	- Remove buffered reading appended in v0.2.0a
+	- Remove SMAxW register check on writing
+	- Support Linux 2.1.x kernel
+	- Rewrite installation script
+
+ver.0.2.0a
+	- Define SEQUENCER_C for tuning.h for FreeBSD system
+	- Improvement of sample loading speed
+	- Fix installation script
+	- Add PnP driver functions for ISA PnP driver support
+
 ver.0.2.0
 	- Includes FreeBSD port
 	- Can load GUS compatible patches
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/Config.tmpl linux/drivers/sound/lowlevel/Config.tmpl
--- v2.1.27/linux/drivers/sound/lowlevel/Config.tmpl	Fri Oct 25 03:06:34 1996
+++ linux/drivers/sound/lowlevel/Config.tmpl	Tue Nov 26 04:36:48 1996
@@ -3,4 +3,55 @@
 if [ "$CONFIG_LOWLEVEL_SOUND" = "y" ]; then
    bool 'ACI mixer (miroPCM12)' CONFIG_ACI_MIXER
    bool 'AWE32 synth' CONFIG_AWE32_SYNTH
+   bool 'Gallant Audio Cards (SC-6000 and SC-6600 based)' CONFIG_AEDSP16
+
+   if [ "$CONFIG_AEDSP16" = "y" ]; then
+      comment 'SC-6600 Audio Cards have no jumper switches at all'
+      bool 'SC-6600 based audio cards (new Audio Excel DSP 16)' CONFIG_SC6600
+
+      if [ "$CONFIG_SB" = "y" -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+         bool 'Audio Excel DSP 16 (SBPro emulation)' CONFIG_AEDSP16_SBPRO
+         if [ "$CONFIG_AEDSP16_SBPRO" = "y" ]; then
+	    comment 'Audio Excel DSP 16 [Sound Blaster Pro]'
+            hex 'I/O base for Audio Excel DSP 16 220 or 240' \
+	                                           AEDSP16_BASE $SBC_BASE
+            int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \
+	                                           AEDSP16_SBC_IRQ $SBC_IRQ
+            int 'Audio Excel DSP 16 DMA 0, 1 or 3' AEDSP16_SBC_DMA $SBC_DMA
+         fi
+      fi
+   
+      if [ "$CONFIG_MSS" = "y" -a "$CONFIG_AEDSP16_SBPRO" != "y" ]; then
+         bool 'Audio Excel DSP 16 (MSS emulation)' CONFIG_AEDSP16_MSS
+         if [ "$CONFIG_AEDSP16_MSS" = "y" ]; then
+	    comment 'Audio Excel DSP 16 [Microsoft Sound System]'
+	    hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+            int 'Audio Excel DSP 16 IRQ 5, 7, 9, 10, 11' \
+	                                           AEDSP16_MSS_IRQ $MSS_IRQ
+            int 'Audio Excel DSP 16 DMA 0, 1 or 3' AEDSP16_MSS_DMA $MSS_DMA
+         fi
+      fi
+
+      if [ "$CONFIG_MIDI" = "y" ]; then
+         bool 'Audio Excel DSP 16 (MPU401 emulation)' CONFIG_AEDSP16_MPU401
+         if [ "$CONFIG_AEDSP16_MPU401" = "y" ]; then
+	    comment 'Audio Excel DSP 16 [MPU-401]'
+	    if [ "$CONFIG_AEDSP16_SBPRO" != "y" \
+	         -a "$CONFIG_AEDSP16_MSS" != "y" ]; then
+	       hex 'I/O base for Audio Excel DSP 16 220 or 240' AEDSP16_BASE 220
+	    fi
+            int 'MPU401 IRQ for Audio Excel DSP 16 5, 7, 9, 10 or 0 (disable)' \
+	    						AEDSP16_MPU_IRQ $MPU_IRQ
+         fi
+      fi
+
+      if [ "$CONFIG_SC6600" = "y" ]; then
+         comment 'SC-6600 specific configuration'
+         bool 'Activate SC-6600 Joystick Interface' CONFIG_SC6600_JOY
+         int 'SC-6600 CDROM Interface (4=None, 3=IDE, 1=Panasonic, 0=?Sony?)' \
+	 					CONFIG_SC6600_CDROM 4
+         hex 'SC-6600 CDROM Interface I/O Address' CONFIG_SC6600_CDROMBASE 0
+      fi
+
+   fi
 fi
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/Makefile linux/drivers/sound/lowlevel/Makefile
--- v2.1.27/linux/drivers/sound/lowlevel/Makefile	Fri Oct 25 03:06:34 1996
+++ linux/drivers/sound/lowlevel/Makefile	Tue Nov 26 04:36:48 1996
@@ -1,6 +1,6 @@
 all:	lowlevel.o
 
-ALLOBJS = init.o aci.o awe_wave.o
+ALLOBJS = init.o aci.o awe_wave.o aedsp16.o
 OBJS = init.o
 
 ifdef CONFIG_LOWLEVEL_SOUND
@@ -10,6 +10,9 @@
 ifdef CONFIG_AWE32_SYNTH
 OBJS := $(OBJS) awe_wave.o
 endif
+ifdef CONFIG_AEDSP16
+OBJS := $(OBJS) aedsp16.o
+endif
 endif
 
 lowlevel.o:	$(OBJS)
@@ -27,8 +30,20 @@
 	@exit 1
 
 clean:
-	rm -f core x y z *~ *.o module
+	rm -f core x y z *~ *.o module .depend
+
+dep:
+	$(CPP) -M $(CFLAGS) -I. *.c > .depend
 
 ifdef HOSTCC
 include $(TOPDIR)/Rules.make
+else
+USE_DEPEND=y
+endif
+
+ifdef USE_DEPEND
+# include a dependency file if one exists
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
 endif
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/README.aedsp16 linux/drivers/sound/lowlevel/README.aedsp16
--- v2.1.27/linux/drivers/sound/lowlevel/README.aedsp16	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/lowlevel/README.aedsp16	Tue Nov 26 04:36:48 1996
@@ -0,0 +1,106 @@
+Driver
+------
+
+Informations about Audio Excel DSP 16 driver can be found in the source
+file lowlevel/aedsp16.c
+Please, read the head of the source before using it. It contain useful
+informations.
+
+Configuration
+-------------
+
+The Audio Excel configuration, is now done with the standard Linux setup.
+You have to configure the sound card (Sound Blaster or Microsoft Sound System)
+and, if you want it, the Roland MPU-401 (do not use the Sound Blaster MPU-401,
+SB-MPU401) in the main driver menu. Activate the lowlevel drivers then select
+the Audio Excel hardware that you want to initialize. Check the IRQ/DMA/MIRQ
+of the Audio Excel initialization: it must be the same as the SBPRO (or MSS)
+setup. If the parameters are different, correct it.
+I you own a Gallant's audio card based on SC-6600, activate the SC-6600 support.
+If you want to change the configuration of the sound board, be sure to
+check off all the configuration items before re-configure it.
+
+Sound cards supported
+---------------------
+This driver supports the SC-6000 and SC-6600 based Gallant's sound card.
+It don't support the Audio Excel DSP 16 III (try the SC-6600 code).
+I'm working on the III version of the card: if someone have useful
+informations about it, please let me know.
+For all the non-supported audio cards, you have to boot MS-DOS (or WIN95)
+activating the audio card with the MS-DOS device driver, then you have to
+<ctrl>-<alt>-<del> and boot Linux.
+Follow these steps:
+
+1) Compile Linux kernel with standard sound driver, using the emulation
+   you want, with the parameters of your audio card,
+   e.g. Microsoft Sound System irq10 dma3
+2) Install your new kernel as the default boot kernel.
+3) Boot MS-DOS and configure the audio card with the boot time device
+   driver, for MSS irq10 dma3 in our example.
+4) <ctrl>-<alt>-<del> and boot Linux. This will mantain the DOS configuration
+   and will boot the new kernel with sound driver. The sound driver will find
+   the audio card and will recognize and attach it.
+
+Reports on User successes
+-------------------------
+
+[--------------------------------------------------------------------------]
+
+From POPmail Mon Jul 29 18:23:12 1996
+Received: from cdc8g5.cdc.polimi.it by mbox.vol.it with SMTP
+	(1.39.111.2/16.2) id AA257995686; Mon, 29 Jul 1996 09:34:46 +0200
+Return-Path: <sjg95@unixfe.rl.ac.uk>
+Received: from [130.246.12.16] by cdc8g5.cdc.polimi.it with SMTP
+	(1.38.193.4/15.6) id AA27426; Mon, 29 Jul 1996 09:42:49 +0200
+Posted-Date: Mon, 29 Jul 1996 08:35:40 +0100
+Received-Date: Mon, 29 Jul 1996 09:42:49 +0200
+Received: (from sjg95@localhost) by unixfe.rl.ac.uk (8.7.3/8.7.3) 
+          id IAA58788 for riccardo@cdc8g5.cdc.polimi.it; Mon, 29 Jul 1996 08:35:40 +0100
+Date: Mon, 29 Jul 1996 08:35:40 +0100
+From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk>
+Message-Id: <199607290735.IAA58788@unixfe.rl.ac.uk>
+To: riccardo@cdc8g5.cdc.polimi.it (Riccardo Facchetti)
+Subject: Re: Audio Excel DSP 16 initialization code
+Newsgroups: comp.os.linux.announce
+X-Newsreader: TIN [version 1.2 PL2]
+Status: RO
+X-Status: A
+
+Just to let you know got my Audio Excel (emulating a MSS) working
+with my original SB16, thanks for the driver!
+
+/dev/sndstat:
+
+Linux huey 2.0.9 #10 Sat Jul 27 11:41:42 BST 1996 i586)
+Kernel: Linux huey 2.0.9 #14 Sun Jul 28 12:50:43 BST 1996 i586
+Config options: c0202
+
+Installed drivers: 
+Type 10: MS Sound System
+Type 24: MS Sound System (AXP)
+Type 27: Compaq Deskpro XL
+Type 2: Sound Blaster
+
+Card config: 
+MS Sound System at 0x530 irq 11 drq 3
+Sound Blaster at 0x240 irq 5 drq 1,5
+
+Audio devices:
+0: MSS audio codec (CS4231A)
+1: Sound Blaster 16 (4.12)
+
+Synth devices:
+
+Midi devices: NOT ENABLED IN CONFIG
+
+Timers:
+0: System clock
+1: MSS audio codec (CS4231A)
+
+Mixers:
+0: MSS audio codec (CS4231A)
+1: Sound Blaster
+
+[--------------------------------------------------------------------------]
+
+    Riccardo
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/README.awe linux/drivers/sound/lowlevel/README.awe
--- v2.1.27/linux/drivers/sound/lowlevel/README.awe	Fri Oct 25 03:06:34 1996
+++ linux/drivers/sound/lowlevel/README.awe	Wed Jan 29 00:54:11 1997
@@ -1,29 +1,127 @@
 ================================================================
 	AWE32 Sound Driver for Linux / FreeBSD
-		version 0.2.0; Oct. 16, 1996
+		version 0.3.1; Jan. 11, 1997
 ================================================================
 
 * GENERAL NOTES
 
-This is a sound driver extension for SB-AWE32 cards to enable the
-wave synth operations.  The driver is provided for both Linux 1.2.x
-and 2.0.x kernels, and also FreeBSD.  Follow the corresponding
-instruction in the installation document if you use old sound drivers.
+This is a sound driver extension for SoundBlaster AWE32 and other
+compatible cards (AWE32-PnP, SB32, SB32-PnP, AWE64 & etc) to enable
+the wave synth operations.  The driver is provided for both Linux
+1.2.x and 2.[01].x kernels, and also FreeBSD.  Consult the
+installation document for installation on the original sound driver
+package.
 
 This driver was written by Takashi Iwai (iwai@dragon.mm.t.u-tokyo.ac.jp)  
 who also maintains the code.  Please forward any questions, bug fixes
-and suggestions directly to me (_NOT_ to Linus Torvalds or Hannu
+and suggestions directly to Iwai (_NOT_ to Linus Torvalds or Hannu
 Savolainen).
 
 
 * CAUTION
 
-- In the v0.2.0, parameters of hardware control are changed to have a
-compatibility with GUS driver.  If you have older awesfx and drvmidi
-utilities, please get new versions and install them again.
+- On ver.0.3.1, some zero size array entries are removed from
+awe_voice.h to avoid compile error in some non-ANSI compilers.
+Due to this fix, the size of awe_voice_rec structure is changed from
+older versions.  Use a constant AWE_VOICE_REC_SIZE instead of
+sizeof(awe_voice_rec).
+You can still have a compatibility by defining AWE_COMPAT_030=1, but
+this feature will be omitted in the future release.
+
+
+* NOTE TO LINUX USERS
+
+To enable this driver on linux-2.[01].x kernels, you need turn on both 
+"lowlevel drivers support" and "AWE32 synth support" options in sound
+menu when configure your linux kernel and modules.  For more details,
+see the installation document in the original driver package
+(awedrv-0.x.x.tar.gz) available at the web page:
+	http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/
 
-- The SFX file after v0.1.5 is no more compatible from 0.1.4x.
-Please remake the obsolete SFX files again by txt2sfx utility.
+If you're using PnP cards, the card must be initialized before loading
+the sound driver.  There're several options to do this:
+    - Initialize the card via ISA PnP tools, and load the sound module.
+    - Initialize the card on DOS, and load linux by loadlin.exe
+    - Use PnP driver (for Linux-2.0.x)
+See the FAQ list on the URL above.
+
+
+* COMPILE FLAGS
+
+Compile conditions are defined in awe_config.h.
+
+- AWE_OBSOLETE_VOXWARE		(default: not defined)
+    indicates the system is VoxWare-3.0.x (with linux 1.2.x or
+    FreeBSD) if defined.  This option will be set automatically when
+    you use installation script.
+
+- AWE_NEW_KERNEL_INTERFACE	(default: not defined)
+    indicates the system is OSSLite on Linux 2.1.6 or later if
+    defined.  This option will be set automatically when you use
+    installation script.
+
+- HAS_LOWLEVEL_H		(default: not defined)
+    indicates the system has "lowlevel.h" in the sound/lowlevel
+    directory.  OSS driver has this file.
+
+- AWE_NO_PATCHMGR		(default: not defined)
+    indicates the sound driver has no patch manager function (for
+    OSS-3.707 (in Linux-2.1.13) or newer). 
+
+- AWE_DEFAULT_BASE_ADDR		(default: not defined)
+    specifies the base port address of your AWE32 card.
+    Define this only when the driver couldn't detect your card
+    properly. 
+
+- AWE_DEFAULT_MEM_SIZE		(default: not defined)
+    specifies the memory size of your AWE32 card by kilo bytes.
+    Define this only when the driver couldn't detect memory size
+    properly. 
+    
+- AWE_MAX_SAMPLES		(default: 400)
+    specifies the maximum number of wave samples.
+    The default size is set for the original GM and GS presets from
+    CreativeLab.  If you have a large set of samples (eg 2MB & 8MB GM
+    presets), increase this value to appropriate size. 
+
+- AWE_MAX_INFOS			(default: 900)
+    specifies the maximum number of instruments.
+    The default size is set for the original GM and GS presets from
+    CreativeLab.  If you have a large set of samples (eg 2MB & 8MB GM
+    presets), increase this value to appropriate size. 
+
+- AWE_ALWAYS_INIT_FM		(default: not defined)
+    indicates the AWE driver always initialize FM passthrough even
+    without DRAM on board.  Emu8000 chip has a restriction for playing
+    samples on DRAM that at least two channels must be occupied as
+    passthrough channels. 
+
+- AWE_DEBUG_ON			(default: defined)
+    turns on debuggin messages if defined.
+
+- AWE_CHECKSUM_DATA		(default: defined)
+    verifies check sum of sample data with the transferred data if
+    defined. 
+
+- AWE_CHECKSUM_MEMORY		(default: defined)
+    Verifies check sum of sample data with the written data on DRAM.
+
+- AWE_HAS_GUS_COMPATIBILITY	(default: defined)
+    Enables GUS compatibility mode if defined, reading GUS patches and 
+    GUS control commands.  Define this option to use GMOD or other
+    GUS module players.
+
+- AWE_ACCEPT_ALL_SOUNDS_CONTROL	(default: not defined)
+    Enables MIDI control #120 and #123 as "all notes off" and "all
+    sounds off" events, respectively.
+
+- DEF_FM_CHORUS_DEPTH		(default: 0x10)
+    The default strength to be sent to the chorus effect engine.
+    From 0 to 0xff.  Larger numbers may often cause weird sounds.
+
+- DEF_FM_REVERB_DEPTH		(default: 0x10)
+    The default strength to be sent to the reverb effect engine.
+    From 0 to 0xff.  Larger numbers may often cause weird sounds.
 
 
 * USING THE DRIVER
@@ -32,8 +130,20 @@
 All AWE32 driver and utilities can be downloaded from:
 	http://bahamut.mm.t.u-tokyo.ac.jp/~iwai/awedrv/
 
-Follow the instruction in awesfx package to make patched GM and
-GS presets.  Then, load the SFX file on driver by sfxload utility.
+The GM and GS sounds include multiple instrument layers.  The older
+driver couldn't handle multiple instruments.  The current version
+supports this type of sounds with a special extension, but so far only 
+drvmidi and playmidi can play the multiple instruments and stereo
+sounds.
+
+To play drvmidi, load the SoundFont file directly uing sfxload utility.
+
+	% sfxload -i synthgm.sf2
+
+To use other sequencers like musserver, some sounds may become
+inaudible unless converting to SFX file.  Follow the instruction in
+awesfx package to make patched GM and GS presets.  Then, load the SFX
+file on driver by sfxload utility.
 
 	% sfxload -i gm.sfx
 
@@ -58,12 +168,29 @@
 
 * BUGS & TODO'S
 
-- Some instruments are inaudible, eg. glockenspiel and sinewaves.
-- No stereo sample can be used.
 - More smart patch management
 - More smart DRAM memory control
 - Dynamic buffer allocation
 - etc, etc, etc.
+
+
+* COPYRIGHT
+
+Copyright (C) 1996,1997 Takashi Iwai
+
+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., 675 Mass Ave, Cambridge, MA 02139, USA.
 
 
 Takashi Iwai <iwai@dragon.mm.t.u-tokyo.ac.jp>
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/aci.c linux/drivers/sound/lowlevel/aci.c
--- v2.1.27/linux/drivers/sound/lowlevel/aci.c	Fri Oct 25 03:06:35 1996
+++ linux/drivers/sound/lowlevel/aci.c	Wed Jan 15 02:15:08 1997
@@ -22,9 +22,9 @@
  *        First version written.
  *   1995-12-31  Markus Kuhn
  *        Second revision, general code cleanup.
- *   1996-05-16	 Hannu Savolainen
+ *   1997-05-16	 Hannu Savolainen
  *	  Integrated with other parts of the driver.
- *   1996-05-28  Markus Kuhn
+ *   1997-05-28  Markus Kuhn
  *        Initialize CS4231A mixer, make ACI first mixer,
  *        use new private mixer API for solo mode.
  */
@@ -73,6 +73,13 @@
 
 static int aci_present = 0;
 
+#ifdef MODULE                  /* Whether the aci mixer is to be reset.    */
+int aci_reset = 0;             /* Default: don't reset if the driver is a  */
+#else                          /* module; use "insmod sound.o aci_reset=1" */
+int aci_reset = 1;             /* to override.                             */
+#endif
+
+
 #define COMMAND_REGISTER    (aci_port)
 #define STATUS_REGISTER     (aci_port + 1)
 #define BUSY_REGISTER       (aci_port + 2)
@@ -282,7 +289,7 @@
   if (indexed_cmd(0xf0, right_index, &buf)) return -EIO;
   vol |= SCALE(0x20, 100, buf < 0x20 ? 0x20-buf : 0) << 8;
 
-  return snd_ioctl_return((int *) arg, vol);
+  return (*(int *) arg = vol);
 }
 
 
@@ -290,23 +297,21 @@
 		     unsigned char left_index, unsigned char right_index)
 {
   int vol, ret;
-  unsigned param;
 
-  param = get_user((int *) arg);
   /* left channel */
-  vol = param & 0xff;
+  vol = *(int *)arg & 0xff;
   if (vol > 100) vol = 100;
   vol = SCALE(100, 0x20, vol);
   if (write_cmd(left_index, 0x20 - vol)) return -EIO;
   ret = SCALE(0x20, 100, vol);
   /* right channel */
-  vol = (param >> 8) & 0xff;
+  vol = (*(int *)arg >> 8) & 0xff;
   if (vol > 100) vol = 100;
   vol = SCALE(100, 0x20, vol);
   if (write_cmd(right_index, 0x20 - vol)) return -EIO;
   ret |= SCALE(0x20, 100, vol) << 8;
  
-  return snd_ioctl_return((int *) arg, ret);
+  return (*(int *) arg = ret);
 }
 
 
@@ -318,14 +323,14 @@
 
   /* handle solo mode control */
   if (cmd == SOUND_MIXER_PRIVATE1) {
-    if (get_user((int *) arg) >= 0) {
-      aci_solo = !!get_user((int *) arg);
+    if (*(int *) arg >= 0) {
+      aci_solo = !!*(int *) arg;
       if (write_cmd(0xd2, aci_solo)) return -EIO;
     } else if (aci_version >= 0xb0) {
       if ((status = read_general_status()) < 0) return -EIO;
-      return snd_ioctl_return ((int *) arg, (status & 0x20) == 0);
+      return (*(int *) arg = (status & 0x20) == 0);
     }
-    return snd_ioctl_return((int *) arg, aci_solo);
+    return (*(int *) arg = aci_solo);
   }
 
   if (((cmd >> 8) & 0xff) == 'M') {
@@ -349,14 +354,14 @@
       case SOUND_MIXER_LINE2:  /* AUX2 */
 	return setvolume(arg, 0x3e, 0x36);
       case SOUND_MIXER_IGAIN:  /* MIC pre-amp */
-	vol = get_user((int *) arg) & 0xff;
+	vol = *(int *) arg & 0xff;
 	if (vol > 100) vol = 100;
 	vol = SCALE(100, 3, vol);
 	if (write_cmd(0x03, vol)) return -EIO;
 	vol = SCALE(3, 100, vol);
-	return snd_ioctl_return((int *) arg, vol | (vol << 8));
+	return (*(int *) arg = vol | (vol << 8));
       case SOUND_MIXER_RECSRC:
-	return snd_ioctl_return ((int *) arg, 0);
+	return (*(int *) arg = 0);
 	break;
       default:
 	return -EINVAL;
@@ -365,7 +370,7 @@
       /* only read */
       switch (cmd & 0xff) {
       case SOUND_MIXER_DEVMASK:
-	return snd_ioctl_return ((int *) arg,
+	return (*(int *) arg =
 				 SOUND_MASK_VOLUME | SOUND_MASK_CD    |
 				 SOUND_MASK_MIC    | SOUND_MASK_LINE  |
 				 SOUND_MASK_SYNTH  | SOUND_MASK_PCM   |
@@ -375,20 +380,20 @@
 				 SOUND_MASK_LINE1  | SOUND_MASK_LINE2);
 	break;
       case SOUND_MIXER_STEREODEVS:
-	return snd_ioctl_return ((int *) arg,
+	return (*(int *) arg =
 				 SOUND_MASK_VOLUME | SOUND_MASK_CD   |
 				 SOUND_MASK_MIC    | SOUND_MASK_LINE |
 				 SOUND_MASK_SYNTH  | SOUND_MASK_PCM  |
 				 SOUND_MASK_LINE1  | SOUND_MASK_LINE2);
 	break;
       case SOUND_MIXER_RECMASK:
-	return snd_ioctl_return ((int *) arg, 0);
+	return (*(int *) arg = 0);
 	break;
       case SOUND_MIXER_RECSRC:
-	return snd_ioctl_return ((int *) arg, 0);
+	return (*(int *) arg = 0);
 	break;
       case SOUND_MIXER_CAPS:
-	return snd_ioctl_return ((int *) arg, 0);
+	return (*(int *) arg = 0);
 	break;
       case SOUND_MIXER_VOLUME:
 	return getvolume(arg, 0x04, 0x03);
@@ -410,7 +415,7 @@
 	if (indexed_cmd(0xf0, 0x21, &buf)) return -EIO;
 	vol = SCALE(3, 100, buf <= 3 ? buf : 3);
 	vol |= vol << 8;
-	return snd_ioctl_return((int *) arg, vol);
+	return (*(int *) arg = vol);
       default:
 	return -EINVAL;
       }
@@ -507,15 +512,17 @@
   printk("<ACI %02x, id %02x %02x (%s)> at 0x%03x\n",
 	 aci_version, aci_idcode[0], aci_idcode[1], boardname, aci_port);
 
-  /* initialize ACI mixer */
-  implied_cmd(0xff);
-  aci_solo = 0;
+  if (aci_reset) {
+    /* initialize ACI mixer */
+    implied_cmd(0xff);
+    aci_solo = 0;
+  }
 
   /* attach the mixer */
   request_region(aci_port, 3, "sound mixer (ACI)");
   if (num_mixers < MAX_MIXER_DEV) {
     if (num_mixers > 0 &&
-        !strcmp("MAD16 WSS (CS4231A)", mixer_devs[num_mixers-1]->name)) {
+        !strncmp("MAD16 WSS", mixer_devs[num_mixers-1]->name, 9)) {
       /*
        * The previously registered mixer device is the CS4231A which
        * has no function on an ACI card. Make the ACI mixer the first
@@ -560,23 +567,30 @@
       mixer_devs[num_mixers++] = &aci_mixer_operations;
   }
 
-  /* Initialize ACI mixer with reasonable power-up values */
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_VOLUME, (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_SYNTH,  (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_PCM,    (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE,   (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_MIC,    (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_CD,     (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE1,  (caddr_t) &volume);
-  volume = 0x3232;
-  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE2,  (caddr_t) &volume);
+  /* Just do something; otherwise the first write command fails, at
+   * least with my PCM20.
+   */
+  aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_READ_VOLUME, (caddr_t) &volume);
+
+  if (aci_reset) {
+    /* Initialize ACI mixer with reasonable power-up values */
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_VOLUME, (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_SYNTH,  (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_PCM,    (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE,   (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_MIC,    (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_CD,     (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE1,  (caddr_t) &volume);
+    volume = 0x3232;
+    aci_mixer_ioctl(num_mixers-1, SOUND_MIXER_WRITE_LINE2,  (caddr_t) &volume);
+  }
   
   aci_present = 1;
 
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/aedsp16.c linux/drivers/sound/lowlevel/aedsp16.c
--- v2.1.27/linux/drivers/sound/lowlevel/aedsp16.c	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/lowlevel/aedsp16.c	Wed Jan  8 03:44:55 1997
@@ -0,0 +1,1334 @@
+/*
+   sound/aedsp16.c
+
+   Audio Excel DSP 16 software configuration routines
+   Copyright (C) 1995  Riccardo Facchetti (riccardo@cdc8g5.cdc.polimi.it)
+
+   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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+/*
+ * Include the main OSS Lite header file. It include all the os, OSS Lite, etc
+ * headers needed by this source.
+ */
+#include <linux/config.h>
+#include "../sound_config.h"
+#include <linux/delay.h>
+
+#ifndef AEDSP16_BASE
+#undef CONFIG_AEDSP16
+#endif
+
+#if defined(CONFIG_AEDSP16)
+
+#if defined(CONFIG_AEDSP16_SBPRO) && defined(CONFIG_AEDSP16_MSS)
+#error You have to enable only one of the MSS and SBPRO emulations.
+#endif
+
+/*
+
+   READ THIS
+
+   This module started to configure the Audio Excel DSP 16 Sound Card.
+   Now works with the SC-6000 (old aedsp16) and new SC-6600 based cards.
+
+   NOTE: I have NO idea about Audio Excel DSP 16 III. If someone owns this
+   audio card and want to see the kernel support for it, please contact me.
+
+   Audio Excel DSP 16 is an SB pro II, Microsoft Sound System and MPU-401
+   compatible card.
+   It is software-only configurable (no jumpers to hard-set irq/dma/mpu-irq),
+   so before this module, the only way to configure the DSP under linux was
+   boot the MS-DOS loading the sound.sys device driver (this driver soft-
+   configure the sound board hardware by massaging someone of its registers),
+   and then ctrl-alt-del to boot linux with the DSP configured by the DOS
+   driver.
+
+   This module works configuring your Audio Excel DSP 16's irq, dma and
+   mpu-401-irq. The OSS Lite routines rely on the fact that if the
+   hardware is there, they can detect it. The problem with AEDSP16 is
+   that no hardware can be found by the probe routines if the sound card
+   is not configured properly. Sometimes the kernel probe routines can find
+   an SBPRO even when the card is not configured (this is the standard setup
+   of the card), but the SBPRO emulation don't work well if the card is not
+   properly initialized. For this reason
+
+   aedsp16_init_board()
+
+   routine is called before the OSS Lite probe routines try to detect the
+   hardware.
+
+   NOTE (READ THE NOTE TOO, IT CONTAIN USEFUL INFORMATIONS)
+
+   NOTE: Now it works with SC-6000 and SC-6600 based audio cards. The new cards
+   have no jumper switch at all. No more WSS or MPU-401 I/O port switches. They
+   have to be configured by software.
+
+   NOTE: The driver is merged with the new OSS Lite sound driver. It works
+   as a lowlevel driver.
+
+   The Audio Excel DSP 16 Sound Card emulates both SBPRO and MSS;
+   the OSS Lite sound driver can be configured for SBPRO and MSS cards
+   at the same time, but the aedsp16 can't be two cards!!
+   When we configure it, we have to choose the SBPRO or the MSS emulation
+   for AEDSP16. We also can install a *REAL* card of the other type (see [1]).
+
+   NOTE: If someone can test the combination AEDSP16+MSS or AEDSP16+SBPRO
+   please let me know if it works.
+
+   The MPU-401 support can be compiled in together with one of the other
+   two operating modes.
+
+   NOTE: This is something like plug-and-play: we have only to plug
+   the AEDSP16 board in the socket, and then configure and compile
+   a kernel that uses the AEDSP16 software configuration capability.
+   No jumper setting is needed!
+
+   For example, if you want AEDSP16 to be an SBPro, on irq 10, dma 3
+   you have just to make config the OSS Lite package, configuring
+   the AEDSP16 sound card, then activating the SBPro emulation mode
+   and at last configuring IRQ and DMA.
+   Compile the kernel and run it.
+
+   NOTE: This means for SC-6000 cards that you can choose irq and dma,
+   but not the I/O addresses. To change I/O addresses you have to set
+   them with jumpers. For SC-6600 cards you have no jumpers so you have
+   to set up your full card configuration in the make config.
+
+   You can change the irq/dma/mirq settings WITHOUT THE NEED to open
+   your computer and massage the jumpers (there are no irq/dma/mirq
+   jumpers to be configured anyway, only I/O BASE values have to be
+   configured with jumpers)
+
+   For some ununderstandable reason, the card default of irq 7, dma 1,
+   don't work for me. Seems to be an IRQ or DMA conflict. Under heavy
+   HDD work, the kernel start to erupt out a lot of messages like:
+
+   'Sound: DMA timed out - IRQ/DRQ config error?'
+
+   For what I can say, I have NOT any conflict at irq 7 (under linux I'm
+   using the lp polling driver), and dma line 1 is unused as stated by
+   /proc/dma. I can suppose this is a bug of AEDSP16. I know my hardware so
+   I'm pretty sure I have not any conflict, but may be I'm wrong. Who knows!
+   Anyway a setting of irq 10, dma 3 works really fine.
+
+   NOTE: if someone can use AEDSP16 with irq 7, dma 1, please let me know
+   the emulation mode, all the installed hardware and the hardware
+   configuration (irq and dma settings of all the hardware).
+
+   This init module should work with SBPRO+MSS, when one of the two is
+   the AEDSP16 emulation and the other the real card. (see [1])
+   For example:
+
+   AEDSP16 (0x220) in SBPRO emu (0x220) + real MSS + other
+   AEDSP16 (0x220) in MSS emu + real SBPRO (0x240) + other
+
+   MPU401 should work. (see [2])
+
+   [1]
+       ---
+       Date: Mon, 29 Jul 1997 08:35:40 +0100
+       From: Mr S J Greenaway <sjg95@unixfe.rl.ac.uk>
+
+       [...]
+       Just to let you know got my Audio Excel (emulating a MSS) working
+       with my original SB16, thanks for the driver!
+       [...]
+       ---
+
+   [2] Not tested by me for lack of hardware.
+
+   TODO, WISHES AND TECH
+
+   - About I/O ports allocation -
+
+   Request the 2x0h region (port base) in any case if we are using this card.
+
+   NOTE: the "aedsp16 (base)" string with which we are requesting the aedsp16
+   port base region (see code) does not mean necessarily that we are emulating
+   sbpro.  Even if this region is the sbpro I/O ports region, we use this
+   region to access the control registers of the card, and if emulating
+   sbpro, I/O sbpro registers too. If we are emulating MSS, the sbpro
+   registers are not used, in no way, to emulate an sbpro: they are
+   used only for configuration purposes.
+
+   Started Fri Mar 17 16:13:18 MET 1995
+
+   v0.1 (ALPHA, was an user-level program called AudioExcelDSP16.c)
+   - Initial code.
+   v0.2 (ALPHA)
+   - Cleanups.
+   - Integrated with Linux voxware v 2.90-2 kernel sound driver.
+   - SoundBlaster Pro mode configuration.
+   - Microsoft Sound System mode configuration.
+   - MPU-401 mode configuration.
+   v0.3 (ALPHA)
+   - Cleanups.
+   - Rearranged the code to let aedsp16_init_board be more general.
+   - Erased the REALLY_SLOW_IO. We don't need it. Erased the linux/io.h
+   inclusion too. We rely on os.h
+   - Used the  to get a variable
+   len string (we are not sure about the len of Copyright string).
+   This works with any SB and compatible.
+   - Added the code to request_region at device init (should go in
+   the main body of voxware).
+   v0.4 (BETA)
+   - Better configure.c patch for aedsp16 configuration (better
+   logic of inclusion of AEDSP16 support)
+   - Modified the conditional compilation to better support more than
+   one sound card of the emulated type (read the NOTES above)
+   - Moved the sb init routine from the attach to the very first
+   probe in sb_card.c
+   - Rearrangements and cleanups
+   - Wiped out some unnecessary code and variables: this is kernel
+   code so it is better save some TEXT and DATA
+   - Fixed the request_region code. We must allocate the aedsp16 (sbpro)
+   I/O ports in any case because they are used to access the DSP
+   configuration registers and we can not allow anyone to get them.
+   v0.5
+   - cleanups on comments
+   - prep for diffs against v3.0-proto-950402
+   v0.6
+   - removed the request_region()s when compiling the MODULE sound.o
+   because we are not allowed (by the actual voxware structure) to
+   release_region()
+   v0.7 (pre ALPHA, not distributed)
+   - started porting this module to kernel 1.3.84. Dummy probe/attach
+   routines.
+   v0.8 (ALPHA)
+   - attached all the init routines.
+   v0.9 (BETA)
+   - Integrated with linux-pre2.0.7
+   - Integrated with configuration scripts.
+   - Cleaned up and beautyfied the code.
+   v0.9.9 (BETA)
+   - Thanks to Piercarlo Grandi: corrected the conditonal compilation code.
+     Now only the code configured is compiled in, with some memory saving.
+   v0.9.10
+   - Integration into the sound/lowlevel/ section of the sound driver.
+   - Re-organized the code.
+   v0.9.11 (not distributed)
+   - Rewritten the init interface-routines to initialize the AEDSP16 in
+     one shot.
+   - More cosmetics.
+   - SC-6600 support.
+   - More soft/hard configuration.
+   v0.9.12
+   - Refined the v0.9.11 code with conditional compilation to distinguish
+     between SC-6000 and SC-6600 code.
+   v1.0.0
+   - Prep for merging with OSS Lite and Linux kernel 2.1.13
+   - Corrected a bug in request/check/release region calls (thanks to the
+     new kernel exception handling).
+
+   Known Problems:
+   - Audio Excel DSP 16 III don't work with this driver.
+
+   Credits:
+   Many thanks to Gerald Britton <gbritton@CapAccess.org>. He helped me a
+   lot in testing the 0.9.11 and 0.9.12 versions of this driver.
+
+ */
+
+
+#define VERSION "1.0.0"		/* Version of Audio Excel DSP 16 driver */
+
+#undef	AEDSP16_DEBUG 1		/* Define this to enable debug code     */
+#undef	AEDSP16_DEBUG_MORE 1	/* Define this to enable more debug     */
+#undef	AEDSP16_INFO  1		/* Define this to enable info code      */
+
+#if defined(AEDSP16_DEBUG)
+# define DBG(x) printk x
+# if defined(AEDSP16_DEBUG_MORE)
+#  define DBG1(x) printk x
+# else
+#  define DBG1(x)
+# endif
+#else
+# define DBG(x)
+# define DBG1(x)
+#endif
+
+/*
+ * Misc definitions
+ */
+#define TRUE	1
+#define FALSE	0
+
+/*
+ * Region Size for request/check/release region.
+ */
+#define IOBASE_REGION_SIZE	0x10
+
+/*
+ * Hardware related defaults
+ */
+#define DEF_AEDSP16_IOB 0x220   /* 0x220(default) 0x240                 */
+#define DEF_AEDSP16_IRQ 7	/* 5 7(default) 9 10 11                 */
+#define DEF_AEDSP16_MRQ 0	/* 5 7 9 10 0(default), 0 means disable */
+#define DEF_AEDSP16_DMA 1	/* 0 1(default) 3                       */
+
+/*
+ * Commands of AEDSP16's DSP (SBPRO+special).
+ * Some of them are COMMAND_xx, in the future they may change.
+ */
+#define WRITE_MDIRQ_CFG   0x50	/* Set M&I&DRQ mask (the real config)   */
+#define COMMAND_52        0x52	/*                                      */
+#define READ_HARD_CFG     0x58	/* Read Hardware Config (I/O base etc)  */
+#define COMMAND_5C        0x5c	/*                                      */
+#define COMMAND_60        0x60	/*                                      */
+#define COMMAND_66        0x66	/*                                      */
+#define COMMAND_6C        0x6c	/*                                      */
+#define COMMAND_6E        0x6e	/*                                      */
+#define COMMAND_88        0x88	/*                                      */
+#define DSP_INIT_MSS      0x8c	/* Enable Microsoft Sound System mode   */
+#define COMMAND_C5        0xc5	/*                                      */
+#define GET_DSP_VERSION   0xe1	/* Get DSP Version                      */
+#define GET_DSP_COPYRIGHT 0xe3	/* Get DSP Copyright                    */
+
+/*
+ * Offsets of AEDSP16 DSP I/O ports. The offset is added to base I/O port
+ * to have the actual I/O port.
+ * Register permissions are:
+ * (wo) == Write Only
+ * (ro) == Read  Only
+ * (w-) == Write
+ * (r-) == Read
+ */
+#define DSP_RESET    0x06	/* offset of DSP RESET             (wo) */
+#define DSP_READ     0x0a	/* offset of DSP READ              (ro) */
+#define DSP_WRITE    0x0c	/* offset of DSP WRITE             (w-) */
+#define DSP_COMMAND  0x0c	/* offset of DSP COMMAND           (w-) */
+#define DSP_STATUS   0x0c	/* offset of DSP STATUS            (r-) */
+#define DSP_DATAVAIL 0x0e	/* offset of DSP DATA AVAILABLE    (ro) */
+
+
+#define RETRY           10	/* Various retry values on I/O opera-   */
+#define STATUSRETRY   1000	/* tions. Sometimes we have to          */
+#define HARDRETRY   500000	/* wait for previous cmd to complete    */
+
+/*
+ * Size of character arrays that store name and version of sound card
+ */
+#define CARDNAMELEN 15		/* Size of the card's name in chars     */
+#define CARDVERLEN  2		/* Size of the card's version in chars  */
+
+#if defined(CONFIG_SC6600)
+/*
+ * Bitmapped flags of hard configuration
+ */
+/*
+ * Decode macros (xl == low byte, xh = high byte)
+ */
+#define IOBASE(xl)		((xl & 0x01)?0x240:0x220)
+#define JOY(xl)  		(xl & 0x02)
+#define MPUADDR(xl)		( 			\
+				(xl & 0x0C)?0x330:	\
+				(xl & 0x08)?0x320:	\
+				(xl & 0x04)?0x310:	\
+						0x300)
+#define WSSADDR(xl)		((xl & 0x10)?0xE80:0x530)
+#define CDROM(xh)		(xh & 0x20)
+#define CDROMADDR(xh)		(((xh & 0x1F) << 4) + 0x200)
+/*
+ * Encode macros
+ */
+#define BLDIOBASE(xl, val) {		\
+	xl &= ~0x01; 			\
+	if (val == 0x240)		\
+		xl |= 0x01;		\
+	}
+#define BLDJOY(xl, val) {		\
+	xl &= ~0x02; 			\
+	if (val == 1)			\
+		xl |= 0x02;		\
+	}
+#define BLDMPUADDR(xl, val) {		\
+	xl &= ~0x0C;			\
+	switch (val) {			\
+		case 0x330:		\
+			xl |= 0x0C;	\
+			break;		\
+		case 0x320:		\
+			xl |= 0x08;	\
+			break;		\
+		case 0x310:		\
+			xl |= 0x04;	\
+			break;		\
+		case 0x300:		\
+			xl |= 0x00;	\
+			break;		\
+		default:		\
+			xl |= 0x00;	\
+			break;		\
+		}			\
+	}
+#define BLDWSSADDR(xl, val) {		\
+	xl &= ~0x10; 			\
+	if (val == 0xE80)		\
+		xl |= 0x10;		\
+	}
+#define BLDCDROM(xh, val) {		\
+	xh &= ~0x20; 			\
+	if (val == 1)			\
+		xh |= 0x20;		\
+	}
+#define BLDCDROMADDR(xh, val) {		\
+	int tmp = val;			\
+	tmp -= 0x200;			\
+	tmp >>= 4;			\
+	tmp &= 0x1F;			\
+	xh |= tmp;			\
+	xh &= 0x7F;			\
+	xh |= 0x40;			\
+	}
+#endif /* CONFIG_SC6600 */
+
+/*
+ * Bit mapped flags for calling aedsp16_init_board(), and saving the current
+ * emulation mode.
+ */
+#define INIT_NONE   (0   )
+#define INIT_SBPRO  (1<<0)
+#define INIT_MSS    (1<<1)
+#define INIT_MPU401 (1<<2)
+
+static int      soft_cfg = 0;	/* Will contain or'ed values of soft cf */
+static int      soft_cfg_1 = 0;	/* Will contain or'ed values of some cf */
+static int      gc = 0;		/* generic counter (utility counter)    */
+static int      ver[3];         /* DSP Version, hi<-ver[0], lo<-ver[1]  */
+
+#if defined(CONFIG_SC6600)
+static int	hard_cfg[2]     /* lo<-hard_cfg[0] hi<-hard_cfg[1]      */
+                     = { 0, 0};
+#endif /* CONFIG_SC6600 */
+
+#if defined(CONFIG_SC6600)
+/* Decoded hard configuration */
+struct	d_hcfg {
+	int iobase;
+	int joystick;
+	int mpubase;
+	int wssbase;
+	int cdrom;
+	int cdrombase;
+} decoded_hcfg;
+#endif /* CONFIG_SC6600 */
+
+/* orVals contain the values to be or'ed       				*/
+struct orVals {
+	int	val;		/* irq|mirq|dma                         */
+	int	or;		/* soft_cfg |= TheStruct.or             */
+};
+
+/* aedsp16_info contain the audio card configuration                  */
+struct aedsp16_info {
+	int base_io;            /* base I/O address for accessing card  */
+	int irq;                /* irq value for DSP I/O                */
+	int mpu_irq;            /* irq for mpu401 interface I/O         */
+	int dma;                /* dma value for DSP I/O                */
+	int init;               /* Initialization status of the card    */
+};
+
+/*
+ * Magic values that the DSP will eat when configuring irq/mirq/dma
+ */
+/* DSP IRQ conversion array             */
+static struct orVals orIRQ[] = {
+	{0x05, 0x28},
+	{0x07, 0x08},
+	{0x09, 0x10},
+	{0x0a, 0x18},
+	{0x0b, 0x20},
+	{0x00, 0x00}
+};
+
+/* MPU-401 IRQ conversion array         */
+static struct orVals orMIRQ[] = {
+	{0x05, 0x04},
+	{0x07, 0x44},
+	{0x09, 0x84},
+	{0x0a, 0xc4},
+	{0x00, 0x00}
+};
+
+/* DMA Channels conversion array        */
+static struct orVals orDMA[] = {
+	{0x00, 0x01},
+	{0x01, 0x02},
+	{0x03, 0x03},
+	{0x00, 0x00}
+};
+
+static struct aedsp16_info ae_config = {
+	DEF_AEDSP16_IOB,
+	DEF_AEDSP16_IRQ,
+	DEF_AEDSP16_MRQ,
+	DEF_AEDSP16_DMA,
+	INIT_NONE
+};
+
+/*
+ * Buffers to store audio card informations
+ */
+static char     DSPCopyright[CARDNAMELEN + 1];
+static char     DSPVersion[CARDVERLEN + 1];
+
+static void aedsp16_delay_10msec(void)
+{
+	for (gc = 0; gc < 1000; gc++)
+		udelay(10);
+}
+
+static int aedsp16_wait_data(int port)
+{
+	int             loop = STATUSRETRY;
+	unsigned char   ret = 0;
+
+	DBG1(("aedsp16_wait_data (0x%x): ", port));
+
+	do {
+		  ret = inb(port + DSP_DATAVAIL);
+	/*
+	 * Wait for data available (bit 7 of ret == 1)
+	 */
+	  } while (!(ret & 0x80) && loop--);
+
+	if (ret & 0x80) {
+		DBG1(("success.\n"));
+		return TRUE;
+	}
+
+	DBG1(("failure.\n"));
+	return FALSE;
+}
+
+static int aedsp16_read(int port)
+{
+	int inbyte;
+
+	DBG(("    Read DSP Byte (0x%x): ", port));
+
+	if (aedsp16_wait_data(port) == FALSE) {
+		DBG(("failure.\n"));
+		return -1;
+	}
+
+	inbyte = inb(port + DSP_READ);
+
+	DBG(("read [0x%x]/{%c}.\n", inbyte, inbyte));
+
+	return inbyte;
+}
+
+static int aedsp16_test_dsp(int port)
+{
+	return ((aedsp16_read(port) == 0xaa) ? TRUE : FALSE);
+}
+
+static int aedsp16_dsp_reset(int port)
+{
+	/*
+	 * Reset DSP
+	 */
+
+	DBG(("Reset DSP:\n"));
+
+	outb(1, (port + DSP_RESET));
+	udelay(10);
+	outb(0, (port + DSP_RESET));
+	udelay(10);
+	udelay(10);
+	if (aedsp16_test_dsp(port) == TRUE) {
+		DBG(("success.\n"));
+		return TRUE;
+	} else
+		DBG(("failure.\n"));
+	return FALSE;
+}
+
+static int aedsp16_write(int port, int cmd)
+{
+	unsigned char   ret;
+	int             loop = HARDRETRY;
+
+	DBG(("    Write DSP Byte (0x%x) [0x%x]: ", port, cmd));
+
+	do {
+		ret = inb(port + DSP_STATUS);
+		/*
+		 * DSP ready to receive data if bit 7 of ret == 0
+		 */
+		if (!(ret & 0x80)) {
+			outb(cmd, port + DSP_COMMAND);
+			DBG(("success.\n"));
+			return 0;
+		}
+	} while (loop--);
+
+	DBG(("timeout.\n"));
+	printk("[AEDSP16] DSP Command (0x%x) timeout.\n", cmd);
+
+	return -1;
+}
+
+#if defined(CONFIG_SC6600)
+
+#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+void aedsp16_pinfo(void) {
+	DBG(("\n Base address:  %x\n", decoded_hcfg.iobase));
+	DBG((" Joystick    : %s present\n", decoded_hcfg.joystick?"":" not"));
+	DBG((" WSS addr    :  %x\n", decoded_hcfg.wssbase));
+	DBG((" MPU-401 addr:  %x\n", decoded_hcfg.mpubase));
+	DBG((" CDROM       : %s present\n", (decoded_hcfg.cdrom!=4)?"":" not"));
+	DBG((" CDROMADDR   :  %x\n\n", decoded_hcfg.cdrombase));
+}
+#endif
+
+void aedsp16_hard_decode(void) {
+
+	DBG((" aedsp16_hard_decode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+
+/*
+ * Decode Cfg Bytes.
+ */
+	decoded_hcfg.iobase	= IOBASE(hard_cfg[0]);
+	decoded_hcfg.joystick	= JOY(hard_cfg[0]);
+	decoded_hcfg.wssbase	= WSSADDR(hard_cfg[0]);
+	decoded_hcfg.mpubase	= MPUADDR(hard_cfg[0]);
+	decoded_hcfg.cdrom	= CDROM(hard_cfg[1]);
+	decoded_hcfg.cdrombase	= CDROMADDR(hard_cfg[1]);
+
+#if defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+	printk(" Original sound card configuration:\n");
+	aedsp16_pinfo();
+#endif
+
+/*
+ * Now set up the real kernel configuration.
+ */
+	decoded_hcfg.iobase	= AEDSP16_BASE;
+#if defined(CONFIG_AEDSP16_MSS)
+	decoded_hcfg.wssbase	= MSS_BASE;
+#endif
+#if defined(CONFIG_AEDSP16_MPU401)
+	decoded_hcfg.mpubase	= MPU_BASE;
+#endif
+
+#if defined(CONFIG_SC6600_JOY)
+ 	decoded_hcfg.joystick	= CONFIG_SC6600_JOY; /* Enable */
+#endif
+#if defined(CONFIG_SC6600_CDROM)
+	decoded_hcfg.cdrom	= CONFIG_SC6600_CDROM; /* 4:N-3:I-2:G-1:P-0:S */
+#endif
+#if defined(CONFIG_SC6600_CDROMBASE)
+	decoded_hcfg.cdrombase	= CONFIG_SC6600_CDROMBASE; /* 0 Disable */
+#endif
+
+#if defined(AEDSP16_DEBUG)
+	DBG((" New Values:\n"));
+	aedsp16_pinfo();
+#endif
+
+	DBG(("success.\n"));
+}
+
+void aedsp16_hard_encode(void) {
+
+	DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+
+	hard_cfg[0] = 0;
+	hard_cfg[1] = 0;
+
+	hard_cfg[0] |= 0x20;
+
+	BLDIOBASE (hard_cfg[0], decoded_hcfg.iobase);
+	BLDWSSADDR(hard_cfg[0], decoded_hcfg.wssbase);
+	BLDMPUADDR(hard_cfg[0], decoded_hcfg.mpubase);
+	BLDJOY(hard_cfg[0], decoded_hcfg.joystick);
+	BLDCDROM(hard_cfg[1], decoded_hcfg.cdrom);
+	BLDCDROMADDR(hard_cfg[1], decoded_hcfg.cdrombase);
+
+#if defined(AEDSP16_DEBUG)
+	aedsp16_pinfo();
+#endif
+
+	DBG((" aedsp16_hard_encode: 0x%x, 0x%x\n", hard_cfg[0], hard_cfg[1]));
+	DBG(("success.\n"));
+
+}
+
+static int aedsp16_hard_write(int port) {
+
+	DBG(("aedsp16_hard_write:\n"));
+
+	if (aedsp16_write(port, COMMAND_6C)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6C);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	if (aedsp16_write(port, COMMAND_5C)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	if (aedsp16_write(port, hard_cfg[0])) {
+		printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[0]);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	if (aedsp16_write(port, hard_cfg[1])) {
+		printk("[AEDSP16] DATA 0x%x: failed!\n", hard_cfg[1]);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	if (aedsp16_write(port, COMMAND_C5)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_C5);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+
+	DBG(("success.\n"));
+
+	return TRUE;
+}
+
+static int aedsp16_hard_read(int port) {
+
+	DBG(("aedsp16_hard_read:\n"));
+
+	if (aedsp16_write(port, READ_HARD_CFG)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", READ_HARD_CFG);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+
+	if ((hard_cfg[0] = aedsp16_read(port)) == -1) {
+		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+			READ_HARD_CFG);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	if ((hard_cfg[1] = aedsp16_read(port)) == -1) {
+		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+			READ_HARD_CFG);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	if (aedsp16_read(port) == -1) {
+		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+			READ_HARD_CFG);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+
+	DBG(("success.\n"));
+
+	return TRUE;
+}
+
+static int aedsp16_ext_cfg_write(int port) {
+
+	int extcfg, val;
+
+	if (aedsp16_write(port, COMMAND_66)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_66);
+		return FALSE;
+	}
+
+	extcfg = 7;
+	if (decoded_hcfg.cdrom != 2)
+		extcfg = 0x0F;
+	if ((decoded_hcfg.cdrom == 4) ||
+	    (decoded_hcfg.cdrom == 3))
+		extcfg &= ~2;
+	if (decoded_hcfg.cdrombase == 0)
+		extcfg &= ~2;
+	if (decoded_hcfg.mpubase == 0)
+		extcfg &= ~1;
+
+	if (aedsp16_write(port, extcfg)) {
+		printk("[AEDSP16] Write extcfg: failed!\n");
+		return FALSE;
+	}
+	if (aedsp16_write(port, 0)) {
+		printk("[AEDSP16] Write extcfg: failed!\n");
+		return FALSE;
+	}
+	if (decoded_hcfg.cdrom == 3) {
+		if (aedsp16_write(port, COMMAND_52)) {
+			printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
+			return FALSE;
+		}
+		if ((val = aedsp16_read(port)) == -1) {
+			printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n"
+					, COMMAND_52);
+			return FALSE;
+		}
+		val &= 0x7F;
+		if (aedsp16_write(port, COMMAND_60)) {
+			printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
+			return FALSE;
+		}
+		if (aedsp16_write(port, val)) {
+			printk("[AEDSP16] Write val: failed!\n");
+			return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+#endif /* CONFIG_SC6600 */
+
+static int aedsp16_cfg_write(int port) {
+	if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
+		return FALSE;
+	}
+	if (aedsp16_write(port, soft_cfg)) {
+		printk("[AEDSP16] Initialization of (M)IRQ and DMA: failed!\n");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+#if defined(CONFIG_AEDSP16_MSS)
+
+static int aedsp16_init_mss(int port)
+{
+	DBG(("aedsp16_init_mss:\n"));
+
+	aedsp16_delay_10msec();
+
+	if (aedsp16_write(port, DSP_INIT_MSS)) {
+		printk("[AEDSP16] aedsp16_init_mss [0x%x]: failed!\n",
+				DSP_INIT_MSS);
+		DBG(("failure.\n"));
+		return FALSE;
+	}
+	aedsp16_delay_10msec();
+
+	if (aedsp16_cfg_write(port) == FALSE)
+		return FALSE;
+
+	outb(soft_cfg_1, MSS_BASE);
+
+	DBG(("success.\n"));
+
+	return TRUE;
+}
+
+#endif /* CONFIG_AEDSP16_MSS */
+
+static int aedsp16_setup_board(int port) {
+	int	loop = RETRY;
+
+#if defined(CONFIG_SC6600)
+	int	val = 0;
+
+	if (aedsp16_hard_read(port) == FALSE) {
+		printk("[AEDSP16] aedsp16_hard_read: failed!\n");
+		return FALSE;
+	}
+
+	if (aedsp16_write(port, COMMAND_52)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_52);
+		return FALSE;
+	}
+
+	if ((val = aedsp16_read(port)) == -1) {
+		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+				COMMAND_52);
+		return FALSE;
+	}
+#endif
+
+	do {
+		if (aedsp16_write(port, COMMAND_88)) {
+			printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_88);
+			return FALSE;
+		}
+		aedsp16_delay_10msec();
+	} while ((aedsp16_wait_data(port) == FALSE) && loop--);
+
+	if (aedsp16_read(port) == -1) {
+		printk("[AEDSP16] aedsp16_read after CMD 0x%x: failed\n",
+				COMMAND_88);
+		return FALSE;
+	}
+
+#if !defined(CONFIG_SC6600)
+	if (aedsp16_write(port, COMMAND_5C)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+		return FALSE;
+	}
+#endif
+
+	if (aedsp16_cfg_write(port) == FALSE)
+		return FALSE;
+
+#if defined(CONFIG_SC6600)
+	if (aedsp16_write(port, COMMAND_60)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_60);
+		return FALSE;
+	}
+	if (aedsp16_write(port, val)) {
+		printk("[AEDSP16] DATA 0x%x: failed!\n", val);
+		return FALSE;
+	}
+	if (aedsp16_write(port, COMMAND_6E)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_6E);
+		return FALSE;
+	}
+	if (aedsp16_write(port, ver[0])) {
+		printk("[AEDSP16] DATA 0x%x: failed!\n", ver[0]);
+		return FALSE;
+	}
+	if (aedsp16_write(port, ver[1])) {
+		printk("[AEDSP16] DATA 0x%x: failed!\n", ver[1]);
+		return FALSE;
+	}
+
+	if (aedsp16_hard_write(port) == FALSE) {
+		printk("[AEDSP16] aedsp16_hard_write: failed!\n");
+		return FALSE;
+	}
+
+	if (aedsp16_write(port, COMMAND_5C)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", COMMAND_5C);
+		return FALSE;
+	}
+
+#if defined(THIS_IS_A_THING_I_HAVE_NOT_TESTED_YET)
+	if (aedsp16_cfg_write(port) == FALSE)
+		return FALSE;
+#endif
+
+#endif
+
+	return TRUE;
+}
+
+static int aedsp16_stdcfg(int port) {
+	if (aedsp16_write(port, WRITE_MDIRQ_CFG)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", WRITE_MDIRQ_CFG);
+		return FALSE;
+	}
+	/*
+	 * 0x0A == (IRQ 7, DMA 1, MIRQ 0)
+	 */
+	if (aedsp16_write(port, 0x0A)) {
+		printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
+		return FALSE;
+	}
+	return TRUE;
+}
+
+static int aedsp16_dsp_version(int port)
+{
+	int             len = 0;
+	int             ret;
+
+	DBG(("Get DSP Version:\n"));
+
+	if (aedsp16_write(ae_config.base_io, GET_DSP_VERSION)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_VERSION);
+		DBG(("failed.\n"));
+		return FALSE;
+	}
+
+	do {
+		if ((ret = aedsp16_read(port)) == -1) {
+			DBG(("failed.\n"));
+			return FALSE;
+		}
+	/*
+	 * We already know how many int are stored (2), so we know when the
+	 * string is finished.
+	 */
+		ver[len++] = ret;
+	  } while (len < CARDVERLEN);
+	sprintf(DSPVersion, "%d.%d", ver[0], ver[1]);
+
+	DBG(("success.\n"));
+
+	return TRUE;
+}
+
+static int aedsp16_dsp_copyright(int port)
+{
+	int             len = 0;
+	int             ret;
+
+	DBG(("Get DSP Copyright:\n"));
+
+	if (aedsp16_write(ae_config.base_io, GET_DSP_COPYRIGHT)) {
+		printk("[AEDSP16] CMD 0x%x: failed!\n", GET_DSP_COPYRIGHT);
+		DBG(("failed.\n"));
+		return FALSE;
+	}
+
+	do {
+		if ((ret = aedsp16_read(port)) == -1) {
+	/*
+	 * If no more data available, return to the caller, no error if len>0.
+	 * We have no other way to know when the string is finished.
+	 */
+			if (len)
+				break;
+			else {
+				DBG(("failed.\n"));
+				return FALSE;
+			}
+		}
+
+		DSPCopyright[len++] = ret;
+
+	  } while (len < CARDNAMELEN);
+
+	DBG(("success.\n"));
+
+	return TRUE;
+}
+
+static void aedsp16_init_tables(void)
+{
+	memset(DSPCopyright, 0, CARDNAMELEN + 1);
+	memset(DSPVersion, 0, CARDVERLEN + 1);
+
+	for (gc = 0; orIRQ[gc].or; gc++)
+		if (orIRQ[gc].val == ae_config.irq) {
+			soft_cfg |= orIRQ[gc].or;
+			soft_cfg_1 |= orIRQ[gc].or;
+		}
+
+	for (gc = 0; orMIRQ[gc].or; gc++)
+		if (orMIRQ[gc].or == ae_config.mpu_irq)
+			soft_cfg |= orMIRQ[gc].or;
+
+	for (gc = 0; orDMA[gc].or; gc++)
+		if (orDMA[gc].val == ae_config.dma) {
+			soft_cfg |= orDMA[gc].or;
+			soft_cfg_1 |= orDMA[gc].or;
+		}
+}
+
+static int aedsp16_init_board(void)
+{
+	aedsp16_init_tables();
+
+	if (aedsp16_dsp_reset(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_dsp_reset: failed!\n");
+		return FALSE;
+	}
+	if (aedsp16_dsp_copyright(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_dsp_copyright: failed!\n");
+		return FALSE;
+	}
+
+	/*
+	 * My AEDSP16 card return SC-6000 in DSPCopyright, so
+	 * if we have something different, we have to be warned.
+	 */
+	if (strcmp("SC-6000", DSPCopyright))
+		printk("[AEDSP16] Warning: non SC-6000 audio card!\n");
+
+	if (aedsp16_dsp_version(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_dsp_version: failed!\n");
+		return FALSE;
+	}
+
+	if (aedsp16_stdcfg(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_stdcfg: failed!\n");
+		return FALSE;
+	}
+
+#if defined(CONFIG_SC6600)
+	if (aedsp16_hard_read(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_hard_read: failed!\n");
+		return FALSE;
+	}
+
+	aedsp16_hard_decode();
+
+	aedsp16_hard_encode();
+
+	if (aedsp16_hard_write(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_hard_write: failed!\n");
+		return FALSE;
+	}
+
+	if (aedsp16_ext_cfg_write(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_ext_cfg_write: failed!\n");
+		return FALSE;
+	}
+#endif /* CONFIG_SC6600 */
+
+	if (aedsp16_setup_board(ae_config.base_io) == FALSE) {
+		printk("[AEDSP16] aedsp16_setup_board: failed!\n");
+		return FALSE;
+	}
+
+#if defined(CONFIG_AEDSP16_MSS)
+	if (ae_config.init & INIT_MSS) {
+		if (aedsp16_init_mss(ae_config.base_io) == FALSE) {
+			printk(
+		"[AEDSP16] Can not initialize Microsoft Sound System mode.\n");
+			return FALSE;
+		}
+	}
+#endif
+
+#if !defined(MODULE) || defined(AEDSP16_INFO) || defined(AEDSP16_DEBUG)
+
+	printk("Audio Excel DSP 16 init v%s (%s %s) [",
+		VERSION, DSPCopyright,
+		DSPVersion);
+
+#if defined(CONFIG_AEDSP16_MPU401)
+	if (ae_config.init & INIT_MPU401) {
+		printk("MPU401");
+		if ((ae_config.init & INIT_MSS) ||
+		    (ae_config.init & INIT_SBPRO))
+			printk(" ");
+	}
+#endif
+
+#if defined(CONFIG_AEDSP16_SBPRO)
+	if (ae_config.init & INIT_SBPRO) {
+		printk("SBPro");
+		if (ae_config.init & INIT_MSS)
+			printk(" ");
+	}
+#endif
+
+#if defined(CONFIG_AEDSP16_MSS)
+	if (ae_config.init & INIT_MSS)
+		printk("MSS");
+#endif
+
+	printk("]\n");
+#endif /* MODULE || AEDSP16_INFO || AEDSP16_DEBUG */
+
+	aedsp16_delay_10msec();
+
+	return TRUE;
+}
+
+#if defined(CONFIG_AEDSP16_SBPRO)
+
+static int init_aedsp16_sb(void)
+{
+	DBG(("init_aedsp16_sb: "));
+
+/*
+ * If the card is already init'ed MSS, we can not init it to SBPRO too
+ * because the board can not emulate simultaneously MSS and SBPRO.
+ */
+	if (ae_config.init & INIT_MSS)
+		return FALSE;
+	if (ae_config.init & INIT_SBPRO)
+		return FALSE;
+
+	ae_config.init |= INIT_SBPRO;
+
+	DBG(("done.\n"));
+
+	return TRUE;
+}
+
+static void uninit_aedsp16_sb(void)
+{
+	DBG(("uninit_aedsp16_sb: "));
+
+	ae_config.init &= ~INIT_SBPRO;
+
+	DBG(("done.\n"));
+}
+#endif
+
+#if defined(CONFIG_AEDSP16_MSS)
+
+static int init_aedsp16_mss(void)
+{
+	DBG(("init_aedsp16_mss: "));
+
+/*
+ * If the card is already init'ed SBPRO, we can not init it to MSS too
+ * because the board can not emulate simultaneously MSS and SBPRO.
+ */
+	if (ae_config.init & INIT_SBPRO)
+		return FALSE;
+	if (ae_config.init & INIT_MSS)
+		return FALSE;
+/*
+ * We must check the AEDSP16_BASE region too because these are the I/O ports
+ * to access card's control registers.
+ */
+	if (!(ae_config.init & INIT_MPU401)) {
+		if (check_region(ae_config.base_io, IOBASE_REGION_SIZE)) {
+			printk(
+			"AEDSP16 BASE I/O port region is already in use.\n");
+			return FALSE;
+		}
+	}
+
+/*
+ * We must allocate the AEDSP16_BASE region too because these are the I/O ports
+ * to access card's control registers.
+ */
+	if (!(ae_config.init & INIT_MPU401))
+		request_region(ae_config.base_io, IOBASE_REGION_SIZE,
+				"aedsp16 (base)");
+
+	ae_config.init |= INIT_MSS;
+
+	DBG(("done.\n"));
+
+	return TRUE;
+}
+
+static void uninit_aedsp16_mss(void)
+{
+	DBG(("uninit_aedsp16_mss: "));
+
+	if ((!(ae_config.init & INIT_MPU401)) &&
+	   (ae_config.init & INIT_MSS)) {
+		release_region(ae_config.base_io, IOBASE_REGION_SIZE);
+		DBG(("AEDSP16 base region released.\n"));
+	}
+
+	ae_config.init &= ~INIT_MSS;
+	DBG(("done.\n"));
+}
+#endif
+
+#if defined(CONFIG_AEDSP16_MPU401)
+
+static int init_aedsp16_mpu(void)
+{
+	DBG(("init_aedsp16_mpu: "));
+
+	if (ae_config.init & INIT_MPU401)
+		return FALSE;
+
+/*
+ * We must check the AEDSP16_BASE region too because these are the I/O ports
+ * to access card's control registers.
+ */
+	if (!(ae_config.init & (INIT_MSS | INIT_SBPRO))) {
+		if (check_region(ae_config.base_io, IOBASE_REGION_SIZE)) {
+			printk(
+			"AEDSP16 BASE I/O port region is already in use.\n");
+			return FALSE;
+		}
+	}
+
+	if (!(ae_config.init & (INIT_MSS | INIT_SBPRO)))
+		request_region(ae_config.base_io, IOBASE_REGION_SIZE,
+				"aedsp16 (base)");
+
+	ae_config.init |= INIT_MPU401;
+
+	DBG(("done.\n"));
+
+	return TRUE;
+}
+
+static void uninit_aedsp16_mpu(void)
+{
+	DBG(("uninit_aedsp16_mpu: "));
+
+	if ((!(ae_config.init & (INIT_MSS | INIT_SBPRO))) &&
+	   (ae_config.init & INIT_MPU401)) {
+		release_region(ae_config.base_io, IOBASE_REGION_SIZE);
+		DBG(("AEDSP16 base region released.\n"));
+	}
+
+	ae_config.init &= ~INIT_MPU401;
+
+	DBG(("done.\n"));
+}
+#endif
+
+int init_aedsp16(void)
+{
+	int initialized = FALSE;
+
+	ae_config.base_io = AEDSP16_BASE;
+#if defined(CONFIG_AEDSP16_SBPRO)
+	ae_config.irq = AEDSP16_SBC_IRQ;
+	ae_config.dma = AEDSP16_SBC_DMA;
+#endif
+#if defined(CONFIG_AEDSP16_MSS)
+	ae_config.irq = AEDSP16_MSS_IRQ;
+	ae_config.dma = AEDSP16_MSS_DMA;
+#endif
+#if defined(CONFIG_AEDSP16_MPU401)
+	ae_config.mpu_irq = AEDSP16_MPU_IRQ;
+#endif
+
+	DBG(("Initializing BASE[0x%x] IRQ[%d] DMA[%d] MIRQ[%d]\n",
+	     ae_config.base_io,ae_config.irq,ae_config.dma,ae_config.mpu_irq));
+
+#if defined(CONFIG_AEDSP16_SBPRO)
+	if (init_aedsp16_sb() == FALSE)
+		uninit_aedsp16_sb();
+	else
+		initialized = TRUE;
+#endif
+
+#if defined(CONFIG_AEDSP16_MPU401)
+	if (init_aedsp16_mpu() == FALSE)
+		uninit_aedsp16_mpu();
+	else
+		initialized = TRUE;
+#endif
+
+#if defined(CONFIG_AEDSP16_MSS)
+
+/*
+ * In the sequence of init routines, the MSS init MUST be the last!
+ * This because of the special register programming the MSS mode needs.
+ * A board reset would disable the MSS mode restoring the default SBPRO
+ * mode.
+ */
+	if (init_aedsp16_mss() == FALSE)
+		uninit_aedsp16_mss();
+	else
+		initialized = TRUE;
+#endif
+
+	if (initialized)
+		initialized = aedsp16_init_board();
+	return initialized;
+}
+
+void uninit_aedsp16(void)
+{
+#if defined(CONFIG_AEDSP16_MSS)
+	uninit_aedsp16_mss();
+#endif
+
+#if defined(CONFIG_AEDSP16_SBPRO)
+	uninit_aedsp16_sb();
+#endif
+
+#if defined(CONFIG_AEDSP16_MPU401)
+	uninit_aedsp16_mpu();
+#endif
+}
+#endif				/* CONFIG_AEDSP16 */
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/awe_config.h linux/drivers/sound/lowlevel/awe_config.h
--- v2.1.27/linux/drivers/sound/lowlevel/awe_config.h	Wed Dec 31 16:00:00 1969
+++ linux/drivers/sound/lowlevel/awe_config.h	Mon Jan 13 00:18:46 1997
@@ -0,0 +1,141 @@
+/*
+ * sound/awe_config.h
+ *
+ * Configuration of AWE32 sound driver
+ *   version 0.2.99e; Dec. 10, 1997
+ *
+ * Copyright (C) 1996,1997 Takashi Iwai
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef AWE_CONFIG_H_DEF
+#define AWE_CONFIG_H_DEF
+
+/*----------------------------------------------------------------
+ * system configuration
+ *----------------------------------------------------------------*/
+
+/* if you're using obsolete VoxWare 3.0.x on Linux 1.2.x (or FreeBSD),
+ * define the following line.
+ */
+#undef AWE_OBSOLETE_VOXWARE
+
+#ifdef __FreeBSD__
+#  define AWE_OBSOLETE_VOXWARE
+#endif
+
+/* if you're using OSS-Lite on Linux 2.1.6 or later, define the
+ * following line.
+ */
+#define AWE_NEW_KERNEL_INTERFACE
+
+/* if you have lowlevel.h in the lowlevel directory (OSS-Lite), define
+ * the following line.
+ */
+#define HAS_LOWLEVEL_H
+
+/* if your system doesn't support patch manager (OSS 3.7 or newer),
+ * define the following line.
+ */
+#define AWE_NO_PATCHMGR
+ 
+
+/*----------------------------------------------------------------
+ * AWE32 card configuration:
+ * uncomment the following lines only when auto detection doesn't
+ * work properly on your machine.
+ *----------------------------------------------------------------*/
+
+/*#define AWE_DEFAULT_BASE_ADDR	0x620*/	/* base port address */
+/*#define AWE_DEFAULT_MEM_SIZE	512*/	/* kbytes */
+
+
+/*----------------------------------------------------------------
+ * maximum size of sample table:
+ * the followings are for ROM GM and 512k GS samples.  if your have
+ * additional DRAM and SoundFonts, increase these values.
+ *----------------------------------------------------------------*/
+
+#define AWE_MAX_SAMPLES 400
+#define AWE_MAX_INFOS 1500
+
+
+/*----------------------------------------------------------------
+ * chorus & reverb effects send for FM chip: from 0 to 0xff
+ * larger numbers often cause weird sounds.
+ *----------------------------------------------------------------*/
+
+#define DEF_FM_CHORUS_DEPTH	0x10
+#define DEF_FM_REVERB_DEPTH	0x10
+
+
+/*----------------------------------------------------------------*
+ * other compile conditions
+ *----------------------------------------------------------------*/
+
+/* initialize FM passthrough even without extended RAM */
+#undef AWE_ALWAYS_INIT_FM
+
+/* debug on */
+#define AWE_DEBUG_ON
+
+/* verify checksum for uploading samples */
+#define AWE_CHECKSUM_DATA
+#define AWE_CHECKSUM_MEMORY
+
+/* GUS compatible mode */
+#define AWE_HAS_GUS_COMPATIBILITY
+
+/* accept all notes/sounds off controls */
+#undef AWE_ACCEPT_ALL_SOUNDS_CONTROL
+
+
+#ifdef linux
+/* i tested this only on my linux */
+#define INLINE  __inline__
+#else
+#define INLINE /**/
+#endif
+
+
+/*----------------------------------------------------------------*/
+
+/* reading configuration of sound driver */
+
+#ifdef AWE_OBSOLETE_VOXWARE
+
+#ifdef __FreeBSD__
+#  include <i386/isa/sound/sound_config.h>
+#else
+#  include "sound_config.h"
+#endif
+
+#if defined(CONFIGURE_SOUNDCARD) && !defined(EXCLUDE_AWE32)
+#define CONFIG_AWE32_SYNTH
+#endif
+
+#else /* AWE_OBSOLETE_VOXWARE */
+
+#ifdef HAS_LOWLEVEL_H
+#include "lowlevel.h"
+#endif
+
+#include "../sound_config.h"
+
+#endif /* AWE_OBSOLETE_VOXWARE */
+
+
+#endif  /* AWE_CONFIG_H_DEF */
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/awe_hw.h linux/drivers/sound/lowlevel/awe_hw.h
--- v2.1.27/linux/drivers/sound/lowlevel/awe_hw.h	Fri Oct 25 03:06:35 1996
+++ linux/drivers/sound/lowlevel/awe_hw.h	Wed Jan 29 00:55:45 1997
@@ -1,26 +1,29 @@
-/*================================================================
- * AWE32 access routines
- *================================================================*/
-
-#ifndef AWE_HW_H_DEF
-#define AWE_HW_H_DEF
-
-/*
- * user configuration:
- * if auto detection can't work properly, define the following values
- * for your machine.
- */
-/*#define AWE_DEFAULT_BASE_ADDR	0x620*/	/* base port address */
-/*#define AWE_DEFAULT_MEM_SIZE	512*/	/* kbytes */
-
-
 /*
- * maximum size of sample table:
- * if your data overflow, increase the following values.
+ * sound/awe_hw.h
+ *
+ * Access routines and definitions for the low level driver for the 
+ * AWE32/Sound Blaster 32 wave table synth.
+ *   version 0.3.1; Jan. 21, 1997
+ *
+ * Copyright (C) 1996,1997 Takashi Iwai
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-#define AWE_MAX_SAMPLES		400
-#define AWE_MAX_INFOS		900	/* GS presets has 801 infos! */
 
+#ifndef AWE_HW_H_DEF
+#define AWE_HW_H_DEF
 
 /*
  * Emu-8000 control registers
@@ -45,6 +48,7 @@
 #define AWE_HWCF4	awe_cmd_idx(1,9),  Data1	/* DW: config dw 4 */
 #define AWE_HWCF5	awe_cmd_idx(1,10), Data1	/* DW: config dw 5 */
 #define AWE_HWCF6	awe_cmd_idx(1,13), Data1	/* DW: config dw 6 */
+#define AWE_HWCF7	awe_cmd_idx(1,14), Data1	/* DW: config dw 7? (not documented) */
 #define AWE_SMALR	awe_cmd_idx(1,20), Data1	/* DW: sound memory address for left read */
 #define AWE_SMARR	awe_cmd_idx(1,21), Data1	/* DW:    for right read */
 #define AWE_SMALW	awe_cmd_idx(1,22), Data1	/* DW: sound memory address for left write */
@@ -76,7 +80,7 @@
 #define AWE_TREMFRQ(ch)	awe_cmd_idx(4,ch), Data3	/* W: LFO#1 tremolo amount and freq */
 #define AWE_FM2FRQ2(ch)	awe_cmd_idx(5,ch), Data3	/* W: LFO#2 vibrato amount and freq */
 
-/*  used during detection (returns ROM version ?)                    */
+/* used during detection (returns ROM version?; not documented in ADIP) */
 #define AWE_U1		0xE0, Data3	  /* (R)(W) used in initialization */
 #define AWE_U2(ch)	0xC0+(ch), Data3  /* (W)(W) used in init envelope  */
 
@@ -84,6 +88,11 @@
 #define AWE_MAX_VOICES		32
 #define AWE_NORMAL_VOICES	30	/*30&31 are reserved for DRAM refresh*/
 
+#define AWE_MAX_CHANNELS	32	/* max midi channels (must >= voices) */
+
 #define AWE_DRAM_OFFSET		0x200000
+#define AWE_MAX_DRAM_SIZE	(28 * 1024)	/* 28 MB is max onboard memory */
+
+#define AWE_DEFAULT_ATTENUATION	32	/* 12dB below */
 
 #endif
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/awe_voice.h linux/drivers/sound/lowlevel/awe_voice.h
--- v2.1.27/linux/drivers/sound/lowlevel/awe_voice.h	Fri Oct 25 03:06:35 1996
+++ linux/drivers/sound/lowlevel/awe_voice.h	Wed Dec 31 16:00:00 1969
@@ -1,242 +0,0 @@
-#ifndef AWE_VOICE_H
-#define AWE_VOICE_H
-/*================================================================
- * awe_voice.h -- voice information for AWE32 wave table synth
- *	ver.0.2.0; Oct. 16, 1996
- *	copyright (c) 1996  by Takashi Iwai
- *================================================================*/
-
-#ifndef SAMPLE_TYPE_AWE32
-#define SAMPLE_TYPE_AWE32	0x20
-#endif
-
-#ifndef _PATCHKEY
-#define _PATCHKEY(id) ((id<<8)|0xfd)
-#endif
-
-/*----------------------------------------------------------------
- * patch information record
- *----------------------------------------------------------------*/
-
-/* patch interface header: 16 bytes */
-typedef struct awe_patch_info {
-	short key;			/* use AWE_PATCH here */
-#define AWE_PATCH	_PATCHKEY(0x07)
-
-	short device_no;		/* synthesizer number */
-	unsigned short sf_id;		/* file id (should be zero) */
-	short sf_version;		/* patch version (not referred) */
-	long len;			/* data length (without this header) */
-
-	short type;			/* following data type */
-#define AWE_LOAD_INFO		0
-#define AWE_LOAD_DATA		1
-
-	short reserved;			/* word alignment data */
-	char data[0];			/* patch data follows here */
-} awe_patch_info;
-
-
-/*----------------------------------------------------------------
- * raw voice information record
- *----------------------------------------------------------------*/
-
-/* wave table envelope & effect parameters to control EMU8000 */
-typedef struct _awe_voice_parm {
-	unsigned short moddelay;	/* modulation delay (0x8000) */
-	unsigned short modatkhld;	/* modulation attack & hold time (0x7f7f) */
-	unsigned short moddcysus;	/* modulation decay & sustain (0x7f7f) */
-	unsigned short modrelease;	/* modulation release time (0x807f) */
-	short modkeyhold, modkeydecay;	/* envelope change per key (not used) */
-	unsigned short voldelay;	/* volume delay (0x8000) */
-	unsigned short volatkhld;	/* volume attack & hold time (0x7f7f) */
-	unsigned short voldcysus;	/* volume decay & sustain (0x7f7f) */
-	unsigned short volrelease;	/* volume release time (0x807f) */
-	short volkeyhold, volkeydecay;	/* envelope change per key (not used) */
-	unsigned short lfo1delay;	/* LFO1 delay (0x8000) */
-	unsigned short lfo2delay;	/* LFO2 delay (0x8000) */
-	unsigned short pefe;		/* modulation pitch & cutoff (0x0000) */
-	unsigned short fmmod;		/* LFO1 pitch & cutoff (0x0000) */
-	unsigned short tremfrq;		/* LFO1 volume & freq (0x0000) */
-	unsigned short fm2frq2;		/* LFO2 pitch & freq (0x0000) */
-	unsigned char cutoff;		/* initial cutoff (0xff) */
-	unsigned char filterQ;		/* initial filter Q [0-15] (0x0) */
-	unsigned char chorus;		/* chorus send (0x00) */
-	unsigned char reverb;		/* reverb send (0x00) */
-	unsigned short reserved[4];	/* not used */
-} awe_voice_parm;
-
-/* wave table parameters: 92 bytes */
-typedef struct _awe_voice_info {
-	unsigned short sf_id;		/* file id (should be zero) */
-	unsigned short sample;		/* sample id */
-	long start, end;		/* sample offset correction */
-	long loopstart, loopend;	/* loop offset correction */
-	short rate_offset;		/* sample rate pitch offset */
-	unsigned short mode;		/* sample mode */
-#define AWE_MODE_ROMSOUND		0x8000
-#define AWE_MODE_STEREO			1
-#define AWE_MODE_LOOPING		2
-#define AWE_MODE_NORELEASE		4	/* obsolete */
-#define AWE_MODE_INIT_PARM		8
-
-	short root;			/* midi root key */
-	short tune;			/* pitch tuning (in cents) */
-	char low, high;			/* key note range */
-	char vellow, velhigh;		/* velocity range */
-	char fixkey, fixvel;		/* fixed key, velocity */
-	char pan, fixpan;		/* panning, fixed panning */
-	short exclusiveClass;		/* exclusive class (0 = none) */
-	unsigned char amplitude;	/* sample volume (127 max) */
-	unsigned char attenuation;	/* attenuation (0.375dB) */
-	short scaleTuning;		/* pitch scale tuning(%), normally 100 */
-	awe_voice_parm parm;		/* voice envelope parameters */
-	short index;			/* internal index (set by driver) */
-} awe_voice_info;
-
-/* instrument info header: 4 bytes */
-typedef struct _awe_voice_rec {
-	unsigned char bank;		/* midi bank number */
-	unsigned char instr;		/* midi preset number */
-	short nvoices;			/* number of voices */
-	awe_voice_info info[0];		/* voice information follows here */
-} awe_voice_rec;
-
-
-/*----------------------------------------------------------------
- * sample wave information
- *----------------------------------------------------------------*/
-
-/* wave table sample header: 32 bytes */
-typedef struct awe_sample_info {
-	unsigned short sf_id;		/* file id (should be zero) */
-	unsigned short sample;		/* sample id */
-	long start, end;		/* start & end offset */
-	long loopstart, loopend;	/* loop start & end offset */
-	long size;			/* size (0 = ROM) */
-	short checksum_flag;		/* use check sum = 1 */
-	unsigned short mode_flags;	/* mode flags */
-#define AWE_SAMPLE_8BITS	1	/* wave data is 8bits */
-#define AWE_SAMPLE_UNSIGNED	2	/* wave data is unsigned */
-#define AWE_SAMPLE_NO_BLANK	4	/* no blank loop is attached */
-#define AWE_SAMPLE_SINGLESHOT	8	/* single-shot w/o loop */
-#define AWE_SAMPLE_BIDIR_LOOP	16	/* bidirectional looping */
-#define AWE_SAMPLE_STEREO_LEFT	32	/* stereo left sound */
-#define AWE_SAMPLE_STEREO_RIGHT	64	/* stereo right sound */
-	unsigned long checksum;		/* check sum */
-	unsigned short data[0];		/* sample data follows here */
-} awe_sample_info;
-
-
-/*----------------------------------------------------------------
- * awe hardware controls
- *----------------------------------------------------------------*/
-
-#define _AWE_DEBUG_MODE			0x00
-#define _AWE_REVERB_MODE		0x01
-#define _AWE_CHORUS_MODE		0x02
-#define _AWE_REMOVE_LAST_SAMPLES	0x03
-#define _AWE_INITIALIZE_CHIP		0x04
-#define _AWE_SEND_EFFECT		0x05
-#define _AWE_TERMINATE_CHANNEL		0x06
-#define _AWE_TERMINATE_ALL		0x07
-#define _AWE_INITIAL_VOLUME		0x08
-#define _AWE_SET_GUS_BANK		0x09
-
-#define _AWE_MODE_FLAG			0x80
-#define _AWE_COOKED_FLAG		0x40	/* not supported */
-#define _AWE_MODE_VALUE_MASK		0x3F
-
-#define _AWE_CMD(chn, voice, cmd, p1, p2) \
-{_SEQ_NEEDBUF(8); _seqbuf[_seqbufptr] = SEQ_PRIVATE;\
- _seqbuf[_seqbufptr+1] = chn;\
- _seqbuf[_seqbufptr+2] = _AWE_MODE_FLAG|(cmd);\
- _seqbuf[_seqbufptr+3] = voice;\
- *(unsigned short*)&_seqbuf[_seqbufptr+4] = p1;\
- *(unsigned short*)&_seqbuf[_seqbufptr+6] = p2;\
- _SEQ_ADVBUF(8);}
-
-#define AWE_DEBUG_MODE(dev,p1)	_AWE_CMD(dev, 0, _AWE_DEBUG_MODE, p1, 0)
-#define AWE_REVERB_MODE(dev,p1)	_AWE_CMD(dev, 0, _AWE_REVERB_MODE, p1, 0)
-#define AWE_CHORUS_MODE(dev,p1)	_AWE_CMD(dev, 0, _AWE_CHORUS_MODE, p1, 0)
-#define AWE_REMOVE_LAST_SAMPLES(dev) _AWE_CMD(dev, 0, _AWE_REMOVE_LAST_SAMPLES, 0, 0)
-#define AWE_INITIALIZE_CHIP(dev) _AWE_CMD(dev, 0, _AWE_INITIALIZE_CHIP, 0, 0)
-#define AWE_SEND_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,type,value)
-#define AWE_TERMINATE_CHANNEL(dev,voice) _AWE_CMD(dev,voice,_AWE_TERMINATE_CHANNEL,0,0)
-#define AWE_TERMINATE_ALL(dev) _AWE_CMD(dev, 0, _AWE_TERMINATE_ALL, 0, 0)
-#define AWE_INITIAL_VOLUME(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 0)
-#define AWE_SET_GUS_BANK(dev,bank) _AWE_CMD(dev, 0, _AWE_SET_GUS_BANK, bank, 0)
-
-/* reverb mode */
-#define	AWE_REVERB_ROOM1	0
-#define AWE_REVERB_ROOM2	1
-#define	AWE_REVERB_ROOM3	2
-#define	AWE_REVERB_HALL1	3
-#define	AWE_REVERB_HALL2	4
-#define	AWE_REVERB_PLATE	5
-#define	AWE_REVERB_DELAY	6
-#define	AWE_REVERB_PANNINGDELAY 7
-
-/* chorus mode */
-#define AWE_CHORUS_1		0
-#define	AWE_CHORUS_2		1
-#define	AWE_CHORUS_3		2
-#define	AWE_CHORUS_4		3
-#define	AWE_CHORUS_FEEDBACK	4
-#define	AWE_CHORUS_FLANGER	5
-#define	AWE_CHORUS_SHORTDELAY	6
-#define	AWE_CHORUS_SHORTDELAY2	7
-
-/* effects */
-enum {
-
-/* modulation envelope parameters */
-/* 0*/	AWE_FX_ENV1_DELAY,	/* WORD: ENVVAL */
-/* 1*/	AWE_FX_ENV1_ATTACK,	/* BYTE: up ATKHLD */
-/* 2*/	AWE_FX_ENV1_HOLD,	/* BYTE: lw ATKHLD */
-/* 3*/	AWE_FX_ENV1_DECAY,	/* BYTE: lw DCYSUS */
-/* 4*/	AWE_FX_ENV1_RELEASE,	/* BYTE: lw DCYSUS */
-/* 5*/	AWE_FX_ENV1_SUSTAIN,	/* BYTE: up DCYSUS */
-/* 6*/	AWE_FX_ENV1_PITCH,	/* BYTE: up PEFE */
-/* 7*/	AWE_FX_ENV1_CUTOFF,	/* BYTE: lw PEFE */
-
-/* volume envelope parameters */
-/* 8*/	AWE_FX_ENV2_DELAY,	/* WORD: ENVVOL */
-/* 9*/	AWE_FX_ENV2_ATTACK,	/* BYTE: up ATKHLDV */
-/*10*/	AWE_FX_ENV2_HOLD,	/* BYTE: lw ATKHLDV */
-/*11*/	AWE_FX_ENV2_DECAY,	/* BYTE: lw DCYSUSV */
-/*12*/	AWE_FX_ENV2_RELEASE,	/* BYTE: lw DCYSUSV */
-/*13*/	AWE_FX_ENV2_SUSTAIN,	/* BYTE: up DCYSUSV */
-	
-/* LFO1 (tremolo & vibrato) parameters */
-/*14*/	AWE_FX_LFO1_DELAY,	/* WORD: LFO1VAL */
-/*15*/	AWE_FX_LFO1_FREQ,	/* BYTE: lo TREMFRQ */
-/*16*/	AWE_FX_LFO1_VOLUME,	/* BYTE: up TREMFRQ */
-/*17*/	AWE_FX_LFO1_PITCH,	/* BYTE: up FMMOD */
-/*18*/	AWE_FX_LFO1_CUTOFF,	/* BYTE: lo FMMOD */
-
-/* LFO2 (vibrato) parameters */
-/*19*/	AWE_FX_LFO2_DELAY,	/* WORD: LFO2VAL */
-/*20*/	AWE_FX_LFO2_FREQ,	/* BYTE: lo FM2FRQ2 */
-/*21*/	AWE_FX_LFO2_PITCH,	/* BYTE: up FM2FRQ2 */
-
-/* Other overall effect parameters */
-/*22*/	AWE_FX_INIT_PITCH,	/* SHORT: pitch offset */
-/*23*/	AWE_FX_CHORUS,		/* BYTE: chorus effects send (0-255) */
-/*24*/	AWE_FX_REVERB,		/* BYTE: reverb effects send (0-255) */
-/*25*/	AWE_FX_CUTOFF,		/* BYTE: up IFATN */
-/*26*/	AWE_FX_FILTERQ,		/* BYTE: up CCCA */
-
-/* Sample / loop offset changes */
-/*27*/	AWE_FX_SAMPLE_START,	/* SHORT: offset */
-/*28*/	AWE_FX_LOOP_START,	/* SHORT: offset */
-/*29*/	AWE_FX_LOOP_END,	/* SHORT: offset */
-/*30*/	AWE_FX_COARSE_SAMPLE_START,	/* SHORT: upper word offset */
-/*31*/	AWE_FX_COARSE_LOOP_START,	/* SHORT: upper word offset */
-/*32*/	AWE_FX_COARSE_LOOP_END,		/* SHORT: upper word offset */
-
-	AWE_FX_END,
-};
-
-
-#endif /* AWE_VOICE_H */
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/awe_wave.c linux/drivers/sound/lowlevel/awe_wave.c
--- v2.1.27/linux/drivers/sound/lowlevel/awe_wave.c	Wed Oct 30 21:17:02 1996
+++ linux/drivers/sound/lowlevel/awe_wave.c	Sun Feb 23 23:24:48 1997
@@ -1,58 +1,54 @@
-/*================================================================
- * awe_wave.c -- driver for AWE32 wave table synth
- *	version 0.2.0; Oct. 16, 1996
- *	copyright (c) 1996  by Takashi Iwai
- *================================================================*/
-
-/* if you're using obsolete VoxWare 3.0.x on Linux 1.2.x (or FreeBSD),
- * uncomment the following line
+/*
+ * sound/awe_wave.c
+ *
+ * The low level driver for the AWE32/Sound Blaster 32 wave table synth.
+ *   version 0.3.1b; Jan. 21, 1997
+ *
+ * Copyright (C) 1996,1997 Takashi Iwai
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
-/* #define AWE_OBSOLETE_VOXWARE */
 
-#include "lowlevel.h"
-
-#ifdef AWE_OBSOLETE_VOXWARE
-
-#include "sound_config.h"
-#if !defined(EXCLUDE_AWE32)
-#define CONFIG_AWE32_SYNTH
+#define AWEDRV_VERSION		"0.3.1b"
+#ifdef __FreeBSD__
+#  include <i386/isa/sound/awe_config.h>
+#else
+#  include "awe_config.h"
 #endif
 
-#else /* AWE_OBSOLETE_VOXWARE */
-
-#include "../sound_config.h"
-
-#endif /* AWE_OBSOLETE_VOXWARE */
-
-
-/*----------------------------------------------------------------*
- * compile condition
- *----------------------------------------------------------------*/
-
-/* initialize FM passthrough even without extended RAM */
-/*#define AWE_ALWAYS_INIT_FM*/
-
-/* debug on */
-#define AWE_DEBUG_ON
-
-/* verify checksum for uploading samples */
-#define AWE_CHECKSUM_DATA
-#define AWE_CHECKSUM_MEMORY
-
-/* disable interruption during sequencer operation */
-/*#define AWE_NEED_DISABLE_INTR*/
-
 /*----------------------------------------------------------------*/
 
 #ifdef CONFIG_AWE32_SYNTH
 
-#include "awe_hw.h"
-#include "awe_voice.h"
+#ifdef __FreeBSD__
+#  include <i386/isa/sound/awe_hw.h>
+#  include <i386/isa/sound/awe_voice.h>
+#else
+#  include "awe_hw.h"
+#  include <linux/awe_voice.h>
+#endif
 
 #ifdef AWE_OBSOLETE_VOXWARE
-#include "tuning.h"
+#  ifdef __FreeBSD__
+#    define SEQUENCER_C
+#    include <i386/isa/sound/tuning.h>
+#  else
+#    include "tuning.h"
+#  endif
 #else
-#include "../tuning.h"
+#  include "../tuning.h"
 #endif
 
 #ifdef linux
@@ -66,8 +62,8 @@
  * debug message
  *----------------------------------------------------------------*/
 
-#ifdef AWE_DEBUG_ON
 static int debug_mode = 0;
+#ifdef AWE_DEBUG_ON
 #define DEBUG(LVL,XXX)	{if (debug_mode > LVL) { XXX; }}
 #define ERRMSG(XXX)	{if (debug_mode) { XXX; }}
 #define FATALERR(XXX)	XXX
@@ -95,6 +91,7 @@
 
 #define AWE_MAX_PRESETS		256
 #define AWE_DEFAULT_BANK	0
+#define AWE_DRUM_BANK		128
 
 /* preset table index */
 static awe_voice_list *preset_table[AWE_MAX_PRESETS];
@@ -103,35 +100,68 @@
  * voice table
  *----------------------------------------------------------------*/
 
-#define AWE_FX_BYTES	((AWE_FX_END+7)/8)
+/* effects table */
+#define AWE_FX_NBYTES	((AWE_FX_END+7)/8)
+typedef	struct FX_Rec { /* channel effects */
+	unsigned char flags[AWE_FX_NBYTES];
+	short val[AWE_FX_END];
+} FX_Rec;
 
-typedef struct _voice_info {
-	int state;		/* status (on = 1, off = 0) */
-	int note;		/* midi key (0-127) */
-	int velocity;		/* midi velocity (0-127) */
+
+/* channel parameters */
+typedef struct _awe_chan_info {
+	int bank;		/* current tone bank */
+	int instr;		/* current program */
 	int bender;		/* midi pitchbend (-8192 - 8192) */
 	int bender_range;	/* midi bender range (x100) */
 	int panning;		/* panning (0-127) */
 	int main_vol;		/* channel volume (0-127) */
 	int expression_vol;	/* midi expression (0-127) */
+	awe_voice_list *vrec;	/* instrument list */
+	awe_voice_list *def_vrec; /* default instrument list */
+	FX_Rec fx;		/* effects */
+	int sustained;		/* sustain status in MIDI */
+} awe_chan_info;
+
+/* voice parameters */
+typedef struct _voice_info {
+	int state;
+#define AWE_ST_OFF		0	/* no sound */
+#define AWE_ST_ON		1	/* playing */
+#define AWE_ST_STANDBY		2	/* stand by for playing */
+#define AWE_ST_SUSTAINED	3	/* sustained */
+#define AWE_ST_MARK		4	/* marked for allocation */
+
+	int ch;			/* midi channel */
+	int key;		/* internal key for search */
+	int time;		/* allocated time */
+	awe_chan_info	*cinfo;	/* channel info */
+
+	int note;		/* midi key (0-127) */
+	int velocity;		/* midi velocity (0-127) */
+	awe_voice_info *sample;	/* assigned voice */
 
 	/* EMU8000 parameters */
 	int apitch;		/* pitch parameter */
 	int avol;		/* volume parameter */
-
-	/* instrument parameters */
-	int bank;		/* current tone bank */
-	int instr;		/* current program */
-	awe_voice_list *vrec;
-	awe_voice_info *sample;
-
-	/* channel effects */
-	unsigned char fx_flags[AWE_FX_BYTES];
-	short fx[AWE_FX_END];
+	int apan;		/* panning parameter */
 } voice_info;
 
+/* voice information */
 static voice_info voices[AWE_MAX_VOICES];
 
+#define IS_NO_SOUND(v)	(voices[v].state == AWE_ST_OFF || voices[v].state == AWE_ST_STANDBY)
+#define IS_NO_EFFECT(v)	(voices[v].state != AWE_ST_ON)
+#define IS_PLAYING(v)	(!IS_NO_SOUND(v))
+
+
+/* MIDI channel effects information (for hw control) */
+#if AWE_MAX_CHANNELS < AWE_MAX_VOICES
+static awe_chan_info channels[AWE_MAX_VOICES];
+#else
+static awe_chan_info channels[AWE_MAX_CHANNELS];
+#endif
+
 
 /*----------------------------------------------------------------
  * global variables
@@ -155,12 +185,21 @@
 
 static int reverb_mode = 0;		/* reverb mode */
 static int chorus_mode = 0;		/* chorus mode */
-static unsigned short init_atten = 32;  /* 12dB */
+static unsigned short init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */
+
+static int awe_present = FALSE;		/* awe device present? */
+static int awe_busy = FALSE;		/* awe device opened? */
+
+#define DEFAULT_DRUM_FLAGS	(1 << 9)
+#define IS_DRUM_CHANNEL(c)	(drum_flags & (1 << (c)))
+static unsigned long drum_flags = DEFAULT_DRUM_FLAGS; /* channel flags */
+
+static int awe_channel_mode = 0;		/* channel control mode */
 
-static int awe_present = 0;		/* awe device present? */
-static int awe_busy = 0;		/* awe device opened? */
+static int current_alloc_time = 0;		/* voice allocation time */
 
 static int awe_gus_bank = AWE_DEFAULT_BANK;	/* GUS default bank number */
+static int awe_exclusive_sound = TRUE;		/* exclusive sound on */
 
 
 static struct synth_info awe_info = {
@@ -202,6 +241,7 @@
 /* set voice parameters */
 static void awe_init_voice_info(awe_voice_info *vp);
 static void awe_init_voice_parm(awe_voice_parm *pp);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 static int freq_to_note(int freq);
 static int calc_rate_offset(int Hz);
 /*static int calc_parm_delay(int msec);*/
@@ -209,26 +249,31 @@
 static int calc_parm_attack(int msec);
 static int calc_parm_decay(int msec);
 static int calc_parm_search(int msec, short *table);
+#endif
 
 /* turn on/off note */
 static void awe_note_on(int voice);
 static void awe_note_off(int voice);
 static void awe_terminate(int voice);
-static void awe_exclusive_off(int voice);
+static void awe_exclusive_off(int voice, int exclass);
 
 /* calculate voice parameters */
-static void awe_set_pitch(int voice);
-static void awe_set_volume(int voice);
+typedef void (*fx_affect_func)(int voice, int forced);
+static void awe_set_pitch(int voice, int forced);
+static void awe_set_voice_pitch(int voice, int forced);
+static void awe_set_volume(int voice, int forced);
+static void awe_set_voice_vol(int voice, int forced);
 static void awe_set_pan(int voice, int forced);
-static void awe_fx_fmmod(int voice);
-static void awe_fx_tremfrq(int voice);
-static void awe_fx_fm2frq2(int voice);
-static void awe_fx_cutoff(int voice);
-static void awe_fx_initpitch(int voice);
+static void awe_fx_fmmod(int voice, int forced);
+static void awe_fx_tremfrq(int voice, int forced);
+static void awe_fx_fm2frq2(int voice, int forced);
+static void awe_fx_cutoff(int voice, int forced);
 static void awe_calc_pitch(int voice);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 static void awe_calc_pitch_from_freq(int voice, int freq);
+#endif
 static void awe_calc_volume(int voice);
-static void awe_voice_init(int voice, int inst_only);
+static void awe_voice_init(int voice);
 
 /* sequencer interface */
 static int awe_open(int dev, int mode);
@@ -237,6 +282,7 @@
 static int awe_kill_note(int dev, int voice, int note, int velocity);
 static int awe_start_note(int dev, int v, int note_num, int volume);
 static int awe_set_instr(int dev, int voice, int instr_no);
+static int awe_set_instr_2(int dev, int voice, int instr_no);
 static void awe_reset(int dev);
 static void awe_hw_control(int dev, unsigned char *event);
 static int awe_load_patch(int dev, int format, const char *addr,
@@ -245,23 +291,35 @@
 static void awe_controller(int dev, int voice, int ctrl_num, int value);
 static void awe_panning(int dev, int voice, int value);
 static void awe_volume_method(int dev, int mode);
+#ifndef AWE_NO_PATCHMGR
+static int awe_patchmgr(int dev, struct patmgr_info *rec);
+#endif
 static void awe_bender(int dev, int voice, int value);
 static int awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc);
 static void awe_setup_voice(int dev, int voice, int chn);
 
 /* hardware controls */
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 static void awe_hw_gus_control(int dev, int cmd, unsigned char *event);
+#endif
 static void awe_hw_awe_control(int dev, int cmd, unsigned char *event);
+static void awe_voice_change(int voice, fx_affect_func func);
+static void awe_sustain_off(int voice, int forced);
 
 /* voice search */
-static awe_voice_info *awe_search_voice(int voice, int note);
 static awe_voice_list *awe_search_instr(int bank, int preset);
+static int awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist);
+static void awe_alloc_multi_voices(int ch, int note, int velocity);
+static void awe_alloc_one_voice(int voice, int note, int velocity);
+static int awe_clear_voice(void);
 
 /* load / remove patches */
 static void awe_check_loaded(void);
 static int awe_load_info(awe_patch_info *patch, const char *addr);
 static int awe_load_data(awe_patch_info *patch, const char *addr);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 static int awe_load_guspatch(const char *addr, int offs, int size, int pmgr_flag);
+#endif
 static int awe_write_wave_data(const char *addr, long offset, int size);
 static awe_voice_list *awe_get_removed_list(awe_voice_list *curp);
 static void awe_remove_samples(void);
@@ -272,6 +330,7 @@
 static void awe_init_dma(void);
 static void awe_init_array(void);
 static void awe_send_array(unsigned short *data);
+static void awe_tweak_voice(int voice);
 static void awe_tweak(void);
 static void awe_init_fm(void);
 static int awe_open_dram_for_write(int offset);
@@ -280,6 +339,7 @@
 static void awe_close_dram(void);
 static void awe_close_dram_for_read(void);
 static void awe_write_dram(unsigned short c);
+static int awe_detect_base(int addr);
 static int awe_detect(void);
 static int awe_check_dram(void);
 static void awe_set_chorus_mode(int mode);
@@ -302,17 +362,6 @@
 #endif /* AWE_OBSOLETE_VOXWARE */
 
 
-#ifdef AWE_NEED_DISABLE_INTR
-#define DECL_INTR_FLAGS(x)	unsigned long x
-#else
-#undef DISABLE_INTR
-#undef RESTORE_INTR
-#define DECL_INTR_FLAGS(x) /**/
-#define DISABLE_INTR(x) /**/
-#define RESTORE_INTR(x) /**/
-#endif
-
-
 /* macros for Linux and FreeBSD compatibility */
 
 #undef OUTW
@@ -324,6 +373,8 @@
 #ifdef linux
 #  define NO_DATA_ERR                 ENODATA
 #  define OUTW(data, addr)            outw(data, addr)
+
+#ifdef AWE_NEW_KERNEL_INTERFACE
 #  define COPY_FROM_USER(target, source, offs, count) \
               copy_from_user( ((caddr_t)(target)),(source)+(offs),(count) )
 #  define GET_BYTE_FROM_USER(target, addr, offs)      \
@@ -332,8 +383,22 @@
 		get_user(target, (unsigned short*)&((addr)[offs]))
 #  define IOCTL_TO_USER(target, offs, source, count)  \
               copy_to_user  ( ((caddr_t)(target)),(source)+(offs),(count) )
+#else /* AWE_NEW_KERNEL_INTERFACE */
+#  define COPY_FROM_USER(target, source, offs, count) \
+              memcpy_fromfs( ((caddr_t)(target)),(source)+(offs),(count) )
+#  define GET_BYTE_FROM_USER(target, addr, offs)      \
+              *((char  *)&(target)) = get_fs_byte( (addr)+(offs) )
+#  define GET_SHORT_FROM_USER(target, addr, offs)     \
+              *((short *)&(target)) = get_fs_word( (addr)+(offs) )
+#  define IOCTL_TO_USER(target, offs, source, count)  \
+              memcpy_tofs  ( ((caddr_t)(target)),(source)+(offs),(count) )
+#endif /* AWE_NEW_KERNEL_INTERFACE */
+
 #  define BZERO(target,len)                           \
               memset( (caddr_t)target, '\0', len )
+#  define MEMCPY(dst,src,len) \
+              memcpy((caddr_t)dst, (caddr_t)src, len)
+
 #elif defined(__FreeBSD__)
 #  define NO_DATA_ERR                 EINVAL
 #  define OUTW(data, addr)            outw(addr, data)
@@ -347,6 +412,8 @@
               memcpy( &((target)[offs]), (source), (count) )
 #  define BZERO(target,len)                           \
               bzero( (caddr_t)target, len )
+#  define MEMCPY(dst,src,len) \
+              bcopy((caddr_t)src, (caddr_t)dst, len)
 #endif
 
 
@@ -356,6 +423,7 @@
 
 static struct synth_operations awe_operations =
 {
+	"EMU8K",
 	&awe_info,
 	0,
 	SYNTH_TYPE_SAMPLE,
@@ -365,7 +433,7 @@
 	awe_ioctl,
 	awe_kill_note,
 	awe_start_note,
-	awe_set_instr,
+	awe_set_instr_2,
 	awe_reset,
 	awe_hw_control,
 	awe_load_patch,
@@ -373,6 +441,9 @@
 	awe_controller,
 	awe_panning,
 	awe_volume_method,
+#ifndef AWE_NO_PATCHMGR
+	awe_patchmgr,
+#endif
 	awe_bender,
 	awe_alloc,
 	awe_setup_voice
@@ -404,7 +475,7 @@
 
 	/* allocate sample tables */
 	PERMANENT_MALLOC(awe_sample_info *, samples,
-			 AWE_MAX_SAMPLES * sizeof(awe_sample_info), mem_start);
+			 AWE_MAX_SAMPLES * AWE_SAMPLE_INFO_SIZE, mem_start);
 	PERMANENT_MALLOC(awe_voice_list *, infos,
 			 AWE_MAX_INFOS * sizeof(awe_voice_list), mem_start);
 	if (samples == NULL || infos == NULL) {
@@ -429,14 +500,21 @@
 	/* intialize AWE32 hardware */
 	awe_initialize();
 
-	printk("<AWE32 SynthCard (%dk)>\n", (int)awe_mem_size/1024);
-	sprintf(awe_info.name, "AWE32 Synth (%dk)", (int)awe_mem_size/1024);
+#ifndef __FreeBSD__
+	printk("AWE32 Sound Driver v%s (DRAM %dk)\n",
+	       AWEDRV_VERSION, (int)awe_mem_size/1024);
+#else
+	DEBUG(0,printk("AWE32 Sound Driver v%s (DRAM %dk)\n",
+                   AWEDRV_VERSION, (int)awe_mem_size/1024));
+#endif
+	sprintf(awe_info.name, "AWE32 Driver v%s (DRAM %dk)",
+		AWEDRV_VERSION, (int)awe_mem_size/1024);
 
 	/* set reverb & chorus modes */
 	awe_set_reverb_mode(reverb_mode);
 	awe_set_chorus_mode(chorus_mode);
 
-	awe_present = 1;
+	awe_present = TRUE;
 
 #ifdef AWE_OBSOLETE_VOXWARE
 	return mem_start;
@@ -491,11 +569,13 @@
  *================================================================*/
 
 /* select a given AWE32 pointer */
-#define awe_set_cmd(cmd)	OUTW(cmd, awe_base + 0x802)
+static int awe_cur_cmd = -1;
+#define awe_set_cmd(cmd) \
+if (awe_cur_cmd != cmd) { OUTW(cmd, awe_base + 0x802); awe_cur_cmd = cmd; }
 #define awe_port(port)		(awe_base - 0x620 + port)
 
 /* write 16bit data */
-static void
+INLINE static void
 awe_poke(unsigned short cmd, unsigned short port, unsigned short data)
 {
 	awe_set_cmd(cmd);
@@ -503,7 +583,7 @@
 }
 
 /* write 32bit data */
-static void
+INLINE static void
 awe_poke_dw(unsigned short cmd, unsigned short port, unsigned long data)
 {
 	awe_set_cmd(cmd);
@@ -512,7 +592,7 @@
 }
 
 /* read 16bit data */
-static unsigned short
+INLINE static unsigned short
 awe_peek(unsigned short cmd, unsigned short port)
 {
 	unsigned short k;
@@ -522,7 +602,7 @@
 }
 
 /* read 32bit data */
-static unsigned long
+INLINE static unsigned long
 awe_peek_dw(unsigned short cmd, unsigned short port)
 {
 	unsigned long k1, k2;
@@ -567,25 +647,25 @@
 static int
 awe_check_port(void)
 {
-	return (check_region(awe_port(Data0), 3) ||
-		check_region(awe_port(Data1), 3) ||
-		check_region(awe_port(Data3), 3));
+	return (check_region(awe_port(Data0), 4) ||
+		check_region(awe_port(Data1), 4) ||
+		check_region(awe_port(Data3), 4));
 }
 
 static void
 awe_request_region(void)
 {
-	request_region(awe_port(Data0), 3, "sound driver (AWE32)");
-	request_region(awe_port(Data1), 3, "sound driver (AWE32)");
-	request_region(awe_port(Data3), 3, "sound driver (AWE32)");
+	request_region(awe_port(Data0), 4, "sound driver (AWE32)");
+	request_region(awe_port(Data1), 4, "sound driver (AWE32)");
+	request_region(awe_port(Data3), 4, "sound driver (AWE32)");
 }
 
 static void
 awe_release_region(void)
 {
-	release_region(awe_port(Data0), 3);
-	release_region(awe_port(Data1), 3);
-	release_region(awe_port(Data3), 3);
+	release_region(awe_port(Data0), 4);
+	release_region(awe_port(Data1), 4);
+	release_region(awe_port(Data3), 4);
 }
 
 #endif /* !AWE_OBSOLETE_VOXWARE */
@@ -597,30 +677,22 @@
 static void
 awe_initialize(void)
 {
-	unsigned short data;
-	DECL_INTR_FLAGS(flags);
-
 	DEBUG(0,printk("AWE32: initializing..\n"));
-	DISABLE_INTR(flags);
-
-	/* check for an error condition */
-	data = awe_peek(AWE_U1);
-	if (!(data & 0x000F) == 0x000C) {
-		FATALERR(printk("AWE32: can't initialize AWE32\n"));
-	}
 
 	/* initialize hardware configuration */
 	awe_poke(AWE_HWCF1, 0x0059);
 	awe_poke(AWE_HWCF2, 0x0020);
 
-	/* disable audio output */
-	awe_poke(AWE_HWCF3, 0x0000);
+	/* disable audio; this seems to reduce a clicking noise a bit.. */
+	awe_poke(AWE_HWCF3, 0);
 
 	/* initialize audio channels */
 	awe_init_audio();
 
-	/* initialize init array */
+	/* initialize DMA */
 	awe_init_dma();
+
+	/* initialize init array */
 	awe_init_array();
 
 	/* check DRAM memory size */
@@ -634,13 +706,6 @@
 
 	/* enable audio */
 	awe_poke(AWE_HWCF3, 0x0004);
-
-	data = awe_peek(AWE_HWCF2);
-	if (~data & 0x40) {
-		FATALERR(printk("AWE32: Unable to initialize AWE32.\n"));
-	}
-
-	RESTORE_INTR(flags);
 }
 
 
@@ -720,6 +785,8 @@
 }	
 
 
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
 /* convert frequency mHz to abstract cents (= midi key * 100) */
 static int
 freq_to_note(int mHz)
@@ -811,27 +878,27 @@
  * convert envelope time parameter to AWE32 raw parameter
  *----------------------------------------------------------------*/
 
-/* attack & decay/release time table (mHz) */
+/* attack & decay/release time table (msec) */
 static short attack_time_tbl[128] = {
-32767, 5939, 3959, 2969, 2375, 1979, 1696, 1484, 1319, 1187, 1079, 989, 913, 848, 791, 742,
+32767, 11878, 5939, 3959, 2969, 2375, 1979, 1696, 1484, 1319, 1187, 1079, 989, 913, 848, 791, 742,
  698, 659, 625, 593, 565, 539, 516, 494, 475, 456, 439, 424, 409, 395, 383, 371,
  359, 344, 330, 316, 302, 290, 277, 266, 255, 244, 233, 224, 214, 205, 196, 188,
  180, 173, 165, 158, 152, 145, 139, 133, 127, 122, 117, 112, 107, 103, 98, 94,
  90, 86, 83, 79, 76, 73, 69, 67, 64, 61, 58, 56, 54, 51, 49, 47,
  45, 43, 41, 39, 38, 36, 35, 33, 32, 30, 29, 28, 27, 25, 24, 23,
  22, 21, 20, 20, 19, 18, 17, 16, 16, 15, 14, 14, 13, 13, 12, 11,
- 11, 10, 10, 10, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 0,
+ 11, 10, 10, 10, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 0,
 };
 
 static short decay_time_tbl[128] = {
-32767, 3651, 3508, 3371, 3239, 3113, 2991, 2874, 2761, 2653, 2550, 2450, 2354, 2262, 2174, 2089,
- 2007, 1928, 1853, 1781, 1711, 1644, 1580, 1518, 1459, 1401, 1347, 1294, 1243, 1195, 1148, 1103,
- 1060, 1018, 979, 940, 904, 868, 834, 802, 770, 740, 711, 683, 657, 631, 606, 582,
- 560, 538, 517, 496, 477, 458, 440, 423, 407, 391, 375, 361, 347, 333, 320, 307,
- 295, 284, 273, 262, 252, 242, 232, 223, 215, 206, 198, 190, 183, 176, 169, 162,
- 156, 150, 144, 138, 133, 128, 123, 118, 113, 109, 104, 100, 96, 93, 89, 85,
- 82, 79, 76, 73, 70, 67, 64, 62, 60, 57, 55, 53, 51, 49, 47, 45,
- 43, 41, 40, 38, 37, 35, 34, 32, 31, 30, 29, 28, 27, 25, 24, 0,
+32767, 32766, 4589, 4400, 4219, 4045, 3879, 3719, 3566, 3419, 3279, 3144, 3014, 2890, 2771, 2657,
+ 2548, 2443, 2343, 2246, 2154, 2065, 1980, 1899, 1820, 1746, 1674, 1605, 1539, 1475, 1415, 1356,
+ 1301, 1247, 1196, 1146, 1099, 1054, 1011, 969, 929, 891, 854, 819, 785, 753, 722, 692,
+ 664, 636, 610, 585, 561, 538, 516, 494, 474, 455, 436, 418, 401, 384, 368, 353,
+ 339, 325, 311, 298, 286, 274, 263, 252, 242, 232, 222, 213, 204, 196, 188, 180,
+ 173, 166, 159, 152, 146, 140, 134, 129, 123, 118, 113, 109, 104, 100, 96, 92,
+ 88, 84, 81, 77, 74, 71, 68, 65, 63, 60, 58, 55, 53, 51, 49, 47,
+ 45, 43, 41, 39, 38, 36, 35, 33, 32, 30, 29, 28, 27, 26, 25, 24,
 };
 
 /*
@@ -842,6 +909,7 @@
 }
 */
 
+/* delay time = 0x8000 - msec/92 */
 static int
 calc_parm_hold(int msec)
 {
@@ -851,22 +919,25 @@
 	return val;
 }
 
+/* attack time: search from time table */
 static int
 calc_parm_attack(int msec)
 {
 	return calc_parm_search(msec, attack_time_tbl);
 }
 
+/* decay/release time: search from time table */
 static int
 calc_parm_decay(int msec)
 {
 	return calc_parm_search(msec, decay_time_tbl);
 }
 
+/* search an index for specified time from given time table */
 static int
 calc_parm_search(int msec, short *table)
 {
-	int left = 0, right = 127, mid;
+	int left = 1, right = 127, mid;
 	while (left < right) {
 		mid = (left + right) / 2;
 		if (msec < (int)table[mid])
@@ -876,6 +947,7 @@
 	}
 	return left;
 }
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
 
 
 /*================================================================
@@ -883,103 +955,53 @@
  *================================================================*/
 
 /* set an effect value */
-#define FX_SET(v,type,value) \
-(voices[v].fx_flags[(type)/8] |= (1<<((type)%8)),\
- voices[v].fx[type] = (value))
-/* check the effect value is set */
-#define FX_ON(v,type)	(voices[v].fx_flags[(type)/8] & (1<<((type)%8)))
-
-#if 0
-#define FX_BYTE(v,type,value)\
-	(FX_ON(v,type) ? (unsigned char)voices[v].fx[type] :\
-	 (unsigned char)(value))
-#define FX_WORD(v,type,value)\
-	(FX_ON(v,type) ? (unsigned short)voices[v].fx[type] :\
-	 (unsigned short)(value))
+#define FX_SET(rec,type,value) \
+	((rec)->flags[(type)/8] |= (1 << ((type) % 8)), \
+	 (rec)->val[type] = (value))
 
-#else
+/* check the effect value is set */
+#define FX_ON(rec,type)	((rec)->flags[(type)/8] & (1<<((type)%8)))
 
 /* get byte effect value */
-static unsigned char FX_BYTE(int v, int type, unsigned char value)
-{
-	unsigned char tmp;
-	if (FX_ON(v,type))
-		tmp = (unsigned char)voices[v].fx[type];
-	else
-		tmp = value;
-	DEBUG(4,printk("AWE32: [-- byte(%d) = %x]\n", type, tmp));
-	return tmp;
-}
-
+#define FX_BYTE(rec,type,value) \
+	(unsigned char)(FX_ON(rec,type) ? (rec)->val[type] : (value))
 /* get word effect value */
-static unsigned short FX_WORD(int v, int type, unsigned short value)
-{
-	unsigned short tmp;
-	if (FX_ON(v,type))
-		tmp = (unsigned short)voices[v].fx[type];
-	else
-		tmp = value;
-	DEBUG(4,printk("AWE32: [-- word(%d) = %x]\n", type, tmp));
-	return tmp;
-}
-
-#endif
+#define FX_WORD(rec,type,value) \
+	(unsigned short)(FX_ON(rec,type) ? (rec)->val[type] : (value))
 
 /* get word (upper=type1/lower=type2) effect value */
-static unsigned short FX_COMB(int v, int type1, int type2, unsigned short value)
+static unsigned short
+FX_COMB(FX_Rec *rec, int type1, int type2, unsigned short value)
 {
 	unsigned short tmp;
-	if (FX_ON(v, type1))
-		tmp = (unsigned short)(voices[v].fx[type1]) << 8;
+	if (FX_ON(rec, type1))
+		tmp = (unsigned short)(rec->val[type1]) << 8;
 	else
 		tmp = value & 0xff00;
-	if (FX_ON(v, type2))
-		tmp |= (unsigned short)(voices[v].fx[type2]) & 0xff;
+	if (FX_ON(rec, type2))
+		tmp |= (unsigned short)(rec->val[type2]) & 0xff;
 	else
 		tmp |= value & 0xff;
-	DEBUG(4,printk("AWE32: [-- comb(%d/%d) = %x]\n", type1, type2, tmp));
 	return tmp;
 }
 
 /* address offset */
 static long
-FX_OFFSET(int voice, int lo, int hi)
+FX_OFFSET(FX_Rec *rec, int lo, int hi, int mode)
 {
-	awe_voice_info *vp;
-	long addr;
-	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
-		return 0;
-
-	addr = 0;
-	if (FX_ON(voice, hi)) {
-		addr = (short)voices[voice].fx[hi];
+	long addr = 0;
+	if (FX_ON(rec, hi)) {
+		addr = (short)rec->val[hi];
 		addr = addr << 15;
 	}
-	if (FX_ON(voice, lo))
-		addr += (short)voices[voice].fx[lo];
-	if (!(vp->mode & (AWE_SAMPLE_8BITS<<6)))
+	if (FX_ON(rec, lo))
+		addr += (short)rec->val[lo];
+	if (!(mode & AWE_SAMPLE_8BITS))
 		addr /= 2;
 	return addr;
 }
 
 
-typedef void (*fx_affect_func)(int voice);
-static fx_affect_func fx_realtime[] = {
-	/* env1: delay, attack, hold, decay, release, sustain, pitch, cutoff*/
-	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-	/* env2: delay, attack, hold, decay, release, sustain */
-	NULL, NULL, NULL, NULL, NULL, NULL,
-	/* lfo1: delay, freq, volume, pitch, cutoff */
-	NULL, awe_fx_tremfrq, awe_fx_tremfrq, awe_fx_fmmod, awe_fx_fmmod,
-	/* lfo2: delay, freq, pitch */
-	NULL, awe_fx_fm2frq2, awe_fx_fm2frq2,
-	/* global: initpitch, chorus, reverb, cutoff, filterQ */
-	awe_fx_initpitch, NULL, NULL, awe_fx_cutoff, NULL,
-	/* sample: start, loopstart, loopend */
-	NULL, NULL, NULL,
-};
-
-
 /*================================================================
  * turn on/off sample
  *================================================================*/
@@ -989,8 +1011,8 @@
 {
 	unsigned long temp;
 	long addr;
-	unsigned short tmp2;
 	awe_voice_info *vp;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
 
 	/* A voice sample must assigned before calling */
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
@@ -1005,71 +1027,65 @@
 
 	/* modulation & volume envelope */
 	awe_poke(AWE_ENVVAL(voice),
-		 FX_WORD(voice, AWE_FX_ENV1_DELAY, vp->parm.moddelay));
+		 FX_WORD(fx, AWE_FX_ENV1_DELAY, vp->parm.moddelay));
 	awe_poke(AWE_ATKHLD(voice),
-		 FX_COMB(voice, AWE_FX_ENV1_ATTACK, AWE_FX_ENV1_HOLD,
+		 FX_COMB(fx, AWE_FX_ENV1_HOLD, AWE_FX_ENV1_ATTACK,
 			 vp->parm.modatkhld));
 	awe_poke(AWE_DCYSUS(voice),
-		 FX_COMB(voice, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
+		 FX_COMB(fx, AWE_FX_ENV1_SUSTAIN, AWE_FX_ENV1_DECAY,
 			  vp->parm.moddcysus));
 	awe_poke(AWE_ENVVOL(voice),
-		 FX_WORD(voice, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
+		 FX_WORD(fx, AWE_FX_ENV2_DELAY, vp->parm.voldelay));
 	awe_poke(AWE_ATKHLDV(voice),
-		 FX_COMB(voice, AWE_FX_ENV2_ATTACK, AWE_FX_ENV2_HOLD,
+		 FX_COMB(fx, AWE_FX_ENV2_HOLD, AWE_FX_ENV2_ATTACK,
 			 vp->parm.volatkhld));
 	/* decay/sustain parameter for volume envelope must be set at last */
 
 	/* pitch offset */
-	awe_poke(AWE_IP(voice), voices[voice].apitch);
-	DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch));
+	awe_set_pitch(voice, TRUE);
 
 	/* cutoff and volume */
-	tmp2 = FX_BYTE(voice, AWE_FX_CUTOFF, vp->parm.cutoff);
-	tmp2 = (tmp2 << 8) | voices[voice].avol;
-	awe_poke(AWE_IFATN(voice), tmp2);
+	awe_set_volume(voice, TRUE);
 
 	/* modulation envelope heights */
 	awe_poke(AWE_PEFE(voice),
-		 FX_COMB(voice, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
+		 FX_COMB(fx, AWE_FX_ENV1_PITCH, AWE_FX_ENV1_CUTOFF,
 			 vp->parm.pefe));
 
 	/* lfo1/2 delay */
 	awe_poke(AWE_LFO1VAL(voice),
-		 FX_WORD(voice, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
+		 FX_WORD(fx, AWE_FX_LFO1_DELAY, vp->parm.lfo1delay));
 	awe_poke(AWE_LFO2VAL(voice),
-		 FX_WORD(voice, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
+		 FX_WORD(fx, AWE_FX_LFO2_DELAY, vp->parm.lfo2delay));
 
 	/* lfo1 pitch & cutoff shift */
-	awe_poke(AWE_FMMOD(voice),
-		 FX_COMB(voice, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
-			 vp->parm.fmmod));
+	awe_fx_fmmod(voice, TRUE);
 	/* lfo1 volume & freq */
-	awe_poke(AWE_TREMFRQ(voice),
-		 FX_COMB(voice, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
-			 vp->parm.tremfrq));
+	awe_fx_tremfrq(voice, TRUE);
 	/* lfo2 pitch & freq */
-	awe_poke(AWE_FM2FRQ2(voice),
-		 FX_COMB(voice, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
-			 vp->parm.fm2frq2));
-
+	awe_fx_fm2frq2(voice, TRUE);
 	/* pan & loop start */
 	 awe_set_pan(voice, 1);
 
 	/* chorus & loop end (chorus 8bit, MSB) */
 	addr = vp->loopend - 1;
-	addr += FX_OFFSET(voice, AWE_FX_LOOP_END,
-			  AWE_FX_COARSE_LOOP_END);
-	temp = FX_BYTE(voice, AWE_FX_CHORUS, vp->parm.chorus);
+	addr += FX_OFFSET(fx, AWE_FX_LOOP_END,
+			  AWE_FX_COARSE_LOOP_END, vp->mode);
+	temp = FX_BYTE(fx, AWE_FX_CHORUS, vp->parm.chorus);
 	temp = (temp <<24) | (unsigned long)addr;
 	awe_poke_dw(AWE_CSL(voice), temp);
+	DEBUG(4,printk("AWE32: [-- loopend=%x/%x]\n",
+		       (int)vp->loopend, (int)addr));
 
 	/* Q & current address (Q 4bit value, MSB) */
 	addr = vp->start - 1;
-	addr += FX_OFFSET(voice, AWE_FX_SAMPLE_START,
-			  AWE_FX_COARSE_SAMPLE_START);
-	temp = FX_BYTE(voice, AWE_FX_FILTERQ, vp->parm.filterQ);
+	addr += FX_OFFSET(fx, AWE_FX_SAMPLE_START,
+			  AWE_FX_COARSE_SAMPLE_START, vp->mode);
+	temp = FX_BYTE(fx, AWE_FX_FILTERQ, vp->parm.filterQ);
 	temp = (temp<<28) | (unsigned long)addr;
 	awe_poke_dw(AWE_CCCA(voice), temp);
+	DEBUG(4,printk("AWE32: [-- startaddr=%x/%x]\n",
+		       (int)vp->start, (int)addr));
 
 	/* reset volume */
 	awe_poke_dw(AWE_VTFT(voice), 0x0000FFFF);
@@ -1077,36 +1093,41 @@
 
 	/* turn on envelope */
 	awe_poke(AWE_DCYSUSV(voice),
-		 FX_COMB(voice, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
+		 FX_COMB(fx, AWE_FX_ENV2_SUSTAIN, AWE_FX_ENV2_DECAY,
 			  vp->parm.voldcysus));
 	/* set chorus */
-	temp = FX_BYTE(voice, AWE_FX_REVERB, vp->parm.reverb);
+	temp = FX_BYTE(fx, AWE_FX_REVERB, vp->parm.reverb);
 	temp = (awe_peek_dw(AWE_PTRX(voice)) & 0xffff0000) | (temp<<8);
 	awe_poke_dw(AWE_PTRX(voice), temp);
 	awe_poke_dw(AWE_CPF(voice), 0x40000000);
-
-	DEBUG(3,printk("AWE32: [-- start=%x loop=%x]\n",
-		       (int)vp->start, (int)vp->loopstart));
 }
 
+
 /* turn off the voice */
 static void
 awe_note_off(int voice)
 {
 	awe_voice_info *vp;
 	unsigned short tmp;
-	if ((vp = voices[voice].sample) == NULL || !voices[voice].state)
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	if ((vp = voices[voice].sample) == NULL) {
+		awe_voice_init(voice);
 		return;
-	if (FX_ON(voice, AWE_FX_ENV1_RELEASE))
-		tmp = 0x8000 | voices[voice].fx[AWE_FX_ENV1_RELEASE];
+	}
+
+	if (FX_ON(fx, AWE_FX_ENV1_RELEASE))
+		tmp = 0x8000 | fx->val[AWE_FX_ENV1_RELEASE];
 	else
 		tmp = vp->parm.modrelease;
 	awe_poke(AWE_DCYSUS(voice), tmp);
-	if (FX_ON(voice, AWE_FX_ENV2_RELEASE))
-		tmp = 0x8000 | voices[voice].fx[AWE_FX_ENV2_RELEASE];
+	if (FX_ON(fx, AWE_FX_ENV2_RELEASE))
+		tmp = 0x8000 | fx->val[AWE_FX_ENV2_RELEASE];
 	else
 		tmp = vp->parm.volrelease;
 	awe_poke(AWE_DCYSUSV(voice), tmp);
+	voices[voice].state = AWE_ST_OFF;
+
+	awe_voice_init(voice);
 }
 
 /* force to terminate the voice (no releasing echo) */
@@ -1114,29 +1135,27 @@
 awe_terminate(int voice)
 {
 	awe_poke(AWE_DCYSUSV(voice), 0x807F);
+	awe_tweak_voice(voice);
+	awe_voice_init(voice);
 }
 
 
 /* turn off other voices with the same exclusive class (for drums) */
 static void
-awe_exclusive_off(int voice)
+awe_exclusive_off(int voice, int exclass)
 {
-	int i, excls;
+	int i;
 
-	if (voices[voice].sample == NULL) /* no sample */
-		return;
-	excls = voices[voice].sample->exclusiveClass;
-	if (excls == 0)	/* not exclusive */
+	if (exclass == 0) /* not exclusive */
 		return;
 
 	/* turn off voices with the same class */
 	for (i = 0; i < awe_max_voices; i++) {
-		if (i != voice && voices[voice].state &&
+		if (i != voice && IS_PLAYING(voice) &&
 		    voices[i].sample &&
-		    voices[i].sample->exclusiveClass == excls) {
+		    voices[i].sample->exclusiveClass == exclass) {
 			DEBUG(4,printk("AWE32: [exoff(%d)]\n", i));
 			awe_note_off(i);
-			awe_voice_init(i, 1);
 		}
 	}
 }
@@ -1148,26 +1167,47 @@
 
 /* change pitch */
 static void
-awe_set_pitch(int voice)
+awe_set_pitch(int voice, int forced)
 {
-	if (!voices[voice].state) return;
+	if (IS_NO_EFFECT(voice) && !forced) return;
 	awe_poke(AWE_IP(voice), voices[voice].apitch);
+	DEBUG(3,printk("AWE32: [-- pitch=%x]\n", voices[voice].apitch));
+}
+
+/* calculate & change pitch */
+static void
+awe_set_voice_pitch(int voice, int forced)
+{
+	awe_calc_pitch(voice);
+	awe_set_pitch(voice, forced);
 }
 
 /* change volume */
 static void
-awe_set_volume(int voice)
+awe_set_volume(int voice, int forced)
 {
 	awe_voice_info *vp;
 	unsigned short tmp2;
-	if (!voices[voice].state) return;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+
+	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
-	tmp2 = FX_BYTE(voice, AWE_FX_CUTOFF, vp->parm.cutoff);
+
+	tmp2 = FX_BYTE(fx, AWE_FX_CUTOFF, vp->parm.cutoff);
 	tmp2 = (tmp2 << 8) | voices[voice].avol;
 	awe_poke(AWE_IFATN(voice), tmp2);
 }
 
+/* calculate & change volume */
+static void
+awe_set_voice_vol(int voice, int forced)
+{
+	awe_calc_volume(voice);
+	awe_set_volume(voice, forced);
+}
+
+
 /* change pan; this could make a click noise.. */
 static void
 awe_set_pan(int voice, int forced)
@@ -1175,8 +1215,9 @@
 	unsigned long temp;
 	long addr;
 	awe_voice_info *vp;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
 
-	if (!voices[voice].state && !forced) return;
+	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
 
@@ -1187,7 +1228,7 @@
 		int pos = 0;
 		if (vp->pan >= 0) /* 0-127 */
 			pos = (int)vp->pan * 2 - 128;
-		pos += voices[voice].panning; /* -128 - 127 */
+		pos += voices[voice].cinfo->panning; /* -128 - 127 */
 		pos = 127 - pos;
 		if (pos < 0)
 			temp = 0;
@@ -1196,76 +1237,75 @@
 		else
 			temp = pos;
 	}
-	addr = vp->loopstart - 1;
-	addr += FX_OFFSET(voice, AWE_FX_LOOP_START,
-			  AWE_FX_COARSE_LOOP_START);
-	temp = (temp<<24) | (unsigned long)addr;
-	awe_poke_dw(AWE_PSST(voice), temp);
+	if (forced || temp != voices[voice].apan) {
+		addr = vp->loopstart - 1;
+		addr += FX_OFFSET(fx, AWE_FX_LOOP_START,
+				  AWE_FX_COARSE_LOOP_START, vp->mode);
+		temp = (temp<<24) | (unsigned long)addr;
+		awe_poke_dw(AWE_PSST(voice), temp);
+		voices[voice].apan = temp;
+		DEBUG(4,printk("AWE32: [-- loopstart=%x/%x]\n",
+			       (int)vp->loopstart, (int)addr));
+	}
 }
 
 /* effects change during playing */
 static void
-awe_fx_fmmod(int voice)
+awe_fx_fmmod(int voice, int forced)
 {
 	awe_voice_info *vp;
-	if (!voices[voice].state) return;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
 	awe_poke(AWE_FMMOD(voice),
-		 FX_COMB(voice, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
+		 FX_COMB(fx, AWE_FX_LFO1_PITCH, AWE_FX_LFO1_CUTOFF,
 			 vp->parm.fmmod));
 }
 
+/* set tremolo (lfo1) volume & frequency */
 static void
-awe_fx_tremfrq(int voice)
+awe_fx_tremfrq(int voice, int forced)
 {
 	awe_voice_info *vp;
-	if (!voices[voice].state) return;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
 	awe_poke(AWE_TREMFRQ(voice),
-		 FX_COMB(voice, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
+		 FX_COMB(fx, AWE_FX_LFO1_VOLUME, AWE_FX_LFO1_FREQ,
 			 vp->parm.tremfrq));
 }
 
+/* set lfo2 pitch & frequency */
 static void
-awe_fx_fm2frq2(int voice)
+awe_fx_fm2frq2(int voice, int forced)
 {
 	awe_voice_info *vp;
-	if (!voices[voice].state) return;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	if (IS_NO_EFFECT(voice) && !forced) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
 	awe_poke(AWE_FM2FRQ2(voice),
-		 FX_COMB(voice, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
+		 FX_COMB(fx, AWE_FX_LFO2_PITCH, AWE_FX_LFO2_FREQ,
 			 vp->parm.fm2frq2));
 }
 
+/* set total cutoff  & current pitch */
 static void
-awe_fx_cutoff(int voice)
+awe_fx_cutoff(int voice, int forced)
 {
 	unsigned short tmp2;
 	awe_voice_info *vp;
-	if (!voices[voice].state) return;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
+	if (IS_NO_EFFECT(voice)) return;
 	if ((vp = voices[voice].sample) == NULL || vp->index < 0)
 		return;
-	tmp2 = FX_BYTE(voice, AWE_FX_CUTOFF, vp->parm.cutoff);
+	tmp2 = FX_BYTE(fx, AWE_FX_CUTOFF, vp->parm.cutoff);
 	tmp2 = (tmp2 << 8) | voices[voice].avol;
 	awe_poke(AWE_IFATN(voice), tmp2);
 }
 
-static void
-awe_fx_initpitch(int voice)
-{
-	if (!voices[voice].state) return;
-	if (FX_ON(voice, AWE_FX_INIT_PITCH)) {
-		DEBUG(3,printk("AWE32: initpitch ok\n"));
-	} else {
-		DEBUG(3,printk("AWE32: BAD initpitch %d\n", AWE_FX_INIT_PITCH));
-	}
-	awe_calc_pitch(voice);
-	awe_poke(AWE_IP(voice), voices[voice].apitch);
-}
-
 
 /*================================================================
  * calculate pitch offset
@@ -1279,6 +1319,7 @@
 {
 	voice_info *vp = &voices[voice];
 	awe_voice_info *ap;
+	awe_chan_info *cp = voices[voice].cinfo;
 	int offset;
 
 	/* search voice information */
@@ -1300,18 +1341,18 @@
 	}
 	offset += ap->tune * 4096 / 1200;
 	DEBUG(4,printk("AWE32: p-> tune+ ofs=%d\n", offset));
-	if (vp->bender != 0) {
-		DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, vp->bender));
+	if (cp->bender != 0) {
+		DEBUG(3,printk("AWE32: p-> bend(%d) %d\n", voice, cp->bender));
 		/* (819200: 1 semitone) ==> (4096: 12 semitones) */
-		offset += vp->bender * vp->bender_range / 2400;
+		offset += cp->bender * cp->bender_range / 2400;
 	}
 	offset = (offset * ap->scaleTuning) / 100;
 	DEBUG(4,printk("AWE32: p-> scale* ofs=%d\n", offset));
 
 	/* add initial pitch correction */
-	if (FX_ON(voice, AWE_FX_INIT_PITCH)) {
-		DEBUG(3,printk("AWE32: fx_pitch(%d) %d\n", voice, vp->fx[AWE_FX_INIT_PITCH]));
-		offset += vp->fx[AWE_FX_INIT_PITCH];
+	if (FX_ON(&cp->fx, AWE_FX_INIT_PITCH)) {
+		DEBUG(3,printk("AWE32: fx_pitch(%d) %d\n", voice, cp->fx.val[AWE_FX_INIT_PITCH]));
+		offset += cp->fx.val[AWE_FX_INIT_PITCH];
 	}
 
 	/* 0xe000: root pitch */
@@ -1324,11 +1365,14 @@
 }
 
 
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+/* calculate MIDI key and semitone from the specified frequency */
 static void
 awe_calc_pitch_from_freq(int voice, int freq)
 {
 	voice_info *vp = &voices[voice];
 	awe_voice_info *ap;
+	FX_Rec *fx = &voices[voice].cinfo->fx;
 	int offset;
 	int note;
 
@@ -1342,14 +1386,16 @@
 	note = freq_to_note(freq);
 	offset = (note - ap->root * 100 + ap->tune) * 4096 / 1200;
 	offset = (offset * ap->scaleTuning) / 100;
-	if (FX_ON(voice, AWE_FX_INIT_PITCH))
-		offset += vp->fx[AWE_FX_INIT_PITCH];
+	if (FX_ON(fx, AWE_FX_INIT_PITCH))
+		offset += fx->val[AWE_FX_INIT_PITCH];
 	vp->apitch = 0xe000 + ap->rate_offset + offset;
 	if (vp->apitch > 0xffff)
 		vp->apitch = 0xffff;
 	if (vp->apitch < 0)
 		vp->apitch = 0;
 }
+#endif /* AWE_HAS_GUS_COMPATIBILITY */
+
 
 /*================================================================
  * calculate volume attenuation
@@ -1375,6 +1421,7 @@
 {
 	voice_info *vp = &voices[voice];
 	awe_voice_info *ap;
+	awe_chan_info *cp = voices[voice].cinfo;
 	int vol;
 
 	/* search voice information */
@@ -1387,13 +1434,8 @@
 			return;
 	}
 	
-	if (vp->velocity < ap->vellow)
-		vp->velocity = ap->vellow;
-	else if (vp->velocity > ap->velhigh)
-		vp->velocity = ap->velhigh;
-
 	/* 0 - 127 */
-	vol = (vp->velocity * vp->main_vol * vp->expression_vol) / (127*127);
+	vol = (vp->velocity * cp->main_vol * cp->expression_vol) / (127*127);
 	vol = vol * ap->amplitude / 127;
 	if (vol < 0) vol = 0;
 	if (vol > 127) vol = 127;
@@ -1412,35 +1454,52 @@
  * synth operation routines
  *================================================================*/
 
+#define AWE_VOICE_KEY(v)	(0x8000 | (v))
+#define AWE_CHAN_KEY(c,n)	(((c) << 8) | ((n) + 1))
+
 /* initialize the voice */
 static void
-awe_voice_init(int voice, int inst_only)
+awe_voice_init(int voice)
 {
-	if (! inst_only) {
-		/* clear voice parameters */
-		voices[voice].note = -1;
-		voices[voice].velocity = 0;
-		voices[voice].panning = 0; /* zero center */
-		voices[voice].bender = 0; /* zero tune skew */
-		voices[voice].bender_range = 200; /* sense * 100 */
-		voices[voice].main_vol = 127;
-		voices[voice].expression_vol = 127;
-		voices[voice].bank = AWE_DEFAULT_BANK;
-		voices[voice].instr = -1;
-		voices[voice].vrec = NULL;
-		voices[voice].sample = NULL;
-	}
+	voices[voice].note = -1;
+	voices[voice].velocity = 0;
+	voices[voice].sample = NULL;
+	voices[voice].state = AWE_ST_OFF;
+	voices[voice].cinfo = &channels[voice];
+	voices[voice].ch = -1;
+	voices[voice].key = AWE_VOICE_KEY(voice);
+	voices[voice].time = current_alloc_time;
 
 	/* clear voice mapping */
-	voices[voice].state = 0;
 	voice_alloc->map[voice] = 0;
 
 	/* emu8000 parameters */
 	voices[voice].apitch = 0;
 	voices[voice].avol = 255;
+	voices[voice].apan = -1;
 
 	/* clear effects */
-	BZERO(voices[voice].fx_flags, sizeof(voices[voice].fx_flags));
+	if (! awe_channel_mode)
+		BZERO(&voices[voice].cinfo->fx, sizeof(FX_Rec));
+}
+
+/* initialize channel info */
+static void awe_channel_init(int ch)
+{
+	channels[ch].panning = 0; /* zero center */
+	channels[ch].bender = 0; /* zero tune skew */
+	channels[ch].bender_range = 200; /* sense * 100 */
+	channels[ch].main_vol = 127;
+	channels[ch].expression_vol = 127;
+	if (awe_channel_mode && IS_DRUM_CHANNEL(ch))
+		channels[ch].bank = AWE_DRUM_BANK;
+	else
+		channels[ch].bank = AWE_DEFAULT_BANK;
+	channels[ch].instr = 0;
+	channels[ch].vrec = NULL;
+	channels[ch].def_vrec = NULL;
+	channels[ch].sustained = 0;
+	BZERO(&channels[ch].fx, sizeof(FX_Rec));
 }
 
 
@@ -1457,14 +1516,19 @@
 	if (awe_busy)
 		return RET_ERROR(EBUSY);
 
-	awe_busy = 1;
+	awe_busy = TRUE;
 	awe_reset(dev);
 
 	/* clear sample position flag */
 	loaded_once = 0;
 
-	/* set GUS bank to default */
+	/* set default mode */
+	init_atten = AWE_DEFAULT_ATTENUATION; /* 12dB below */
 	awe_gus_bank = AWE_DEFAULT_BANK;
+	drum_flags = DEFAULT_DRUM_FLAGS;
+	awe_exclusive_sound = TRUE;
+	awe_channel_mode = 0;
+
 	return 0;
 }
 
@@ -1476,7 +1540,7 @@
 awe_close(int dev)
 {
 	awe_reset(dev);
-	awe_busy = 0;
+	awe_busy = FALSE;
 }
 
 
@@ -1494,7 +1558,7 @@
 
 	case SNDCTL_SEQ_RESETSAMPLES:
 		awe_reset_samples();
-		awe_reset(dev); /* better to reset emu8k chip... */
+		awe_reset(dev);
 		return 0;
 		break;
 
@@ -1520,88 +1584,139 @@
 static int
 awe_kill_note(int dev, int voice, int note, int velocity)
 {
-	awe_voice_info *vp;
-	DECL_INTR_FLAGS(flags);
+	int i, key, besttime;
 
-	DEBUG(2,printk("AWE32: [off(%d)]\n", voice));
-	if (voice < 0 || voice >= awe_max_voices)
-		      return RET_ERROR(EINVAL);
-	if ((vp = voices[voice].sample) == NULL)
-		return 0;
-       
-	if (!(vp->mode & AWE_MODE_NORELEASE)) {
-		DISABLE_INTR(flags);
-		awe_note_off(voice);
-		RESTORE_INTR(flags);
+	DEBUG(2,printk("AWE32: [off(%d) nt=%d vl=%d]\n", voice, note, velocity));
+	if (awe_channel_mode) {
+		if (awe_channel_mode == 2) { /* get channel */
+			int v2 = voice_alloc->map[voice] >> 8;
+			voice_alloc->map[voice] = 0;
+			voice = v2;
+		}
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return RET_ERROR(EINVAL);
+		if (channels[voice].instr > 128)
+			note = channels[voice].instr - 128;
+		key = AWE_CHAN_KEY(voice, note);
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
+			return RET_ERROR(EINVAL);
+		key = AWE_VOICE_KEY(voice);
 	}
-	awe_voice_init(voice, 1);
-	return 0;
-}
-
 
-/* search the note with the specified key range */
-static awe_voice_info *
-awe_search_voice(int voice, int note)
-{
-	awe_voice_list *rec;
-	int maxc;
-
-	for (rec = voices[voice].vrec, maxc = AWE_MAX_INFOS;
-	     rec && maxc; rec = rec->next_instr, maxc--) {
-		if (rec->v.low <= note && note <= rec->v.high)
-			return &rec->v;
+	besttime = current_alloc_time + 1;
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].key == key) {
+			if (voices[i].time < besttime)
+				besttime = voices[i].time;
+		}
 	}
-	return NULL;
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].key == key &&
+		    (!awe_exclusive_sound || voices[i].time == besttime)) {
+			if (voices[i].cinfo->sustained)
+				voices[i].state = AWE_ST_SUSTAINED;
+			else
+				awe_note_off(i);
+		}
+	}
+	return 0;
 }
 
+
 /* start a voice:
  *   if note is 255, identical with aftertouch function.
  *   Otherwise, start a voice with specified not and volume.
  */
 static int
-awe_start_note(int dev, int v, int note_num, int volume)
+awe_start_note(int dev, int voice, int note, int velocity)
 {
-	DECL_INTR_FLAGS(flags);
+	int i, key, state, volonly;
 
-	DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", v, note_num, volume));
-	if (v < 0 || v >= awe_max_voices)
+	DEBUG(2,printk("AWE32: [on(%d) nt=%d vl=%d]\n", voice, note, velocity));
+	volonly = 0;
+	if (awe_channel_mode) {
+		if (awe_channel_mode == 2) /* get channel */
+			voice = voice_alloc->map[voice] >> 8;
+		else if (voice & 0x80) { /* channel volume mode */
+			voice &= ~0x80;
+			volonly = 2;
+		}
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return RET_ERROR(EINVAL);
+		if (channels[voice].instr > 128)
+			note = channels[voice].instr - 128;
+		if (note >= 128) { /* key volume mode */
+			note -= 128;
+			volonly = 1;
+		}
+		key = AWE_CHAN_KEY(voice, note);
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
 		      return RET_ERROR(EINVAL);
-	/* an instrument must be set before starting a note */
-	if (voices[v].vrec == NULL) {
-		DEBUG(1,printk("AWE32: [-- vrec is null]\n"));
+		if (voices[voice].cinfo->instr > 128)
+			note = voices[voice].cinfo->instr - 128;
+		key = AWE_VOICE_KEY(voice);
+		if (note == 255)
+			volonly = 1;
+	}
+
+	/* dynamic volume change */
+	if (volonly) {
+		for (i = 0; i < awe_max_voices; i++) {
+			if ((volonly == 2 && voices[i].ch == voice) ||
+			    voices[i].key == key) {
+				voices[i].velocity = velocity;
+				if (velocity == 0) /* for GUS compatibility */
+					return awe_kill_note(dev, voice, note, velocity);
+				awe_calc_volume(i);
+				state = voices[i].state;
+				voices[i].state = AWE_ST_ON;
+				if (state == AWE_ST_STANDBY)
+					awe_note_on(i);
+				else
+					awe_set_volume(i, FALSE);
+			}
+		}
 		return 0;
 	}
 
-	if (note_num == 255) {
-		/* dynamic volume change; sample is already assigned */
-		if (! voices[v].state || voices[v].sample == NULL)
-			return 0;
-		/* calculate volume parameter */
-		voices[v].velocity = volume;
-		awe_calc_volume(v);
-		DISABLE_INTR(flags);
-		awe_set_volume(v);
-		RESTORE_INTR(flags);
-		return 0;
-	}
-	/* assign a sample with the corresponding note */
-	if ((voices[v].sample = awe_search_voice(v, note_num)) == NULL) {
-		DEBUG(1,printk("AWE32: [-- sample is null]\n"));
-		return 0;
+	/* stop the sound if still playing */
+	if (awe_exclusive_sound) {
+		for (i = 0; i < awe_max_voices; i++)
+			if (voices[i].key == key &&
+			    voices[i].state != AWE_ST_OFF)
+				awe_note_off(i);
 	}
-	/* calculate pitch & volume parameters */
-	voices[v].note = note_num;
-	voices[v].velocity = volume;
-	awe_calc_pitch(v);
-	awe_calc_volume(v);
 
-	DISABLE_INTR(flags);
+	/* allocate voices */
+	if (awe_channel_mode)
+		awe_alloc_multi_voices(voice, note, velocity);
+	else
+		awe_alloc_one_voice(voice, note, velocity);
+
 	/* turn off other voices (for drums) */
-	awe_exclusive_off(v);
-	/* turn on the voice */
-	awe_note_on(v);
-	voices[v].state = 1;	/* flag up */
-	RESTORE_INTR(flags);
+	for (i = 0; i < awe_max_voices; i++)
+		if (voices[i].key == key && voices[i].sample)
+			awe_exclusive_off(i, voices[i].sample->exclusiveClass);
+
+	if (velocity == 0)
+		state = AWE_ST_STANDBY; /* stand by for playing */
+	else
+		state = AWE_ST_ON;	/* really play */
+
+	/* set up pitch and volume parameters */
+	for (i = 0; i < awe_max_voices; i++)
+		if (voices[i].key == key) {
+			/* calculate pitch & volume parameters */
+			voices[i].state = state;
+			voices[i].note = note;
+			voices[i].velocity = velocity;
+			awe_calc_pitch(i);
+			awe_calc_volume(i);
+			if (state == AWE_ST_ON)
+				awe_note_on(i);
+		}
 
 	return 0;
 }
@@ -1625,28 +1740,58 @@
 
 /* assign the instrument to a voice */
 static int
+awe_set_instr_2(int dev, int voice, int instr_no)
+{
+	if (awe_channel_mode == 2)
+		voice = voice_alloc->map[voice] >> 8;
+	return awe_set_instr(dev, voice, instr_no);
+}
+
+/* assign the instrument to a voice */
+static int
 awe_set_instr(int dev, int voice, int instr_no)
 {
-	awe_voice_list *rec;
+	awe_chan_info *cinfo;
+	int def_bank;
 
-	if (voice < 0 || voice >= awe_max_voices)
-		return RET_ERROR(EINVAL);
+	if (awe_channel_mode) {
+		/* skip the percussion instr in SEQ2 mode */
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return RET_ERROR(EINVAL);
+		cinfo = &channels[voice];
+		if (IS_DRUM_CHANNEL(voice))
+			def_bank = AWE_DRUM_BANK;
+		else
+			def_bank = cinfo->bank;
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
+			return RET_ERROR(EINVAL);
+		cinfo = voices[voice].cinfo;
+		def_bank = cinfo->bank;
+	}
 
 	if (instr_no < 0 || instr_no >= AWE_MAX_PRESETS)
 		return RET_ERROR(EINVAL);
 
-	if ((rec = awe_search_instr(voices[voice].bank, instr_no)) == NULL) {
-		/* if bank is not defined, use the default bank 0 */
-		if (voices[voice].bank != AWE_DEFAULT_BANK &&
-		    (rec = awe_search_instr(AWE_DEFAULT_BANK, instr_no)) == NULL) {
-			DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no));
-			return 0;
-		}
+	cinfo->vrec = NULL;
+	cinfo->def_vrec = NULL;
+	if (instr_no > 128) {
+		cinfo->vrec = awe_search_instr(128, cinfo->bank);
+		if (cinfo->bank != 0)
+			cinfo->def_vrec = awe_search_instr(128, 0);
+	} else {
+		cinfo->vrec = awe_search_instr(def_bank, instr_no);
+		if (def_bank == AWE_DRUM_BANK)
+			cinfo->def_vrec = awe_search_instr(def_bank, 0);
+		else
+			cinfo->def_vrec = awe_search_instr(AWE_DEFAULT_BANK, instr_no);
+	}
+	if (cinfo->vrec == NULL && cinfo->def_vrec == NULL) {
+		DEBUG(1,printk("AWE32 Warning: can't find instrument %d\n", instr_no));
+		return 0;
 	}
 
-	voices[voice].instr = instr_no;
-	voices[voice].vrec = rec;
-	voices[voice].sample = NULL;  /* not set yet */
+	cinfo->instr = instr_no;
 
 	return 0;
 }
@@ -1657,10 +1802,15 @@
 awe_reset(int dev)
 {
 	int i;
+	current_alloc_time = 0;
 	/* don't turn off voice 31 and 32.  they are used also for FM voices */
-	for (i = 0; i < AWE_NORMAL_VOICES; i++) {
+	for (i = 0; i < AWE_NORMAL_VOICES; i++)
 		awe_terminate(i);
-		awe_voice_init(i, 0);
+	for (i = 0; i < AWE_MAX_CHANNELS; i++)
+		awe_channel_init(i);
+	for (i = 0; i < 16; i++) {
+		awe_operations.chn_info[i].controllers[CTL_MAIN_VOLUME] = 127;
+		awe_operations.chn_info[i].controllers[CTL_EXPRESSION] = 127;
 	}
 	awe_init_fm();
 	awe_tweak();
@@ -1676,10 +1826,15 @@
 	int cmd = event[2];
 	if (cmd & _AWE_MODE_FLAG)
 		awe_hw_awe_control(dev, cmd & _AWE_MODE_VALUE_MASK, event);
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 	else
 		awe_hw_gus_control(dev, cmd & _AWE_MODE_VALUE_MASK, event);
+#endif
 }
 
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
 /* GUS compatible controls */
 static void
 awe_hw_gus_control(int dev, int cmd, unsigned char *event)
@@ -1688,7 +1843,6 @@
 	unsigned short p1;
 	short p2;
 	int plong;
-	DECL_INTR_FLAGS(flags);
 
 	voice = event[3];
 	p1 = *(unsigned short *) &event[4];
@@ -1698,7 +1852,7 @@
 	switch (cmd) {
 	case _GUS_NUMVOICES:
 		if (p1 >= awe_max_voices)
-			printk("AWE32: num_voices: voices out of range %d\n", p1);
+			DEBUG(0,printk("AWE32: num_voices: voices out of range %d\n", p1));
 		break;
 	case _GUS_VOICESAMPLE:
 		if (voice < awe_max_voices)
@@ -1706,19 +1860,13 @@
 		break;
 
 	case _GUS_VOICEON:
-		if (voice < awe_max_voices) {
-			DISABLE_INTR(flags);
+		if (voice < awe_max_voices)
 			awe_note_on(voice);
-			RESTORE_INTR(flags);
-		}
 		break;
 		
 	case _GUS_VOICEOFF:
-		if (voice < awe_max_voices) {
-			DISABLE_INTR(flags);
+		if (voice < awe_max_voices)
 			awe_note_off(voice);
-			RESTORE_INTR(flags);
-		}
 		break;
 		
 	case _GUS_VOICEMODE:
@@ -1726,9 +1874,9 @@
 		break;
 
 	case _GUS_VOICEBALA:
-		/* -128 to 127 */
+		/* 0 to 15 --> -128 to 127 */
 		if (voice < awe_max_voices)
-			awe_panning(dev, voice, (short)p1);
+			awe_panning(dev, voice, ((int)p1 << 4) - 128);
 		break;
 
 	case _GUS_VOICEFREQ:
@@ -1754,13 +1902,35 @@
 
 	case _GUS_VOICE_POS:
 		if (voice < awe_max_voices) {
-			FX_SET(voice, AWE_FX_SAMPLE_START, (short)(plong & 0x7fff));
-			FX_SET(voice, AWE_FX_COARSE_SAMPLE_START, (plong >> 15) & 0xffff);
+			FX_SET(&voices[voice].cinfo->fx, AWE_FX_SAMPLE_START,
+			       (short)(plong & 0x7fff));
+			FX_SET(&voices[voice].cinfo->fx, AWE_FX_COARSE_SAMPLE_START,
+			       (plong >> 15) & 0xffff);
 		}
 		break;
 	}
 }
 
+#endif
+
+
+/* converter function table for realtime paramter change */
+
+static fx_affect_func fx_realtime[] = {
+	/* env1: delay, attack, hold, decay, release, sustain, pitch, cutoff*/
+	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+	/* env2: delay, attack, hold, decay, release, sustain */
+	NULL, NULL, NULL, NULL, NULL, NULL,
+	/* lfo1: delay, freq, volume, pitch, cutoff */
+	NULL, awe_fx_tremfrq, awe_fx_tremfrq, awe_fx_fmmod, awe_fx_fmmod,
+	/* lfo2: delay, freq, pitch */
+	NULL, awe_fx_fm2frq2, awe_fx_fm2frq2,
+	/* global: initpitch, chorus, reverb, cutoff, filterQ */
+	awe_set_voice_pitch, NULL, NULL, awe_fx_cutoff, NULL,
+	/* sample: start, loopstart, loopend */
+	NULL, NULL, NULL,
+};
+
 
 /* AWE32 specific controls */
 static void
@@ -1770,20 +1940,29 @@
 	unsigned short p1;
 	short p2;
 	int chn;
+	awe_chan_info *cinfo;
+	int i;
 
 	chn = event[1];
 	voice = event[3];
 	p1 = *(unsigned short *) &event[4];
 	p2 = *(short *) &event[6];
 	
+	if (awe_channel_mode) {
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+		cinfo = &channels[voice];
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
+			return;
+		cinfo = voices[voice].cinfo;
+	}
 
-#ifdef AWE_DEBUG_ON
 	switch (cmd) {
 	case _AWE_DEBUG_MODE:
 		debug_mode = p1;
 		printk("AWE32: debug mode = %d\n", debug_mode);
 		break;
-#endif
 	case _AWE_REVERB_MODE:
 		if (p1 <= 7) {
 			reverb_mode = p1;
@@ -1810,32 +1989,36 @@
 		break;
 
 	case _AWE_SEND_EFFECT:
-		if (voice < awe_max_voices && p1 < AWE_FX_END) {
-			FX_SET(voice, p1, p2);
-			DEBUG(0,printk("AWE32: effects (%d) %d %d\n", voice, p1, voices[voice].fx[p1]));
+		if (p1 < AWE_FX_END) {
+			FX_SET(&cinfo->fx, p1, p2);
+			DEBUG(0,printk("AWE32: effects (%d) %d %d\n", voice, p1, cinfo->fx.val[p1]));
+			FX_SET(&cinfo->fx, p1, p2);
 			if (fx_realtime[p1]) {
 				DEBUG(0,printk("AWE32: fx_realtime (%d)\n", voice));
-				fx_realtime[p1](voice);
+				awe_voice_change(voice, fx_realtime[p1]);
 			}
 		}
 		break;
 
-	case _AWE_TERMINATE_CHANNEL:
-		if (voice < awe_max_voices) {
-			DEBUG(0,printk("AWE32: terminate (%d)\n", voice));
-			awe_terminate(voice);
-			awe_voice_init(voice, 1);
-		}
-		break;
-
 	case _AWE_TERMINATE_ALL:
 		DEBUG(0,printk("AWE32: terminate all\n"));
 		awe_reset(0);
 		break;
 
+	case _AWE_TERMINATE_CHANNEL:
+		awe_voice_change(voice, (fx_affect_func)awe_terminate);
+		break;
+
+	case _AWE_NOTEOFF_ALL:
+		for (i = 0; i < awe_max_voices; i++)
+			awe_note_off(i);
+		break;
+
 	case _AWE_INITIAL_VOLUME:
 		DEBUG(0,printk("AWE32: init attenuation %d\n", p1));
 		init_atten = p1;
+		for (i = 0; i < awe_max_voices; i++)
+			awe_set_voice_vol(i, FALSE);
 		break;
 
 	case _AWE_SET_GUS_BANK:
@@ -1843,6 +2026,46 @@
 		awe_gus_bank = p1;
 		break;
 		
+	 /* v0.3 stuffs */
+	case _AWE_CHANNEL_MODE:
+		DEBUG(0,printk("AWE32: channel mode = %d\n", p1));
+		awe_channel_mode = p1;
+		awe_reset(0);
+		break;
+
+	case _AWE_DRUM_CHANNELS:
+		DEBUG(0,printk("AWE32: drum flags = %x\n", p1));
+		drum_flags = p1;
+		break;
+
+	case _AWE_EXCLUSIVE_SOUND:
+		DEBUG(0,printk("AWE32: exclusive mode = %d\n", p1));
+		awe_exclusive_sound = p1;
+		break;
+
+	case _AWE_GET_CURRENT_MODE:
+		{
+			awe_mode_rec tmprec;
+			tmprec.base_addr = awe_base;
+			tmprec.mem_size = awe_mem_size / 2;
+			tmprec.max_voices = awe_max_voices;
+			tmprec.max_infos = AWE_MAX_INFOS;
+			tmprec.max_samples = AWE_MAX_SAMPLES;
+			tmprec.current_sf_id = current_sf_id;
+			tmprec.free_mem = free_mem_ptr;
+			tmprec.free_info = free_info;
+			tmprec.free_sample = free_sample;
+			tmprec.reverb_mode = reverb_mode;
+			tmprec.chorus_mode = chorus_mode;
+			tmprec.init_atten = init_atten;
+			tmprec.channel_mode = awe_channel_mode;
+			tmprec.gus_bank = awe_gus_bank;
+			tmprec.exclusive_sound = awe_exclusive_sound;
+			tmprec.drum_flags = drum_flags;
+			tmprec.debug_mode = debug_mode;
+			IOCTL_TO_USER(*(awe_mode_rec**)&event[4], 0, &tmprec, sizeof(tmprec));
+			break;
+		}
 	default:
 		DEBUG(0,printk("AWE32: hw control cmd=%d voice=%d\n", cmd, voice));
 		break;
@@ -1862,9 +2085,12 @@
 	awe_patch_info patch;
 	int rc = 0;
 
+#ifdef AWE_HAS_GUS_COMPATIBILITY
 	if (format == GUS_PATCH) {
 		return awe_load_guspatch(addr, offs, count, pmgr_flag);
-	} else if (format == SYSEX_PATCH) {
+	} else
+#endif
+	if (format == SYSEX_PATCH) {
 		/* no system exclusive message supported yet */
 		return 0;
 	} else if (format != AWE_PATCH) {
@@ -1872,14 +2098,14 @@
 		return RET_ERROR(EINVAL);
 	}
 	
-	if (count < sizeof(awe_patch_info)) {
+	if (count < AWE_PATCH_INFO_SIZE) {
 		FATALERR(printk("AWE32 Error: Patch header too short\n"));
 		return RET_ERROR(EINVAL);
 	}
 	COPY_FROM_USER(((char*)&patch) + offs, addr, offs, 
-		       sizeof(awe_patch_info) - offs);
+		       AWE_PATCH_INFO_SIZE - offs);
 
-	count -= sizeof(awe_patch_info);
+	count -= AWE_PATCH_INFO_SIZE;
 	if (count < patch.len) {
 		FATALERR(printk("AWE32 Warning: Patch record too short (%d<%d)\n",
 		       count, (int)patch.len));
@@ -1919,12 +2145,12 @@
 	unsigned char bank, instr;
 	int total_size;
 
-	if (patch->len < sizeof(awe_voice_rec)) {
+	if (patch->len < AWE_VOICE_REC_SIZE) {
 		FATALERR(printk("AWE32 Error: invalid patch info length\n"));
 		return RET_ERROR(EINVAL);
 	}
 
-	offset = sizeof(awe_patch_info);
+	offset = AWE_PATCH_INFO_SIZE;
 	GET_BYTE_FROM_USER(bank, addr, offset); offset++;
 	GET_BYTE_FROM_USER(instr, addr, offset); offset++;
 	GET_SHORT_FROM_USER(nvoices, addr, offset); offset+=2;
@@ -1938,7 +2164,7 @@
 		return RET_ERROR(ENOSPC);
 	}
 
-	total_size = sizeof(awe_voice_rec) + sizeof(awe_voice_info) * nvoices;
+	total_size = AWE_VOICE_REC_SIZE + AWE_VOICE_INFO_SIZE * nvoices;
 	if (patch->len < total_size) {
 		ERRMSG(printk("AWE32 Error: patch length(%d) is smaller than nvoices(%d)\n",
 		       (int)patch->len, nvoices));
@@ -1958,8 +2184,8 @@
 		rec->next_bank = NULL;
 
 		/* copy awe_voice_info parameters */
-		COPY_FROM_USER(&rec->v, addr, offset, sizeof(awe_voice_info));
-		offset += sizeof(awe_voice_info);
+		COPY_FROM_USER(&rec->v, addr, offset, AWE_VOICE_INFO_SIZE);
+		offset += AWE_VOICE_INFO_SIZE;
 		rec->v.sf_id = current_sf_id;
 		if (rec->v.mode & AWE_MODE_INIT_PARM)
 			awe_init_voice_parm(&rec->v.parm);
@@ -1988,11 +2214,11 @@
 		return RET_ERROR(ENOSPC);
 	}
 
-	size = (patch->len - sizeof(awe_sample_info)) / 2;
-	offset = sizeof(awe_patch_info);
+	size = (patch->len - AWE_SAMPLE_INFO_SIZE) / 2;
+	offset = AWE_PATCH_INFO_SIZE;
 	COPY_FROM_USER(&samples[free_sample], addr, offset,
-		       sizeof(awe_sample_info));
-	offset += sizeof(awe_sample_info);
+		       AWE_SAMPLE_INFO_SIZE);
+	offset += AWE_SAMPLE_INFO_SIZE;
 	if (size != samples[free_sample].size) {
 		ERRMSG(printk("AWE32 Warning: sample size differed (%d != %d)\n",
 		       (int)samples[free_sample].size, (int)size));
@@ -2015,14 +2241,51 @@
 {
 	if (!loaded_once) {
 		/* it's the first time */
-		last_sample = free_sample;
-		last_info = free_info;
+		if (current_sf_id == 1) {
+			last_sample = free_sample;
+			last_info = free_info;
+		}
 		current_sf_id++;
 		loaded_once = 1;
 	}
 }
 
 
+/*----------------------------------------------------------------*/
+
+static const char *readbuf_addr;
+static long readbuf_offs;
+static int readbuf_flags;
+
+/* initialize read buffer */
+static void
+awe_init_readbuf(const char *addr, long offset, int size, int mode_flags)
+{
+	readbuf_addr = addr;
+	readbuf_offs = offset;
+	readbuf_flags = mode_flags;
+}
+
+/* read directly from user buffer */
+static unsigned short
+awe_read_word(int pos)
+{
+	unsigned short c;
+	/* read from user buffer */
+	if (readbuf_flags & AWE_SAMPLE_8BITS) {
+		unsigned char cc;
+		GET_BYTE_FROM_USER(cc, readbuf_addr, readbuf_offs + pos);
+		c = cc << 8; /* convert 8bit -> 16bit */
+	} else {
+		GET_SHORT_FROM_USER(c, readbuf_addr, readbuf_offs + pos * 2);
+	}
+	if (readbuf_flags & AWE_SAMPLE_UNSIGNED)
+		c ^= 0x8000; /* unsigned -> signed */
+	return c;
+}
+
+
+
 #define BLANK_LOOP_START	8
 #define BLANK_LOOP_END		40
 #define BLANK_LOOP_SIZE		48
@@ -2035,7 +2298,6 @@
 	int i, truesize;
 	int rc;
 	unsigned long csum1, csum2;
-	DECL_INTR_FLAGS(flags);
 
 	/* be sure loop points start < end */
 	if (sp->loopstart > sp->loopend) {
@@ -2066,42 +2328,33 @@
 	sp->loopstart += free_mem_ptr + AWE_DRAM_OFFSET;
 	sp->loopend += free_mem_ptr + AWE_DRAM_OFFSET;
 
-	DISABLE_INTR(flags);
 	if ((rc = awe_open_dram_for_write(free_mem_ptr)) != 0) {
-		RESTORE_INTR(flags);
 		return rc;
 	}
 
+	awe_init_readbuf(addr, offset, size, sp->mode_flags);
 	csum1 = 0;
 	for (i = 0; i < size; i++) {
-		unsigned char cc;
 		unsigned short c;
-		if (sp->mode_flags & AWE_SAMPLE_8BITS) {
-			GET_BYTE_FROM_USER(cc, addr, offset); offset++;
-			c = cc << 8; /* convert 8bit -> 16bit */
-		} else {
-			GET_SHORT_FROM_USER(c, addr, offset); offset += 2;
-		}
-		if (sp->mode_flags & AWE_SAMPLE_UNSIGNED)
-			c ^= 0x8000; /* unsigned -> signed */
+		c = awe_read_word(i);
 		csum1 += c;
 		awe_write_dram(c);
 		if (i == sp->loopend &&
-		    (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP)) {
+		    (sp->mode_flags & (AWE_SAMPLE_BIDIR_LOOP|AWE_SAMPLE_REVERSE_LOOP))) {
 			int looplen = sp->loopend - sp->loopstart;
 			/* copy reverse loop */
 			int k;
 			for (k = 0; k < looplen; k++) {
-				if (sp->mode_flags & AWE_SAMPLE_8BITS) {
-					GET_BYTE_FROM_USER(cc, addr, offset-k);
-					c = cc << 8;
-				} else {
-					GET_SHORT_FROM_USER(c, addr, offset-k*2);
-				}
-				if (sp->mode_flags & AWE_SAMPLE_UNSIGNED)
-					c ^= 0x8000;
+				/* non-buffered data */
+				c = awe_read_word(i - k);
 				awe_write_dram(c);
 			}
+			if (sp->mode_flags & AWE_SAMPLE_BIDIR_LOOP) {
+				sp->end += looplen;
+			} else {
+				sp->start += looplen;
+				sp->end += looplen;
+			}
 		}
 	}
 
@@ -2117,19 +2370,15 @@
 	}
 
 	awe_close_dram();
-	RESTORE_INTR(flags);
 	if (sp->checksum_flag) {
 #ifdef AWE_CHECKSUM_DATA
 		if (sp->checksum_flag != 2 && csum1 != sp->checksum) {
 			ERRMSG(printk("AWE32: [%d] checksum mismatch on data %x:%x\n",
-			       free_sample,
-			       (int)samples[free_sample].checksum,
-			       (int)csum1));
+			       free_sample, (int)sp->checksum, (int)csum1));
 			return RET_ERROR(NO_DATA_ERR);
 		}
 #endif /* AWE_CHECKSUM_DATA */
 #ifdef AWE_CHECKSUM_MEMORY
-		DISABLE_INTR(flags);
 		if (awe_open_dram_for_read(free_mem_ptr) == 0) {
 			csum2 = 0;
 			for (i = 0; i < size; i++) {
@@ -2138,30 +2387,28 @@
 				csum2 += c;
 			}
 			awe_close_dram_for_read();
-			if (csum2 != samples[free_sample].checksum) {
-				RESTORE_INTR(flags);
+			if (csum1 != csum2) {
 				ERRMSG(printk("AWE32: [%d] checksum mismatch on DRAM %x:%x\n",
-					      free_sample,
-					      (int)samples[free_sample].checksum,
-					      (int)csum2));
+					      free_sample, (int)csum1, (int)csum2));
 				return RET_ERROR(NO_DATA_ERR);
 			}
 		}
-		RESTORE_INTR(flags);
 #endif /* AWE_CHECKSUM_MEMORY */
 	}
 	free_mem_ptr += sp->size;
 
 	/* re-initialize FM passthrough */
-	DISABLE_INTR(flags);
 	awe_init_fm();
 	awe_tweak();
-	RESTORE_INTR(flags);
 
 	return 0;
 }
 
 
+/*----------------------------------------------------------------*/
+
+#ifdef AWE_HAS_GUS_COMPATIBILITY
+
 /* calculate GUS envelope time:
  * is this correct?  i have no idea..
  */
@@ -2219,7 +2466,7 @@
 	samples[free_sample].start = 0;
 	samples[free_sample].end = patch.len;
 	samples[free_sample].loopstart = patch.loop_start;
-	samples[free_sample].loopend = patch.loop_end;
+	samples[free_sample].loopend = patch.loop_end + 1;
 	samples[free_sample].size = patch.len;
 
 	/* set up mode flags */
@@ -2229,10 +2476,12 @@
 	if (patch.mode & WAVE_UNSIGNED)
 		samples[free_sample].mode_flags |= AWE_SAMPLE_UNSIGNED;
 	samples[free_sample].mode_flags |= AWE_SAMPLE_NO_BLANK;
-	if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP)))
+	if (!(patch.mode & (WAVE_LOOPING|WAVE_BIDIR_LOOP|WAVE_LOOP_BACK)))
 		samples[free_sample].mode_flags |= AWE_SAMPLE_SINGLESHOT;
 	if (patch.mode & WAVE_BIDIR_LOOP)
 		samples[free_sample].mode_flags |= AWE_SAMPLE_BIDIR_LOOP;
+	if (patch.mode & WAVE_LOOP_BACK)
+		samples[free_sample].mode_flags |= AWE_SAMPLE_REVERSE_LOOP;
 
 	DEBUG(0,printk("AWE32: [sample %d mode %x]\n", patch.instr_no,
 		       samples[free_sample].mode_flags));
@@ -2336,8 +2585,11 @@
 	return 0;
 }
 
+#endif  /* AWE_HAS_GUS_COMPATIBILITY */
 
-/* remove samples with current sf_id from instrument list */
+/*----------------------------------------------------------------*/
+
+/* remove samples with different sf_id from instrument list */
 static awe_voice_list *
 awe_get_removed_list(awe_voice_list *curp)
 {
@@ -2347,7 +2599,7 @@
 	prevp = &lastp;
 	for (maxc = AWE_MAX_INFOS;
 	     curp && maxc; curp = curp->next_instr, maxc--) {
-		if (curp->v.sf_id == current_sf_id)
+		if (curp->v.sf_id != 1)
 			*prevp = curp->next_instr;
 		else
 			prevp = &curp->next_instr;
@@ -2364,9 +2616,14 @@
 	int maxc;
 	int i;
 
+	/* no sample is loaded yet */
 	if (last_sample == free_sample && last_info == free_info)
 		return;
 
+	/* only the primary samples are loaded */
+	if (current_sf_id <= 1)
+		return;
+
 	/* remove the records from preset table */
 	for (i = 0; i < AWE_MAX_PRESETS; i++) {
 		prevp = &preset_table[i];
@@ -2388,7 +2645,7 @@
 
 	free_sample = last_sample;
 	free_info = last_info;
-	current_sf_id--;
+	current_sf_id = 1;
 	loaded_once = 0;
 }
 
@@ -2407,7 +2664,7 @@
 			vp->loopstart += samples[i].loopstart;
 			vp->loopend += samples[i].loopend;
 			/* copy mode flags */
-			vp->mode |= (samples[i].mode_flags << 6);
+			vp->mode = samples[i].mode_flags;
 			/* set index */
 			vp->index = i;
 			return i;
@@ -2421,16 +2678,17 @@
 static void
 awe_aftertouch(int dev, int voice, int pressure)
 {
-	DECL_INTR_FLAGS(flags);
-
 	DEBUG(2,printk("AWE32: [after(%d) %d]\n", voice, pressure));
-	if (voice < 0 || voice >= awe_max_voices)
-		return;
-	voices[voice].velocity = pressure;
-	awe_calc_volume(voice);
-	DISABLE_INTR(flags);
-	awe_set_volume(voice);
-	RESTORE_INTR(flags);
+	if (awe_channel_mode == 2) {
+		int note = (voice_alloc->map[voice] & 0xff) - 1;
+		awe_start_note(dev, voice, note + 0x80, pressure);
+
+	} else if (awe_channel_mode == 0) {
+		if (voice < 0 || voice >= awe_max_voices)
+			return;
+		voices[voice].velocity = pressure;
+		awe_set_voice_vol(voice, FALSE);
+	}
 }
 
 
@@ -2438,77 +2696,98 @@
 static void
 awe_controller(int dev, int voice, int ctrl_num, int value)
 {
-	DECL_INTR_FLAGS(flags);
+	awe_chan_info *cinfo;
 
-	if (voice < 0 || voice >= awe_max_voices)
-		return;
+	if (awe_channel_mode) {
+		if (awe_channel_mode == 2) /* get channel */
+			voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+		cinfo = &channels[voice];
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
+			return;
+		cinfo = voices[voice].cinfo;
+	}
 
 	switch (ctrl_num) {
-	case CTL_BANK_SELECT:
+	case CTL_BANK_SELECT: /* SEQ1 control */
 		DEBUG(2,printk("AWE32: [bank(%d) %d]\n", voice, value));
-		voices[voice].bank = value;
+		cinfo->bank = value;
+		awe_set_instr(dev, voice, cinfo->instr);
 		break;
 
-	case CTRL_PITCH_BENDER:
+	case CTRL_PITCH_BENDER: /* SEQ1 V2 contorl */
 		DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, value));
 		/* zero centered */
-		voices[voice].bender = value;
-		awe_calc_pitch(voice);
-		DISABLE_INTR(flags);
-		awe_set_pitch(voice);
-		RESTORE_INTR(flags);
+		cinfo->bender = value;
+		awe_voice_change(voice, awe_set_voice_pitch);
 		break;
 
-	case CTRL_PITCH_BENDER_RANGE:
+	case CTRL_PITCH_BENDER_RANGE: /* SEQ1 V2 control */
 		DEBUG(2,printk("AWE32: [range(%d) %d]\n", voice, value));
-		/* sense x 100 */
-		voices[voice].bender_range = value;
+		/* value = sense x 100 */
+		cinfo->bender_range = value;
 		/* no audible pitch change yet.. */
 		break;
 
-	case CTL_EXPRESSION:
-		value /= 128;
-	case CTRL_EXPRESSION:
+	case CTL_EXPRESSION: /* SEQ1 control */
+		if (!awe_channel_mode)
+			value /= 128;
+	case CTRL_EXPRESSION: /* SEQ1 V2 control */
 		DEBUG(2,printk("AWE32: [expr(%d) %d]\n", voice, value));
 		/* 0 - 127 */
-		voices[voice].expression_vol = value;
-		awe_calc_volume(voice);
-		DISABLE_INTR(flags);
-		awe_set_volume(voice);
-		RESTORE_INTR(flags);
+		cinfo->expression_vol = value;
+		awe_voice_change(voice, awe_set_voice_vol);
 		break;
 
-	case CTL_PAN:
+	case CTL_PAN:	/* SEQ1 control */
 		DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, value));
 		/* (0-127) -> signed 8bit */
-		voices[voice].panning = value * 2 - 128;
-		DISABLE_INTR(flags);
-		awe_set_pan(voice, 0);
-		RESTORE_INTR(flags);
+		cinfo->panning = value * 2 - 128;
+		awe_voice_change(voice, awe_set_pan);
 		break;
 
-	case CTL_MAIN_VOLUME:
-		value = (value * 127) / 16383;
-	case CTRL_MAIN_VOLUME:
+	case CTL_MAIN_VOLUME:	/* SEQ1 control */
+		if (!awe_channel_mode)
+			value = (value * 100) / 16383;
+	case CTRL_MAIN_VOLUME:	/* SEQ1 V2 control */
 		DEBUG(2,printk("AWE32: [mainvol(%d) %d]\n", voice, value));
 		/* 0 - 127 */
-		voices[voice].main_vol = value;
-		awe_calc_volume(voice);
-		DISABLE_INTR(flags);
-		awe_set_volume(voice);
-		RESTORE_INTR(flags);
+		cinfo->main_vol = value;
+		awe_voice_change(voice, awe_set_voice_vol);
 		break;
 
 	case CTL_EXT_EFF_DEPTH: /* reverb effects: 0-127 */
 		DEBUG(2,printk("AWE32: [reverb(%d) %d]\n", voice, value));
-		FX_SET(voice, AWE_FX_REVERB, value * 2);
-		break;		
+		FX_SET(&cinfo->fx, AWE_FX_REVERB, value * 2);
+		break;
 
 	case CTL_CHORUS_DEPTH: /* chorus effects: 0-127 */
 		DEBUG(2,printk("AWE32: [chorus(%d) %d]\n", voice, value));
-		FX_SET(voice, AWE_FX_CHORUS, value * 2);
-		break;		
+		FX_SET(&cinfo->fx, AWE_FX_CHORUS, value * 2);
+		break;
 
+#ifdef AWE_ACCEPT_ALL_SOUNDS_CONTROLL
+	case 120:  /* all sounds off */
+		{int i; for (i = 0; i < AWE_NORMAL_VOICES; i++)
+			awe_terminate(i);
+		}
+		/*awe_reset(0);*/
+		break;
+	case 123:  /* all notes off */
+		{int i;
+		for (i = 0; i < awe_max_voices; i++)
+			awe_note_off(i);
+		}
+		break;
+#endif
+
+	case CTL_SUSTAIN: /* sustain the channel */
+		cinfo->sustained = value;
+		if (value == 0)
+			awe_voice_change(voice, awe_sustain_off);
+		break;
 
 	default:
 		DEBUG(0,printk("AWE32: [control(%d) ctrl=%d val=%d]\n",
@@ -2518,18 +2797,48 @@
 }
 
 
+
+/* change the voice parameters */
+static void awe_voice_change(int voice, fx_affect_func func)
+{
+	int i; 
+	if (! awe_channel_mode) {
+		func(voice, FALSE);
+		return;
+	}
+
+	for (i = 0; i < awe_max_voices; i++)
+		if (voices[i].ch == voice)
+			func(i, FALSE);
+}
+
+
+/* drop sustain */
+static void awe_sustain_off(int voice, int forced)
+{
+	if (voices[voice].state == AWE_ST_SUSTAINED)
+		awe_note_off(voice);
+}
+
+
 /* voice pan change (value = -128 - 127) */
 static void
 awe_panning(int dev, int voice, int value)
 {
-	DECL_INTR_FLAGS(flags);
-	if (voice >= 0 || voice < awe_max_voices) {
-		voices[voice].panning = value;
-		DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, voices[voice].panning));
-		DISABLE_INTR(flags);
-		awe_set_pan(voice, 0);
-		RESTORE_INTR(flags);
+	awe_chan_info *cinfo;
+	if (awe_channel_mode) {
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+		cinfo = &channels[voice];
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
+			return;
+		cinfo = voices[voice].cinfo;
 	}
+
+	cinfo->panning = value;
+	DEBUG(2,printk("AWE32: [pan(%d) %d]\n", voice, cinfo->panning));
+	awe_voice_change(voice, awe_set_pan);
 }
 
 
@@ -2542,63 +2851,173 @@
 }
 
 
+#ifndef AWE_NO_PATCHMGR
+/* patch manager */
+static int
+awe_patchmgr(int dev, struct patmgr_info *rec)
+{
+	FATALERR(printk("AWE32 Warning: patch manager control not supported\n"));
+	return 0;
+}
+#endif
+
+
 /* pitch wheel change: 0-16384 */
 static void
 awe_bender(int dev, int voice, int value)
 {
-	DECL_INTR_FLAGS(flags);
-
-	if (voice < 0 || voice >= awe_max_voices)
-		return;
+	awe_chan_info *cinfo;
+	if (awe_channel_mode) {
+		if (awe_channel_mode == 2)
+			voice = voice_alloc->map[voice] >> 8;
+		if (voice < 0 || voice >= AWE_MAX_CHANNELS)
+			return;
+		cinfo = &channels[voice];
+	} else {
+		if (voice < 0 || voice >= awe_max_voices)
+			return;
+		cinfo = voices[voice].cinfo;
+	}
 	/* convert to zero centered value */
-	voices[voice].bender = value - 8192;
-	DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, voices[voice].bender));
-	awe_calc_pitch(voice);
-	DISABLE_INTR(flags);
-	awe_set_pitch(voice);
-	RESTORE_INTR(flags);
+	cinfo->bender = value - 8192;
+	DEBUG(2,printk("AWE32: [bend(%d) %d]\n", voice, cinfo->bender));
+	awe_voice_change(voice, awe_set_voice_pitch);
 }
 
-
-/* search an empty voice; used by sequencer2 */
+/* calculate the number of voices for this note & velocity */
 static int
-awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
+awe_search_multi_voices(awe_voice_list *rec, int note, int velocity, awe_voice_info **vlist)
 {
-	int i, p, best = -1, best_time = 0x7fffffff;
+	int maxc;
+	int nvoices;
+	unsigned short sf_id;
 
-	p = alloc->ptr;
-	/* First look for a completely stopped voice */
+	sf_id = current_sf_id;
+	nvoices = 0;
+	for (maxc = AWE_MAX_INFOS;
+	     rec && maxc; rec = rec->next_instr, maxc--) {
+		if (rec->v.low <= note && note <= rec->v.high &&
+		    velocity >= rec->v.vellow && velocity <= rec->v.velhigh) {
+			if (nvoices == 0)
+				sf_id = rec->v.sf_id;
+			else if (rec->v.sf_id != sf_id)
+				continue;
+			vlist[nvoices] = &rec->v;
+			nvoices++;
+			if (nvoices >= AWE_MAX_VOICES)
+				break;
+		}
+	}
+	return nvoices;	
+}
 
-	for (i = 0; i < alloc->max_voice; i++) {
-		if (alloc->map[p] == 0) {
-			alloc->ptr = p;
-			return p;
+/* allocate voices corresponding note and velocity; supports multiple insts. */
+static void
+awe_alloc_multi_voices(int ch, int note, int velocity)
+{
+	int i, v, nvoices;
+	awe_voice_info *vlist[AWE_MAX_VOICES];
+
+	if (channels[ch].vrec == NULL && channels[ch].def_vrec == NULL)
+		awe_set_instr(0, ch, channels[ch].instr);
+
+	nvoices = awe_search_multi_voices(channels[ch].vrec, note, velocity, vlist);
+	if (nvoices == 0)
+		nvoices = awe_search_multi_voices(channels[ch].def_vrec, note, velocity, vlist);
+
+	/* allocate the voices */
+	current_alloc_time++;
+	for (i = 0; i < nvoices; i++) {
+		v = awe_clear_voice();
+		voices[v].key = AWE_CHAN_KEY(ch, note);
+		voices[v].ch = ch;
+		voices[v].time = current_alloc_time;
+		voices[v].cinfo = &channels[ch];
+		voices[v].sample = vlist[i];
+		voices[v].state = AWE_ST_MARK;
+	}
+
+	/* clear the mark in allocated voices */
+	for (i = 0; i < awe_max_voices; i++)
+		if (voices[i].state == AWE_ST_MARK)
+			voices[i].state = AWE_ST_OFF;
+}
+
+
+/* search an empty voice; used internally */
+static int
+awe_clear_voice(void)
+{
+	int i, time, best;
+
+	/* looking for the oldest empty voice */
+	best = -1;
+	time = 0x7fffffff;
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].state == AWE_ST_OFF && voices[i].time < time) {
+			best = i;
+			time = voices[i].time;
 		}
-		if (alloc->alloc_times[p] < best_time) {
-			best = p;
-			best_time = alloc->alloc_times[p];
+	}
+	if (best >= 0)
+		return best;
+
+	/* looking for the oldest sustained voice */
+	time = 0x7fffffff;
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].state == AWE_ST_SUSTAINED &&
+		    voices[i].time < time) {
+			best = i;
+			time = voices[i].time;
 		}
-		p = (p + 1) % alloc->max_voice;
+	}
+	if (best >= 0) {
+		awe_note_off(best);
+		return best;
 	}
 
-	/* Then look for a releasing voice */
-	for (i = 0; i < alloc->max_voice; i++) {
-		if (alloc->map[p] == 0xffff) {
-			alloc->ptr = p;
-			return p;
+	/* looking for the oldest voice not marked */
+	time = 0x7fffffff;
+	best = 0;
+	for (i = 0; i < awe_max_voices; i++) {
+		if (voices[i].state != AWE_ST_MARK && voices[i].time < time) {
+			best = i;
+			time = voices[i].time;
 		}
-		p = (p + 1) % alloc->max_voice;
 	}
+	/*awe_terminate(best);*/
+	awe_note_off(best);
+	return best;
+}
 
-	if (best >= 0)
-		p = best;
 
-	/* terminate the voice */
-	if (voices[p].state)
-		awe_terminate(p);
+/* allocate a voice corresponding note and velocity; single instrument */
+static void
+awe_alloc_one_voice(int voice, int note, int velocity)
+{
+	int nvoices;
+	awe_voice_info *vlist[AWE_MAX_VOICES];
+
+	if (voices[voice].cinfo->vrec == NULL && voices[voice].cinfo->def_vrec == NULL)
+		awe_set_instr(0, voice, voices[voice].cinfo->instr);
+
+	nvoices = awe_search_multi_voices(voices[voice].cinfo->vrec, note, velocity, vlist);
+	if (nvoices == 0)
+		nvoices = awe_search_multi_voices(voices[voice].cinfo->def_vrec, note, velocity, vlist);
 
-	alloc->ptr = p;
-	return p;
+	if (nvoices > 0) {
+		voices[voice].time = ++current_alloc_time;
+		voices[voice].sample = vlist[0]; /* use the first one */
+	}
+}
+
+
+/* search an empty voice; used by sequencer2 */
+static int
+awe_alloc(int dev, int chn, int note, struct voice_alloc_info *alloc)
+{
+	awe_channel_mode = 2;
+	return awe_clear_voice();
 }
 
 
@@ -2610,18 +3029,27 @@
 	if (synth_devs[dev] == NULL ||
 	    (info = &synth_devs[dev]->chn_info[chn]) == NULL)
 		return;
+
 	if (voice < 0 || voice >= awe_max_voices)
 		return;
 
 	DEBUG(2,printk("AWE32: [setup(%d) ch=%d]\n", voice, chn));
-	voices[voice].expression_vol = info->controllers[CTL_EXPRESSION];
-	voices[voice].main_vol =
-		(info->controllers[CTL_MAIN_VOLUME] * 100) / 128; /* 0 - 127 */
-	voices[voice].panning =
+	channels[chn].expression_vol = info->controllers[CTL_EXPRESSION];
+	channels[chn].main_vol = info->controllers[CTL_MAIN_VOLUME];
+	channels[chn].panning =
 		info->controllers[CTL_PAN] * 2 - 128; /* signed 8bit */
-	voices[voice].bender = info->bender_value; /* zero center */
-	voices[voice].bank = info->controllers[CTL_BANK_SELECT];
-	awe_set_instr(dev, voice, info->pgm_num);
+	channels[chn].bender = info->bender_value; /* zero center */
+	channels[chn].bank = info->controllers[CTL_BANK_SELECT];
+	channels[chn].sustained = info->controllers[CTL_SUSTAIN];
+	if (info->controllers[CTL_EXT_EFF_DEPTH]) {
+		FX_SET(&channels[chn].fx, AWE_FX_REVERB,
+		       info->controllers[CTL_EXT_EFF_DEPTH] * 2);
+	}
+	if (info->controllers[CTL_CHORUS_DEPTH]) {
+		FX_SET(&channels[chn].fx, AWE_FX_CHORUS,
+		       info->controllers[CTL_CHORUS_DEPTH] * 2);
+	}
+	awe_set_instr(dev, chn, info->pgm_num);
 }
 
 
@@ -2637,9 +3065,10 @@
 
 	/* turn off envelope engines */
 	for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
-		awe_poke(AWE_DCYSUSV(ch), 0x0080);
+		awe_poke(AWE_DCYSUSV(ch), 0x80);
 	}
   
+	/* reset all other parameters to zero */
 	for (ch = 0; ch < AWE_MAX_VOICES; ch++) {
 		awe_poke(AWE_ENVVOL(ch), 0);
 		awe_poke(AWE_ENVVAL(ch), 0);
@@ -2672,14 +3101,14 @@
 static void
 awe_init_dma(void)
 {
-	awe_poke_dw(AWE_SMALR, 0x00000000);
-	awe_poke_dw(AWE_SMARR, 0x00000000);
-	awe_poke_dw(AWE_SMALW, 0x00000000);
-	awe_poke_dw(AWE_SMARW, 0x00000000);
+	awe_poke_dw(AWE_SMALR, 0);
+	awe_poke_dw(AWE_SMARR, 0);
+	awe_poke_dw(AWE_SMALW, 0);
+	awe_poke_dw(AWE_SMARW, 0);
 }
 
 
-/* initialization arrays */
+/* initialization arrays; from ADIP */
 
 static unsigned short init1[128] = {
 	0x03ff, 0x0030,  0x07ff, 0x0130, 0x0bff, 0x0230,  0x0fff, 0x0330,
@@ -2718,6 +3147,7 @@
 	0x93ff, 0x8430, 0x97ff, 0x8530, 0x9bff, 0x8630, 0x9fff, 0x8730,
 	0xa3ff, 0x8830, 0xa7ff, 0x8930, 0xabff, 0x8a30, 0xafff, 0x8b30,
 	0xb3ff, 0x8c30, 0xb7ff, 0x8d30, 0xbbff, 0x8e30, 0xbfff, 0x8f30,
+
 	0xc3ff, 0x8030, 0xc7ff, 0x8130, 0xcbff, 0x8230, 0xcfff, 0x8330,
 	0xd3ff, 0x8430, 0xd7ff, 0x8530, 0xdbff, 0x8630, 0xdfff, 0x8730,
 	0xe3ff, 0x8830, 0xe7ff, 0x8930, 0xebff, 0x8a30, 0xefff, 0x8b30,
@@ -2806,35 +3236,39 @@
  * set up awe32 channels to some known state.
  */
 
+/* set the envelope & LFO parameters to the default values; see ADIP */
+static void
+awe_tweak_voice(int i)
+{
+	/* set all mod/vol envelope shape to minimum */
+	awe_poke(AWE_ENVVOL(i), 0x8000);
+	awe_poke(AWE_ENVVAL(i), 0x8000);
+	awe_poke(AWE_DCYSUS(i), 0x7F7F);
+	awe_poke(AWE_ATKHLDV(i), 0x7F7F);
+	awe_poke(AWE_ATKHLD(i), 0x7F7F);
+	awe_poke(AWE_PEFE(i), 0);  /* mod envelope height to zero */
+	awe_poke(AWE_LFO1VAL(i), 0x8000); /* no delay for LFO1 */
+	awe_poke(AWE_LFO2VAL(i), 0x8000);
+	awe_poke(AWE_IP(i), 0xE000);	/* no pitch shift */
+	awe_poke(AWE_IFATN(i), 0xFF00);	/* volume to minimum */
+	awe_poke(AWE_FMMOD(i), 0);
+	awe_poke(AWE_TREMFRQ(i), 0);
+	awe_poke(AWE_FM2FRQ2(i), 0);
+}
+
 static void
 awe_tweak(void)
 {
 	int i;
-
-	/* Set the envelope engine parameters to the "default" values for
-	   simply playing back unarticulated audio at 44.1kHz.  Set all
-	   of the channels: */
-
-	for (i = 0; i < AWE_MAX_VOICES; i++) {
-		awe_poke(AWE_ENVVOL(i)   , 0x8000);
-		awe_poke(AWE_ENVVAL(i)   , 0x8000);
-		awe_poke(AWE_DCYSUS(i)   , 0x7F7F);
-		awe_poke(AWE_ATKHLDV(i)  , 0x7F7F);
-		awe_poke(AWE_LFO1VAL(i)  , 0x8000);
-		awe_poke(AWE_ATKHLD(i)   , 0x7F7F);
-		awe_poke(AWE_LFO2VAL(i)  , 0x8000);
-		awe_poke(AWE_IP(i)       , 0xE000);
-		awe_poke(AWE_IFATN(i)    , 0xFF00);
-		awe_poke(AWE_PEFE(i)     , 0x0000);
-		awe_poke(AWE_FMMOD(i)    , 0x0000);
-		awe_poke(AWE_TREMFRQ(i)  , 0x0010);
-		awe_poke(AWE_FM2FRQ2(i)  , 0x0010);
-	}
+	/* reset all channels */
+	for (i = 0; i < awe_max_voices; i++)
+		awe_tweak_voice(i);
 }
 
 
 /*
- *  initializes the FM section of AWE32
+ *  initializes the FM section of AWE32;
+ *   see Vince Vu's unofficial AWE32 programming guide
  */
 
 static void
@@ -2845,39 +3279,32 @@
 	if (awe_mem_size <= 0)
 		return;
 #endif
-	DEBUG(0,printk("AWE32: initializing FM\n"));
+	DEBUG(3,printk("AWE32: initializing FM\n"));
 
 	/* Initialize the last two channels for DRAM refresh and producing
 	   the reverb and chorus effects for Yamaha OPL-3 synthesizer */
 
-	awe_poke(   AWE_DCYSUSV(30)   , 0x0080);
-	awe_poke_dw(AWE_PSST(30)      , 0xFFFFFFE0);
-	awe_poke_dw(AWE_CSL(30)       , 0xFFFFFFE8);
-	awe_poke_dw(AWE_PTRX(30)      , 0x00FFFF00);
-	awe_poke_dw(AWE_CPF(30)       , 0x00000000);
-	awe_poke_dw(AWE_CCCA(30)      , 0x00FFFFE3);
-
-	awe_poke(   AWE_DCYSUSV(31)   , 0x0080);
-	awe_poke_dw(AWE_PSST(31)      , 0x00FFFFE0);
-	awe_poke_dw(AWE_CSL(31)       , 0xFFFFFFE8);
-	awe_poke_dw(AWE_PTRX(31)      , 0x00FFFF00);
-	awe_poke_dw(AWE_CPF(31)       , 0x00000000);
-	awe_poke_dw(AWE_CCCA(31)      , 0x00FFFFE3);
-
-	/* Timing loop */
-
-	/* PTRX is 32 bit long but do not write to the MS byte */
-	awe_poke(AWE_PTRX(30)      , 0x0000);
-
-	while(! (inw(awe_base-0x620+Pointer) & 0x1000));
-	while(   inw(awe_base-0x620+Pointer) & 0x1000);
-
-	/* now write the MS byte of PTRX */
-	OUTW(0x4828, awe_base-0x620+Data0+0x002);
-
-	awe_poke(   AWE_IFATN(28)     , 0x0000);
-	awe_poke_dw(AWE_VTFT(30)      , 0x8000FFFF);
-	awe_poke_dw(AWE_VTFT(31)      , 0x8000FFFF);
+	/* 31: FM left channel, 0xffffe0-0xffffe8 */
+	awe_poke(AWE_DCYSUSV(30), 0x80);
+	awe_poke_dw(AWE_PSST(30), 0xFFFFFFE0); /* full left */
+	awe_poke_dw(AWE_CSL(30), 0x00FFFFE8 |
+		    (DEF_FM_CHORUS_DEPTH << 24));
+	awe_poke_dw(AWE_PTRX(30), (DEF_FM_REVERB_DEPTH << 8));
+	awe_poke_dw(AWE_CPF(30), 0);
+	awe_poke_dw(AWE_CCCA(30), 0x00FFFFE3);
+
+	/* 32: FM right channel, 0xfffff0-0xfffff8 */
+	awe_poke(AWE_DCYSUSV(31), 0x80);
+	awe_poke_dw(AWE_PSST(31), 0x00FFFFF0); /* full right */
+	awe_poke_dw(AWE_CSL(31), 0x00FFFFF8 |
+		    (DEF_FM_CHORUS_DEPTH << 24));
+	awe_poke_dw(AWE_PTRX(31), (DEF_FM_REVERB_DEPTH << 8));
+	awe_poke_dw(AWE_CPF(31), 0);
+	awe_poke_dw(AWE_CCCA(31), 0x00FFFFF3);
+
+	/* skew volume & cutoff */
+	awe_poke_dw(AWE_VTFT(30), 0x8000FFFF);
+	awe_poke_dw(AWE_VTFT(31), 0x8000FFFF);
 
 	/* change maximum channels to 30 */
 	awe_max_voices = AWE_NORMAL_VOICES;
@@ -2933,22 +3360,19 @@
 static void
 awe_open_dram_for_check(void)
 {
-	int k;
-	unsigned long scratch;
-
-	awe_poke(AWE_HWCF2 , 0x0020);
-
-	for (k = 0; k < AWE_NORMAL_VOICES; k++) {
-		awe_poke(AWE_DCYSUSV(k), 0x0080);
-		awe_poke_dw(AWE_VTFT(k), 0x00000000);
-		awe_poke_dw(AWE_CVCF(k), 0x00000000);
-		awe_poke_dw(AWE_PTRX(k), 0x40000000);
-		awe_poke_dw(AWE_CPF(k), 0x40000000);
-		awe_poke_dw(AWE_PSST(k), 0x00000000);
-		awe_poke_dw(AWE_CSL(k), 0x00000000);
-		scratch = (((k&1) << 9) + 0x400);
-		scratch = scratch << 16;
-		awe_poke_dw(AWE_CCCA(k), scratch);
+	int i;
+	for (i = 0; i < AWE_NORMAL_VOICES; i++) {
+		awe_poke(AWE_DCYSUSV(i), 0x80);
+		awe_poke_dw(AWE_VTFT(i), 0);
+		awe_poke_dw(AWE_CVCF(i), 0);
+		awe_poke_dw(AWE_PTRX(i), 0x40000000);
+		awe_poke_dw(AWE_CPF(i), 0x40000000);
+		awe_poke_dw(AWE_PSST(i), 0);
+		awe_poke_dw(AWE_CSL(i), 0);
+		if (i & 1) /* DMA write */
+			awe_poke_dw(AWE_CCCA(i), 0x06000000);
+		else	   /* DMA read */
+			awe_poke_dw(AWE_CCCA(i), 0x04000000);
 	}
 }
 
@@ -3037,13 +3461,6 @@
 static void
 awe_write_dram(unsigned short c)
 {
-	int k;
-	/* wait until FULL bit in SMAxW register be false */
-	for (k = 0; k < 10000; k++) {
-		if (!(awe_peek_dw(AWE_SMALW) & 0x80000000))
-			break;
-		awe_wait(10);
-	}
 	awe_poke(AWE_SMLD, c);
 }
 
@@ -3051,27 +3468,34 @@
  * detect presence of AWE32 and check memory size
  *================================================================*/
 
+/* detect emu8000 chip on the specified address; from VV's guide */
+
+static int
+awe_detect_base(int addr)
+{
+	awe_base = addr;
+	if ((awe_peek(AWE_U1) & 0x000F) != 0x000C)
+		return 0;
+	if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058)
+		return 0;
+	if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003)
+		return 0;
+        DEBUG(0,printk("AWE32 found at %x\n", awe_base));
+	return 1;
+}
+	
 static int
 awe_detect(void)
 {
+	int base;
 #ifdef AWE_DEFAULT_BASE_ADDR
-	awe_base = AWE_DEFAULT_BASE_ADDR;
-	if (((awe_peek(AWE_U1) & 0x000F) == 0x000C) &&
-	    ((awe_peek(AWE_HWCF1) & 0x007E) == 0x0058) &&
-	    ((awe_peek(AWE_HWCF2) & 0x0003) == 0x0003))
+	if (awe_detect_base(AWE_DEFAULT_BASE_ADDR))
 		return 1;
 #endif
 	if (awe_base == 0) {
-		for (awe_base = 0x620; awe_base <= 0x680; awe_base += 0x20) {
-			if ((awe_peek(AWE_U1) & 0x000F) != 0x000C)
-				continue;
-			if ((awe_peek(AWE_HWCF1) & 0x007E) != 0x0058)
-				continue;
-			if ((awe_peek(AWE_HWCF2) & 0x0003) != 0x0003)
-			        continue;
-		        DEBUG(0,printk("AWE32 found at %x\n", awe_base));
-			return 1;
-		}
+		for (base = 0x620; base <= 0x680; base += 0x20)
+			if (awe_detect_base(base))
+				return 1;
 	}
 	FATALERR(printk("AWE32 not found\n"));
 	awe_base = 0;
@@ -3082,28 +3506,40 @@
 /*================================================================
  * check dram size on AWE board
  *================================================================*/
+
+/* any three numbers you like */
+#define UNIQUE_ID1	0x1234
+#define UNIQUE_ID2	0x4321
+#define UNIQUE_ID3	0xFFFF
+
 static int
 awe_check_dram(void)
 {
 	awe_open_dram_for_check();
 
-	awe_poke_dw(AWE_SMALW    , 0x00200000);     /* DRAM start address */
-	awe_poke(   AWE_SMLD     , 0x1234);
-	awe_poke(   AWE_SMLD     , 0x7777);
+	/* set up unique two id numbers */
+	awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET);
+	awe_poke(AWE_SMLD, UNIQUE_ID1);
+	awe_poke(AWE_SMLD, UNIQUE_ID2);
 
 	awe_mem_size = 0;
-	while (awe_mem_size < 28*1024) {     /* 28 MB is max onboard memory */
+	while (awe_mem_size < AWE_MAX_DRAM_SIZE) {
 		awe_wait(2);
-		awe_poke_dw(AWE_SMALR, 0x00200000); /* Address for reading */
-		awe_peek(AWE_SMLD);		/* Discard stale data  */
-		if (awe_peek(AWE_SMLD) != 0x1234)
+		/* read a data on the DRAM start address */
+		awe_poke_dw(AWE_SMALR, AWE_DRAM_OFFSET);
+		awe_peek(AWE_SMLD); /* discard stale data  */
+		if (awe_peek(AWE_SMLD) != UNIQUE_ID1)
 			break;
-		if (awe_peek(AWE_SMLD) != 0x7777)
+		if (awe_peek(AWE_SMLD) != UNIQUE_ID2)
 			break;
-		awe_mem_size += 32;
-		/* Address for writing */
-		awe_poke_dw(AWE_SMALW, 0x00200000+awe_mem_size*512L);
-		awe_poke(AWE_SMLD, 0xFFFF);
+		awe_mem_size += 32;  /* increment 32 Kbytes */
+		/* Write a unique data on the test address;
+		 * if the address is out of range, the data is written on
+		 * 0x200000(=AWE_DRAM_OFFSET).  Then the two id words are
+		 * broken by this data.
+		 */
+		awe_poke_dw(AWE_SMALW, AWE_DRAM_OFFSET + awe_mem_size*512L);
+		awe_poke(AWE_SMLD, UNIQUE_ID3);
 	}
 	awe_close_dram();
 
@@ -3119,113 +3555,107 @@
 
 
 /*================================================================
- * chorus and reverb controls
+ * chorus and reverb controls; from VV's guide
  *================================================================*/
 
-static unsigned short ChorusEffects[24] =
-{
-  0xE600,0x03F6,0xBC2C,0xE608,0x031A,0xBC6E,0xE610,0x031A,
-  0xBC84,0xE620,0x0269,0xBC6E,0xE680,0x04D3,0xBCA6,0xE6E0,
-  0x044E,0xBC37,0xE600,0x0B06,0xBC00,0xE6C0,0x0B06,0xBC00
+/* 5 parameters for each chorus mode; 3 x 16bit, 2 x 32bit */
+static unsigned short chorus_parm[8][5] = {
+	{0xE600, 0x03F6, 0xBC2C ,0x0000, 0x006D}, /* chorus 1 */
+	{0xE608, 0x031A, 0xBC6E, 0x0000, 0x017C}, /* chorus 2 */
+	{0xE610, 0x031A, 0xBC84, 0x0000, 0x0083}, /* chorus 3 */
+	{0xE620, 0x0269, 0xBC6E, 0x0000, 0x017C}, /* chorus 4 */
+	{0xE680, 0x04D3, 0xBCA6, 0x0000, 0x005B}, /* feedback */
+	{0xE6E0, 0x044E, 0xBC37, 0x0000, 0x0026}, /* flanger */
+	{0xE600, 0x0B06, 0xBC00, 0xE000, 0x0083}, /* short delay */
+	{0xE6C0, 0x0B06, 0xBC00, 0xE000, 0x0083}, /* short delay + feedback */
 };
 
-static unsigned long ChorusEffects2[] =
+static void awe_set_chorus_mode(int effect)
 {
-  0x0000 ,0x006D,0x8000,0x0000,0x0000 ,0x017C,0x8000,0x0000,
-  0x0000 ,0x0083,0x8000,0x0000,0x0000 ,0x017C,0x8000,0x0000,
-  0x0000 ,0x005B,0x8000,0x0000,0x0000 ,0x0026,0x8000,0x0000,
-  0x6E000,0x0083,0x8000,0x0000,0x6E000,0x0083,0x8000,0x0000
-};
+	awe_poke(AWE_INIT3(9), chorus_parm[effect][0]);
+	awe_poke(AWE_INIT3(12), chorus_parm[effect][1]);
+	awe_poke(AWE_INIT4(3), chorus_parm[effect][2]);
+	awe_poke_dw(AWE_HWCF4, (unsigned long)chorus_parm[effect][3]);
+	awe_poke_dw(AWE_HWCF5, (unsigned long)chorus_parm[effect][4]);
+	awe_poke_dw(AWE_HWCF6, 0x8000);
+	awe_poke_dw(AWE_HWCF7, 0x0000);
+}
 
-static unsigned short ChorusCommand[14] =
-{
-  0x69,0xA20,0x6C,0xA20,0x63,0xA22,0x29,
-  0xA20,0x2A,0xA20,0x2D,0xA20,0x2E,0xA20
-};
+/*----------------------------------------------------------------*/
 
-static unsigned short ReverbEffects[224] =
-{
-  /* Room 1 */
-  0xB488,0xA450,0x9550,0x84B5,0x383A,0x3EB5,0x72F4,
-  0x72A4,0x7254,0x7204,0x7204,0x7204,0x4416,0x4516,
-  0xA490,0xA590,0x842A,0x852A,0x842A,0x852A,0x8429,
-  0x8529,0x8429,0x8529,0x8428,0x8528,0x8428,0x8528,
-  /* Room 2 */
-  0xB488,0xA458,0x9558,0x84B5,0x383A,0x3EB5,0x7284,
-  0x7254,0x7224,0x7224,0x7254,0x7284,0x4448,0x4548,
-  0xA440,0xA540,0x842A,0x852A,0x842A,0x852A,0x8429,
-  0x8529,0x8429,0x8529,0x8428,0x8528,0x8428,0x8528,
-  /* Room 3 */
-  0xB488,0xA460,0x9560,0x84B5,0x383A,0x3EB5,0x7284,
-  0x7254,0x7224,0x7224,0x7254,0x7284,0x4416,0x4516,
-  0xA490,0xA590,0x842C,0x852C,0x842C,0x852C,0x842B,
-  0x852B,0x842B,0x852B,0x842A,0x852A,0x842A,0x852A,
-  /* Hall 1 */
-  0xB488,0xA470,0x9570,0x84B5,0x383A,0x3EB5,0x7284,
-  0x7254,0x7224,0x7224,0x7254,0x7284,0x4448,0x4548,
-  0xA440,0xA540,0x842B,0x852B,0x842B,0x852B,0x842A,
-  0x852A,0x842A,0x852A,0x8429,0x8529,0x8429,0x8529,
-  /* Hall 2 */
-  0xB488,0xA470,0x9570,0x84B5,0x383A,0x3EB5,0x7254,
-  0x7234,0x7224,0x7254,0x7264,0x7294,0x44C3,0x45C3,
-  0xA404,0xA504,0x842A,0x852A,0x842A,0x852A,0x8429,
-  0x8529,0x8429,0x8529,0x8428,0x8528,0x8428,0x8528,
-  /* Plate */
-  0xB4FF,0xA470,0x9570,0x84B5,0x383A,0x3EB5,0x7234,
-  0x7234,0x7234,0x7234,0x7234,0x7234,0x4448,0x4548,
-  0xA440,0xA540,0x842A,0x852A,0x842A,0x852A,0x8429,
-  0x8529,0x8429,0x8529,0x8428,0x8528,0x8428,0x8528,
-  /* Delay */
-  0xB4FF,0xA470,0x9500,0x84B5,0x333A,0x39B5,0x7204,
-  0x7204,0x7204,0x7204,0x7204,0x72F4,0x4400,0x4500,
-  0xA4FF,0xA5FF,0x8420,0x8520,0x8420,0x8520,0x8420,
-  0x8520,0x8420,0x8520,0x8420,0x8520,0x8420,0x8520,
-  /* Panning Delay */
-  0xB4FF,0xA490,0x9590,0x8474,0x333A,0x39B5,0x7204,
-  0x7204,0x7204,0x7204,0x7204,0x72F4,0x4400,0x4500,
-  0xA4FF,0xA5FF,0x8420,0x8520,0x8420,0x8520,0x8420,
-  0x8520,0x8420,0x8520,0x8420,0x8520,0x8420,0x8520
+/* reverb mode settings; write the following 28 data of 16 bit length
+ *   on the corresponding ports in the reverb_cmds array
+ */
+static unsigned short reverb_parm[8][28] = {
+{  /* room 1 */
+	0xB488, 0xA450, 0x9550, 0x84B5, 0x383A, 0x3EB5, 0x72F4,
+	0x72A4, 0x7254, 0x7204, 0x7204, 0x7204, 0x4416, 0x4516,
+	0xA490, 0xA590, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+},
+{  /* room 2 */
+	0xB488, 0xA458, 0x9558, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+},
+{  /* room 3 */
+	0xB488, 0xA460, 0x9560, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4416, 0x4516,
+	0xA490, 0xA590, 0x842C, 0x852C, 0x842C, 0x852C, 0x842B,
+	0x852B, 0x842B, 0x852B, 0x842A, 0x852A, 0x842A, 0x852A,
+},
+{  /* hall 1 */
+	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7284,
+	0x7254, 0x7224, 0x7224, 0x7254, 0x7284, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842B, 0x852B, 0x842B, 0x852B, 0x842A,
+	0x852A, 0x842A, 0x852A, 0x8429, 0x8529, 0x8429, 0x8529,
+},
+{  /* hall 2 */
+	0xB488, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7254,
+	0x7234, 0x7224, 0x7254, 0x7264, 0x7294, 0x44C3, 0x45C3,
+	0xA404, 0xA504, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+},
+{  /* plate */
+	0xB4FF, 0xA470, 0x9570, 0x84B5, 0x383A, 0x3EB5, 0x7234,
+	0x7234, 0x7234, 0x7234, 0x7234, 0x7234, 0x4448, 0x4548,
+	0xA440, 0xA540, 0x842A, 0x852A, 0x842A, 0x852A, 0x8429,
+	0x8529, 0x8429, 0x8529, 0x8428, 0x8528, 0x8428, 0x8528,
+},
+{  /* delay */
+	0xB4FF, 0xA470, 0x9500, 0x84B5, 0x333A, 0x39B5, 0x7204,
+	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+},
+{  /* panning delay */
+	0xB4FF, 0xA490, 0x9590, 0x8474, 0x333A, 0x39B5, 0x7204,
+	0x7204, 0x7204, 0x7204, 0x7204, 0x72F4, 0x4400, 0x4500,
+	0xA4FF, 0xA5FF, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420,
+	0x8520, 0x8420, 0x8520, 0x8420, 0x8520, 0x8420, 0x8520,
+},
 };
 
-static unsigned short ReverbCommand[56] =
-{
-  0x43,0xA20,0x45,0xA20,0x7F,0xA22,0x47,0xA20,
-  0x54,0xA22,0x56,0xA22,0x4F,0xA20,0x57,0xA20,
-  0x5F,0xA20,0x47,0xA22,0x4F,0xA22,0x57,0xA22,
-  0x5D,0xA22,0x5F,0xA22,0x61,0xA20,0x63,0xA20,
-  0x49,0xA20,0x4B,0xA20,0x51,0xA20,0x53,0xA20,
-  0x59,0xA20,0x5B,0xA20,0x41,0xA22,0x43,0xA22,
-  0x49,0xA22,0x4B,0xA22,0x51,0xA22,0x53,0xA22
+static struct ReverbCmdPair {
+	unsigned short cmd, port;
+} reverb_cmds[28] = {
+  {AWE_INIT1(0x03)}, {AWE_INIT1(0x05)}, {AWE_INIT4(0x1F)}, {AWE_INIT1(0x07)},
+  {AWE_INIT2(0x14)}, {AWE_INIT2(0x16)}, {AWE_INIT1(0x0F)}, {AWE_INIT1(0x17)},
+  {AWE_INIT1(0x1F)}, {AWE_INIT2(0x07)}, {AWE_INIT2(0x0F)}, {AWE_INIT2(0x17)},
+  {AWE_INIT2(0x1D)}, {AWE_INIT2(0x1F)}, {AWE_INIT3(0x01)}, {AWE_INIT3(0x03)},
+  {AWE_INIT1(0x09)}, {AWE_INIT1(0x0B)}, {AWE_INIT1(0x11)}, {AWE_INIT1(0x13)},
+  {AWE_INIT1(0x19)}, {AWE_INIT1(0x1B)}, {AWE_INIT2(0x01)}, {AWE_INIT2(0x03)},
+  {AWE_INIT2(0x09)}, {AWE_INIT2(0x0B)}, {AWE_INIT2(0x11)}, {AWE_INIT2(0x13)},
 };
 
-static void awe_set_chorus_mode(int effect)
-{
-	int k;
-	DECL_INTR_FLAGS(flags);
-
-	DISABLE_INTR(flags);
-	for (k = 0; k < 3; k++)
-		awe_poke(ChorusCommand[k*2],
-			 ChorusCommand[k*2+1],
-			 ChorusEffects[k+effect*3]);
-	for (k = 0; k < 4; k++)
-		awe_poke_dw(ChorusCommand[6+k*2],
-			    ChorusCommand[6+k*2+1],
-			    ChorusEffects2[k+effect*4]);
-	RESTORE_INTR(flags);
-}
 
 static void awe_set_reverb_mode(int effect)
 {
-	int k;
-	DECL_INTR_FLAGS(flags);
-
-	DISABLE_INTR(flags);
-	for (k = 0; k < 28; k++)
-		awe_poke(ReverbCommand[k*2],
-			 ReverbCommand[k*2+1],
-			 ReverbEffects[k+effect*28]);
-	RESTORE_INTR(flags);
+	int i;
+	for (i = 0; i < 28; i++)
+		awe_poke(reverb_cmds[i].cmd, reverb_cmds[i].port,
+			 reverb_parm[effect][i]);
 }
 
 #endif /* CONFIG_AWE32_SYNTH */
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/lowlevel/init.c linux/drivers/sound/lowlevel/init.c
--- v2.1.27/linux/drivers/sound/lowlevel/init.c	Fri Oct 25 03:06:38 1996
+++ linux/drivers/sound/lowlevel/init.c	Wed Jan  8 03:44:55 1997
@@ -16,6 +16,8 @@
 extern void unload_aci(void);
 extern int attach_awe(void);
 extern void unload_awe(void);
+extern int init_aedsp16(void);
+extern void uninit_aedsp16(void);
 
 /*
  * There are two places where you can insert initialization calls of
@@ -26,6 +28,9 @@
 void
 sound_preinit_lowlevel_drivers(void)
 {
+#ifdef CONFIG_AEDSP16
+   init_aedsp16();
+#endif
 }
 
 void
@@ -50,5 +55,10 @@
 #ifdef CONFIG_AWE32_SYNTH
    unload_awe();
 #endif
+
+#ifdef CONFIG_AEDSP16
+   uninit_aedsp16();
+#endif
+
 }
 #endif
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/mad16.c linux/drivers/sound/mad16.c
--- v2.1.27/linux/drivers/sound/mad16.c	Fri Nov 15 00:15:23 1996
+++ linux/drivers/sound/mad16.c	Wed Feb 26 02:35:17 1997
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/maui.c linux/drivers/sound/maui.c
--- v2.1.27/linux/drivers/sound/maui.c	Fri Nov 15 00:15:23 1996
+++ linux/drivers/sound/maui.c	Wed Feb 26 02:35:18 1997
@@ -4,7 +4,7 @@
  * The low level driver for Turtle Beach Maui and Tropez.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -127,7 +127,7 @@
   return 0;
 }
 
-void
+static void
 mauiintr (int irq, void *dev_id, struct pt_regs *dummy)
 {
   irq_ok = 1;
@@ -286,7 +286,7 @@
   return 0;
 }
 
-int
+static int
 maui_load_patch (int dev, int format, const char *addr,
 		 int offs, int count, int pmgr_flag)
 {
@@ -459,6 +459,7 @@
        */
 
       synth = midi_devs[this_dev]->converter;
+      synth->id = "MAUI";
 
       if (synth != NULL)
 	{
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/midi_ctrl.h linux/drivers/sound/midi_ctrl.h
--- v2.1.27/linux/drivers/sound/midi_ctrl.h	Fri Nov 15 00:14:53 1996
+++ linux/drivers/sound/midi_ctrl.h	Wed Feb 26 02:34:47 1997
@@ -1,7 +1,7 @@
 static unsigned char ctrl_def_values[128] =
 {
-	0x40,0x40,0x40,0x40,  0x40,0x40,0x40,0x40,	/*   0 to   7 */
-	0x40,0x40,0x40,0x40,  0x40,0x40,0x40,0x40,	/*   8 to  15 */
+	0x40,0x00,0x40,0x40,  0x40,0x40,0x40,0x7f,	/*   0 to   7 */
+	0x40,0x40,0x40,0x7f,  0x40,0x40,0x40,0x40,	/*   8 to  15 */
 	0x40,0x40,0x40,0x40,  0x40,0x40,0x40,0x40,	/*  16 to  23 */
 	0x40,0x40,0x40,0x40,  0x40,0x40,0x40,0x40,	/*  24 to  31 */
 
@@ -15,7 +15,7 @@
 	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  80 to  87 */
 	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  88 to  95 */
 
-	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/*  96 to 103 */
+	0x00,0x00,0x7f,0x7f,  0x7f,0x7f,0x00,0x00,	/*  96 to 103 */
 	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/* 104 to 111 */
 	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/* 112 to 119 */
 	0x00,0x00,0x00,0x00,  0x00,0x00,0x00,0x00,	/* 120 to 127 */
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/midi_synth.c linux/drivers/sound/midi_synth.c
--- v2.1.27/linux/drivers/sound/midi_synth.c	Fri Nov 15 00:15:24 1996
+++ linux/drivers/sound/midi_synth.c	Wed Feb 26 02:35:18 1997
@@ -4,7 +4,7 @@
  * High level midi sequencer manager for dumb MIDI interfaces.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -275,10 +275,7 @@
     {
 
     case SNDCTL_SYNTH_INFO:
-      {
-	char           *fixit = (char *) synth_devs[dev]->info;
-	copy_to_user (&((char *) arg)[0], fixit, sizeof (struct synth_info));
-      };
+      memcpy ((&((char *) arg)[0]), (char *) synth_devs[dev]->info, sizeof (struct synth_info));
 
       return 0;
       break;
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/midi_synth.h linux/drivers/sound/midi_synth.h
--- v2.1.27/linux/drivers/sound/midi_synth.h	Fri Nov 15 00:14:54 1996
+++ linux/drivers/sound/midi_synth.h	Wed Feb 26 02:34:47 1997
@@ -22,6 +22,7 @@
 
 static struct synth_operations std_midi_synth =
 {
+  "MIDI",
   &std_synth_info,
   0,
   SYNTH_TYPE_MIDI,
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/midibuf.c linux/drivers/sound/midibuf.c
--- v2.1.27/linux/drivers/sound/midibuf.c	Tue Jan 28 00:02:45 1997
+++ linux/drivers/sound/midibuf.c	Wed Feb 26 02:35:19 1997
@@ -4,14 +4,13 @@
  * Device file manager for /dev/midi#
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
 #include <linux/config.h>
-#include <linux/poll.h>
 
 
 #include "sound_config.h"
@@ -84,7 +83,7 @@
 	  restore_flags(flags); \
 	}
 
-void
+static void
 drain_midi_queue (int dev)
 {
 
@@ -484,13 +483,13 @@
       {
 
       case SNDCTL_MIDI_PRETIME:
-	get_user (val, (int *) arg);
+	val = *(int *) arg;
 	if (val < 0)
 	  val = 0;
 
 	val = (HZ * val) / 10;
 	parms[dev].prech_timeout = val;
-	return ioctl_out (arg, val);
+	return (*(int *) arg = val);
 	break;
 
       default:
@@ -498,21 +497,40 @@
       }
 }
 
-unsigned int
-MIDIbuf_poll (kdev_t dev, struct fileinfo *file, poll_table * wait)
+int
+MIDIbuf_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
 {
-  unsigned int mask = 0;
+  dev = dev >> 4;
+
+  switch (sel_type)
+    {
+    case SEL_IN:
+      if (!DATA_AVAIL (midi_in_buf[dev]))
+	{
+
+	  input_sleep_flag[dev].opts = WK_SLEEP;
+	  poll_wait (&input_sleeper[dev], wait);
+	  return 0;
+	}
+      return 1;
+      break;
+
+    case SEL_OUT:
+      if (SPACE_AVAIL (midi_out_buf[dev]))
+	{
+
+	  midi_sleep_flag[dev].opts = WK_SLEEP;
+	  poll_wait (&midi_sleeper[dev], wait);
+	  return 0;
+	}
+      return 1;
+      break;
+
+    case SEL_EX:
+      return 0;
+    }
 
-  input_sleep_flag[dev].opts = WK_SLEEP;
-  poll_wait (&input_sleeper[dev], wait);
-  midi_sleep_flag[dev].opts = WK_SLEEP;
-  poll_wait (&midi_sleeper[dev], wait);
-
-  if (DATA_AVAIL (midi_in_buf[dev]))
-    mask |= POLLIN | POLLRDNORM;
-  if (!SPACE_AVAIL (midi_out_buf[dev]))
-    mask |= POLLOUT | POLLWRNORM;
-  return mask;
+  return 0;
 }
 
 
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/mpu401.c linux/drivers/sound/mpu401.c
--- v2.1.27/linux/drivers/sound/mpu401.c	Fri Nov 15 00:15:27 1996
+++ linux/drivers/sound/mpu401.c	Wed Feb 26 02:35:21 1997
@@ -4,7 +4,7 @@
  * The low level driver for Roland MPU-401 compatible Midi cards.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -785,7 +785,7 @@
 	  printk ("MPU-401: Intelligent mode not supported by the HW\n");
 	  return -EINVAL;
 	}
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
       set_uart_mode (dev, devc, !val);
       return 0;
       break;
@@ -795,16 +795,12 @@
 	int             ret;
 	mpu_command_rec rec;
 
-	copy_from_user ((char *) &rec, &((char *) arg)[0], sizeof (rec));
+	memcpy ((char *) &rec, (&((char *) arg)[0]), sizeof (rec));
 
 	if ((ret = mpu401_command (dev, &rec)) < 0)
 	  return ret;
 
-	{
-	  char           *fixit = (char *) &rec;
-
-	  copy_to_user (&((char *) arg)[0], fixit, sizeof (rec));
-	};
+	memcpy ((&((char *) arg)[0]), (char *) &rec, sizeof (rec));
 	return 0;
       }
       break;
@@ -845,10 +841,7 @@
     {
 
     case SNDCTL_SYNTH_INFO:
-      {
-	char           *fixit = (char *) &mpu_synth_info[midi_dev];
-	copy_to_user (&((char *) arg)[0], fixit, sizeof (struct synth_info));
-      };
+      memcpy ((&((char *) arg)[0]), (char *) &mpu_synth_info[midi_dev], sizeof (struct synth_info));
 
       return 0;
       break;
@@ -954,6 +947,7 @@
 
 static struct synth_operations mpu401_synth_proto =
 {
+  "MPU401",
   NULL,
   0,
   SYNTH_TYPE_MIDI,
@@ -1104,6 +1098,7 @@
 
 
   mpu401_synth_operations[num_midis] = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct synth_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
@@ -1594,7 +1589,7 @@
       {
 	int             parm;
 
-	get_user (parm, (int *) arg);
+	parm = *(int *) arg;
 	parm &= timer_caps;
 
 	if (parm != 0)
@@ -1607,7 +1602,7 @@
 	      mpu_cmd (midi_dev, 0x3d, 0);	/* Use SMPTE sync */
 	  }
 
-	return ioctl_out (arg, timer_mode);
+	return (*(int *) arg = timer_mode);
       }
       break;
 
@@ -1635,11 +1630,11 @@
       {
 	int             val;
 
-	get_user (val, (int *) arg);
+	val = *(int *) arg;
 	if (val)
 	  set_timebase (midi_dev, val);
 
-	return ioctl_out (arg, curr_timebase);
+	return (*(int *) arg = curr_timebase);
       }
       break;
 
@@ -1648,7 +1643,7 @@
 	int             val;
 	int             ret;
 
-	get_user (val, (int *) arg);
+	val = *(int *) arg;
 
 	if (val)
 	  {
@@ -1665,7 +1660,7 @@
 	    curr_tempo = val;
 	  }
 
-	return ioctl_out (arg, curr_tempo);
+	return (*(int *) arg = curr_tempo);
       }
       break;
 
@@ -1673,20 +1668,20 @@
       {
 	int             val;
 
-	get_user (val, (int *) arg);
+	val = *(int *) arg;
 	if (val != 0)		/* Can't change */
 	  return -EINVAL;
 
-	return ioctl_out (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+	return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
       }
       break;
 
     case SNDCTL_SEQ_GETTIME:
-      return ioctl_out (arg, curr_ticks);
+      return (*(int *) arg = curr_ticks);
       break;
 
     case SNDCTL_TMR_METRONOME:
-      get_user (metronome_mode, (int *) arg);
+      metronome_mode = *(int *) arg;
       setup_metronome (midi_dev);
       return 0;
       break;
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/opl3.c linux/drivers/sound/opl3.c
--- v2.1.27/linux/drivers/sound/opl3.c	Fri Nov 15 00:15:28 1996
+++ linux/drivers/sound/opl3.c	Wed Feb 26 02:35:22 1997
@@ -4,7 +4,7 @@
  * A low level driver for Yamaha YM3812 and OPL-3 -chips
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -68,8 +68,6 @@
 
 static struct opl_devinfo *devc = NULL;
 
-static int      force_opl3_mode = 0;
-
 static int      detected_model;
 
 static int      store_instr (int instr_no, struct sbi_instrument *instr);
@@ -77,12 +75,6 @@
 static void     opl3_command (int io_addr, unsigned int addr, unsigned int val);
 static int      opl3_kill_note (int dev, int voice, int note, int velocity);
 
-void
-enable_opl3_mode (int left, int right, int both)
-{
-  force_opl3_mode = 1;
-}
-
 static void
 enter_4op_mode (void)
 {
@@ -119,7 +111,7 @@
       {
 	struct sbi_instrument ins;
 
-	copy_from_user ((char *) &ins, &((char *) arg)[0], sizeof (ins));
+	memcpy ((char *) &ins, (&((char *) arg)[0]), sizeof (ins));
 
 	if (ins.channel < 0 || ins.channel >= SBFM_MAXINSTR)
 	  {
@@ -134,11 +126,7 @@
     case SNDCTL_SYNTH_INFO:
       devc->fm_info.nr_voices = (devc->nr_voice == 12) ? 6 : devc->nr_voice;
 
-      {
-	char           *fixit = (char *) &devc->fm_info;
-
-	copy_to_user (&((char *) arg)[0], fixit, sizeof (devc->fm_info));
-      };
+      memcpy ((&((char *) arg)[0]), (char *) &devc->fm_info, sizeof (devc->fm_info));
       return 0;
       break;
 
@@ -180,6 +168,7 @@
 
 
   devc = (struct opl_devinfo *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (*devc)));
+  sound_mem_sizes[sound_nblocks] = sizeof (*devc);
   if (sound_nblocks < 1024)
     sound_nblocks++;;
 
@@ -200,17 +189,18 @@
 
   signature = stat1 = inb (ioaddr);	/* Status register */
 
-  if (signature != 0x00 && signature != 0x06 && signature != 0x02)
+  if (signature != 0x00 && signature != 0x06 && signature != 0x02 &&
+      signature != 0x0f)
     {
       DDB (printk ("OPL3 not detected %x\n", signature));
       return 0;
     }
 
-  if (signature == 0x06 && !force_opl3_mode)	/* OPL2 */
+  if (signature == 0x06)	/* OPL2 */
     {
       detected_model = 2;
     }
-  else if (signature == 0x00)	/* OPL3 or OPL4 */
+  else if (signature == 0x00 || signature == 0x0f)	/* OPL3 or OPL4 */
     {
       unsigned char   tmp;
 
@@ -341,7 +331,7 @@
  * volume -8 it was implemented as a table because it is only 128 bytes and
  * it saves a lot of log() calculations. (RH)
  */
-char            fm_volume_table[128] =
+static char     fm_volume_table[128] =
 {-64, -48, -40, -35, -32, -29, -27, -26,
  -24, -23, -21, -20, -19, -18, -18, -17,
  -16, -15, -15, -14, -13, -13, -12, -12,
@@ -664,7 +654,7 @@
    * have to calculate the bending now.
    */
 
-  freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range);
+  freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
   devc->voc[voice].current_freq = freq;
 
   freq_to_fnum (freq, &block, &fnum);
@@ -741,7 +731,7 @@
 
   outb (((unsigned char) (addr & 0xff)), io_addr);
 
-  if (!devc->model != 2)
+  if (devc->model != 2)
     tenmicrosec (devc->osp);
   else
     for (i = 0; i < 2; i++)
@@ -973,7 +963,7 @@
 				 * Not keyed on
 				 */
 
-  freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range);
+  freq = compute_finetune (devc->voc[voice].orig_freq, devc->voc[voice].bender, devc->voc[voice].bender_range, 0);
   devc->voc[voice].current_freq = freq;
 
   freq_to_fnum (freq, &block, &fnum);
@@ -1099,7 +1089,8 @@
   opl3_set_instr (dev, voice,
 		  info->pgm_num);
 
-  devc->voc[voice].bender = info->bender_value;
+  devc->voc[voice].bender = 0;
+  devc->voc[voice].bender_range = info->bender_range;
   devc->voc[voice].volume =
     info->controllers[CTL_MAIN_VOLUME];
   devc->voc[voice].panning = (info->controllers[CTL_PAN] * 2) - 128;
@@ -1107,6 +1098,7 @@
 
 static struct synth_operations opl3_operations =
 {
+  "OPL",
   NULL,
   0,
   SYNTH_TYPE_FM,
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/opl3.h linux/drivers/sound/opl3.h
--- v2.1.27/linux/drivers/sound/opl3.h	Fri Nov 15 00:14:54 1996
+++ linux/drivers/sound/opl3.h	Wed Feb 26 02:34:47 1997
@@ -2,7 +2,7 @@
  *	opl3.h	- Definitions of the OPL-3 registers
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/os.h linux/drivers/sound/os.h
--- v2.1.27/linux/drivers/sound/os.h	Fri Nov 15 00:14:57 1996
+++ linux/drivers/sound/os.h	Wed Feb 26 02:34:51 1997
@@ -1,4 +1,8 @@
 
+#ifdef __alpha__
+#else
+#endif
+
 #define ALLOW_SELECT
 #undef NO_INLINE_ASM
 #define SHORT_BANNERS
@@ -16,36 +20,36 @@
 #define LINUX21X
 #endif
 
-#include <linux/param.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/fcntl.h>
-#include <linux/sched.h>
-#include <linux/ctype.h>
-#include <asm/io.h>
-#include <asm/segment.h>
-#include <asm/system.h>
+#include <linux/utsname.h>
+#include <linux/string.h>
+#include <linux/fs.h>
 #include <asm/dma.h>
-#include <linux/wait.h>
+#include <asm/param.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
 #include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#ifdef __alpha__
+#include <asm/segment.h>
+#endif
 #include <linux/vmalloc.h>
 #include <asm/uaccess.h>
-#include <linux/string.h>
-#include <linux/ioport.h>
-#include <linux/utsname.h>
+#include <linux/poll.h>
 
 #include <linux/wrapper.h>
 
 #include <linux/soundcard.h>
 
+
 #define FALSE	0
 #define TRUE	1
 
 
 
 struct snd_wait {
-	  int opts;
+	  volatile int opts;
 	};
 
 extern int sound_alloc_dma(int chn, char *deviceID);
@@ -54,9 +58,13 @@
 extern void sound_close_dma(int chn);
 
 #define RUNTIME_DMA_ALLOC
+#define USE_AUTOINIT_DMA
 
 extern caddr_t sound_mem_blocks[1024];
+extern int sound_mem_sizes[1024];
 extern int sound_nblocks;
+
+
 
 #undef PSEUDO_DMA_AUTOINIT
 #define ALLOW_BUFFER_MAPPING
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/pas2_card.c linux/drivers/sound/pas2_card.c
--- v2.1.27/linux/drivers/sound/pas2_card.c	Fri Nov 15 00:15:29 1996
+++ linux/drivers/sound/pas2_card.c	Wed Feb 26 02:35:23 1997
@@ -1,4 +1,3 @@
-#define _PAS2_CARD_C_
 /*
  * sound/pas2_card.c
  *
@@ -55,7 +54,7 @@
 
 /******************* Begin of the Interrupt Handler ********************/
 
-void
+static void
 pasintr (int irq, void *dev_id, struct pt_regs *dummy)
 {
   int             status;
@@ -107,7 +106,7 @@
 
 /******************* Begin of the Initialization Code ******************/
 
-int
+static int
 config_pas_hw (struct address_info *hw_config)
 {
   char            ok = 1;
@@ -116,26 +115,11 @@
   pas_irq = hw_config->irq;
 
   pas_write (0x00, 0x0B8B);
-
-  pas_write (0x36, 0x138B);	/*
-				   * Local timer control *
-				   * register
-				 */
-
-  pas_write (0x36, 0x1388);	/*
-				   * Sample rate timer (16 bit)
-				 */
+  pas_write (0x36, 0x138B);
+  pas_write (0x36, 0x1388);
   pas_write (0, 0x1388);
-
-  pas_write (0x74, 0x138B);	/*
-				   * Local timer control *
-				   * register
-				 */
-
-  pas_write (0x74, 0x1389);	/*
-				   * Sample count register (16
-				   * * bit)
-				 */
+  pas_write (0x74, 0x138B);
+  pas_write (0x74, 0x1389);
   pas_write (0, 0x1389);
 
   pas_write (0x80 | 0x40 | 0x20 | 1, 0x0B8A);
@@ -153,7 +137,7 @@
 
   if (pas_irq < 0 || pas_irq > 15)
     {
-      printk ("PAS2: Invalid IRQ %d", pas_irq);
+      printk ("PAS16: Invalid IRQ %d", pas_irq);
       ok = 0;
     }
   else
@@ -163,7 +147,7 @@
       pas_write (int_ptrs, 0xF38A);
       if (!irq_bits[pas_irq])
 	{
-	  printk ("PAS2: Invalid IRQ %d", pas_irq);
+	  printk ("PAS16: Invalid IRQ %d", pas_irq);
 	  ok = 0;
 	}
       else
@@ -175,7 +159,7 @@
 
   if (hw_config->dma < 0 || hw_config->dma > 7)
     {
-      printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
+      printk ("PAS16: Invalid DMA selection %d", hw_config->dma);
       ok = 0;
     }
   else
@@ -183,7 +167,7 @@
       pas_write (dma_bits[hw_config->dma], 0xF389);
       if (!dma_bits[hw_config->dma])
 	{
-	  printk ("PAS2: Invalid DMA selection %d", hw_config->dma);
+	  printk ("PAS16: Invalid DMA selection %d", hw_config->dma);
 	  ok = 0;
 	}
       else
@@ -268,7 +252,7 @@
   return ok;
 }
 
-int
+static int
 detect_pas_hw (struct address_info *hw_config)
 {
   unsigned char   board_id, foo;
@@ -291,8 +275,8 @@
     return 0;
 
   /*
-   * We probably have a PAS-series board, now check for a PAS2-series board
-   * by trying to change the board revision bits. PAS2-series hardware won't
+   * We probably have a PAS-series board, now check for a PAS16-series board
+   * by trying to change the board revision bits. PAS16-series hardware won't
    * let you do this - the bits are read-only.
    */
 
@@ -302,9 +286,7 @@
   foo = inb (0x0B8B);
   pas_write (board_id, 0x0B8B);
 
-  if (board_id != foo)		/*
-				 * Not a PAS2
-				 */
+  if (board_id != foo)
     return 0;
 
   pas_model = pas_read (0xFF88);
@@ -332,7 +314,6 @@
 
       if (config_pas_hw (hw_config))
 	{
-
 #ifdef CONFIG_AUDIO
 	  pas_pcm_init (hw_config);
 #endif
@@ -342,15 +323,12 @@
 	  sb_dsp_disable_midi (pas_sb_base);	/* No MIDI capability */
 #endif
 
-
 #ifdef CONFIG_MIDI
 	  pas_midi_init ();
 #endif
-
 	  pas_init_mixer ();
 	}
     }
-
 }
 
 int
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/pas2_midi.c linux/drivers/sound/pas2_midi.c
--- v2.1.27/linux/drivers/sound/pas2_midi.c	Fri Nov 15 00:15:29 1996
+++ linux/drivers/sound/pas2_midi.c	Wed Feb 26 02:35:23 1997
@@ -4,7 +4,7 @@
  * The low level driver for the PAS Midi Interface.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -39,7 +39,7 @@
 
   if (midi_busy)
     {
-      printk ("PAS2: Midi busy\n");
+      printk ("PAS16: Midi busy\n");
       return -EBUSY;
     }
 
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/pas2_mixer.c linux/drivers/sound/pas2_mixer.c
--- v2.1.27/linux/drivers/sound/pas2_mixer.c	Fri Nov 15 00:15:30 1996
+++ linux/drivers/sound/pas2_mixer.c	Wed Feb 26 02:35:24 1997
@@ -1,5 +1,3 @@
-#define _PAS2_MIXER_C_
-
 /*
  * sound/pas2_mixer.c
  *
@@ -7,7 +5,7 @@
  */
 
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -85,17 +83,13 @@
 
 
   if (bits & 0x10)
-    {				/*
-				 * Select input or output mixer
-				 */
+    {
       left |= mixer;
       right |= mixer;
     }
 
   if (bits == 0x03 || bits == 0x04)
-    {				/*
-				 * Bass and treble are mono devices
-				 */
+    {
       mix_write (0x80 | bits, 0x078B);
       mix_write (left, 0x078B);
       right_vol = left_vol;
@@ -111,7 +105,7 @@
   return (left_vol | (right_vol << 8));
 }
 
-void
+static void
 set_mode (int new_mode)
 {
   mix_write (0x80 | 0x05, 0x078B);
@@ -219,7 +213,7 @@
   set_mode (0x04 | 0x01);
 }
 
-int
+static int
 pas_mixer_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
   DEB (printk ("pas2_mixer.c: int pas_mixer_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
@@ -228,14 +222,14 @@
     {
       int             level;
 
-      get_user (level, (int *) arg);
+      level = *(int *) arg;
 
       if (level == -1)		/* Return current settings */
 	{
 	  if (mode_control & 0x04)
-	    return ioctl_out (arg, 1);
+	    return (*(int *) arg = 1);
 	  else
-	    return ioctl_out (arg, 0);
+	    return (*(int *) arg = 0);
 	}
       else
 	{
@@ -243,7 +237,7 @@
 	  if (level)
 	    mode_control |= 0x04;
 	  set_mode (mode_control);
-	  return ioctl_out (arg, !!level);	/* 0 or 1 */
+	  return (*(int *) arg = !!level);	/* 0 or 1 */
 	}
     }
 
@@ -252,13 +246,13 @@
     {
       int             level;
 
-      get_user (level, (int *) arg);
+      level = *(int *) arg;
 
       if (level == -1)		/* Return current settings */
 	{
 	  if (!(mode_control & 0x03))
-	    return ioctl_out (arg, 0);
-	  return ioctl_out (arg, ((mode_control & 0x03) + 1) * 20);
+	    return (*(int *) arg = 0);
+	  return (*(int *) arg = ((mode_control & 0x03) + 1) * 20);
 	}
       else
 	{
@@ -283,11 +277,11 @@
     {
       int             level;
 
-      get_user (level, (int *) arg);
+      level = *(int *) arg;
 
       if (level == -1)		/* Return current settings */
 	{
-	  return ioctl_out (arg, !(pas_read (0x0B8A) & 0x20));
+	  return (*(int *) arg = !(pas_read (0x0B8A) & 0x20));
 	}
       else
 	{
@@ -306,41 +300,39 @@
     {
       int             v;
 
-      get_user (v, (int *) arg);
+      v = *(int *) arg;
 
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
-	return ioctl_out (arg, pas_mixer_set (cmd & 0xff, v));
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
+	return (*(int *) arg = pas_mixer_set (cmd & 0xff, v));
       else
-	{			/*
-				 * Read parameters
-				 */
+	{
 
 	  switch (cmd & 0xff)
 	    {
 
 	    case SOUND_MIXER_RECSRC:
-	      return ioctl_out (arg, rec_devices);
+	      return (*(int *) arg = rec_devices);
 	      break;
 
 	    case SOUND_MIXER_STEREODEVS:
-	      return ioctl_out (arg, SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
+	      return (*(int *) arg = SUPPORTED_MIXER_DEVICES & ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE));
 	      break;
 
 	    case SOUND_MIXER_DEVMASK:
-	      return ioctl_out (arg, SUPPORTED_MIXER_DEVICES);
+	      return (*(int *) arg = SUPPORTED_MIXER_DEVICES);
 	      break;
 
 	    case SOUND_MIXER_RECMASK:
-	      return ioctl_out (arg, POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
+	      return (*(int *) arg = POSSIBLE_RECORDING_DEVICES & SUPPORTED_MIXER_DEVICES);
 	      break;
 
 	    case SOUND_MIXER_CAPS:
-	      return ioctl_out (arg, 0);	/* No special capabilities */
+	      return (*(int *) arg = 0);	/* No special capabilities */
 	      break;
 
 
 	    default:
-	      return ioctl_out (arg, levels[cmd & 0xff]);
+	      return (*(int *) arg = levels[cmd & 0xff]);
 	    }
 	}
     }
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/pas2_pcm.c linux/drivers/sound/pas2_pcm.c
--- v2.1.27/linux/drivers/sound/pas2_pcm.c	Fri Nov 15 00:15:30 1996
+++ linux/drivers/sound/pas2_pcm.c	Wed Feb 26 02:35:24 1997
@@ -1,11 +1,15 @@
-#define _PAS2_PCM_C_
 /*
- * sound/pas2_pcm.c
+ * pas2_pcm.c Audio routines for PAS16
+ */
+/*
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
- * The low level driver for the Pro Audio Spectrum ADC/DAC.
+ * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
+ * Version 2 (June 1991). See the "COPYING" file distributed with this software
+ * for more info.
  */
-
 #include <linux/config.h>
+
 #include "sound_config.h"
 
 #if defined(CONFIG_PAS) && defined(CONFIG_AUDIO)
@@ -34,7 +38,7 @@
 int             pas_audiodev = 0;
 static int      open_mode = 0;
 
-int
+static int
 pcm_set_speed (int arg)
 {
   int             foo, tmp;
@@ -106,7 +110,7 @@
   return pcm_speed;
 }
 
-int
+static int
 pcm_set_channels (int arg)
 {
 
@@ -118,15 +122,13 @@
       pas_write (pas_read (0xF8A) ^ 0x20, 0xF8A);
 
       pcm_channels = arg;
-      pcm_set_speed (pcm_speed);	/*
-					   * The speed must be reinitialized
-					 */
+      pcm_set_speed (pcm_speed);	/* The speed must be reinitialized */
     }
 
   return pcm_channels;
 }
 
-int
+static int
 pcm_set_bits (int arg)
 {
   if (arg == 0)
@@ -146,7 +148,7 @@
 }
 
 static int
-pas_audio_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
+pas_audio_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
   int             val;
 
@@ -155,62 +157,35 @@
   switch (cmd)
     {
     case SOUND_PCM_WRITE_RATE:
-      if (local)
-	return pcm_set_speed ((int) arg);
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, pcm_set_speed (val));
+      val = *(int *) arg;
+      return (*(int *) arg = pcm_set_speed (val));
       break;
 
     case SOUND_PCM_READ_RATE:
-      if (local)
-	return pcm_speed;
-      return ioctl_out (arg, pcm_speed);
+      return (*(int *) arg = pcm_speed);
       break;
 
     case SNDCTL_DSP_STEREO:
-      if (local)
-	return pcm_set_channels ((int) arg + 1) - 1;
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, pcm_set_channels (val + 1) - 1);
+      val = *(int *) arg;
+      return (*(int *) arg = pcm_set_channels (val + 1) - 1);
       break;
 
     case SOUND_PCM_WRITE_CHANNELS:
-      if (local)
-	return pcm_set_channels ((int) arg);
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, pcm_set_channels (val));
+      val = *(int *) arg;
+      return (*(int *) arg = pcm_set_channels (val));
       break;
 
     case SOUND_PCM_READ_CHANNELS:
-      if (local)
-	return pcm_channels;
-      return ioctl_out (arg, pcm_channels);
+      return (*(int *) arg = pcm_channels);
       break;
 
     case SNDCTL_DSP_SETFMT:
-      if (local)
-	return pcm_set_bits ((int) arg);
-      get_user (val, (int *) arg);
-      return ioctl_out (arg, pcm_set_bits (val));
+      val = *(int *) arg;
+      return (*(int *) arg = pcm_set_bits (val));
       break;
 
     case SOUND_PCM_READ_BITS:
-      if (local)
-	return pcm_bits;
-      return ioctl_out (arg, pcm_bits);
-
-    case SOUND_PCM_WRITE_FILTER:	/*
-					 * NOT YET IMPLEMENTED
-					 */
-      get_user (val, (int *) arg);
-      if (val > 1)
-	return -EINVAL;
-      pcm_filter = val;
-      break;
-
-    case SOUND_PCM_READ_FILTER:
-      return ioctl_out (arg, pcm_filter);
-      break;
+      return (*(int *) arg = pcm_bits);
 
     default:
       return -EINVAL;
@@ -224,7 +199,7 @@
 {
   DEB (printk ("pas2_pcm.c: static void pas_audio_reset(void)\n"));
 
-  pas_write (pas_read (0xF8A) & ~0x40, 0xF8A);
+  pas_write (pas_read (0xF8A) & ~0x40, 0xF8A);	/* Disable PCM */
 }
 
 static int
@@ -276,22 +251,20 @@
 
 static void
 pas_audio_output_block (int dev, unsigned long buf, int count,
-			int intrflag, int restart_dma)
+			int intrflag)
 {
   unsigned long   flags, cnt;
 
   DEB (printk ("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));
 
   cnt = count;
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     cnt >>= 1;
 
   if (audio_devs[dev]->flags & DMA_AUTOMODE &&
       intrflag &&
       cnt == pcm_count)
-    return;			/*
-				 * Auto mode on. No need to react
-				 */
+    return;
 
   save_flags (flags);
   cli ();
@@ -299,10 +272,9 @@
   pas_write (pas_read (0xF8A) & ~0x40,
 	     0xF8A);
 
-  if (restart_dma)
-    DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
 
   if (count != pcm_count)
@@ -327,7 +299,7 @@
 
 static void
 pas_audio_start_input (int dev, unsigned long buf, int count,
-		       int intrflag, int restart_dma)
+		       int intrflag)
 {
   unsigned long   flags;
   int             cnt;
@@ -335,23 +307,20 @@
   DEB (printk ("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));
 
   cnt = count;
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     cnt >>= 1;
 
   if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
       intrflag &&
       cnt == pcm_count)
-    return;			/*
-				 * Auto mode on. No need to react
-				 */
+    return;
 
   save_flags (flags);
   cli ();
 
-  if (restart_dma)
-    DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
 
   if (count != pcm_count)
@@ -398,12 +367,14 @@
 static int
 pas_audio_prepare_for_input (int dev, int bsize, int bcount)
 {
+  pas_audio_reset (dev);
   return 0;
 }
 
 static int
 pas_audio_prepare_for_output (int dev, int bsize, int bcount)
 {
+  pas_audio_reset (dev);
   return 0;
 }
 
@@ -417,7 +388,6 @@
   pas_audio_prepare_for_input,
   pas_audio_prepare_for_output,
   pas_audio_reset,
-  pas_audio_reset,
   NULL,
   NULL,
   NULL,
@@ -425,15 +395,6 @@
   pas_audio_trigger
 };
 
-static struct audio_operations pas_audio_operations =
-{
-  "Pro Audio Spectrum",
-  DMA_AUTOMODE,
-  AFMT_U8 | AFMT_S16_LE,
-  NULL,
-  &pas_audio_driver
-};
-
 void
 pas_pcm_init (struct address_info *hw_config)
 {
@@ -447,20 +408,28 @@
 
   if (num_audiodevs < MAX_AUDIO_DEV)
     {
-      audio_devs[pas_audiodev = num_audiodevs++] = &pas_audio_operations;
-      audio_devs[pas_audiodev]->dmachan1 = hw_config->dma;
-      audio_devs[pas_audiodev]->buffsize = DSP_BUFFSIZE;
+
+      if ((pas_audiodev = sound_install_audiodrv (AUDIO_DRIVER_VERSION,
+						  "Pro Audio Spectrum",
+						  &pas_audio_driver,
+					       sizeof (struct audio_driver),
+						  DMA_AUTOMODE,
+						  AFMT_U8 | AFMT_S16_LE,
+						  NULL,
+						  hw_config->dma,
+						  hw_config->dma)) < 0)
+	{
+	  return;
+	}
     }
   else
-    printk ("PAS2: Too many PCM devices available\n");
+    printk ("PAS16: Too many PCM devices available\n");
 }
 
 void
 pas_pcm_interrupt (unsigned char status, int cause)
 {
-  if (cause == 1)		/*
-				 * PCM buffer done
-				 */
+  if (cause == 1)
     {
       /*
        * Halt the PCM first. Otherwise we don't have time to start a new
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/pss.c linux/drivers/sound/pss.c
--- v2.1.27/linux/drivers/sound/pss.c	Fri Nov 15 00:15:31 1996
+++ linux/drivers/sound/pss.c	Wed Feb 26 02:35:26 1997
@@ -4,7 +4,7 @@
  * The low level driver for the Personal Sound System (ECHO ESC614).
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -348,7 +348,7 @@
   /*
      * Disable all emulations. Will be enabled later (if required).
    */
-  outw (0x0000, REG (CONF_PSS));
+  outw (0x0000, REG (CONF_PSS));	/* 0x0400 enables joystick */
   outw (0x0000, REG (CONF_WSS));
   outw (0x0000, REG (CONF_SB));
   outw (0x0000, REG (CONF_MIDI));
@@ -536,7 +536,7 @@
 	if (buf == NULL)
 	  return -ENOSPC;
 
-	copy_from_user ((char *) buf, &((char *) arg)[0], sizeof (*buf));
+	memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
 	err = download_boot_block (dev_info, buf);
 	vfree (buf);
 	return err;
@@ -554,7 +554,7 @@
 	if (buf == NULL)
 	  return -ENOSPC;
 
-	copy_from_user ((char *) buf, &((char *) arg)[0], sizeof (*buf));
+	memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
 
 	data = (unsigned short *) (buf->data);
 
@@ -567,11 +567,7 @@
 	      {
 		restore_flags (flags);
 		buf->len = i;	/* feed back number of WORDs sent */
-		{
-		  char           *fixit = (char *) buf;
-
-		  copy_to_user (&((char *) arg)[0], fixit, sizeof (*buf));
-		};
+		memcpy ((&((char *) arg)[0]), (char *) buf, sizeof (*buf));
 		vfree (buf);
 		return -EIO;
 	      }
@@ -616,11 +612,7 @@
 
 	restore_flags (flags);
 
-	{
-	  char           *fixit = (char *) buf;
-
-	  copy_to_user (&((char *) arg)[0], fixit, sizeof (*buf));
-	};
+	memcpy ((&((char *) arg)[0]), (char *) buf, sizeof (*buf));
 	vfree (buf);
 
 	return err;
@@ -634,7 +626,7 @@
 	unsigned long   flags;
 	unsigned short  tmp;
 
-	copy_from_user ((char *) &buf, &((char *) arg)[0], sizeof (buf));
+	memcpy ((char *) &buf, (&((char *) arg)[0]), sizeof (buf));
 
 	save_flags (flags);
 	cli ();
@@ -659,11 +651,7 @@
 	buf.parm1 = tmp;
 	restore_flags (flags);
 
-	{
-	  char           *fixit = (char *) &buf;
-
-	  copy_to_user (&((char *) arg)[0], fixit, sizeof (buf));
-	};
+	memcpy ((&((char *) arg)[0]), (char *) &buf, sizeof (buf));
 	return 0;
       }
       break;
@@ -674,7 +662,7 @@
 	unsigned long   flags;
 	unsigned short  tmp;
 
-	copy_from_user ((char *) &buf, &((char *) arg)[0], sizeof (buf));
+	memcpy ((char *) &buf, (&((char *) arg)[0]), sizeof (buf));
 
 	save_flags (flags);
 	cli ();
@@ -708,7 +696,7 @@
 	unsigned long   flags;
 	unsigned short  tmp;
 
-	copy_from_user ((char *) &buf, &((char *) arg)[0], sizeof (buf));
+	memcpy ((char *) &buf, (&((char *) arg)[0]), sizeof (buf));
 
 	save_flags (flags);
 	cli ();
@@ -749,7 +737,7 @@
 	unsigned long   flags;
 	unsigned short  tmp;
 
-	copy_from_user ((char *) &buf, &((char *) arg)[0], sizeof (buf));
+	memcpy ((char *) &buf, (&((char *) arg)[0]), sizeof (buf));
 
 	save_flags (flags);
 	cli ();
@@ -783,11 +771,7 @@
 
 	restore_flags (flags);
 
-	{
-	  char           *fixit = (char *) &buf;
-
-	  copy_to_user (&((char *) arg)[0], fixit, sizeof (buf));
-	};
+	memcpy ((&((char *) arg)[0]), (char *) &buf, sizeof (buf));
 	return 0;
       }
       break;
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sb.h linux/drivers/sound/sb.h
--- v2.1.27/linux/drivers/sound/sb.h	Fri Nov 15 00:14:54 1996
+++ linux/drivers/sound/sb.h	Wed Feb 26 02:34:47 1997
@@ -41,6 +41,8 @@
 #define MDL_SMW		11	/* Logitech SoundMan Wave (Jazz16) */
 #define MDL_ESS		12	/* ESS ES688 and ES1688 */
 #define MDL_AZTECH	13	/* Aztech Sound Galaxy family */
+#define MDL_ES1868MIDI	14	/* MIDI port of ESS1868 */
+#define MDL_AEDSP	15	/* Audio Excel DSP 16 */
 
 /*
  * Config flags
@@ -107,7 +109,6 @@
 	} sb_devc;
 
 int sb_dsp_command (sb_devc *devc, unsigned char val);
-int sb_dsp_get_byte (sb_devc *devc);
 int sb_dsp_reset (sb_devc *devc);
 void sb_setmixer (sb_devc *devc, unsigned int port, unsigned int value);
 unsigned int sb_getmixer (sb_devc *devc, unsigned int port);
@@ -115,6 +116,7 @@
 void sb_dsp_init (struct address_info *hw_config);
 void sb_dsp_unload(struct address_info *hw_config);
 int sb_mixer_init(sb_devc *devc);
+void sb_mixer_set_stereo (sb_devc *devc, int mode);
 void smw_mixer_init(sb_devc *devc);
 void sb_dsp_midi_init (sb_devc *devc);
 void sb_audio_init (sb_devc *devc, char *name);
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sb_audio.c linux/drivers/sound/sb_audio.c
--- v2.1.27/linux/drivers/sound/sb_audio.c	Fri Nov 15 00:15:33 1996
+++ linux/drivers/sound/sb_audio.c	Wed Feb 26 02:35:27 1997
@@ -4,7 +4,7 @@
  * Audio routines for Sound Blaster compatible cards.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -67,8 +67,8 @@
 {
   sb_devc        *devc = audio_devs[dev]->devc;
 
-  audio_devs[dev]->dmachan1 =
-    audio_devs[dev]->dmachan2 =
+  audio_devs[dev]->dmap_in->dma =
+    audio_devs[dev]->dmap_out->dma =
     devc->dma8;
 
   if (devc->dma16 != -1 && devc->dma16 != devc->dma8)
@@ -79,27 +79,24 @@
 
 static void
 sb_set_output_parms (int dev, unsigned long buf, int nr_bytes,
-		     int intrflag, int restart_dma)
+		     int intrflag)
 {
   sb_devc        *devc = audio_devs[dev]->devc;
 
   devc->trg_buf = buf;
   devc->trg_bytes = nr_bytes;
   devc->trg_intrflag = intrflag;
-  devc->trg_restart = restart_dma;
   devc->irq_mode = IMODE_OUTPUT;
 }
 
 static void
-sb_set_input_parms (int dev, unsigned long buf, int count, int intrflag,
-		    int restart_dma)
+sb_set_input_parms (int dev, unsigned long buf, int count, int intrflag)
 {
   sb_devc        *devc = audio_devs[dev]->devc;
 
   devc->trg_buf = buf;
   devc->trg_bytes = count;
   devc->trg_intrflag = intrflag;
-  devc->trg_restart = restart_dma;
   devc->irq_mode = IMODE_INPUT;
 }
 
@@ -109,15 +106,15 @@
 
 static void
 sb1_audio_output_block (int dev, unsigned long buf, int nr_bytes,
-			int intrflag, int restart_dma)
+			int intrflag)
 {
   unsigned long   flags;
   int             count = nr_bytes;
   sb_devc        *devc = audio_devs[dev]->devc;
 
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
   count--;
 
@@ -137,8 +134,7 @@
 }
 
 static void
-sb1_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag,
-		       int restart_dma)
+sb1_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag)
 {
   unsigned long   flags;
   int             count = nr_bytes;
@@ -148,9 +144,9 @@
    * Start a DMA input to the buffer pointed by dmaqtail
    */
 
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
   count--;
 
@@ -185,12 +181,12 @@
 	{
 	case IMODE_INPUT:
 	  sb1_audio_start_input (dev, devc->trg_buf, devc->trg_bytes,
-				 devc->trg_intrflag, devc->trg_restart);
+				 devc->trg_intrflag);
 	  break;
 
 	case IMODE_OUTPUT:
 	  sb1_audio_output_block (dev, devc->trg_buf, devc->trg_bytes,
-				  devc->trg_intrflag, devc->trg_restart);
+				  devc->trg_intrflag);
 	  break;
 	}
     }
@@ -279,9 +275,13 @@
 static void
 sb1_audio_halt_xfer (int dev)
 {
+  unsigned long   flags;
   sb_devc        *devc = audio_devs[dev]->devc;
 
+  save_flags (flags);
+  cli ();
   sb_dsp_reset (devc);
+  restore_flags (flags);
 }
 
 /*
@@ -290,16 +290,16 @@
 
 static void
 sb20_audio_output_block (int dev, unsigned long buf, int nr_bytes,
-			 int intrflag, int restart_dma)
+			 int intrflag)
 {
   unsigned long   flags;
   int             count = nr_bytes;
   sb_devc        *devc = audio_devs[dev]->devc;
   unsigned char   cmd;
 
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
   count--;
 
@@ -328,8 +328,7 @@
 }
 
 static void
-sb20_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag,
-			int restart_dma)
+sb20_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag)
 {
   unsigned long   flags;
   int             count = nr_bytes;
@@ -340,9 +339,9 @@
    * Start a DMA input to the buffer pointed by dmaqtail
    */
 
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
   count--;
 
@@ -385,12 +384,12 @@
 	{
 	case IMODE_INPUT:
 	  sb20_audio_start_input (dev, devc->trg_buf, devc->trg_bytes,
-				  devc->trg_intrflag, devc->trg_restart);
+				  devc->trg_intrflag);
 	  break;
 
 	case IMODE_OUTPUT:
 	  sb20_audio_output_block (dev, devc->trg_buf, devc->trg_bytes,
-				   devc->trg_intrflag, devc->trg_restart);
+				   devc->trg_intrflag);
 	  break;
 	}
     }
@@ -444,8 +443,8 @@
   unsigned char   bits = 0;
 
   if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
-    audio_devs[dev]->dmachan1 =
-      audio_devs[dev]->dmachan2 =
+    audio_devs[dev]->dmap_out->dma =
+      audio_devs[dev]->dmap_in->dma =
       devc->bits == 16 ? devc->dma16 : devc->dma8;
 
   if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
@@ -476,9 +475,11 @@
   unsigned char   bits = 0;
 
   if (devc->dma16 >= 0 && devc->dma16 != devc->dma8)
-    audio_devs[dev]->dmachan1 =
-      audio_devs[dev]->dmachan2 =
+    audio_devs[dev]->dmap_out->dma =
+      audio_devs[dev]->dmap_in->dma =
       devc->bits == 16 ? devc->dma16 : devc->dma8;
+  if (devc->model == MDL_SBPRO)
+    sb_mixer_set_stereo (devc, devc->channels == 2);
 
   save_flags (flags);
   cli ();
@@ -747,18 +748,15 @@
 
 static void
 ess_audio_output_block (int dev, unsigned long buf, int nr_bytes,
-			int intrflag, int restart_dma)
+			int intrflag)
 {
   int             count = nr_bytes;
   sb_devc        *devc = audio_devs[dev]->devc;
   short           c = -nr_bytes;
 
-  if (!restart_dma)
-    return;
-
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
   count--;
 
@@ -772,23 +770,19 @@
 }
 
 static void
-ess_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag,
-		       int restart_dma)
+ess_audio_start_input (int dev, unsigned long buf, int nr_bytes, int intrflag)
 {
   int             count = nr_bytes;
   sb_devc        *devc = audio_devs[dev]->devc;
   short           c = -nr_bytes;
 
-  if (!restart_dma)
-    return;
-
   /*
    * Start a DMA input to the buffer pointed by dmaqtail
    */
 
-  DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 
-  if (audio_devs[dev]->dmachan1 > 3)
+  if (audio_devs[dev]->dmap_out->dma > 3)
     count >>= 1;
   count--;
 
@@ -816,12 +810,12 @@
 	{
 	case IMODE_INPUT:
 	  ess_audio_start_input (dev, devc->trg_buf, devc->trg_bytes,
-				 devc->trg_intrflag, devc->trg_restart);
+				 devc->trg_intrflag);
 	  break;
 
 	case IMODE_OUTPUT:
 	  ess_audio_output_block (dev, devc->trg_buf, devc->trg_bytes,
-				  devc->trg_intrflag, devc->trg_restart);
+				  devc->trg_intrflag);
 	  break;
 	}
     }
@@ -871,8 +865,8 @@
 {
   sb_devc        *devc = audio_devs[dev]->devc;
 
-  audio_devs[dev]->dmachan1 =
-    audio_devs[dev]->dmachan2 =
+  audio_devs[dev]->dmap_out->dma =
+    audio_devs[dev]->dmap_in->dma =
     devc->bits == AFMT_S16_LE ? devc->dma16 : devc->dma8;
 
   devc->trigger_bits = 0;
@@ -884,8 +878,8 @@
 {
   sb_devc        *devc = audio_devs[dev]->devc;
 
-  audio_devs[dev]->dmachan1 =
-    audio_devs[dev]->dmachan2 =
+  audio_devs[dev]->dmap_out->dma =
+    audio_devs[dev]->dmap_in->dma =
     devc->bits == AFMT_S16_LE ? devc->dma16 : devc->dma8;
 
   devc->trigger_bits = 0;
@@ -894,7 +888,7 @@
 
 static void
 sb16_audio_output_block (int dev, unsigned long buf, int count,
-			 int intrflag, int restart_dma)
+			 int intrflag)
 {
   unsigned long   flags, cnt;
   sb_devc        *devc = audio_devs[dev]->devc;
@@ -902,9 +896,6 @@
   devc->irq_mode = IMODE_OUTPUT;
   devc->intr_active = 1;
 
-  if (!restart_dma)
-    return;
-
   cnt = count;
   if (devc->bits == AFMT_S16_LE)
     cnt >>= 1;
@@ -913,8 +904,7 @@
   save_flags (flags);
   cli ();
 
-  if (restart_dma)
-    DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
 
   sb_dsp_command (devc, 0x41);
   sb_dsp_command (devc, (unsigned char) ((devc->speed >> 8) & 0xff));
@@ -930,8 +920,7 @@
 }
 
 static void
-sb16_audio_start_input (int dev, unsigned long buf, int count, int intrflag,
-			int restart_dma)
+sb16_audio_start_input (int dev, unsigned long buf, int count, int intrflag)
 {
   unsigned long   flags, cnt;
   sb_devc        *devc = audio_devs[dev]->devc;
@@ -939,9 +928,6 @@
   devc->irq_mode = IMODE_INPUT;
   devc->intr_active = 1;
 
-  if (!restart_dma)
-    return;
-
   cnt = count;
   if (devc->bits == AFMT_S16_LE)
     cnt >>= 1;
@@ -950,8 +936,7 @@
   save_flags (flags);
   cli ();
 
-  if (restart_dma)
-    DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
+  /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
 
   sb_dsp_command (devc, 0x42);
   sb_dsp_command (devc, (unsigned char) ((devc->speed >> 8) & 0xff));
@@ -981,12 +966,12 @@
 	{
 	case IMODE_INPUT:
 	  sb16_audio_start_input (dev, devc->trg_buf, devc->trg_bytes,
-				  devc->trg_intrflag, devc->trg_restart);
+				  devc->trg_intrflag);
 	  break;
 
 	case IMODE_OUTPUT:
 	  sb16_audio_output_block (dev, devc->trg_buf, devc->trg_bytes,
-				   devc->trg_intrflag, devc->trg_restart);
+				   devc->trg_intrflag);
 	  break;
 	}
     }
@@ -995,23 +980,11 @@
 }
 
 static int
-sb_audio_ioctl (int dev, unsigned int cmd, caddr_t arg, int local)
+sb_audio_ioctl (int dev, unsigned int cmd, caddr_t arg)
 {
   return -EINVAL;
 }
 
-static void
-sb_audio_reset (int dev)
-{
-  unsigned long   flags;
-  sb_devc        *devc = audio_devs[dev]->devc;
-
-  save_flags (flags);
-  cli ();
-  sb_dsp_reset (devc);
-  restore_flags (flags);
-}
-
 static struct audio_driver sb1_audio_driver =	/* SB1.x */
 {
   sb_audio_open,
@@ -1021,7 +994,6 @@
   sb_audio_ioctl,
   sb1_audio_prepare_for_input,
   sb1_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,				/* local_qlen */
   NULL,				/* copy_from_user */
@@ -1042,7 +1014,6 @@
   sb_audio_ioctl,
   sb1_audio_prepare_for_input,
   sb1_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,				/* local_qlen */
   NULL,				/* copy_from_user */
@@ -1063,7 +1034,6 @@
   sb_audio_ioctl,
   sb1_audio_prepare_for_input,
   sb1_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,				/* local_qlen */
   NULL,				/* copy_from_user */
@@ -1084,7 +1054,6 @@
   sb_audio_ioctl,
   sbpro_audio_prepare_for_input,
   sbpro_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,				/* local_qlen */
   NULL,				/* copy_from_user */
@@ -1105,7 +1074,6 @@
   sb_audio_ioctl,
   sbpro_audio_prepare_for_input,
   sbpro_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,				/* local_qlen */
   NULL,				/* copy_from_user */
@@ -1126,7 +1094,6 @@
   sb_audio_ioctl,
   sb16_audio_prepare_for_input,
   sb16_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,				/* local_qlen */
   NULL,				/* copy_from_user */
@@ -1147,7 +1114,6 @@
   sb_audio_ioctl,
   ess_audio_prepare_for_input,
   ess_audio_prepare_for_output,
-  sb_audio_reset,
   sb1_audio_halt_xfer,
   NULL,				/* local_qlen */
   NULL,				/* copy_from_user */
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sb_card.c linux/drivers/sound/sb_card.c
--- v2.1.27/linux/drivers/sound/sb_card.c	Fri Nov 15 00:15:33 1996
+++ linux/drivers/sound/sb_card.c	Wed Feb 26 02:35:27 1997
@@ -4,7 +4,7 @@
  * Detection routine for the Sound Blaster cards.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sb_common.c linux/drivers/sound/sb_common.c
--- v2.1.27/linux/drivers/sound/sb_common.c	Fri Nov 15 00:15:34 1996
+++ linux/drivers/sound/sb_common.c	Wed Feb 26 02:35:28 1997
@@ -4,7 +4,7 @@
  * Common routines for Sound Blaster compatible cards.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -47,8 +47,8 @@
 #ifdef SMW_MIDI0001_INCLUDED
 #include "smw-midi0001.h"
 #else
-unsigned char  *smw_ucode = NULL;
-int             smw_ucodeLen = 0;
+static unsigned char *smw_ucode = NULL;
+static int      smw_ucodeLen = 0;
 
 #endif
 
@@ -83,7 +83,7 @@
   return 0;
 }
 
-int
+static int
 sb_dsp_get_byte (sb_devc * devc)
 {
   int             i;
@@ -122,7 +122,7 @@
   return sb_dsp_get_byte (devc);
 }
 
-void
+static void
 sbintr (int irq, void *dev_id, struct pt_regs *dummy)
 {
   int             status;
@@ -315,8 +315,6 @@
       ival = 8;
       break;
     default:
-      if (devc->type == MDL_SBPNP)
-	return 1;
       printk ("SB16 IRQ%d is not possible\n", level);
       return 0;
     }
@@ -430,6 +428,7 @@
 
 
   hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc (strlen (name + 1)));
+  sound_mem_sizes[sound_nblocks] = strlen (name + 1);
   if (sound_nblocks < 1024)
     sound_nblocks++;;
   if (hw_config->name != NULL)
@@ -496,6 +495,7 @@
   devc->submodel = ess_minor & 0x0f;
 
   hw_config->name = (char *) (sound_mem_blocks[sound_nblocks] = vmalloc (strlen (name + 1)));
+  sound_mem_sizes[sound_nblocks] = strlen (name + 1);
   if (sound_nblocks < 1024)
     sound_nblocks++;;
   if (hw_config->name != NULL)
@@ -621,19 +621,21 @@
     dsp_get_vers (devc);
 
   if (devc->major == 3 && devc->minor == 1)
-    if (devc->type == MDL_AZTECH)	/* SG Washington? */
-      {
-	if (sb_dsp_command (devc, 0x09))
-	  if (sb_dsp_command (devc, 0x00))	/* Enter WSS mode */
-	    {
-	      int             i;
+    {
+      if (devc->type == MDL_AZTECH)	/* SG Washington? */
+	{
+	  if (sb_dsp_command (devc, 0x09))
+	    if (sb_dsp_command (devc, 0x00))	/* Enter WSS mode */
+	      {
+		int             i;
 
-	      /* Have some delay */
-	      for (i = 0; i < 10000; i++)
-		inb (DSP_DATA_AVAIL);
-	      devc->caps = SB_NO_AUDIO | SB_NO_MIDI;	/* Mixer only */
-	    }
-      }
+		/* Have some delay */
+		for (i = 0; i < 10000; i++)
+		  inb (DSP_DATA_AVAIL);
+		devc->caps = SB_NO_AUDIO | SB_NO_MIDI;	/* Mixer only */
+	      }
+	}
+    }
 
 /*
  * Save device information for sb_dsp_init()
@@ -641,6 +643,7 @@
 
 
   detected_devc = (sb_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (sb_devc)));
+  sound_mem_sizes[sound_nblocks] = sizeof (sb_devc);
   if (sound_nblocks < 1024)
     sound_nblocks++;;
 
@@ -724,6 +727,10 @@
 	      }
 	}
 
+#ifdef __SMP__
+      /* Skip IRQ detection if SMP (doesn't work) */
+      devc->irq_ok = 1;
+#else
       if (devc->major == 4 && devc->minor <= 11)	/* Won't work */
 	devc->irq_ok = 1;
       else
@@ -738,15 +745,16 @@
 
 	  if (!devc->irq_ok)
 	    {
-	      printk ("sb: Interrupt test on IRQ%d failed - Propable IRQ conflict\n", devc->irq);
+	      printk ("sb: Interrupt test on IRQ%d failed - Probable IRQ conflict\n", devc->irq);
 	    }
 	  else
 	    {
 	      DDB (printk ("IRQ test OK (IRQ%d)\n", devc->irq));
 	    }
 
-	}			/* IRQ setup */
-    }
+	}
+#endif /* __SMP__ */
+    }				/* IRQ setup */
 
   request_region (hw_config->io_base, 16, "soundblaster");
 
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sb_midi.c linux/drivers/sound/sb_midi.c
--- v2.1.27/linux/drivers/sound/sb_midi.c	Fri Nov 15 00:15:34 1996
+++ linux/drivers/sound/sb_midi.c	Wed Feb 26 02:35:29 1997
@@ -4,7 +4,7 @@
  * The low level driver for the Sound Blaster DS chips.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -28,7 +28,6 @@
  * future version of this driver.
  */
 
-void            (*midi_input_intr) (int dev, unsigned char data);
 
 static int
 sb_midi_open (int dev, int mode,
@@ -188,6 +187,7 @@
 
 
   midi_devs[num_midis] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct midi_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct midi_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
@@ -204,6 +204,7 @@
 
 
   midi_devs[num_midis]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct synth_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
@@ -217,6 +218,7 @@
   memcpy ((char *) midi_devs[num_midis]->converter, (char *) &std_midi_synth,
 	  sizeof (struct synth_operations));
 
+  midi_devs[num_midis]->converter->id = "SBMIDI";
   num_midis++;
 }
 
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sb_mixer.c linux/drivers/sound/sb_mixer.c
--- v2.1.27/linux/drivers/sound/sb_mixer.c	Fri Nov 15 00:15:35 1996
+++ linux/drivers/sound/sb_mixer.c	Wed Feb 26 02:35:29 1997
@@ -5,7 +5,7 @@
  * The low level mixer driver for the Sound Blaster compatible cards.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -24,7 +24,7 @@
 
 static int      sbmixnum = 1;
 
-void            sb_mixer_reset (sb_devc * devc);
+static void     sb_mixer_reset (sb_devc * devc);
 
 void
 sb_mixer_set_stereo (sb_devc * devc, int mode)
@@ -311,7 +311,7 @@
     {
       int             tmp;
 
-      get_user (tmp, (int *) arg);
+      tmp = *(int *) arg;
 
       sb_setmixer (devc, 0x43, (~tmp) & 0x01);
       return 0;
@@ -319,48 +319,48 @@
 
   if (((cmd >> 8) & 0xff) == 'M')
     {
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
 	switch (cmd & 0xff)
 	  {
 	  case SOUND_MIXER_RECSRC:
-	    get_user (val, (int *) arg);
-	    return ioctl_out (arg, set_recmask (devc, val));
+	    val = *(int *) arg;
+	    return (*(int *) arg = set_recmask (devc, val));
 	    break;
 
 	  default:
 
-	    get_user (val, (int *) arg);
-	    return ioctl_out (arg, sb_mixer_set (devc, cmd & 0xff, val));
+	    val = *(int *) arg;
+	    return (*(int *) arg = sb_mixer_set (devc, cmd & 0xff, val));
 	  }
       else
 	switch (cmd & 0xff)
 	  {
 
 	  case SOUND_MIXER_RECSRC:
-	    return ioctl_out (arg, devc->recmask);
+	    return (*(int *) arg = devc->recmask);
 	    break;
 
 	  case SOUND_MIXER_DEVMASK:
-	    return ioctl_out (arg, devc->supported_devices);
+	    return (*(int *) arg = devc->supported_devices);
 	    break;
 
 	  case SOUND_MIXER_STEREODEVS:
 	    if (devc->model == MDL_JAZZ || devc->model == MDL_SMW)
-	      return ioctl_out (arg, devc->supported_devices);
+	      return (*(int *) arg = devc->supported_devices);
 	    else
-	      return ioctl_out (arg, devc->supported_devices & ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
+	      return (*(int *) arg = devc->supported_devices & ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX));
 	    break;
 
 	  case SOUND_MIXER_RECMASK:
-	    return ioctl_out (arg, devc->supported_rec_devices);
+	    return (*(int *) arg = devc->supported_rec_devices);
 	    break;
 
 	  case SOUND_MIXER_CAPS:
-	    return ioctl_out (arg, devc->mixer_caps);
+	    return (*(int *) arg = devc->mixer_caps);
 	    break;
 
 	  default:
-	    return ioctl_out (arg, sb_mixer_get (devc, cmd & 0xff));
+	    return (*(int *) arg = sb_mixer_get (devc, cmd & 0xff));
 	  }
     }
   else
@@ -374,7 +374,7 @@
   sb_mixer_ioctl
 };
 
-void
+static void
 sb_mixer_reset (sb_devc * devc)
 {
   char            name[32];
@@ -445,6 +445,7 @@
 
 
   mixer_devs[num_mixers] = (struct mixer_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct mixer_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct mixer_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sb_mixer.h linux/drivers/sound/sb_mixer.h
--- v2.1.27/linux/drivers/sound/sb_mixer.h	Fri Nov 15 00:14:54 1996
+++ linux/drivers/sound/sb_mixer.h	Wed Feb 26 02:34:48 1997
@@ -4,7 +4,7 @@
  * Definitions for the SB Pro and SB16 mixers
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -98,7 +98,7 @@
 	{{reg_l, bit_l, len_l}, {reg_r, bit_r, len_r}}
 
 #ifdef __SB_MIXER_C__
-mixer_tab sbpro_mix = {
+static mixer_tab sbpro_mix = {
 MIX_ENT(SOUND_MIXER_VOLUME,	0x22, 7, 4, 0x22, 3, 4),
 MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
@@ -112,7 +112,7 @@
 MIX_ENT(SOUND_MIXER_ALTPCM,	0x00, 0, 0, 0x00, 0, 0),
 MIX_ENT(SOUND_MIXER_RECLEV,	0x00, 0, 0, 0x00, 0, 0)
 };
-mixer_tab es688_mix = {
+static mixer_tab es688_mix = {
 MIX_ENT(SOUND_MIXER_VOLUME,	0x32, 7, 4, 0x32, 3, 4),
 MIX_ENT(SOUND_MIXER_BASS,	0x00, 0, 0, 0x00, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,	0x00, 0, 0, 0x00, 0, 0),
@@ -133,7 +133,7 @@
 };
 
 #ifdef	__SGNXPRO__
-mixer_tab sgnxpro_mix = {
+static mixer_tab sgnxpro_mix = {
 MIX_ENT(SOUND_MIXER_VOLUME,	0x22, 7, 4, 0x22, 3, 4),
 MIX_ENT(SOUND_MIXER_BASS,	0x46, 2, 3, 0x00, 0, 0),
 MIX_ENT(SOUND_MIXER_TREBLE,	0x44, 2, 3, 0x00, 0, 0),
@@ -151,7 +151,7 @@
 };
 #endif
 
-mixer_tab sb16_mix = {
+static mixer_tab sb16_mix = {
 MIX_ENT(SOUND_MIXER_VOLUME,	0x30, 7, 5, 0x31, 7, 5),
 MIX_ENT(SOUND_MIXER_BASS,	0x46, 7, 4, 0x47, 7, 4),
 MIX_ENT(SOUND_MIXER_TREBLE,	0x44, 7, 4, 0x45, 7, 4),
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sequencer.c linux/drivers/sound/sequencer.c
--- v2.1.27/linux/drivers/sound/sequencer.c	Tue Jan 28 00:02:45 1997
+++ linux/drivers/sound/sequencer.c	Wed Feb 26 02:35:31 1997
@@ -4,14 +4,13 @@
  * The sequencer personality manager.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
  * for more info.
  */
 #include <linux/config.h>
-#include <linux/poll.h>
 
 
 #define SEQUENCER_C
@@ -200,6 +199,7 @@
     return;
 
   tstamp = jiffies - seq_time;
+
   if (tstamp != prev_input_time)
     {
       tstamp = (tstamp << 8) | SEQ_WAIT;
@@ -267,7 +267,7 @@
 
       if (ev_code == SEQ_FULLSIZE)
 	{
-	  int             err;
+	  int             err, fmt;
 
 	  dev = *(unsigned short *) &event_rec[2];
 	  if (dev < 0 || dev >= max_synthdev)
@@ -276,7 +276,8 @@
 	  if (!(synth_open_mask & (1 << dev)))
 	    return -ENXIO;
 
-	  err = synth_devs[dev]->load_patch (dev, *(short *) &event_rec[0], buf, p + 4, c, 0);
+	  fmt = (*(short *) &event_rec[0]) & 0xffff;
+	  err = synth_devs[dev]->load_patch (dev, fmt, buf, p + 4, c, 0);
 	  if (err < 0)
 	    return err;
 
@@ -364,9 +365,7 @@
   if (!seq_playing)
     seq_startplay ();
 
-  return count;			/* This will "eat" chunks shorter than 4 bytes (if written
-				   * alone) Should we really do that ?
-				 */
+  return count;
 }
 
 static int
@@ -429,6 +428,11 @@
       if (q[4] > 127 && q[4] != 255)
 	return 0;
 
+      if (q[5] == 0)
+	{
+	  synth_devs[dev]->kill_note (dev, q[3], q[4], q[5]);
+	  break;
+	}
       synth_devs[dev]->start_note (dev, q[3], q[4], q[5]);
       break;
 
@@ -545,13 +549,7 @@
 	    {
 	      synth_devs[dev]->set_instr (dev, voice, 128 + note);
 	      synth_devs[dev]->chn_info[chn].pgm_num = 128 + note;
-	      note = 60;	/* Middle C */
-
 	    }
-	}
-
-      if (seq_mode == SEQ_2)
-	{
 	  synth_devs[dev]->setup_voice (dev, voice, chn);
 	}
 
@@ -574,6 +572,7 @@
     }
 }
 
+
 static void
 seq_chn_common_event (unsigned char *event_rec)
 {
@@ -806,13 +805,13 @@
     return;
   if (!synth_devs[dev])
     return;
-  if (!synth_devs[dev]->send_sysex)
-    return;
 
   l = 0;
   for (i = 0; i < 6 && buf[i] != 0xff; i++)
     l = i + 1;
 
+  if (!synth_devs[dev]->send_sysex)
+    return;
   if (l > 0)
     synth_devs[dev]->send_sysex (dev, buf, l);
 }
@@ -890,7 +889,7 @@
       break;
 
     case SEQ_SYNCTIMER:	/*
-				   * Reset timer
+				 * Reset timer
 				 */
       seq_time = jiffies;
       prev_input_time = 0;
@@ -1044,6 +1043,9 @@
     {
       int             chn;
 
+      synth_devs[dev]->sysex_ptr = 0;
+      synth_devs[dev]->emulation = 0;
+
       for (chn = 0; chn < 16; chn++)
 	{
 	  synth_devs[dev]->chn_info[chn].pgm_num = 0;
@@ -1051,6 +1053,7 @@
 			     synth_devs[dev]->chn_info[chn].controllers,
 			     0);
 	  synth_devs[dev]->chn_info[chn].bender_value = (1 << 7);	/* Neutral */
+	  synth_devs[dev]->chn_info[chn].bender_range = 200;
 	}
     }
 
@@ -1065,6 +1068,9 @@
   int             level, tmp;
   unsigned long   flags;
 
+  if (!sequencer_ok)
+    sequencer_init ();
+
   level = ((dev & 0x0f) == SND_DEV_SEQ2) ? 2 : 1;
 
   dev = dev >> 4;
@@ -1083,6 +1089,14 @@
       return -ENXIO;
     }
 
+  if (mode == OPEN_READ)
+    if (!num_midis)
+      {
+	printk ("Sequencer: No MIDI devices. Input not possible\n");
+	sequencer_busy = 0;
+	return -ENXIO;
+      }
+
   save_flags (flags);
   cli ();
   if (sequencer_busy)
@@ -1133,14 +1147,6 @@
       setup_mode2 ();
     }
 
-  if (seq_mode == SEQ_1 && mode == OPEN_READ)
-    if (!max_mididev)
-      {
-	printk ("Sequencer: No Midi devices. Input not possible\n");
-	sequencer_busy = 0;
-	return -ENXIO;
-      }
-
   if (!max_synthdev && !max_mididev)
     return -ENXIO;
 
@@ -1152,28 +1158,24 @@
       midi_written[i] = 0;
     }
 
-  /*
-   * if (mode == OPEN_WRITE || mode == OPEN_READWRITE)
-   */
-  for (i = 0; i < max_synthdev; i++)	/*
-					 * Open synth devices
-					 */
-    if ((tmp = synth_devs[i]->open (i, mode)) < 0)
-      {
-	printk ("Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
-	if (synth_devs[i]->midi_dev)
-	  printk ("(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
-      }
-    else
-      {
-	synth_open_mask |= (1 << i);
-	if (synth_devs[i]->midi_dev)	/*
-					 * Is a midi interface
-					 */
-	  midi_opened[synth_devs[i]->midi_dev] = 1;
-      }
+  for (i = 0; i < max_synthdev; i++)
+    {
+      if ((tmp = synth_devs[i]->open (i, mode)) < 0)
+	{
+	  printk ("Sequencer: Warning! Cannot open synth device #%d (%d)\n", i, tmp);
+	  if (synth_devs[i]->midi_dev)
+	    printk ("(Maps to MIDI dev #%d)\n", synth_devs[i]->midi_dev);
+	}
+      else
+	{
+	  synth_open_mask |= (1 << i);
+	  if (synth_devs[i]->midi_dev)
+	    midi_opened[synth_devs[i]->midi_dev] = 1;
+	}
+    }
 
   seq_time = jiffies;
+
   prev_input_time = 0;
   prev_event_time = 0;
 
@@ -1260,7 +1262,7 @@
   DEB (printk ("sequencer_release(dev=%d)\n", dev));
 
   /*
-   * * Wait until the queue is empty (if we don't have nonblock)
+   * Wait until the queue is empty (if we don't have nonblock)
    */
 
   if (mode != OPEN_READ && !(file->flags & (O_NONBLOCK) ?
@@ -1268,6 +1270,23 @@
     while (!(current->signal & ~current->blocked) && qlen > 0)
       {
 	seq_sync ();
+
+	{
+	  unsigned long   tlimit;
+
+	  if (3 * HZ)
+	    current->timeout = tlimit = jiffies + (3 * HZ);
+	  else
+	    tlimit = (unsigned long) -1;
+	  seq_sleep_flag.opts = WK_SLEEP;
+	  interruptible_sleep_on (&seq_sleeper);
+	  if (!(seq_sleep_flag.opts & WK_WAKEUP))
+	    {
+	      if (jiffies >= tlimit)
+		seq_sleep_flag.opts |= WK_TIMEOUT;
+	    }
+	  seq_sleep_flag.opts &= ~WK_SLEEP;
+	};			/* Extra delay */
       }
 
   if (mode != OPEN_READ)
@@ -1397,6 +1416,7 @@
   unsigned long   flags;
 
   sound_stop_timer ();
+
   seq_time = jiffies;
   prev_input_time = 0;
   prev_event_time = 0;
@@ -1519,7 +1539,7 @@
 
       if (seq_mode != SEQ_2)
 	return -EINVAL;
-      get_user (pending_timer, (int *) arg);
+      pending_timer = *(int *) arg;
 
       if (pending_timer < 0 || pending_timer >= num_sound_timers)
 	{
@@ -1527,7 +1547,7 @@
 	  return -EINVAL;
 	}
 
-      return ioctl_out (arg, pending_timer);
+      return (*(int *) arg = pending_timer);
       break;
 
     case SNDCTL_SEQ_PANIC:
@@ -1553,7 +1573,7 @@
       break;
 
     case SNDCTL_SEQ_TESTMIDI:
-      get_user (midi_dev, (int *) arg);
+      midi_dev = *(int *) arg;
       if (midi_dev < 0 || midi_dev >= max_mididev)
 	return -ENXIO;
 
@@ -1576,21 +1596,21 @@
     case SNDCTL_SEQ_GETINCOUNT:
       if (mode == OPEN_WRITE)
 	return 0;
-      return ioctl_out (arg, iqlen);
+      return (*(int *) arg = iqlen);
       break;
 
     case SNDCTL_SEQ_GETOUTCOUNT:
 
       if (mode == OPEN_READ)
 	return 0;
-      return ioctl_out (arg, SEQ_MAX_QUEUE - qlen);
+      return (*(int *) arg = SEQ_MAX_QUEUE - qlen);
       break;
 
     case SNDCTL_SEQ_GETTIME:
       if (seq_mode == SEQ_2)
 	return tmr->ioctl (tmr_no, cmd, arg);
 
-      return ioctl_out (arg, jiffies - seq_time);
+      return (*(int *) arg = jiffies - seq_time);
       break;
 
     case SNDCTL_SEQ_CTRLRATE:
@@ -1600,18 +1620,18 @@
       if (seq_mode == SEQ_2)
 	return tmr->ioctl (tmr_no, cmd, arg);
 
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
       if (val != 0)
 	return -EINVAL;
 
-      return ioctl_out (arg, HZ);
+      return (*(int *) arg = HZ);
       break;
 
     case SNDCTL_SEQ_RESETSAMPLES:
       {
 	int             err;
 
-	get_user (dev, (int *) arg);
+	dev = *(int *) arg;
 	if (dev < 0 || dev >= num_synths)
 	  {
 	    return -ENXIO;
@@ -1628,18 +1648,18 @@
       break;
 
     case SNDCTL_SEQ_NRSYNTHS:
-      return ioctl_out (arg, max_synthdev);
+      return (*(int *) arg = max_synthdev);
       break;
 
     case SNDCTL_SEQ_NRMIDIS:
-      return ioctl_out (arg, max_mididev);
+      return (*(int *) arg = max_mididev);
       break;
 
     case SNDCTL_SYNTH_MEMAVL:
       {
 	int             dev;
 
-	get_user (dev, (int *) arg);
+	dev = *(int *) arg;
 
 	if (dev < 0 || dev >= num_synths)
 	  return -ENXIO;
@@ -1647,7 +1667,7 @@
 	if (!(synth_open_mask & (1 << dev)) && !orig_dev)
 	  return -EBUSY;
 
-	return ioctl_out (arg, synth_devs[dev]->ioctl (dev, cmd, arg));
+	return (*(int *) arg = synth_devs[dev]->ioctl (dev, cmd, arg));
       }
       break;
 
@@ -1655,7 +1675,7 @@
       {
 	int             dev;
 
-	get_user (dev, (int *) arg);
+	dev = *(int *) arg;
 
 	if (dev < 0 || dev >= num_synths)
 	  return -ENXIO;
@@ -1673,7 +1693,7 @@
 	struct synth_info inf;
 	int             dev;
 
-	copy_from_user ((char *) &inf, &((char *) arg)[0], sizeof (inf));
+	memcpy ((char *) &inf, (&((char *) arg)[0]), sizeof (inf));
 	dev = inf.device;
 
 	if (dev < 0 || dev >= max_synthdev)
@@ -1686,12 +1706,35 @@
       }
       break;
 
+
+      /* Like SYNTH_INFO but returns ID in the name field */
+    case SNDCTL_SYNTH_ID:
+      {
+	struct synth_info inf;
+	int             dev;
+
+	memcpy ((char *) &inf, (&((char *) arg)[0]), sizeof (inf));
+	dev = inf.device;
+
+	if (dev < 0 || dev >= max_synthdev)
+	  return -ENXIO;
+
+	if (!(synth_open_mask & (1 << dev)) && !orig_dev)
+	  return -EBUSY;
+
+	memcpy ((char *) &inf, (char *) synth_devs[dev]->info, sizeof (inf));
+	strcpy (inf.name, synth_devs[dev]->id);
+	memcpy ((&((char *) arg)[0]), (char *) &inf, sizeof (inf));
+	return 0;
+      }
+      break;
+
     case SNDCTL_SEQ_OUTOFBAND:
       {
 	struct seq_event_rec event_rec;
 	unsigned long   flags;
 
-	copy_from_user ((char *) &event_rec, &((char *) arg)[0], sizeof (event_rec));
+	memcpy ((char *) &event_rec, (&((char *) arg)[0]), sizeof (event_rec));
 
 	save_flags (flags);
 	cli ();
@@ -1708,18 +1751,14 @@
 	int             dev;
 	char           *pp;
 
-	copy_from_user ((char *) &inf, &((char *) arg)[0], sizeof (inf));
+	memcpy ((char *) &inf, (&((char *) arg)[0]), sizeof (inf));
 	dev = inf.device;
 
 	if (dev < 0 || dev >= max_mididev)
 	  return -ENXIO;
 
 	pp = (char *) &midi_devs[dev]->info;
-	{
-	  char           *fixit = pp;
-
-	  copy_to_user (&((char *) arg)[0], fixit, sizeof (inf));
-	};
+	memcpy ((&((char *) arg)[0]), pp, sizeof (inf));
 	return 0;
       }
       break;
@@ -1728,7 +1767,7 @@
       {
 	int             tmp;
 
-	get_user (tmp, (int *) arg);
+	tmp = *(int *) arg;
 
 	if (tmp < 1)
 	  tmp = 1;
@@ -1743,14 +1782,14 @@
       {
 	int             val;
 
-	get_user (val, (int *) arg);
+	val = *(int *) arg;
 
 	if (val < 0)
 	  val = 0;
 
 	val = (HZ * val) / 10;
 	pre_event_timeout = val;
-	return ioctl_out (arg, val);
+	return (*(int *) arg = val);
       }
       break;
 
@@ -1769,28 +1808,50 @@
   return -EINVAL;
 }
 
-unsigned int
-sequencer_poll (kdev_t dev, struct fileinfo *file, poll_table * wait)
+int
+sequencer_select (int dev, struct fileinfo *file, int sel_type, poll_table * wait)
 {
-  unsigned int	  mask = 0;
   unsigned long   flags;
 
-  save_flags (flags);
-  cli ();
+  dev = dev >> 4;
 
-  midi_sleep_flag.opts = WK_SLEEP;
-  poll_wait (&midi_sleeper, wait);
-  seq_sleep_flag.opts = WK_SLEEP;
-  poll_wait (&seq_sleeper, wait);
+  switch (sel_type)
+    {
+    case SEL_IN:
+      save_flags (flags);
+      cli ();
+      if (!iqlen)
+	{
 
-  restore_flags (flags);
+	  midi_sleep_flag.opts = WK_SLEEP;
+	  poll_wait (&midi_sleeper, wait);
+	  restore_flags (flags);
+	  return 0;
+	}
+      restore_flags (flags);
+      return 1;
+      break;
 
-  if (iqlen)
-    mask |= POLLIN | POLLRDNORM;
-  if ((SEQ_MAX_QUEUE - qlen) >= output_threshold)
-    mask |= POLLOUT | POLLWRNORM;
+    case SEL_OUT:
+      save_flags (flags);
+      cli ();
+      if ((SEQ_MAX_QUEUE - qlen) < output_threshold)
+	{
 
-  return mask;
+	  seq_sleep_flag.opts = WK_SLEEP;
+	  poll_wait (&seq_sleeper, wait);
+	  restore_flags (flags);
+	  return 0;
+	}
+      restore_flags (flags);
+      return 1;
+      break;
+
+    case SEL_EX:
+      return 0;
+    }
+
+  return 0;
 }
 
 
@@ -1835,7 +1896,8 @@
 }
 
 unsigned long
-compute_finetune (unsigned long base_freq, int bend, int range)
+compute_finetune (unsigned long base_freq, int bend, int range,
+		  int vibrato_cents)
 {
   unsigned long   amount;
   int             negative, semitones, cents, multiplier = 1;
@@ -1851,7 +1913,9 @@
   if (range >= 8192)
     range = 8192;
 
-  bend = bend * range / 8192;
+  bend = bend * range / 8192;	/* Convert to cents */
+  bend += vibrato_cents;
+
   if (!bend)
     return base_freq;
 
@@ -1888,9 +1952,12 @@
 void
 sequencer_init (void)
 {
+  if (sequencer_ok)
+    return;
 
 
   queue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc (SEQ_MAX_QUEUE * EV_SZ));
+  sound_mem_sizes[sound_nblocks] = SEQ_MAX_QUEUE * EV_SZ;
   if (sound_nblocks < 1024)
     sound_nblocks++;;
   if (queue == NULL)
@@ -1901,6 +1968,7 @@
 
 
   iqueue = (unsigned char *) (sound_mem_blocks[sound_nblocks] = vmalloc (SEQ_MAX_QUEUE * IEV_SZ));
+  sound_mem_sizes[sound_nblocks] = SEQ_MAX_QUEUE * IEV_SZ;
   if (sound_nblocks < 1024)
     sound_nblocks++;;
   if (queue == NULL)
@@ -1908,6 +1976,7 @@
       printk ("Sound: Can't allocate memory for sequencer input queue\n");
       return;
     }
+
 
   sequencer_ok = 1;
 }
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sound_calls.h linux/drivers/sound/sound_calls.h
--- v2.1.27/linux/drivers/sound/sound_calls.h	Tue Jan 28 00:02:45 1997
+++ linux/drivers/sound/sound_calls.h	Wed Feb 26 02:34:48 1997
@@ -5,27 +5,30 @@
 int DMAbuf_open(int dev, int mode);
 int DMAbuf_release(int dev, int mode);
 int DMAbuf_getwrbuffer(int dev, char **buf, int *size, int dontblock);
-int DMAbuf_get_curr_buffer(int dev, int *buff_no, char **dma_buf, int *buff_ptr, int *buff_size);
 int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock);
 int DMAbuf_rmchars(int dev, int buff_no, int c);
 int DMAbuf_start_output(int dev, int buff_no, int l);
-int DMAbuf_set_count(int dev, int buff_no, int l);
-int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t arg, int local);
+int DMAbuf_move_wrpointer(int dev, int l);
+/* int DMAbuf_ioctl(int dev, unsigned int cmd, caddr_t arg, int local); */
 void DMAbuf_init(void);
+void DMAbuf_deinit(int dev);
 int DMAbuf_start_dma (int dev, unsigned long physaddr, int count, int dma_mode);
 int DMAbuf_open_dma (int dev);
 void DMAbuf_close_dma (int dev);
-void DMAbuf_reset_dma (int dev);
 void DMAbuf_inputintr(int dev);
 void DMAbuf_outputintr(int dev, int underflow_flag);
-unsigned int DMAbuf_poll(kdev_t dev, struct fileinfo *file, poll_table * wait);
-void DMAbuf_start_device(int dev);
+struct dma_buffparms;
+int DMAbuf_space_in_queue (int dev);
+int DMAbuf_activate_recording (int dev, struct dma_buffparms *dmap);
+int DMAbuf_get_buffer_pointer (int dev, struct dma_buffparms *dmap);
+void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap);
+int DMAbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
 void DMAbuf_start_devices(unsigned int devmask);
 void DMAbuf_reset (int dev);
 int DMAbuf_sync (int dev);
 
 /*
- *	System calls for /dev/dsp and /dev/audio
+ *	System calls for /dev/dsp and /dev/audio (audio.c)
  */
 
 int audio_read (int dev, struct fileinfo *file, char *buf, int count);
@@ -36,7 +39,8 @@
 	   unsigned int cmd, caddr_t arg);
 void audio_init_devices (void);
 
-unsigned int audio_poll(kdev_t dev, struct fileinfo *file, poll_table * wait);
+int audio_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
+void reorganize_buffers (int dev, struct dma_buffparms *dmap, int recording);
 
 /*
  *	System calls for the /dev/sequencer
@@ -52,11 +56,12 @@
 void sequencer_init (void);
 void sequencer_timer(unsigned long dummy);
 int note_to_freq(int note_num);
-unsigned long compute_finetune(unsigned long base_freq, int bend, int range);
+unsigned long compute_finetune(unsigned long base_freq, int bend, int range,
+			       int vibrato_bend);
 void seq_input_event(unsigned char *event, int len);
 void seq_copy_to_input (unsigned char *event, int len);
 
-unsigned int sequencer_poll(kdev_t dev, struct fileinfo *file, poll_table * wait);
+int sequencer_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
 
 /*
  *	System calls for the /dev/midi
@@ -72,7 +77,7 @@
 void MIDIbuf_bytes_received(int dev, unsigned char *buf, int count);
 void MIDIbuf_init(void);
 
-unsigned int MIDIbuf_poll(kdev_t dev, struct fileinfo *file, poll_table * wait);
+int MIDIbuf_select(int dev, struct fileinfo *file, int sel_type, poll_table * wait);
 
 /*
  *
@@ -80,11 +85,9 @@
  */
 
 /*	From soundcard.c	*/
-void soundcard_init(void);
 void tenmicrosec(int *osp);
 void request_sound_timer (int count);
 void sound_stop_timer(void);
-int snd_ioctl_return(int *addr, int value);
 int snd_set_irq_handler (int interrupt_level, void(*iproc)(int, void*, struct pt_regs *), char *name, int *osp);
 void snd_release_irq(int vect);
 void sound_dma_malloc(int dev);
@@ -159,7 +162,6 @@
 void gus_wave_init(struct address_info *hw_config);
 void gus_wave_unload (void);
 void gus_voice_irq(void);
-unsigned char gus_read8 (int reg);
 void gus_write8(int reg, unsigned int data);
 void guswave_dma_irq(void);
 void gus_delay(void);
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sound_config.h linux/drivers/sound/sound_config.h
--- v2.1.27/linux/drivers/sound/sound_config.h	Fri Nov 15 00:14:55 1996
+++ linux/drivers/sound/sound_config.h	Wed Feb 26 02:34:48 1997
@@ -3,7 +3,7 @@
  * A driver for Soundcards, misc configuration parameters.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -95,9 +95,9 @@
 
 #define MAX_AUDIO_DEV	5
 #define MAX_MIXER_DEV	5
-#define MAX_SYNTH_DEV	3
+#define MAX_SYNTH_DEV	5
 #define MAX_MIDI_DEV	6
-#define MAX_TIMER_DEV	3
+#define MAX_TIMER_DEV	4
 
 struct fileinfo {
        	  int mode;	      /* Open mode */
@@ -132,6 +132,7 @@
 struct channel_info {
 		int pgm_num;
 		int bender_value;
+		int bender_range;
 		unsigned char controllers[128];
 	};
 
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sound_switch.c linux/drivers/sound/sound_switch.c
--- v2.1.27/linux/drivers/sound/sound_switch.c	Fri Nov 15 00:15:39 1996
+++ linux/drivers/sound/sound_switch.c	Wed Feb 26 02:35:34 1997
@@ -4,7 +4,7 @@
  * The system call switch handler
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -73,15 +73,11 @@
   if ((buf = (mixer_vol_table *) vmalloc (sizeof (mixer_vol_table))) == NULL)
     return -ENOSPC;
 
-  copy_from_user ((char *) buf, &((char *) arg)[0], sizeof (*buf));
+  memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
 
   load_mixer_volumes (buf->name, buf->levels, 0);
 
-  {
-    char           *fixit = (char *) buf;
-
-    copy_to_user (&((char *) arg)[0], fixit, sizeof (*buf));
-  };
+  memcpy ((&((char *) arg)[0]), (char *) buf, sizeof (*buf));
   vfree (buf);
 
   return err;
@@ -96,7 +92,7 @@
   if ((buf = (mixer_vol_table *) vmalloc (sizeof (mixer_vol_table))) == NULL)
     return -ENOSPC;
 
-  copy_from_user ((char *) buf, &((char *) arg)[0], sizeof (*buf));
+  memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
 
   n = buf->num;
   if (n < 0 || n >= num_mixer_volumes)
@@ -106,11 +102,7 @@
       memcpy ((char *) buf, (char *) &mixer_vols[n], sizeof (*buf));
     }
 
-  {
-    char           *fixit = (char *) buf;
-
-    copy_to_user (&((char *) arg)[0], fixit, sizeof (*buf));
-  };
+  memcpy ((&((char *) arg)[0]), (char *) buf, sizeof (*buf));
   vfree (buf);
 
   return err;
@@ -358,7 +350,7 @@
     }
 #endif
 
-#ifdef CONFIG_MIDI
+#ifdef CONFIG_SEQUENCER
   if (!put_status ("\nTimers:\n"))
     return;
 
@@ -612,11 +604,7 @@
   strcpy (info.name, mixer_devs[dev]->name);
   info.modify_counter = mixer_devs[dev]->modify_counter;
 
-  {
-    char           *fixit = (char *) &info;
-
-    copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-  };
+  memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
   return 0;
 }
 
@@ -631,11 +619,7 @@
   strcpy (info.id, mixer_devs[dev]->id);
   strcpy (info.name, mixer_devs[dev]->name);
 
-  {
-    char           *fixit = (char *) &info;
-
-    copy_to_user (&((char *) arg)[0], fixit, sizeof (info));
-  };
+  memcpy ((&((char *) arg)[0]), (char *) &info, sizeof (info));
   return 0;
 }
 
@@ -648,7 +632,7 @@
   if (cmd == SOUND_OLD_MIXER_INFO)
     return get_old_mixer_info (mixdev, arg);
 
-  if (_IOC_DIR (cmd) & _IOC_WRITE)
+  if (_SIOC_DIR (cmd) & _SIOC_WRITE)
     mixer_devs[mixdev]->modify_counter++;
 
   return mixer_devs[mixdev]->ioctl (mixdev, cmd, arg);
@@ -661,7 +645,7 @@
   DEB (printk ("sound_ioctl_sw(dev=%d, cmd=0x%x, arg=0x%x)\n", dev, cmd, arg));
 
   if (cmd == OSS_GETVERSION)
-    return ioctl_out (arg, SOUND_VERSION);
+    return (*(int *) arg = SOUND_VERSION);
 
 
   if (((cmd >> 8) & 0xff) == 'M' && num_mixers > 0)	/* Mixer ioctl */
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sound_timer.c linux/drivers/sound/sound_timer.c
--- v2.1.27/linux/drivers/sound/sound_timer.c	Fri Nov 15 00:15:40 1996
+++ linux/drivers/sound/sound_timer.c	Wed Feb 26 02:35:35 1997
@@ -2,7 +2,7 @@
  * sound/sound_timer.c
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -11,7 +11,6 @@
 #include <linux/config.h>
 
 
-#define SEQUENCER_C
 #include "sound_config.h"
 
 #if defined(CONFIG_SEQUENCER)
@@ -31,7 +30,7 @@
 tmr2ticks (int tmr_value)
 {
   /*
-     *    Convert timer ticks to MIDI ticks
+   *    Convert timer ticks to MIDI ticks
    */
 
   unsigned long   tmp;
@@ -195,7 +194,7 @@
   switch (cmd)
     {
     case SNDCTL_TMR_SOURCE:
-      return ioctl_out (arg, TMR_INTERNAL);
+      return (*(int *) arg = TMR_INTERNAL);
       break;
 
     case SNDCTL_TMR_START:
@@ -215,7 +214,7 @@
       break;
 
     case SNDCTL_TMR_TIMEBASE:
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
 
       if (val)
 	{
@@ -226,11 +225,11 @@
 	  curr_timebase = val;
 	}
 
-      return ioctl_out (arg, curr_timebase);
+      return (*(int *) arg = curr_timebase);
       break;
 
     case SNDCTL_TMR_TEMPO:
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
 
       if (val)
 	{
@@ -245,20 +244,20 @@
 	  reprogram_timer ();
 	}
 
-      return ioctl_out (arg, curr_tempo);
+      return (*(int *) arg = curr_tempo);
       break;
 
     case SNDCTL_SEQ_CTRLRATE:
-      get_user (val, (int *) arg);
+      val = *(int *) arg;
 
       if (val != 0)		/* Can't change */
 	return -EINVAL;
 
-      return ioctl_out (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+      return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
       break;
 
     case SNDCTL_SEQ_GETTIME:
-      return ioctl_out (arg, curr_ticks);
+      return (*(int *) arg = curr_ticks);
       break;
 
     case SNDCTL_TMR_METRONOME:
@@ -286,7 +285,7 @@
 
 static struct sound_timer_operations sound_timer =
 {
-  {"GUS Timer", 0},
+  {"Sound Timer", 0},
   1,				/* Priority */
   0,				/* Local device link */
   timer_open,
@@ -323,10 +322,15 @@
 {
   int             n;
 
-  if (initialized || t == NULL)
-    return;			/* There is already a similar timer */
-
+  if (initialized)
+    {
+      if (t->priority <= tmr->priority)
+	return;			/* There is already a similar or better timer */
+      tmr = t;
+      return;
+    }
   initialized = 1;
+
   tmr = t;
 
   if (num_sound_timers >= MAX_TIMER_DEV)
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/soundcard.c linux/drivers/sound/soundcard.c
--- v2.1.27/linux/drivers/sound/soundcard.c	Tue Jan 28 00:02:45 1997
+++ linux/drivers/sound/soundcard.c	Wed Feb 26 02:35:35 1997
@@ -4,7 +4,7 @@
  * Soundcard driver for Linux
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -14,11 +14,23 @@
 
 
 #include "sound_config.h"
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/fcntl.h>
+#include <linux/ctype.h>
+#ifdef __KERNEL__
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <linux/wait.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#endif /* __KERNEL__ */
+#include <linux/delay.h>
 
 #include <linux/major.h>
 
 
-int            *sound_osp = NULL;
 static int      chrdev_registered = 0;
 static int      sound_major = SOUND_MAJOR;
 
@@ -28,6 +40,7 @@
  * Table for permanently allocated memory (used when unloading the module)
  */
 caddr_t         sound_mem_blocks[1024];
+int             sound_mem_sizes[1024];
 int             sound_nblocks = 0;
 
 static int      soundcard_configured = 0;
@@ -42,31 +55,6 @@
 #define DMA_MAP_BUSY		2
 
 
-int
-ioctl_in (caddr_t arg)
-{
-  int             xx;
-
-  get_user (xx, (int *) arg);
-  return xx;
-}
-
-int
-ioctl_out (caddr_t arg, int result)
-{
-  put_user (result, (int *) arg);
-  return 0;
-}
-
-int
-snd_ioctl_return (int *addr, int value)
-{
-  if (value < 0)
-    return value;
-
-  put_user (value, (int *) &((addr)[0]));
-  return 0;
-}
 
 static long
 sound_read (struct inode *inode, struct file *file, char *buf, unsigned long count)
@@ -159,27 +147,37 @@
 	     unsigned int cmd, unsigned long arg)
 {
   int             dev, err;
+  int             len = 0;
+  int             alloced = 0;
+  char           *ptr = (char *) arg;
 
   dev = MINOR (inode->i_rdev);
 
   files[dev].flags = file->f_flags;
 
-  if (_IOC_DIR (cmd) != _IOC_NONE)
+  if (_SIOC_DIR (cmd) != _SIOC_NONE)
     {
       /*
          * Have to validate the address given by the process.
        */
-      int             len;
 
-      len = _IOC_SIZE (cmd);
+      len = _SIOC_SIZE (cmd);
+      if (len < 1 || len > 65536 || arg == 0)
+	return -EFAULT;
+
+      ptr = vmalloc (len);
+      alloced = 1;
+      if (ptr == NULL)
+	return -EFAULT;
 
-      if (_IOC_DIR (cmd) & _IOC_WRITE)
+      if (_SIOC_DIR (cmd) & _SIOC_WRITE)
 	{
 	  if ((err = verify_area (VERIFY_READ, (void *) arg, len)) < 0)
 	    return err;
+	  copy_from_user (ptr, (char *) arg, len);
 	}
 
-      if (_IOC_DIR (cmd) & _IOC_READ)
+      if (_SIOC_DIR (cmd) & _SIOC_READ)
 	{
 	  if ((err = verify_area (VERIFY_WRITE, (void *) arg, len)) < 0)
 	    return err;
@@ -187,34 +185,42 @@
 
     }
 
-  err = sound_ioctl_sw (dev, &files[dev], cmd, (caddr_t) arg);
+  err = sound_ioctl_sw (dev, &files[dev], cmd, (caddr_t) ptr);
+
+  if (_SIOC_DIR (cmd) & _SIOC_READ)
+    {
+      copy_to_user ((char *) arg, ptr, len);
+    }
+
+  if (ptr != NULL && alloced)
+    vfree (ptr);
 
   return err;
 }
 
-static unsigned int
-sound_poll (struct file *file, poll_table * wait)
+static int
+sound_select (struct inode *inode, struct file *file, int sel_type, poll_table * wait)
 {
   int             dev;
 
-  dev = MINOR (file->f_inode->i_rdev);
+  dev = MINOR (inode->i_rdev);
 
   files[dev].flags = file->f_flags;
 
-  DEB (printk ("sound_poll(dev=%d)\n", dev));
+  DEB (printk ("sound_select(dev=%d, type=0x%x)\n", dev, sel_type));
 
   switch (dev & 0x0f)
     {
 #ifdef CONFIG_SEQUENCER
     case SND_DEV_SEQ:
     case SND_DEV_SEQ2:
-      return sequencer_poll (dev, &files[dev], wait);
+      return sequencer_select (dev, &files[dev], sel_type, wait);
       break;
 #endif
 
 #ifdef CONFIG_MIDI
     case SND_DEV_MIDIN:
-      return MIDIbuf_poll (dev, &files[dev], wait);
+      return MIDIbuf_select (dev, &files[dev], sel_type, wait);
       break;
 #endif
 
@@ -222,7 +228,7 @@
     case SND_DEV_DSP:
     case SND_DEV_DSP16:
     case SND_DEV_AUDIO:
-      return audio_poll (dev, &files[dev], wait);
+      return audio_select (dev, &files[dev], sel_type, wait);
       break;
 #endif
 
@@ -233,6 +239,21 @@
   return 0;
 }
 
+static unsigned int
+sound_poll (struct file *file, poll_table * wait)
+{
+  struct inode   *inode;
+  int             ret = 0;
+
+  inode = file->f_inode;
+
+  if (sound_select (inode, file, SEL_IN, wait))
+    ret |= POLLIN;
+  if (sound_select (inode, file, SEL_OUT, wait))
+    ret |= POLLOUT;
+  return ret;
+}
+
 static int
 sound_mmap (struct inode *inode, struct file *file, struct vm_area_struct *vma)
 {
@@ -334,7 +355,11 @@
   sound_release
 };
 
+#ifdef MODULE
+static void
+#else
 void
+#endif
 soundcard_init (void)
 {
 #ifndef MODULE
@@ -444,6 +469,11 @@
 {
   int             i;
 
+  if (MOD_IN_USE)
+    {
+      return;
+    }
+
   if (chrdev_registered)
     unregister_chrdev (sound_major, "sound");
 
@@ -460,9 +490,6 @@
 #endif
   sound_unload_drivers ();
 
-  for (i = 0; i < sound_nblocks; i++)
-    vfree (sound_mem_blocks[i]);
-
   free_all_irqs ();		/* If something was left allocated by accident */
 
   for (i = 0; i < 8; i++)
@@ -473,16 +500,18 @@
       }
 
 
+  for (i = 0; i < sound_nblocks; i++)
+    {
+      vfree (sound_mem_blocks[i]);
+    }
+
 }
 #endif
 
 void
 tenmicrosec (int *osp)
 {
-  int             i;
-
-  for (i = 0; i < 16; i++)
-    inb (0x80);
+  udelay (10);
 }
 
 int
@@ -493,7 +522,7 @@
 
   save_flags (flags);
   cli ();
-  retcode = request_irq (interrupt_level, iproc, 0 /* SA_INTERRUPT */ , name, NULL);
+  retcode = request_irq (interrupt_level, iproc, 0, name, NULL);
   if (retcode < 0)
     {
       printk ("Sound: IRQ%d already in use\n", interrupt_level);
@@ -544,7 +573,7 @@
 
   if (dma_alloc_map[chn] != DMA_MAP_FREE)
     {
-      printk ("sound_open_dma: DMA channel %d busy or not allocated\n", chn);
+      printk ("sound_open_dma: DMA channel %d busy or not allocated (%d)\n", chn, dma_alloc_map[chn]);
       restore_flags (flags);
       return 1;
     }
@@ -557,7 +586,7 @@
 void
 sound_free_dma (int chn)
 {
-  if (dma_alloc_map[chn] != DMA_MAP_FREE)
+  if (dma_alloc_map[chn] == DMA_MAP_UNAVAIL)
     {
       /* printk ("sound_free_dma: Bad access to DMA channel %d\n", chn); */
       return;
@@ -628,14 +657,7 @@
 
 #ifdef CONFIG_AUDIO
 
-#ifdef KMALLOC_DMA_BROKEN
-fatal_error__This_version_is_not_compatible_with_this_kernel;
-#endif
-
 static int      dma_buffsize = DSP_BUFFSIZE;
-#ifdef MODULE_PARM
-MODULE_PARM(dma_buffsize, "i");
-#endif
 
 int
 sound_alloc_dmap (int dev, struct dma_buffparms *dmap, int chan)
@@ -753,15 +775,6 @@
   dmap->raw_buf = NULL;
 }
 
-int
-sound_map_buffer (int dev, struct dma_buffparms *dmap, buffmem_desc * info)
-{
-  printk ("Entered sound_map_buffer()\n");
-  printk ("Exited sound_map_buffer()\n");
-  return -EINVAL;
-}
-#endif
-
 void
 conf_printf (char *name, struct address_info *hw_config)
 {
@@ -803,3 +816,29 @@
 
   printk ("\n");
 }
+
+/* Intel version !!!!!!!!! */
+int 
+sound_start_dma (int dev, struct dma_buffparms *dmap, int chan,
+		 unsigned long physaddr,
+		 int count, int dma_mode, int autoinit)
+{
+  unsigned long   flags;
+
+/* printk("Start DMA %d, %d\n", (int)(physaddr-dmap->raw_buf_phys), count); */
+  if (autoinit)
+    dma_mode |= DMA_AUTOINIT;
+  save_flags (flags);
+  cli ();
+  disable_dma (chan);
+  clear_dma_ff (chan);
+  set_dma_mode (chan, dma_mode);
+  set_dma_addr (chan, physaddr);
+  set_dma_count (chan, count);
+  enable_dma (chan);
+  restore_flags (flags);
+
+  return 0;
+}
+
+#endif
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/soundvers.h linux/drivers/sound/soundvers.h
--- v2.1.27/linux/drivers/sound/soundvers.h	Fri Nov 15 00:14:56 1996
+++ linux/drivers/sound/soundvers.h	Wed Feb 26 02:34:50 1997
@@ -1,2 +1,2 @@
-#define SOUND_VERSION_STRING "3.7-beta12-961115"
-#define SOUND_INTERNAL_VERSION 0x030707
+#define SOUND_VERSION_STRING "3.8-beta9-970226"
+#define SOUND_INTERNAL_VERSION 0x030803
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sscape.c linux/drivers/sound/sscape.c
--- v2.1.27/linux/drivers/sound/sscape.c	Fri Nov 15 00:15:42 1996
+++ linux/drivers/sound/sscape.c	Wed Feb 26 02:35:37 1997
@@ -4,7 +4,7 @@
  * Low level driver for Ensoniq SoundScape
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -284,45 +284,6 @@
   return tmp;
 }
 
-void
-sscapeintr (int irq, void *dev_id, struct pt_regs *dummy)
-{
-  unsigned char   bits, tmp;
-  static int      debug = 0;
-
-  bits = sscape_read (devc, GA_INTSTAT_REG);
-  if ((sscape_sleep_flag.opts & WK_SLEEP))
-    {
-      {
-	sscape_sleep_flag.opts = WK_WAKEUP;
-	wake_up (&sscape_sleeper);
-      };
-    }
-
-  if (bits & 0x02)		/* Host interface interrupt */
-    {
-      printk ("SSCAPE: Host interrupt, data=%02x\n", host_read (devc));
-    }
-
-#if defined(CONFIG_MPU_EMU) && defined(CONFIG_MIDI)
-  if (bits & 0x01)
-    {
-      mpuintr (irq, NULL, NULL);
-      if (debug++ > 10)		/* Temporary debugging hack */
-	{
-	  sscape_write (devc, GA_INTENA_REG, 0x00);	/* Disable all interrupts */
-	}
-    }
-#endif
-
-  /*
-     * Acknowledge interrupts (toggle the interrupt bits)
-   */
-
-  tmp = sscape_read (devc, GA_INTENA_REG);
-  sscape_write (devc, GA_INTENA_REG, (~bits & 0x0e) | (tmp & 0xf1));
-
-}
 
 
 static void
@@ -652,7 +613,7 @@
 	buf = (copr_buffer *) vmalloc (sizeof (copr_buffer));
 	if (buf == NULL)
 	  return -ENOSPC;
-	copy_from_user ((char *) buf, &((char *) arg)[0], sizeof (*buf));
+	memcpy ((char *) buf, (&((char *) arg)[0]), sizeof (*buf));
 	err = download_boot_block (dev_info, buf);
 	vfree (buf);
 	return err;
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/sys_timer.c linux/drivers/sound/sys_timer.c
--- v2.1.27/linux/drivers/sound/sys_timer.c	Fri Nov 15 00:15:42 1996
+++ linux/drivers/sound/sys_timer.c	Wed Feb 26 02:35:37 1997
@@ -5,7 +5,7 @@
  * Uses the (1/HZ sec) timer of kernel.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -14,7 +14,6 @@
 #include <linux/config.h>
 
 
-#define SEQUENCER_C
 #include "sound_config.h"
 
 #ifdef CONFIG_SEQUENCER
@@ -192,7 +191,7 @@
   switch (cmd)
     {
     case SNDCTL_TMR_SOURCE:
-      return ioctl_out (arg, TMR_INTERNAL);
+      return (*(int *) arg = TMR_INTERNAL);
       break;
 
     case SNDCTL_TMR_START:
@@ -215,7 +214,7 @@
       {
 	int             val;
 
-	get_user (val, (int *) arg);
+	val = *(int *) arg;
 
 	if (val)
 	  {
@@ -226,7 +225,7 @@
 	    curr_timebase = val;
 	  }
 
-	return ioctl_out (arg, curr_timebase);
+	return (*(int *) arg = curr_timebase);
       }
       break;
 
@@ -234,7 +233,7 @@
       {
 	int             val;
 
-	get_user (val, (int *) arg);
+	val = *(int *) arg;
 
 	if (val)
 	  {
@@ -248,7 +247,7 @@
 	    curr_tempo = val;
 	  }
 
-	return ioctl_out (arg, curr_tempo);
+	return (*(int *) arg = curr_tempo);
       }
       break;
 
@@ -256,16 +255,16 @@
       {
 	int             val;
 
-	get_user (val, (int *) arg);
+	val = *(int *) arg;
 	if (val != 0)		/* Can't change */
 	  return -EINVAL;
 
-	return ioctl_out (arg, ((curr_tempo * curr_timebase) + 30) / 60);
+	return (*(int *) arg = ((curr_tempo * curr_timebase) + 30) / 60);
       }
       break;
 
     case SNDCTL_SEQ_GETTIME:
-      return ioctl_out (arg, curr_ticks);
+      return (*(int *) arg = curr_ticks);
       break;
 
     case SNDCTL_TMR_METRONOME:
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/trix.c linux/drivers/sound/trix.c
--- v2.1.27/linux/drivers/sound/trix.c	Fri Nov 15 00:15:43 1996
+++ linux/drivers/sound/trix.c	Wed Feb 26 02:35:38 1997
@@ -5,7 +5,7 @@
  * (MT-0002-PC Control Chip)
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -201,7 +201,12 @@
   ret = ad1848_detect (hw_config->io_base + 4, NULL, hw_config->osp);
 
   if (ret)
-    request_region (0x390, 2, "AudioTrix");
+    {
+#ifdef TRIX_ENABLE_JOYSTICK
+      trix_write (0x15, 0x80);
+#endif
+      request_region (0x390, 2, "AudioTrix");
+    }
 
   return ret;
 }
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/uart401.c linux/drivers/sound/uart401.c
--- v2.1.27/linux/drivers/sound/uart401.c	Fri Nov 15 00:15:43 1996
+++ linux/drivers/sound/uart401.c	Wed Feb 26 02:35:38 1997
@@ -4,7 +4,7 @@
  * MPU-401 UART driver (formerly uart401_midi.c)
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -263,6 +263,7 @@
 
 
   devc = (uart401_devc *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (uart401_devc)));
+  sound_mem_sizes[sound_nblocks] = sizeof (uart401_devc);
   if (sound_nblocks < 1024)
     sound_nblocks++;;
   if (devc == NULL)
@@ -290,7 +291,7 @@
     if (snd_set_irq_handler (devc->irq, uart401intr, "uart401", devc->osp) < 0)
       {
 	printk ("uart401: Failed to allocate IRQ%d\n", devc->irq);
-	return;
+	devc->share_irq = 1;
       }
 
   irq2devc[devc->irq] = devc;
@@ -311,6 +312,7 @@
 
 
   midi_devs[num_midis] = (struct midi_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct midi_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct midi_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
@@ -327,6 +329,7 @@
 
 
   midi_devs[num_midis]->converter = (struct synth_operations *) (sound_mem_blocks[sound_nblocks] = vmalloc (sizeof (struct synth_operations)));
+  sound_mem_sizes[sound_nblocks] = sizeof (struct synth_operations);
 
   if (sound_nblocks < 1024)
     sound_nblocks++;;
@@ -341,6 +344,7 @@
 	  sizeof (struct synth_operations));
 
   strcpy (midi_devs[num_midis]->info.name, name);
+  midi_devs[num_midis]->converter->id = "UART401";
   num_midis++;
   devc->opened = 0;
 }
@@ -397,6 +401,7 @@
 probe_uart401 (struct address_info *hw_config)
 {
   int             ok = 0;
+  unsigned long   flags;
 
   static uart401_devc hw_info;
   uart401_devc   *devc = &hw_info;
@@ -417,7 +422,10 @@
   devc->my_dev = 0;
   devc->share_irq = 0;
 
+  save_flags (flags);
+  cli ();
   ok = reset_uart401 (devc);
+  restore_flags (flags);
 
   if (ok)
     detected_devc = devc;
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/uart6850.c linux/drivers/sound/uart6850.c
--- v2.1.27/linux/drivers/sound/uart6850.c	Fri Nov 15 00:15:44 1996
+++ linux/drivers/sound/uart6850.c	Wed Feb 26 02:35:39 1997
@@ -2,7 +2,7 @@
  * sound/uart6850.c
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
@@ -14,7 +14,6 @@
  *      added 6850 support, used with COVOX SoundMaster II and custom cards.
  */
 
-#include <linux/config.h>
 #include "sound_config.h"
 
 #if defined(CONFIG_UART6850) && defined(CONFIG_MIDI)
diff -u --recursive --new-file v2.1.27/linux/drivers/sound/vivo.c linux/drivers/sound/vivo.c
--- v2.1.27/linux/drivers/sound/vivo.c	Fri Nov 22 11:52:49 1996
+++ linux/drivers/sound/vivo.c	Wed Dec 31 16:00:00 1969
@@ -1,19 +0,0 @@
-
-/*
- * sound/vivo.c
- *
- * Support routines for Ensoniq Soundscape VIVO. The actual driver is
- * in the vivo subdirectory.
- */
-/*
- * Copyright (C) by Hannu Savolainen 1993-1996
- *
- * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
- * Version 2 (June 1991). See the "COPYING" file distributed with this software
- * for more info.
- */
-
-#include "sound_config.h"
-
-extern void     otto_init (int options);
-extern void     synth_init (void);
diff -u --recursive --new-file v2.1.27/linux/fs/buffer.c linux/fs/buffer.c
--- v2.1.27/linux/fs/buffer.c	Sun Jan 26 02:07:30 1997
+++ linux/fs/buffer.c	Mon Mar  3 11:56:10 1997
@@ -1,3 +1,4 @@
+extern void allow_interrupts(void);
 /*
  *  linux/fs/buffer.c
  *
@@ -625,6 +626,7 @@
 	}
 
 repeat:
+	allow_interrupts();
 	/* OK, we cannot grow the buffer cache, now try to get some
 	   from the lru list */
 
@@ -706,6 +708,7 @@
 	   now so as to ensure that there are still clean buffers available
 	   for user processes to use (and dirty) */
 repeat:
+	allow_interrupts();
 	bh = get_hash_table(dev, block, size);
 	if (bh) {
 		if (!buffer_dirty(bh)) {
@@ -717,7 +720,10 @@
 		return bh;
 	}
 
-	while(!free_list[isize]) refill_freelist(size);
+	while(!free_list[isize]) {
+		allow_interrupts();
+		refill_freelist(size);
+	}
 	
 	if (find_buffer(dev,block,size))
 		 goto repeat;
@@ -1510,6 +1516,8 @@
 		ndirty = 0;
 		nwritten = 0;
 	repeat:
+		allow_interrupts();
+	
 		bh = lru_list[nlist];
 		if(bh) 
 			 for (i = nr_buffers_type[nlist]; i-- > 0; bh = next) {
@@ -1651,6 +1659,8 @@
 			 ndirty = 0;
 			 refilled = 0;
 		 repeat:
+		 	allow_interrupts();
+		 	
 			 bh = lru_list[nlist];
 			 if(bh) 
 				  for (i = nr_buffers_type[nlist]; i-- > 0 && ndirty < bdf_prm.b_un.ndirty; 
@@ -1711,7 +1721,6 @@
 		
 		/* If there are still a lot of dirty buffers around, skip the sleep
 		   and flush some more */
-		
 		if(nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) {
 			current->signal = 0;
 			interruptible_sleep_on(&bdflush_wait);
diff -u --recursive --new-file v2.1.27/linux/fs/proc/Makefile linux/fs/proc/Makefile
--- v2.1.27/linux/fs/proc/Makefile	Sun Jan 26 02:07:45 1997
+++ linux/fs/proc/Makefile	Fri Feb 28 15:00:55 1997
@@ -8,7 +8,8 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 O_TARGET := proc.o
-O_OBJS   := inode.o root.o base.o mem.o link.o fd.o array.o kmsg.o net.o scsi.o
+O_OBJS   := inode.o root.o base.o generic.o mem.o link.o fd.o array.o \
+		kmsg.o net.o scsi.o proc_tty.o
 OX_OBJS  := procfs_syms.o
 M_OBJS   := 
 
diff -u --recursive --new-file v2.1.27/linux/fs/proc/generic.c linux/fs/proc/generic.c
--- v2.1.27/linux/fs/proc/generic.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/proc/generic.c	Fri Feb 28 15:00:55 1997
@@ -0,0 +1,195 @@
+/*
+ * proc/fs/generic.c --- generic routines for the proc-fs
+ *
+ * This file contains generic proc-fs routines for handling
+ * directories and files.
+ * 
+ * Copyright (C) 1991, 1992 Linus Torvalds.
+ * Copyright (C) 1997 Theodore Ts'o
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/config.h>
+#include <asm/bitops.h>
+
+static long proc_file_read(struct inode * inode, struct file * file,
+			   char * buf, unsigned long nbytes);
+static long proc_file_write(struct inode * inode, struct file * file,
+			    const char * buffer, unsigned long count);
+static long long proc_file_lseek(struct inode * inode, struct file * file, 
+				 long long offset, int orig);
+
+static struct file_operations proc_file_operations = {
+    proc_file_lseek,	/* lseek   */
+    proc_file_read,	/* read	   */
+    proc_file_write,	/* write   */
+    NULL,		/* readdir */
+    NULL,		/* poll    */
+    NULL,		/* ioctl   */
+    NULL,		/* mmap	   */
+    NULL,		/* no special open code	   */
+    NULL,		/* no special release code */
+    NULL		/* can't fsync */
+};
+
+/*
+ * proc files can do almost nothing..
+ */
+struct inode_operations proc_file_inode_operations = {
+    &proc_file_operations,  /* default scsi directory file-ops */
+    NULL,	    /* create	   */
+    NULL,	    /* lookup	   */
+    NULL,	    /* link	   */
+    NULL,	    /* unlink	   */
+    NULL,	    /* symlink	   */
+    NULL,	    /* mkdir	   */
+    NULL,	    /* rmdir	   */
+    NULL,	    /* mknod	   */
+    NULL,	    /* rename	   */
+    NULL,	    /* readlink	   */
+    NULL,	    /* follow_link */
+    NULL,	    /* readpage	   */
+    NULL,	    /* writepage   */
+    NULL,	    /* bmap	   */
+    NULL,	    /* truncate	   */
+    NULL	    /* permission  */
+};
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+#endif
+
+/* 4K page size but our output routines use some slack for overruns */
+#define PROC_BLOCK_SIZE	(3*1024)
+
+static long proc_file_read(struct inode * inode, struct file * file,
+			   char * buf, unsigned long nbytes)
+{
+	char 	*page;
+	int	retval=0;
+	int	n;
+	char	*start;
+	struct proc_dir_entry * dp;
+
+	if (nbytes < 0)
+		return -EINVAL;
+	dp = (struct proc_dir_entry *) inode->u.generic_ip;
+	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+
+	while (nbytes > 0)
+	{
+		n = MIN(PROC_BLOCK_SIZE, nbytes);
+
+		if (dp->get_info) {
+			/*
+			 * Handle backwards compatibility with the old net
+			 * routines.
+			 * 
+			 * XXX What gives with the file->f_flags & O_ACCMODE
+			 * test?  Seems stupid to me....
+			 */
+			n = dp->get_info(page, &start, file->f_pos, n,
+				 (file->f_flags & O_ACCMODE) == O_RDWR);
+		} else if (dp->read_proc) {
+			n = dp->read_proc(page, &start, file->f_pos,
+					  n, dp->data);
+		} else
+			break;
+			
+		if (n == 0)
+			break;	/* End of file */
+		if (n < 0) {
+			if (retval == 0)
+				retval = n;
+			break;
+		}
+		
+		n -= copy_to_user(buf, start, n);
+		if (n == 0) {
+			if (retval == 0)
+				retval = -EFAULT;
+			break;
+		}
+		
+		file->f_pos += n;	/* Move down the file */
+		nbytes -= n;
+		buf += n;
+		retval += n;
+	}
+	free_page((unsigned long) page);
+	return retval;
+}
+
+static long
+proc_file_write(struct inode * inode, struct file * file,
+		const char * buffer, unsigned long count)
+{
+	struct proc_dir_entry * dp;
+	char 	*page;
+	
+	if (count < 0)
+		return -EINVAL;
+	dp = (struct proc_dir_entry *) inode->u.generic_ip;
+	if (!(page = (char*) __get_free_page(GFP_KERNEL)))
+		return -ENOMEM;
+
+	if (!dp->write_proc)
+		return -EIO;
+
+	return dp->write_proc(file, buffer, count, dp->data);
+}
+
+
+
+static long long proc_file_lseek(struct inode * inode, struct file * file, 
+				 long long offset, int orig)
+{
+    switch (orig) {
+    case 0:
+	file->f_pos = offset;
+	return(file->f_pos);
+    case 1:
+	file->f_pos += offset;
+	return(file->f_pos);
+    case 2:
+	return(-EINVAL);
+    default:
+	return(-EINVAL);
+    }
+}
+
+struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
+					 struct proc_dir_entry *parent)
+{
+	struct proc_dir_entry *ent;
+
+	ent = kmalloc(sizeof(struct proc_dir_entry), GFP_KERNEL);
+	if (!ent)
+		return NULL;
+	memset(ent, 0, sizeof(struct proc_dir_entry));
+
+	if (mode == S_IFDIR)
+		mode |= S_IRUGO | S_IXUGO;
+	else if (mode == 0)
+		mode = S_IFREG | S_IRUGO;
+	
+	ent->name = name;
+	ent->namelen = strlen(ent->name);
+	ent->mode = mode;
+	if (S_ISDIR(mode)) 
+		ent->nlink = 2;
+	else
+		ent->nlink = 1;
+
+	if (parent)
+		proc_register(parent, ent);
+	
+	return ent;
+}
+
diff -u --recursive --new-file v2.1.27/linux/fs/proc/inode.c linux/fs/proc/inode.c
--- v2.1.27/linux/fs/proc/inode.c	Thu Dec 19 01:03:36 1996
+++ linux/fs/proc/inode.c	Fri Feb 28 15:00:55 1997
@@ -81,6 +81,7 @@
 struct inode * proc_get_inode(struct super_block * s, int ino, struct proc_dir_entry * de)
 {
 	struct inode * inode = iget(s, ino);
+	struct task_struct *p;
 	
 #ifdef CONFIG_SUN_OPENPROMFS_MODULE
 	if ((inode->i_ino >= PROC_OPENPROM_FIRST)
@@ -106,6 +107,14 @@
 				de->fill_inode(inode);
 		}
 	}
+	/*
+	 * Fixup the root inode's nlink value
+	 */
+	if (inode->i_ino == PROC_ROOT_INO) {
+		for_each_task(p)
+			if (p && p->pid)
+				inode->i_nlink++;
+	}
 	return inode;
 }			
 
@@ -162,92 +171,19 @@
 	inode->i_nlink = 1;
 	inode->i_size = 0;
 	pid = ino >> 16;
+	if (!pid)
+		return;
 	p = task[0];
 	for (i = 0; i < NR_TASKS ; i++)
 		if ((p = task[i]) && (p->pid == pid))
 			break;
 	if (!p || i >= NR_TASKS)
 		return;
-	if (ino == PROC_ROOT_INO) {
-		inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
-		inode->i_nlink = 2;
-		for (i = 1 ; i < NR_TASKS ; i++)
-			if (task[i])
-				inode->i_nlink++;
-		return;
-	}
 
-	if (!pid) {
-		switch (ino) {
-			case PROC_KMSG:
-				inode->i_mode = S_IFREG | S_IRUSR;
-				inode->i_op = &proc_kmsg_inode_operations;
-				break;
-			case PROC_NET:
-				inode->i_nlink = 2;
-				break;
-			case PROC_SCSI:
-                                inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
-                                inode->i_nlink = 2;
-                                inode->i_op = &proc_scsi_inode_operations;
-                                break;
-			case PROC_KCORE:
-				inode->i_mode = S_IFREG | S_IRUSR;
-				inode->i_op = &proc_kcore_inode_operations;
-				inode->i_size = (MAP_NR(high_memory) << PAGE_SHIFT) + PAGE_SIZE;
-				break;
-			case PROC_PROFILE:
-				inode->i_mode = S_IFREG | S_IRUGO | S_IWUSR;
-				inode->i_op = &proc_profile_inode_operations;
-				inode->i_size = (1+prof_len) * sizeof(unsigned long);
-				break;
-			default:
-				inode->i_mode = S_IFREG | S_IRUGO;
-				inode->i_op = &proc_array_inode_operations;
-				break;
-		}
-		return;
-	}
 	ino &= 0x0000ffff;
 	if (ino == PROC_PID_INO || p->dumpable) {
 		inode->i_uid = p->euid;
 		inode->i_gid = p->egid;
-	}
-	switch (ino) {
-		case PROC_PID_INO:
-			inode->i_nlink = 4;
-			return;
-		case PROC_PID_MEM:
-			inode->i_op = &proc_mem_inode_operations;
-			inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR;
-			return;
-		case PROC_PID_CWD:
-		case PROC_PID_ROOT:
-		case PROC_PID_EXE:
-			inode->i_op = &proc_link_inode_operations;
-			inode->i_size = 64;
-			inode->i_mode = S_IFLNK | S_IRWXU;
-			return;
-		case PROC_PID_FD:
-			inode->i_mode = S_IFDIR | S_IRUSR | S_IXUSR;
-			inode->i_op = &proc_fd_inode_operations;
-			inode->i_nlink = 2;
-			return;
-		case PROC_PID_ENVIRON:
-			inode->i_mode = S_IFREG | S_IRUSR;
-			inode->i_op = &proc_array_inode_operations;
-			return;
-		case PROC_PID_CMDLINE:
-		case PROC_PID_STATUS:
-		case PROC_PID_STAT:
-		case PROC_PID_STATM:
-			inode->i_mode = S_IFREG | S_IRUGO;
-			inode->i_op = &proc_array_inode_operations;
-			return;
-		case PROC_PID_MAPS:
-			inode->i_mode = S_IFIFO | S_IRUGO;
-			inode->i_op = &proc_arraylong_inode_operations;
-			return;
 	}
 	switch (ino >> 8) {
 		case PROC_PID_FD_DIR:
diff -u --recursive --new-file v2.1.27/linux/fs/proc/proc_tty.c linux/fs/proc/proc_tty.c
--- v2.1.27/linux/fs/proc/proc_tty.c	Wed Dec 31 16:00:00 1969
+++ linux/fs/proc/proc_tty.c	Fri Feb 28 15:00:55 1997
@@ -0,0 +1,183 @@
+/*
+ * proc_tty.c -- handles /proc/tty
+ *
+ * Copyright 1997, Theodore Ts'o
+ */
+
+#include <asm/uaccess.h>
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <linux/tty.h>
+#include <linux/config.h>
+#include <asm/bitops.h>
+
+extern struct tty_driver *tty_drivers;	/* linked list of tty drivers */
+extern struct tty_ldisc ldiscs[];
+
+
+static int tty_drivers_read_proc(char *page, char **start, off_t off,
+				 int count, void *data);
+static int tty_ldiscs_read_proc(char *page, char **start, off_t off,
+				int count, void *data);
+
+/*
+ * The /proc/tty directory inodes...
+ */
+static struct proc_dir_entry *proc_tty, *proc_tty_ldisc, *proc_tty_driver;
+
+/*
+ * This is the handler for /proc/tty/drivers
+ */
+static int tty_drivers_read_proc(char *page, char **start, off_t off,
+				 int count, void *data)
+{
+	int	len = 0;
+	off_t	begin = 0;
+	struct tty_driver *p;
+	char	range[20], deftype[20];
+	char	*type;
+
+	for (p = tty_drivers; p; p = p->next) {
+		if (p->num > 1)
+			sprintf(range, "%d-%d", p->minor_start,
+				p->minor_start + p->num - 1);
+		else
+			sprintf(range, "%d", p->minor_start);
+		switch (p->type) {
+		case TTY_DRIVER_TYPE_SYSTEM:
+			if (p->subtype == SYSTEM_TYPE_TTY)
+				type = "system:/dev/tty";
+			else if (p->subtype == SYSTEM_TYPE_CONSOLE)
+				type = "system:console";
+			else
+				type = "system";
+			break;
+		case TTY_DRIVER_TYPE_CONSOLE:
+			type = "console";
+			break;
+		case TTY_DRIVER_TYPE_SERIAL:
+			if (p->subtype == 2)
+				type = "serial:callout";
+			else
+				type = "serial";
+			break;
+		case TTY_DRIVER_TYPE_PTY:
+			if (p->subtype == PTY_TYPE_MASTER)
+				type = "pty:master";
+			else if (p->subtype == PTY_TYPE_SLAVE)
+				type = "pty:slave";
+			else
+				type = "pty";
+			break;
+		default:
+			sprintf(deftype, "type:%d.%d", p->type, p->subtype);
+			type = deftype;
+			break;
+		}
+		len += sprintf(page+len, "%-20s /dev/%-8s %3d %7s %s\n",
+			       p->driver_name ? p->driver_name : "",
+			       p->name, p->major, range, type);
+		if (len+begin > off+count)
+			break;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	if (off >= len+begin)
+		return 0;
+	*start = page + (begin-off);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * This is the handler for /proc/tty/ldiscs
+ */
+static int tty_ldiscs_read_proc(char *page, char **start, off_t off,
+				int count, void *data)
+{
+	int	i;
+	int	len = 0;
+	off_t	begin = 0;
+
+	for (i=0; i < NR_LDISCS; i++) {
+		if (!(ldiscs[i].flags & LDISC_FLAG_DEFINED))
+			continue;
+		len += sprintf(page+len, "%-10s %2d\n",
+			       ldiscs[i].name ? ldiscs[i].name : "???", i);
+		if (len+begin > off+count)
+			break;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	if (off >= len+begin)
+		return 0;
+	*start = page + (begin-off);
+	return ((count < begin+len-off) ? count : begin+len-off);
+}
+
+/*
+ * Thsi function is called by register_tty_driver() to handle
+ * registering the driver's /proc handler into /proc/tty/driver/<foo>
+ */
+void proc_tty_register_driver(struct tty_driver *driver)
+{
+	struct proc_dir_entry *ent;
+		
+	if ((!driver->read_proc && !driver->write_proc) ||
+	    !driver->driver_name ||
+	    driver->proc_entry)
+		return;
+
+	ent = create_proc_entry(driver->driver_name, 0, proc_tty_driver);
+	if (!ent)
+		return;
+	ent->read_proc = driver->read_proc;
+	ent->write_proc = driver->write_proc;
+	ent->data = driver;
+
+	driver->proc_entry = ent;
+}
+
+/*
+ * This function is called by unregister_tty_driver()
+ */
+void proc_tty_unregister_driver(struct tty_driver *driver)
+{
+	struct proc_dir_entry *ent;
+
+	ent = driver->proc_entry;
+	if (!ent)
+		return;
+		
+	proc_unregister(proc_tty_driver, ent->low_ino);
+	
+	driver->proc_entry = 0;
+	kfree(ent);
+}
+
+/*
+ * Called by proc_root_init() to initialize the /proc/tty subtree
+ */
+void proc_tty_init(void)
+{
+	struct proc_dir_entry *ent;
+	
+	proc_tty = create_proc_entry("tty", S_IFDIR, &proc_root);
+	if (!proc_tty)
+		return;
+	proc_tty_ldisc = create_proc_entry("ldisc", S_IFDIR, proc_tty);
+	proc_tty_driver = create_proc_entry("driver", S_IFDIR, proc_tty);
+
+	ent = create_proc_entry("ldiscs", 0, proc_tty);
+	ent->read_proc = tty_ldiscs_read_proc;
+
+	ent = create_proc_entry("drivers", 0, proc_tty);
+	ent->read_proc = tty_drivers_read_proc;
+}
+
diff -u --recursive --new-file v2.1.27/linux/fs/proc/root.c linux/fs/proc/root.c
--- v2.1.27/linux/fs/proc/root.c	Wed Jan 29 03:32:21 1997
+++ linux/fs/proc/root.c	Fri Feb 28 15:00:55 1997
@@ -299,13 +299,41 @@
 extern void openpromfs_init (void);
 #endif /* CONFIG_SUN_OPENPROMFS */
 
+static int make_inode_number(void)
+{
+	int i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC);
+	if (i<0 || i>=PROC_NDYNAMIC) 
+		return -1;
+	set_bit(i, (void *) proc_alloc_map);
+	return PROC_DYNAMIC_FIRST + i;
+}
+
 int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp)
 {
+	int	i;
+	
+	if (dp->low_ino == 0) {
+		i = make_inode_number();
+		if (i < 0)
+			return -EAGAIN;
+		dp->low_ino = i;
+	}
 	dp->next = dir->subdir;
 	dp->parent = dir;
 	dir->subdir = dp;
-	if (S_ISDIR(dp->mode))
+	if (S_ISDIR(dp->mode)) {
+		if (dp->ops == NULL)
+			dp->ops = &proc_dir_inode_operations;
 		dir->nlink++;
+	} else {
+		if (dp->ops == NULL)
+			dp->ops = &proc_file_inode_operations;
+	}
+	/*
+	 * kludge until we fixup the md device driver
+	 */
+	if (dp->low_ino == PROC_MD)
+		dp->ops = &proc_array_inode_operations;
 	return 0;
 }
 
@@ -330,28 +358,16 @@
 	return -EINVAL;
 }	
 
-static int make_inode_number(void)
-{
-	int i = find_first_zero_bit((void *) proc_alloc_map, PROC_NDYNAMIC);
-	if (i<0 || i>=PROC_NDYNAMIC) 
-		return -1;
-	set_bit(i, (void *) proc_alloc_map);
-	return PROC_DYNAMIC_FIRST + i;
-}
-
 int proc_register_dynamic(struct proc_dir_entry * dir,
 			  struct proc_dir_entry * dp)
 {
-	int i = make_inode_number();
-	if (i < 0)
-		return -EAGAIN;
-	dp->low_ino = i;
-	dp->next = dir->subdir;
-	dp->parent = dir;
-	dir->subdir = dp;
-	if (S_ISDIR(dp->mode))
-		dir->nlink++;
-	return 0;
+	/*
+	 * Make sure we use a dynamically allocated inode.
+	 * In the future, all procedures should just call
+	 * proc_register....
+	 */
+	dp->low_ino = 0;
+	return proc_register(dir, dp);
 }
 
 /*
@@ -404,38 +420,46 @@
 static struct proc_dir_entry proc_root_loadavg = {
 	PROC_LOADAVG, 7, "loadavg",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_uptime = {
 	PROC_UPTIME, 6, "uptime",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_meminfo = {
 	PROC_MEMINFO, 7, "meminfo",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_kmsg = {
 	PROC_KMSG, 4, "kmsg",
 	S_IFREG | S_IRUSR, 1, 0, 0,
+	0, &proc_kmsg_inode_operations
 };
 static struct proc_dir_entry proc_root_version = {
 	PROC_VERSION, 7, "version",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 #ifdef CONFIG_PCI
 static struct proc_dir_entry proc_root_pci = {
 	PROC_PCI, 3, "pci",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 #endif
 #ifdef CONFIG_ZORRO
 static struct proc_dir_entry proc_root_zorro = {
 	PROC_ZORRO, 5, "zorro",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 #endif
 static struct proc_dir_entry proc_root_cpuinfo = {
 	PROC_CPUINFO, 7, "cpuinfo",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_self = {
 	PROC_SELF, 4, "self",
@@ -446,81 +470,99 @@
 static struct proc_dir_entry proc_root_malloc = {
 	PROC_MALLOC, 6, "malloc",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 #endif
 static struct proc_dir_entry proc_root_kcore = {
 	PROC_KCORE, 5, "kcore",
 	S_IFREG | S_IRUSR, 1, 0, 0,
+	0, &proc_kcore_inode_operations
 };
 #ifdef CONFIG_MODULES
 static struct proc_dir_entry proc_root_modules = {
 	PROC_MODULES, 7, "modules",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_ksyms = {
 	PROC_KSYMS, 5, "ksyms",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 #endif
 static struct proc_dir_entry proc_root_stat = {
 	PROC_STAT, 4, "stat",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_devices = {
 	PROC_DEVICES, 7, "devices",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_interrupts = {
 	PROC_INTERRUPTS, 10,"interrupts",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 #ifdef __SMP_PROF__
 static struct proc_dir_entry proc_root_smp = {
 	PROC_SMP_PROF, 3,"smp",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 #endif
 static struct proc_dir_entry proc_root_filesystems = {
 	PROC_FILESYSTEMS, 11,"filesystems",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_dma = {
 	PROC_DMA, 3, "dma",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_ioports = {
 	PROC_IOPORTS, 7, "ioports",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_cmdline = {
 	PROC_CMDLINE, 7, "cmdline",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 #ifdef CONFIG_RTC
 static struct proc_dir_entry proc_root_rtc = {
 	PROC_RTC, 3, "rtc",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 #endif
 static struct proc_dir_entry proc_root_locks = {
 	PROC_LOCKS, 5, "locks",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_mounts = {
 	PROC_MTAB, 6, "mounts",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_swaps = {
 	PROC_SWAP, 5, "swaps",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 static struct proc_dir_entry proc_root_profile = {
 	PROC_PROFILE, 7, "profile",
 	S_IFREG | S_IRUGO | S_IWUSR, 1, 0, 0,
+	0, &proc_profile_inode_operations
 };
 static struct proc_dir_entry proc_root_slab = {
 	PROC_SLABINFO, 8, "slabinfo",
 	S_IFREG | S_IRUGO, 1, 0, 0,
+	0, &proc_array_inode_operations
 };
 
 void proc_root_init(void)
@@ -555,6 +597,7 @@
 	proc_register(&proc_root, &proc_root_malloc);
 #endif
 	proc_register(&proc_root, &proc_root_kcore);
+	proc_root_kcore.size = (MAP_NR(high_memory) << PAGE_SHIFT) + PAGE_SIZE;
 
 #ifdef CONFIG_MODULES
 	proc_register(&proc_root, &proc_root_modules);
@@ -589,7 +632,10 @@
 
 	if (prof_shift) {
 		proc_register(&proc_root, &proc_root_profile);
+		proc_root_profile.size = (1+prof_len) * sizeof(unsigned long);
 	}
+
+	proc_tty_init();
 }
 
 
@@ -666,8 +712,17 @@
 {
 	unsigned int pid, c;
 	int i, ino, retval;
+	struct task_struct *p;
 
 	dir->i_count++;
+
+	if (dir->i_ino == PROC_ROOT_INO) { /* check for safety... */
+		dir->i_nlink = proc_root.nlink;
+		for_each_task(p)
+			if (p && p->pid)
+				dir->i_nlink++;
+	}
+
 	retval = proc_lookup(dir, name, len, result);
 	if (retval != -ENOENT) {
 		iput(dir);
diff -u --recursive --new-file v2.1.27/linux/include/asm-alpha/checksum.h linux/include/asm-alpha/checksum.h
--- v2.1.27/linux/include/asm-alpha/checksum.h	Thu Feb  6 02:44:41 1997
+++ linux/include/asm-alpha/checksum.h	Thu Feb 27 14:15:47 1997
@@ -53,7 +53,7 @@
  * this is a new version of the above that records errors it finds in *errp,
  * but continues and zeros the rest of the buffer.
  */
-unsigned int csum_partial_copy_from_user(int *errp, char *src, char *dst, int len, unsigned int sum);
+unsigned int csum_partial_copy_from_user(char *src, char *dst, int len, unsigned int sum, int *errp);
 
 /*
  * this routine is used for miscellaneous IP-like checksums, mainly
diff -u --recursive --new-file v2.1.27/linux/include/asm-i386/termbits.h linux/include/asm-i386/termbits.h
--- v2.1.27/linux/include/asm-i386/termbits.h	Thu Feb  6 02:53:33 1997
+++ linux/include/asm-i386/termbits.h	Fri Feb 28 15:00:53 1997
@@ -122,6 +122,7 @@
 #define  B230400 0010003
 #define  B460800 0010004
 #define CIBAUD	  002003600000	/* input baud rate (not used) */
+#define CMSPAR	  010000000000		/* mark or space (stick) parity */
 #define CRTSCTS	  020000000000		/* flow control */
 
 /* c_lflag bits */
diff -u --recursive --new-file v2.1.27/linux/include/asm-i386/termios.h linux/include/asm-i386/termios.h
--- v2.1.27/linux/include/asm-i386/termios.h	Thu Feb  6 02:53:33 1997
+++ linux/include/asm-i386/termios.h	Fri Feb 28 15:00:53 1997
@@ -33,6 +33,8 @@
 #define TIOCM_DSR	0x100
 #define TIOCM_CD	TIOCM_CAR
 #define TIOCM_RI	TIOCM_RNG
+#define TIOCM_OUT1	0x2000
+#define TIOCM_OUT2	0x4000
 
 /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
 
diff -u --recursive --new-file v2.1.27/linux/include/linux/awe_voice.h linux/include/linux/awe_voice.h
--- v2.1.27/linux/include/linux/awe_voice.h	Wed Dec 31 16:00:00 1969
+++ linux/include/linux/awe_voice.h	Wed Jan 29 00:54:12 1997
@@ -0,0 +1,338 @@
+/*
+ * sound/awe_voice.h
+ *
+ * Voice information definitions for the low level driver for the 
+ * AWE32/Sound Blaster 32 wave table synth.
+ *   version 0.3.1b; Jan. 21, 1997
+ *
+ * Copyright (C) 1996,1997 Takashi Iwai
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef AWE_VOICE_H
+#define AWE_VOICE_H
+
+#ifndef SAMPLE_TYPE_AWE32
+#define SAMPLE_TYPE_AWE32	0x20
+#endif
+
+#ifndef _PATCHKEY
+#define _PATCHKEY(id) ((id<<8)|0xfd)
+#endif
+
+/*----------------------------------------------------------------
+ * patch information record
+ *----------------------------------------------------------------*/
+
+/* patch interface header: 16 bytes */
+typedef struct awe_patch_info {
+	short key;			/* use AWE_PATCH here */
+#define AWE_PATCH	_PATCHKEY(0x07)
+
+	short device_no;		/* synthesizer number */
+	unsigned short sf_id;		/* file id (should be zero) */
+	short sf_version;		/* patch version (not referred) */
+	long len;			/* data length (without this header) */
+
+	short type;			/* following data type */
+#define AWE_LOAD_INFO		0
+#define AWE_LOAD_DATA		1
+#define AWE_APPEND_DATA		0x00
+#define AWE_REPLACE_DATA	0x80
+
+	short reserved;			/* word alignment data */
+
+	/* the actual patch data begins after this */
+#if defined(AWE_COMPAT_030) && AWE_COMPAT_030
+	char data[0];
+#endif
+} awe_patch_info;
+
+#define AWE_PATCH_INFO_SIZE	16
+
+
+/*----------------------------------------------------------------
+ * raw voice information record
+ *----------------------------------------------------------------*/
+
+/* wave table envelope & effect parameters to control EMU8000 */
+typedef struct _awe_voice_parm {
+	unsigned short moddelay;	/* modulation delay (0x8000) */
+	unsigned short modatkhld;	/* modulation attack & hold time (0x7f7f) */
+	unsigned short moddcysus;	/* modulation decay & sustain (0x7f7f) */
+	unsigned short modrelease;	/* modulation release time (0x807f) */
+	short modkeyhold, modkeydecay;	/* envelope change per key (not used) */
+	unsigned short voldelay;	/* volume delay (0x8000) */
+	unsigned short volatkhld;	/* volume attack & hold time (0x7f7f) */
+	unsigned short voldcysus;	/* volume decay & sustain (0x7f7f) */
+	unsigned short volrelease;	/* volume release time (0x807f) */
+	short volkeyhold, volkeydecay;	/* envelope change per key (not used) */
+	unsigned short lfo1delay;	/* LFO1 delay (0x8000) */
+	unsigned short lfo2delay;	/* LFO2 delay (0x8000) */
+	unsigned short pefe;		/* modulation pitch & cutoff (0x0000) */
+	unsigned short fmmod;		/* LFO1 pitch & cutoff (0x0000) */
+	unsigned short tremfrq;		/* LFO1 volume & freq (0x0000) */
+	unsigned short fm2frq2;		/* LFO2 pitch & freq (0x0000) */
+	unsigned char cutoff;		/* initial cutoff (0xff) */
+	unsigned char filterQ;		/* initial filter Q [0-15] (0x0) */
+	unsigned char chorus;		/* chorus send (0x00) */
+	unsigned char reverb;		/* reverb send (0x00) */
+	unsigned short reserved[4];	/* not used */
+} awe_voice_parm;
+
+#define AWE_VOICE_PARM_SIZE	48
+
+
+/* wave table parameters: 92 bytes */
+typedef struct _awe_voice_info {
+	unsigned short sf_id;		/* file id (should be zero) */
+	unsigned short sample;		/* sample id */
+	long start, end;		/* sample offset correction */
+	long loopstart, loopend;	/* loop offset correction */
+	short rate_offset;		/* sample rate pitch offset */
+	unsigned short mode;		/* sample mode */
+#define AWE_MODE_ROMSOUND		0x8000
+#define AWE_MODE_STEREO			1
+#define AWE_MODE_LOOPING		2
+#define AWE_MODE_NORELEASE		4	/* obsolete */
+#define AWE_MODE_INIT_PARM		8
+
+	short root;			/* midi root key */
+	short tune;			/* pitch tuning (in cents) */
+	char low, high;			/* key note range */
+	char vellow, velhigh;		/* velocity range */
+	char fixkey, fixvel;		/* fixed key, velocity */
+	char pan, fixpan;		/* panning, fixed panning */
+	short exclusiveClass;		/* exclusive class (0 = none) */
+	unsigned char amplitude;	/* sample volume (127 max) */
+	unsigned char attenuation;	/* attenuation (0.375dB) */
+	short scaleTuning;		/* pitch scale tuning(%), normally 100 */
+	awe_voice_parm parm;		/* voice envelope parameters */
+	short index;			/* internal index (set by driver) */
+} awe_voice_info;
+
+#define AWE_VOICE_INFO_SIZE	92
+
+/*----------------------------------------------------------------*/
+
+/* The info entry of awe_voice_rec is changed from 0 to 1
+ * for some compilers refusing zero size array.
+ * Due to this change, sizeof(awe_voice_rec) becomes different
+ * from older versions.
+ * Use AWE_VOICE_REC_SIZE instead.
+ */
+#if defined(AWE_COMPAT_030) && AWE_COMPAT_030
+#define AWE_INFOARRAY_SIZE	0
+#else
+#define AWE_INFOARRAY_SIZE	1
+#endif
+
+/* instrument info header: 4 bytes */
+typedef struct _awe_voice_rec {
+	unsigned char bank;		/* midi bank number */
+	unsigned char instr;		/* midi preset number */
+	short nvoices;			/* number of voices */
+
+	/* voice information follows here */
+	awe_voice_info info[AWE_INFOARRAY_SIZE];
+} awe_voice_rec;
+
+#define AWE_VOICE_REC_SIZE	4
+
+
+/*----------------------------------------------------------------
+ * sample wave information
+ *----------------------------------------------------------------*/
+
+/* wave table sample header: 32 bytes */
+typedef struct awe_sample_info {
+	unsigned short sf_id;		/* file id (should be zero) */
+	unsigned short sample;		/* sample id */
+	long start, end;		/* start & end offset */
+	long loopstart, loopend;	/* loop start & end offset */
+	long size;			/* size (0 = ROM) */
+	short checksum_flag;		/* use check sum = 1 */
+	unsigned short mode_flags;	/* mode flags */
+#define AWE_SAMPLE_8BITS	1	/* wave data is 8bits */
+#define AWE_SAMPLE_UNSIGNED	2	/* wave data is unsigned */
+#define AWE_SAMPLE_NO_BLANK	4	/* no blank loop is attached */
+#define AWE_SAMPLE_SINGLESHOT	8	/* single-shot w/o loop */
+#define AWE_SAMPLE_BIDIR_LOOP	16	/* bidirectional looping */
+#define AWE_SAMPLE_STEREO_LEFT	32	/* stereo left sound */
+#define AWE_SAMPLE_STEREO_RIGHT	64	/* stereo right sound */
+#define AWE_SAMPLE_REVERSE_LOOP 128	/* reverse looping */
+	unsigned long checksum;		/* check sum */
+#if defined(AWE_COMPAT_030) && AWE_COMPAT_030
+	unsigned short data[0];		/* sample data follows here */
+#endif
+} awe_sample_info;
+
+#define AWE_SAMPLE_INFO_SIZE	32
+
+
+/*----------------------------------------------------------------
+ * awe hardware controls
+ *----------------------------------------------------------------*/
+
+typedef struct _awe_mode_rec {
+	int base_addr;
+	long mem_size; /* word size */
+	int max_voices, max_infos, max_samples;
+	unsigned short current_sf_id;
+	long free_mem; /* word offset */
+	int free_info;
+	int free_sample;
+	short reverb_mode;
+	short chorus_mode;
+	unsigned short init_atten;
+	short channel_mode;
+	short gus_bank;
+	short exclusive_sound;
+	unsigned long drum_flags;
+	int debug_mode;
+} awe_mode_rec;
+
+#define _AWE_DEBUG_MODE			0x00
+#define _AWE_REVERB_MODE		0x01
+#define _AWE_CHORUS_MODE		0x02
+#define _AWE_REMOVE_LAST_SAMPLES	0x03
+#define _AWE_INITIALIZE_CHIP		0x04
+#define _AWE_SEND_EFFECT		0x05
+#define _AWE_TERMINATE_CHANNEL		0x06
+#define _AWE_TERMINATE_ALL		0x07
+#define _AWE_INITIAL_VOLUME		0x08
+#define _AWE_SET_GUS_BANK		0x09
+#define _AWE_CHANNEL_MODE		0x0a	/* v0.3 features */
+#define _AWE_DRUM_CHANNELS		0x0b	/* v0.3 features */
+#define _AWE_EXCLUSIVE_SOUND		0x0c	/* v0.3 features */
+#define _AWE_INITIAL_ATTEN	_AWE_INITIAL_VOLUME
+#define _AWE_NOTEOFF_ALL		0x0e
+#define _AWE_GET_CURRENT_MODE		0x10	/* v0.3 features */
+
+#define _AWE_MODE_FLAG			0x80
+#define _AWE_COOKED_FLAG		0x40	/* not supported */
+#define _AWE_MODE_VALUE_MASK		0x3F
+
+#define _AWE_CMD(chn, voice, cmd, p1, p2) \
+{_SEQ_NEEDBUF(8); _seqbuf[_seqbufptr] = SEQ_PRIVATE;\
+ _seqbuf[_seqbufptr+1] = chn;\
+ _seqbuf[_seqbufptr+2] = _AWE_MODE_FLAG|(cmd);\
+ _seqbuf[_seqbufptr+3] = voice;\
+ *(unsigned short*)&_seqbuf[_seqbufptr+4] = p1;\
+ *(unsigned short*)&_seqbuf[_seqbufptr+6] = p2;\
+ _SEQ_ADVBUF(8);}
+
+#define AWE_DEBUG_MODE(dev,p1)	_AWE_CMD(dev, 0, _AWE_DEBUG_MODE, p1, 0)
+#define AWE_REVERB_MODE(dev,p1)	_AWE_CMD(dev, 0, _AWE_REVERB_MODE, p1, 0)
+#define AWE_CHORUS_MODE(dev,p1)	_AWE_CMD(dev, 0, _AWE_CHORUS_MODE, p1, 0)
+#define AWE_REMOVE_LAST_SAMPLES(dev) _AWE_CMD(dev, 0, _AWE_REMOVE_LAST_SAMPLES, 0, 0)
+#define AWE_INITIALIZE_CHIP(dev) _AWE_CMD(dev, 0, _AWE_INITIALIZE_CHIP, 0, 0)
+#define AWE_SEND_EFFECT(dev,voice,type,value) _AWE_CMD(dev,voice,_AWE_SEND_EFFECT,type,value)
+#define AWE_TERMINATE_CHANNEL(dev,voice) _AWE_CMD(dev,voice,_AWE_TERMINATE_CHANNEL,0,0)
+#define AWE_TERMINATE_ALL(dev) _AWE_CMD(dev, 0, _AWE_TERMINATE_ALL, 0, 0)
+#define AWE_NOTEOFF_ALL(dev) _AWE_CMD(dev, 0, _AWE_NOTEOFF_ALL, 0, 0)
+#define AWE_INITIAL_VOLUME(dev,atten) _AWE_CMD(dev, 0, _AWE_INITIAL_VOLUME, atten, 0)
+#define AWE_INITIAL_ATTEN  AWE_INITIAL_VOLUME
+#define AWE_SET_GUS_BANK(dev,bank) _AWE_CMD(dev, 0, _AWE_SET_GUS_BANK, bank, 0)
+#define AWE_SET_CHANNEL_MODE(dev,mode) _AWE_CMD(dev, 0, _AWE_CHANNEL_MODE, mode, 0)
+#define AWE_DRUM_CHANNELS(dev,channels) _AWE_CMD(dev, 0, _AWE_DRUM_CHANNELS, channels, 0)
+#define AWE_EXCLUSIVE_SOUND(dev,mode) _AWE_CMD(dev, 0, _AWE_EXCLUSIVE_SOUND, mode, 0)
+
+/* it must be direct access */
+#define AWE_GET_CURRENT_MODE(dev,addr) \
+{char tmpbuf[8];\
+ tmpbuf[0] = SEQ_PRIVATE; tmpbuf[1] = dev;\
+ tmpbuf[2] = _AWE_MODE_FLAG|_AWE_GET_CURRENT_MODE;\
+ tmpbuf[3] = 0; *(awe_mode_rec**)(tmpbuf +4) = (awe_mode_rec*)(addr);\
+ write(seqfd, tmpbuf, 8);}
+
+/* extended pressure controls; not portable with other sound drivers */
+#define AWE_KEY_PRESSURE(dev,ch,note,vel) SEQ_START_NOTE(dev,ch,(note)+128,vel)
+#define AWE_CHN_PRESSURE(dev,ch,vel) SEQ_START_NOTE(dev,(ch)+128,0,vel)
+
+/* reverb mode */
+#define	AWE_REVERB_ROOM1	0
+#define AWE_REVERB_ROOM2	1
+#define	AWE_REVERB_ROOM3	2
+#define	AWE_REVERB_HALL1	3
+#define	AWE_REVERB_HALL2	4
+#define	AWE_REVERB_PLATE	5
+#define	AWE_REVERB_DELAY	6
+#define	AWE_REVERB_PANNINGDELAY 7
+
+/* chorus mode */
+#define AWE_CHORUS_1		0
+#define	AWE_CHORUS_2		1
+#define	AWE_CHORUS_3		2
+#define	AWE_CHORUS_4		3
+#define	AWE_CHORUS_FEEDBACK	4
+#define	AWE_CHORUS_FLANGER	5
+#define	AWE_CHORUS_SHORTDELAY	6
+#define	AWE_CHORUS_SHORTDELAY2	7
+
+/* effects */
+enum {
+
+/* modulation envelope parameters */
+/* 0*/	AWE_FX_ENV1_DELAY,	/* WORD: ENVVAL */
+/* 1*/	AWE_FX_ENV1_ATTACK,	/* BYTE: up ATKHLD */
+/* 2*/	AWE_FX_ENV1_HOLD,	/* BYTE: lw ATKHLD */
+/* 3*/	AWE_FX_ENV1_DECAY,	/* BYTE: lw DCYSUS */
+/* 4*/	AWE_FX_ENV1_RELEASE,	/* BYTE: lw DCYSUS */
+/* 5*/	AWE_FX_ENV1_SUSTAIN,	/* BYTE: up DCYSUS */
+/* 6*/	AWE_FX_ENV1_PITCH,	/* BYTE: up PEFE */
+/* 7*/	AWE_FX_ENV1_CUTOFF,	/* BYTE: lw PEFE */
+
+/* volume envelope parameters */
+/* 8*/	AWE_FX_ENV2_DELAY,	/* WORD: ENVVOL */
+/* 9*/	AWE_FX_ENV2_ATTACK,	/* BYTE: up ATKHLDV */
+/*10*/	AWE_FX_ENV2_HOLD,	/* BYTE: lw ATKHLDV */
+/*11*/	AWE_FX_ENV2_DECAY,	/* BYTE: lw DCYSUSV */
+/*12*/	AWE_FX_ENV2_RELEASE,	/* BYTE: lw DCYSUSV */
+/*13*/	AWE_FX_ENV2_SUSTAIN,	/* BYTE: up DCYSUSV */
+	
+/* LFO1 (tremolo & vibrato) parameters */
+/*14*/	AWE_FX_LFO1_DELAY,	/* WORD: LFO1VAL */
+/*15*/	AWE_FX_LFO1_FREQ,	/* BYTE: lo TREMFRQ */
+/*16*/	AWE_FX_LFO1_VOLUME,	/* BYTE: up TREMFRQ */
+/*17*/	AWE_FX_LFO1_PITCH,	/* BYTE: up FMMOD */
+/*18*/	AWE_FX_LFO1_CUTOFF,	/* BYTE: lo FMMOD */
+
+/* LFO2 (vibrato) parameters */
+/*19*/	AWE_FX_LFO2_DELAY,	/* WORD: LFO2VAL */
+/*20*/	AWE_FX_LFO2_FREQ,	/* BYTE: lo FM2FRQ2 */
+/*21*/	AWE_FX_LFO2_PITCH,	/* BYTE: up FM2FRQ2 */
+
+/* Other overall effect parameters */
+/*22*/	AWE_FX_INIT_PITCH,	/* SHORT: pitch offset */
+/*23*/	AWE_FX_CHORUS,		/* BYTE: chorus effects send (0-255) */
+/*24*/	AWE_FX_REVERB,		/* BYTE: reverb effects send (0-255) */
+/*25*/	AWE_FX_CUTOFF,		/* BYTE: up IFATN */
+/*26*/	AWE_FX_FILTERQ,		/* BYTE: up CCCA */
+
+/* Sample / loop offset changes */
+/*27*/	AWE_FX_SAMPLE_START,	/* SHORT: offset */
+/*28*/	AWE_FX_LOOP_START,	/* SHORT: offset */
+/*29*/	AWE_FX_LOOP_END,	/* SHORT: offset */
+/*30*/	AWE_FX_COARSE_SAMPLE_START,	/* SHORT: upper word offset */
+/*31*/	AWE_FX_COARSE_LOOP_START,	/* SHORT: upper word offset */
+/*32*/	AWE_FX_COARSE_LOOP_END,		/* SHORT: upper word offset */
+
+	AWE_FX_END,
+};
+
+
+#endif /* AWE_VOICE_H */
diff -u --recursive --new-file v2.1.27/linux/include/linux/proc_fs.h linux/include/linux/proc_fs.h
--- v2.1.27/linux/include/linux/proc_fs.h	Fri Feb  7 05:54:55 1997
+++ linux/include/linux/proc_fs.h	Mon Mar  3 13:02:59 1997
@@ -223,6 +223,10 @@
 	void (*fill_inode)(struct inode *);
 	struct proc_dir_entry *next, *parent, *subdir;
 	void *data;
+	int (*read_proc)(char *page, char **start, off_t off,
+			 int count, void *data);
+	int (*write_proc)(struct file *file, const char *buffer,
+			  unsigned long count, void *data);
 };
 
 extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
@@ -327,6 +331,7 @@
 extern int proc_openprom_unregdev(struct openpromfs_dev *);
   
 extern struct inode_operations proc_dir_inode_operations;
+extern struct inode_operations proc_file_inode_operations;
 extern struct inode_operations proc_net_inode_operations;
 extern struct inode_operations proc_netdir_inode_operations;
 extern struct inode_operations proc_scsi_inode_operations;
@@ -344,3 +349,16 @@
 extern struct inode_operations proc_ringbuf_inode_operations;
 #endif
 #endif
+
+/*
+ * generic.c
+ */
+struct proc_dir_entry *create_proc_entry(const char *name, mode_t mode,
+					 struct proc_dir_entry *parent);
+
+/*
+ * proc_tty.c
+ */
+extern void proc_tty_init(void);
+extern void proc_tty_register_driver(struct tty_driver *driver);
+extern void proc_tty_unregister_driver(struct tty_driver *driver);
diff -u --recursive --new-file v2.1.27/linux/include/linux/serial.h linux/include/linux/serial.h
--- v2.1.27/linux/include/linux/serial.h	Thu Feb  6 02:53:43 1997
+++ linux/include/linux/serial.h	Fri Feb 28 15:40:33 1997
@@ -123,7 +123,10 @@
  */
 struct serial_icounter_struct {
 	int cts, dsr, rng, dcd;
-	int reserved[16];
+	int rx, tx;
+	int frame, overrun, parity, brk;
+	int buf_overrun;
+	int reserved[9];
 };
 
 
@@ -144,7 +147,9 @@
  * Counters of the input lines (CTS, DSR, RI, CD) interrupts
  */
 struct async_icount {
-	__u32	cts, dsr, rng, dcd;	
+	__u32	cts, dsr, rng, dcd, tx, rx;
+	__u32	frame, parity, overrun, brk;
+	__u32	buf_overrun;
 };
 
 struct serial_state {
@@ -178,6 +183,7 @@
 	int			read_status_mask;
 	int			ignore_status_mask;
 	int			timeout;
+	int			quot;
 	int			x_char;	/* xon/xoff character */
 	int			close_delay;
 	unsigned short		closing_wait;
@@ -235,5 +241,6 @@
 /* Export to allow PCMCIA to use this - Dave Hinds */
 extern int register_serial(struct serial_struct *req);
 extern void unregister_serial(int line);
+
 #endif /* __KERNEL__ */
 #endif /* _LINUX_SERIAL_H */
diff -u --recursive --new-file v2.1.27/linux/include/linux/soundcard.h linux/include/linux/soundcard.h
--- v2.1.27/linux/include/linux/soundcard.h	Fri Nov 15 00:14:56 1996
+++ linux/include/linux/soundcard.h	Wed Feb 26 02:34:50 1997
@@ -1,7 +1,7 @@
 #ifndef SOUNDCARD_H
 #define SOUNDCARD_H
 /*
- * Copyright by Hannu Savolainen 1993-1996
+ * Copyright by Hannu Savolainen 1993-1997
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are
@@ -33,7 +33,7 @@
  * Use ioctl(fd, OSS_GETVERSION, &int) to get the version number of
  * the currently active driver.
  */
-#define SOUND_VERSION	0x030700
+#define SOUND_VERSION	0x0307f1
 #define OPEN_SOUND_SYSTEM
 
 /* In Linux we need to be prepared for cross compiling */
@@ -76,13 +76,18 @@
  */
 
 #ifndef _SIOWR
-#if defined(_IOWR) && !defined(sun)
+#if defined(_IOWR) && !defined(sun) && !defined(sparc)
 /* Use already defined ioctl defines if they exist (except with Sun) */
 #define	SIOCPARM_MASK	IOCPARM_MASK
 #define	SIOC_VOID	IOC_VOID
 #define	SIOC_OUT	IOC_OUT
 #define	SIOC_IN		IOC_IN
 #define	SIOC_INOUT	IOC_INOUT
+#define _SIOC_SIZE	_IOC_SIZE
+#define _SIOC_DIR	_IOC_DIR
+#define _SIOC_NONE	_IOC_NONE
+#define _SIOC_READ	_IOC_READ
+#define _SIOC_WRITE	_IOC_WRITE
 #define	_SIO		_IO
 #define	_SIOR		_IOR
 #define	_SIOW		_IOW
@@ -93,7 +98,7 @@
  * and the size of any in or out parameters in the upper
  * word.  The high 2 bits of the upper word are used
  * to encode the in/out status of the parameter; for now
- * we restrict parameters to at most 128 bytes.
+ * we restrict parameters to at most 8191 bytes.
  */
 /* #define	SIOCTYPE		(0xff<<8) */
 #define	SIOCPARM_MASK	0x1fff		/* parameters must be < 8192 bytes */
@@ -107,6 +112,11 @@
 #define	_SIOW(x,y,t)	((int)(SIOC_IN|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y))
 /* this should be _SIORW, but stdio got there first */
 #define	_SIOWR(x,y,t)	((int)(SIOC_INOUT|((sizeof(t)&SIOCPARM_MASK)<<16)|(x<<8)|y))
+#define _SIOC_SIZE(x)	((x>>16)&SIOCPARM_MASK)	
+#define _SIOC_DIR(x)	(x & 0xf0000000)
+#define _SIOC_NONE	SIOC_VOID
+#define _SIOC_READ	SIOC_OUT
+#define _SIOC_WRITE	SIOC_IN
 #  endif /* _IOWR */
 #endif  /* !_SIOWR */
 
@@ -129,6 +139,7 @@
 #define SNDCTL_SEQ_PANIC		_SIO  ('Q',17)
 #define SNDCTL_SEQ_OUTOFBAND		_SIOW ('Q',18, struct seq_event_rec)
 #define SNDCTL_SEQ_GETTIME		_SIOR ('Q',19, int)
+#define SNDCTL_SYNTH_ID			_SIOWR('Q',20, struct synth_info)
 
 	struct seq_event_rec {
 			unsigned char arr[8];
@@ -166,13 +177,13 @@
  *	Sample loading mechanism for internal synthesizers (/dev/sequencer)
  *	The following patch_info structure has been designed to support
  *	Gravis UltraSound. It tries to be universal format for uploading
- *	sample based patches but is propably too limited.
+ *	sample based patches but is probably too limited.
  */
 
 struct patch_info {
-		unsigned short key;		/* Use GUS_PATCH here */
-#define GUS_PATCH	_PATCHKEY(0x04)
-#define OBSOLETE_GUS_PATCH	_PATCHKEY(0x02)
+		unsigned short key;		/* Use WAVE_PATCH here */
+#define WAVE_PATCH	_PATCHKEY(0x04)
+#define GUS_PATCH	WAVE_PATCH
 
 		short device_no;	/* Synthesizer number */
 		short instr_no;		/* Midi pgm# */
@@ -189,6 +200,7 @@
 #define WAVE_LOOP_BACK	0x10	/* bit 4 = Set is looping backward. */
 #define WAVE_SUSTAIN_ON	0x20	/* bit 5 = Turn sustaining on. (Env. pts. 3)*/
 #define WAVE_ENVELOPES	0x40	/* bit 6 = Enable envelopes - 1 */
+#define WAVE_FAST_RELEASE 0x80	/* bit 7 = Shut off immediately after note off */
 				/* 	(use the env_rate/env_offs fields). */
 /* Linux specific bits */
 #define WAVE_VIBRATO	0x00010000	/* The vibrato info is valid */
@@ -257,7 +269,7 @@
 	};
 
 struct sysex_info {
-		short key;		/* Use GUS_PATCH here */
+		short key;		/* Use SYSEX_PATCH or MAUI_PATCH here */
 #define SYSEX_PATCH	_PATCHKEY(0x05)
 #define MAUI_PATCH	_PATCHKEY(0x06)
 		short device_no;	/* Synthesizer number */
@@ -413,8 +425,8 @@
  *	Set the key field of the structure to FM_PATCH. The device field is used to
  *	route the patch to the corresponding device.
  *
- *	For Gravis UltraSound use struct patch_info. Initialize the key field
- *      to GUS_PATCH.
+ *	For wave table use struct patch_info. Initialize the key field
+ *      to WAVE_PATCH.
  */
 #define SEQ_PRIVATE		0xfe	/* Low level HW dependent events (8 bytes) */
 #define SEQ_EXTENDED		0xff	/* Extended events (8 bytes) OBSOLETE */
@@ -447,7 +459,8 @@
 #define FM_TYPE_OPL3			0x01
 #define MIDI_TYPE_MPU401		0x401
 
-#define SAMPLE_TYPE_GUS			0x10
+#define SAMPLE_TYPE_BASIC		0x10
+#define SAMPLE_TYPE_GUS			SAMPLE_TYPE_BASIC
 
 		int	perc_mode;	/* No longer supported */
 		int	nr_voices;
@@ -572,6 +585,22 @@
 #define SNDCTL_DSP_SETSYNCRO		_SIO  ('P', 21)
 #define SNDCTL_DSP_SETDUPLEX		_SIO  ('P', 22)
 
+/*
+ * Application's profile defines the way how playback underrun situations should be handled.
+ * 
+ *	APF_NORMAL (the default) and APF_NETWORK make the driver to cleanup the
+ *	playback buffer whenever an underrun occurs. This consumes some time
+ *	preven's looping the existing buffer.
+ *	APF_CPUINTENS is intended to be set by CPU intensive applications which
+ *	are likely to run out of time occasionally. In this mode the buffer cleanup is
+ *	disabled which saves CPU time but also let's the previous buffer content to
+ *	be played during the "pause" after the underrun.
+ */
+#define SNDCTL_DSP_PROFILE		_SIOW ('P', 23, int)
+#define	  APF_NORMAL	0	/* Normal applications */
+#define	  APF_NETWORK	1	/* Underruns probably caused by an "external" delay */
+#define   APF_CPUINTENS 2	/* Underruns probably caused by "overheating" the CPU */
+
 #define SOUND_PCM_READ_RATE		_SIOR ('P', 2, int)
 #define SOUND_PCM_READ_CHANNELS		_SIOR ('P', 6, int)
 #define SOUND_PCM_READ_BITS		_SIOR ('P', 5, int)
@@ -928,10 +957,45 @@
  *
  *	These macros define the API which should be used when possible.
  */
+#define SEQ_DECLAREBUF()		SEQ_USE_EXTBUF()
 
-#ifndef USE_SIMPLE_MACROS
 void seqbuf_dump(void);	/* This function must be provided by programs */
 
+extern int OSS_init(int seqfd, int buflen);
+extern void OSS_seqbuf_dump(int fd, unsigned char *buf, int buflen);
+extern void OSS_seq_advbuf(int len, int fd, unsigned char *buf, int buflen);
+extern void OSS_seq_needbuf(int len, int fd, unsigned char *buf, int buflen);
+extern void OSS_patch_caching(int dev, int chn, int patch,
+			      int fd, unsigned char *buf, int buflen);
+extern void OSS_drum_caching(int dev, int chn, int patch,
+			      int fd, unsigned char *buf, int buflen);
+extern void OSS_write_patch(int fd, unsigned char *buf, int len);
+extern int OSS_write_patch2(int fd, unsigned char *buf, int len);
+
+#define SEQ_PM_DEFINES int __foo_bar___
+#ifdef OSSLIB
+#  define SEQ_USE_EXTBUF() \
+		extern unsigned char *_seqbuf; \
+		extern int _seqbuflen;extern int _seqbufptr
+#  define SEQ_DEFINEBUF(len) SEQ_USE_EXTBUF();static int _requested_seqbuflen=len
+#  define _SEQ_ADVBUF(len) OSS_seq_advbuf(len, seqfd, _seqbuf, _seqbuflen)
+#  define _SEQ_NEEDBUF(len) OSS_seq_needbuf(len, seqfd, _seqbuf, _seqbuflen)
+#  define SEQ_DUMPBUF() OSS_seqbuf_dump(seqfd, _seqbuf, _seqbuflen)
+
+#  define SEQ_LOAD_GMINSTR(dev, instr) \
+		OSS_patch_caching(dev, -1, instr, seqfd, _seqbuf, _seqbuflen)
+#  define SEQ_LOAD_GMDRUM(dev, drum) \
+		OSS_drum_caching(dev, -1, drum, seqfd, _seqbuf, _seqbuflen)
+#else /* !OSSLIB */
+
+#  define SEQ_LOAD_GMINSTR(dev, instr)
+#  define SEQ_LOAD_GMDRUM(dev, drum)
+
+#  define SEQ_USE_EXTBUF() \
+		extern unsigned char _seqbuf[]; \
+		extern int _seqbuflen;extern int _seqbufptr
+
+#ifndef USE_SIMPLE_MACROS
 /* Sample seqbuf_dump() implementation:
  *
  *	SEQ_DEFINEBUF (2048);	-- Defines a buffer for 2048 bytes
@@ -952,8 +1016,6 @@
  */
 
 #define SEQ_DEFINEBUF(len)		unsigned char _seqbuf[len]; int _seqbuflen = len;int _seqbufptr = 0
-#define SEQ_USE_EXTBUF()		extern unsigned char _seqbuf[]; extern int _seqbuflen;extern int _seqbufptr
-#define SEQ_DECLAREBUF()		SEQ_USE_EXTBUF()
 #define _SEQ_NEEDBUF(len)		if ((_seqbufptr+(len)) > _seqbuflen) seqbuf_dump()
 #define _SEQ_ADVBUF(len)		_seqbufptr += len
 #define SEQ_DUMPBUF			seqbuf_dump
@@ -974,6 +1036,7 @@
  */
 #define _SEQ_NEEDBUF(len)	/* empty */
 #endif
+#endif /* !OSSLIB */
 
 #define SEQ_VOLUME_MODE(dev, mode)	{_SEQ_NEEDBUF(8);\
 					_seqbuf[_seqbufptr] = SEQ_EXTENDED;\
@@ -1041,18 +1104,30 @@
  * cause fatal problems with some other devices (such as MPU401).
  */
 #define SEQ_SYSEX(dev, buf, len) \
-					{int i, l=(len); if (l>6)l=6;\
+					{int ii, ll=(len); \
+					 unsigned char *bufp=buf;\
+					 if (ll>6)ll=6;\
 					_SEQ_NEEDBUF(8);\
 					_seqbuf[_seqbufptr] = EV_SYSEX;\
-					for(i=0;i<l;i++)_seqbuf[_seqbufptr+i+1] = (buf)[i];\
-					for(i=l;i<6;i++)_seqbuf[_seqbufptr+i+1] = 0xff;\
+					_seqbuf[_seqbufptr+1] = (dev);\
+					for(ii=0;ii<ll;ii++)\
+					   _seqbuf[_seqbufptr+ii+2] = bufp[ii];\
+					for(ii=ll;ii<6;ii++)\
+					   _seqbuf[_seqbufptr+ii+2] = 0xff;\
 					_SEQ_ADVBUF(8);}
 
 #define SEQ_CHN_PRESSURE(dev, chn, pressure) \
 		_CHN_COMMON(dev, MIDI_CHN_PRESSURE, chn, pressure, 0, 0)
 
-#define SEQ_SET_PATCH(dev, chn, patch) \
+#define SEQ_SET_PATCH SEQ_PGM_CHANGE
+#ifdef OSSLIB
+#   define SEQ_PGM_CHANGE(dev, chn, patch) \
+		{OSS_patch_caching(dev, chn, patch, seqfd, _seqbuf, _seqbuflen); \
+		 _CHN_COMMON(dev, MIDI_PGM_CHANGE, chn, patch, 0, 0);}
+#else
+#   define SEQ_PGM_CHANGE(dev, chn, patch) \
 		_CHN_COMMON(dev, MIDI_PGM_CHANGE, chn, patch, 0, 0)
+#endif
 
 #define SEQ_CONTROL(dev, chn, controller, value) \
 		_CHN_COMMON(dev, MIDI_CTL_CHANGE, chn, controller, 0, value)
@@ -1130,10 +1205,19 @@
 /*
  * Patch loading.
  */
-#define SEQ_WRPATCH(patchx, len)		{if (_seqbufptr) seqbuf_dump();\
-					if (write(seqfd, (char*)(patchx), len)==-1) \
-					   perror("Write patch: /dev/sequencer");}
-#define SEQ_WRPATCH2(patchx, len)	(seqbuf_dump(), write(seqfd, (char*)(patchx), len))
+#ifdef OSSLIB
+#   define SEQ_WRPATCH(patchx, len) \
+		OSS_write_patch(seqfd, (char*)(patchx), len)
+#   define SEQ_WRPATCH2(patchx, len) \
+		OSS_write_patch2(seqfd, (char*)(patchx), len)
+#else
+#   define SEQ_WRPATCH(patchx, len) \
+		{if (_seqbufptr) SEQ_DUMPBUF();\
+		 if (write(seqfd, (char*)(patchx), len)==-1) \
+		    perror("Write patch: /dev/sequencer");}
+#   define SEQ_WRPATCH2(patchx, len) \
+		(SEQ_DUMPBUF(), write(seqfd, (char*)(patchx), len))
+#endif
 
 #endif
 #endif
diff -u --recursive --new-file v2.1.27/linux/include/linux/tty.h linux/include/linux/tty.h
--- v2.1.27/linux/include/linux/tty.h	Thu Feb  6 02:53:33 1997
+++ linux/include/linux/tty.h	Mon Mar  3 13:02:51 1997
@@ -328,13 +328,8 @@
 extern int n_tty_ioctl(struct tty_struct * tty, struct file * file,
 		       unsigned int cmd, unsigned long arg);
 
-/* serial.c */
+/* pcxx.c */
 
-extern int  rs_open(struct tty_struct * tty, struct file * filp);
-
-/* pty.c */
-
-extern int  pty_open(struct tty_struct * tty, struct file * filp);
 extern int pcxe_open(struct tty_struct *tty, struct file *filp);
 
 /* console.c */
diff -u --recursive --new-file v2.1.27/linux/include/linux/tty_driver.h linux/include/linux/tty_driver.h
--- v2.1.27/linux/include/linux/tty_driver.h	Thu Feb  6 02:53:33 1997
+++ linux/include/linux/tty_driver.h	Mon Mar  3 13:02:51 1997
@@ -107,6 +107,7 @@
 
 struct tty_driver {
 	int	magic;		/* magic number for this structure */
+	const char	*driver_name;
 	const char	*name;
 	int	name_base;	/* offset of printed name */
 	short	major;		/* major device number */
@@ -117,6 +118,7 @@
 	struct termios init_termios; /* Initial termios */
 	int	flags;		/* tty driver flags */
 	int	*refcount;	/* for loadable tty drivers */
+	struct proc_dir_entry *proc_entry; /* /proc fs entry */
 	struct tty_driver *other; /* only used for the PTY driver */
 
 	/*
@@ -150,6 +152,10 @@
 	void (*set_ldisc)(struct tty_struct *tty);
 	void (*wait_until_sent)(struct tty_struct *tty, int timeout);
 	void (*send_xchar)(struct tty_struct *tty, char ch);
+	int (*read_proc)(char *page, char **start, off_t off,
+			  int count, void *data);
+	int (*write_proc)(struct file *file, const char *buffer,
+			  unsigned long count, void *data);
 
 	/*
 	 * linked list pointers
@@ -196,5 +202,9 @@
 /* pty subtypes (magic, used by tty_io.c) */
 #define PTY_TYPE_MASTER			0x0001
 #define PTY_TYPE_SLAVE			0x0002
+
+/* serial subtype definitions */
+#define SERIAL_TYPE_NORMAL	1
+#define SERIAL_TYPE_CALLOUT	2
 
 #endif /* #ifdef _LINUX_TTY_DRIVER_H */
diff -u --recursive --new-file v2.1.27/linux/include/linux/tty_ldisc.h linux/include/linux/tty_ldisc.h
--- v2.1.27/linux/include/linux/tty_ldisc.h	Thu Feb  6 02:53:33 1997
+++ linux/include/linux/tty_ldisc.h	Mon Mar  3 13:02:51 1997
@@ -102,6 +102,7 @@
 
 struct tty_ldisc {
 	int	magic;
+	char	*name;
 	int	num;
 	int	flags;
 	/*
diff -u --recursive --new-file v2.1.27/linux/include/linux/ultrasound.h linux/include/linux/ultrasound.h
--- v2.1.27/linux/include/linux/ultrasound.h	Fri Nov 15 00:14:57 1996
+++ linux/include/linux/ultrasound.h	Wed Feb 26 02:34:50 1997
@@ -6,7 +6,7 @@
  *			and not portable.
  */
 /*
- * Copyright (C) by Hannu Savolainen 1993-1996
+ * Copyright (C) by Hannu Savolainen 1993-1997
  *
  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
  * Version 2 (June 1991). See the "COPYING" file distributed with this software
diff -u --recursive --new-file v2.1.27/linux/include/net/inet_common.h linux/include/net/inet_common.h
--- v2.1.27/linux/include/net/inet_common.h	Sat Jan 25 13:46:14 1997
+++ linux/include/net/inet_common.h	Mon Mar  3 09:37:44 1997
@@ -3,9 +3,6 @@
 
 extern struct proto_ops		inet_stream_ops;
 extern struct proto_ops		inet_dgram_ops;
-extern struct sock *		tcp_sock_array[SOCK_ARRAY_SIZE];
-extern struct sock *		udp_sock_array[SOCK_ARRAY_SIZE];
-
 
 /*
  *	INET4 prototypes used by INET6
diff -u --recursive --new-file v2.1.27/linux/include/net/raw.h linux/include/net/raw.h
--- v2.1.27/linux/include/net/raw.h	Thu Dec 12 06:54:21 1996
+++ linux/include/net/raw.h	Mon Mar  3 09:37:44 1997
@@ -21,7 +21,17 @@
 extern struct proto raw_prot;
 
 
-extern int 	raw_err(struct sock *, struct sk_buff *);
+extern void 	raw_err(struct sock *, struct sk_buff *);
 extern int 	raw_rcv(struct sock *, struct sk_buff *);
+
+/* Note: v4 ICMP wants to get at this stuff, if you change the
+ *       hashing mechanism, make sure you update icmp.c as well.
+ */
+#define RAWV4_HTABLE_SIZE	MAX_INET_PROTOS
+extern struct sock *raw_v4_htable[RAWV4_HTABLE_SIZE];
+
+
+extern struct sock *raw_v4_lookup(struct sock *sk, unsigned short num,
+				  unsigned long raddr, unsigned long laddr);
 
 #endif	/* _RAW_H */
diff -u --recursive --new-file v2.1.27/linux/include/net/rawv6.h linux/include/net/rawv6.h
--- v2.1.27/linux/include/net/rawv6.h	Sun Nov  3 01:04:42 1996
+++ linux/include/net/rawv6.h	Mon Mar  3 09:37:44 1997
@@ -2,6 +2,14 @@
 #define _NET_RAWV6_H
 
 #ifdef __KERNEL__
+
+#define RAWV6_HTABLE_SIZE	MAX_INET_PROTOS
+extern struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
+
+
+extern struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
+				  struct in6_addr *loc_addr, struct in6_addr *rmt_addr);
+
 extern int			rawv6_rcv(struct sk_buff *skb, 
 					  struct device *dev,
 					  struct in6_addr *saddr, 
diff -u --recursive --new-file v2.1.27/linux/include/net/sock.h linux/include/net/sock.h
--- v2.1.27/linux/include/net/sock.h	Thu Feb 27 10:57:31 1997
+++ linux/include/net/sock.h	Mon Mar  3 13:08:16 1997
@@ -22,6 +22,7 @@
  *		Alan Cox	:	New fields for options
  *	Pauline Middelink	:	identd support
  *		Alan Cox	:	Eliminate low level recv/recvfrom
+ *		David S. Miller	:	New socket lookup architecture.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -73,10 +74,6 @@
 
 #include <asm/atomic.h>
 
-/* Think big (also on some systems a byte is faster) */
-#define SOCK_ARRAY_SIZE	256
-
-
 /*
  *	The AF_UNIX specific socket options
  */
@@ -307,6 +304,10 @@
 
 struct sock 
 {
+	/* This must be first. */
+	struct sock		*sklist_next;
+	struct sock		*sklist_prev;
+
 	atomic_t		wmem_alloc;
 	atomic_t		rmem_alloc;
 	unsigned long		allocation;		/* Allocation mode */
@@ -341,8 +342,11 @@
 				bsdism;
 	unsigned long	        lingertime;
 	int			proc;
+
+	struct sock             **hashtable;
+	int			hashent;
 	struct sock		*next;
-	struct sock		*prev; /* Doubly linked chain.. */
+	struct sock		*prev;
 	struct sock		*pair;
 
 	struct sk_buff		* send_head;
@@ -528,6 +532,10 @@
  
 struct proto 
 {
+	/* These must be first. */
+	struct sock		*sklist_next;
+	struct sock		*sklist_prev;
+
 	void			(*close)(struct sock *sk, 
 					unsigned long timeout);
 	int			(*connect)(struct sock *sk,
@@ -562,11 +570,17 @@
 	int			(*backlog_rcv) (struct sock *sk, 
 						struct sk_buff *skb);
 
+	/* Keeping track of sk's, looking them up, and port selection methods. */
+	void			(*hash)(struct sock *sk);
+	void			(*unhash)(struct sock *sk);
+	void			(*rehash)(struct sock *sk);
+	unsigned short		(*good_socknum)(void);
+	int			(*verify_bind)(struct sock *sk, unsigned short snum);
+
 	unsigned short		max_header;
 	unsigned long		retransmits;
 	char			name[32];
 	int			inuse, highestinuse;
-	struct sock **		sock_array;
 };
 
 #define TIME_WRITE	1	/* Not yet used */
@@ -594,6 +608,46 @@
 #define RCV_SHUTDOWN	1
 #define SEND_SHUTDOWN	2
 
+/* Per-protocol hash table implementations use this to make sure
+ * nothing changes.
+ */
+#define SOCKHASH_LOCK()		start_bh_atomic()
+#define SOCKHASH_UNLOCK()	end_bh_atomic()
+
+/* Some things in the kernel just want to get at a protocols
+ * entire socket list commensurate, thus...
+ */
+static __inline__ void add_to_prot_sklist(struct sock *sk)
+{
+	SOCKHASH_LOCK();
+	if(!sk->sklist_next) {
+		struct proto *p = sk->prot;
+
+		sk->sklist_prev = (struct sock *) p;
+		sk->sklist_next = p->sklist_next;
+		p->sklist_next->sklist_prev = sk;
+		p->sklist_next = sk;
+
+		/* Charge the protocol. */
+		sk->prot->inuse += 1;
+		if(sk->prot->highestinuse < sk->prot->inuse)
+			sk->prot->highestinuse = sk->prot->inuse;
+	}
+	SOCKHASH_UNLOCK();
+}
+
+static __inline__ void del_from_prot_sklist(struct sock *sk)
+{
+	SOCKHASH_LOCK();
+	if(sk->sklist_next) {
+		sk->sklist_next->sklist_prev = sk->sklist_prev;
+		sk->sklist_prev->sklist_next = sk->sklist_next;
+		sk->sklist_next = NULL;
+		sk->prot->inuse--;
+	}
+	SOCKHASH_UNLOCK();
+}
+
 /*
  * Used by processes to "lock" a socket state, so that
  * interrupts and bottom half handlers won't change it
@@ -660,21 +714,6 @@
 extern struct sock *		sk_alloc(int priority);
 extern void			sk_free(struct sock *sk);
 extern void			destroy_sock(struct sock *sk);
-extern unsigned short		get_new_socknum(struct proto *,
-						unsigned short);
-extern void			inet_put_sock(unsigned short, struct sock *); 
-extern struct sock		*get_sock(struct proto *, unsigned short,
-					  unsigned long, unsigned short,
-					  unsigned long);
-extern struct sock		*get_sock_proxy(struct proto *, unsigned short,
-					  unsigned long, unsigned short,
-					  unsigned long,
-					  unsigned long, unsigned short);
-extern struct sock		*get_sock_mcast(struct sock *, unsigned short,
-					  unsigned long, unsigned short,
-					  unsigned long);
-extern struct sock		*get_sock_raw(struct sock *, unsigned short,
-					  unsigned long, unsigned long);
 
 extern struct sk_buff		*sock_wmalloc(struct sock *sk,
 					      unsigned long size, int force,
diff -u --recursive --new-file v2.1.27/linux/include/net/tcp.h linux/include/net/tcp.h
--- v2.1.27/linux/include/net/tcp.h	Thu Feb  6 02:59:02 1997
+++ linux/include/net/tcp.h	Mon Mar  3 13:23:47 1997
@@ -22,6 +22,73 @@
 #include <linux/tcp.h>
 #include <net/checksum.h>
 
+/* This is for all connections with a full identity, no wildcards. */
+#define TCP_HTABLE_SIZE		128
+
+/* This is for listening sockets, thus all sockets which possess wildcards. */
+#define TCP_LHTABLE_SIZE	16	/* Yes, really, this is all you need. */
+
+/* This is for all sockets, to keep track of the local port allocations. */
+#define TCP_BHTABLE_SIZE	64
+
+/* tcp_ipv4.c: These need to be shared by v4 and v6 because the lookup
+ *             and hashing code needs to work with different AF's yet
+ *             the port space is shared.
+ */
+extern struct sock *tcp_established_hash[TCP_HTABLE_SIZE];
+extern struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE];
+extern struct sock *tcp_bound_hash[TCP_BHTABLE_SIZE];
+
+/* These are AF independant. */
+static __inline__ int tcp_bhashfn(__u16 lport)
+{
+	return (lport ^ (lport >> 7)) & (TCP_BHTABLE_SIZE - 1);
+}
+
+static __inline__ int tcp_sk_bhashfn(struct sock *sk)
+{
+	__u16 lport = sk->num;
+	return tcp_bhashfn(lport);
+}
+
+/* These can have wildcards, don't try too hard.
+ * XXX deal with thousands of IP aliases for listening ports later
+ */
+static __inline__ int tcp_lhashfn(unsigned short num)
+{
+	return num & (TCP_LHTABLE_SIZE - 1);
+}
+
+static __inline__ int tcp_sk_listen_hashfn(struct sock *sk)
+{
+	return tcp_lhashfn(sk->num);
+}
+
+/* Only those holding the sockhash lock call these two things here.
+ * Note the slightly gross overloading of sk->prev, AF_UNIX is the
+ * only other main benefactor of that member of SK, so who cares.
+ */
+static __inline__ void tcp_sk_bindify(struct sock *sk)
+{
+	int hashent = tcp_sk_bhashfn(sk);
+
+	sk->prev = tcp_bound_hash[hashent];
+	tcp_bound_hash[hashent] = sk;
+}
+
+static __inline__ void tcp_sk_unbindify(struct sock *sk)
+{
+	int hashent = tcp_sk_bhashfn(sk);
+	struct sock **htable = &tcp_bound_hash[hashent];
+
+	while(*htable) {
+		if(*htable == sk) {
+			*htable = sk->prev;
+			break;
+		}
+		htable = &((*htable)->prev);
+	}
+}
 
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 #define NETHDR_SIZE	sizeof(struct ipv6hdr)
@@ -80,9 +147,9 @@
 #define TCP_PROBEWAIT_LEN (1*HZ)/* time to wait between probes when
 				 * I've got something to write and
 				 * there is no window			*/
-#define TCP_KEEPALIVE_TIME (180*60*HZ)	/* two hours */
-#define TCP_KEEPALIVE_PROBES	9	/* Max of 9 keepalive probes	*/
-#define TCP_KEEPALIVE_PERIOD (75*HZ)	/* period of keepalive check	*/
+#define TCP_KEEPALIVE_TIME (180*60*HZ)		/* two hours */
+#define TCP_KEEPALIVE_PROBES	9		/* Max of 9 keepalive probes	*/
+#define TCP_KEEPALIVE_PERIOD ((75*HZ)>>2)	/* period of keepalive check	*/
 #define TCP_NO_CHECK	0	/* turn to one if you want the default
 				 * to be no checksum			*/
 
@@ -236,6 +303,8 @@
 extern struct proto tcp_prot;
 extern struct tcp_mib tcp_statistics;
 
+extern unsigned short		tcp_good_socknum(void);
+
 extern void			tcp_v4_err(struct sk_buff *skb,
 					   unsigned char *);
 
@@ -325,9 +394,6 @@
 extern void tcp_send_ack(struct sock *sk);
 extern void tcp_send_delayed_ack(struct sock *sk, int max_timeout);
 
-/* tcp_input.c */
-extern void tcp_cache_zap(void);
-
 /* CONFIG_IP_TRANSPARENT_PROXY */
 extern int tcp_chkaddr(struct sk_buff *);
 
@@ -443,7 +509,6 @@
 		break;
 
 	case TCP_CLOSE:
-		tcp_cache_zap();
 		/* Should be about 2 rtt's */
 		net_reset_timer(sk, TIME_DONE, min(tp->srtt * 2, TCP_DONE_TIME));
 		/* fall through */
diff -u --recursive --new-file v2.1.27/linux/include/net/udp.h linux/include/net/udp.h
--- v2.1.27/linux/include/net/udp.h	Thu Dec 12 06:54:21 1996
+++ linux/include/net/udp.h	Mon Mar  3 09:37:44 1997
@@ -24,6 +24,15 @@
 
 #include <linux/udp.h>
 
+#define UDP_HTABLE_SIZE		128
+
+/* udp.c: This needs to be shared by v4 and v6 because the lookup
+ *        and hashing code needs to work with different AF's yet
+ *        the port space is shared.
+ */
+extern struct sock *udp_hash[UDP_HTABLE_SIZE];
+
+extern unsigned short udp_good_socknum(void);
 
 #define UDP_NO_CHECK	0
 
@@ -39,7 +48,6 @@
 
 extern int	udp_rcv(struct sk_buff *skb, unsigned short len);
 extern int	udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
-extern void	udp_cache_zap(void);	/* Remove udp last socket cache */
 
 /* CONFIG_IP_TRANSPARENT_PROXY */
 extern int	udp_chkaddr(struct sk_buff *skb);
diff -u --recursive --new-file v2.1.27/linux/kernel/sched.c linux/kernel/sched.c
--- v2.1.27/linux/kernel/sched.c	Thu Feb 27 10:57:31 1997
+++ linux/kernel/sched.c	Mon Mar  3 11:57:28 1997
@@ -274,6 +274,25 @@
 	return weight;
 }
 
+void allow_interrupts(void)
+{
+#if defined(__SMP__) && defined(__i386__)
+/* UGH UGH UGH. Damn broken IRQ handling test-fix for x86.. */
+	int this_cpu=smp_processor_id();
+	int lock_depth = current_set[this_cpu]->lock_depth;
+	if (lock_depth) {
+		cli();
+		current_set[this_cpu]->lock_depth = 0;
+		active_kernel_processor = NO_PROC_ID;
+		__asm__ __volatile__("lock ; btrl    $0, kernel_flag");
+		sti();
+		/* interrupts should work here */
+		lock_kernel();
+		current_set[this_cpu]->lock_depth += lock_depth-1;
+	}
+#endif
+}		
+
 /*
  *  'schedule()' is the scheduler function. It's a very simple and nice
  * scheduler: it's not perfect, but certainly works for most things.
@@ -293,7 +312,7 @@
 	int this_cpu=smp_processor_id();
 
 /* check alarm, wake up any interruptible tasks that have got a signal */
-
+	allow_interrupts();
 	lock_kernel();
 	if (intr_count)
 		goto scheduling_in_interrupt;
diff -u --recursive --new-file v2.1.27/linux/net/core/sock.c linux/net/core/sock.c
--- v2.1.27/linux/net/core/sock.c	Thu Feb 27 10:57:32 1997
+++ linux/net/core/sock.c	Mon Mar  3 09:37:44 1997
@@ -328,17 +328,14 @@
 		
 		case SO_LINGER:	
 		  {
-		        int err;
 			len=min(len,sizeof(ling));
 			if (put_user(len, optlen)) 
-			if (!err) {
-				ling.l_onoff=sk->linger;
-				ling.l_linger=sk->lingertime;
-				err = copy_to_user(optval,&ling,len);
-				if (err)
-				    err = -EFAULT;
-			}
-			return err;
+				return -EFAULT;
+			ling.l_onoff=sk->linger;
+			ling.l_linger=sk->lingertime;
+			if (copy_to_user(optval,&ling,len))
+				return -EFAULT;
+			return 0;
 		  }
 		
 		case SO_BSDCOMPAT:
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/af_inet.c linux/net/ipv4/af_inet.c
--- v2.1.27/linux/net/ipv4/af_inet.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ipv4/af_inet.c	Mon Mar  3 09:37:44 1997
@@ -50,6 +50,8 @@
  *		Alan Cox	:	Loosened bind a little.
  *		Mike McLagan	:	ADD/DEL DLCI Ioctls
  *	Willy Konynenberg	:	Transparent proxying support.
+ *		David S. Miller	:	New socket lookup architecture.
+ *					Some other random speedups.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -119,10 +121,6 @@
 extern int udp_get_info(char *, char **, off_t, int, int);
 
 
-struct sock * tcp_sock_array[SOCK_ARRAY_SIZE];
-struct sock * udp_sock_array[SOCK_ARRAY_SIZE];
-struct sock * raw_sock_array[SOCK_ARRAY_SIZE];
-
 #ifdef CONFIG_DLCI
 extern int dlci_ioctl(unsigned int, void*);
 #endif
@@ -134,293 +132,94 @@
 int (*rarp_ioctl_hook)(unsigned int,void*) = NULL;
 
 /*
- *	See if a socket number is in use.
+ *	Destroy an AF_INET socket
  */
  
-static int sk_inuse(struct proto *prot, int num)
+static __inline__ void kill_sk_queues(struct sock *sk)
 {
-	struct sock *sk;
+	struct sk_buff *skb;
 
-	for(sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];
-		sk != NULL;  sk=sk->next) 
-	{
-		if (sk->num == num) 
-			return(1);
+	/* First the read buffer. */
+	while((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
+		/* This will take care of closing sockets that were
+		 * listening and didn't accept everything.
+		 */
+		if (skb->sk != NULL && skb->sk != sk)
+			skb->sk->prot->close(skb->sk, 0);
+		kfree_skb(skb, FREE_READ);
 	}
-	return(0);
-}
 
+	/* Next, the error queue. */
+	while((skb = skb_dequeue(&sk->error_queue)) != NULL)
+		kfree_skb(skb, FREE_READ);
 
-/*
- *	Pick a new socket number
- */
+  	/* Now the backlog. */
+  	while((skb=skb_dequeue(&sk->back_log)) != NULL)
+		kfree_skb(skb, FREE_READ);
+}
 
-unsigned short get_new_socknum(struct proto *prot, unsigned short base)
+static __inline__ void kill_sk_now(struct sock *sk)
 {
-	static int start=0;
-
-	/*
-	 * Used to cycle through the port numbers so the
-	 * chances of a confused connection drop.
-	 */
-	 
-	int i, j;
-	int best = 0;
-	int size = 32767; /* a big num. */
-	struct sock *sk;
+	/* No longer exists. */
+	del_from_prot_sklist(sk);
 
-	if (base == 0) 
-		base = PROT_SOCK+1+(start & 1023);
-	if (base <= PROT_SOCK) 
-	{
-		base += PROT_SOCK+(start & 1023);
-	}
-
-	/*
-	 *	Now look through the entire array and try to find an empty ptr. 
-	 */
-	 
-	for(i=0; i < SOCK_ARRAY_SIZE; i++) 
-	{
-		j = 0;
-		sk = prot->sock_array[(i+base+1) &(SOCK_ARRAY_SIZE -1)];
-		while(sk != NULL) 
-		{
-			sk = sk->next;
-			j++;
-		}
-		if (j == 0) 
-		{
-			start =(i+1+start )&1023;
-			return(i+base+1);
-		}
-		if (j < size) 
-		{
-			best = i;
-			size = j;
-		}
-	}
+	/* This is gross, but needed for SOCK_PACKET -DaveM */
+	if(sk->prot->unhash)
+		sk->prot->unhash(sk);
 
-	/* Now make sure the one we want is not in use. */
-
-	while(sk_inuse(prot, base +best+1)) 
-	{
-		best += SOCK_ARRAY_SIZE;
-	}
-	return(best+base+1);
+	if(sk->opt)
+		kfree(sk->opt);
+	dst_release(sk->dst_cache);
+	sk_free(sk);
 }
 
-/*
- *	Add a socket into the socket tables by number.
- */
-
-void inet_put_sock(unsigned short num, struct sock *sk)
+static __inline__ void kill_sk_later(struct sock *sk)
 {
-	struct sock **skp, *tmp;
-	int mask;
-	unsigned long flags;
-	
-	if(sk->type==SOCK_PACKET)
-		return;
-
-	sk->num = num;
-	sk->next = NULL;
-	num = num &(SOCK_ARRAY_SIZE -1);
-
+	/* this should never happen. */
+	/* actually it can if an ack has just been sent. */
 	/* 
-	 *	We can't have an interrupt re-enter here. 
-	 */
-	 
-	save_flags(flags);
-	cli();
-
-	sk->prot->inuse += 1;
-	if (sk->prot->highestinuse < sk->prot->inuse)
-		sk->prot->highestinuse = sk->prot->inuse;
-
-	if (sk->prot->sock_array[num] == NULL) 
-	{
-		sk->prot->sock_array[num] = sk;
-		restore_flags(flags);
-		return;
-	}
-	
-	restore_flags(flags);
-	for(mask = 0xff000000; mask != 0xffffffff; mask = (mask >> 8) | mask) 
-	{
-		if ((mask & sk->rcv_saddr) &&
-		    (mask & sk->rcv_saddr) != (mask & 0xffffffff)) 
-		{
-			mask = mask << 8;
-			break;
-		}
-	}
-
-	/*
-	 * add the socket to the sock_array[]..
+	 * It's more normal than that...
+	 * It can happen because a skb is still in the device queues
+	 * [PR]
 	 */
-	skp = sk->prot->sock_array + num;
-	cli();
-	while ((tmp = *skp) != NULL) {
-		if (!(tmp->rcv_saddr & mask))
-			break;
-		skp = &tmp->next;
-	}
-	sk->next = tmp;
-	*skp = sk;
-	sti();
-}
-
-/*
- *	Remove a socket from the socket tables.
- */
-
-void inet_remove_sock(struct sock *sk1)
-{
-	struct sock **p;
-	unsigned long flags;
-
-	if (sk1->type==SOCK_PACKET)
-		return;
-		
-	if (!sk1->prot) 
-	{
-		NETDEBUG(printk("sock.c: remove_sock: sk1->prot == NULL\n"));
-		return;
-	}
+		  
+	printk("Socket destroy delayed (r=%d w=%d)\n",
+	       sk->rmem_alloc, sk->wmem_alloc);
 
-	/* We can't have this changing out from under us. */
-	save_flags(flags);
-	cli();
-	
-	p=&(sk1->prot->sock_array[sk1->num & (SOCK_ARRAY_SIZE -1)]);
-	
-	while(*p!=NULL)
-	{
-		if(*p==sk1)
-		{
-			sk1->prot->inuse--;
-			*p=sk1->next;
-			break;
-		}
-		p=&((*p)->next);
-	}
-	restore_flags(flags);
+	sk->destroy = 1;
+	sk->ack_backlog = 0;
+	release_sock(sk);
+	net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
 }
 
-/*
- *	Destroy an AF_INET socket
- */
- 
 void destroy_sock(struct sock *sk)
 {
-	struct sk_buff *skb;
-
 	lock_sock(sk);			/* just to be safe. */
 
-  	/*
-  	 *	Now we can no longer get new packets or once the
-  	 *	timers are killed, send them.
+  	/* Now we can no longer get new packets or once the
+  	 * timers are killed, send them.
   	 */
-  	 
   	net_delete_timer(sk);
 
 	if (sk->prot->destroy)
 		sk->prot->destroy(sk);
 
-  	/*
-  	 *	Clean up the read buffer.
-  	 */
-
-	while((skb=skb_dequeue(&sk->receive_queue))!=NULL) 
-	{
-		/*
-		 * This will take care of closing sockets that were
-		 * listening and didn't accept everything.
-		 */
-		if (skb->sk != NULL && skb->sk != sk) 
-		{
-			IS_SKB(skb);
-			skb->sk->prot->close(skb->sk, 0);
-		}
-		IS_SKB(skb);
-		kfree_skb(skb, FREE_READ);
-	}
-
-  	/*
-  	 *	Clean up the error queue.
-  	 */
-
-	while((skb=skb_dequeue(&sk->error_queue))!=NULL) 
-	{
-		IS_SKB(skb);
-		kfree_skb(skb, FREE_READ);
-	}
-
-  	/*
-  	 *	Now the backlog. 
-  	 */
-  	 
-  	while((skb=skb_dequeue(&sk->back_log))!=NULL) 
-  	{
-		IS_SKB(skb);
-		kfree_skb(skb, FREE_READ);
-	}
+	kill_sk_queues(sk);
 
-	/*
-	 *	Now if it has a half accepted/ closed socket. 
-	 */
-	 
-	if (sk->pair) 
-	{
+	/* Now if it has a half accepted/ closed socket. */
+	if (sk->pair) {
 		sk->pair->prot->close(sk->pair, 0);
 		sk->pair = NULL;
   	}
 
-	/*
-	 * Now if everything is gone we can free the socket
+	/* Now if everything is gone we can free the socket
 	 * structure, otherwise we need to keep it around until
 	 * everything is gone.
 	 */
-
-	if (sk->rmem_alloc == 0 && sk->wmem_alloc == 0) 
-	{
-/*
- *	It is wrong! We MUST unlink socket from socket table
- *	even earlier, than it used to be.
- *	F.e. TCP socket must be unlinked at the moment, when
- *	it goes to TCP_CLOSE. --ANK
- */
-	    	inet_remove_sock(sk);
-
-		if(sk->opt)
-			kfree(sk->opt);
-		dst_release(sk->dst_cache);
-		/*
-		 *	This one is pure paranoia. I'll take it out
-		 *	later once I know the bug is buried.
-		 */
-		tcp_cache_zap();
-		sk_free(sk);
-	} 
-	else 
-	{
-		/* this should never happen. */
-		/* actually it can if an ack has just been sent. */
-		/* 
-		 * It's more normal than that...
-		 * It can happen because a skb is still in the device queues
-		 * [PR]
-		 */
-		  
-		printk("Socket destroy delayed (r=%d w=%d)\n",
- 			sk->rmem_alloc, sk->wmem_alloc);
-
-		sk->destroy = 1;
-		sk->ack_backlog = 0;
-		release_sock(sk);
-		net_reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
-  	}
+	if (sk->rmem_alloc == 0 && sk->wmem_alloc == 0)
+		kill_sk_now(sk);
+	else
+		kill_sk_later(sk);
 }
 
 /*
@@ -467,15 +266,13 @@
 static int inet_autobind(struct sock *sk)
 {
 	/* We may need to bind the socket. */
-	if (sk->num == 0) 
-	{
-		sk->num = get_new_socknum(sk->prot, 0);
+	if (sk->num == 0) {
+		sk->num = sk->prot->good_socknum();
 		if (sk->num == 0) 
 			return(-EAGAIN);
-		udp_cache_zap();
-		tcp_cache_zap();
-		inet_put_sock(sk->num, sk);
-		sk->dummy_th.source = ntohs(sk->num);
+		sk->dummy_th.source = htons(sk->num);
+		sk->prot->hash(sk);
+		add_to_prot_sklist(sk);
 	}
 	return 0;
 }
@@ -491,7 +288,7 @@
 	if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)
 		return(-EINVAL);
 
-	if (inet_autobind(sk)!=0)
+	if (inet_autobind(sk) != 0)
 		return -EAGAIN;
 
 	/* We might as well re use these. */ 
@@ -506,10 +303,11 @@
 	if ((unsigned) backlog > SOMAXCONN)
 		backlog = SOMAXCONN;
 	sk->max_ack_backlog = backlog;
-	if (sk->state != TCP_LISTEN)
-	{
+	if (sk->state != TCP_LISTEN) {
 		sk->ack_backlog = 0;
 		sk->state = TCP_LISTEN;
+		sk->prot->rehash(sk);
+		add_to_prot_sklist(sk);
 	}
 	sk->socket->flags |= SO_ACCEPTCON;
 	return(0);
@@ -526,87 +324,44 @@
 {
 	struct sock *sk;
 	struct proto *prot;
-	int err;
 
 	sock->state = SS_UNCONNECTED;
 	sk = sk_alloc(GFP_KERNEL);
 	if (sk == NULL) 
-		return(-ENOBUFS);
+		goto do_oom;
 
-	/*
-	 *	Note for tcp that also wiped the dummy_th block for us.
-	 */
-	switch (sock->type) 
-	{
-		case SOCK_STREAM:
-		case SOCK_SEQPACKET:
-			if (protocol && protocol != IPPROTO_TCP) 
-			{
-				sk_free(sk);
-				return(-EPROTONOSUPPORT);
-			}
-			protocol = IPPROTO_TCP;
-			sk->no_check = TCP_NO_CHECK;
-			if (ipv4_config.no_pmtu_disc)
-				sk->ip_pmtudisc = IP_PMTUDISC_DONT;
-			else
-				sk->ip_pmtudisc = IP_PMTUDISC_WANT;
-			prot = &tcp_prot;
-			sock->ops = &inet_stream_ops;
-			break;
-
-		case SOCK_DGRAM:
-			if (protocol && protocol != IPPROTO_UDP) 
-			{
-				sk_free(sk);
-				return(-EPROTONOSUPPORT);
-			}
-			protocol = IPPROTO_UDP;
-			sk->no_check = UDP_NO_CHECK;
+	/* Note for tcp that also wiped the dummy_th block for us. */
+	if(sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET) {
+		if (protocol && protocol != IPPROTO_TCP)
+			goto free_and_noproto;
+		protocol = IPPROTO_TCP;
+		sk->no_check = TCP_NO_CHECK;
+		if (ipv4_config.no_pmtu_disc)
 			sk->ip_pmtudisc = IP_PMTUDISC_DONT;
-			prot=&udp_prot;
-			sock->ops = &inet_dgram_ops;
-			break;
-      
-		case SOCK_RAW:
-			if (!suser()) 
-			{
-				sk_free(sk);
-				return(-EPERM);
-			}
-			if (!protocol) 
-			{
-				sk_free(sk);
-				return(-EPROTONOSUPPORT);
-			}
-			prot = &raw_prot;
-			sk->reuse = 1;
-			sk->ip_pmtudisc = IP_PMTUDISC_DONT;
-			sk->num = protocol;
-			sock->ops = &inet_dgram_ops;
-			break;
-
-		case SOCK_PACKET:
-			if (!suser()) 
-			{
-				sk_free(sk);
-				return(-EPERM);
-			}
-			if (!protocol) 
-			{
-				sk_free(sk);
-				return(-EPROTONOSUPPORT);
-			}
-			prot = &packet_prot;
-			sk->reuse = 1;
-			sk->ip_pmtudisc = IP_PMTUDISC_DONT;
-			sk->num = protocol;
-			sock->ops = &inet_dgram_ops;
-			break;
-
-		default:
-			sk_free(sk);
-			return(-ESOCKTNOSUPPORT);
+		else
+			sk->ip_pmtudisc = IP_PMTUDISC_WANT;
+		prot = &tcp_prot;
+		sock->ops = &inet_stream_ops;
+	} else if(sock->type == SOCK_DGRAM) {
+		if (protocol && protocol != IPPROTO_UDP)
+			goto free_and_noproto;
+		protocol = IPPROTO_UDP;
+		sk->no_check = UDP_NO_CHECK;
+		sk->ip_pmtudisc = IP_PMTUDISC_DONT;
+		prot=&udp_prot;
+		sock->ops = &inet_dgram_ops;
+	} else if(sock->type == SOCK_RAW || sock->type == SOCK_PACKET) {
+		if (!suser())
+			goto free_and_badperm;
+		if (!protocol)
+			goto free_and_noproto;
+		prot = (sock->type == SOCK_RAW) ? &raw_prot : &packet_prot;
+		sk->reuse = 1;
+		sk->ip_pmtudisc = IP_PMTUDISC_DONT;
+		sk->num = protocol;
+		sock->ops = &inet_dgram_ops;
+	} else {
+		goto free_and_badtype;
 	}
 
 	sock_init_data(sock,sk);
@@ -636,33 +391,47 @@
 	sk->ip_mc_index=0;
 	sk->ip_mc_list=NULL;
 	
-	/*
-	 *	Speed up by setting some standard state for the dummy_th
+	/*	Speed up by setting some standard state for the dummy_th
 	 *	if TCP uses it (maybe move to tcp_init later)
 	 */
   	
-	if (sk->num) 
-	{
-		/*
-		 * It assumes that any protocol which allows
+	if (sk->num) {
+		/* It assumes that any protocol which allows
 		 * the user to assign a number at socket
 		 * creation time automatically
 		 * shares.
 		 */
-		inet_put_sock(sk->num, sk);
 		sk->dummy_th.source = ntohs(sk->num);
+
+		/* This is gross, but needed for SOCK_PACKET -DaveM */
+		if(sk->prot->hash)
+			sk->prot->hash(sk);
+		add_to_prot_sklist(sk);
 	}
 
-	if (sk->prot->init) 
-	{
-		err = sk->prot->init(sk);
-		if (err != 0) 
-		{
+	if (sk->prot->init) {
+		int err = sk->prot->init(sk);
+		if (err != 0) {
 			destroy_sock(sk);
 			return(err);
 		}
 	}
 	return(0);
+
+free_and_badtype:
+	sk_free(sk);
+	return -ESOCKTNOSUPPORT;
+
+free_and_badperm:
+	sk_free(sk);
+	return -EPERM;
+
+free_and_noproto:
+	sk_free(sk);
+	return -EPROTONOSUPPORT;
+
+do_oom:
+	return -ENOBUFS;
 }
 
 
@@ -684,193 +453,99 @@
 int inet_release(struct socket *sock, struct socket *peersock)
 {
 	struct sock *sk = sock->sk;
-	unsigned long timeout;
-
-	if (sk==NULL)
-		return 0;
-
-	if (sock->state != SS_UNCONNECTED)
-		sock->state = SS_DISCONNECTING;
-
-	sk->state_change(sk);
-
-	/* Start closing the connection.  This may take a while. */
 
-	/* Applications forget to leave groups before exiting */
-	ip_mc_drop_socket(sk);
+	if (sk) {
+		unsigned long timeout;
 
-	/*
-	 * If linger is set, we don't return until the close
-	 * is complete.  Otherwise we return immediately. The
-	 * actually closing is done the same either way.
-	 *
-	 * If the close is due to the process exiting, we never
-	 * linger..
-	 */
-	timeout = 0;
-	if (sk->linger)
-	{
-		timeout = ~0UL;
-		if (!sk->lingertime)
-			timeout = jiffies + HZ*sk->lingertime;
-	}
-	if (current->flags & PF_EXITING)
+		/* Begin closedown and wake up sleepers. */
+		if (sock->state != SS_UNCONNECTED)
+			sock->state = SS_DISCONNECTING;
+		sk->state_change(sk);
+
+		/* Applications forget to leave groups before exiting */
+		ip_mc_drop_socket(sk);
+
+		/* If linger is set, we don't return until the close
+		 * is complete.  Otherwise we return immediately. The
+		 * actually closing is done the same either way.
+		 *
+		 * If the close is due to the process exiting, we never
+		 * linger..
+		 */
 		timeout = 0;
+		if (sk->linger && !(current->flags & PF_EXITING)) {
+			timeout = ~0UL;
 
-	sock->sk = NULL;
-	sk->socket = NULL;
-
-	sk->prot->close(sk, timeout);
+			/* XXX This makes no sense whatsoever... -DaveM */
+			if (!sk->lingertime)
+				timeout = jiffies + HZ*sk->lingertime;
+		}
+		sock->sk = NULL;
+		sk->socket = NULL;
+		sk->prot->close(sk, timeout);
+	}
 	return(0);
 }
 
-
-static int inet_bind(struct socket *sock, struct sockaddr *uaddr,
-		     int addr_len)
+static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
 	struct sockaddr_in *addr=(struct sockaddr_in *)uaddr;
-	struct sock *sk=sock->sk, *sk2;
-	unsigned short snum = 0 /* Stoopid compiler.. this IS ok */;
+	struct sock *sk=sock->sk;
+	unsigned short snum;
 	int chk_addr_ret;
 
-	/*
-	 *	If the socket has its own bind function then use it.
-	 */
-	 
+	/* If the socket has its own bind function then use it. (RAW and PACKET) */
 	if(sk->prot->bind)
-		return sk->prot->bind(sk,uaddr, addr_len);
+		return sk->prot->bind(sk, uaddr, addr_len);
 		
-	/* check this error. */
-	if (sk->state != TCP_CLOSE)
-		return(-EINVAL);
-	if(addr_len<sizeof(struct sockaddr_in))
+	/* Check these errors (active socket, bad address length, double bind). */
+	if ((sk->state != TCP_CLOSE)			||
+	    (addr_len < sizeof(struct sockaddr_in))	||
+	    (sk->num != 0))
 		return -EINVAL;
 		
-	if (sock->type != SOCK_RAW)
-	{
-		if (sk->num != 0) 
-			return(-EINVAL);
-
-		snum = ntohs(addr->sin_port);
-		
+	snum = ntohs(addr->sin_port);
 #ifdef CONFIG_IP_MASQUERADE
-		/*
-		 *	The kernel masquerader needs some ports
-		 */		
-		if(snum>=PORT_MASQ_BEGIN && snum<=PORT_MASQ_END)
-			return -EADDRINUSE;
+	/* The kernel masquerader needs some ports. */
+	if((snum >= PORT_MASQ_BEGIN) && (snum <= PORT_MASQ_END))
+		return -EADDRINUSE;
 #endif		 
-
-		if (snum == 0) 
-			snum = get_new_socknum(sk->prot, 0);
-		if (snum < PROT_SOCK && !suser()) 
-			return(-EACCES);
-	}
+	if (snum == 0) 
+		snum = sk->prot->good_socknum();
+	if (snum < PROT_SOCK && !suser())
+		return(-EACCES);
 	
 	chk_addr_ret = __ip_chk_addr(addr->sin_addr.s_addr);
+	if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR &&
+	    chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST) {
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
-	/*
-	 * Superuser may bind to any address to allow transparent proxying.
-	 */
-	if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST && !suser())
-#else
-	if (addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR && chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST)
-#endif
-		return(-EADDRNOTAVAIL);	/* Source address MUST be ours! */
-
-#ifndef CONFIG_IP_TRANSPARENT_PROXY
-	/*
-	 * Am I just thick or is this test really always true after the one
-	 * above?  Just taking the test out appears to be the easiest way to
-	 * make binds to remote addresses for transparent proxying work.
-	 */
-	if (chk_addr_ret || addr->sin_addr.s_addr == 0)
-	{
+		/* Superuser may bind to any address to allow transparent proxying. */
+		if(!suser())
 #endif
-		/*
-		 *      We keep a pair of addresses. rcv_saddr is the one
-		 *      used by get_sock_*(), and saddr is used for transmit.
-		 *
-		 *      In the BSD API these are the same except where it
-		 *      would be illegal to use them (multicast/broadcast) in
-		 *      which case the sending device address is used.
-		 */
-		sk->rcv_saddr = addr->sin_addr.s_addr;
-		if(chk_addr_ret==IS_MULTICAST||chk_addr_ret==IS_BROADCAST)
-			sk->saddr = 0;  /* Use device */
-		else
-			sk->saddr = addr->sin_addr.s_addr;
-#ifndef CONFIG_IP_TRANSPARENT_PROXY
+			return -EADDRNOTAVAIL;	/* Source address MUST be ours! */
 	}
-#endif
-	if (sock->type != SOCK_RAW)
-	{
-		/* Make sure we are allowed to bind here. */
-		cli();
-		for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
-					sk2 != NULL; sk2 = sk2->next) 
-		{
-			/*
-			 *	Hash collision or real match ?
-			 */
-			 
-			if (sk2->num != snum) 
-				continue;
-				
-			/*
-			 *	Either bind on the port is wildcard means
-			 *	they will overlap and thus be in error
-			 */			
-			 
-			if (!sk2->rcv_saddr || !sk->rcv_saddr)
-			{
-				/*
-				 *	Allow only if both are setting reuse.
-				 */
-				if(sk2->reuse && sk->reuse && sk2->state!=TCP_LISTEN)
-					continue;
-				sti();
-				return(-EADDRINUSE);
-			}
-
-			/*
-			 *	Two binds match ?
-			 */
-
-			if (sk2->rcv_saddr != sk->rcv_saddr) 
-				continue;
-			/*
-			 *	Reusable port ?
-			 */
-
-			if (!sk->reuse)
-			{
-				sti();
-				return(-EADDRINUSE);
-			}
-			
-			/*
-			 *	Reuse ?
-			 */
-			 
-			if (!sk2->reuse || sk2->state==TCP_LISTEN)
-			{
-				sti();
-				return(-EADDRINUSE);
-			}
-		}
-		sti();
 
-		inet_remove_sock(sk);
-		if (sock->type==SOCK_DGRAM)
-			udp_cache_zap();
-		if (sock->type==SOCK_STREAM)
-			tcp_cache_zap();
-		inet_put_sock(snum, sk);
-		sk->dummy_th.source = ntohs(sk->num);
-		sk->daddr = 0;
-		sk->dummy_th.dest = 0;
-	}
+	/*      We keep a pair of addresses. rcv_saddr is the one
+	 *      used by hash lookups, and saddr is used for transmit.
+	 *
+	 *      In the BSD API these are the same except where it
+	 *      would be illegal to use them (multicast/broadcast) in
+	 *      which case the sending device address is used.
+	 */
+	sk->rcv_saddr = sk->saddr = addr->sin_addr.s_addr;
+	if(chk_addr_ret == IS_MULTICAST || chk_addr_ret == IS_BROADCAST)
+		sk->saddr = 0;  /* Use device */
+
+	/* Make sure we are allowed to bind here. */
+	if(sk->prot->verify_bind(sk, snum))
+		return -EADDRINUSE;
+
+	sk->num = snum;
+	sk->dummy_th.source = ntohs(snum);
+	sk->daddr = 0;
+	sk->dummy_th.dest = 0;
+	sk->prot->rehash(sk);
+	add_to_prot_sklist(sk);
 	dst_release(sk->dst_cache);
 	sk->dst_cache=NULL;
 	return(0);
@@ -882,7 +557,7 @@
 	struct sock *sk=sock->sk;
 	int err;
 
-	if (inet_autobind(sk)!=0)
+	if (inet_autobind(sk) != 0)
 		return(-EAGAIN);
 	if (sk->prot->connect == NULL) 
 		return(-EOPNOTSUPP);
@@ -903,85 +578,61 @@
 	struct sock *sk=sock->sk;
 	int err;
 
-	switch (sock->state)
-	{
-		case SS_UNCONNECTED:
-			/* This is ok... continue with connect */
-			break;
-		case SS_CONNECTED:
-			/* Socket is already connected */
+	if(sock->state != SS_UNCONNECTED && sock->state != SS_CONNECTING) {
+		if(sock->state == SS_CONNECTED)
 			return -EISCONN;
-		case SS_CONNECTING:
-			/* Not yet connected... we will check this. */
-		
-			/*
-			 *	FIXME:  for all protocols what happens if you start
-			 *	an async connect fork and both children connect. Clean
-			 *	this up in the protocols!
-			 */
-			break;
-		default:
-			return(-EINVAL);
-	}
-
-	if (sock->state == SS_CONNECTING && tcp_connected(sk->state))
-	{
-		sock->state = SS_CONNECTED;
-		/* Connection completing after a connect/EINPROGRESS/select/connect */
-		return 0;	/* Rock and roll */
+		return -EINVAL;
 	}
 
-	if (sock->state == SS_CONNECTING && sk->protocol == IPPROTO_TCP && (flags & O_NONBLOCK))
-	{
-		if(sk->err!=0)
-			return sock_error(sk);
-		return -EALREADY;	/* Connecting is currently in progress */
-  	}
-	if (sock->state != SS_CONNECTING) 
-	{
+	if(sock->state == SS_CONNECTING) {
+		if(tcp_connected(sk->state)) {
+			sock->state = SS_CONNECTED;
+			return 0;
+		}
+		if(sk->protocol == IPPROTO_TCP && (flags & O_NONBLOCK)) {
+			if(sk->err)
+				return sock_error(sk);
+			return -EALREADY;
+		}
+	} else {
 		/* We may need to bind the socket. */
-		if (inet_autobind(sk)!=0)
+		if (inet_autobind(sk) != 0)
 			return(-EAGAIN);
 		if (sk->prot->connect == NULL) 
 			return(-EOPNOTSUPP);
 		err = sk->prot->connect(sk, uaddr, addr_len);
-		if (err < 0) 
+		if (err < 0)
 			return(err);
   		sock->state = SS_CONNECTING;
 	}
 	
-	if (sk->state > TCP_FIN_WAIT2 && sock->state==SS_CONNECTING)
-	{
-		sock->state=SS_UNCONNECTED;
+	if (sk->state > TCP_FIN_WAIT2 && sock->state == SS_CONNECTING) {
+		sock->state = SS_UNCONNECTED;
 		return sock_error(sk);
 	}
 
 	if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) 
 	  	return (-EINPROGRESS);
 
-	cli(); /* avoid the race condition */
-	while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) 
-	{
+	cli();
+	while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
 		interruptible_sleep_on(sk->sleep);
-		if (current->signal & ~current->blocked) 
-		{
+		if (current->signal & ~current->blocked) {
 			sti();
 			return(-ERESTARTSYS);
 		}
 		/* This fixes a nasty in the tcp/ip code. There is a hideous hassle with
 		   icmp error packets wanting to close a tcp or udp socket. */
-		if (sk->err && sk->protocol == IPPROTO_TCP)
-		{
+		if (sk->err && sk->protocol == IPPROTO_TCP) {
 			sock->state = SS_UNCONNECTED;
 			sti();
 			return sock_error(sk); /* set by tcp_err() */
 		}
 	}
 	sti();
-	sock->state = SS_CONNECTED;
 
-	if (sk->state != TCP_ESTABLISHED && sk->err) 
-	{
+	sock->state = SS_CONNECTED;
+	if ((sk->state != TCP_ESTABLISHED) && sk->err) {
 		sock->state = SS_UNCONNECTED;
 		return sock_error(sk);
 	}
@@ -994,32 +645,24 @@
 
 int inet_accept(struct socket *sock, struct socket *newsock, int flags)
 {
-	struct sock *sk1 = sock->sk;
+	struct sock *sk1 = sock->sk, *sk2;
 	struct sock *newsk = newsock->sk;
-	struct sock *sk2;
-	int err;
+	int err = -EINVAL;
 
-	if (sock->state != SS_UNCONNECTED)
-		return -EINVAL;
-	if (!(sock->flags & SO_ACCEPTCON)) 
-		return -EINVAL;
-	if (sk1->prot->accept == NULL) 
-		return -EOPNOTSUPP;
+	if (sock->state != SS_UNCONNECTED || !(sock->flags & SO_ACCEPTCON))
+		goto do_err;
 
-	/*
-	 *	Restore the state if we have been interrupted, and then returned. 
-	 */
-	 
-	if (sk1->pair != NULL ) 
-	{
+	err = -EOPNOTSUPP;
+	if (sk1->prot->accept == NULL)
+		goto do_err;
+
+	/* Restore the state if we have been interrupted, and then returned. */
+	if (sk1->pair != NULL) {
 		sk2 = sk1->pair;
 		sk1->pair = NULL;
-	} 
-	else
-	{
-		sk2 = sk1->prot->accept(sk1,flags);
-		if (sk2 == NULL) 
-			return sock_error(sk1);
+	} else {
+		if((sk2 = sk1->prot->accept(sk1,flags)) == NULL)
+			goto do_sk1_err;
 	}
 
 	/*
@@ -1027,7 +670,6 @@
 	 *	We need to free it up because the tcp module creates
 	 *	its own when it accepts one.
 	 */
-
 	sk2->sleep = newsk->sleep;
 
 	newsock->sk = sk2;
@@ -1035,56 +677,54 @@
 	newsk->socket = NULL;
 
 	if (flags & O_NONBLOCK)
-	{
-		destroy_sock(newsk);
-		return(0);
-	}
+		goto do_half_success;
 
-	cli(); /* avoid the race. */
-	while (sk2->state == TCP_SYN_RECV) 
-	{
+	cli();
+	while (sk2->state == TCP_SYN_RECV) {
 		interruptible_sleep_on(sk2->sleep);
-		if (current->signal & ~current->blocked) 
-		{
-			sti();
-			sk1->pair = sk2;
-			sk2->sleep = NULL;
-			sk2->socket = NULL;
-
-			newsock->sk = newsk;
-			newsk->socket = newsock;
-			return -ERESTARTSYS;
-		}
+		if (current->signal & ~current->blocked)
+			goto do_interrupted;
 	}
 	sti();
-
-	if (sk2->state != TCP_ESTABLISHED && sk2->err > 0) 
-	{
-		err = sock_error(sk2);
-		sk2->sleep = NULL;
-		sk2->socket = NULL;
-		destroy_sock(sk2);
-
-		newsock->sk = newsk;
-		newsk->socket = newsock;
-
-		return err;
-	}
+	if(sk2->state == TCP_ESTABLISHED)
+		goto do_full_success;
+	if(sk2->err > 0)
+		goto do_connect_err;
+	err = -ECONNABORTED;
 	if (sk2->state == TCP_CLOSE)
-	{
-		sk2->sleep = NULL;
-		sk2->socket = NULL;
-		destroy_sock(sk2);
-
-		newsock->sk = newsk;
-		newsk->socket = newsock;
-
-		return -ECONNABORTED;
-	}
-
+		goto do_bad_connection;
+do_full_success:
 	destroy_sock(newsk);
 	newsock->state = SS_CONNECTED;
+	return 0;
+
+do_half_success:
+	destroy_sock(newsk);
 	return(0);
+
+do_connect_err:
+	err = sock_error(sk2);
+do_bad_connection:
+	sk2->sleep = NULL;
+	sk2->socket = NULL;
+	destroy_sock(sk2);
+	newsock->sk = newsk;
+	newsk->socket = newsock;
+	return err;
+
+do_interrupted:
+	sti();
+	sk1->pair = sk2;
+	sk2->sleep = NULL;
+	sk2->socket = NULL;
+	newsock->sk = newsk;
+	newsk->socket = newsock;
+	err = -ERESTARTSYS;
+do_err:
+	return err;
+do_sk1_err:
+	err = sock_error(sk1);
+	return err;
 }
 
 
@@ -1095,19 +735,16 @@
 static int inet_getname(struct socket *sock, struct sockaddr *uaddr,
 		 int *uaddr_len, int peer)
 {
-	struct sock *sk=sock->sk;
-	struct sockaddr_in *sin=(struct sockaddr_in *)uaddr;
+	struct sock *sk		= sock->sk;
+	struct sockaddr_in *sin	= (struct sockaddr_in *)uaddr;
   
 	sin->sin_family = AF_INET;
-	if (peer) 
-	{
+	if (peer) {
 		if (!tcp_connected(sk->state)) 
 			return(-ENOTCONN);
 		sin->sin_port = sk->dummy_th.dest;
 		sin->sin_addr.s_addr = sk->daddr;
-	} 
-	else 
-	{
+	} else {
 		__u32 addr = sk->rcv_saddr;
 		if (!addr)
 			addr = sk->saddr;
@@ -1134,7 +771,7 @@
 	if (sk->err)
 		return sock_error(sk);
 	/* We may need to bind the socket. */
-	if (inet_autobind(sk)!=0)
+	if (inet_autobind(sk) != 0)
 		return(-EAGAIN);
 	err = sk->prot->recvmsg(sk, msg, size, flags&MSG_DONTWAIT,
 				flags&~MSG_DONTWAIT, &addr_len);
@@ -1157,9 +794,11 @@
 		return(-EOPNOTSUPP);
 	if(sk->err)
 		return sock_error(sk);
+
 	/* We may need to bind the socket. */
-	if(inet_autobind(sk)!=0)
+	if(inet_autobind(sk) != 0)
 		return -EAGAIN;
+
 	return sk->prot->sendmsg(sk, msg, size);
 }
 
@@ -1168,8 +807,7 @@
 {
 	struct sock *sk = sock->sk;
 
-	/*
-	 * This should really check to make sure
+	/* This should really check to make sure
 	 * the socket is a TCP socket. (WHY AC...)
 	 */
 	how++; /* maps 0->1 has the advantage of making bit 1 rcvs and
@@ -1329,256 +967,6 @@
 	return(0);
 }
 
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-/*
- * Some routines for the for loop in get_sock which sometimes needs to walk
- * two linked lists in sequence.  Could use macros as well.
- * Does anyone know a nicer way to code this?
- */
-static __inline__ struct sock *secondlist(unsigned short hpnum, struct sock *s,
-				int *pfirstpass, struct proto *prot)
-{
-	if (s == NULL && (*pfirstpass)-- )
-		return prot->sock_array[hpnum & (SOCK_ARRAY_SIZE - 1)];
-	else
-		return s;
-}
-static __inline__ struct sock *get_sock_loop_init(unsigned short hnum,
-			unsigned short hpnum, struct sock *s,
-			int *pfirstpass, struct proto *prot)
-{
-	s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
-	return secondlist(hpnum, s, pfirstpass, prot);
-}
-static __inline__ struct sock *get_sock_loop_next(unsigned short hnum,
-			unsigned short hpnum, struct sock *s,
-			int *pfirstpass, struct proto *prot)
-{
-	s = s->next;
-	return secondlist(hpnum, s, pfirstpass, prot);
-}
-
-struct sock *get_sock_proxy(struct proto *prot, unsigned short num,
-				unsigned long raddr,
-				unsigned short rnum, unsigned long laddr,
-				unsigned long paddr, unsigned short pnum)
-{
-	struct sock *s = 0;
-	struct sock *result = NULL;
-	int badness = -1;
-	unsigned short hnum;
-	unsigned short hpnum;
-	int firstpass = 1;
-
-	hnum = ntohs(num);
-	hpnum = ntohs(pnum);
-
-	/*
-	 * SOCK_ARRAY_SIZE must be a power of two.  This will work better
-	 * than a prime unless 3 or more sockets end up using the same
-	 * array entry.  This should not be a problem because most
-	 * well known sockets don't overlap that much, and for
-	 * the other ones, we can just be careful about picking our
-	 * socket number when we choose an arbitrary one.
-	 */
-
-	for(s = get_sock_loop_init(hnum, hpnum, s, &firstpass, prot);
-		s != NULL;
-		s = get_sock_loop_next(hnum, hpnum, s, &firstpass, prot))
-	{
-		int score = 0;
-
-		/* accept the addressed port or the redirect (proxy) port */
-		if (s->num != hnum && s->num != hpnum)
-			continue;
-
-		if(s->dead && (s->state == TCP_CLOSE))
-			continue;
-		/* local address matches? */
-		if (s->rcv_saddr) {
-			/*
-			 * If this is redirected traffic, it must either
-			 * match on the redirected port/ip-address or on
-			 * the actual destination, not on a mixture.
-			 * There must be a simpler way to express this...
-			 */
-			if ((s->num != hpnum || s->rcv_saddr != paddr)
-			    && (s->num != hnum || s->rcv_saddr != laddr))
-				continue;
-			score++;
-		}
-		/* remote address matches? */
-		if (s->daddr) {
-			if (s->daddr != raddr)
-				continue;
-			score++;
-		}
-		/* remote port matches? */
-		if (s->dummy_th.dest) {
-			if (s->dummy_th.dest != rnum)
-				continue;
-			score++;
-		}
-		/* perfect match? */
-		if (score == 3 && s->num == hnum)
-			return s;
-		/* no, check if this is the best so far.. */
-		if (score <= badness)
-			continue;
-		/* don't accept near matches on the actual destination
-		 * port with IN_ADDR_ANY for redirected traffic, but do
-		 * allow explicit remote address listens.  (disputable)
-		 */
-		if (s->num != hpnum && !s->rcv_saddr)
-			continue;
-		result = s;
-		badness = score;
-  	}
-  	return result;
-}
-#endif
-
-/*
- * This routine must find a socket given a TCP or UDP header.
- * Everything is assumed to be in net order.
- *
- * We give priority to more closely bound ports: if some socket
- * is bound to a particular foreign address, it will get the packet
- * rather than somebody listening to any address..
- */
-
-struct sock *get_sock(struct proto *prot, unsigned short num,
-				unsigned long raddr,
-				unsigned short rnum, unsigned long laddr)
-{
-	struct sock *s = 0;
-	struct sock *result = NULL;
-	int badness = -1;
-	unsigned short hnum;
-
-	hnum = ntohs(num);
-
-	/*
-	 * SOCK_ARRAY_SIZE must be a power of two.  This will work better
-	 * than a prime unless 3 or more sockets end up using the same
-	 * array entry.  This should not be a problem because most
-	 * well known sockets don't overlap that much, and for
-	 * the other ones, we can just be careful about picking our
-	 * socket number when we choose an arbitrary one.
-	 */
-
-	for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
-			s != NULL; s = s->next) 
-	{
-		int score = 0;
-
-		if (s->num != hnum) 
-			continue;
-
-		if(s->dead && (s->state == TCP_CLOSE))
-			continue;
-		/* local address matches? */
-		if (s->rcv_saddr) {
-			if (s->rcv_saddr != laddr)
-				continue;
-			score++;
-		}
-		/* remote address matches? */
-		if (s->daddr) {
-			if (s->daddr != raddr)
-				continue;
-			score++;
-		}
-		/* remote port matches? */
-		if (s->dummy_th.dest) {
-			if (s->dummy_th.dest != rnum)
-				continue;
-			score++;
-		}
-		/* perfect match? */
-		if (score == 3)
-			return s;
-		/* no, check if this is the best so far.. */
-		if (score <= badness)
-			continue;
-		result = s;
-		badness = score;
-  	}
-  	return result;
-}
-
-
-/*
- *	Deliver a datagram to raw sockets.
- */
- 
-struct sock *get_sock_raw(struct sock *sk, 
-				unsigned short num,
-				unsigned long raddr,
-				unsigned long laddr)
-{
-	struct sock *s;
-
-	s=sk;
-
-	for(; s != NULL; s = s->next) 
-	{
-		if (s->num != num) 
-			continue;
-		if(s->dead && (s->state == TCP_CLOSE))
-			continue;
-		if(s->daddr && s->daddr!=raddr)
-			continue;
- 		if(s->rcv_saddr && s->rcv_saddr != laddr)
-			continue;
-		return(s);
-  	}
-  	return(NULL);
-}
-
-/*
- *	Deliver a datagram to broadcast/multicast sockets.
- */
- 
-struct sock *get_sock_mcast(struct sock *sk, 
-				unsigned short num,
-				unsigned long raddr,
-				unsigned short rnum, unsigned long laddr)
-{
-	struct sock *s;
-	unsigned short hnum;
-
-	hnum = ntohs(num);
-
-	/*
-	 * SOCK_ARRAY_SIZE must be a power of two.  This will work better
-	 * than a prime unless 3 or more sockets end up using the same
-	 * array entry.  This should not be a problem because most
-	 * well known sockets don't overlap that much, and for
-	 * the other ones, we can just be careful about picking our
-	 * socket number when we choose an arbitrary one.
-	 */
-	
-	s=sk;
-
-	for(; s != NULL; s = s->next) 
-	{
-		if (s->num != hnum) 
-			continue;
-		if(s->dead && (s->state == TCP_CLOSE))
-			continue;
-		if(s->daddr && s->daddr!=raddr)
-			continue;
-		if (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) 
-			continue;
- 		if(s->rcv_saddr  && s->rcv_saddr != laddr)
-			continue;
-		return(s);
-  	}
-  	return(NULL);
-}
-
-
 struct proto_ops inet_stream_ops = {
 	AF_INET,
 
@@ -1621,9 +1009,6 @@
 	inet_recvmsg
 };
 
-
-
-
 struct net_proto_family inet_family_ops = {
 	AF_INET,
 	inet_create
@@ -1681,8 +1066,6 @@
 {
 	struct sk_buff *dummy_skb;
 	struct inet_protocol *p;
-	int i;
-
 
 	printk("Swansea University Computer Society TCP/IP for NET3.037\n");
 
@@ -1703,22 +1086,6 @@
 	/*
 	 *	Add all the protocols. 
 	 */
-	 
-	for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
-	{
-		tcp_sock_array[i] = NULL;
-		udp_sock_array[i] = NULL;
-		raw_sock_array[i] = NULL;
-  	}
-	tcp_prot.inuse = 0;
-	tcp_prot.highestinuse = 0;
-	tcp_prot.sock_array = tcp_sock_array;
-	udp_prot.inuse = 0;
-	udp_prot.highestinuse = 0;
-	udp_prot.sock_array = udp_sock_array;
-	raw_prot.inuse = 0;
-	raw_prot.highestinuse = 0;
-	raw_prot.sock_array = raw_sock_array;
 
 	printk("IP Protocols: ");
 	for(p = inet_protocol_base; p != NULL;) 
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/icmp.c linux/net/ipv4/icmp.c
--- v2.1.27/linux/net/ipv4/icmp.c	Wed Dec 18 02:07:55 1996
+++ linux/net/ipv4/icmp.c	Mon Mar  3 09:37:44 1997
@@ -737,22 +737,18 @@
 	/* Deliver ICMP message to raw sockets. Pretty useless feature?
 	 */
 
-	hash = iph->protocol & (SOCK_ARRAY_SIZE-1);
-	if ((raw_sk=raw_prot.sock_array[hash]) != NULL) {
-		raw_sk = get_sock_raw(raw_sk, iph->protocol, iph->saddr, iph->daddr);
+	/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
+	hash = iph->protocol & (MAX_INET_PROTOS - 1);
+	if ((raw_sk = raw_v4_htable[hash]) != NULL) {
+		raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr);
 		while (raw_sk) {
 			raw_err(raw_sk, skb);
-			raw_sk=get_sock_raw(raw_sk->next, iph->protocol, iph->saddr, iph->daddr);
+			raw_sk = raw_v4_lookup(raw_sk->next, iph->protocol,
+					       iph->saddr, iph->daddr);
 		}
 	}
 
 	/*
-	 *	Get the protocol(s).
-	 */
-	 
-	hash = iph->protocol & (MAX_INET_PROTOS -1);
-
-	/*
 	 *	This can't change while we are doing it. 
 	 *
 	 *	FIXME: Deliver to appropriate raw sockets too.
@@ -981,6 +977,10 @@
  *	in udp.c or tcp.c...
  */
 
+/* This should work with the new hashes now. -DaveM */
+extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
+extern struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
+
 int icmp_chkaddr(struct sk_buff *skb)
 {
 	struct icmphdr *icmph=(struct icmphdr *)(skb->nh.raw + skb->nh.iph->ihl*4);
@@ -995,8 +995,7 @@
 			{
 			struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
-			sk = get_sock(&tcp_prot, th->source, iph->daddr,
-						th->dest, iph->saddr);
+			sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
 			if (!sk) return 0;
 			if (sk->saddr != iph->saddr) return 0;
 			if (sk->daddr != iph->daddr) return 0;
@@ -1010,8 +1009,7 @@
 			{
 			struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
-			sk = get_sock(&udp_prot, uh->source, iph->daddr,
-						uh->dest, iph->saddr);
+			sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
 			if (!sk) return 0;
 			if (sk->saddr != iph->saddr && __ip_chk_addr(iph->saddr) != IS_MYADDR)
 				return 0;
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/ip_input.c linux/net/ipv4/ip_input.c
--- v2.1.27/linux/net/ipv4/ip_input.c	Sun Jan 26 02:07:50 1997
+++ linux/net/ipv4/ip_input.c	Mon Mar  3 09:37:44 1997
@@ -245,39 +245,34 @@
 	 *	RFC 1122: SHOULD pass TOS value up to the transport layer.
 	 */
  
-	hash = iph->protocol & (SOCK_ARRAY_SIZE-1);
+	/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
+	hash = iph->protocol & (MAX_INET_PROTOS - 1);
 
 	/* 
 	 *	If there maybe a raw socket we must check - if not we don't care less 
 	 */
 		 
-	if((raw_sk=raw_prot.sock_array[hash])!=NULL)
-	{
-		struct sock *sknext=NULL;
+	if((raw_sk = raw_v4_htable[hash]) != NULL) {
+		struct sock *sknext = NULL;
 		struct sk_buff *skb1;
-		raw_sk=get_sock_raw(raw_sk, iph->protocol,  iph->saddr, iph->daddr);
-		if(raw_sk)	/* Any raw sockets */
-		{
-			do
-			{
+		raw_sk = raw_v4_lookup(raw_sk, iph->protocol, iph->saddr, iph->daddr);
+		if(raw_sk) {	/* Any raw sockets */
+			do {
 				/* Find the next */
-				sknext=get_sock_raw(raw_sk->next, iph->protocol, iph->saddr, iph->daddr);
+				sknext = raw_v4_lookup(raw_sk->next, iph->protocol,
+						       iph->saddr, iph->daddr);
 				if(sknext)
-					skb1=skb_clone(skb, GFP_ATOMIC);
+					skb1 = skb_clone(skb, GFP_ATOMIC);
 				else
 					break;	/* One pending raw socket left */
 				if(skb1)
 					raw_rcv(raw_sk, skb1);
-				raw_sk=sknext;
-			}
-			while(raw_sk!=NULL);
+				raw_sk = sknext;
+			} while(raw_sk!=NULL);
 				
-			/*
-			 *	Here either raw_sk is the last raw socket, or NULL if none 
-			 */
-			
-			/*
-			 *	We deliver to the last raw socket AFTER the protocol checks as it avoids a surplus copy 
+			/*	Here either raw_sk is the last raw socket, or NULL if
+			 *	none.  We deliver to the last raw socket AFTER the
+			 *	protocol checks as it avoids a surplus copy.
 			 */
 		}
 	}
@@ -286,7 +281,6 @@
 	 *	skb->h.raw now points at the protocol beyond the IP header.
 	 */
 	
-	hash = iph->protocol & (MAX_INET_PROTOS -1);
 	for (ipprot = (struct inet_protocol *)inet_protos[hash];ipprot != NULL;ipprot=(struct inet_protocol *)ipprot->next)
 	{
 		struct sk_buff *skb2;
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/ip_sockglue.c linux/net/ipv4/ip_sockglue.c
--- v2.1.27/linux/net/ipv4/ip_sockglue.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ipv4/ip_sockglue.c	Mon Mar  3 09:37:44 1997
@@ -171,17 +171,17 @@
 {
 	int val=0,err;
 	unsigned char ucval = 0;
-	int len;
 #if defined(CONFIG_IP_FIREWALL) || defined(CONFIG_IP_ACCT)
 	struct ip_fw tmp_fw;
 #endif	
 
-	if(get_user(len, optval))
-		return -EFAULT;
-	if(len>=sizeof(int) && get_user(val, (int *) optval))
-		return -EFAULT;
-	if(len>=sizeof(char) && get_user(ucval, (unsigned char *) optval))
-		return -EFAULT;
+	if(optlen>=sizeof(int)) {
+		if(get_user(val, (int *) optval))
+			return -EFAULT;
+	} else if(optlen>=sizeof(char)) {
+		if(get_user(ucval, (unsigned char *) optval))
+			return -EFAULT;
+	}
 	
 	if(level!=SOL_IP)
 		return -ENOPROTOOPT;
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/packet.c linux/net/ipv4/packet.c
--- v2.1.27/linux/net/ipv4/packet.c	Sat Jan 25 13:46:14 1997
+++ linux/net/ipv4/packet.c	Mon Mar  3 09:37:44 1997
@@ -491,27 +491,33 @@
  
 struct proto packet_prot = 
 {
-	packet_close,
-	NULL,
-	NULL,			/* accept */
-	NULL,
-	NULL,
-	NULL,
-	datagram_poll,
-	NULL,			/* No ioctl */
-	packet_init,
-	NULL,
-	NULL,
-	NULL,			/* No set/get socket options */
-	NULL,
-	packet_sendmsg,		/* Sendmsg */
-	packet_recvmsg,		/* Recvmsg */
-	packet_bind,		/* Bind */
-	NULL,			/* Backlog_rcv */
-	128,
-	0,
-	"PACKET",
-	0, 0
+	(struct sock *)&packet_prot,	/* sklist_next */
+	(struct sock *)&packet_prot,	/* sklist_prev */
+	packet_close,			/* close */
+	NULL,				/* connect */
+	NULL,				/* accept */
+	NULL,				/* retransmit */
+	NULL,				/* write_wakeup */
+	NULL,				/* read_wakeup */
+	datagram_poll,			/* poll */
+	NULL,				/* ioctl */
+	packet_init,			/* init */
+	NULL,				/* destroy */
+	NULL,				/* shutdown */
+	NULL,				/* setsockopt */
+	NULL,				/* getsockopt */
+	packet_sendmsg,			/* Sendmsg */
+	packet_recvmsg,			/* Recvmsg */
+	packet_bind,			/* bind */
+	NULL,				/* backlog_rcv */
+	NULL,				/* hash */
+	NULL,				/* unhash */
+	NULL,				/* rehash */
+	NULL,				/* good_socknum */
+	NULL,				/* verify_bind */
+	128,				/* max_header */
+	0,				/* retransmits */
+	"PACKET",			/* name */
+	0,				/* inuse */
+	0				/* highestinuse */
 };
-
-	
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/proc.c linux/net/ipv4/proc.c
--- v2.1.27/linux/net/ipv4/proc.c	Thu Dec 12 06:54:24 1996
+++ linux/net/ipv4/proc.c	Mon Mar  3 09:37:44 1997
@@ -57,22 +57,17 @@
 static int
 get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t offset, int length)
 {
-	struct sock **s_array;
 	struct sock *sp;
 	struct tcp_opt *tp;
-	int i;
-	int timer_active;
-	int timer_active1;
-	int timer_active2;
+	int timer_active, timer_active1, timer_active2;
 	unsigned long timer_expires;
 	unsigned long  dest, src;
 	unsigned short destp, srcp;
-	int len=0;
+	int len=0, i = 0;
 	off_t pos=0;
 	off_t begin;
 	char tmpbuf[129];
   
-	s_array = pro->sock_array;
 	if (offset < 128) 
 		len += sprintf(buffer, "%-127s\n",
 			       "  sl  local_address rem_address   st tx_queue "
@@ -85,72 +80,56 @@
  *	concede defeat and cli().
  */
 	start_bh_atomic();
-
-	for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
-	{
-		sp = s_array[i];
-
-		while(sp != NULL) 
-		{
-			pos += 128;
-			if (pos < offset)
-			{
-				sp = sp->next;
-				continue;
-			}
-
-			tp = &(sp->tp_pinfo.af_tcp);
-
-			dest  = sp->daddr;
-			src   = sp->saddr;
-			destp = sp->dummy_th.dest;
-			srcp  = sp->dummy_th.source;
-
-			/* Since we are Little Endian we need to swap the bytes :-( */
-			destp = ntohs(destp);
-			srcp  = ntohs(srcp);
-			timer_active1 = del_timer(&sp->retransmit_timer);
-			timer_active2 = del_timer(&sp->timer);
-			if (!timer_active1) sp->retransmit_timer.expires=0;
-			if (!timer_active2) sp->timer.expires=0;
-			timer_active=0;
-			timer_expires=(unsigned)-1;
-			if (timer_active1 &&
-			  sp->retransmit_timer.expires < timer_expires) {
-			    timer_active=timer_active1;
-			    timer_expires=sp->retransmit_timer.expires;
-			}
-			if (timer_active2 &&
-			  sp->timer.expires < timer_expires) {
-			    timer_active=timer_active2;
-			    timer_expires=sp->timer.expires;
-			}
-			sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
-				" %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
-				i, src, srcp, dest, destp, sp->state, 
-				format==0?sp->write_seq-tp->snd_una:sp->wmem_alloc, 
-				format==0?tp->rcv_nxt-sp->copied_seq:sp->rmem_alloc,
-				timer_active, timer_expires-jiffies, (unsigned) sp->retransmits,
-				sp->socket ? sp->socket->inode->i_uid:0,
-				timer_active?sp->timeout:0,
-				sp->socket ? sp->socket->inode->i_ino:0);
-
-			if (timer_active1) add_timer(&sp->retransmit_timer);
-			if (timer_active2) add_timer(&sp->timer);
-			len += sprintf(buffer+len, "%-127s\n", tmpbuf);
-			/*
-			 * All sockets with (port mod SOCK_ARRAY_SIZE) = i
-			 * are kept in sock_array[i], so we must follow the
-			 * 'next' link to get them all.
-			 */
-			if(len >= length)
-				break;
-			sp = sp->next;
+	sp = pro->sklist_next;
+	while(sp != (struct sock *)pro) {
+		pos += 128;
+		if (pos < offset)
+			goto next;
+
+		tp = &(sp->tp_pinfo.af_tcp);
+		dest  = sp->daddr;
+		src   = sp->saddr;
+		destp = sp->dummy_th.dest;
+		srcp  = sp->dummy_th.source;
+
+		/* Since we are Little Endian we need to swap the bytes :-( */
+		destp = ntohs(destp);
+		srcp  = ntohs(srcp);
+		timer_active1 = del_timer(&sp->retransmit_timer);
+		timer_active2 = del_timer(&sp->timer);
+		if (!timer_active1) sp->retransmit_timer.expires=0;
+		if (!timer_active2) sp->timer.expires=0;
+		timer_active=0;
+		timer_expires=(unsigned)-1;
+		if (timer_active1 && sp->retransmit_timer.expires < timer_expires) {
+			timer_active=timer_active1;
+			timer_expires=sp->retransmit_timer.expires;
+		}
+		if (timer_active2 && sp->timer.expires < timer_expires) {
+			timer_active=timer_active2;
+			timer_expires=sp->timer.expires;
 		}
-		if(len>= length)
+		sprintf(tmpbuf, "%4d: %08lX:%04X %08lX:%04X"
+			" %02X %08X:%08X %02X:%08lX %08X %5d %8d %ld",
+			i, src, srcp, dest, destp, sp->state, 
+			format==0?sp->write_seq-tp->snd_una:sp->wmem_alloc, 
+			format==0?tp->rcv_nxt-sp->copied_seq:sp->rmem_alloc,
+			timer_active, timer_expires-jiffies, (unsigned) sp->retransmits,
+			sp->socket ? sp->socket->inode->i_uid:0,
+			timer_active?sp->timeout:0,
+			sp->socket ? sp->socket->inode->i_ino:0);
+
+		if (timer_active1) add_timer(&sp->retransmit_timer);
+		if (timer_active2) add_timer(&sp->timer);
+		len += sprintf(buffer+len, "%-127s\n", tmpbuf);
+		if(len >= length)
 			break;
+	next:
+		sp = sp->sklist_next;
+		i++;
 	}
 	end_bh_atomic();
+
 	begin = len - (pos - offset);
 	*start = buffer + begin;
 	len -= begin;
@@ -189,7 +168,6 @@
 
 	int len  = socket_get_info(buffer,start,offset,length);
 
-	len += sprintf(buffer+len,"SOCK_ARRAY_SIZE=%d\n",SOCK_ARRAY_SIZE);
 	len += sprintf(buffer+len,"TCP: inuse %d highest %d\n",
 		       tcp_prot.inuse, tcp_prot.highestinuse);
 	len += sprintf(buffer+len,"UDP: inuse %d highest %d\n",
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/raw.c linux/net/ipv4/raw.c
--- v2.1.27/linux/net/ipv4/raw.c	Sat Jan 25 13:46:14 1997
+++ linux/net/ipv4/raw.c	Mon Mar  3 09:37:44 1997
@@ -30,6 +30,7 @@
  *		Alan Cox	:	Beginnings of mrouted support.
  *		Alan Cox	:	Added IP_HDRINCL option.
  *		Alan Cox	:	Skip broadcast check if BSDism set.
+ *		David S. Miller	:	New socket lookup architecture.
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -58,12 +59,89 @@
 #include <net/sock.h>
 #include <net/icmp.h>
 #include <net/udp.h>
+#include <net/raw.h>
 #include <net/checksum.h>
 
 #ifdef CONFIG_IP_MROUTE
 struct sock *mroute_socket=NULL;
 #endif
 
+struct sock *raw_v4_htable[RAWV4_HTABLE_SIZE];
+
+static void raw_v4_hash(struct sock *sk)
+{
+	struct sock **skp;
+	int num = sk->num;
+
+	num &= (RAWV4_HTABLE_SIZE - 1);
+	skp = &raw_v4_htable[num];
+	SOCKHASH_LOCK();
+	sk->next = *skp;
+	*skp = sk;
+	sk->hashent = num;
+	SOCKHASH_UNLOCK();
+}
+
+static void raw_v4_unhash(struct sock *sk)
+{
+	struct sock **skp;
+	int num = sk->num;
+
+	num &= (RAWV4_HTABLE_SIZE - 1);
+	skp = &raw_v4_htable[num];
+
+	SOCKHASH_LOCK();
+	while(*skp != NULL) {
+		if(*skp == sk) {
+			*skp = sk->next;
+			break;
+		}
+		skp = &((*skp)->next);
+	}
+	SOCKHASH_UNLOCK();
+}
+
+static void raw_v4_rehash(struct sock *sk)
+{
+	struct sock **skp;
+	int num = sk->num;
+	int oldnum = sk->hashent;
+
+	num &= (RAWV4_HTABLE_SIZE - 1);
+	skp = &raw_v4_htable[oldnum];
+
+	SOCKHASH_LOCK();
+	while(*skp != NULL) {
+		if(*skp == sk) {
+			*skp = sk->next;
+			break;
+		}
+		skp = &((*skp)->next);
+	}
+	sk->next = raw_v4_htable[num];
+	raw_v4_htable[num] = sk;
+	sk->hashent = num;
+	SOCKHASH_UNLOCK();
+}
+
+/* Grumble... icmp and ip_input want to get at this... */
+struct sock *raw_v4_lookup(struct sock *sk, unsigned short num,
+			   unsigned long raddr, unsigned long laddr)
+{
+	struct sock *s = sk;
+
+	SOCKHASH_LOCK();
+	for(s = sk; s; s = s->next) {
+		if((s->num == num) 				&&
+		   !(s->dead && (s->state == TCP_CLOSE))	&&
+		   !(s->daddr && s->daddr != raddr) 		&&
+		   !(s->rcv_saddr && s->rcv_saddr != laddr))
+			break; /* gotcha */
+	}
+	SOCKHASH_UNLOCK();
+	return s;
+}
+
 /*
  *	Raw_err does not currently get called by the icmp module - FIXME:
  */
@@ -339,12 +417,30 @@
 	destroy_sock(sk);
 }
 
-
-static int raw_init(struct sock *sk)
+/* This gets rid of all the nasties in af_inet. -DaveM */
+static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
 {
-	return(0);
-}
+	struct sockaddr_in *addr = (struct sockaddr_in *) uaddr;
+	int chk_addr_ret;
 
+	if((sk->state != TCP_CLOSE) || (addr_len < sizeof(struct sockaddr_in)))
+		return -EINVAL;
+	chk_addr_ret = __ip_chk_addr(addr->sin_addr.s_addr);
+	if(addr->sin_addr.s_addr != 0 && chk_addr_ret != IS_MYADDR &&
+	   chk_addr_ret != IS_MULTICAST && chk_addr_ret != IS_BROADCAST) {
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+		/* Superuser may bind to any address to allow transparent proxying. */
+		if(!suser())
+#endif
+			return -EADDRNOTAVAIL;
+	}
+	sk->rcv_saddr = sk->saddr = addr->sin_addr.s_addr;
+	if(chk_addr_ret == IS_MULTICAST || chk_addr_ret == IS_BROADCAST)
+		sk->saddr = 0;  /* Use device */
+	dst_release(sk->dst_cache);
+	sk->dst_cache = NULL;
+	return 0;
+}
 
 /*
  *	This should be easy, if there is something there
@@ -406,30 +502,37 @@
 
 
 struct proto raw_prot = {
-	raw_close,
-	udp_connect,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	datagram_poll,
-#ifdef CONFIG_IP_MROUTE	
-	ipmr_ioctl,
+	(struct sock *)&raw_prot,	/* sklist_next */
+	(struct sock *)&raw_prot,	/* sklist_prev */
+	raw_close,			/* close */
+	udp_connect,			/* connect */
+	NULL,				/* accept */
+	NULL,				/* retransmit */
+	NULL,				/* write_wakeup */
+	NULL,				/* read_wakeup */
+	datagram_poll,			/* poll */
+#ifdef CONFIG_IP_MROUTE
+	ipmr_ioctl,			/* ioctl */
 #else
-	NULL,
-#endif		
-	raw_init,
-	NULL,
-	NULL,
-	ip_setsockopt,
-	ip_getsockopt,
-	raw_sendmsg,
-	raw_recvmsg,
-	NULL,		/* No special bind */
-	raw_rcv_skb,
-	128,
-	0,
-	"RAW",
-	0, 0,
-	NULL
+	NULL,				/* ioctl */
+#endif
+	NULL,				/* init */
+	NULL,				/* destroy */
+	NULL,				/* shutdown */
+	ip_setsockopt,			/* setsockopt */
+	ip_getsockopt,			/* getsockopt */
+	raw_sendmsg,			/* sendmsg */
+	raw_recvmsg,			/* recvmsg */
+	raw_bind,			/* bind */
+	raw_rcv_skb,			/* backlog_rcv */
+	raw_v4_hash,			/* hash */
+	raw_v4_unhash,			/* unhash */
+	raw_v4_rehash,			/* rehash */
+	NULL,				/* good_socknum */
+	NULL,				/* verify_bind */
+	128,				/* max_header */
+	0,				/* retransmits */
+	"RAW",				/* name */
+	0,				/* inuse */
+	0				/* highestinuse */
 };
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/tcp.c linux/net/ipv4/tcp.c
--- v2.1.27/linux/net/ipv4/tcp.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ipv4/tcp.c	Mon Mar  3 09:37:44 1997
@@ -817,6 +817,7 @@
 		
 		if (sk->state != TCP_SYN_SENT && sk->state != TCP_SYN_RECV)
 		{
+			printk("tcp_do_sendmsg1: EPIPE dude...\n");
 			if (sk->keepopen)
 				send_sig(SIGPIPE, current, 0);
 			return -EPIPE;
@@ -869,6 +870,7 @@
 			{
 				if (copied)
 					return copied;
+				printk("tcp_do_sendmsg2: SEND_SHUTDOWN, EPIPE...\n");
 				send_sig(SIGPIPE,current,0);
 				return -EPIPE;
 			}
@@ -1613,7 +1615,6 @@
 
 	lock_sock(sk);
 
-	tcp_cache_zap();
 	if(sk->state == TCP_LISTEN)
 	{
 		/* Special case */
@@ -1621,6 +1622,7 @@
 		tcp_close_pending(sk);
 		release_sock(sk);
 		sk->dead = 1;
+		sk->prot->unhash(sk);
 		return;
 	}
 
@@ -1667,12 +1669,6 @@
 		sti();
 	}
 
-	/*
-	 * This will destroy it. The timers will take care of actually
-	 * free'ing up the memory.
-	 */
-	tcp_cache_zap();	/* Kill the cache again. */
-
 	/* Now that the socket is dead, if we are in the FIN_WAIT2 state
 	 * we may need to set up a timer.
          */
@@ -1687,6 +1683,9 @@
 
 	release_sock(sk);
 	sk->dead = 1;
+
+	if(sk->state == TCP_CLOSE)
+		sk->prot->unhash(sk);
 }
 
 
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/tcp_input.c linux/net/ipv4/tcp_input.c
--- v2.1.27/linux/net/ipv4/tcp_input.c	Sun Feb  2 05:18:49 1997
+++ linux/net/ipv4/tcp_input.c	Mon Mar  3 09:37:44 1997
@@ -21,12 +21,6 @@
  */
 
 /*
- *	TODO
- *		- A better sock cache
- *
- */
-
-/*
  * Changes:
  *		Pedro Roque	:	Fast Retransmit/Recovery.
  *					Two receive queues.
@@ -1333,7 +1327,6 @@
 
 	if(th->syn && skb->seq != sk->syn_seq)
 	{
-		printk(KERN_DEBUG "syn in established state\n");
 		tcp_reset(sk, skb);
 		kfree_skb(skb, FREE_READ);
 		return 1;
@@ -1409,7 +1402,7 @@
 
 	/*
 	 *	state == CLOSED
-	 *	tested in tcp_v{4,6}_rcv
+	 *	Hash lookup always fails, so no worries. -DaveM 
 	 */
 
 	switch (sk->state) {
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/tcp_ipv4.c linux/net/ipv4/tcp_ipv4.c
--- v2.1.27/linux/net/ipv4/tcp_ipv4.c	Tue Feb  4 06:44:25 1997
+++ linux/net/ipv4/tcp_ipv4.c	Mon Mar  3 09:37:44 1997
@@ -22,6 +22,12 @@
  *      2 of the License, or (at your option) any later version.
  */
 
+/*
+ * Changes:
+ *		David S. Miller	:	New socket lookup architecture.
+ *					This code is dedicated to John Dyson.
+ */
+
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
@@ -38,44 +44,360 @@
 void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len, 
 		       struct sk_buff *skb);
 
-/*
- *	Cached last hit socket
+/* This is for sockets with full identity only.  Sockets here will always
+ * be without wildcards and will have the following invariant:
+ *          TCP_ESTABLISHED <= sk->state < TCP_CLOSE
  */
- 
-static volatile unsigned long 	th_cache_saddr, th_cache_daddr;
-static volatile unsigned short  th_cache_dport, th_cache_sport;
-static volatile struct sock	*th_cache_sk;
+struct sock *tcp_established_hash[TCP_HTABLE_SIZE];
+
+/* All sockets in TCP_LISTEN state will be in here.  This is the only table
+ * where wildcard'd TCP sockets can exist.  Hash function here is just local
+ * port number.  XXX Fix or we'll lose with thousands of IP aliases...
+ */
+struct sock *tcp_listening_hash[TCP_LHTABLE_SIZE];
 
-void tcp_cache_zap(void)
+/* Ok, let's try this, I give up, we do need a local binding
+ * TCP hash as well as the others for fast bind/connect.
+ */
+struct sock *tcp_bound_hash[TCP_BHTABLE_SIZE];
+
+static __inline__ int tcp_hashfn(__u32 laddr, __u16 lport,
+				 __u32 faddr, __u16 fport)
 {
-	th_cache_sk=NULL;
+	return ((laddr ^ lport) ^ (faddr ^ fport)) & (TCP_HTABLE_SIZE - 1);
 }
 
-/*
- *	Find the socket, using the last hit cache if applicable.
- *	The cache is not quite right...
+static __inline__ int tcp_sk_hashfn(struct sock *sk)
+{
+	__u32 laddr = sk->rcv_saddr;
+	__u16 lport = sk->num;
+	__u32 faddr = sk->daddr;
+	__u16 fport = sk->dummy_th.dest;
+
+	return tcp_hashfn(laddr, lport, faddr, fport);
+}
+
+static int tcp_v4_verify_bind(struct sock *sk, unsigned short snum)
+{
+	struct sock *sk2;
+	int retval = 0, sk_reuse = sk->reuse;
+
+	SOCKHASH_LOCK();
+	sk2 = tcp_bound_hash[tcp_bhashfn(snum)];
+	for(; sk2 != NULL; sk2 = sk2->prev) {
+		if((sk2->num == snum) && (sk2 != sk)) {
+			unsigned char state = sk2->state;
+			int sk2_reuse = sk2->reuse;
+
+			if(!sk2->rcv_saddr || !sk->rcv_saddr) {
+				if((!sk2_reuse)			||
+				   (!sk_reuse)			||
+				   (state != TCP_LISTEN)) {
+					retval = 1;
+					break;
+				}
+			} else if(sk2->rcv_saddr == sk->rcv_saddr) {
+				if((!sk_reuse)			||
+				   (!sk2_reuse)			||
+				   (state == TCP_LISTEN)) {
+					retval = 1;
+					break;
+				}
+			}
+		}
+	}
+	SOCKHASH_UNLOCK();
+
+	return retval;
+}
+
+static __inline__ int tcp_lport_inuse(int num)
+{
+	struct sock *sk = tcp_bound_hash[tcp_bhashfn(num)];
+
+	for(; sk != NULL; sk = sk->prev) {
+		if(sk->num == num)
+			return 1;
+	}
+	return 0;
+}
+
+/* Find a "good" local port, this is family independant.
+ * There are several strategies working in unison here to
+ * get the best possible performance.  The current socket
+ * load is kept track of, if it is zero there is a strong
+ * likely hood that there is a zero length chain we will
+ * find with a small amount of searching, else the load is
+ * what we shoot for for when the chains all have at least
+ * one entry.  The base helps us walk the chains in an
+ * order such that a good chain is found as quickly as possible.  -DaveM
  */
+unsigned short tcp_good_socknum(void)
+{
+	static int start = PROT_SOCK;
+	static int binding_contour = 0;
+	int best = 0;
+	int size = 32767; /* a big num. */
+	int retval = 0, i, end, bc;
+
+	SOCKHASH_LOCK();
+	i = tcp_bhashfn(start);
+	end = i + TCP_BHTABLE_SIZE;
+	bc = binding_contour;
+	do {
+		struct sock *sk = tcp_bound_hash[tcp_bhashfn(i)];
+		if(!sk) {
+			retval = (start + i);
+			start  = (retval + 1);
+
+			/* Check for decreasing load. */
+			if(bc != 0)
+				binding_contour = 0;
+			goto done;
+		} else {
+			int j = 0;
+			do { sk = sk->prev; } while(++j < size && sk);
+			if(j < size) {
+				best = (start + i);
+				size = j;
+				if(bc && size <= bc) {
+					start = best + 1;
+					goto verify;
+				}
+			}
+		}
+	} while(++i != end);
 
-static inline struct sock * get_tcp_sock(u32 saddr, u16 sport,
-					 u32 daddr, u16 dport)
+	/* Socket load is increasing, adjust our load average. */
+	binding_contour = size;
+verify:
+	if(size < binding_contour)
+		binding_contour = size;
+
+	if(best > 32767)
+		best -= (32768 - PROT_SOCK);
+
+	while(tcp_lport_inuse(best))
+		best += TCP_BHTABLE_SIZE;
+	retval = best;
+done:
+	if(start > 32767)
+		start -= (32768 - PROT_SOCK);
+
+	SOCKHASH_UNLOCK();
+
+	return retval;
+}
+
+static void tcp_v4_hash(struct sock *sk)
 {
-	struct sock * sk;
+	unsigned char state;
+
+	SOCKHASH_LOCK();
+	state = sk->state;
+	if(state != TCP_CLOSE || !sk->dead) {
+		struct sock **htable;
+
+		if(state == TCP_LISTEN) {
+			sk->hashent = tcp_sk_listen_hashfn(sk);
+			htable = &tcp_listening_hash[0];
+		} else {
+			sk->hashent = tcp_sk_hashfn(sk);
+			htable = &tcp_established_hash[0];
+		}
+		sk->next = htable[sk->hashent];
+		htable[sk->hashent] = sk;
+		sk->hashtable = htable;
+		tcp_sk_bindify(sk);
+	}
+	SOCKHASH_UNLOCK();
+}
+
+static void tcp_v4_unhash(struct sock *sk)
+{
+	struct sock **htable;
+
+	SOCKHASH_LOCK();
+	htable = sk->hashtable;
+	if(htable) {
+		struct sock **skp = &(htable[sk->hashent]);
+		while(*skp != NULL) {
+			if(*skp == sk) {
+				*skp = sk->next;
+				break;
+			}
+			skp = &((*skp)->next);
+		}
+		sk->hashtable = NULL;
+	}
+	if(sk->state == TCP_CLOSE && sk->dead)
+		tcp_sk_unbindify(sk);
+	SOCKHASH_UNLOCK();
+}
+
+static void tcp_v4_rehash(struct sock *sk)
+{
+	struct sock **htable;
+	unsigned char state;
+
+	SOCKHASH_LOCK();
+	htable = &(sk->hashtable[sk->hashent]);
+	state = sk->state;
+	if(htable) {
+		while(*htable != NULL) {
+			if(*htable == sk) {
+				*htable = sk->next;
+				break;
+			}
+			htable = &((*htable)->next);
+		}
+	}
+	tcp_sk_unbindify(sk);
+	htable = NULL;
+	if(state != TCP_CLOSE || !sk->dead) {
+		if(state == TCP_LISTEN) {
+			sk->hashent = tcp_sk_listen_hashfn(sk);
+			htable = &tcp_listening_hash[0];
+		} else {
+			sk->hashent = tcp_sk_hashfn(sk);
+			htable = &tcp_established_hash[0];
+		}
+		sk->next = htable[sk->hashent];
+		htable[sk->hashent] = sk;
+		tcp_sk_bindify(sk);
+	}
+	sk->hashtable = htable;
+	SOCKHASH_UNLOCK();
+}
 
-	sk = (struct sock *) th_cache_sk;
-	if (!sk || saddr != th_cache_saddr || daddr != th_cache_daddr ||
-	    sport != th_cache_sport || dport != th_cache_dport) {
-		sk = get_sock(&tcp_prot, dport, saddr, sport, daddr);
-		if (sk) {
-			th_cache_saddr=saddr;
-			th_cache_daddr=daddr;
-  			th_cache_dport=dport;
-			th_cache_sport=sport;
-			th_cache_sk=sk;
+/* Don't inline this cruft.  Here are some nice properties to
+ * exploit here.  The BSD API does not allow a listening TCP
+ * to specify the remote port nor the remote address for the
+ * connection.  So always assume those are both wildcarded
+ * during the search since they can never be otherwise.
+ *
+ * XXX Later on, hash on both local port _and_ local address,
+ * XXX to handle a huge IP alias'd box.  Keep in mind that
+ * XXX such a scheme will require us to run through the listener
+ * XXX hash twice, once for local addresses bound, and once for
+ * XXX the local address wildcarded (because the hash is different).
+ */
+static struct sock *tcp_v4_lookup_longway(u32 daddr, unsigned short hnum)
+{
+	struct sock *sk = tcp_listening_hash[tcp_lhashfn(hnum)];
+	struct sock *result = NULL;
+
+	for(; sk; sk = sk->next) {
+		if(sk->num == hnum) {
+			__u32 rcv_saddr = sk->rcv_saddr;
+
+			if(rcv_saddr) {
+				if(rcv_saddr == daddr)
+					return sk; /* Best possible match. */
+			} else if(!result)
+				result = sk;
 		}
 	}
+	return result;
+}
+
+/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
+ * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
+ */
+static inline struct sock *__tcp_v4_lookup(struct tcphdr *th,
+					   u32 saddr, u16 sport, u32 daddr, u16 dport)
+{
+	unsigned short hnum = ntohs(dport);
+	struct sock *sk;
+
+	/* Optimize here for direct hit, only listening connections can
+	 * have wildcards anyways.  It is assumed that this code only
+	 * gets called from within NET_BH.
+	 */
+	sk = tcp_established_hash[tcp_hashfn(daddr, hnum, saddr, sport)];
+	for(; sk; sk = sk->next)
+		if(sk->daddr		== saddr		&& /* remote address */
+		   sk->dummy_th.dest	== sport		&& /* remote port    */
+		   sk->num		== hnum			&& /* local port     */
+		   sk->rcv_saddr	== daddr)		   /* local address  */
+			goto hit; /* You sunk my battleship! */
+	sk = tcp_v4_lookup_longway(daddr, hnum);
+hit:
 	return sk;
 }
 
+__inline__ struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport)
+{
+	return __tcp_v4_lookup(0, saddr, sport, daddr, dport);
+}
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+#define secondlist(hpnum, sk, fpass) \
+({ struct sock *s1; if(!(sk) && (fpass)--) \
+	s1 = tcp_bound_hash[tcp_bhashfn(hpnum)]; \
+   else \
+	s1 = (sk); \
+   s1; \
+})
+
+#define tcp_v4_proxy_loop_init(hnum, hpnum, sk, fpass) \
+	secondlist((hpnum), tcp_bound_hash[tcp_bhashfn(hnum)],(fpass))
+
+#define tcp_v4_proxy_loop_next(hnum, hpnum, sk, fpass) \
+	secondlist((hpnum),(sk)->next,(fpass))
+
+struct sock *tcp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
+				 unsigned short rnum, unsigned long laddr,
+				 unsigned long paddr, unsigned short pnum)
+{
+	struct sock *s, *result = NULL;
+	int badness = -1;
+	unsigned short hnum = ntohs(num);
+	unsigned short hpnum = ntohs(pnum);
+	int firstpass = 1;
+
+	/* This code must run only from NET_BH. */
+	for(s = tcp_v4_proxy_loop_init(hnum, hpnum, s, firstpass);
+	    s != NULL;
+	    s = tcp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) {
+		if(s->num == hnum || s->num == hpnum) {
+			int score = 0;
+			if(s->dead && (s->state == TCP_CLOSE))
+				continue;
+			if(s->rcv_saddr) {
+				if((s->num != hpnum || s->rcv_saddr != paddr) &&
+				   (s->num != hnum || s->rcv_saddr != laddr))
+					continue;
+				score++;
+			}
+			if(s->daddr) {
+				if(s->daddr != raddr)
+					continue;
+				score++;
+			}
+			if(s->dummy_th.dest) {
+				if(s->dummy_th.dest != rnum)
+					continue;
+				score++;
+			}
+			if(score == 3 && s->num == hnum) {
+				result = s;
+				break;
+			} else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
+					result = s;
+					badness = score;
+			}
+		}
+	}
+	return result;
+}
+
+#undef secondlist
+#undef tcp_v4_proxy_loop_init
+#undef tcp_v4_proxy_loop_next
+
+#endif
+
 static __u32 tcp_v4_init_sequence(struct sock *sk, struct sk_buff *skb)
 {
 	return secure_tcp_sequence_number(sk->saddr, sk->daddr,
@@ -94,27 +416,24 @@
 
 static int tcp_unique_address(u32 saddr, u16 snum, u32 daddr, u16 dnum)
 {
-	int retval = 1;
+	int retval = 1, hashent = tcp_hashfn(saddr, snum, daddr, dnum);
 	struct sock * sk;
 
-	/* Make sure we are allowed to connect here. */
-	cli();
-	for (sk = tcp_prot.sock_array[snum & (SOCK_ARRAY_SIZE -1)];
-			sk != NULL; sk = sk->next)
-	{
-		/* hash collision? */
-		if (sk->num != snum)
-			continue;
-		if (sk->saddr != saddr)
-			continue;
-		if (sk->daddr != daddr)
-			continue;
-		if (sk->dummy_th.dest != dnum)
-			continue;
-		retval = 0;
-		break;
+	/* Make sure we are allowed to connect here.
+	 * But freeze the hash while we snoop around.
+	 */
+	SOCKHASH_LOCK();
+	sk = tcp_established_hash[hashent];
+	for (; sk != NULL; sk = sk->next) {
+		if(sk->daddr		== daddr		&& /* remote address */
+		   sk->dummy_th.dest	== dnum			&& /* remote port */
+		   sk->num		== snum			&& /* local port */
+		   sk->saddr		== saddr) {		   /* local address */
+			retval = 0;
+			break;
+		}
 	}
-	sti();
+	SOCKHASH_UNLOCK();
 	return retval;
 }
 
@@ -184,6 +503,7 @@
 		sk->priority = rt->u.dst.priority;
 
 	sk->dummy_th.dest = usin->sin_port;
+
 	sk->write_seq = secure_tcp_sequence_number(sk->saddr, sk->daddr,
 						   sk->dummy_th.source,
 						   usin->sin_port);
@@ -262,14 +582,13 @@
 	buff->csum = csum_partial(ptr, 4, 0);
 	tcp_v4_send_check(sk, t1, sizeof(struct tcphdr) + 4, buff);
 
-	/*
-	 *	This must go first otherwise a really quick response 
-	 *	will get reset.
-	 */
-
-	tcp_cache_zap();
 	tcp_set_state(sk,TCP_SYN_SENT);
 
+	/* Socket identity change complete, no longer
+	 * in TCP_CLOSE, so rehash.
+	 */
+	tcp_v4_rehash(sk);
+
 	tp->rto = rt->u.dst.rtt;
 
 	tcp_init_xmit_timers(sk);
@@ -348,7 +667,7 @@
 	int code = skb->h.icmph->code;
 	struct sock *sk;
 
-	sk = get_sock(&tcp_prot, th->source, iph->daddr, th->dest, iph->saddr);
+	sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
 
 	if (sk == NULL)
 		return;
@@ -402,39 +721,13 @@
 	}
 }
 
-/*
- *	This routine computes a TCP checksum.
- *
- *	Modified January 1995 from a go-faster DOS routine by
- *	Jorge Cwik <jorge@laser.satlink.net>
- */
+/* This routine computes an IPv4 TCP checksum. */
 void tcp_v4_send_check(struct sock *sk, struct tcphdr *th, int len, 
 		       struct sk_buff *skb)
 {
-	__u32 saddr = sk->saddr;
-	__u32 daddr = sk->daddr;
-#ifdef DEBUG_TCP_CHECK
-	u16 check;
-#endif
 	th->check = 0;
-	th->check = tcp_v4_check(th, len, saddr, daddr,
-				 csum_partial((char *)th, sizeof(*th), 
-					      skb->csum));
-
-#ifdef DEBUG_TCP_CHECK
-	check = th->check;
-	th->check = 0;
-	th->check = tcp_v4_check(th, len, saddr, daddr,
-		csum_partial((char *)th,len,0));
-	if (check != th->check) {
-		static int count = 0;
-		if (++count < 10) {
-			printk("Checksum %x (%x) from %p\n", th->check, check,
-			       __builtin_return_address(0));
-			printk("TCP=<off:%d a:%d s:%d f:%d> len=%d\n", th->doff*4, th->ack, th->syn, th->fin, len);
-		}
-	}
-#endif
+	th->check = tcp_v4_check(th, len, sk->saddr, sk->daddr,
+				 csum_partial((char *)th, sizeof(*th), skb->csum));
 }
 
 /*
@@ -504,7 +797,7 @@
 	struct tcphdr *th = (struct tcphdr *)(skb->nh.raw + iph->ihl*4);
 	struct sock *sk;
 
-	sk = get_sock(&tcp_prot, th->dest, iph->saddr, th->source, iph->daddr);
+	sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
 
 	if (!sk)
 		return 0;
@@ -717,6 +1010,10 @@
 	}
 
 	memcpy(newsk, sk, sizeof(*newsk));
+
+	/* Or else we die! -DaveM */
+	newsk->sklist_next = NULL;
+
 	newsk->opt = NULL;
 	newsk->dst_cache  = NULL;
 	skb_queue_head_init(&newsk->write_queue);
@@ -833,10 +1130,8 @@
 	
 	newsk->mss = min(req->mss, snd_mss);
 	
-	inet_put_sock(newsk->num, newsk);
-
-	tcp_cache_zap();
-
+	tcp_v4_hash(newsk);
+	add_to_prot_sklist(newsk);
 	return newsk;
 }
 
@@ -1009,11 +1304,11 @@
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 	if (IPCB(skb)->redirport)
-		sk = get_sock_proxy(&tcp_prot, th->dest, saddr, th->source, daddr, skb->dev->pa_addr, IPCB(skb)->redirport);
+		sk = tcp_v4_proxy_lookup(th->dest, saddr, th->source, daddr,
+					 skb->dev->pa_addr, IPCB(skb)->redirport);
 	else
 #endif
-	sk = get_tcp_sock(saddr, th->source, daddr, th->dest);
-
+	sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest);
 	if (!sk)
 		goto no_tcp_socket;
 
@@ -1084,9 +1379,8 @@
 
 static struct sock * tcp_v4_get_sock(struct sk_buff *skb, struct tcphdr *th)
 {
-	struct sock *sk;
-	sk = get_tcp_sock(skb->nh.iph->saddr, th->source, skb->nh.iph->daddr, th->dest);
-	return sk;
+	return tcp_v4_lookup(skb->nh.iph->saddr, th->source,
+			     skb->nh.iph->daddr, th->dest);
 }
 
 static void v4_addr2sockaddr(struct sock *sk, struct sockaddr * uaddr)
@@ -1197,26 +1491,33 @@
 }
 
 struct proto tcp_prot = {
-	tcp_close,
-	tcp_v4_connect,
-	tcp_accept,
-	NULL,
-	tcp_write_wakeup,
-	tcp_read_wakeup,
-	tcp_poll,
-	tcp_ioctl,
-	tcp_v4_init_sock,
-	tcp_v4_destroy_sock,
-	tcp_shutdown,
-	tcp_setsockopt,
-	tcp_getsockopt,
-	tcp_v4_sendmsg,
-	tcp_recvmsg,
-	NULL,		/* No special bind()	*/
-	tcp_v4_backlog_rcv,
-	128,
-	0,
-	"TCP",
-	0, 0,
-	NULL
+	(struct sock *)&tcp_prot,	/* sklist_next */
+	(struct sock *)&tcp_prot,	/* sklist_prev */
+	tcp_close,			/* close */
+	tcp_v4_connect,			/* connect */
+	tcp_accept,			/* accept */
+	NULL,				/* retransmit */
+	tcp_write_wakeup,		/* write_wakeup */
+	tcp_read_wakeup,		/* read_wakeup */
+	tcp_poll,			/* poll */
+	tcp_ioctl,			/* ioctl */
+	tcp_v4_init_sock,		/* init */
+	tcp_v4_destroy_sock,		/* destroy */
+	tcp_shutdown,			/* shutdown */
+	tcp_setsockopt,			/* setsockopt */
+	tcp_getsockopt,			/* getsockopt */
+	tcp_v4_sendmsg,			/* sendmsg */
+	tcp_recvmsg,			/* recvmsg */
+	NULL,				/* bind */
+	tcp_v4_backlog_rcv,		/* backlog_rcv */
+	tcp_v4_hash,			/* hash */
+	tcp_v4_unhash,			/* unhash */
+	tcp_v4_rehash,			/* rehash */
+	tcp_good_socknum,		/* good_socknum */
+	tcp_v4_verify_bind,		/* verify_bind */
+	128,				/* max_header */
+	0,				/* retransmits */
+	"TCP",				/* name */
+	0,				/* inuse */
+	0				/* highestinuse */
 };
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/tcp_output.c linux/net/ipv4/tcp_output.c
--- v2.1.27/linux/net/ipv4/tcp_output.c	Mon Jan 27 01:04:48 1997
+++ linux/net/ipv4/tcp_output.c	Mon Mar  3 09:37:45 1997
@@ -27,6 +27,8 @@
  *				:	AF independence
  *
  *		Linus Torvalds	:	send_delayed_ack
+ *		David S. Miller	:	Charge memory using the right skb
+ *					during syn/ack processing.
  *
  */
 
@@ -905,7 +907,7 @@
 	skb->when = jiffies;
 	buff = skb_clone(skb, GFP_ATOMIC);
 
-	skb_set_owner_w(skb, sk);
+	skb_set_owner_w(buff, sk);
 
 	tp->af_specific->queue_xmit(buff);
 
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/tcp_timer.c linux/net/ipv4/tcp_timer.c
--- v2.1.27/linux/net/ipv4/tcp_timer.c	Thu Dec 12 06:54:25 1996
+++ linux/net/ipv4/tcp_timer.c	Mon Mar  3 09:37:45 1997
@@ -348,28 +348,29 @@
  */
 #define MAX_KA_PROBES	5
 
+/* Keepopen's are only valid for "established" TCP's, nicely our listener
+ * hash gets rid of most of the useless testing, so we run through a couple
+ * of the established hash chains each clock tick.  -DaveM
+ */
 static void tcp_keepalive(unsigned long data)
 {
-	struct sock *sk;
+	static int chain_start = 0;
 	int count = 0;
 	int i;
 	
-	for(i=0; i < SOCK_ARRAY_SIZE; i++)
-	{
-		sk = tcp_prot.sock_array[i];
-		while (sk)
-		{
-			if (sk->keepopen)
-			{
+	for(i = chain_start; i < (chain_start + (TCP_HTABLE_SIZE >> 2)); i++) {
+		struct sock *sk = tcp_established_hash[i];
+		while(sk) {
+			if(sk->keepopen) {
 				count += tcp_keepopen_proc(sk);
+				if(count == MAX_KA_PROBES)
+					goto out;
 			}
-
-			if (count == MAX_KA_PROBES)
-				return;
-			
-			sk = sk->next;	    
+			sk = sk->next;
 		}
 	}
+out:
+	chain_start = ((chain_start + (TCP_HTABLE_SIZE>>2)) & (TCP_HTABLE_SIZE - 1));
 }
 
 /*
@@ -457,26 +458,24 @@
  *	Slow timer for SYN-RECV sockets
  */
 
+/* This now scales very nicely. -DaveM */
 static void tcp_syn_recv_timer(unsigned long data)
 {
 	struct sock *sk;
 	unsigned long now = jiffies;
 	int i;
 
-	for(i=0; i < SOCK_ARRAY_SIZE; i++)
-	{
-		sk = tcp_prot.sock_array[i];
-		while (sk)
-		{
+	for(i = 0; i < TCP_LHTABLE_SIZE; i++) {
+		sk = tcp_listening_hash[i];
+
+		while(sk) {
 			struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
 			
-			if (sk->state == TCP_LISTEN && !sk->users &&
-			    tp->syn_wait_queue)
-			{
+			/* TCP_LISTEN is implied. */
+			if (!sk->users && tp->syn_wait_queue) {
 				struct open_request *req;
 
 				req = tp->syn_wait_queue;
-
 				do {
 					struct open_request *conn;
 				  
@@ -484,17 +483,13 @@
 					req = req->dl_next;
 
 					if (conn->sk)
-					{
 						continue;
-					}
-					
+
 					if ((long)(now - conn->expires) <= 0)
 						break;
 
 					tcp_synq_unlink(tp, conn);
-					
-					if (conn->retrans >= TCP_RETR1)
-					{
+					if (conn->retrans >= TCP_RETR1) {
 #ifdef TCP_DEBUG
 						printk(KERN_DEBUG "syn_recv: "
 						       "too many retransmits\n");
@@ -506,16 +501,15 @@
 
 						if (!tp->syn_wait_queue)
 							break;
-					}
-					else
-					{
+					} else {
 						__u32 timeo;
-						
+
 						(*conn->class->rtx_syn_ack)(sk, conn);
 
 						conn->retrans++;
 #ifdef TCP_DEBUG
-						printk(KERN_DEBUG "syn_ack rtx %d\n", conn->retrans);
+						printk(KERN_DEBUG "syn_ack rtx %d\n",
+						       conn->retrans);
 #endif
 						timeo = min((TCP_TIMEOUT_INIT 
 							     << conn->retrans),
@@ -525,7 +519,6 @@
 					}
 				} while (req != tp->syn_wait_queue);
 			}
-			
 			sk = sk->next;
 		}
 	}
diff -u --recursive --new-file v2.1.27/linux/net/ipv4/udp.c linux/net/ipv4/udp.c
--- v2.1.27/linux/net/ipv4/udp.c	Fri Feb  7 05:54:55 1997
+++ linux/net/ipv4/udp.c	Mon Mar  3 09:37:45 1997
@@ -49,6 +49,9 @@
  *		Mike Shaver	:	RFC1122 checks.
  *		Alan Cox	:	Nonblocking error fix.
  *	Willy Konynenberg	:	Transparent proxying support.
+ *		David S. Miller	:	New socket lookup architecture.
+ *					Last socket cache retained as it
+ *					does have a high hit rate.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -115,29 +118,295 @@
 
 struct udp_mib		udp_statistics;
 
-/*
- *	Cached last hit socket
+struct sock *udp_hash[UDP_HTABLE_SIZE];
+
+static int udp_v4_verify_bind(struct sock *sk, unsigned short snum)
+{
+	struct sock *sk2;
+	int retval = 0;
+
+	SOCKHASH_LOCK();
+	for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
+		if((sk2->num == snum) && (sk2 != sk)) {
+			if(!sk2->rcv_saddr || !sk->rcv_saddr) {
+				if(sk->reuse && sk->reuse && (sk2->state != TCP_LISTEN))
+					continue;
+				retval = 1;
+				break;
+			}
+			if((sk2->rcv_saddr == sk->rcv_saddr) &&
+			   (!sk->reuse || !sk2->reuse || (sk2->state == TCP_LISTEN))) {
+				retval = 1;
+				break;
+			}
+		}
+	}
+	SOCKHASH_UNLOCK();
+	return retval;
+}
+
+static inline int udp_lport_inuse(int num)
+{
+	struct sock *sk = udp_hash[num & (UDP_HTABLE_SIZE - 1)];
+
+	for(; sk != NULL; sk = sk->next) {
+		if(sk->num == num)
+			return 1;
+	}
+	return 0;
+}
+
+/* Shared by v4/v6 tcp. */
+unsigned short udp_good_socknum(void)
+{
+	static int start = 0;
+	unsigned short base;
+	int i, best = 0, size = 32767; /* a big num. */
+	int result;
+
+	base = PROT_SOCK + (start & 1023) + 1;
+
+	SOCKHASH_LOCK();
+	for(i = 0; i < UDP_HTABLE_SIZE; i++) {
+		struct sock *sk = udp_hash[i];
+		if(!sk) {
+			start = (i + 1 + start) & 1023;
+			result = i + base + 1;
+			goto out;
+		} else {
+			int j = 0;
+			do {
+				if(++j >= size)
+					goto next;
+			} while((sk = sk->next));
+			best = i;
+			size = j;
+		}
+	next:
+	}
+
+	while(udp_lport_inuse(base + best + 1))
+		best += UDP_HTABLE_SIZE;
+	result = (best + base + 1);
+out:
+	SOCKHASH_UNLOCK();
+	return result;
+}
+
+/* Last hit UDP socket cache, this is ipv4 specific so make it static. */
+static u32 uh_cache_saddr, uh_cache_daddr;
+static u16 uh_cache_dport, uh_cache_sport;
+static struct sock *uh_cache_sk = NULL;
+
+static void udp_v4_hash(struct sock *sk)
+{
+	struct sock **skp;
+	int num = sk->num;
+
+	num &= (UDP_HTABLE_SIZE - 1);
+	skp = &udp_hash[num];
+
+	SOCKHASH_LOCK();
+	sk->next = *skp;
+	*skp = sk;
+	sk->hashent = num;
+	SOCKHASH_UNLOCK();
+}
+
+static void udp_v4_unhash(struct sock *sk)
+{
+	struct sock **skp;
+	int num = sk->num;
+
+	num &= (UDP_HTABLE_SIZE - 1);
+	skp = &udp_hash[num];
+
+	SOCKHASH_LOCK();
+	while(*skp != NULL) {
+		if(*skp == sk) {
+			*skp = sk->next;
+			break;
+		}
+		skp = &((*skp)->next);
+	}
+	if(uh_cache_sk == sk)
+		uh_cache_sk = NULL;
+	SOCKHASH_UNLOCK();
+}
+
+static void udp_v4_rehash(struct sock *sk)
+{
+	struct sock **skp;
+	int num = sk->num;
+	int oldnum = sk->hashent;
+
+	num &= (UDP_HTABLE_SIZE - 1);
+	skp = &udp_hash[oldnum];
+
+	SOCKHASH_LOCK();
+	while(*skp != NULL) {
+		if(*skp == sk) {
+			*skp = sk->next;
+			break;
+		}
+		skp = &((*skp)->next);
+	}
+	sk->next = udp_hash[num];
+	udp_hash[num] = sk;
+	sk->hashent = num;
+	if(uh_cache_sk == sk)
+		uh_cache_sk = NULL;
+	SOCKHASH_UNLOCK();
+}
+
+/* UDP is nearly always wildcards out the wazoo, it makes no sense to try
+ * harder than this here plus the last hit cache. -DaveM
  */
- 
-volatile unsigned long 	uh_cache_saddr,uh_cache_daddr;
-volatile unsigned short  uh_cache_dport, uh_cache_sport;
-volatile struct sock *uh_cache_sk;
-
-void udp_cache_zap(void)
-{
-	unsigned long flags;
-	save_flags(flags);
-	cli();
-	uh_cache_saddr=0;
-	uh_cache_daddr=0;
-	uh_cache_dport=0;
-	uh_cache_sport=0;
-	uh_cache_sk=NULL;
-	restore_flags(flags);
+struct sock *udp_v4_lookup_longway(u32 saddr, u16 sport, u32 daddr, u16 dport)
+{
+	struct sock *sk, *result = NULL;
+	unsigned short hnum = ntohs(dport);
+	int badness = -1;
+
+	for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
+		if((sk->num == hnum) && !(sk->dead && (sk->state == TCP_CLOSE))) {
+			int score = 0;
+			if(sk->rcv_saddr) {
+				if(sk->rcv_saddr != daddr)
+					continue;
+				score++;
+			}
+			if(sk->daddr) {
+				if(sk->daddr != saddr)
+					continue;
+				score++;
+			}
+			if(sk->dummy_th.dest) {
+				if(sk->dummy_th.dest != sport)
+					continue;
+				score++;
+			}
+			if(score == 3) {
+				result = sk;
+				break;
+			} else if(score > badness) {
+				result = sk;
+				badness = score;
+			}
+		}
+	}
+	return result;
 }
 
-#define min(a,b)	((a)<(b)?(a):(b))
+__inline__ struct sock *udp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport)
+{
+	struct sock *sk;
 
+	if(uh_cache_sk			&&
+	   uh_cache_saddr == saddr	&&
+	   uh_cache_sport == sport	&&
+	   uh_cache_dport == dport	&&
+	   uh_cache_daddr == daddr)
+		return uh_cache_sk;
+
+	sk = udp_v4_lookup_longway(saddr, sport, daddr, dport);
+	uh_cache_sk	= sk;
+	uh_cache_saddr	= saddr;
+	uh_cache_daddr	= daddr;
+	uh_cache_sport	= sport;
+	uh_cache_dport	= dport;
+	return sk;
+}
+
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+#define secondlist(hpnum, sk, fpass) \
+({ struct sock *s1; if(!(sk) && (fpass)--) \
+	s1 = udp_hash[(hpnum) & (TCP_HTABLE_SIZE - 1)]; \
+   else \
+	s1 = (sk); \
+   s1; \
+})
+
+#define udp_v4_proxy_loop_init(hnum, hpnum, sk, fpass) \
+	secondlist((hpnum), udp_hash[(hnum)&(TCP_HTABLE_SIZE-1)],(fpass))
+
+#define udp_v4_proxy_loop_next(hnum, hpnum, sk, fpass) \
+	secondlist((hpnum),(sk)->next,(fpass))
+
+struct sock *udp_v4_proxy_lookup(unsigned short num, unsigned long raddr,
+				 unsigned short rnum, unsigned long laddr,
+				 unsigned long paddr, unsigned short pnum)
+{
+	struct sock *s, *result = NULL;
+	int badness = -1;
+	unsigned short hnum = ntohs(num);
+	unsigned short hpnum = ntohs(pnum);
+	int firstpass = 1;
+
+	SOCKHASH_LOCK();
+	for(s = udp_v4_proxy_loop_init(hnum, hpnum, s, firstpass);
+	    s != NULL;
+	    s = udp_v4_proxy_loop_next(hnum, hpnum, s, firstpass)) {
+		if(s->num == hnum || s->num == hpnum) {
+			int score = 0;
+			if(s->dead && (s->state == TCP_CLOSE))
+				continue;
+			if(s->rcv_saddr) {
+				if((s->num != hpnum || s->rcv_saddr != paddr) &&
+				   (s->num != hnum || s->rcv_saddr != laddr))
+					continue;
+				score++;
+			}
+			if(s->daddr) {
+				if(s->daddr != raddr)
+					continue;
+				score++;
+			}
+			if(s->dummy_th.dest) {
+				if(s->dummy_th.dest != rnum)
+					continue;
+				score++;
+			}
+			if(score == 3 && s->num == hnum) {
+				result = s;
+				break;
+			} else if(score > badness && (s->num == hpnum || s->rcv_saddr)) {
+					result = s;
+					badness = score;
+			}
+		}
+	}
+	SOCKHASH_UNLOCK();
+	return result;
+}
+
+#undef secondlist
+#undef udp_v4_proxy_loop_init
+#undef udp_v4_proxy_loop_next
+
+#endif
+
+static inline struct sock *udp_v4_mcast_next(struct sock *sk,
+					     unsigned short num,
+					     unsigned long raddr,
+					     unsigned short rnum,
+					     unsigned long laddr)
+{
+	struct sock *s = sk;
+	unsigned short hnum = ntohs(num);
+	for(; s; s = s->next) {
+		if ((s->num != hnum)					||
+		    (s->dead && (s->state == TCP_CLOSE))		||
+		    (s->daddr && s->daddr!=raddr)			||
+		    (s->dummy_th.dest != rnum && s->dummy_th.dest != 0) ||
+		    (s->rcv_saddr  && s->rcv_saddr != laddr))
+			continue;
+		break;
+  	}
+  	return s;
+}
+
+#define min(a,b)	((a)<(b)?(a):(b))
 
 /*
  * This routine is called by the ICMP module when it gets some
@@ -158,8 +427,7 @@
 	int code = skb->h.icmph->code;
 	struct sock *sk;
 
-	sk = get_sock(&udp_prot, uh->source, iph->daddr, uh->dest, iph->saddr);
-
+	sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
 	if (sk == NULL)
 	  	return;	/* No socket for error */
 
@@ -234,9 +502,10 @@
 };
 
 /*
- *	Copy and checksum a UDP packet from user space into a buffer. We still have to do the planning to
- *	get ip_build_xmit to spot direct transfer to network card and provide an additional callback mode
- *	for direct user->board I/O transfers. That one will be fun.
+ *	Copy and checksum a UDP packet from user space into a buffer. We still have
+ *	to do the planning to get ip_build_xmit to spot direct transfer to network
+ *	card and provide an additional callback mode for direct user->board I/O
+ *	transfers. That one will be fun.
  */
  
 static int udp_getfrag(const void *p, char * to, unsigned int offset, unsigned int fraglen) 
@@ -271,9 +540,9 @@
 
 /*
  *	Unchecksummed UDP is sufficiently critical to stuff like ATM video conferencing
- *	that we use two routines for this for speed. Probably we ought to have a CONFIG_FAST_NET
- *	set for >10Mb/second boards to activate this sort of coding. Timing needed to verify if
- *	this is a valid decision.
+ *	that we use two routines for this for speed. Probably we ought to have a
+ *	CONFIG_FAST_NET set for >10Mb/second boards to activate this sort of coding.
+ *	Timing needed to verify if this is a valid decision.
  */
  
 static int udp_getfrag_nosum(const void *p, char * to, unsigned int offset, unsigned int fraglen) 
@@ -616,7 +885,8 @@
 		sk->rcv_saddr=INADDR_ANY;
 		sk->daddr=INADDR_ANY;
 		sk->state = TCP_CLOSE;
-		udp_cache_zap();
+		if(uh_cache_sk == sk)
+			uh_cache_sk = NULL;
 		return 0;
 	}
 
@@ -638,7 +908,8 @@
 	sk->daddr = rt->rt_dst;
 	sk->dummy_th.dest = usin->sin_port;
 	sk->state = TCP_ESTABLISHED;
-	udp_cache_zap();
+	if(uh_cache_sk == sk)
+		uh_cache_sk = NULL;
 	ip_rt_put(rt);
 	return(0);
 }
@@ -648,8 +919,8 @@
 {
 	lock_sock(sk);
 	sk->state = TCP_CLOSE;
-	if(uh_cache_sk==sk)
-		udp_cache_zap();
+	if(uh_cache_sk == sk)
+		uh_cache_sk = NULL;
 	release_sock(sk);
 	sk->dead = 1;
 	destroy_sock(sk);
@@ -682,6 +953,41 @@
 	udp_queue_rcv_skb(sk, skb);
 }
 
+/*
+ *	Multicasts and broadcasts go to each listener.
+ */
+static int udp_v4_mcast_deliver(struct sk_buff *skb, struct udphdr *uh,
+				 u32 saddr, u32 daddr)
+{
+	struct sock *sk;
+	int given = 0;
+
+	SOCKHASH_LOCK();
+	sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
+	sk = udp_v4_mcast_next(sk, uh->dest, saddr, uh->source, daddr);
+	if(sk) {
+		struct sock *sknext = NULL;
+
+		do {
+			struct sk_buff *skb1 = skb;
+
+			sknext = udp_v4_mcast_next(sk->next, uh->dest, saddr,
+						   uh->source, daddr);
+			if(sknext)
+				skb1 = skb_clone(skb, GFP_ATOMIC);
+
+			if(skb1)
+				udp_deliver(sk, skb1);
+			sk = sknext;
+		} while(sknext);
+		given = 1;
+	}
+	SOCKHASH_UNLOCK();
+	if(!given)
+		kfree_skb(skb, FREE_READ);
+	return 0;
+}
+
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 /*
  *	Check whether a received UDP packet might be for one of our
@@ -694,35 +1000,18 @@
 	struct udphdr *uh = (struct udphdr *)(skb->nh.raw + iph->ihl*4);
 	struct sock *sk;
 
-	sk = get_sock(&udp_prot, uh->dest, iph->saddr, uh->source, iph->daddr);
+	sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
+	if (!sk)
+		return 0;
 
-	if (!sk) return 0;
 	/* 0 means accept all LOCAL addresses here, not all the world... */
-	if (sk->rcv_saddr == 0) return 0;
+	if (sk->rcv_saddr == 0)
+		return 0;
+
 	return 1;
 }
 #endif
 
-
-static __inline__ struct sock *
-get_udp_sock(unsigned short dport, unsigned long saddr, unsigned short sport,
-	     unsigned long daddr)
-{
-	struct sock *sk;
-
-	if (saddr==uh_cache_saddr && daddr==uh_cache_daddr &&
-	    dport==uh_cache_dport && sport==uh_cache_sport)
-		 return (struct sock *)uh_cache_sk;
-	sk = get_sock(&udp_prot, dport, saddr, sport, daddr);
-	uh_cache_saddr=saddr;
-	uh_cache_daddr=daddr;
-	uh_cache_dport=dport;
-	uh_cache_sport=sport;
-	uh_cache_sk=sk;
-	return sk;
-}
-
-
 /*
  *	All we need to do is get the socket, and then do a checksum. 
  */
@@ -769,12 +1058,10 @@
 	/* FIXME list for IP, though, so I wouldn't worry about it. */
 	/* (That's the Right Place to do it, IMHO.) -- MS */
 
-	if (uh->check && (
-		( (skb->ip_summed == CHECKSUM_HW) && udp_check(uh, len, saddr, daddr, skb->csum ) ) ||
-		( (skb->ip_summed == CHECKSUM_NONE) && udp_check(uh, len, saddr, daddr,csum_partial((char*)uh, len, 0)))
-			  /* skip if CHECKSUM_UNNECESSARY */
-		         )
-	   ) {
+	if (uh->check &&
+	    (((skb->ip_summed==CHECKSUM_HW)&&udp_check(uh,len,saddr,daddr,skb->csum)) ||
+	     ((skb->ip_summed==CHECKSUM_NONE) &&
+	      (udp_check(uh,len,saddr,daddr, csum_partial((char*)uh, len, 0)))))) {
 		/* <mea@utu.fi> wants to know, who sent it, to
 		   go and stomp on the garbage sender... */
 
@@ -802,38 +1089,17 @@
 	skb_trim(skb,len);
 
 
-	if (rt->rt_flags&(RTF_BROADCAST|RTF_MULTICAST))
-	{
-		/*
-		 *	Multicasts and broadcasts go to each listener.
-		 */
-		struct sock *sknext=NULL;
-		sk=get_sock_mcast(udp_prot.sock_array[ntohs(uh->dest)&(SOCK_ARRAY_SIZE-1)], uh->dest,
-				saddr, uh->source, daddr);
-		if(sk) {		
-			do {
-				struct sk_buff *skb1;
-
-				sknext=get_sock_mcast(sk->next, uh->dest, saddr, uh->source, daddr);
-				if(sknext)
-					skb1=skb_clone(skb,GFP_ATOMIC);
-				else
-					skb1=skb;
-				if(skb1)
-					udp_deliver(sk, skb1);
-				sk=sknext;
-			} while(sknext!=NULL);
-		} else
-			kfree_skb(skb, FREE_READ);
-		return 0;
-	}
+	if(rt->rt_flags & (RTF_BROADCAST|RTF_MULTICAST))
+		return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
 
 #ifdef CONFIG_IP_TRANSPARENT_PROXY
 	if (IPCB(skb)->redirport)
-		sk = get_sock_proxy(&udp_prot, uh->dest, saddr, uh->source, daddr, skb->dev->pa_addr, IPCB(skb)->redirport);
+		sk = udp_v4_proxy_lookup(uh->dest, saddr, uh->source,
+					 daddr, skb->dev->pa_addr,
+					 IPCB(skb)->redirport);
 	else
 #endif
-	sk = get_udp_sock(uh->dest, saddr, uh->source, daddr);
+	sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest);
 	
 	if (sk == NULL) {
   		udp_statistics.UdpNoPorts++;
@@ -851,26 +1117,33 @@
 }
 
 struct proto udp_prot = {
-	udp_close,
-	udp_connect,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	datagram_poll,
-	udp_ioctl,
-	NULL,
-	NULL,
-	NULL,
-	ip_setsockopt,
-	ip_getsockopt,
-	udp_sendmsg,
-	udp_recvmsg,
-	NULL,		/* No special bind function */
-	udp_queue_rcv_skb,
-	128,
-	0,
-	"UDP",
-	0, 0,
-	NULL,
+	(struct sock *)&udp_prot,	/* sklist_next */
+	(struct sock *)&udp_prot,	/* sklist_prev */
+	udp_close,			/* close */
+	udp_connect,			/* connect */
+	NULL,				/* accept */
+	NULL,				/* retransmit */
+	NULL,				/* write_wakeup */
+	NULL,				/* read_wakeup */
+	datagram_poll,			/* poll */
+	udp_ioctl,			/* ioctl */
+	NULL,				/* init */
+	NULL,				/* destroy */
+	NULL,				/* shutdown */
+	ip_setsockopt,			/* setsockopt */
+	ip_getsockopt,			/* getsockopt */
+	udp_sendmsg,			/* sendmsg */
+	udp_recvmsg,			/* recvmsg */
+	NULL,				/* bind */
+	udp_queue_rcv_skb,		/* backlog_rcv */
+	udp_v4_hash,			/* hash */
+	udp_v4_unhash,			/* unhash */
+	udp_v4_rehash,			/* rehash */
+	udp_good_socknum,		/* good_socknum */
+	udp_v4_verify_bind,		/* verify_bind */
+	128,				/* max_header */
+	0,				/* retransmits */
+	"UDP",				/* name */
+	0,				/* inuse */
+	0				/* highestinuse */
 };
diff -u --recursive --new-file v2.1.27/linux/net/ipv6/af_inet6.c linux/net/ipv6/af_inet6.c
--- v2.1.27/linux/net/ipv6/af_inet6.c	Thu Feb 27 10:57:32 1997
+++ linux/net/ipv6/af_inet6.c	Mon Mar  3 09:37:45 1997
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/af_inet.c
  *
- *	$Id: af_inet6.c,v 1.8 1997/01/26 07:14:56 davem Exp $
+ *	$Id: af_inet6.c,v 1.12 1997/03/02 06:14:44 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -61,8 +61,6 @@
 #include <linux/ip_fw.h>
 #include <net/addrconf.h>
 
-struct sock * rawv6_sock_array[SOCK_ARRAY_SIZE];
-
 extern struct proto_ops inet6_stream_ops;
 extern struct proto_ops inet6_dgram_ops;
 
@@ -70,62 +68,37 @@
 {
 	struct sock *sk;
 	struct proto *prot;
-	int err;
 
 	sk = sk_alloc(GFP_KERNEL);
 	if (sk == NULL) 
-		return(-ENOBUFS);
-
-	/*
-	 *	Note for tcp that also wiped the dummy_th block for us.
-	 */
+		goto do_oom;
 
-	switch(sock->type) 
-	{
-		case SOCK_STREAM:
-		case SOCK_SEQPACKET:
-			if (protocol && protocol != IPPROTO_TCP) 
-			{
-				sk_free(sk);
-				return(-EPROTONOSUPPORT);
-			}
-			protocol = IPPROTO_TCP;
-			sk->no_check = TCP_NO_CHECK;
-			prot = &tcpv6_prot;
-			sock->ops = &inet6_stream_ops;
-			break;
-
-		case SOCK_DGRAM:
-			if (protocol && protocol != IPPROTO_UDP) 
-			{
-				sk_free(sk);
-				return(-EPROTONOSUPPORT);
-			}
-			protocol = IPPROTO_UDP;
-			sk->no_check = UDP_NO_CHECK;
-			prot=&udpv6_prot;
-			sock->ops = &inet6_dgram_ops;
-			break;
-      
-		case SOCK_RAW:
-			if (!suser()) 
-			{
-				sk_free(sk);
-				return(-EPERM);
-			}
-			if (!protocol) 
-			{
-				sk_free(sk);
-				return(-EPROTONOSUPPORT);
-			}
-			prot = &rawv6_prot;
-			sock->ops = &inet6_dgram_ops;
-			sk->reuse = 1;
-			sk->num = protocol;
-			break;
-		default:
-			sk_free(sk);
-			return(-ESOCKTNOSUPPORT);
+	/* Note for tcp that also wiped the dummy_th block for us. */
+	if(sock->type == SOCK_STREAM || sock->type == SOCK_SEQPACKET) {
+		if (protocol && protocol != IPPROTO_TCP) 
+			goto free_and_noproto;
+		protocol = IPPROTO_TCP;
+		sk->no_check = TCP_NO_CHECK;
+		prot = &tcpv6_prot;
+		sock->ops = &inet6_stream_ops;
+	} else if(sock->type == SOCK_DGRAM) {
+		if (protocol && protocol != IPPROTO_UDP) 
+			goto free_and_noproto;
+		protocol = IPPROTO_UDP;
+		sk->no_check = UDP_NO_CHECK;
+		prot=&udpv6_prot;
+		sock->ops = &inet6_dgram_ops;
+	} else if(sock->type == SOCK_RAW) {
+		if (!suser()) 
+			goto free_and_badperm;
+		if (!protocol) 
+			goto free_and_noproto;
+		prot = &rawv6_prot;
+		sock->ops = &inet6_dgram_ops;
+		sk->reuse = 1;
+		sk->num = protocol;
+	} else {
+		goto free_and_badtype;
 	}
 	
 	sock_init_data(sock,sk);
@@ -145,11 +118,9 @@
 	sk->net_pinfo.af_inet6.mcast_hops = IPV6_DEFAULT_MCASTHOPS;
 	sk->net_pinfo.af_inet6.mc_loop	  = 1;
 
-	/*
-	 *	init the ipv4 part of the socket since
-	 *	we can have sockets using v6 API for ipv4
+	/* Init the ipv4 part of the socket since we can have sockets
+	 * using v6 API for ipv4.
 	 */
-
 	sk->ip_ttl=64;
 
 	sk->ip_mc_loop=1;
@@ -157,34 +128,41 @@
 	sk->ip_mc_index=0;
 	sk->ip_mc_list=NULL;
 
-
 	if (sk->type==SOCK_RAW && protocol==IPPROTO_RAW)
 		sk->ip_hdrincl=1;
 
-	if (sk->num) 
-	{
-		/*
-		 * It assumes that any protocol which allows
+	if (sk->num) {
+		/* It assumes that any protocol which allows
 		 * the user to assign a number at socket
-		 * creation time automatically
-		 * shares.
+		 * creation time automatically shares.
 		 */
-
-		inet_put_sock(sk->num, sk);
 		sk->dummy_th.source = ntohs(sk->num);
+		if(sk->prot->hash)
+			sk->prot->hash(sk);
+		add_to_prot_sklist(sk);
 	}
 
-	if (sk->prot->init) 
-	{
-		err = sk->prot->init(sk);
-		if (err != 0) 
-		{
+	if (sk->prot->init) {
+		int err = sk->prot->init(sk);
+		if (err != 0) {
 			destroy_sock(sk);
 			return(err);
 		}
 	}
 	MOD_INC_USE_COUNT;
 	return(0);
+
+free_and_badtype:
+	sk_free(sk);
+	return -ESOCKTNOSUPPORT;
+free_and_badperm:
+	sk_free(sk);
+	return -EPERM;
+free_and_noproto:
+	sk_free(sk);
+	return -EPROTONOSUPPORT;
+do_oom:
+	return -ENOBUFS;
 }
 
 static int inet6_dup(struct socket *newsock, struct socket *oldsock)
@@ -192,80 +170,47 @@
 	return(inet6_create(newsock, oldsock->sk->protocol));
 }
 
-
-/*
- *	bind for INET6 API	
- */
-
-static int inet6_bind(struct socket *sock, struct sockaddr *uaddr,
-		      int addr_len)
+/* bind for INET6 API */
+static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
 {
 	struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr;
-	struct sock *sk = sock->sk, *sk2;
+	struct sock *sk = sock->sk;
 	__u32 v4addr = 0;
 	unsigned short snum = 0;
 	int addr_type = 0;
 
-	/*
-	 *	If the socket has its own bind function then use it.
-	 */
-	 
+	/* If the socket has its own bind function then use it. */
 	if(sk->prot->bind)
 		return sk->prot->bind(sk, uaddr, addr_len);
 		
-	/* check this error. */
-	if (sk->state != TCP_CLOSE)
-		return(-EINVAL);
-
-	if(addr_len < sizeof(struct sockaddr_in6))
+	/* Check these errors (active socket, bad address length, double bind). */
+	if ((sk->state != TCP_CLOSE)			||
+	    (addr_len < sizeof(struct sockaddr_in6))	||
+	    (sk->num != 0))
 		return -EINVAL;
 		
-	if(sock->type != SOCK_RAW)
-	{
-		if (sk->num != 0) 
-			return(-EINVAL);
-
-		snum = ntohs(addr->sin6_port);
-		
-		if (snum == 0) 
-			snum = get_new_socknum(sk->prot, 0);
-
-		if (snum < PROT_SOCK && !suser()) 
-			return(-EACCES);
-	}
+	snum = ntohs(addr->sin6_port);
+	if (snum == 0) 
+		snum = sk->prot->good_socknum();
+	if (snum < PROT_SOCK && !suser()) 
+		return(-EACCES);
 	
 	addr_type = ipv6_addr_type(&addr->sin6_addr);
-
 	if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM)
-	{
 		return(-EINVAL);
-	}
-
-	/*
-	 *	check if the address belongs to the host
-	 */
 
-	if (addr_type == IPV6_ADDR_MAPPED)
-	{
+	/* Check if the address belongs to the host. */
+	if (addr_type == IPV6_ADDR_MAPPED) {
 		v4addr = addr->sin6_addr.s6_addr32[3];
-
 		if (__ip_chk_addr(v4addr) != IS_MYADDR)
 			return(-EADDRNOTAVAIL);
-	}
-	else
-	{
-		if (addr_type != IPV6_ADDR_ANY)
-		{
-			/* 
-			 *	ipv4 addr of the socket is invalid.
-			 *	only the unpecified and mapped address	
-			 *	have a v4 equivalent.
+	} else {
+		if (addr_type != IPV6_ADDR_ANY) {
+			/* ipv4 addr of the socket is invalid.  Only the
+			 * unpecified and mapped address have a v4 equivalent.
 			 */
-
 			v4addr = LOOPBACK4_IPV6;
-
-			if (!(addr_type & IPV6_ADDR_MULTICAST))
-			{
+			if (!(addr_type & IPV6_ADDR_MULTICAST))	{
 				if (ipv6_chk_addr(&addr->sin6_addr) == NULL)
 					return(-EADDRNOTAVAIL);
 			}
@@ -282,82 +227,16 @@
 		memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr, 
 		       sizeof(struct in6_addr));
 
-	if(sock->type != SOCK_RAW)
-	{
-		/* Make sure we are allowed to bind here. */
-		cli();
-		for(sk2 = sk->prot->sock_array[snum & (SOCK_ARRAY_SIZE -1)];
-					sk2 != NULL; sk2 = sk2->next) 
-		{
-			/*
-			 *	Hash collision or real match ?
-			 */
-			 
-			if (sk2->num != snum) 
-				continue;
-				
-			/*
-			 *	Either bind on the port is wildcard means
-			 *	they will overlap and thus be in error.
-			 *	We use the sk2 v4 address to test the 
-			 *	other socket since addr_any in av4 implies
-			 *	addr_any in v6
-			 */			
-			 
-			if (addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr))
-			{
-				/*
-				 *	Allow only if both are setting reuse.
-				 */
-				if(sk2->reuse && sk->reuse && sk2->state!=TCP_LISTEN)
-					continue;
-				sti();
-				return(-EADDRINUSE);
-			}
-
-			/*
-			 *	Two binds match ?
-			 */
-
-			if (ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
-					  &sk2->net_pinfo.af_inet6.rcv_saddr))
-
-				continue;
-			/*
-			 *	Reusable port ?
-			 */
-
-			if (!sk->reuse)
-			{
-				sti();
-				return(-EADDRINUSE);
-			}
-			
-			/*
-			 *	Reuse ?
-			 */
-			 
-			if (!sk2->reuse || sk2->state==TCP_LISTEN) 
-			{
-				sti();
-				return(-EADDRINUSE);
-			}
-		}
-		sti();
-
-		inet_remove_sock(sk);
-		
-		/*
-		if(sock->type==SOCK_DGRAM)
-			udp_cache_zap();
-		if(sock->type==SOCK_STREAM)
-			tcp_cache_zap();
-			*/
-		inet_put_sock(snum, sk);
-		sk->dummy_th.source = ntohs(sk->num);
-		sk->dummy_th.dest = 0;
-		sk->daddr = 0;
-	}
+	/* Make sure we are allowed to bind here. */
+	if(sk->prot->verify_bind(sk, snum))
+		return -EADDRINUSE;
+
+	sk->num = snum;
+	sk->dummy_th.source = ntohs(sk->num);
+	sk->dummy_th.dest = 0;
+	sk->daddr = 0;
+	sk->prot->rehash(sk);
+	add_to_prot_sklist(sk);
 
 	return(0);
 }
@@ -385,31 +264,24 @@
   
 	sin->sin6_family = AF_INET6;
 	sk = sock->sk;
-	if (peer) 
-	{
+	if (peer) {
 		if (!tcp_connected(sk->state))
 			return(-ENOTCONN);
 		sin->sin6_port = sk->dummy_th.dest;
 		memcpy(&sin->sin6_addr, &sk->net_pinfo.af_inet6.daddr,
 		       sizeof(struct in6_addr));
-	} 
-	else 
-	{
-		if (ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr) ==
-		    IPV6_ADDR_ANY)
+	} else {
+		if (ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr) == IPV6_ADDR_ANY)
 			memcpy(&sin->sin6_addr, 
 			       &sk->net_pinfo.af_inet6.saddr,
 			       sizeof(struct in6_addr));
-
 		else
 			memcpy(&sin->sin6_addr, 
 			       &sk->net_pinfo.af_inet6.rcv_saddr,
 			       sizeof(struct in6_addr));
 
 		sin->sin6_port = sk->dummy_th.source;
-
 	}
-	
 	*uaddr_len = sizeof(*sin);	
 	return(0);
 }
@@ -510,214 +382,6 @@
 	return(0);
 }
 
-/*
- * This routine must find a socket given a TCP or UDP header.
- * Everything is assumed to be in net order.
- *
- * We give priority to more closely bound ports: if some socket
- * is bound to a particular foreign address, it will get the packet
- * rather than somebody listening to any address..
- */
-
-struct sock *inet6_get_sock(struct proto *prot, 
-			    struct in6_addr *loc_addr, 
-			    struct in6_addr *rmt_addr, 			   
-			    unsigned short loc_port,
-			    unsigned short rmt_port)
-{
-	struct sock *s;
-	struct sock *result = NULL;
-	int badness = -1;
-	unsigned short hnum;
-	struct ipv6_pinfo *np;
-	hnum = ntohs(loc_port);
-
-	/*
-	 * SOCK_ARRAY_SIZE must be a power of two.  This will work better
-	 * than a prime unless 3 or more sockets end up using the same
-	 * array entry.  This should not be a problem because most
-	 * well known sockets don't overlap that much, and for
-	 * the other ones, we can just be careful about picking our
-	 * socket number when we choose an arbitrary one.
-	 */
-
-	for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
-			s != NULL; s = s->next) 
-	{
-		int score = 0;
-		
-		if ((s->num != hnum) || s->family != AF_INET6)
-			continue;
-
-		if(s->dead && (s->state == TCP_CLOSE))
-		{
-			printk(KERN_DEBUG "dead or closed socket\n");
-			continue;
-		}
-
-		np = &s->net_pinfo.af_inet6;
-
-		/* remote port matches? */
-
-		if (s->dummy_th.dest) {
-			if (s->dummy_th.dest != rmt_port)
-			{
-				continue;
-			}
-			score++;
-		}
-
-		/* local address matches? */
-
-		if (!ipv6_addr_any(&np->rcv_saddr))
-		{
-			if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr))
-			{
-				continue;
-			}
-			score++;
-		}
-
-		/* remote address matches? */
-		if (!ipv6_addr_any(&np->daddr))
-		{
-			if (ipv6_addr_cmp(&np->daddr, rmt_addr))
-			{
-				continue;
-			}
-			score++;
-		}
-
-		/* perfect match? */
-		if (score == 3)
-			return s;
-		/* no, check if this is the best so far.. */
-		if (score <= badness)
-			continue;
-		result = s;
-		badness = score;
-  	}
-  	return result;
-}
-
-static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
-{
-	struct ipv6_mc_socklist *mc;
-		
-	for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next)
-	{
-		if (ipv6_addr_cmp(&mc->addr, addr) == 0)
-			return 1;
-	}
-
-	return 0;
-}
-
-/*
- *	Deliver a datagram to raw sockets.
- */
- 
-struct sock *inet6_get_sock_raw(struct sock *sk, unsigned short num,
-				struct in6_addr *loc_addr, 
-				struct in6_addr *rmt_addr)
-			  
-{
-	struct sock *s;
-	struct ipv6_pinfo *np;
-	int addr_type = 0;
-
-	s=sk;
-
-	addr_type = ipv6_addr_type(loc_addr);
-
-	for(; s != NULL; s = s->next) 
-	{
-		if (s->num != num) 
-			continue;
-
-		if(s->dead && (s->state == TCP_CLOSE))
-			continue;
-
-		np = &s->net_pinfo.af_inet6;
-
-		if (!ipv6_addr_any(&np->daddr) &&
-		    ipv6_addr_cmp(&np->daddr, rmt_addr))
-		{
-			continue;
-		}
-
-		if (!ipv6_addr_any(&np->rcv_saddr))
-		{
-			if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
-				return(s);
-		
-			if ((addr_type & IPV6_ADDR_MULTICAST) &&
-			    inet6_mc_check(s, loc_addr))
-				return (s);
-			
-			continue;
-		}
-
-		return(s);
-  	}
-  	return(NULL);
-}
-
-/*
- *	inet6_get_sock_mcast for UDP sockets.
- */
-
-struct sock *inet6_get_sock_mcast(struct sock *sk, 
-				  unsigned short num, unsigned short rmt_port,
-				  struct in6_addr *loc_addr, 
-				  struct in6_addr *rmt_addr)
-{	
-	struct sock *s;
-	struct ipv6_pinfo *np;
-
-	s=sk;
-
-	for(; s != NULL; s = s->next) 
-	{
-		if (s->num != num) 
-			continue;
-
-		if(s->dead && (s->state == TCP_CLOSE))
-			continue;
-
-		np = &s->net_pinfo.af_inet6;
-
-		if (s->dummy_th.dest) {
-			if (s->dummy_th.dest != rmt_port)
-			{
-				continue;
-			}
-		}
-
-		if (!ipv6_addr_any(&np->daddr) &&
-		    ipv6_addr_cmp(&np->daddr, rmt_addr))
-		{
-			continue;
-		}
-
-
-		if (!ipv6_addr_any(&np->rcv_saddr))
-		{
-			if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
-				return(s);
-		}
-		
-		if (!inet6_mc_check(s, loc_addr))
-		{
-			continue;
-		}
-
-		return(s);
-  	}
-  	return(NULL);
-}
-	
-
 struct proto_ops inet6_stream_ops = {
 	AF_INET6,
 
@@ -773,7 +437,6 @@
 void inet6_proto_init(struct net_proto *pro)
 #endif
 {
-	int i;
 	struct sk_buff *dummy_skb;
 
 	printk(KERN_INFO "IPv6 v0.1 for NET3.037\n");
@@ -786,30 +449,13 @@
 
   	(void) sock_register(&inet6_family_ops);
 	
-	for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
-	{
-		rawv6_sock_array[i] = NULL;
-  	}
-
 	/*
 	 *	ipngwg API draft makes clear that the correct semantics
 	 *	for TCP and UDP is to consider one TCP and UDP instance
 	 *	in a host availiable by both INET and INET6 APIs and
-	 *	hable to communicate via both network protocols.
+	 *	able to communicate via both network protocols.
 	 */
-	
-	tcpv6_prot.inuse = 0;
-	tcpv6_prot.highestinuse = 0;       
-	tcpv6_prot.sock_array = tcp_sock_array;
-
-	udpv6_prot.inuse = 0;
-	udpv6_prot.highestinuse = 0;
-	udpv6_prot.sock_array = udp_sock_array;
-
-	rawv6_prot.inuse = 0;
-	rawv6_prot.highestinuse = 0;
-	rawv6_prot.sock_array = rawv6_sock_array;
-	
+
 	ipv6_init();
 
 	icmpv6_init(&inet6_family_ops);
diff -u --recursive --new-file v2.1.27/linux/net/ipv6/icmp.c linux/net/ipv6/icmp.c
--- v2.1.27/linux/net/ipv6/icmp.c	Mon Jan 13 22:16:49 1997
+++ linux/net/ipv6/icmp.c	Mon Mar  3 09:37:45 1997
@@ -356,7 +356,7 @@
 		len -= hdrlen;
 	}
 
-	hash = nexthdr & (MAX_INET_PROTOS -1);
+	hash = nexthdr & (MAX_INET_PROTOS - 1);
 
 	for (ipprot = (struct inet6_protocol *) inet6_protos[hash]; 
 	     ipprot != NULL; 
@@ -375,15 +375,14 @@
 
 	/* delivery to upper layer protocols failed. try raw sockets */
 
-	sk = rawv6_prot.sock_array[hash];
+	sk = raw_v6_htable[hash];
 
 	if (sk == NULL)
 	{
 		return;
 	}
 
-	while ((sk = inet6_get_sock_raw(sk, nexthdr, daddr, saddr)))
-	{
+	while((sk = raw_v6_lookup(sk, nexthdr, daddr, saddr))) {
 		rawv6_err(sk, type, code, pbuff, saddr, daddr);
 		sk = sk->next;
 	}
diff -u --recursive --new-file v2.1.27/linux/net/ipv6/ipv6_input.c linux/net/ipv6/ipv6_input.c
--- v2.1.27/linux/net/ipv6/ipv6_input.c	Sun Jan 19 05:47:28 1997
+++ linux/net/ipv6/ipv6_input.c	Mon Mar  3 09:37:45 1997
@@ -8,7 +8,7 @@
  *
  *	Based in linux/net/ipv4/ip_input.c
  *
- *	$Id: ipv6_input.c,v 1.13 1996/10/11 16:03:06 roque Exp $
+ *	$Id: ipv6_input.c,v 1.4 1997/02/28 09:56:33 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -212,10 +212,9 @@
 	struct sock *sk, *sk2;
 	__u8 hash;
 
-	hash = nexthdr & (SOCK_ARRAY_SIZE-1);
+	hash = nexthdr & (MAX_INET_PROTOS - 1);
 
-	sk = rawv6_prot.sock_array[hash];
-	
+	sk = raw_v6_htable[hash];
 
 	/*
 	 *	The first socket found will be delivered after
@@ -225,14 +224,13 @@
 	if (sk == NULL)
 		return NULL;
 	
-	sk = inet6_get_sock_raw(sk, nexthdr, daddr, saddr);
+	sk = raw_v6_lookup(sk, nexthdr, daddr, saddr);
 
 	if (sk)
 	{
 		sk2 = sk;
 
-		while ((sk2 = inet6_get_sock_raw(sk2->next, nexthdr, 
-						 daddr, saddr)))
+		while ((sk2 = raw_v6_lookup(sk2->next, nexthdr, daddr, saddr)))
 		{
 			struct sk_buff *buff;
 
diff -u --recursive --new-file v2.1.27/linux/net/ipv6/raw.c linux/net/ipv6/raw.c
--- v2.1.27/linux/net/ipv6/raw.c	Sun Jan 26 02:07:50 1997
+++ linux/net/ipv6/raw.c	Mon Mar  3 09:37:45 1997
@@ -7,7 +7,7 @@
  *
  *	Adapted from linux/net/ipv4/raw.c
  *
- *	$Id: raw.c,v 1.7 1997/01/26 07:14:56 davem Exp $
+ *	$Id: raw.c,v 1.8 1997/02/28 09:56:34 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -39,8 +39,151 @@
 #include <net/addrconf.h>
 #include <net/transp_v6.h>
 
+#include <net/rawv6.h>
+
 #include <asm/uaccess.h>
 
+struct sock *raw_v6_htable[RAWV6_HTABLE_SIZE];
+
+static void raw_v6_hash(struct sock *sk)
+{
+	struct sock **skp;
+	int num = sk->num;
+
+	num &= (RAWV6_HTABLE_SIZE - 1);
+	skp = &raw_v6_htable[num];
+	SOCKHASH_LOCK();
+	sk->next = *skp;
+	*skp = sk;
+	sk->hashent = num;
+	SOCKHASH_UNLOCK();
+}
+
+static void raw_v6_unhash(struct sock *sk)
+{
+	struct sock **skp;
+	int num = sk->num;
+
+	num &= (RAWV6_HTABLE_SIZE - 1);
+	skp = &raw_v6_htable[num];
+
+	SOCKHASH_LOCK();
+	while(*skp != NULL) {
+		if(*skp == sk) {
+			*skp = sk->next;
+			break;
+		}
+		skp = &((*skp)->next);
+	}
+	SOCKHASH_UNLOCK();
+}
+
+static void raw_v6_rehash(struct sock *sk)
+{
+	struct sock **skp;
+	int num = sk->num;
+	int oldnum = sk->hashent;
+
+	num &= (RAWV6_HTABLE_SIZE - 1);
+	skp = &raw_v6_htable[oldnum];
+
+	SOCKHASH_LOCK();
+	while(*skp != NULL) {
+		if(*skp == sk) {
+			*skp = sk->next;
+			break;
+		}
+		skp = &((*skp)->next);
+	}
+	sk->next = raw_v6_htable[num];
+	raw_v6_htable[num] = sk;
+	sk->hashent = num;
+	SOCKHASH_UNLOCK();
+}
+
+static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+	struct ipv6_mc_socklist *mc;
+		
+	for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+		if (ipv6_addr_cmp(&mc->addr, addr) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+/* Grumble... icmp and ip_input want to get at this... */
+struct sock *raw_v6_lookup(struct sock *sk, unsigned short num,
+			   struct in6_addr *loc_addr, struct in6_addr *rmt_addr)
+{
+	struct sock *s = sk;
+	int addr_type = ipv6_addr_type(loc_addr);
+
+	for(s = sk; s; s = s->next) {
+		if((s->num == num) 		&&
+		   !(s->dead && (s->state == TCP_CLOSE))) {
+			struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
+
+			if (!ipv6_addr_any(&np->daddr) &&
+			    ipv6_addr_cmp(&np->daddr, rmt_addr))
+				continue;
+
+			if (!ipv6_addr_any(&np->rcv_saddr)) {
+				if (ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
+					return(s);
+				if ((addr_type & IPV6_ADDR_MULTICAST) &&
+				    inet6_mc_check(s, loc_addr))
+					return (s);
+				continue;
+			}
+			return(s);
+		}
+	}
+	return NULL;
+}
+
+/* This cleans up af_inet6 a bit. -DaveM */
+static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+	struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;
+	__u32 v4addr = 0;
+	int addr_type;
+
+	/* Check these errors. */
+	if (sk->state != TCP_CLOSE || (addr_len < sizeof(struct sockaddr_in6)))
+		return -EINVAL;
+
+	addr_type = ipv6_addr_type(&addr->sin6_addr);
+
+	/* Check if the address belongs to the host. */
+	if (addr_type == IPV6_ADDR_MAPPED) {
+		v4addr = addr->sin6_addr.s6_addr32[3];
+		if (__ip_chk_addr(v4addr) != IS_MYADDR)
+			return(-EADDRNOTAVAIL);
+	} else {
+		if (addr_type != IPV6_ADDR_ANY) {
+			/* ipv4 addr of the socket is invalid.  Only the
+			 * unpecified and mapped address have a v4 equivalent.
+			 */
+			v4addr = LOOPBACK4_IPV6;
+			if (!(addr_type & IPV6_ADDR_MULTICAST))	{
+				if (ipv6_chk_addr(&addr->sin6_addr) == NULL)
+					return(-EADDRNOTAVAIL);
+			}
+		}
+	}
+
+	sk->rcv_saddr = v4addr;
+	sk->saddr = v4addr;
+	memcpy(&sk->net_pinfo.af_inet6.rcv_saddr, &addr->sin6_addr, 
+	       sizeof(struct in6_addr));
+	if (!(addr_type & IPV6_ADDR_MULTICAST))
+		memcpy(&sk->net_pinfo.af_inet6.saddr, &addr->sin6_addr, 
+		       sizeof(struct in6_addr));
+	return 0;
+}
+
 void rawv6_err(struct sock *sk, int type, int code, unsigned char *buff,
 	       struct in6_addr *saddr, struct in6_addr *daddr)
 {
@@ -443,28 +586,35 @@
 }
 
 struct proto rawv6_prot = {
-	rawv6_close,
-	udpv6_connect,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	datagram_poll,
-	NULL,
-	rawv6_init_sk,
-	NULL,
-	NULL,
-	rawv6_setsockopt,
-	ipv6_getsockopt,		/* FIXME */
-	rawv6_sendmsg,
-	rawv6_recvmsg,
-	NULL,		/* No special bind */
-	rawv6_rcv_skb,
-	128,
-	0,
-	"RAW",
-	0, 0,
-	NULL
+	(struct sock *)&rawv6_prot,	/* sklist_next */
+	(struct sock *)&rawv6_prot,	/* sklist_prev */
+	rawv6_close,			/* close */
+	udpv6_connect,			/* connect */
+	NULL,				/* accept */
+	NULL,				/* retransmit */
+	NULL,				/* write_wakeup */
+	NULL,				/* read_wakeup */
+	datagram_poll,			/* poll */
+	NULL,				/* ioctl */
+	rawv6_init_sk,			/* init */
+	NULL,				/* destroy */
+	NULL,				/* shutdown */
+	rawv6_setsockopt,		/* setsockopt */
+	ipv6_getsockopt,		/* getsockopt - FIXME */
+	rawv6_sendmsg,			/* sendmsg */
+	rawv6_recvmsg,			/* recvmsg */
+	rawv6_bind,			/* bind */
+	rawv6_rcv_skb,			/* backlog_rcv */
+	raw_v6_hash,			/* hash */
+	raw_v6_unhash,			/* unhash */
+	raw_v6_rehash,			/* rehash */
+	NULL,				/* good_socknum */
+	NULL,				/* verify_bind */
+	128,				/* max_header */
+	0,				/* retransmits */
+	"RAW",				/* name */
+	0,				/* inuse */
+	0				/* highestinuse */
 };
 
 /*
diff -u --recursive --new-file v2.1.27/linux/net/ipv6/tcp_ipv6.c linux/net/ipv6/tcp_ipv6.c
--- v2.1.27/linux/net/ipv6/tcp_ipv6.c	Sun Feb  2 05:18:51 1997
+++ linux/net/ipv6/tcp_ipv6.c	Mon Mar  3 10:34:43 1997
@@ -5,7 +5,7 @@
  *	Authors:
  *	Pedro Roque		<roque@di.fc.ul.pt>	
  *
- *	$Id: tcp_ipv6.c,v 1.7 1997/01/26 07:14:57 davem Exp $
+ *	$Id: tcp_ipv6.c,v 1.11 1997/03/03 18:27:31 davem Exp $
  *
  *	Based on: 
  *	linux/net/ipv4/tcp.c
@@ -55,6 +55,192 @@
 static struct tcp_func ipv6_mapped;
 static struct tcp_func ipv6_specific;
 
+/* I have no idea if this is a good hash for v6 or not. -DaveM */
+static __inline__ int tcp_v6_hashfn(struct in6_addr *laddr, u16 lport,
+				    struct in6_addr *faddr, u16 fport)
+{
+	int hashent = (lport ^ fport);
+
+	hashent ^= (laddr->s6_addr32[0] ^ laddr->s6_addr32[1]);
+	hashent ^= (faddr->s6_addr32[0] ^ faddr->s6_addr32[1]);
+	return (hashent & (TCP_HTABLE_SIZE - 1));
+}
+
+static __inline__ int tcp_v6_sk_hashfn(struct sock *sk)
+{
+	struct in6_addr *laddr = &sk->net_pinfo.af_inet6.rcv_saddr;
+	struct in6_addr *faddr = &sk->net_pinfo.af_inet6.daddr;
+	__u16 lport = sk->num;
+	__u16 fport = sk->dummy_th.dest;
+	return tcp_v6_hashfn(laddr, lport, faddr, fport);
+}
+
+/* Grrr, addr_type already calculated by caller, but I don't want
+ * to add some silly "cookie" argument to this method just for that.
+ */
+static int tcp_v6_verify_bind(struct sock *sk, unsigned short snum)
+{
+	struct sock *sk2;
+	int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+	int retval = 0, sk_reuse = sk->reuse;
+
+	SOCKHASH_LOCK();
+	sk2 = tcp_bound_hash[tcp_sk_bhashfn(sk)];
+	for(; sk2 != NULL; sk2 = sk2->prev) {
+		if((sk2->num == snum) && (sk2 != sk)) {
+			unsigned char state = sk2->state;
+			int sk2_reuse = sk2->reuse;
+			if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
+				if((!sk2_reuse)			||
+				   (!sk_reuse)			||
+				   (state != TCP_LISTEN)) {
+					retval = 1;
+					break;
+				}
+			} else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+						 &sk2->net_pinfo.af_inet6.rcv_saddr)) {
+				if((!sk_reuse)			||
+				   (!sk2_reuse)			||
+				   (state == TCP_LISTEN)) {
+					retval = 1;
+					break;
+				}
+			}
+		}
+	}
+	SOCKHASH_UNLOCK();
+
+	return retval;
+}
+
+static void tcp_v6_hash(struct sock *sk)
+{
+	unsigned char state;
+
+	SOCKHASH_LOCK();
+	state = sk->state;
+	if(state != TCP_CLOSE) {
+		struct sock **htable;
+
+		if(state == TCP_LISTEN) {
+			sk->hashent = tcp_sk_listen_hashfn(sk);
+			htable = &tcp_listening_hash[0];
+		} else {
+			sk->hashent = tcp_v6_sk_hashfn(sk);
+			htable = &tcp_established_hash[0];
+		}
+		sk->next = htable[sk->hashent];
+		htable[sk->hashent] = sk;
+		sk->hashtable = htable;
+		tcp_sk_bindify(sk);
+	}
+	SOCKHASH_UNLOCK();
+}
+
+static void tcp_v6_unhash(struct sock *sk)
+{
+	struct sock **htable;
+
+	SOCKHASH_LOCK();
+	htable = sk->hashtable;
+	if(htable) {
+		struct sock **skp = &(htable[sk->hashent]);
+		while(*skp != NULL) {
+			if(*skp == sk) {
+				*skp = sk->next;
+				break;
+			}
+			skp = &((*skp)->next);
+		}
+		sk->hashtable = NULL;
+		tcp_sk_unbindify(sk);
+	}
+	SOCKHASH_UNLOCK();
+}
+
+static void tcp_v6_rehash(struct sock *sk)
+{
+	struct sock **htable;
+	unsigned char state;
+
+	SOCKHASH_LOCK();
+	htable = &(sk->hashtable[sk->hashent]);
+	state = sk->state;
+	if(htable) {
+		while(*htable != NULL) {
+			if(*htable == sk) {
+				*htable = sk->next;
+				break;
+			}
+			htable = &((*htable)->next);
+		}
+	}
+	htable = NULL;
+	if(state != TCP_CLOSE) {
+		if(state == TCP_LISTEN) {
+			sk->hashent = tcp_sk_listen_hashfn(sk);
+			htable = &tcp_listening_hash[0];
+		} else {
+			sk->hashent = tcp_v6_sk_hashfn(sk);
+			htable = &tcp_established_hash[0];
+		}
+		sk->next = htable[sk->hashent];
+		htable[sk->hashent] = sk;
+	} else {
+		tcp_sk_unbindify(sk);
+	}
+	sk->hashtable = htable;
+	SOCKHASH_UNLOCK();
+}
+
+static struct sock *tcp_v6_lookup_longway(struct in6_addr *daddr, unsigned short hnum)
+{
+	struct sock *sk = tcp_listening_hash[tcp_lhashfn(hnum)];
+	struct sock *result = NULL;
+
+	for(; sk; sk = sk->next) {
+		if((sk->num == hnum) && (sk->family == AF_INET6)) {
+			struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+			if(!ipv6_addr_any(&np->rcv_saddr)) {
+				if(!ipv6_addr_cmp(&np->rcv_saddr, daddr))
+					return sk; /* Best possible match. */
+			} else if(!result)
+				result = sk;
+		}
+	}
+	return result;
+}
+
+/* Sockets in TCP_CLOSE state are _always_ taken out of the hash, so
+ * we need not check it for TCP lookups anymore, thanks Alexey. -DaveM
+ */
+static inline struct sock *__tcp_v6_lookup(struct tcphdr *th,
+					   struct in6_addr *saddr, u16 sport,
+					   struct in6_addr *daddr, u16 dport)
+{
+	unsigned short hnum = ntohs(dport);
+	struct sock *sk;
+
+	/* Optimize here for direct hit, only listening connections can
+	 * have wildcards anyways.  It is assumed that this code only
+	 * gets called from within NET_BH.
+	 */
+	sk = tcp_established_hash[tcp_v6_hashfn(daddr, hnum, saddr, sport)];
+	for(; sk; sk = sk->next)
+		/* For IPV6 do the cheaper port and family tests first. */
+		if(sk->num		== hnum			&& /* local port     */
+		   sk->family		== AF_INET6		&& /* address family */
+		   sk->dummy_th.dest	== sport		&& /* remote port    */
+		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.daddr, saddr)	&&
+		   !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr, daddr))
+			goto hit; /* You sunk my battleship! */
+	sk = tcp_v6_lookup_longway(daddr, hnum);
+hit:
+	return sk;
+}
+
+#define tcp_v6_lookup(sa, sp, da, dp) __tcp_v6_lookup((0),(sa),(sp),(da),(dp))
+
 static __inline__ u16 tcp_v6_check(struct tcphdr *th, int len,
 				   struct in6_addr *saddr, 
 				   struct in6_addr *daddr, 
@@ -264,6 +450,11 @@
 	
 	tcp_set_state(sk, TCP_SYN_SENT);
 	
+	/* Socket identity change complete, no longer
+	 * in TCP_CLOSE, so rehash.
+	 */
+	sk->prot->rehash(sk);
+
 	/* FIXME: should use dcache->rtt if availiable */
 	tp->rto = TCP_TIMEOUT_INIT;
 
@@ -340,7 +531,7 @@
 	int err;
 	int opening;
 
-	sk = inet6_get_sock(&tcpv6_prot, daddr, saddr, th->source, th->dest);
+	sk = tcp_v6_lookup(saddr, th->source, daddr, th->dest);
 
 	if (sk == NULL)
 	{
@@ -602,6 +793,10 @@
 	}
 
 	memcpy(newsk, sk, sizeof(*newsk));
+
+	/* Or else we die! -DaveM */
+	newsk->sklist_next = NULL;
+
 	newsk->opt = NULL;
 	newsk->dst_cache  = NULL;
 	skb_queue_head_init(&newsk->write_queue);
@@ -706,7 +901,7 @@
 	newsk->saddr	= LOOPBACK4_IPV6;
 	newsk->rcv_saddr= LOOPBACK4_IPV6;
 	
-	inet_put_sock(newsk->num, newsk);
+	newsk->prot->hash(newsk);
 
 	return newsk;
 
@@ -907,8 +1102,7 @@
 
 		tcp_statistics.TcpInSegs++;
 		
-		sk = inet6_get_sock(&tcpv6_prot, daddr, saddr, 
-				    th->dest, th->source);
+		sk = __tcp_v6_lookup(th, saddr, th->source, daddr, th->dest);
 
 		if (!sk) 
 		{
@@ -1057,7 +1251,7 @@
 	saddr = &skb->nh.ipv6h->saddr;
 	daddr = &skb->nh.ipv6h->daddr;
 
-	sk = inet6_get_sock(&tcpv6_prot, daddr, saddr, th->source, th->dest);
+	sk = tcp_v6_lookup(saddr, th->source, daddr, th->dest);
 
 	return sk;
 }
@@ -1229,28 +1423,35 @@
 
 
 struct proto tcpv6_prot = {
-	tcp_close,
-	tcp_v6_connect,
-	tcp_accept,
-	NULL,
-	tcp_write_wakeup,
-	tcp_read_wakeup,
-	tcp_poll,
-	tcp_ioctl,
-	tcp_v6_init_sock,
-	tcp_v6_destroy_sock,
-	tcp_shutdown,
-	tcp_setsockopt,
-	tcp_getsockopt,
-	tcp_v6_sendmsg,
-	tcp_recvmsg,
-	NULL,			/* No special bind()	*/
-	tcp_v6_backlog_rcv,
-	128,
-	0,
-	"TCPv6",
-	0, 0,
-	NULL
+	(struct sock *)&tcpv6_prot,	/* sklist_next */
+	(struct sock *)&tcpv6_prot,	/* sklist_prev */
+	tcp_close,			/* close */
+	tcp_v6_connect,			/* connect */
+	tcp_accept,			/* accept */
+	NULL,				/* retransmit */
+	tcp_write_wakeup,		/* write_wakeup */
+	tcp_read_wakeup,		/* read_wakeup */
+	tcp_poll,			/* poll */
+	tcp_ioctl,			/* ioctl */
+	tcp_v6_init_sock,		/* init */
+	tcp_v6_destroy_sock,		/* destroy */
+	tcp_shutdown,			/* shutdown */
+	tcp_setsockopt,			/* setsockopt */
+	tcp_getsockopt,			/* getsockopt */
+	tcp_v6_sendmsg,			/* sendmsg */
+	tcp_recvmsg,			/* recvmsg */
+	NULL,				/* bind */
+	tcp_v6_backlog_rcv,		/* backlog_rcv */
+	tcp_v6_hash,			/* hash */
+	tcp_v6_unhash,			/* unhash */
+	tcp_v6_rehash,			/* rehash */
+	tcp_good_socknum,		/* good_socknum */
+	tcp_v6_verify_bind,		/* verify_bind */
+	128,				/* max_header */
+	0,				/* retransmits */
+	"TCPv6",			/* name */
+	0,				/* inuse */
+	0				/* highestinuse */
 };
 
 static struct inet6_protocol tcpv6_protocol = 
diff -u --recursive --new-file v2.1.27/linux/net/ipv6/udp.c linux/net/ipv6/udp.c
--- v2.1.27/linux/net/ipv6/udp.c	Sun Jan 26 02:07:51 1997
+++ linux/net/ipv6/udp.c	Mon Mar  3 09:37:45 1997
@@ -7,7 +7,7 @@
  *
  *	Based on linux/ipv4/udp.c
  *
- *	$Id: udp.c,v 1.7 1997/01/26 07:14:58 davem Exp $
+ *	$Id: udp.c,v 1.8 1997/02/28 09:56:35 davem Exp $
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -43,6 +43,139 @@
 
 struct udp_mib udp_stats_in6;
 
+/* Grrr, addr_type already calculated by caller, but I don't want
+ * to add some silly "cookie" argument to this method just for that.
+ */
+static int udp_v6_verify_bind(struct sock *sk, unsigned short snum)
+{
+	struct sock *sk2;
+	int addr_type = ipv6_addr_type(&sk->net_pinfo.af_inet6.rcv_saddr);
+	int retval = 0, sk_reuse = sk->reuse;
+
+	SOCKHASH_LOCK();
+	for(sk2 = udp_hash[snum & (UDP_HTABLE_SIZE - 1)]; sk2 != NULL; sk2 = sk2->next) {
+		if((sk2->num == snum) && (sk2 != sk)) {
+			unsigned char state = sk2->state;
+			int sk2_reuse = sk2->reuse;
+			if(addr_type == IPV6_ADDR_ANY || (!sk2->rcv_saddr)) {
+				if((!sk2_reuse)			||
+				   (!sk_reuse)			||
+				   (state != TCP_LISTEN)) {
+					retval = 1;
+					break;
+				}
+			} else if(!ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+						 &sk2->net_pinfo.af_inet6.rcv_saddr)) {
+				if((!sk_reuse)			||
+				   (!sk2_reuse)			||
+				   (state == TCP_LISTEN)) {
+					retval = 1;
+					break;
+				}
+			}
+		}
+	}
+	SOCKHASH_UNLOCK();
+	return retval;
+}
+
+static void udp_v6_hash(struct sock *sk)
+{
+	struct sock **skp;
+	int num = sk->num;
+
+	num &= (UDP_HTABLE_SIZE - 1);
+	skp = &udp_hash[num];
+
+	SOCKHASH_LOCK();
+	sk->next = *skp;
+	*skp = sk;
+	sk->hashent = num;
+	SOCKHASH_UNLOCK();
+}
+
+static void udp_v6_unhash(struct sock *sk)
+{
+	struct sock **skp;
+	int num = sk->num;
+
+	num &= (UDP_HTABLE_SIZE - 1);
+	skp = &udp_hash[num];
+
+	SOCKHASH_LOCK();
+	while(*skp != NULL) {
+		if(*skp == sk) {
+			*skp = sk->next;
+			break;
+		}
+		skp = &((*skp)->next);
+	}
+	SOCKHASH_UNLOCK();
+}
+
+static void udp_v6_rehash(struct sock *sk)
+{
+	struct sock **skp;
+	int num = sk->num;
+	int oldnum = sk->hashent;
+
+	num &= (UDP_HTABLE_SIZE - 1);
+	skp = &udp_hash[oldnum];
+
+	SOCKHASH_LOCK();
+	while(*skp != NULL) {
+		if(*skp == sk) {
+			*skp = sk->next;
+			break;
+		}
+		skp = &((*skp)->next);
+	}
+	sk->next = udp_hash[num];
+	udp_hash[num] = sk;
+	sk->hashent = num;
+	SOCKHASH_UNLOCK();
+}
+
+static struct sock *udp_v6_lookup(struct in6_addr *saddr, u16 sport,
+				  struct in6_addr *daddr, u16 dport)
+{
+	struct sock *sk, *result = NULL;
+	unsigned short hnum = ntohs(dport);
+	int badness = -1;
+
+	for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
+		if((sk->num == hnum)		&&
+		   (sk->family == AF_INET6)	&&
+		   !(sk->dead && (sk->state == TCP_CLOSE))) {
+			struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6;
+			int score = 0;
+			if(sk->dummy_th.dest) {
+				if(sk->dummy_th.dest != sport)
+					continue;
+				score++;
+			}
+			if(!ipv6_addr_any(&np->rcv_saddr)) {
+				if(ipv6_addr_cmp(&np->rcv_saddr, daddr))
+					continue;
+				score++;
+			}
+			if(!ipv6_addr_any(&np->daddr)) {
+				if(ipv6_addr_cmp(&np->daddr, saddr))
+					continue;
+				score++;
+			}
+			if(score == 3) {
+				result = sk;
+				break;
+			} else if(score > badness) {
+				result = sk;
+				badness = score;
+			}
+		}
+	}
+	return result;
+}
+
 /*
  *
  */
@@ -250,7 +383,7 @@
 	
 	uh = (struct udphdr *) buff;
 
-	sk = inet6_get_sock(&udpv6_prot, daddr, saddr, uh->source, uh->dest);
+	sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest);
    
 	if (sk == NULL)
 	{
@@ -285,6 +418,74 @@
 	return 0;
 }
 
+static int __inline__ inet6_mc_check(struct sock *sk, struct in6_addr *addr)
+{
+	struct ipv6_mc_socklist *mc;
+		
+	for (mc = sk->net_pinfo.af_inet6.ipv6_mc_list; mc; mc=mc->next) {
+		if (ipv6_addr_cmp(&mc->addr, addr) == 0)
+			return 1;
+	}
+
+	return 0;
+}
+
+static struct sock *udp_v6_mcast_next(struct sock *sk,
+				      u16 loc_port, struct in6_addr *loc_addr,
+				      u16 rmt_port, struct in6_addr *rmt_addr)
+{
+	struct sock *s = sk;
+	unsigned short num = ntohs(loc_port);
+	for(; s; s = s->next) {
+		if((s->num == num)		&&
+		   !(s->dead && (s->state == TCP_CLOSE))) {
+			struct ipv6_pinfo *np = &s->net_pinfo.af_inet6;
+			if(s->dummy_th.dest) {
+				if(s->dummy_th.dest != rmt_port)
+					continue;
+			}
+			if(!ipv6_addr_any(&np->daddr) &&
+			   ipv6_addr_cmp(&np->daddr, rmt_addr))
+				continue;
+
+			if(!ipv6_addr_any(&np->rcv_saddr)) {
+				if(ipv6_addr_cmp(&np->rcv_saddr, loc_addr) == 0)
+					return s;
+			}
+			if(!inet6_mc_check(s, loc_addr))
+				continue;
+			return s;
+		}
+	}
+	return NULL;
+}
+
+static void udpv6_mcast_deliver(struct udphdr *uh,
+				struct in6_addr *saddr, struct in6_addr *daddr,
+				struct sk_buff *skb)
+{
+	struct sock *sk, *sk2;
+
+	sk = udp_hash[ntohs(uh->dest) & (UDP_HTABLE_SIZE - 1)];
+	sk = udp_v6_mcast_next(sk, uh->dest, daddr, uh->source, saddr);
+	if(sk) {
+		sk2 = sk;
+		while((sk2 = udp_v6_mcast_next(sk2->next,
+					       uh->dest, saddr,
+					       uh->source, daddr))) {
+			struct sk_buff *buff = skb_clone(skb, GFP_ATOMIC);
+			if(sock_queue_rcv_skb(sk, buff) < 0) {
+				buff->sk = NULL;
+				kfree_skb(buff, FREE_READ);
+			}
+		}
+	}
+	if(!sk || sock_queue_rcv_skb(sk, skb) < 0) {
+		skb->sk = NULL;
+		kfree_skb(skb, FREE_READ);
+	}
+}
+
 int udpv6_rcv(struct sk_buff *skb, struct device *dev,
 	      struct in6_addr *saddr, struct in6_addr *daddr,
 	      struct ipv6_options *opt, unsigned short len,
@@ -335,41 +536,8 @@
 	/* 
 	 *	Multicast receive code 
 	 */
-	if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST)
-	{
-		struct sock *sk2;
-		int lport;
-		
-		lport = ntohs(uh->dest);
-		sk = udpv6_prot.sock_array[lport & (SOCK_ARRAY_SIZE-1)];
-
-		sk = inet6_get_sock_mcast(sk, lport, uh->source,
-					  daddr, saddr);
-
-		if (sk)
-		{
-			sk2 = sk;
-			
-			while ((sk2 = inet6_get_sock_mcast(sk2->next, lport,
-							   uh->source,
-							   daddr, saddr)))
-			{
-				struct sk_buff *buff;
-
-				buff = skb_clone(skb, GFP_ATOMIC);
-
-				if (sock_queue_rcv_skb(sk, buff) < 0) 
-				{
-					buff->sk = NULL;
-					kfree_skb(buff, FREE_READ);
-				}
-			}
-		}
-		if (!sk || sock_queue_rcv_skb(sk, skb) < 0)
-		{
-			skb->sk = NULL;
-			kfree_skb(skb, FREE_READ);
-		}
+	if (ipv6_addr_type(daddr) & IPV6_ADDR_MULTICAST) {
+		udpv6_mcast_deliver(uh, saddr, daddr, skb);
 		return 0;
 	}
 
@@ -380,7 +548,7 @@
 	 * for sock caches... i'll skip this for now.
 	 */
 
-	sk = inet6_get_sock(&udpv6_prot, daddr, saddr, uh->dest, uh->source);
+	sk = udp_v6_lookup(saddr, uh->source, daddr, uh->dest);
 
 	if (sk == NULL)
 	{
@@ -596,28 +764,35 @@
 
 
 struct proto udpv6_prot = {
-	udpv6_close,
-	udpv6_connect,
-	NULL,
-	NULL,
-	NULL,
-	NULL,
-	datagram_poll,
-	udp_ioctl,
-	NULL,
-	NULL,
-	NULL,
-	ipv6_setsockopt,
-	ipv6_getsockopt,
-	udpv6_sendmsg,
-	udpv6_recvmsg,
-	NULL,		/* No special bind function */
-	udpv6_queue_rcv_skb,
-	128,
-	0,
-	"UDP",
-	0, 0,
-	NULL
+	(struct sock *)&udpv6_prot,	/* sklist_next */
+	(struct sock *)&udpv6_prot,	/* sklist_prev */
+	udpv6_close,			/* close */
+	udpv6_connect,			/* connect */
+	NULL,				/* accept */
+	NULL,				/* retransmit */
+	NULL,				/* write_wakeup */
+	NULL,				/* read_wakeup */
+	datagram_poll,			/* poll */
+	udp_ioctl,			/* ioctl */
+	NULL,				/* init */
+	NULL,				/* destroy */
+	NULL,				/* shutdown */
+	ipv6_setsockopt,		/* setsockopt */
+	ipv6_getsockopt,		/* getsockopt */
+	udpv6_sendmsg,			/* sendmsg */
+	udpv6_recvmsg,			/* recvmsg */
+	NULL,				/* bind */
+	udpv6_queue_rcv_skb,		/* backlog_rcv */
+	udp_v6_hash,			/* hash */
+	udp_v6_unhash,			/* unhash */
+	udp_v6_rehash,			/* rehash */
+	udp_good_socknum,		/* good_socknum */
+	udp_v6_verify_bind,		/* verify_bind */
+	128,				/* max_header */
+	0,				/* retransmits */
+	"UDP",				/* name */
+	0,				/* inuse */
+	0				/* highestinuse */
 };
 
 void udpv6_init(void)
diff -u --recursive --new-file v2.1.27/linux/net/netsyms.c linux/net/netsyms.c
--- v2.1.27/linux/net/netsyms.c	Sun Feb  2 05:18:52 1997
+++ linux/net/netsyms.c	Mon Mar  3 09:37:45 1997
@@ -95,6 +95,9 @@
 EXPORT_SYMBOL(sock_alloc_send_skb);
 EXPORT_SYMBOL(sock_init_data);
 EXPORT_SYMBOL(sock_no_fcntl);
+EXPORT_SYMBOL(sock_no_listen);
+EXPORT_SYMBOL(sock_no_getsockopt);
+EXPORT_SYMBOL(sock_no_setsockopt);
 EXPORT_SYMBOL(sock_rfree);
 EXPORT_SYMBOL(sock_wfree);
 EXPORT_SYMBOL(skb_recv_datagram);
@@ -136,7 +139,6 @@
 
 #ifdef CONFIG_INET
 /* Internet layer registration */
-EXPORT_SYMBOL(get_new_socknum);
 EXPORT_SYMBOL(inet_add_protocol);
 EXPORT_SYMBOL(inet_del_protocol);
 EXPORT_SYMBOL(rarp_ioctl_hook);
@@ -156,7 +158,6 @@
 /* inet functions common to v4 and v6 */
 EXPORT_SYMBOL(inet_stream_ops);
 EXPORT_SYMBOL(inet_dgram_ops);
-EXPORT_SYMBOL(inet_remove_sock);
 EXPORT_SYMBOL(inet_release);
 EXPORT_SYMBOL(inet_stream_connect);
 EXPORT_SYMBOL(inet_dgram_connect);
@@ -168,24 +169,28 @@
 EXPORT_SYMBOL(inet_getsockopt);
 EXPORT_SYMBOL(inet_sendmsg);
 EXPORT_SYMBOL(inet_recvmsg);
-EXPORT_SYMBOL(tcp_sock_array);
-EXPORT_SYMBOL(udp_sock_array);
+
+/* Socket demultiplexing. */
+EXPORT_SYMBOL(tcp_good_socknum);
+EXPORT_SYMBOL(tcp_established_hash);
+EXPORT_SYMBOL(tcp_listening_hash);
+EXPORT_SYMBOL(tcp_bound_hash);
+EXPORT_SYMBOL(udp_good_socknum);
+EXPORT_SYMBOL(udp_hash);
+
 EXPORT_SYMBOL(destroy_sock);
 EXPORT_SYMBOL(ip_queue_xmit);
 EXPORT_SYMBOL(csum_partial);
 EXPORT_SYMBOL(dev_lockct);
 EXPORT_SYMBOL(ndisc_eth_hook);
 EXPORT_SYMBOL(memcpy_fromiovecend);
-EXPORT_SYMBOL(csum_partial_copy);
 EXPORT_SYMBOL(csum_partial_copy_fromiovecend);
 EXPORT_SYMBOL(__release_sock);
 EXPORT_SYMBOL(net_timer);
-EXPORT_SYMBOL(inet_put_sock);
 /* UDP/TCP exported functions for TCPv6 */
 EXPORT_SYMBOL(udp_ioctl);
 EXPORT_SYMBOL(udp_connect);
 EXPORT_SYMBOL(udp_sendmsg);
-EXPORT_SYMBOL(tcp_cache_zap);
 EXPORT_SYMBOL(tcp_close);
 EXPORT_SYMBOL(tcp_accept);
 EXPORT_SYMBOL(tcp_write_wakeup);
