]> git.neil.brown.name Git - history.git/commitdiff
Import 2.0.30 2.0.30
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:11:25 +0000 (15:11 -0500)
committerAlan Cox <alan@lxorguk.ukuu.org.uk>
Fri, 23 Nov 2007 20:11:25 +0000 (15:11 -0500)
140 files changed:
Documentation/Configure.help
Documentation/networking/00-INDEX
Documentation/networking/masquerading.txt [new file with mode: 0644]
MAINTAINERS
Makefile
arch/alpha/kernel/ksyms.c
arch/i386/kernel/bios32.c
arch/i386/kernel/ksyms.c
arch/i386/kernel/ldt.c
arch/i386/kernel/vm86.c
arch/i386/lib/semaphore.S
drivers/block/floppy.c
drivers/block/ide-cd.c
drivers/block/ll_rw_blk.c
drivers/cdrom/cdu31a.c
drivers/char/Config.in
drivers/char/lp.c
drivers/char/random.c
drivers/char/serial.c
drivers/net/3c59x.c
drivers/net/Config.in
drivers/net/README.multicast
drivers/net/README.wavelan
drivers/net/Space.c
drivers/net/a2065.c
drivers/net/ariadne.c
drivers/net/atarilance.c
drivers/net/defxx.c
drivers/net/depca.c
drivers/net/eexpress.c
drivers/net/eth16i.c
drivers/net/ewrk3.c
drivers/net/hydra.c
drivers/net/i82586.h
drivers/net/ibmtr.c
drivers/net/lance.c
drivers/net/lance32.c
drivers/net/ne.c
drivers/net/new_tunnel.c
drivers/net/ni52.c
drivers/net/plip.c
drivers/net/sdla.c
drivers/net/tulip.c
drivers/net/wavelan.c
drivers/net/wavelan.h
drivers/net/wavelan.p.h [new file with mode: 0644]
drivers/net/wic.c
drivers/pci/pci.c
drivers/scsi/AM53C974.c
drivers/scsi/BusLogic.c
drivers/scsi/BusLogic.h
drivers/scsi/Config.in
drivers/scsi/FlashPoint.c [new file with mode: 0644]
drivers/scsi/LICENSE.FlashPoint [new file with mode: 0644]
drivers/scsi/Makefile
drivers/scsi/README.BusLogic
drivers/scsi/README.FlashPoint
drivers/scsi/aha1542.c
drivers/scsi/aha1740.c
drivers/scsi/aha1740.h
drivers/scsi/aic7xxx.c
drivers/scsi/eata.c
drivers/scsi/eata.h
drivers/scsi/scsi.c
drivers/scsi/scsi_ioctl.c
drivers/scsi/sd.c
drivers/scsi/sr.c
drivers/scsi/st.c
drivers/scsi/u14-34f.c
drivers/scsi/u14-34f.h
fs/block_dev.c
fs/buffer.c
fs/ext2/inode.c
fs/inode.c
fs/namei.c
fs/nfs/dir.c
fs/proc/procfs_syms.c
fs/super.c
include/asm-alpha/semaphore.h
include/asm-alpha/unistd.h
include/asm-i386/semaphore.h
include/asm-i386/string-486.h
include/asm-i386/string.h
include/linux/atalk.h
include/linux/blk.h
include/linux/ext2_fs.h
include/linux/fs.h
include/linux/ip_fw.h
include/linux/netdevice.h
include/linux/pci.h
include/linux/proc_fs.h
include/linux/random.h
include/linux/socket.h
include/linux/sysctl.h
include/linux/wireless.h [new file with mode: 0644]
include/net/ip.h
include/net/ip_masq.h
include/net/raw.h
include/net/sock.h
include/net/tcp.h
include/net/udp.h
include/scsi/scsi.h
kernel/fork.c
kernel/ksyms.c
kernel/resource.c
kernel/sched.c
kernel/sys.c
kernel/sysctl.c
mm/swapfile.c
net/core/dev.c
net/core/skbuff.c
net/ipv4/Config.in
net/ipv4/Makefile
net/ipv4/af_inet.c
net/ipv4/arp.c
net/ipv4/icmp.c
net/ipv4/ip_forward.c
net/ipv4/ip_fw.c
net/ipv4/ip_input.c
net/ipv4/ip_masq.c
net/ipv4/ip_masq_cuseeme.c [new file with mode: 0644]
net/ipv4/ip_masq_ftp.c
net/ipv4/ip_masq_irc.c
net/ipv4/ip_masq_quake.c [new file with mode: 0644]
net/ipv4/ip_masq_raudio.c
net/ipv4/ip_masq_vdolive.c [new file with mode: 0644]
net/ipv4/ip_sockglue.c
net/ipv4/packet.c
net/ipv4/proc.c
net/ipv4/protocol.c
net/ipv4/raw.c
net/ipv4/route.c
net/ipv4/sysctl_net_ipv4.c
net/ipv4/tcp.c
net/ipv4/tcp_input.c
net/ipv4/tcp_output.c
net/ipv4/tcp_timer.c
net/ipv4/udp.c
net/netsyms.c
scripts/Configure

index dc9516b6debdfeafb694d345c598504507f92f00..1df2e1d9063bcec22a5a18ffe8f6392c1d3e19f5 100644 (file)
@@ -436,6 +436,35 @@ CONFIG_FIREWALL
   any machine being run as a router and not on a host. If unsure, say
   N.
 
+SYN flood protection
+CONFIG_SYN_COOKIES
+  Normal TCP/IP networking is open to an attack known as SYN flooding.
+  This attack prevents legitimate users from being able to connect to
+  your computer and requires very little work for the attacker.
+  SYN cookies provide protection against this type of attack. With
+  this option turned on the TCP/IP stack will use a cryptographic
+  challenge protocol known as SYN cookies to enable legitimate users
+  to continue to connect, even when your machine is under attack.
+  The RST_COOKIES option provides an alternative method to accomplish
+  the same end. SYN cookies use less space than RST cookies,
+  but have a small probability of introducing an non timed-out
+  failure to connect in the remote TCP. You can use both options
+  simultatenously.
+
+SYN flood protection
+CONFIG_RST_COOKIES
+  Normal TCP/IP networking is open to an attack known as SYN flooding.
+  This attack prevents legitimate users from being able to connect to
+  your computer and requires very little work for the attacker.
+  SYN cookies provide protection against this type of attack. With
+  this option turned on the TCP/IP stack will use a cryptographic
+  challenge protocol known as RST cookies to enable legitimate users
+  to continue to connect, even when your machine is under attack.
+  The SYN_COOKIES option provides an alternative method to accomplish
+  the same end. RST cookies use more space than SYN cookies on your
+  machine, but never increase the probability of a frozen connection
+  in a remote TCP. You can use both options simultatenously.
+
 Sun floppy controller support
 CONFIG_BLK_DEV_SUNFD
   This is support for floppy drives on Sun Sparc workstations. Say Y
@@ -452,7 +481,7 @@ CONFIG_ALPHA_AVANTI
   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
@@ -528,7 +557,7 @@ 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, this
+  older TGA adapters are *not* VGA compatible).  On such systems, this
   option needs to be enabled 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.
@@ -893,7 +922,7 @@ CONFIG_IP_TRANSPARENT_PROXY
   rules (using the ipfwadm utility) and/or by doing an appropriate
   bind() system call.
 
-IP: masquerading (EXPERIMENTAL)
+IP: masquerading
 CONFIG_IP_MASQUERADE
   If one of the computers on your local network for which your Linux
   box acts as a firewall wants to send something to the outside, your
@@ -912,8 +941,28 @@ CONFIG_IP_MASQUERADE
   from ftp://sunsite.unc.edu/pub/Linux/system/Network/serial/].)
   Details on how to set things up are contained in the
   IP Masquerading FAQ, available at http://www.indyramp.com/masq/
-  This is EXPERIMENTAL code, which means that it need not be completely
-  stable. If you want this, say Y.
+  To use masquerading you must also enable Network Firewalls, IP
+  forwarding/gatewaying, IP firewalling and (ideally, but optionally)
+  IP always defragment.
+  If you want this, say Y.
+
+IP: ipautofw masquerade support
+CONFIG_IP_MASQUERADE_IPAUTOFW
+  ipautofw is a program by Richard Lynch allowing additional
+  support for masquerading protocols which do not (as yet)
+  have additional protocol helpers.  
+  Information and source for ipautofw is available from
+  ftp://ftp.netis.com/pub/members/rlynch/
+  If you want this, say Y.
+
+IP: ICMP masquerading
+CONFIG_IP_MASQUERADE_ICMP
+  The basic masquerade code described for CONFIG_IP_MASQUERADE only
+  handles TCP or UDP packets (and ICMP errors for existing 
+  connections).  This option adds additional support for masquerading
+  ICMP packets, such as ping or the probes used by the Windows 95
+  tracert program.
+  If you want this, say Y.
 
 IP: always defragment 
 CONFIG_IP_ALWAYS_DEFRAG 
@@ -1199,7 +1248,7 @@ SCSI support?
 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
@@ -1263,11 +1312,12 @@ CONFIG_CHR_DEV_SG
 Probe all LUNs on each SCSI device
 CONFIG_SCSI_MULTI_LUN
   If you have a SCSI device that supports more than one LUN (Logical
-  Unit Number), e.g. a CD jukebox, you should say Y here so that all
-  will be found by the SCSI driver. An SCSI device with multiple LUNs
-  acts logically like multiple SCSI devices. The vast majority of SCSI
-  devices have only one LUN, and so most people can say N here and
-  should in fact do so, because it is safer.
+  Unit Number), e.g. a CD jukebox, and only one LUN is detected, you
+  can say Y here to force the SCSI driver to probe for multiple LUNs. 
+  A SCSI device with multiple LUNs acts logically like multiple SCSI
+  devices.  The vast majority of SCSI devices have only one LUN, and
+  so most people can say N here and should in fact do so, because it
+  is safer.
 
 Verbose SCSI error reporting (kernel size +=12K)
 CONFIG_SCSI_CONSTANTS
@@ -1277,7 +1327,7 @@ CONFIG_SCSI_CONSTANTS
 
 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
@@ -1287,7 +1337,7 @@ CONFIG_SCSI_ADVANSYS
 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
@@ -1298,7 +1348,7 @@ CONFIG_SCSI_AHA152X
 
 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
@@ -1310,7 +1360,7 @@ CONFIG_SCSI_AHA1542
  
 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
@@ -1321,7 +1371,7 @@ CONFIG_SCSI_AHA1740
 
 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
@@ -1331,23 +1381,25 @@ CONFIG_SCSI_AIC7XXX
 
 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. 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
@@ -1358,7 +1410,7 @@ CONFIG_SCSI_DTC3280
 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.  Please read the
   SCSI-HOWTO, available via ftp (user: anonymous) at
   sunsite.unc.edu:/pub/Linux/docs/HOWTO.  This driver is also
@@ -1369,8 +1421,8 @@ CONFIG_SCSI_EATA_DMA
 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
@@ -1396,9 +1448,9 @@ CONFIG_SCSI_U14_34F
 
 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 ISA-200S,
+  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 driver is
@@ -1455,7 +1507,7 @@ CONFIG_SCSI_NCR53C7xx_sync
 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
@@ -1551,7 +1603,7 @@ CONFIG_SCSI_NCR53C8XX_DISABLE_PARITY_CHECK
 
 Always IN2000 SCSI support
 CONFIG_SCSI_IN2000
-  This is support for an ISA bus SCSI host adaptor. You'll find
+  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 
@@ -1561,7 +1613,7 @@ CONFIG_SCSI_IN2000
 
 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.
@@ -1582,7 +1634,7 @@ CONFIG_SCSI_QLOGIC_FAS
 
 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
@@ -1608,7 +1660,7 @@ CONFIG_SCSI_SEAGATE
 
 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
@@ -1619,7 +1671,7 @@ CONFIG_SCSI_T128
 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
@@ -1631,7 +1683,7 @@ CONFIG_SCSI_ULTRASTOR
  
 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). If you
@@ -2363,7 +2415,7 @@ CONFIG_PT
   drivers/net/README.pt.  NOTE: The card is capable of DMA and full
   duplex but neither of these have been coded in the driver as yet.
 
-WaveLAN support
+AT&T WaveLAN & DEC RoamAbout DS support
 CONFIG_WAVELAN
   These are cards for wireless ethernet-like networking. Supported are
   AT&T GIS and NCR WaveLAN cards. If you want to use a card of this
@@ -2548,10 +2600,10 @@ CONFIG_ZNET
   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
@@ -2565,10 +2617,10 @@ CONFIG_NET_POCKET
   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,
@@ -2580,7 +2632,7 @@ CONFIG_ATP
   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,
@@ -2589,12 +2641,12 @@ CONFIG_DE600
   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. If you intend to use this
-  pocket adaptor as well as a parallel printer, you should compile
+  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,
@@ -2603,7 +2655,7 @@ CONFIG_DE620
   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. If you intend to use this
-  pocket adaptor as well as a parallel printer, you should compile
+  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.
@@ -2615,7 +2667,7 @@ CONFIG_TR
   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
@@ -3792,6 +3844,20 @@ CONFIG_AP1000
   machine see http://cap.anu.edu.au/cap/projects/linux or mail to
   hackers@cafe.anu.edu.au
 
+Sparc ESP SCSI support
+CONFIG_SCSI_SUNESP
+  This is the driver for the Sun ESP SCSI host adapter. The ESP
+  chipset is present in most SPARC-based computers.
+
+Sparc /dev/openprom compatibility driver
+CONFIG_SUN_OPENPROMIO
+  This driver provides user programs with an interface to the Sparc
+  PROM device tree. The driver implements a SunOS-compatible
+  interface and a NetBSD-compatible interface. If 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 and read
+  Documentation/modules.txt. If unsure, say Y.
+
 # need an empty line after last entry, for sed script in Configure.
 
 #
@@ -3879,4 +3945,9 @@ CONFIG_AP1000
 # LocalWords:  mgetty sendfax gert greenie muc lowlevel Lasermate LanManager io
 # LocalWords:  OOPSes trackball binghamton mobileip ncr IOMAPPED settags ns ser
 # LocalWords:  setsync NEGO MPARITY autotuning prefetch PIIX cdwrite utils rc
-# LocalWords:  PCWATCHDOG berkprod bitgate
+# LocalWords:  PCWATCHDOG berkprod bitgate Boldt boldt ucsb jf kyoto jp euc ntt
+# LocalWords:  Tetsuyasu YAMADA tetsu cauchy nslab nevod perm su doc kaf kheops
+# LocalWords:  traduc Bourgin dbourgin wsc helptext menuconfig kfill READMEs DS
+# LocalWords:  HOWTOs firewalls SYN RST QMAGIC ZMAGIC ipautofw netis rlynch syr
+# LocalWords:  ICMP tracert Xterminal Xkernel jmwobus comfaqs dhcp radio's tapr
+# LocalWords:  pkthome lnxbay RoamAbout mconv sed LocalWords
index f5182b1c7cc7975c4d7196dd0d51e05e699fa5c0..879a4969beb85333d723e0ab962e1f06a766ce31 100644 (file)
@@ -14,6 +14,8 @@ ax25.txt
        - info on using AX.25 and NET/ROM code for Linux
 framerelay.txt
        - info on using Frame Relay/Data Link Connection Identifier (DLCI).
+masquerading.txt
+       - using IP masquerading, multiple machines using a single IP address.
 ncsa-telnet
        - notes on how NCSA telnet (DOS) breaks with MTU discovery enabled.
 net-modules.txt
diff --git a/Documentation/networking/masquerading.txt b/Documentation/networking/masquerading.txt
new file mode 100644 (file)
index 0000000..efee3da
--- /dev/null
@@ -0,0 +1,51 @@
+IP Masquerading lets you run multiple machines on a network behind a
+Linux box so that all the machines (including the Linux masquerade box)
+appear as a single IP address to the outside world.
+
+The main use of masquerading is when your ISP only gives you one IP
+address and wants to charge like a wounded bull for multiple IP
+addresses.  Instead of paying your ISP large amounts of money for a
+separate address for each of your machines, funnel them all through a
+Linux box running IP masquerading.  Even when you have multiple IP
+addresses, you can still use masquerading if you want to hide your
+internal networks from the rest of the world.
+
+To activate IP masquerading, compile the kernel with IP Forwarding, IP
+Firewalling and IP Masquerading, the first two options must be on
+before you can see the masquerade option.  Also consider using the
+ipautofw and ICMP masquerading suboptions.
+
+Some of the masq code is in the kernel, some is in modules so you have
+to make zImage and make modules.  There are masq helper modules to
+handle special protocols, you only need to load a helper module if you
+want to use the corresponding protocol.  Helper modules have to be
+explicitly loaded (usually from somewhere in /etc/rc.d), they cannot be
+loaded using kerneld.  The current helper modules are ip_masq_ftp,
+ip_masq_irc, ip_masq_raudio, ip_masq_cuseeme, ip_masq_vdolive,
+ip_masq_quake.
+
+All of the modules can take a parameter specifying the port they work
+on - ie ftp handles connections to port 21 by default.  This parameter,
+which can be ommitted to take the default port(s) makes the command
+line look like this
+       insmod ip_masq_raudio.o ports=7070,7071,7072
+Up to 12 ports can be specified (this value can be changed if you 
+recompile).
+
+Masquerading is more of a server function than a single user function.
+Using it correctly requires some knowledge of TCP, UDP, IP and a high
+level understanding of some protocols.  For more details on IP
+masquerading, visit 
+       http://www.indyramp.com/masq/
+and read the HOWTO.
+
+There is a mailing list covering use of masqueraing, information can
+be found on the indyramp web site given above - please read the basic
+information before posting to the mailing list.
+
+Other information on masquerading can be found at
+       http://www.wwonline.com/~achau/ipmasq/
+
+                                                       March 5, 1997
+                                                       Keith Owens
+                                                       Nigel Metheringham
index bfcc8a2450887cd9959047c51290337d5da1f7ac..7937f97bfa5b703e18a9fef9a3a75b2db83110d8 100644 (file)
@@ -105,6 +105,11 @@ L: linux-net@vger.rutgers.edu
 W:     http://www.dgii.com/linux/
 S:     Maintained
 
+WAVELAN NETWORK DRIVER & WIRELESS EXTENSIONS
+P:     Jean Tourrilhes
+M:     jt@hplb.hpl.hp.com
+S:     Maintained
+
 APM DRIVER
 P:     Rik Faith & Stephen Rothwell
 M:     faith@cs.unc.edu, Stephen.Rothwell@canb.auug.org.au
@@ -269,11 +274,17 @@ L:        linux-smp@vger.rutgers.edu
 S:     Maintained
 
 SPARC:
-P:     Eddie C. Dost
-M:     ecd@skynet.be
+P:     David S. Miller
+M:     davem@caip.rutgers.edu
 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:
 P:     Martin Mares
 M:     mj@k332.feld.cvut.cz
index 84c6b95b7aebdcd8c288ea9c3aaead9cacea24b0..5e96692b3fda2dd2dccc003c904ea7ad1c6caa08 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 0
-SUBLEVEL = 29
+SUBLEVEL = 30
 
 ARCH = i386
 
index 78d22e669fe8d164795e05514b3638c0a35e5b81..c7d23472522ce5fb4b642048618dcacf9f1ae9a9 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/user.h>
 #include <linux/elfcore.h>
 #include <asm/io.h>
+#include <asm/checksum.h>
 #include <asm/hwrpb.h>
 
 extern void bcopy (const char *src, char *dst, int len);
@@ -26,6 +27,7 @@ extern void __remlu (void);
 extern void __divqu (void);
 extern void __remqu (void);
 
+extern void start_thread(struct pt_regs *, unsigned long, unsigned long);
 extern void dump_thread(struct pt_regs *, struct user *);
 extern int dump_fpu(struct pt_regs *, elf_fpregset_t *);
 
@@ -56,9 +58,12 @@ static struct symbol_table arch_symbol_table = {
        X(__remqu),
        X(insl),
        X(insw),
+       X(insb),
        X(outsl),
        X(outsw),
+       X(outsb),
        X(strcat),
+       X(strncat),
        X(strcmp),
        X(strcpy),
        X(strlen),
@@ -68,11 +73,17 @@ static struct symbol_table arch_symbol_table = {
        X(strstr),
        X(strtok),
        X(strchr),
+       X(strrchr),
        X(memcmp),
        X(memmove),
        X(__memcpy),
        X(__constant_c_memset),
 
+       X(csum_tcpudp_magic),
+       X(ip_fast_csum),
+       X(ip_compute_csum),
+
+       X(start_thread),
        X(dump_thread),
        X(dump_fpu),
        X(hwrpb),
index 159fb8f5d351443414e6950437734019c3f56e9f..7e8f3f1fad04067c448ceed64725b73dee482e37 100644 (file)
  * Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code
  *     moved to drivers/pci/pci.c.
  *
+ * Dec 7, 1996  : Added support for direct configuration access of boards
+ *      with Intel compatible access schemes (tsbogend@alpha.franken.de)
+ *
+ * Feb 3, 1997  : Set internal functions to static, save/restore flags
+ *     avoid dead locks reading broken PCI BIOS, werner@suse.de 
  *
  */
 
@@ -56,6 +61,8 @@
 #include <linux/pci.h>
 
 #include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/io.h>
 
 #define PCIBIOS_PCI_FUNCTION_ID        0xb1XX
 #define PCIBIOS_PCI_BIOS_PRESENT       0xb101
@@ -101,6 +108,7 @@ union bios32 {
        char chars[16];
 };
 
+#ifdef CONFIG_PCI
 /*
  * Physical address of the service directory.  I don't know if we're
  * allowed to have more than one of these or not, so just in case
@@ -114,7 +122,28 @@ static struct {
        unsigned short segment;
 } bios32_indirect = { 0, KERNEL_CS };
 
-#ifdef CONFIG_PCI
+
+/*
+ * function table for accessing PCI configuration space
+ */
+struct pci_access {
+    int (*find_device)(unsigned short, unsigned short, unsigned short, unsigned char *, unsigned char *);
+    int (*find_class)(unsigned int, unsigned short, unsigned char *, unsigned char *);
+    int (*read_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char *);
+    int (*read_config_word)(unsigned char, unsigned char, unsigned char, unsigned short *);
+    int (*read_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int *);
+    int (*write_config_byte)(unsigned char, unsigned char, unsigned char, unsigned char);
+    int (*write_config_word)(unsigned char, unsigned char, unsigned char, unsigned short);
+    int (*write_config_dword)(unsigned char, unsigned char, unsigned char, unsigned int);
+};
+
+/*
+ * pointer to selected PCI access function table
+ */
+static struct pci_access *access_pci = NULL;
+
+
+
 /*
  * Returns the entry point for the given service, NULL on error
  */
@@ -125,7 +154,9 @@ static unsigned long bios32_service(unsigned long service)
        unsigned long address;          /* %ebx */
        unsigned long length;           /* %ecx */
        unsigned long entry;            /* %edx */
+       unsigned long flags;
 
+       save_flags(flags);
        __asm__("lcall (%%edi)"
                : "=a" (return_code),
                  "=b" (address),
@@ -134,6 +165,7 @@ static unsigned long bios32_service(unsigned long service)
                : "0" (service),
                  "1" (0),
                  "D" (&bios32_indirect));
+       restore_flags(flags);
 
        switch (return_code) {
                case 0:
@@ -161,11 +193,13 @@ extern unsigned long check_pcibios(unsigned long memory_start, unsigned long mem
        unsigned char present_status;
        unsigned char major_revision;
        unsigned char minor_revision;
+       unsigned long flags;
        int pack;
 
        if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
                pci_indirect.address = pcibios_entry;
 
+               save_flags(flags);
                __asm__("lcall (%%edi)\n\t"
                        "jc 1f\n\t"
                        "xor %%ah, %%ah\n"
@@ -176,6 +210,7 @@ extern unsigned long check_pcibios(unsigned long memory_start, unsigned long mem
                        : "1" (PCIBIOS_PCI_BIOS_PRESENT),
                          "D" (&pci_indirect)
                        : "bx", "cx");
+               restore_flags(flags);
 
                present_status = (pack >> 16) & 0xff;
                major_revision = (pack >> 8) & 0xff;
@@ -200,17 +235,15 @@ extern unsigned long check_pcibios(unsigned long memory_start, unsigned long mem
        return memory_start;
 }
 
-int pcibios_present(void)
-{
-       return pcibios_entry ? 1 : 0;
-}
 
-int pcibios_find_class (unsigned int class_code, unsigned short index,
+static int pci_bios_find_class (unsigned int class_code, unsigned short index,
        unsigned char *bus, unsigned char *device_fn)
 {
        unsigned long bx;
        unsigned long ret;
+       unsigned long flags;
 
+       save_flags(flags);
        __asm__ ("lcall (%%edi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -221,18 +254,21 @@ int pcibios_find_class (unsigned int class_code, unsigned short index,
                  "c" (class_code),
                  "S" ((int) index),
                  "D" (&pci_indirect));
+       restore_flags(flags);
        *bus = (bx >> 8) & 0xff;
        *device_fn = bx & 0xff;
        return (int) (ret & 0xff00) >> 8;
 }
 
 
-int pcibios_find_device (unsigned short vendor, unsigned short device_id,
+static int pci_bios_find_device (unsigned short vendor, unsigned short device_id,
        unsigned short index, unsigned char *bus, unsigned char *device_fn)
 {
        unsigned short bx;
        unsigned short ret;
+       unsigned long flags;
 
+       save_flags(flags);
        __asm__("lcall (%%edi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -244,17 +280,20 @@ int pcibios_find_device (unsigned short vendor, unsigned short device_id,
                  "d" (vendor),
                  "S" ((int) index),
                  "D" (&pci_indirect));
+       restore_flags(flags);
        *bus = (bx >> 8) & 0xff;
        *device_fn = bx & 0xff;
        return (int) (ret & 0xff00) >> 8;
 }
 
-int pcibios_read_config_byte(unsigned char bus,
+static int pci_bios_read_config_byte(unsigned char bus,
        unsigned char device_fn, unsigned char where, unsigned char *value)
 {
        unsigned long ret;
        unsigned long bx = (bus << 8) | device_fn;
+       unsigned long flags;
 
+       save_flags(flags);
        __asm__("lcall (%%esi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -265,15 +304,18 @@ int pcibios_read_config_byte(unsigned char bus,
                  "b" (bx),
                  "D" ((long) where),
                  "S" (&pci_indirect));
+       restore_flags(flags);
        return (int) (ret & 0xff00) >> 8;
 }
 
-int pcibios_read_config_word (unsigned char bus,
+static int pci_bios_read_config_word (unsigned char bus,
        unsigned char device_fn, unsigned char where, unsigned short *value)
 {
        unsigned long ret;
        unsigned long bx = (bus << 8) | device_fn;
+       unsigned long flags;
 
+       save_flags(flags);
        __asm__("lcall (%%esi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -284,15 +326,18 @@ int pcibios_read_config_word (unsigned char bus,
                  "b" (bx),
                  "D" ((long) where),
                  "S" (&pci_indirect));
+       restore_flags(flags);
        return (int) (ret & 0xff00) >> 8;
 }
 
-int pcibios_read_config_dword (unsigned char bus,
+static int pci_bios_read_config_dword (unsigned char bus,
        unsigned char device_fn, unsigned char where, unsigned int *value)
 {
        unsigned long ret;
        unsigned long bx = (bus << 8) | device_fn;
+       unsigned long flags;
 
+       save_flags(flags);
        __asm__("lcall (%%esi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -303,15 +348,18 @@ int pcibios_read_config_dword (unsigned char bus,
                  "b" (bx),
                  "D" ((long) where),
                  "S" (&pci_indirect));
+       restore_flags(flags);
        return (int) (ret & 0xff00) >> 8;
 }
 
-int pcibios_write_config_byte (unsigned char bus,
+static int pci_bios_write_config_byte (unsigned char bus,
        unsigned char device_fn, unsigned char where, unsigned char value)
 {
        unsigned long ret;
        unsigned long bx = (bus << 8) | device_fn;
+       unsigned long flags;
 
+       save_flags(flags);
        __asm__("lcall (%%esi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -322,15 +370,18 @@ int pcibios_write_config_byte (unsigned char bus,
                  "b" (bx),
                  "D" ((long) where),
                  "S" (&pci_indirect));
+       restore_flags(flags);
        return (int) (ret & 0xff00) >> 8;
 }
 
-int pcibios_write_config_word (unsigned char bus,
+static int pci_bios_write_config_word (unsigned char bus,
        unsigned char device_fn, unsigned char where, unsigned short value)
 {
        unsigned long ret;
        unsigned long bx = (bus << 8) | device_fn;
+       unsigned long flags;
 
+       save_flags(flags);
        __asm__("lcall (%%esi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -341,15 +392,18 @@ int pcibios_write_config_word (unsigned char bus,
                  "b" (bx),
                  "D" ((long) where),
                  "S" (&pci_indirect));
+       restore_flags(flags);
        return (int) (ret & 0xff00) >> 8;
 }
 
-int pcibios_write_config_dword (unsigned char bus,
+static int pci_bios_write_config_dword (unsigned char bus,
        unsigned char device_fn, unsigned char where, unsigned int value)
 {
        unsigned long ret;
        unsigned long bx = (bus << 8) | device_fn;
+       unsigned long flags;
 
+       save_flags(flags);
        __asm__("lcall (%%esi)\n\t"
                "jc 1f\n\t"
                "xor %%ah, %%ah\n"
@@ -360,9 +414,421 @@ int pcibios_write_config_dword (unsigned char bus,
                  "b" (bx),
                  "D" ((long) where),
                  "S" (&pci_indirect));
+       restore_flags(flags);
        return (int) (ret & 0xff00) >> 8;
 }
 
+/*
+ * function table for BIOS32 access
+ */
+static struct pci_access pci_bios_access = {
+      pci_bios_find_device,
+      pci_bios_find_class,
+      pci_bios_read_config_byte,
+      pci_bios_read_config_word,
+      pci_bios_read_config_dword,
+      pci_bios_write_config_byte,
+      pci_bios_write_config_word,
+      pci_bios_write_config_dword
+};
+
+
+
+/*
+ * Given the vendor and device ids, find the n'th instance of that device
+ * in the system.  
+ */
+static int pci_direct_find_device (unsigned short vendor, unsigned short device_id,
+                          unsigned short index, unsigned char *bus,
+                          unsigned char *devfn)
+{
+    unsigned int curr = 0;
+    struct pci_dev *dev;
+    unsigned long flags;
+
+    save_flags(flags);
+    for (dev = pci_devices; dev; dev = dev->next) {
+       if (dev->vendor == vendor && dev->device == device_id) {
+           if (curr == index) {
+               *devfn = dev->devfn;
+               *bus = dev->bus->number;
+               restore_flags(flags);
+               return PCIBIOS_SUCCESSFUL;
+           }
+           ++curr;
+       }
+    }
+    restore_flags(flags);
+    return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+
+/*
+ * Given the class, find the n'th instance of that device
+ * in the system.
+ */
+static int pci_direct_find_class (unsigned int class_code, unsigned short index,
+                         unsigned char *bus, unsigned char *devfn)
+{
+    unsigned int curr = 0;
+    struct pci_dev *dev;
+    unsigned long flags;
+
+    save_flags(flags);
+    for (dev = pci_devices; dev; dev = dev->next) {
+       if (dev->class == class_code) {
+           if (curr == index) {
+               *devfn = dev->devfn;
+               *bus = dev->bus->number;
+               restore_flags(flags);
+               return PCIBIOS_SUCCESSFUL;
+           }
+           ++curr;
+       }
+    }
+    restore_flags(flags);
+    return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+/*
+ * Functions for accessing PCI configuration space with type 1 accesses
+ */
+#define CONFIG_CMD(bus, device_fn, where)   (0x80000000 | (bus << 16) | (device_fn << 8) | (where & ~3))
+
+static int pci_conf1_read_config_byte(unsigned char bus, unsigned char device_fn,
+                              unsigned char where, unsigned char *value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+    switch (where & 3) {
+       case 0: *value = inb(0xCFC);
+               break;
+       case 1: *value = inb(0xCFD);
+               break;
+       case 2: *value = inb(0xCFE);
+               break;
+       case 3: *value = inb(0xCFF);
+               break;
+    }
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_read_config_word (unsigned char bus,
+    unsigned char device_fn, unsigned char where, unsigned short *value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
+    if (where & 2)
+       *value = inw(0xCFE);
+    else
+       *value = inw(0xCFC);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;    
+}
+
+static int pci_conf1_read_config_dword (unsigned char bus, unsigned char device_fn, 
+                                unsigned char where, unsigned int *value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+    *value = inl(0xCFC);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;    
+}
+
+static int pci_conf1_write_config_byte (unsigned char bus, unsigned char device_fn, 
+                                unsigned char where, unsigned char value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);    
+    outb(value, 0xCFC);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_write_config_word (unsigned char bus, unsigned char device_fn, 
+                                unsigned char where, unsigned short value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+    outw(value, 0xCFC);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf1_write_config_dword (unsigned char bus, unsigned char device_fn, 
+                                 unsigned char where, unsigned int value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outl(CONFIG_CMD(bus,device_fn,where), 0xCF8);
+    outl(value, 0xCFC);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+#undef CONFIG_CMD
+
+/*
+ * functiontable for type 1
+ */
+static struct pci_access pci_direct_conf1 = {
+      pci_direct_find_device,
+      pci_direct_find_class,
+      pci_conf1_read_config_byte,
+      pci_conf1_read_config_word,
+      pci_conf1_read_config_dword,
+      pci_conf1_write_config_byte,
+      pci_conf1_write_config_word,
+      pci_conf1_write_config_dword
+};
+
+/*
+ * Functions for accessing PCI configuration space with type 2 accesses
+ */
+#define IOADDR(devfn, where)   ((0xC000 | ((devfn & 0x78) << 5)) + where)
+#define FUNC(devfn)            (((devfn & 7) << 1) | 0xf0)
+
+static int pci_conf2_read_config_byte(unsigned char bus, unsigned char device_fn, 
+                              unsigned char where, unsigned char *value)
+{
+    unsigned long flags;
+
+    if (device_fn & 0x80)
+       return PCIBIOS_DEVICE_NOT_FOUND;
+    save_flags(flags);
+    outb (FUNC(device_fn), 0xCF8);
+    outb (bus, 0xCFA);
+    *value = inb(IOADDR(device_fn,where));
+    outb (0, 0xCF8);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_read_config_word (unsigned char bus, unsigned char device_fn, 
+                               unsigned char where, unsigned short *value)
+{
+    unsigned long flags;
+
+    if (device_fn & 0x80)
+       return PCIBIOS_DEVICE_NOT_FOUND;
+    save_flags(flags);
+    outb (FUNC(device_fn), 0xCF8);
+    outb (bus, 0xCFA);
+    *value = inw(IOADDR(device_fn,where));
+    outb (0, 0xCF8);
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_read_config_dword (unsigned char bus, unsigned char device_fn, 
+                                unsigned char where, unsigned int *value)
+{
+    unsigned long flags;
+
+    if (device_fn & 0x80)
+       return PCIBIOS_DEVICE_NOT_FOUND;
+    save_flags(flags);
+    outb (FUNC(device_fn), 0xCF8);
+    outb (bus, 0xCFA);
+    *value = inl (IOADDR(device_fn,where));    
+    outb (0, 0xCF8);    
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_write_config_byte (unsigned char bus, unsigned char device_fn, 
+                                unsigned char where, unsigned char value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outb (FUNC(device_fn), 0xCF8);
+    outb (bus, 0xCFA);
+    outb (value, IOADDR(device_fn,where));
+    outb (0, 0xCF8);    
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_write_config_word (unsigned char bus, unsigned char device_fn, 
+                                unsigned char where, unsigned short value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outb (FUNC(device_fn), 0xCF8);
+    outb (bus, 0xCFA);
+    outw (value, IOADDR(device_fn,where));
+    outb (0, 0xCF8);    
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+static int pci_conf2_write_config_dword (unsigned char bus, unsigned char device_fn, 
+                                 unsigned char where, unsigned int value)
+{
+    unsigned long flags;
+
+    save_flags(flags);
+    outb (FUNC(device_fn), 0xCF8);
+    outb (bus, 0xCFA);
+    outl (value, IOADDR(device_fn,where));    
+    outb (0, 0xCF8);    
+    restore_flags(flags);
+    return PCIBIOS_SUCCESSFUL;
+}
+
+#undef IOADDR
+#undef FUNC
+
+/*
+ * functiontable for type 2
+ */
+static struct pci_access pci_direct_conf2 = {
+      pci_direct_find_device,
+      pci_direct_find_class,
+      pci_conf2_read_config_byte,
+      pci_conf2_read_config_word,
+      pci_conf2_read_config_dword,
+      pci_conf2_write_config_byte,
+      pci_conf2_write_config_word,
+      pci_conf2_write_config_dword
+};
+
+
+static struct pci_access *check_direct_pci(void)
+{
+    unsigned int tmp;
+    unsigned long flags;
+
+    save_flags(flags);
+
+    /*
+     * check if configuration type 1 works
+     */
+    outb (0x01, 0xCFB);
+    tmp = inl (0xCF8);
+    outl (0x80000000, 0xCF8);
+    if (inl (0xCF8) == 0x80000000) {
+       outl (tmp, 0xCF8);
+       restore_flags(flags);
+       printk("pcibios_init: Using configuration type 1\n");
+       return &pci_direct_conf1;
+    }
+    outl (tmp, 0xCF8);
+
+    /*
+     * check if configuration type 2 works
+     */
+    outb (0x00, 0xCFB);
+    outb (0x00, 0xCF8);
+    outb (0x00, 0xCFA);
+    if (inb (0xCF8) == 0x00 && inb (0xCFC) == 0x00) {
+       restore_flags(flags);
+       printk("pcibios_init: Using configuration type 2\n");
+       return &pci_direct_conf2;
+    }
+    restore_flags(flags);
+    printk("pcibios_init: Not supported chipset for direct PCI access !\n");
+    return NULL;
+}
+
+
+/*
+ * access defined pcibios functions via
+ * the function table
+ */
+
+int pcibios_present(void)
+{
+       return access_pci ? 1 : 0;
+}
+
+int pcibios_find_class (unsigned int class_code, unsigned short index,
+       unsigned char *bus, unsigned char *device_fn)
+{
+   if (access_pci && access_pci->find_class)
+      return access_pci->find_class(class_code, index, bus, device_fn);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_find_device (unsigned short vendor, unsigned short device_id,
+       unsigned short index, unsigned char *bus, unsigned char *device_fn)
+{
+    if (access_pci && access_pci->find_device)
+      return access_pci->find_device(vendor, device_id, index, bus, device_fn);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_read_config_byte (unsigned char bus,
+       unsigned char device_fn, unsigned char where, unsigned char *value)
+{
+    if (access_pci && access_pci->read_config_byte)
+      return access_pci->read_config_byte(bus, device_fn, where, value);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_read_config_word (unsigned char bus,
+       unsigned char device_fn, unsigned char where, unsigned short *value)
+{
+    if (access_pci && access_pci->read_config_word)
+      return access_pci->read_config_word(bus, device_fn, where, value);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_read_config_dword (unsigned char bus,
+       unsigned char device_fn, unsigned char where, unsigned int *value)
+{
+    if (access_pci && access_pci->read_config_dword)
+      return access_pci->read_config_dword(bus, device_fn, where, value);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_write_config_byte (unsigned char bus,
+       unsigned char device_fn, unsigned char where, unsigned char value)
+{
+    if (access_pci && access_pci->write_config_byte)
+      return access_pci->write_config_byte(bus, device_fn, where, value);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
+int pcibios_write_config_word (unsigned char bus,
+       unsigned char device_fn, unsigned char where, unsigned short value)
+{
+    if (access_pci && access_pci->write_config_word)
+      return access_pci->write_config_word(bus, device_fn, where, value);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;    
+}
+
+int pcibios_write_config_dword (unsigned char bus,
+       unsigned char device_fn, unsigned char where, unsigned int value)
+{
+    if (access_pci && access_pci->write_config_dword)
+      return access_pci->write_config_dword(bus, device_fn, where, value);
+    
+    return PCIBIOS_FUNC_NOT_SUPPORTED;
+}
+
 const char *pcibios_strerror (int error)
 {
        static char buf[80];
@@ -398,14 +864,14 @@ const char *pcibios_strerror (int error)
 
 unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
 {
-return mem_start;
+    return mem_start;
 }
 
-
 #endif
 
 unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
 {
+#ifdef CONFIG_PCI
        union bios32 *check;
        unsigned char sum;
        int i, length;
@@ -436,14 +902,16 @@ unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
                printk ("pcibios_init : BIOS32 Service Directory structure at 0x%p\n", check);
                if (!bios32_entry) {
                        if (check->fields.entry >= 0x100000) {
-                               printk("pcibios_init: entry in high memory, unable to access\n");
+                               printk("pcibios_init: entry in high memory, trying direct PCI access\n");
+                               access_pci = check_direct_pci();
                        } else {
-                               bios32_indirect.address = bios32_entry = check->fields.entry;
+                               bios32_entry = check->fields.entry;
                                printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
+                               bios32_indirect.address = bios32_entry;
+                               access_pci = &pci_bios_access;
                        }
                }
        }
-#ifdef CONFIG_PCI
        if (bios32_entry) {
                memory_start = check_pcibios (memory_start, memory_end);
        }
index 86925d31491a26cb8c2d6b2b23913fe478be4a18..2f59c1a19992ac7a2e289efac0b1a6cf75a1a487 100644 (file)
@@ -14,6 +14,7 @@ static struct symbol_table arch_symbol_table = {
        X(dump_thread),
        X(dump_fpu),
        XNOVERS(down_failed),
+       XNOVERS(down_failed_interruptible),
        XNOVERS(up_wakeup),
 #ifdef __SMP__
        X(apic_reg),            /* Needed internally for the I386 inlines */
index ce46529e4621a31e45fb3151d4b2ce8a9e0708a6..ed578b674ffe31dc65b1250c9e4d86c0e81c5aac 100644 (file)
@@ -11,6 +11,7 @@
 #include <asm/segment.h>
 #include <asm/system.h>
 #include <linux/ldt.h>
+#include <asm/ptrace.h>
 
 static int read_ldt(void * ptr, unsigned long bytecount)
 {
@@ -59,7 +60,7 @@ static inline int limits_ok(struct modify_ldt_ldt_s *ldt_info)
        return (last >= first && last < TASK_SIZE);
 }
 
-static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
+static int write_ldt(struct pt_regs * regs, void * ptr, unsigned long bytecount, int oldmode)
 {
        struct modify_ldt_ldt_s ldt_info;
        unsigned long *lp;
@@ -101,6 +102,9 @@ static int write_ldt(void * ptr, unsigned long bytecount, int oldmode)
                        && ldt_info.limit_in_pages == 0
                        && ldt_info.seg_not_present == 1
                        && ldt_info.useable == 0 )) ) {
+               unsigned short sel =(ldt_info.entry_number <<3) | 7;
+               if (regs->fs == sel  || regs->gs == sel)
+                       return -EBUSY;
                *lp = 0;
                *(lp+1) = 0;
                return 0;
@@ -125,8 +129,8 @@ asmlinkage int sys_modify_ldt(int func, void *ptr, unsigned long bytecount)
        if (func == 0)
                return read_ldt(ptr, bytecount);
        if (func == 1)
-               return write_ldt(ptr, bytecount, 1);
+               return write_ldt((struct pt_regs *) &func, ptr, bytecount, 1);
        if (func == 0x11)
-               return write_ldt(ptr, bytecount, 0);
+               return write_ldt((struct pt_regs *) &func, ptr, bytecount, 0);
        return -ENOSYS;
 }
index 49dbcfaacfe53567c2a456f98b46861d872c1c1f..96a51512e6ab2bd0018c5069c19196219f8d7519 100644 (file)
@@ -397,10 +397,10 @@ int handle_vm86_trap(struct vm86_regs * regs, long error_code, int trapno)
                if ( (trapno==3) || (trapno==1) )
                        return_to_32bit(regs, VM86_TRAP + (trapno << 8));
                do_int(regs, trapno, (unsigned char *) (regs->ss << 4), SP(regs));
-               return 1;
+               return 0;
        }
        if (trapno !=1)
-               return 0; /* we let this handle by the calling routine */
+               return 1; /* we let this handle by the calling routine */
        if (current->flags & PF_PTRACED)
                current->blocked &= ~(1 << (SIGTRAP-1));
        send_sig(SIGTRAP, current, 1);
index e7c29c46058aa04ea66af0332c4285db789a5ba6..e09655c3f9ead18068f8211484f1bec0c40604f6 100644 (file)
@@ -25,3 +25,11 @@ ENTRY(up_wakeup)
        call SYMBOL_NAME(__up)
        popl %ecx
        ret
+
+ENTRY(down_failed_interruptible)
+       pushl %eax
+       pushl %ecx
+       call SYMBOL_NAME(__down_interruptible)
+       popl %ecx
+       ret
+
index 3c2ec0af0c955a791d8960a0c4ada10d22fff7bc..1cf8d5d89d0d6e3db985fc89f920c1c4ebdfe1c2 100644 (file)
@@ -276,7 +276,7 @@ static inline int DRIVE(kdev_t x) {
  * current disk size is unknown.
  * [Now it is rather a minimum]
  */
-#define MAX_DISK_SIZE 2 /* 3984*/
+#define MAX_DISK_SIZE 4 /* 3984*/
 
 #define K_64   0x10000         /* 64KB */
 
@@ -697,7 +697,7 @@ static int disk_change(int drive)
                                DPRINT("Disk type is undefined after "
                                       "disk change\n");
                        current_type[drive] = NULL;
-                       floppy_sizes[TOMINOR(current_drive)] = MAX_DISK_SIZE;
+                       floppy_sizes[TOMINOR(drive)] = MAX_DISK_SIZE;
                }
 
                /*USETF(FD_DISK_NEWCHANGE);*/
@@ -4123,9 +4123,11 @@ static void floppy_release_irq_and_dma(void)
        }
 
 #ifdef FLOPPY_SANITY_CHECK
+#ifndef __sparc__
        for (drive=0; drive < N_FDC * 4; drive++)
                if (motor_off_timer[drive].next)
                        printk("motor off timer %d still active\n", drive);
+#endif
 
        if (fd_timeout.next)
                printk("floppy timer still active:%s\n", timeout_message);
index 7269c724b4414c404222df78dde624cba6e87b1b..e9cbd842e20d04e918f1129b4d58650e6882977b 100644 (file)
@@ -639,14 +639,6 @@ static void cdrom_end_request (int uptodate, ide_drive_t *drive)
 {
        struct request *rq = HWGROUP(drive)->rq;
 
-       /* The code in blk.h can screw us up on error recovery if the block
-          size is larger than 1k.  Fix that up here. */
-       if (!uptodate && rq->bh != 0) {
-               int adj = rq->current_nr_sectors - 1;
-               rq->current_nr_sectors -= adj;
-               rq->sector += adj;
-       }
-
        if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate) {
                struct packet_command *pc = (struct packet_command *)
                                              rq->buffer;
index 4fa208d4490c9dce93ef32c4b43201f01cd0bf8a..510d7225939bbc125a325346e5a2bb172737502d 100644 (file)
@@ -568,7 +568,7 @@ void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buf)
                for (; j < 8 && i < nb; j++, i++, buf += buffersize)
                {
                        rdev = dev;
-                       rsector = (b[i] * buffersize) >> 9;
+                       rsector = b[i] * (buffersize >> 9);
 #ifdef CONFIG_BLK_DEV_MD
                        if (major==MD_MAJOR &&
                            md_map (MINOR(dev), &rdev,
index b722efbaaa0d514ca390c776f0d31601e1d8d205..edd5103acaddd7343ecc6ed5d54238b5ac4e774b 100644 (file)
@@ -1,25 +1,25 @@
 /*
-* Sony CDU-31A CDROM interface device driver.
-*
-* Corey Minyard (minyard@wf-rch.cirr.com)
-*
-* Colossians 3:17
-*
-* The Sony interface device driver handles Sony interface CDROM
-* drives and provides a complete block-level interface as well as an
-* ioctl() interface compatible with the Sun (as specified in
-* include/linux/cdrom.h).  With this interface, CDROMs can be
-* accessed and standard audio CDs can be played back normally.
-*
-* WARNING -    All autoprobes have been removed from the driver.
-             You MUST configure the CDU31A via a LILO config
-             at boot time or in lilo.conf.  I have the
-             following in my lilo.conf:
-*
-*                append="cdu31a=0x1f88,0,PAS"
-*
-             The first number is the I/O base address of the
-             card.  The second is the interrupt (0 means none).
+ * Sony CDU-31A CDROM interface device driver.
+ *
+ * Corey Minyard (minyard@wf-rch.cirr.com)
+ *
+ * Colossians 3:17
+ *
+ * The Sony interface device driver handles Sony interface CDROM
+ * drives and provides a complete block-level interface as well as an
+ * ioctl() interface compatible with the Sun (as specified in
+ * include/linux/cdrom.h).  With this interface, CDROMs can be
+ * accessed and standard audio CDs can be played back normally.
+ *
+ * WARNING -   All autoprobes have been removed from the driver.
+ *             You MUST configure the CDU31A via a LILO config
+ *             at boot time or in lilo.conf.  I have the
+ *             following in my lilo.conf:
+ *
+ *                append="cdu31a=0x1f88,0,PAS"
+ *
+ *             The first number is the I/O base address of the
+ *             card.  The second is the interrupt (0 means none).
  *             The third should be "PAS" if on a Pro-Audio
  *             spectrum, or nothing if on something else.
  *
  *             still here, if the eject button is pushed while the
  *             drive light is flashing, the drive will return a bad
  *             status and be reset.  It recovers, though.
+ *
+ *  03/07/97 - Fixed a problem with timers.
  */
 
 #include <linux/major.h>
@@ -931,6 +933,9 @@ handle_sony_cd_attention(void)
    volatile int val;
 
 
+#if DEBUG
+   printk("Entering handle_sony_cd_attention\n");
+#endif
    if (is_attention())
    {
       if (num_consecutive_attentions > CDU31A_MAX_CONSECUTIVE_ATTENTIONS)
@@ -938,6 +943,9 @@ handle_sony_cd_attention(void)
          printk("cdu31a: Too many consecutive attentions: %d\n",
                 num_consecutive_attentions);
          num_consecutive_attentions = 0;
+#if DEBUG
+         printk("Leaving handle_sony_cd_attention at %d\n", __LINE__);
+#endif
          return(0);
       }
 
@@ -980,6 +988,9 @@ handle_sony_cd_attention(void)
       }
 
       num_consecutive_attentions++;
+#if DEBUG
+      printk("Leaving handle_sony_cd_attention at %d\n", __LINE__);
+#endif
       return(1);
    }
    else if (abort_read_started)
@@ -996,10 +1007,16 @@ handle_sony_cd_attention(void)
          val = read_data_register();
       }
       abort_read_started = 0;
+#if DEBUG
+      printk("Leaving handle_sony_cd_attention at %d\n", __LINE__);
+#endif
       return(1);
    }
 
    num_consecutive_attentions = 0;
+#if DEBUG
+   printk("Leaving handle_sony_cd_attention at %d\n", __LINE__);
+#endif
    return(0);
 }
 
@@ -1088,6 +1105,9 @@ start_request(unsigned int sector,
    unsigned int retry_count;
 
 
+#if DEBUG
+   printk("Entering start_request\n");
+#endif
    log_to_msf(sector, params);
    /* If requested, read exactly what was asked. */
    if (read_nsect_only)
@@ -1128,6 +1148,9 @@ start_request(unsigned int sector,
    if (is_busy())
    {
       printk("CDU31A: Timeout while waiting to issue command\n");
+#if DEBUG
+      printk("Leaving start_request at %d\n", __LINE__);
+#endif
       return(1);
    }
    else
@@ -1143,8 +1166,14 @@ start_request(unsigned int sector,
       sony_next_block = sector * 4;
       readahead_dataleft = 0;
       readahead_bad = 0;
+#if DEBUG
+      printk("Leaving start_request at %d\n", __LINE__);
+#endif
       return(0);
    }
+#if DEBUG
+   printk("Leaving start_request at %d\n", __LINE__);
+#endif
 }
 
 /* Abort a pending read operation.  Clear all the drive status and
@@ -1186,6 +1215,13 @@ abort_read(void)
 static void
 handle_abort_timeout(unsigned long data)
 {
+   unsigned long flags;
+
+#if DEBUG
+   printk("Entering handle_abort_timeout\n");
+#endif
+   save_flags(flags);
+   cli();
    /* If it is in use, ignore it. */
    if (!sony_inuse)
    {
@@ -1202,6 +1238,10 @@ handle_abort_timeout(unsigned long data)
       readahead_bad = 0;
       abort_read_started = 1;
    }
+   restore_flags(flags);
+#if DEBUG
+   printk("Leaving handle_abort_timeout\n");
+#endif
 }
 
 /* Actually get data and status from the drive. */
@@ -1216,6 +1256,9 @@ input_data(char         *buffer,
    volatile unsigned char val;
 
 
+#if DEBUG
+   printk("Entering input_data\n");
+#endif
    /* If an XA disk on a CDU31A, skip the first 12 bytes of data from
       the disk.  The real data is after that. */
    if (sony_xa_mode)
@@ -1264,6 +1307,9 @@ input_data(char         *buffer,
          val = read_data_register();
       }
    }
+#if DEBUG
+   printk("Leaving input_data at %d\n", __LINE__);
+#endif
 }
 
 /* read data from the drive.  Note the nsect must be <= 4. */
@@ -1280,6 +1326,10 @@ read_data_block(char          *buffer,
    unsigned int skip;
 
 
+#if DEBUG
+   printk("Entering read_data_block\n");
+#endif
+
    res_reg[0] = 0;
    res_reg[1] = 0;
    *res_size = 0;
@@ -1345,6 +1395,9 @@ read_data_block(char          *buffer,
          {
             get_result(res_reg, res_size);
          }
+#if DEBUG
+         printk("Leaving read_data_block at %d\n", __LINE__);
+#endif
          return;
       }
    }
@@ -1464,6 +1517,9 @@ read_data_block(char          *buffer,
          }
       }
    }
+#if DEBUG
+   printk("Leaving read_data_block at %d\n", __LINE__);
+#endif
 }
 
 /*
@@ -1484,6 +1540,10 @@ do_cdu31a_request(void)
    unsigned long flags;
 
 
+#if DEBUG
+         printk("Entering do_cdu31a_request\n");
+#endif
+
    /* 
     * Make sure no one else is using the driver; wait for them
     * to finish if it is so.
@@ -1501,6 +1561,9 @@ do_cdu31a_request(void)
             end_request(0);
          }
          restore_flags(flags);
+#if DEBUG
+         printk("Leaving do_cdu31a_request at %d\n", __LINE__);
+#endif
          return;
       }
    }
@@ -1516,11 +1579,8 @@ do_cdu31a_request(void)
 
    sti();
 
-   /* If the timer is running, cancel it. */
-   if (cdu31a_abort_timer.next != NULL)
-   {
-      del_timer(&cdu31a_abort_timer);
-   }
+   /* Make sure the timer is cancelled. */
+   del_timer(&cdu31a_abort_timer);
 
    while (1)
    {
@@ -1692,6 +1752,7 @@ try_read_again:
    }
 
 end_do_cdu31a_request:
+   cli();
 #if 0
    /* After finished, cancel any pending operations. */
    abort_read();
@@ -1706,6 +1767,9 @@ end_do_cdu31a_request:
    sony_inuse = 0;
    wake_up_interruptible(&sony_wait);
    restore_flags(flags);
+#if DEBUG
+   printk("Leaving do_cdu31a_request at %d\n", __LINE__);
+#endif
 }
 
 /* Copy overlapping buffers. */
@@ -3139,8 +3203,7 @@ cdu31a_init(void)
       /* use 'mount -o block=2048' */
       blksize_size[MAJOR_NR] = &cdu31a_block_size;
       
-      cdu31a_abort_timer.next = NULL;
-      cdu31a_abort_timer.prev = NULL;
+      init_timer(&cdu31a_abort_timer);
       cdu31a_abort_timer.function = handle_abort_timeout;
    }
 
index 81735883a13bb8b7aec19f797f5b17837a46c9b6..5d97997c5b4493c63130047f2d78a8e6708a1392 100644 (file)
@@ -63,9 +63,8 @@ if [ "$CONFIG_WATCHDOG" != "n" ]; then
      if [ "$CONFIG_WDT_501" = "y" ]; then
          bool '       Fan Tachometer' CONFIG_WDT_501_FAN
      fi
-  else
-     tristate '   Software Watchdog' CONFIG_SOFT_WATCHDOG
   fi
+  tristate '   Software Watchdog' CONFIG_SOFT_WATCHDOG
   tristate '   Berkshire Products PC Watchdog' CONFIG_PCWATCHDOG
 fi
 bool 'Enhanced Real Time Clock Support' CONFIG_RTC
index e546dda61b49d22e03e349c515f447cb899a3c23..f91c1664207585ae3da1c9856f957abb0445f23a 100644 (file)
@@ -116,6 +116,8 @@ static inline int lp_char_interrupt(char lpchar, int minor)
        struct lp_stats *stats;
 
        do {
+           if(need_resched)
+               schedule();
            if ((status = LP_S(minor)) & LP_PBUSY) {
                if (!LP_CAREFUL_READY(minor, status))
                        return 0;
index df829a893c5710bb9e75368d8246d78b928949ea..b0cdddded3b5890fe95615b7461251dd7eb5d466 100644 (file)
@@ -1345,6 +1345,139 @@ __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
        return (seq);
 }
 
+#ifdef CONFIG_RST_COOKIES
+/*
+ * TCP security probe sequence number picking. Losely based upon
+ * secure sequence number algorithm above.
+ */
+__u32 secure_tcp_probe_number(__u32 saddr, __u32 daddr,
+                __u16 sport, __u16 dport, __u32 sseq, int validate)
+{
+       static int      is_init = 0;
+       static int      valid_secret[2];
+       static __u32    secret_timestamp[2];
+       static __u32    secret[2][16];
+       static int      offset = 0;
+       __u32           tmp[16];
+       __u32           seq;
+
+       /*
+        * Pick a random secret the first time we open a TCP
+        * connection, and expire secretes older than 5 minutes.
+        */
+       if (is_init == 0 || jiffies-secret_timestamp[offset] > 600*HZ) {
+               if (is_init == 0) valid_secret[0] = valid_secret[1] = 0;
+               else offset = (offset+1)%2;
+               get_random_bytes(&secret[offset], sizeof(secret[offset]));
+               valid_secret[offset] = 1;
+               secret_timestamp[offset] = jiffies;
+               is_init = 1;
+       }
+
+       memcpy(tmp, secret[offset], sizeof(tmp));
+       /*
+        * Pick a unique starting offset for each
+        * TCP connection endpoints (saddr, daddr, sport, dport)
+        */
+       tmp[8]=saddr;
+       tmp[9]=daddr;
+       tmp[10]=(sport << 16) + dport;
+       HASH_TRANSFORM(tmp, tmp);
+       seq = tmp[1];
+
+       if (!validate) {
+               if (seq == sseq) seq++;
+#if 0
+               printk("init_seq(%lx, %lx, %d, %d, %d) = %d\n",
+                      saddr, daddr, sport, dport, sseq, seq);
+#endif
+               return (seq);
+       } else {
+               if (seq == sseq || (seq+1) == sseq) {
+                       printk("validated probe(%lx, %lx, %d, %d, %d)\n",
+                               saddr, daddr, sport, dport, sseq);
+                       return 1;
+               }
+               if (jiffies-secret_timestamp[(offset+1)%2] <= 1200*HZ) {
+                       memcpy(tmp, secret[(offset+1)%2], sizeof(tmp));
+                       tmp[8]=saddr;
+                       tmp[9]=daddr;
+                       tmp[10]=(sport << 16) + dport;
+                       HASH_TRANSFORM(tmp, tmp);
+                       seq = tmp[1];
+                       if (seq == sseq || (seq+1) == sseq) {
+#ifdef 0
+                               printk("validated probe(%lx, %lx, %d, %d, %d)\n",
+                                       saddr, daddr, sport, dport, sseq);
+#endif
+                               return 1;
+                       }
+               }
+#ifdef 0
+               printk("failed validation on probe(%lx, %lx, %d, %d, %d)\n",
+                       saddr, daddr, sport, dport, sseq);
+#endif
+               return 0;
+       }
+}
+#endif
+
+#ifdef CONFIG_SYN_COOKIES
+/*
+ * Secure SYN cookie computation. This is the algorithm worked out by
+ * Dan Bernstien and Eric Schenk.
+ *
+ * For linux I implement the 1 minute counter by looking at the jiffies clock.
+ * The count is passed in as a parameter;
+ *
+ */
+__u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr,
+                __u16 sport, __u16 dport, __u32 sseq, __u32 count)
+{
+       static int      is_init = 0;
+       static __u32    secret[2][16];
+       __u32           tmp[16];
+       __u32           seq;
+
+       /*
+        * Pick two random secret the first time we open a TCP connection.
+        */
+       if (is_init == 0) {
+               get_random_bytes(&secret[0], sizeof(secret[0]));
+               get_random_bytes(&secret[1], sizeof(secret[1]));
+               is_init = 1;
+       }
+
+       /*
+        * Compute the secure sequence number.
+        * The output should be:
+        *   MD5(sec1,saddr,sport,daddr,dport,sec1) + their sequence number
+         *      + (count * 2^24)
+        *      + (MD5(sec2,saddr,sport,daddr,dport,count,sec2) % 2^24).
+        * Where count increases every minute by 1.
+        */
+
+       memcpy(tmp, secret[0], sizeof(tmp));
+       tmp[8]=saddr;
+       tmp[9]=daddr;
+       tmp[10]=(sport << 16) + dport;
+       HASH_TRANSFORM(tmp, tmp);
+       seq = tmp[1];
+
+       memcpy(tmp, secret[1], sizeof(tmp));
+       tmp[8]=saddr;
+       tmp[9]=daddr;
+       tmp[10]=(sport << 16) + dport;
+       tmp[11]=count;  /* minute counter */
+       HASH_TRANSFORM(tmp, tmp);
+
+       seq += sseq + (count << 24) + (tmp[1] & 0x00ffffff);
+
+       /* Zap lower 3 bits to leave room for the MSS representation */
+       return (seq & 0xfffff8);
+}
+#endif
+
 #ifdef RANDOM_BENCHMARK
 /*
  * This is so we can do some benchmarking of the random driver, to see
index 2af378b098b0b53ae23f18640a9182ee0bbb7e96..76ab51082ecb74652ff8ce6c5e0cc2f04ef2ca79 100644 (file)
@@ -2959,5 +2959,9 @@ void cleanup_module(void)
                if (rs_table[i].type != PORT_UNKNOWN)
                        release_region(rs_table[i].port, 8);
        }
+       if (tmp_buf) {
+               free_page(tmp_buf);
+               tmp_buf = NULL;
+       }
 }
 #endif /* MODULE */
index 0b6a7f0009ddda3a4901bd17395df69e9aa6eb2f..49555963c97518b5dc8c87c6be6ce3bf03781dc2 100644 (file)
@@ -775,15 +775,21 @@ vortex_start_xmit(struct sk_buff *skb, struct device *dev)
                        /* Issue TX_RESET and TX_START commands. */
                        outw(TxReset, ioaddr + EL3_CMD);
                        for (i = 20; i >= 0 ; i--)
-                               if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)                                        break;
+                               if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+                                               break;
                        outw(TxEnable, ioaddr + EL3_CMD);
                        dev->trans_start = jiffies;
                        dev->tbusy = 0;
                        vp->stats.tx_errors++;
                        vp->stats.tx_dropped++;
+                       dev_kfree_skb(skb, FREE_WRITE);
                        return 0;                       /* Yes, silently *drop* the packet! */
                }
-               dev->tbusy = 0;
+       }
+
+       if(skb == NULL) {
+               dev_tint(dev);
+               return NULL;
        }
 
        /* Block a timer-based transmit from overlapping.  This could better be
index 4ab4437d2065ec2a390b8a4f208650697558bd35..f8af4691e88438d1c7b4b23903c235e9b54f00c8 100644 (file)
@@ -32,7 +32,7 @@ if [ "$CONFIG_NET_RADIO" != "n" ]; then
     bool 'Ottawa PI and PI/2 support' CONFIG_PI
   fi
   tristate 'STRIP (Metricom starmode radio IP)' CONFIG_STRIP
-  tristate 'WaveLAN support' CONFIG_WAVELAN
+  tristate 'AT&T WaveLAN & DEC RoamAbout DS support' CONFIG_WAVELAN
   tristate 'WIC Radio IP bridge (EXPERIMENTAL)' CONFIG_WIC
   tristate 'Z8530 SCC kiss emulation driver for AX.25' CONFIG_SCC
 fi
index 38cd658a81a7e8f381667655e4869107c0fb99b4..0d49cee4cc41934da6d6df4029d0ff0081cb90a5 100644 (file)
@@ -45,7 +45,7 @@ seeq          NO              NO              NO              N/A
 sk_g16         NO              NO              YES             N/A
 smc-ultra      YES             YES             YES             Software(#)
 tulip          YES             YES             YES             Hardware
-wavelan                --------Buggy--------           YES             N/A
+wavelan                YES             PROMISC         YES             Hardware
 wd             YES             YES             YES             Software(#)
 znet           YES             YES             YES             Software
 
index 5849f287b22599958eafca9d0d1b5260e4887137..4ccccadf4187241852c62e756d6ae655d44c6190 100644 (file)
@@ -1,3 +1,10 @@
+       This file contain the old documentation of the Wavelan
+driver. This is kept here for historical reason. Look the wavelan man
+page or in wavelan.p.h for a more up to date documentation...
+
+       Jean
+
+----------------------------
 Sun Jul  2 01:38:33 EST 1995
 
 1. At present the driver autoprobes for a WaveLAN card only at I/O address 0x390.
index a49ef6667d9bce58487f96bc6cf9e5006ef8bc08..d6f22f7ae94aaacc84a493a6d30d24016aab41f1 100644 (file)
@@ -409,7 +409,6 @@ struct device eql_dev = {
 #endif 
 
 #ifdef CONFIG_NET_IPIP
-#ifdef CONFIG_IP_FORWARD
        extern int tunnel_init(struct device *);
        
        static struct device tunnel_dev1 = 
@@ -442,22 +441,22 @@ struct device eql_dev = {
 #   undef      NEXT_DEV
 #   define     NEXT_DEV        (&tunnel_dev0)
 
-#endif 
 #endif
 
-#ifdef CONFIG_AP1000
+#ifdef CONFIG_APFDDI
     extern int apfddi_init(struct device *dev);
     static struct device fddi_dev = {
        "fddi", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, apfddi_init };
 #   undef       NEXT_DEV
 #   define      NEXT_DEV        (&fddi_dev)
+#endif
 
+#ifdef CONFIG_APBIF
     extern int bif_init(struct device *dev);
     static struct device bif_dev = {
         "bif", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, bif_init };
 #   undef       NEXT_DEV
 #   define      NEXT_DEV        (&bif_dev)
-
 #endif
        
 extern int loopback_init(struct device *dev);
index 39e34ded7a48a4b33493fbb39473e3ea242ab5a8..d68bd355664e0cb0913181499f8ef3f4bffaff09 100644 (file)
@@ -536,7 +536,7 @@ static int a2065_start_xmit(struct sk_buff *skb, struct device *dev)
 
                dev->tbusy = 0;
                dev->trans_start = jiffies;
-
+               dev_kfree_skb(skb, FREE_WRITE);
                return(0);
        }
 
index 31d599477fce804aaf3dd0f6805515b75ab9aa5e..b47e5fa1f6006ed5a13ee78867c596570a0c55b0 100644 (file)
@@ -578,7 +578,7 @@ static int ariadne_start_xmit(struct sk_buff *skb, struct device *dev)
 
        dev->tbusy = 0;
        dev->trans_start = jiffies;
-
+       dev_kfree_skb(skb, FREE_WRITE);
        return(0);
     }
 
index 2e843537f03c8669fa2550b7ce35aa1a7023fb98..0d3ca94d013bdc486f9b377c26f0f597b4597537 100644 (file)
@@ -756,7 +756,7 @@ static int lance_start_xmit( struct sk_buff *skb, struct device *dev )
 
                dev->tbusy = 0;
                dev->trans_start = jiffies;
-
+               dev_kfree_skb(skb, FREE_WRITE);
                return( 0 );
        }
 
index 0f754add4507cb6de7e96707c54d82b477632b0b..2604dae80ef38b124260d3f5b77f73e56e2d2908 100644 (file)
@@ -3138,6 +3138,7 @@ int dfx_xmt_queue_pkt(
                printk("%s: Invalid packet length - %lu bytes\n", dev->name, skb->len);
                bp->xmt_length_errors++;        /* bump error counter */
                dev_tint(dev);                          /* dequeue packets from xmt queue and send them */
+               dev_kfree_skb(skb, FREE_WRITE);
                return(0);                                      /* return "success" */
                }
 
index 0eef451ff8123b2c35292a7fc8e933aebf7be39d..8cf6fc80fb2d514bbea490753e1ed431c7ccf7ec 100644 (file)
@@ -797,6 +797,7 @@ depca_start_xmit(struct sk_buff *skb, struct device *dev)
       dev->tbusy=0;
       dev->trans_start = jiffies;
       InitRestartDepca(dev);
+      dev_kfree_skb(skb, FREE_WRITE);
     }
     return status;
   } else if (skb == NULL) {
index 60661b6dbb4ba08dc971f9486b60fc16a1ecc1c4..bb30e041a24b40dc1cd5169a899a245ac723cacb 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: eexpress.c,v 1.13 1996/05/19 15:59:51 phil Exp $
+/* $Id: eexpress.c,v 1.13.2.2 1997/03/11 05:52:32 davem Exp $
  *
  * Intel EtherExpress device driver for Linux
  *
@@ -447,6 +447,7 @@ static int eexp_xmit(struct sk_buff *buf, struct device *dev)
                }
                dev_tint(dev);
                outb(SIRQ_en|irqrmap[dev->irq],ioaddr+SET_IRQ);
+               dev_kfree_skb(buf, FREE_WRITE);
                return 0;
        }
 
index ee484c387e50b51ac00f9cb1174f8ededb0d8055..61ff8ac95c33d565aac16a4bfec6329459b17d7f 100644 (file)
@@ -181,8 +181,8 @@ static char *version =
 #define BUFFER_WIDTH_8         BIT(4)       /* 1 = 8bit, 0 = 16bit */
 #define TBS1                   BIT(3)       
 #define TBS0                   BIT(2)
-#define BS1                    BIT(1)       /* 00=8kb,  01=16kb  */
-#define BS0                    BIT(0)       /* 10=32kb, 11=64kb  */
+#define _BS1                    BIT(1)       /* 00=8kb,  01=16kb  */
+#define _BS0                    BIT(0)       /* 10=32kb, 11=64kb  */
 
 #ifndef ETH16I_TX_BUF_SIZE                   /* 0 = 2kb, 1 = 4kb  */ 
 #define ETH16I_TX_BUF_SIZE     2             /* 2 = 8kb, 3 = 16kb */
@@ -453,7 +453,7 @@ static int eth16i_probe1(struct device *dev, short ioaddr)
   dev->stop               = eth16i_close;
   dev->hard_start_xmit    = eth16i_tx;
   dev->get_stats          = eth16i_get_stats;
-  dev->set_multicast_list = &eth16i_multicast;
+  dev->set_multicast_list = eth16i_multicast;
 
   /* Fill in the fields of the device structure with ethernet values. */
   ether_setup(dev);
@@ -505,10 +505,10 @@ static void eth16i_initialize(struct device *dev)
   if( (node_w & 0xFF00) == 0x0800)
     node_byte |= BUFFER_WIDTH_8;
 
-  node_byte |= BS1;
+  node_byte |= _BS1;
 
   if( (node_w & 0x00FF) == 64)
-    node_byte |= BS0;
+    node_byte |= _BS0;
   
   node_byte |= DLC_EN | SRAM_CYCLE_TIME_100NS | (ETH16I_TX_BUF_SIZE << 2);
 
index fc7b5a04ecfe7b649615557c79ec6bf69cc966b8..37d92ccc326bd4a019c14dfe7cbc2ae4d1a7b1d5 100644 (file)
@@ -757,6 +757,7 @@ ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev)
 
       dev->tbusy=0;
       dev->trans_start = jiffies;
+      dev_kfree_skb(skb, FREE_WRITE);
     }
   } else if (skb == NULL) {
     dev_tint(dev);
index aea36d412626724b9db0928383733115950423e0..661a066627b9a0ee15399269fa4003529980b5e6 100644 (file)
@@ -441,7 +441,7 @@ static int hydra_start_xmit(struct sk_buff *skb, struct device *dev)
 
        dev->tbusy = 0;
        dev->trans_start = jiffies;
-
+       dev_kfree_skb(skb, FREE_WRITE);
        return(0);
        }
 
index ff229e9876887b0dbb1958135ab6f25da3dec789..d41702e9ee76f9d73d7aa7c7b694ae2f22001215 100644 (file)
@@ -259,9 +259,14 @@ struct ac_mcs_t
 {
        ach_t           mcs_h;
        unsigned short  mcs_cnt;        /* No. of bytes of MC addresses */
-       unsigned short  mcs_data[3];    /* The first MC address ..      */
+#if 0
+       unsigned char   mcs_data[ADDR_LEN]; /* The first MC address ..  */
+       ...
+#endif
 };
 
+#define I82586_MAX_MULTICAST_ADDRESSES 128     /* Hardware hashed filter */
+
 /*
  * The Transmit Action Command.
  */
index 73cba43f20d97f291051d913408c6514846dd4fe..10902b96b170906ac17c31da84b17038311b094f 100644 (file)
@@ -1460,9 +1460,10 @@ static int tok_send_packet(struct sk_buff *skb, struct device *dev)
                return 0;
        }
        
-       if (set_bit(0,(void *)&dev->tbusy)!=0)
+       if (set_bit(0,(void *)&dev->tbusy)!=0) {
+               dev_kfree_skb(skb, FREE_WRITE);
                DPRINTK("Transmitter access conflict\n");
-       else {
+       else {
                /* Save skb; we'll need it when the adapter asks for the data */
                ti->current_skb=skb; 
                writeb(XMIT_UI_FRAME, ti->srb + offsetof(struct srb_xmit, command));
index b8bb6ac9be7f24f2ba3445e520513be010818d37..be0327169ab15b3026d3868ce2617dd7b65dfa80 100644 (file)
@@ -816,7 +816,7 @@ lance_start_xmit(struct sk_buff *skb, struct device *dev)
 
                dev->tbusy=0;
                dev->trans_start = jiffies;
-
+               dev_kfree_skb(skb, FREE_WRITE);
                return 0;
        }
 
index 2e8fd20078609ba16a5a938491d53175a51fbc85..0395606bc36baa08b9f11cfc0461d241851287bb 100644 (file)
@@ -479,7 +479,7 @@ lance32_start_xmit(struct sk_buff *skb, struct device *dev)
 
                dev->tbusy=0;
                dev->trans_start = jiffies;
-
+               dev_kfree_skb(skb, FREE_WRITE);
                return 0;
        }
 
index 76ee26a4f9aea4e55addaa62bd46a754a4b16f37..fb139862fefe1374f69bd2abaa92fe5584f491ba 100644 (file)
@@ -174,8 +174,10 @@ int ne_probe(struct device *dev)
                /* Strip the I/O address out of the returned value */
                pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
                /* Avoid already found cards from previous ne_probe() calls */
-               if (check_region(pci_ioaddr, NE_IO_EXTENT))
+               if (check_region(pci_ioaddr, NE_IO_EXTENT)) {
+                       pci_irq_line=0;
                        continue;
+               }
                printk("ne.c: PCI BIOS reports ne2000 clone at i/o %#x, irq %d.\n",
                                pci_ioaddr, pci_irq_line);
                if (ne_probe1(dev, pci_ioaddr) != 0) {  /* Shouldn't happen. */
index 4b30cb15f0ef7746e676d1144414b830d3f87a64..eaeab06bcd16607bcafae5b5eec08a470688606b 100644 (file)
@@ -62,7 +62,6 @@
 */
 
 #include <linux/module.h>
-#include <linux/config.h>      /* for CONFIG_IP_FORWARD */
 
 /* Only two headers!! :-) */
 #include <net/ip.h>
@@ -303,9 +302,10 @@ printk("Required room: %d, Tunnel hlen: %d\n", max_headroom, TUNL_HLEN);
         *      If ip_forward() made a copy, it will return 1 so we can free.
         */
 
-#ifdef CONFIG_IP_FORWARD
-       if (ip_forward(skb, dev, IPFWD_NOTTLDEC, target))
-#endif
+       if (sysctl_ip_forward) {
+               if (ip_forward(skb, dev, IPFWD_NOTTLDEC, target))
+                       kfree_skb(skb, FREE_WRITE);
+       } else
                kfree_skb(skb, FREE_WRITE);
 
        /*
index ecd11c1fba4b97256477578ba656d6f71c049abf..c72805a0bf930e72662a185e596289d34605b740 100644 (file)
@@ -1133,6 +1133,7 @@ static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
       ni_attn586();
       WAIT_4_SCB_CMD();
       dev->trans_start = jiffies;
+      dev_kfree_skb(skb, FREE_WRITE);
       return 0;
     }
     else
index 9fbf8cf49e2e3c2ee74d6adff4617a8ab55cbb0e..15338f15b6f381816da97c210d8d2b7b74ba946c 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: plip.c,v 1.16 1996-04-06 15:36:57+09 gniibe Exp $ */
+/* $Id: plip.c,v 1.8.2.1 1997/03/09 02:14:32 davem Exp $ */
 /* PLIP: A parallel port "network" driver for Linux. */
 /* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
 /*
@@ -904,6 +904,7 @@ plip_tx_packet(struct sk_buff *skb, struct device *dev)
        if (skb->len > dev->mtu + dev->hard_header_len) {
                printk("%s: packet too big, %d.\n", dev->name, (int)skb->len);
                dev->tbusy = 0;
+               dev_kfree_skb(skb, FREE_WRITE);
                return 0;
        }
 
index 83d37695bafa5bbbdafc3c6478f66747b030458a..b2e3978b6dab64dc913c0e7e65a3a98e13b273c3 100644 (file)
@@ -658,8 +658,10 @@ static int sdla_transmit(struct sk_buff *skb, struct device *dev)
    if (skb == NULL) 
       return(0);
 
-   if (set_bit(0, (void*)&dev->tbusy) != 0)
+   if (set_bit(0, (void*)&dev->tbusy) != 0) {
       printk(KERN_WARNING "%s: transmitter access conflict.\n", dev->name);
+      dev_kfree_skb(skb, FREE_WRITE);
+   }
    else
    {
       /*
index e8b4f30dbdbbe340dd40b95bb758d0aaffd60a38..324262c2ad938d0f885e179d29e7c0a838337afa 100644 (file)
@@ -817,6 +817,7 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev)
                        if (!tp->port_fix) dev->if_port ++;
                        tp->port_select(dev);
                        dev->trans_start = jiffies;
+                       dev_kfree_skb(skb, FREE_WRITE);
                        return(0);
                }
                printk("%s: transmit timed out, status %8.8x,"
@@ -846,6 +847,7 @@ tulip_start_xmit(struct sk_buff *skb, struct device *dev)
 
                dev->tbusy=0;
                dev->trans_start = jiffies;
+               dev_kfree_skb(skb, FREE_WRITE);
                return(0);
        }
 
index 1066ab8bf9f23c0596ccaa560c3362e4e1850769..ff2618df99ddecd49f69bae53b0a2b3eca06b0df 100644 (file)
+/*
+ *     Wavelan ISA driver
+ *
+ *             Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ * Original copyrigth follow (see also end of this file).
+ * See wavelan.p.h for details.
+ */
+
 /*
  * AT&T GIS (nee NCR) WaveLAN card:
  *     An Ethernet-like radio transceiver
  *     controlled by an Intel 82586 coprocessor.
  */
 
-#include       <linux/module.h>
-
-#include       <linux/kernel.h>
-#include       <linux/sched.h>
-#include       <linux/types.h>
-#include       <linux/fcntl.h>
-#include       <linux/interrupt.h>
-#include       <linux/stat.h>
-#include       <linux/ptrace.h>
-#include       <linux/ioport.h>
-#include       <linux/in.h>
-#include       <linux/string.h>
-#include       <linux/delay.h>
-#include       <asm/system.h>
-#include       <asm/bitops.h>
-#include       <asm/io.h>
-#include       <asm/dma.h>
-#include       <linux/errno.h>
-#include       <linux/netdevice.h>
-#include       <linux/etherdevice.h>
-#include       <linux/skbuff.h>
-#include       <linux/malloc.h>
-#include       <linux/timer.h>
-#include       <linux/proc_fs.h>
-#define        STRUCT_CHECK    1
-#include       "i82586.h"
-#include       "wavelan.h"
-
-#ifndef WAVELAN_DEBUG
-#define WAVELAN_DEBUG                  0
-#endif /* WAVELAN_DEBUG */
-
-#define        WATCHDOG_JIFFIES                512     /* TODO: express in HZ. */
-#define        ENABLE_FULL_PROMISCUOUS         0x10000
-
-#define        nels(a)                         (sizeof(a) / sizeof(a[0]))
-
-typedef struct device          device;
-typedef struct enet_statistics en_stats;
-typedef struct net_local       net_local;
-typedef struct timer_list      timer_list;
-
-struct net_local
-{
-       en_stats        stats;
-       unsigned int    tx_n_in_use;
-       unsigned char   nwid[2];
-       unsigned short  hacr;
-       unsigned short  rx_head;
-       unsigned short  rx_last;
-       unsigned short  tx_first_free;
-       unsigned short  tx_first_in_use;
-       unsigned int    nresets;
-       unsigned int    correct_nwid;
-       unsigned int    wrong_nwid;
-       unsigned int    promiscuous;
-       unsigned int    full_promiscuous;
-       timer_list      watchdog;
-       device          *dev;
-       net_local       *prev;
-       net_local       *next;
-};
-
-extern int             wavelan_probe(device *);        /* See Space.c */
-
-static const char      *version        = "wavelan.c:v9 96/11/17\n";
-
-/*
- * Entry point forward declarations.
- */
-static int             wavelan_probe1(device *, unsigned short);
-static int             wavelan_open(device *);
-static int             wavelan_send_packet(struct sk_buff *, device *);
-static void            wavelan_interrupt(int, void *, struct pt_regs *);
-static int             wavelan_close(device *);
-static en_stats                *wavelan_get_stats(device *);
-static void            wavelan_set_multicast_list(device *);
-static int             wavelan_get_info(char*, char**, off_t, int, int);
-
-/*
- * Other forward declarations.
- */
-static void            wavelan_cu_show_one(device *, net_local *, int, unsigned short);
-static void            wavelan_cu_start(device *);
-static void            wavelan_ru_start(device *);
-static void            wavelan_watchdog(unsigned long);
-#if    0
-static void            wavelan_psa_show(psa_t *);
-static void            wavelan_mmc_show(unsigned short);
-#endif /* 0 */
-static void            wavelan_scb_show(unsigned short);
-static void            wavelan_ru_show(device *);
-static void            wavelan_cu_show(device *);
-static void            wavelan_dev_show(device *);
-static void            wavelan_local_show(device *);
+#include "wavelan.p.h"         /* Private header */
 
-static unsigned int    wavelan_debug   = WAVELAN_DEBUG;
-static net_local       *first_wavelan  = (net_local *)0;
+/************************* MISC SUBROUTINES **************************/
+/*
+ * Subroutines which won't fit in one of the following category
+ * (wavelan modem or i82586)
+ */
 
-static
-unsigned long
-wavelan_splhi(void)
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for disabling interrupts.
+ */
+static inline unsigned long
+wv_splhi(void)
 {
-       unsigned long   flags;
+  unsigned long flags;
 
-       save_flags(flags);
-       cli();
+  save_flags(flags);
+  cli();
 
-       return flags;
+  return(flags);
 }
 
-static
-void
-wavelan_splx(unsigned long flags)
+/*------------------------------------------------------------------*/
+/*
+ * Wrapper for re-enabling interrupts.
+ */
+static inline void
+wv_splx(unsigned long  flags)
 {
-       restore_flags(flags);
+  restore_flags(flags);
 }
 
-static
-unsigned short
-hasr_read(unsigned short ioaddr)
+/*------------------------------------------------------------------*/
+/*
+ * Translate irq number to PSA irq parameter
+ */
+static u_char
+wv_irq_to_psa(int      irq)
 {
-       return inw(HASR(ioaddr));
+  if(irq < 0 || irq >= NELS(irqvals))
+    return 0;
+
+  return irqvals[irq];
 }
 
-static
-void
-hacr_write(unsigned short ioaddr, int hacr)
+/*------------------------------------------------------------------*/
+/*
+ * Translate PSA irq parameter to irq number 
+ */
+static int
+wv_psa_to_irq(u_char   irqval)
 {
-       outw(hacr, HACR(ioaddr));
+  int  irq;
+
+  for(irq = 0; irq < NELS(irqvals); irq++)
+    if(irqvals[irq] == irqval)
+      return irq;
+
+  return -1;
 }
 
-static
-void
-hacr_write_slow(unsigned short ioaddr, int hacr)
+#ifdef STRUCT_CHECK
+/*------------------------------------------------------------------*/
+/*
+ * Sanity routine to verify the sizes of the various WaveLAN interface
+ * structures.
+ */
+static char *
+wv_struct_check(void)
 {
-       hacr_write(ioaddr, hacr);
-       /* delay might only be needed sometimes */
-       udelay(1000);
-}
+#define        SC(t,s,n)       if (sizeof(t) != s) return(n);
+
+  SC(psa_t, PSA_SIZE, "psa_t");
+  SC(mmw_t, MMW_SIZE, "mmw_t");
+  SC(mmr_t, MMR_SIZE, "mmr_t");
+  SC(ha_t, HA_SIZE, "ha_t");
+
+#undef SC
+
+  return((char *) NULL);
+} /* wv_structuct_check */
+#endif /* STRUCT_CHECK */
 
+/********************* HOST ADAPTER SUBROUTINES *********************/
 /*
- * Set the channel attention bit.
+ * Usefull subroutines to manage the wavelan ISA interface
+ *
+ * One major difference with the Pcmcia hardware (exept the port mapping)
+ * is that we have to keep the state of the Host Control Register
+ * because of the interrupt enable & bus size flags.
  */
-static
-void
-set_chan_attn(unsigned short ioaddr, unsigned short current_hacr)
+
+/*------------------------------------------------------------------*/
+/*
+ * Read from card's Host Adaptor Status Register.
+ */
+static inline u_short
+hasr_read(u_short      ioaddr)
 {
-       hacr_write(ioaddr, current_hacr | HACR_CA);
-}
+  return(inw(HASR(ioaddr)));
+} /* hasr_read */
 
+/*------------------------------------------------------------------*/
 /*
- * Reset, and then set host adaptor into default mode.
+ * Write to card's Host Adapter Command Register.
  */
-static
-void
-wavelan_reset(unsigned short ioaddr)
+static inline void
+hacr_write(u_short     ioaddr,
+          u_short      hacr)
 {
-       hacr_write_slow(ioaddr, HACR_RESET);
-       hacr_write(ioaddr, HACR_DEFAULT);
-}
+  outw(hacr, HACR(ioaddr));
+} /* hacr_write */
 
-static
-void
-wavelan_16_off(unsigned short ioaddr, unsigned short hacr)
+/*------------------------------------------------------------------*/
+/*
+ * Write to card's Host Adapter Command Register. Include a delay for
+ * those times when it is needed.
+ */
+static inline void
+hacr_write_slow(u_short        ioaddr,
+               u_short hacr)
 {
-       hacr &= ~HACR_16BITS;
+  hacr_write(ioaddr, hacr);
+  /* delay might only be needed sometimes */
+  udelay(1000L);
+} /* hacr_write_slow */
 
-       hacr_write(ioaddr, hacr);
-}
+/*------------------------------------------------------------------*/
+/*
+ * Set the channel attention bit.
+ */
+static inline void
+set_chan_attn(u_short  ioaddr,
+             u_short   hacr)
+{
+  hacr_write(ioaddr, hacr | HACR_CA);
+} /* set_chan_attn */
 
-static
-void
-wavelan_16_on(unsigned short ioaddr, unsigned short hacr)
+/*------------------------------------------------------------------*/
+/*
+ * Reset, and then set host adaptor into default mode.
+ */
+static inline void
+wv_hacr_reset(u_short  ioaddr)
 {
-       hacr |= HACR_16BITS;
+  hacr_write_slow(ioaddr, HACR_RESET);
+  hacr_write(ioaddr, HACR_DEFAULT);
+} /* wv_hacr_reset */
 
-       hacr_write(ioaddr, hacr);
-}
+/*------------------------------------------------------------------*/
+/*
+ * Set the i/o transfer over the ISA bus to 8 bits mode
+ */
+static inline void
+wv_16_off(u_short      ioaddr,
+         u_short       hacr)
+{
+  hacr &= ~HACR_16BITS;
+  hacr_write(ioaddr, hacr);
+} /* wv_16_off */
 
-static
-void
-wavelan_ints_off(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * Set the i/o transfer over the ISA bus to 8 bits mode
+ */
+static inline void
+wv_16_on(u_short       ioaddr,
+        u_short        hacr)
 {
-       unsigned short  ioaddr;
-       net_local       *lp;
-       unsigned long   x;
+  hacr |= HACR_16BITS;
+  hacr_write(ioaddr, hacr);
+} /* wv_16_on */
 
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
+/*------------------------------------------------------------------*/
+/*
+ * Disable interrupts on the wavelan hardware
+ */
+static inline void
+wv_ints_off(device *   dev)
+{
+  net_local *  lp = (net_local *)dev->priv;
+  u_short      ioaddr = dev->base_addr;
+  u_long       x;
 
-       x = wavelan_splhi();
+  x = wv_splhi();
 
-       lp->hacr &= ~HACR_INTRON;
-       hacr_write(ioaddr, lp->hacr);
+  lp->hacr &= ~HACR_INTRON;
+  hacr_write(ioaddr, lp->hacr);
 
-       wavelan_splx(x);
-}
+  wv_splx(x);
+} /* wv_ints_off */
 
-static
-void
-wavelan_ints_on(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * Enable interrupts on the wavelan hardware
+ */
+static inline void
+wv_ints_on(device *    dev)
 {
-       unsigned short  ioaddr;
-       net_local       *lp;
-       unsigned long   x;
+  net_local *  lp = (net_local *)dev->priv;
+  u_short      ioaddr = dev->base_addr;
+  u_long       x;
 
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
+  x = wv_splhi();
 
-       x = wavelan_splhi();
+  lp->hacr |= HACR_INTRON;
+  hacr_write(ioaddr, lp->hacr);
 
-       lp->hacr |= HACR_INTRON;
-       hacr_write(ioaddr, lp->hacr);
+  wv_splx(x);
+} /* wv_ints_on */
 
-       wavelan_splx(x);
-}
+/******************* MODEM MANAGEMENT SUBROUTINES *******************/
+/*
+ * Usefull subroutines to manage the modem of the wavelan
+ */
 
+/*------------------------------------------------------------------*/
+/*
+ * Read the Parameter Storage Area from the WaveLAN card's memory
+ */
 /*
  * Read bytes from the PSA.
  */
-static
-void
-psa_read(unsigned short ioaddr, unsigned short hacr, int o, unsigned char *b, int n)
+static void
+psa_read(u_short       ioaddr,
+        u_short        hacr,
+        int            o,      /* offset in PSA */
+        u_char *       b,      /* buffer to fill */
+        int            n)      /* size to read */
 {
-       wavelan_16_off(ioaddr, hacr);
+  wv_16_off(ioaddr, hacr);
 
-       while (n-- > 0)
-       {
-               outw(o, PIOR2(ioaddr));
-               o++;
-               *b++ = inb(PIOP2(ioaddr));
-       }
+  while(n-- > 0)
+    {
+      outw(o, PIOR2(ioaddr));
+      o++;
+      *b++ = inb(PIOP2(ioaddr));
+    }
 
-       wavelan_16_on(ioaddr, hacr);
-}
+  wv_16_on(ioaddr, hacr);
+} /* psa_read */
 
-#if    defined(IRQ_SET_WORKS)
+/*------------------------------------------------------------------*/
 /*
- * Write bytes to the PSA.
+ * Write the Paramter Storage Area to the WaveLAN card's memory
  */
-static
-void
-psa_write(unsigned short ioaddr, unsigned short hacr, int o, unsigned char *b, int n)
+static void
+psa_write(u_short      ioaddr,
+         u_short       hacr,
+         int           o,      /* Offset in psa */
+         u_char *      b,      /* Buffer in memory */
+         int           n)      /* Length of buffer */
 {
-       wavelan_16_off(ioaddr, hacr);
+  int  count = 0;
 
-       while (n-- > 0)
-       {
-               outw(o, PIOR2(ioaddr));
-               o++;
-               outb(*b, PIOP2(ioaddr));
-               b++;
-       }
+  wv_16_off(ioaddr, hacr);
 
-       wavelan_16_on(ioaddr, hacr);
-}
-#endif /* defined(IRQ_SET_WORKS) */
+  while(n-- > 0)
+    {
+      outw(o, PIOR2(ioaddr));
+      o++;
 
-/*
- * Read bytes from the on-board RAM.
- */
-static
-void
-obram_read(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
-{
-       n = (n + 1) / (sizeof(unsigned short) / sizeof(unsigned char));
+      outb(*b, PIOP2(ioaddr));
+      b++;
 
-       outw(o, PIOR1(ioaddr));
+      /* Wait for the memory to finish its write cycle */
+      count = 0;
+      while((count++ < 100) &&
+           (hasr_read(ioaddr) & HASR_PSA_BUSY))
+       udelay(1000);
+    }
 
-       insw(PIOP1(ioaddr), (unsigned short *)b, n);
-}
+  wv_16_on(ioaddr, hacr);
+} /* psa_write */
 
+#ifdef PSA_CRC
+/*------------------------------------------------------------------*/
 /*
- * Write bytes to the on-board RAM.
+ * Calculate the PSA CRC (not tested yet)
+ * As the Wavelan drivers don't use the CRC, I won't use it either...
+ * Thanks to Valster, Nico <NVALSTER@wcnd.nl.lucent.com> for the code
+ * NOTE: By specifying a length including the CRC position the
+ * returned value should be zero. (i.e. a correct checksum in the PSA)
  */
-static
-void
-obram_write(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+static u_short
+psa_crc(u_short *      psa,    /* The PSA */
+       int             size)   /* Number of short for CRC */
 {
-       n = (n + 1) / (sizeof(unsigned short) / sizeof(unsigned char));
+  int          byte_cnt;       /* Loop on the PSA */
+  u_short      crc_bytes = 0;  /* Data in the PSA */
+  int          bit_cnt;        /* Loop on the bits of the short */
 
-       outw(o, PIOR1(ioaddr));
-
-       outsw(PIOP1(ioaddr), (unsigned short *)b, n);
-}
+  for(byte_cnt = 0; byte_cnt <= size; byte_cnt++ )
+    {
+      crc_bytes ^= psa[byte_cnt];      /* Its an xor */
 
+      for(bit_cnt = 1; bit_cnt < 9; bit_cnt++ )
+       {
+         if(crc_bytes & 0x0001)
+           crc_bytes = (crc_bytes >> 1) ^ 0xA001;
+         else
+           crc_bytes >>= 1 ;
+        }
+    }
+
+  return crc_bytes;
+} /* psa_crc */
+#endif /* PSA_CRC */
+
+/*------------------------------------------------------------------*/
 /*
- * Read bytes from the MMC.
+ * Write 1 byte to the MMC.
  */
-static
-void
-mmc_read(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+static inline void
+mmc_out(u_short                ioaddr,
+       u_short         o,
+       u_char          d)
 {
-       while (n-- > 0)
-       {
-               while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
-                       ;
-
-               outw(o << 1, MMCR(ioaddr));
-               o++;
+  /* Wait for MMC to go idle */
+  while(inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+    ;
 
-               while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
-                       ;
-
-               *b++ = (unsigned char)(inw(MMCR(ioaddr)) >> 8);
-       }
+  outw((u_short) (((u_short) d << 8) | (o << 1) | 1),
+       MMCR(ioaddr));
 }
 
+/*------------------------------------------------------------------*/
 /*
- * Write bytes to the MMC.
+ * Routine to write bytes to the Modem Management Controller.
+ * We start by the end because it is the way it should be !
  */
-static
-void
-mmc_write(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+static inline void
+mmc_write(u_short      ioaddr,
+         u_char        o,
+         u_char *      b,
+         int           n)
 {
-       while (n-- > 0)
-       {
-               while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
-                       ;
+  o += n;
+  b += n;
 
-               outw((unsigned short)(((unsigned short)*b << 8) | (o << 1) | 1), MMCR(ioaddr));
-               b++;
-               o++;
-       }
-}
-
-static int     irqvals[]       =
-{
-          0,    0,    0, 0x01,
-       0x02, 0x04,    0, 0x08,
-          0,    0, 0x10, 0x20,
-       0x40,    0,    0, 0x80,
-};
+  while(n-- > 0 )
+    mmc_out(ioaddr, --o, *(--b));
+} /* mmc_write */
 
-#if    defined(IRQ_SET_WORKS)
-static
-int
-wavelan_unmap_irq(int irq, unsigned char *irqval)
+/*------------------------------------------------------------------*/
+/*
+ * Read 1 byte from the MMC.
+ * Optimised version for 1 byte, avoid using memory...
+ */
+static inline u_char
+mmc_in(u_short ioaddr,
+       u_short o)
 {
-       if (irq < 0 || irq >= nels(irqvals) || irqvals[irq] == 0)
-               return -1;
-       
-       *irqval = (unsigned char)irqvals[irq];
+  while(inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+    ;
+  outw(o << 1, MMCR(ioaddr));
 
-       return 0;
+  while(inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+    ;
+  return (u_char) (inw(MMCR(ioaddr)) >> 8);
 }
-#endif /* defined(IRQ_SET_WORKS) */
 
+/*------------------------------------------------------------------*/
 /*
- * Map values from the irq parameter register to irq numbers.
+ * Routine to read bytes from the Modem Management Controller.
+ * The implementation is complicated by a lack of address lines,
+ * which prevents decoding of the low-order bit.
+ * (code has just been moved in the above function)
+ * We start by the end because it is the way it should be !
  */
-static
-int
-wavelan_map_irq(unsigned char irqval)
+static inline void
+mmc_read(u_short       ioaddr,
+        u_char         o,
+        u_char *       b,
+        int            n)
 {
-       int     irq;
+  o += n;
+  b += n;
 
-       for (irq = 0; irq < nels(irqvals); irq++)
-       {
-               if (irqvals[irq] == (int)irqval)
-                       return irq;
-       }
-
-       return -1;
-}
+  while(n-- > 0)
+    *(--b) = mmc_in(ioaddr, --o);
+} /* mmc_read */
 
+/*------------------------------------------------------------------*/
 /*
- * Initialize the Modem Management Controller.
+ * Wait for the frequency EEprom to complete a command...
+ * I hope this one will be optimally inlined...
  */
-static
-void
-wavelan_mmc_init(device *dev, psa_t *psa)
+static inline void
+fee_wait(u_short       ioaddr, /* i/o port of the card */
+        int            delay,  /* Base delay to wait for */
+        int            number) /* Number of time to wait */
 {
-       unsigned short  ioaddr;
-       net_local       *lp;
-       mmw_t           m;
-       int             configured;
-
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
-       memset(&m, 0x00, sizeof(m));
-
-       /*
-        *      configured = psa->psa_conf_status & 1;
-        *
-        * For now we use the persistent PSA
-        * information as little as possible, thereby
-        * allowing us to return to the same known state
-        * during a hardware reset.
-        */
-       configured = 0;
-       
-       /*
-        * Set default modem control parameters.
-        * See NCR document 407-0024326 Rev. A.
-        */
-       m.mmw_jabber_enable = 0x01;
-       m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
-       m.mmw_ifs = 0x20;
-       m.mmw_mod_delay = 0x04;
-       m.mmw_jam_time = 0x38;
-
-       m.mmw_encr_enable = 0;
-       m.mmw_des_io_invert = 0;
-       m.mmw_freeze = 0;
-       m.mmw_decay_prm = 0;
-       m.mmw_decay_updat_prm = 0;
-
-       if (configured)
-       {
-               /*
-                * Use configuration defaults from parameter storage area.
-                */
-               if (psa->psa_undefined & 1)
-                       m.mmw_loopt_sel = 0x00;
-               else
-                       m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED;
-
-               m.mmw_thr_pre_set = psa->psa_thr_pre_set & 0x3F;
-               m.mmw_quality_thr = psa->psa_quality_thr & 0x0F;
-       }
-       else
-       {
-               if (lp->promiscuous && lp->full_promiscuous)
-                       m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED;
-               else
-                       m.mmw_loopt_sel = 0x00;
-
-               /*
-                * 0x04 for AT,
-                * 0x01 for MCA.
-                */
-               if (psa->psa_comp_number & 1)
-                       m.mmw_thr_pre_set = 0x01;
-               else
-                       m.mmw_thr_pre_set = 0x04;
-
-               m.mmw_quality_thr = 0x03;
-       }
-
-       m.mmw_netw_id_l = lp->nwid[1];
-       m.mmw_netw_id_h = lp->nwid[0];
+  int          count = 0;      /* Wait only a limited time */
 
-       mmc_write(ioaddr, 0, (unsigned char *)&m, sizeof(m));
+  while((count++ < number) &&
+       (mmc_in(ioaddr, mmroff(0, mmr_fee_status)) & MMR_FEE_STATUS_BUSY))
+    udelay(delay);
 }
 
-static
-void
-wavelan_ack(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the Frequency EEprom (frequency select cards).
+ */
+static void
+fee_read(u_short       ioaddr, /* i/o port of the card */
+        u_short        o,      /* destination offset */
+        u_short *      b,      /* data buffer */
+        int            n)      /* number of registers */
 {
-       unsigned short  ioaddr;
-       net_local       *lp;
-       unsigned short  scb_cs;
-       int             i;
-
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
+  b += n;              /* Position at the end of the area */
 
-       obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs));
-       scb_cs &= SCB_ST_INT;
+  /* Write the address */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
 
-       if (scb_cs == 0)
-               return;
+  /* Loop on all buffer */
+  while(n-- > 0)
+    {
+      /* Write the read command */
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_READ);
 
-       obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+      /* Wait until EEprom is ready (should be quick !) */
+      fee_wait(ioaddr, 10, 100);
 
-       set_chan_attn(ioaddr, lp->hacr);
-
-       for (i = 1000; i > 0; i--)
-       {
-               obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
-               if (scb_cs == 0)
-                       break;
-
-               udelay(1000);
-       }
-
-       if (i <= 0)
-               printk("%s: wavelan_ack(): board not accepting command.\n", dev->name);
+      /* Read the value */
+      *--b = ((mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)) << 8) |
+             mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
+    }
 }
 
+/*------------------------------------------------------------------*/
 /*
- * Set channel attention bit and busy wait until command has
- * completed, then acknowledge the command completion.
+ * Write bytes from the Frequency EEprom (frequency select cards).
+ * This is a bit complicated, because the frequency eeprom has to
+ * be unprotected and the write enabled.
+ * Jean II
  */
-static
-int
-wavelan_synchronous_cmd(device *dev, const char *str)
+static void
+fee_write(u_short      ioaddr, /* i/o port of the card */
+         u_short       o,      /* destination offset */
+         u_short *     b,      /* data buffer */
+         int           n)      /* number of registers */
 {
-       unsigned short  ioaddr;
-       net_local       *lp;
-       unsigned short  scb_cmd;
-       ach_t           cb;
-       int             i;
+  b += n;              /* Position at the end of the area */
 
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
+#ifdef EEPROM_IS_PROTECTED     /* disabled */
+#ifdef DOESNT_SEEM_TO_WORK     /* disabled */
+  /* Ask to read the protected register */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRREAD);
 
-       scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO;
-       obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cmd, sizeof(scb_cmd));
+  fee_wait(ioaddr, 10, 100);
 
-       set_chan_attn(ioaddr, lp->hacr);
+  /* Read the protected register */
+  printk("Protected 2 : %02X-%02X\n",
+        mmc_in(ioaddr, mmroff(0, mmr_fee_data_h)),
+        mmc_in(ioaddr, mmroff(0, mmr_fee_data_l)));
+#endif /* DOESNT_SEEM_TO_WORK */
 
-       for (i = 64; i > 0; i--)
-       {
-               obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
-               if (cb.ac_status & AC_SFLD_C)
-                       break;
-
-               udelay(1000);
-       }
-
-       if (i <= 0 || !(cb.ac_status & AC_SFLD_OK))
-       {
-               printk("%s: %s failed; status = 0x%x\n", dev->name, str, cb.ac_status);
-               wavelan_scb_show(ioaddr);
-               return -1;
-       }
+  /* Enable protected register */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PREN);
 
-       wavelan_ack(dev);
+  fee_wait(ioaddr, 10, 100);
 
-       return 0;
-}
+  /* Unprotect area */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n);
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
+#ifdef DOESNT_SEEM_TO_WORK     /* disabled */
+  /* Or use : */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRCLEAR);
+#endif /* DOESNT_SEEM_TO_WORK */
 
-static
-int
-wavelan_hardware_reset(device *dev)
-{
-       unsigned short  ioaddr;
-       psa_t           psa;
-       net_local       *lp;
-       scp_t           scp;
-       iscp_t          iscp;
-       scb_t           scb;
-       ach_t           cb;
-       int             i;
-       ac_cfg_t        cfg;
-       ac_ias_t        ias;
+  fee_wait(ioaddr, 10, 100);
+#endif /* EEPROM_IS_PROTECTED */
 
-       if (wavelan_debug > 0)
-               printk("%s: ->wavelan_hardware_reset(dev=0x%x)\n", dev->name, (unsigned int)dev);
+  /* Write enable */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_EN);
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WREN);
 
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
+  fee_wait(ioaddr, 10, 100);
 
-       lp->nresets++;
+  /* Write the EEprom address */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), o + n - 1);
 
-       wavelan_reset(ioaddr);
-       lp->hacr = HACR_DEFAULT;
+  /* Loop on all buffer */
+  while(n-- > 0)
+    {
+      /* Write the value */
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_data_h), (*--b) >> 8);
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_data_l), *b & 0xFF);
 
-       /*
-        * Clear the onboard RAM.
-        */
-       {
-               unsigned char   zeroes[512];
+      /* Write the write command */
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WRITE);
 
-               memset(&zeroes[0], 0x00, sizeof(zeroes));
+      /* Wavelan doc says : wait at least 10 ms for EEBUSY = 0 */
+      udelay(10000);
+      fee_wait(ioaddr, 10, 100);
+    }
 
-               for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes))
-                       obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes));
-       }
+  /* Write disable */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), MMW_FEE_ADDR_DS);
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_WDS);
 
-       psa_read(ioaddr, lp->hacr, 0, (unsigned char *)&psa, sizeof(psa));
+  fee_wait(ioaddr, 10, 100);
 
-       wavelan_mmc_init(dev, &psa);
+#ifdef EEPROM_IS_PROTECTED     /* disabled */
+  /* Reprotect EEprom */
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x00);
+  mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl), MMW_FEE_CTRL_PRWRITE);
 
-       /*
-        * Construct the command unit structures:
-        * scp, iscp, scb, cb.
-        */
-       memset(&scp, 0x00, sizeof(scp));
-       scp.scp_sysbus = SCP_SY_16BBUS;
-       scp.scp_iscpl = OFFSET_ISCP;
-       obram_write(ioaddr, OFFSET_SCP, (unsigned char *)&scp, sizeof(scp));
+  fee_wait(ioaddr, 10, 100);
+#endif /* EEPROM_IS_PROTECTED */
+}
 
-       memset(&iscp, 0x00, sizeof(iscp));
-       iscp.iscp_busy = 1;
-       iscp.iscp_offset = OFFSET_SCB;
-       obram_write(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
+/************************ I82586 SUBROUTINES *************************/
+/*
+ * Usefull subroutines to manage the Ethernet controler
+ */
 
-       memset(&scb, 0x00, sizeof(scb));
-       scb.scb_command = SCB_CMD_RESET;
-       scb.scb_cbl_offset = OFFSET_CU;
-       scb.scb_rfa_offset = OFFSET_RU;
-       obram_write(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+/*------------------------------------------------------------------*/
+/*
+ * Read bytes from the on-board RAM.
+ * Why inlining this function make it fail ???
+ */
+static /*inline*/ void
+obram_read(u_short     ioaddr,
+          u_short      o,
+          u_char *     b,
+          int          n)
+{
+  outw(o, PIOR1(ioaddr));
+  insw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
+}
 
-       set_chan_attn(ioaddr, lp->hacr);
+/*------------------------------------------------------------------*/
+/*
+ * Write bytes to the on-board RAM.
+ */
+static inline void
+obram_write(u_short    ioaddr,
+           u_short     o,
+           u_char *    b,
+           int         n)
+{
+  outw(o, PIOR1(ioaddr));
+  outsw(PIOP1(ioaddr), (unsigned short *) b, (n + 1) >> 1);
+}
 
-       for (i = 1000; i > 0; i--)
-       {
-               obram_read(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
+/*------------------------------------------------------------------*/
+/*
+ * Acknowledge the reading of the status issued by the i82586
+ */
+static void
+wv_ack(device *                dev)
+{
+  net_local *  lp = (net_local *)dev->priv;
+  u_short      ioaddr = dev->base_addr;
+  u_short      scb_cs;
+  int          i;
+
+  obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
+            (unsigned char *) &scb_cs, sizeof(scb_cs));
+  scb_cs &= SCB_ST_INT;
+
+  if(scb_cs == 0)
+    return;
+
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+             (unsigned char *) &scb_cs, sizeof(scb_cs));
+
+  set_chan_attn(ioaddr, lp->hacr);
+
+  for(i = 1000; i > 0; i--)
+    {
+      obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+      if(scb_cs == 0)
+       break;
+
+      udelay(10);
+    }
+  udelay(100);
+
+#ifdef DEBUG_CONFIG_ERROR
+  if(i <= 0)
+    printk(KERN_INFO "%s: wv_ack(): board not accepting command.\n",
+          dev->name);
+#endif
+}
 
-               if (iscp.iscp_busy == (unsigned short)0)
-                       break;
+/*------------------------------------------------------------------*/
+/*
+ * Set channel attention bit and busy wait until command has
+ * completed, then acknowledge the command completion.
+ */
+static inline int
+wv_synchronous_cmd(device *    dev,
+                  const char * str)
+{
+  net_local *  lp = (net_local *)dev->priv;
+  u_short      ioaddr = dev->base_addr;
+  u_short      scb_cmd;
+  ach_t                cb;
+  int          i;
+
+  scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO;
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+             (unsigned char *) &scb_cmd, sizeof(scb_cmd));
+
+  set_chan_attn(ioaddr, lp->hacr);
+
+  for (i = 1000; i > 0; i--)
+    {
+      obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+      if (cb.ac_status & AC_SFLD_C)
+       break;
+
+      udelay(10);
+    }
+  udelay(100);
+
+  if(i <= 0 || !(cb.ac_status & AC_SFLD_OK))
+    {
+#ifdef DEBUG_CONFIG_ERROR
+      printk(KERN_INFO "%s: %s failed; status = 0x%x\n",
+            dev->name, str, cb.ac_status);
+#endif
+#ifdef DEBUG_I82586_SHOW
+      wv_scb_show(ioaddr);
+#endif
+      return -1;
+    }
+
+  /* Ack the status */
+  wv_ack(dev);
+
+  return 0;
+}
 
-               udelay(1000);
-       }
+/*------------------------------------------------------------------*/
+/*
+ * Configuration commands completion interrupt.
+ * Check if done, and if ok...
+ */
+static inline int
+wv_config_complete(device *    dev,
+                  u_short      ioaddr,
+                  net_local *  lp)
+{
+  unsigned short       mcs_addr;
+  unsigned short       status;
+  int                  ret;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: ->wv_config_complete()\n", dev->name);
+#endif
+
+  mcs_addr = lp->tx_first_in_use + sizeof(ac_tx_t) + sizeof(ac_nop_t)
+    + sizeof(tbd_t) + sizeof(ac_cfg_t) + sizeof(ac_ias_t);
+
+  /* Read the status of the last command (set mc list) */
+  obram_read(ioaddr, acoff(mcs_addr, ac_status), (unsigned char *)&status, sizeof(status));
+
+  /* If not completed -> exit */
+  if((status & AC_SFLD_C) == 0)
+    ret = 0;           /* Not ready to be scrapped */
+  else
+    {
+#ifdef DEBUG_CONFIG_ERROR
+      unsigned short   cfg_addr;
+      unsigned short   ias_addr;
+
+      /* Check mc_config command */
+      if(status & AC_SFLD_OK != 0)
+       printk(KERN_INFO "wv_config_complete(): set_multicast_address failed; status = 0x%x\n",
+              dev->name, str, status);
+
+      /* check ia-config command */
+      ias_addr = mcs_addr - sizeof(ac_ias_t);
+      obram_read(ioaddr, acoff(ias_addr, ac_status), (unsigned char *)&status, sizeof(status));
+      if(status & AC_SFLD_OK != 0)
+       printk(KERN_INFO "wv_config_complete(): set_MAC_address; status = 0x%x\n",
+              dev->name, str, status);
+
+      /* Check config command */
+      cfg_addr = ias_addr - sizeof(ac_cfg_t);
+      obram_read(ioaddr, acoff(cfg_addr, ac_status), (unsigned char *)&status, sizeof(status));
+      if(status & AC_SFLD_OK != 0)
+       printk(KERN_INFO "wv_config_complete(): configure; status = 0x%x\n",
+              dev->name, str, status);
+#endif /* DEBUG_CONFIG_ERROR */
+
+      ret = 1;         /* Ready to be scrapped */
+    }
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: <-wv_config_complete() - %d\n", dev->name, ret);
+#endif
+  return ret;
+}
 
-       if (i <= 0)
-       {
-               printk("%s: wavelan_hardware_reset(): iscp_busy timeout.\n", dev->name);
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
-               return -1;
-       }
+/*------------------------------------------------------------------*/
+/*
+ * Command completion interrupt.
+ * Reclaim as many freed tx buffers as we can.
+ */
+static int
+wv_complete(device *   dev,
+           u_short     ioaddr,
+           net_local * lp)
+{
+  int  nreaped = 0;
 
-       for (i = 15; i > 0; i--)
-       {
-               obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: ->wv_complete()\n", dev->name);
+#endif
 
-               if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA))
-                       break;
+  /* Loop on all the transmit buffers */
+  while(lp->tx_first_in_use != I82586NULL)
+    {
+      unsigned short   tx_status;
 
-               udelay(1000);
-       }
+      /* Read the first transmit buffer */
+      obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), (unsigned char *)&tx_status, sizeof(tx_status));
 
-       if (i <= 0)
-       {
-               printk("%s: wavelan_hardware_reset(): status: expected 0x%02x, got 0x%02x.\n", dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status);
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
-               return -1;
-       }
+      /* Hack for reconfiguration... */
+      if(tx_status == 0xFFFF)
+       if(!wv_config_complete(dev, ioaddr, lp))
+         break;        /* Not completed */
 
-       wavelan_ack(dev);
+      /* If not completed -> exit */
+      if((tx_status & AC_SFLD_C) == 0)
+       break;
 
-       memset(&cb, 0x00, sizeof(cb));
-       cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose);
-       cb.ac_link = OFFSET_CU;
-       obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+      /* We now remove this buffer */
+      nreaped++;
+      --lp->tx_n_in_use;
 
-       if (wavelan_synchronous_cmd(dev, "diag()") == -1)
-       {
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
-               return -1;
-       }
+/*
+if (lp->tx_n_in_use > 0)
+       printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
+*/
 
-       obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
-       if (cb.ac_status & AC_SFLD_FAIL)
+      /* Was it the last one ? */
+      if(lp->tx_n_in_use <= 0)
+       lp->tx_first_in_use = I82586NULL;
+      else
        {
-               printk("%s: wavelan_hardware_reset(): i82586 Self Test failed.\n", dev->name);
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
-               return -1;
+         /* Next one in the chain */
+         lp->tx_first_in_use += TXBLOCKZ;
+         if(lp->tx_first_in_use >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+           lp->tx_first_in_use -= NTXBLOCKS * TXBLOCKZ;
        }
 
-       memset(&cfg, 0x00, sizeof(cfg));
-
-#if    0
-       /*
-        * The default board configuration.
-        */
-       cfg.fifolim_bytecnt     = 0x080c;
-       cfg.addrlen_mode        = 0x2600;
-       cfg.linprio_interframe  = 0x7820;       /* IFS=120, ACS=2 */
-       cfg.slot_time           = 0xf00c;       /* slottime=12    */
-       cfg.hardware            = 0x0008;       /* tx even w/o CD */
-       cfg.min_frame_len       = 0x0040;
-#endif /* 0 */
-
-       /*
-        * For Linux we invert AC_CFG_ALOC(..) so as to conform
-        * to the way that net packets reach us from above.
-        * (See also ac_tx_t.)
-        */
-       cfg.cfg_byte_cnt = AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t));
-       cfg.cfg_fifolim = AC_CFG_FIFOLIM(8);
-       cfg.cfg_byte8 = AC_CFG_SAV_BF(0) |
-                       AC_CFG_SRDY(0);
-       cfg.cfg_byte9 = AC_CFG_ELPBCK(0) |
-                       AC_CFG_ILPBCK(0) |
-                       AC_CFG_PRELEN(AC_CFG_PLEN_2) |
-                       AC_CFG_ALOC(1) |
-                       AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE);
-       cfg.cfg_byte10 = AC_CFG_BOFMET(0) |
-                       AC_CFG_ACR(0) |
-                       AC_CFG_LINPRIO(0);
-       cfg.cfg_ifs = 32;
-       cfg.cfg_slotl = 0;
-       cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) |
-                       AC_CFG_SLTTMHI(2);
-       cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
-                       AC_CFG_BTSTF(0) |
-                       AC_CFG_CRC16(0) |
-                       AC_CFG_NCRC(0) |
-                       AC_CFG_TNCRS(1) |
-                       AC_CFG_MANCH(0) |
-                       AC_CFG_BCDIS(0) |
-                       AC_CFG_PRM(lp->promiscuous);
-       cfg.cfg_byte15 = AC_CFG_ICDS(0) |
-                       AC_CFG_CDTF(0) |
-                       AC_CFG_ICSS(0) |
-                       AC_CFG_CSTF(0);
-/*
-       cfg.cfg_min_frm_len = AC_CFG_MNFRM(64);
-*/
-       cfg.cfg_min_frm_len = AC_CFG_MNFRM(8);
-
-       cfg.cfg_h.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_configure);
-       cfg.cfg_h.ac_link = OFFSET_CU;
-       obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cfg, sizeof(cfg));
+      /* Hack for reconfiguration... */
+      if(tx_status == 0xFFFF)
+       continue;
 
-       if (wavelan_synchronous_cmd(dev, "reset()-configure") == -1)
+      /* Now, check status of the finished command */
+      if(tx_status & AC_SFLD_OK)
        {
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
-
-               return -1;
+         int   ncollisions;
+
+         lp->stats.tx_packets++;
+         ncollisions = tx_status & AC_SFLD_MAXCOL;
+         lp->stats.collisions += ncollisions;
+#ifdef DEBUG_INTERRUPT_INFO
+         if(ncollisions > 0)
+           printk(KERN_DEBUG "%s: wv_complete(): tx completed after %d collisions.\n",
+                  dev->name, ncollisions);
+#endif
        }
-
-       memset(&ias, 0x00, sizeof(ias));
-       ias.ias_h.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_ia_setup);
-       ias.ias_h.ac_link = OFFSET_CU;
-       memcpy(&ias.ias_addr[0], (unsigned char *)&dev->dev_addr[0], sizeof(ias.ias_addr));
-       obram_write(ioaddr, OFFSET_CU, (unsigned char *)&ias, sizeof(ias));
-
-       if (wavelan_synchronous_cmd(dev, "reset()-address") == -1)
+      else
        {
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
-
-               return -1;
+         lp->stats.tx_errors++;
+#ifndef IGNORE_NORMAL_XMIT_ERRS
+         if(tx_status & AC_SFLD_S10)
+           {
+             lp->stats.tx_carrier_errors++;
+#ifdef DEBUG_INTERRUPT_ERROR
+             printk(KERN_INFO "%s: wv_complete(): tx error: no CS.\n",
+                    dev->name);
+#endif
+           }
+#endif /* IGNORE_NORMAL_XMIT_ERRS */
+         if(tx_status & AC_SFLD_S9)
+           {
+             lp->stats.tx_carrier_errors++;
+#ifdef DEBUG_INTERRUPT_ERROR
+             printk(KERN_INFO "%s: wv_complete(): tx error: lost CTS.\n",
+                    dev->name);
+#endif
+           }
+         if(tx_status & AC_SFLD_S8)
+           {
+             lp->stats.tx_fifo_errors++;
+#ifdef DEBUG_INTERRUPT_ERROR
+             printk(KERN_INFO "%s: wv_complete(): tx error: slow DMA.\n",
+                    dev->name);
+#endif
+           }
+#ifndef IGNORE_NORMAL_XMIT_ERRS
+         if(tx_status & AC_SFLD_S6)
+           {
+             lp->stats.tx_heartbeat_errors++;
+#ifdef DEBUG_INTERRUPT_ERROR
+             printk(KERN_INFO "%s: wv_complete(): tx error: heart beat.\n",
+                    dev->name);
+#endif
+           }
+         if(tx_status & AC_SFLD_S5)
+           {
+             lp->stats.tx_aborted_errors++;
+#ifdef DEBUG_INTERRUPT_ERROR
+             printk(KERN_INFO "%s: wv_complete(): tx error: too many collisions.\n",
+                    dev->name);
+#endif
+           }
+#endif /* IGNORE_NORMAL_XMIT_ERRS */
        }
 
-       wavelan_ints_on(dev);
-
-       if (wavelan_debug > 4)
-               wavelan_scb_show(ioaddr);
-
-       wavelan_ru_start(dev);
-       wavelan_cu_start(dev);
-
-       if (wavelan_debug > 0)
-               printk("%s: <-wavelan_hardware_reset(): 0\n", dev->name);
-
-       return 0;
+#ifdef DEBUG_INTERRUPT_INFO
+      printk(KERN_DEBUG "%s: wv_complete(): tx completed, tx_status 0x%04x\n",
+            dev->name, tx_status);
+#endif
+    }
+
+#ifdef DEBUG_INTERRUPT_INFO
+  if(nreaped > 1)
+    printk(KERN_DEBUG "%s: wv_complete(): reaped %d\n", dev->name, nreaped);
+#endif
+
+  /*
+   * Inform upper layers.
+   */
+  if(lp->tx_n_in_use < NTXBLOCKS - 1)
+    {
+      dev->tbusy = 0;
+      mark_bh(NET_BH);
+    }
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: <-wv_complete()\n", dev->name);
+#endif
+  return nreaped;
 }
 
-#if    STRUCT_CHECK == 1
-
-static
-const char     *
-wavelan_struct_check(void)
+/*------------------------------------------------------------------*/
+/*
+ * Reconfigure the i82586, or at least ask for it...
+ * Because wv_82586_config use a transmission buffer, we must do it
+ * when we are sure that there is one left, so we do it now
+ * or in wavelan_packet_xmit() (I can't find any better place,
+ * wavelan_interrupt is not an option...), so you may experience
+ * some delay sometime...
+ */
+static inline void
+wv_82586_reconfig(device *     dev)
 {
-#define        SC(t,s,n)       if (sizeof(t) != s) return n
-       SC(psa_t, PSA_SIZE, "psa_t");
-       SC(mmw_t, MMW_SIZE, "mmw_t");
-       SC(mmr_t, MMR_SIZE, "mmr_t");
-       SC(ha_t, HA_SIZE, "ha_t");
-#undef SC
-
-       return (char *)0;
+  net_local *  lp = (net_local *)dev->priv;
+
+  /* Check if we can do it now ! */
+  if(!(dev->start) || (set_bit(0, (void *)&dev->tbusy) != 0))
+    {
+      lp->reconfig_82586 = 1;
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "%s: wv_82586_reconfig(): delayed (busy = %ld, start = %d)\n",
+            dev->name, dev->tbusy, dev->start);
+#endif
+    }
+  else
+    wv_82586_config(dev);
 }
 
-#endif /* STRUCT_CHECK == 1 */
+/********************* DEBUG & INFO SUBROUTINES *********************/
+/*
+ * This routines are used in the code to show debug informations.
+ * Most of the time, it dump the content of hardware structures...
+ */
 
+#ifdef DEBUG_PSA_SHOW
+/*------------------------------------------------------------------*/
 /*
- * Check for a network adaptor of this type.
- * Return '0' iff one exists.
- * (There seem to be different interpretations of
- * the initial value of dev->base_addr.
- * We follow the example in drivers/net/ne.c.)
+ * Print the formatted contents of the Parameter Storage Area.
  */
-int
-wavelan_probe(device *dev)
+static void
+wv_psa_show(psa_t *    p)
 {
-       int                     i;
-       int                     r;
-       short                   base_addr;
-       static unsigned short   iobase[]        =
-       {
-#if    0
-               Leave out 0x3C0 for now -- seems to clash
-               with some video controllers.
-               Leave out the others too -- we will always
-               use 0x390 and leave 0x300 for the Ethernet device.
-               0x300, 0x390, 0x3E0, 0x3C0,
-#endif /* 0 */
-               0x390,
-       };
-       static struct proc_dir_entry    pe      =
-       {
-               PROC_NET_WAVELAN, 7, "wavelan",
-               S_IFREG | S_IRUGO, 1, 0, 0,
-               0, &proc_net_inode_operations,
-               wavelan_get_info
-       };
-
-       if (wavelan_debug > 0)
-               printk("%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n", dev->name, (unsigned int)dev, (unsigned int)dev->base_addr);
+  printk(KERN_DEBUG "##### wavelan psa contents: #####\n");
+  printk(KERN_DEBUG "psa_io_base_addr_1: 0x%02X %02X %02X %02X\n",
+        p->psa_io_base_addr_1,
+        p->psa_io_base_addr_2,
+        p->psa_io_base_addr_3,
+        p->psa_io_base_addr_4);
+  printk(KERN_DEBUG "psa_rem_boot_addr_1: 0x%02X %02X %02X\n",
+        p->psa_rem_boot_addr_1,
+        p->psa_rem_boot_addr_2,
+        p->psa_rem_boot_addr_3);
+  printk(KERN_DEBUG "psa_holi_params: 0x%02x, ", p->psa_holi_params);
+  printk("psa_int_req_no: %d\n", p->psa_int_req_no);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "psa_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+        p->psa_unused0[0],
+        p->psa_unused0[1],
+        p->psa_unused0[2],
+        p->psa_unused0[3],
+        p->psa_unused0[4],
+        p->psa_unused0[5],
+        p->psa_unused0[6]);
+#endif /* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        p->psa_univ_mac_addr[0],
+        p->psa_univ_mac_addr[1],
+        p->psa_univ_mac_addr[2],
+        p->psa_univ_mac_addr[3],
+        p->psa_univ_mac_addr[4],
+        p->psa_univ_mac_addr[5]);
+  printk(KERN_DEBUG "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+        p->psa_local_mac_addr[0],
+        p->psa_local_mac_addr[1],
+        p->psa_local_mac_addr[2],
+        p->psa_local_mac_addr[3],
+        p->psa_local_mac_addr[4],
+        p->psa_local_mac_addr[5]);
+  printk(KERN_DEBUG "psa_univ_local_sel: %d, ", p->psa_univ_local_sel);
+  printk("psa_comp_number: %d, ", p->psa_comp_number);
+  printk("psa_thr_pre_set: 0x%02x\n", p->psa_thr_pre_set);
+  printk(KERN_DEBUG "psa_feature_select/decay_prm: 0x%02x, ",
+        p->psa_feature_select);
+  printk("psa_subband/decay_update_prm: %d\n", p->psa_subband);
+  printk(KERN_DEBUG "psa_quality_thr: 0x%02x, ", p->psa_quality_thr);
+  printk("psa_mod_delay: 0x%02x\n", p->psa_mod_delay);
+  printk(KERN_DEBUG "psa_nwid: 0x%02x%02x, ", p->psa_nwid[0], p->psa_nwid[1]);
+  printk("psa_nwid_select: %d\n", p->psa_nwid_select);
+  printk(KERN_DEBUG "psa_encryption_select: %d, ", p->psa_encryption_select);
+  printk("psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
+        p->psa_encryption_key[0],
+        p->psa_encryption_key[1],
+        p->psa_encryption_key[2],
+        p->psa_encryption_key[3],
+        p->psa_encryption_key[4],
+        p->psa_encryption_key[5],
+        p->psa_encryption_key[6],
+        p->psa_encryption_key[7]);
+  printk(KERN_DEBUG "psa_databus_width: %d\n", p->psa_databus_width);
+  printk(KERN_DEBUG "psa_call_code/auto_squelch: 0x%02x, ",
+        p->psa_call_code[0]);
+  printk("psa_call_code[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+        p->psa_call_code[0],
+        p->psa_call_code[1],
+        p->psa_call_code[2],
+        p->psa_call_code[3],
+        p->psa_call_code[4],
+        p->psa_call_code[5],
+        p->psa_call_code[6],
+        p->psa_call_code[7]);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "psa_reserved[]: %02X:%02X:%02X:%02X\n",
+        p->psa_reserved[0],
+        p->psa_reserved[1],
+        p->psa_reserved[2],
+        p->psa_reserved[3]);
+#endif /* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "psa_conf_status: %d, ", p->psa_conf_status);
+  printk("psa_crc: 0x%02x%02x, ", p->psa_crc[0], p->psa_crc[1]);
+  printk("psa_crc_status: 0x%02x\n", p->psa_crc_status);
+} /* wv_psa_show */
+#endif /* DEBUG_PSA_SHOW */
+
+#ifdef DEBUG_MMC_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the Modem Management Controller.
+ * This function need to be completed...
+ */
+static void
+wv_mmc_show(device *   dev)
+{
+  u_short      ioaddr = dev->base_addr;
+  net_local *  lp = (net_local *)dev->priv;
+  mmr_t                m;
+
+  /* Basic check */
+  if(hasr_read(ioaddr) & HASR_NO_CLK)
+    {
+      printk(KERN_WARNING "%s: wv_mmc_show: modem not connected\n",
+            dev->name);
+      return;
+    }
+
+  /* Read the mmc */
+  mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+  mmc_read(ioaddr, 0, (u_char *)&m, sizeof(m));
+  mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+#ifdef WIRELESS_EXT    /* If wireless extension exist in the kernel */
+  /* Don't forget to update statistics */
+  lp->wstats.discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+#endif /* WIRELESS_EXT */
+
+  printk(KERN_DEBUG "##### wavelan modem status registers: #####\n");
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "mmc_unused0[]: %02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n",
+        m.mmr_unused0[0],
+        m.mmr_unused0[1],
+        m.mmr_unused0[2],
+        m.mmr_unused0[3],
+        m.mmr_unused0[4],
+        m.mmr_unused0[5],
+        m.mmr_unused0[6],
+        m.mmr_unused0[7]);
+#endif /* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "Encryption algorythm: %02X - Status: %02X\n",
+        m.mmr_des_avail, m.mmr_des_status);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n",
+        m.mmr_unused1[0],
+        m.mmr_unused1[1],
+        m.mmr_unused1[2],
+        m.mmr_unused1[3],
+        m.mmr_unused1[4]);
+#endif /* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "dce_status: 0x%x [%s%s%s%s]\n",
+        m.mmr_dce_status,
+        (m.mmr_dce_status & MMR_DCE_STATUS_RX_BUSY) ? "energy detected,":"",
+        (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ?
+        "loop test indicated," : "",
+        (m.mmr_dce_status & MMR_DCE_STATUS_TX_BUSY) ? "transmitter on," : "",
+        (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ?
+        "jabber timer expired," : "");
+  printk(KERN_DEBUG "Dsp ID: %02X\n",
+        m.mmr_dsp_id);
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "mmc_unused2[]: %02X:%02X\n",
+        m.mmr_unused2[0],
+        m.mmr_unused2[1]);
+#endif /* DEBUG_SHOW_UNUSED */
+  printk(KERN_DEBUG "# correct_nwid: %d, # wrong_nwid: %d\n",
+        (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l,
+        (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
+  printk(KERN_DEBUG "thr_pre_set: 0x%x [current signal %s]\n",
+        m.mmr_thr_pre_set & MMR_THR_PRE_SET,
+        (m.mmr_thr_pre_set & MMR_THR_PRE_SET_CUR) ? "above" : "below");
+  printk(KERN_DEBUG "signal_lvl: %d [%s], ",
+        m.mmr_signal_lvl & MMR_SIGNAL_LVL,
+        (m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) ? "new msg" : "no new msg");
+  printk("silence_lvl: %d [%s], ", m.mmr_silence_lvl & MMR_SILENCE_LVL,
+        (m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) ? "update done" : "no new update");
+  printk("sgnl_qual: 0x%x [%s]\n",
+        m.mmr_sgnl_qual & MMR_SGNL_QUAL,
+        (m.mmr_sgnl_qual & MMR_SGNL_QUAL_ANT) ? "Antenna 1" : "Antenna 0");
+#ifdef DEBUG_SHOW_UNUSED
+  printk(KERN_DEBUG "netw_id_l: %x\n", m.mmr_netw_id_l);
+#endif /* DEBUG_SHOW_UNUSED */
+} /* wv_mmc_show */
+#endif /* DEBUG_MMC_SHOW */
+
+#ifdef DEBUG_I82586_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the last block of the i82586 memory
+ */
+static void
+wv_scb_show(unsigned short     ioaddr)
+{
+  scb_t                scb;
+
+  obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));   
+
+  printk(KERN_DEBUG "##### wavelan system control block: #####\n");
+
+  printk(KERN_DEBUG "status: ");
+  printk("stat 0x%x[%s%s%s%s] ",
+        (scb.scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA | SCB_ST_RNR)) >> 12,
+        (scb.scb_status & SCB_ST_CX) ? "cmd completion interrupt," : "",
+        (scb.scb_status & SCB_ST_FR) ? "frame received," : "",
+        (scb.scb_status & SCB_ST_CNA) ? "cmd unit not active," : "",
+        (scb.scb_status & SCB_ST_RNR) ? "rcv unit not ready," : "");
+  printk("cus 0x%x[%s%s%s] ",
+        (scb.scb_status & SCB_ST_CUS) >> 8,
+        ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_IDLE) ? "idle" : "",
+        ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_SUSP) ? "suspended" : "",
+        ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_ACTV) ? "active" : "");
+  printk("rus 0x%x[%s%s%s%s]\n",
+        (scb.scb_status & SCB_ST_RUS) >> 4,
+        ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_IDLE) ? "idle" : "",
+        ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_SUSP) ? "suspended" : "",
+        ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_NRES) ? "no resources" : "",
+        ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_RDY) ? "ready" : "");
+
+  printk(KERN_DEBUG "command: ");
+  printk("ack 0x%x[%s%s%s%s] ",
+        (scb.scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR | SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12,
+        (scb.scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "",
+        (scb.scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "",
+        (scb.scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "",
+        (scb.scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : "");
+  printk("cuc 0x%x[%s%s%s%s%s] ",
+        (scb.scb_command & SCB_CMD_CUC) >> 8,
+        ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_NOP) ? "nop" : "",
+        ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_GO) ? "start cbl_offset" : "",
+        ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_RES) ? "resume execution" : "",
+        ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_SUS) ? "suspend execution" : "",
+        ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_ABT) ? "abort execution" : "");
+  printk("ruc 0x%x[%s%s%s%s%s]\n",
+        (scb.scb_command & SCB_CMD_RUC) >> 4,
+        ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_NOP) ? "nop" : "",
+        ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_GO) ? "start rfa_offset" : "",
+        ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_RES) ? "resume reception" : "",
+        ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_SUS) ? "suspend reception" : "",
+        ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_ABT) ? "abort reception" : "");
+
+  printk(KERN_DEBUG "cbl_offset 0x%x ", scb.scb_cbl_offset);
+  printk("rfa_offset 0x%x\n", scb.scb_rfa_offset);
+
+  printk(KERN_DEBUG "crcerrs %d ", scb.scb_crcerrs);
+  printk("alnerrs %d ", scb.scb_alnerrs);
+  printk("rscerrs %d ", scb.scb_rscerrs);
+  printk("ovrnerrs %d\n", scb.scb_ovrnerrs);
+}
 
-#if    STRUCT_CHECK == 1
-       if (wavelan_struct_check() != (char *)0)
-       {
-               printk("%s: structure/compiler botch: \"%s\"\n", dev->name, wavelan_struct_check());
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the i82586's receive unit.
+ */
+static void
+wv_ru_show(device *    dev)
+{
+  /* net_local *lp = (net_local *) dev->priv; */
 
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_probe(): ENODEV\n", dev->name);
+  printk(KERN_DEBUG "##### wavelan i82586 receiver unit status: #####\n");
+  printk(KERN_DEBUG "ru:");
+  /*
+   * Not implemented yet...
+   */
+  printk("\n");
+} /* wv_ru_show */
 
-               return ENODEV;
-       }
-#endif /* STRUCT_CHECK == 1 */
+/*------------------------------------------------------------------*/
+/*
+ * Display info about one control block of the i82586 memory
+ */
+static void
+wv_cu_show_one(device *                dev,
+              net_local *      lp,
+              int              i,
+              u_short          p)
+{
+  unsigned short       ioaddr;
+  ac_tx_t              actx;
 
-       base_addr = dev->base_addr;
+  ioaddr = dev->base_addr;
 
-       if (base_addr < 0)
-       {
-               /*
-                * Don't probe at all.
-                */
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_probe(): ENXIO\n", dev->name);
-               return ENXIO;
-       }
+  printk("%d: 0x%x:", i, p);
 
-       if (base_addr > 0x100)
-       {
-               /*
-                * Check a single specified location.
-                */
-               r = wavelan_probe1(dev, base_addr);
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_probe(): %d\n", dev->name, r);
-               if (r == 0)
-                       proc_net_register(&pe);
-               return r;
-       }
+  obram_read(ioaddr, p, (unsigned char *)&actx, sizeof(actx));
+  printk(" status=0x%x,", actx.tx_h.ac_status);
+  printk(" command=0x%x,", actx.tx_h.ac_command);
 
-       for (i = 0; i < nels(iobase); i++)
-       {
-               if (check_region(iobase[i], sizeof(ha_t)))
-                       continue;
-
-               if (wavelan_probe1(dev, iobase[i]) == 0)
-               {
-                       if (wavelan_debug > 0)
-                               printk("%s: <-wavelan_probe(): 0\n", dev->name);
-                       proc_net_register(&pe);
-                       return 0;
-               }
-       }
+  /*
+  {
+    tbd_t      tbd;
 
-       if (wavelan_debug > 0)
-               printk("%s: <-wavelan_probe(): ENODEV\n", dev->name);
+    obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd));
+    printk(" tbd_status=0x%x,", tbd.tbd_status);
+  }
+  */
 
-       return ENODEV;
+  printk("|");
 }
 
-static
-int
-wavelan_probe1(device *dev, unsigned short ioaddr)
+/*------------------------------------------------------------------*/
+/*
+ * Print status of the command unit of the i82586
+ */
+static void
+wv_cu_show(device *    dev)
 {
-       psa_t           psa;
-       int             irq;
-       int             i;
-       net_local       *lp;
-       int             enable_full_promiscuous;
-
-       if (wavelan_debug > 0)
-               printk("%s: ->wavelan_probe1(dev=0x%x, ioaddr=0x%x)\n", dev->name, (unsigned int)dev, ioaddr);
-
-       wavelan_reset(ioaddr);
-
-       psa_read(ioaddr, HACR_DEFAULT, 0, (unsigned char *)&psa, sizeof(psa));
-
-       /*
-        * Check the first three octets of the MAC address
-        * for the manufacturer's code.
-        */ 
-       if
-       (
-               psa.psa_univ_mac_addr[0] != SA_ADDR0
-               ||
-               psa.psa_univ_mac_addr[1] != SA_ADDR1
-               ||
-               (
-                       psa.psa_univ_mac_addr[2] != SA_ADDR2
-                       &&
-                       psa.psa_univ_mac_addr[2] != SA_ALT_ADDR2
-               )
-       )
-       {
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_probe1(): ENODEV\n", dev->name);
-               return ENODEV;
-       }
-
-       printk("%s: WaveLAN at %#x,", dev->name, ioaddr);
-
-       if (dev->irq != 0)
-       {
-               printk("[WARNING: explicit IRQ value %d ignored: using PSA value instead]", dev->irq);
-#if    defined(IRQ_SET_WORKS)
-Leave this out until I can get it to work -- BJ.
-               if (wavelan_unmap_irq(dev->irq, &psa.psa_int_req_no) == -1)
-               {
-                       printk(" could not wavelan_unmap_irq(%d, ..) -- ignored.\n", dev->irq);
-                       dev->irq = 0;
-               }
-               else
-               {
-                       psa_write(ioaddr, HACR_DEFAULT, (char *)&psa.psa_int_req_no - (char *)&psa, (unsigned char *)&psa.psa_int_req_no, sizeof(psa.psa_int_req_no));
-                       wavelan_reset(ioaddr);
-               }
-#endif /* defined(IRQ_SET_WORKS) */
-       }
-
-       if ((irq = wavelan_map_irq(psa.psa_int_req_no)) == -1)
-       {
-               printk(" could not wavelan_map_irq(%d).\n", psa.psa_int_req_no);
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_probe1(): EAGAIN\n", dev->name);
-               return EAGAIN;
-       }
-
-       dev->irq = irq;
-
-       request_region(ioaddr, sizeof(ha_t), "wavelan");
-       dev->base_addr = ioaddr;
-
-       /*
-        * The third numeric argument to LILO's
-        * `ether=' control line arrives here as `dev->mem_start'.
-        *
-        * If bit 16 of dev->mem_start is non-zero we enable
-        * full promiscuity.
-        *
-        * If either of the least significant two bytes of
-        * dev->mem_start are non-zero we use them instead
-        * of the PSA NWID.
-        */
-       enable_full_promiscuous = (dev->mem_start & ENABLE_FULL_PROMISCUOUS) == ENABLE_FULL_PROMISCUOUS;
-       dev->mem_start &= ~ENABLE_FULL_PROMISCUOUS;
-
-       if (dev->mem_start != 0)
-       {
-               psa.psa_nwid[0] = (dev->mem_start >> 8) & 0xFF;
-               psa.psa_nwid[1] = (dev->mem_start >> 0) & 0xFF;
-       }
-
-       dev->mem_start = 0x0000;
-       dev->mem_end = 0x0000;
-       dev->if_port = 0;
-
-       memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE);
-
-       for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-               printk("%s%02x", (i == 0) ? " " : ":", dev->dev_addr[i]);
+  net_local *  lp = (net_local *)dev->priv;
+  unsigned int i;
+  u_short      p;
+
+  printk(KERN_DEBUG "##### wavelan i82586 command unit status: #####\n");
+
+  printk(KERN_DEBUG);
+  for(i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++)
+    {
+      wv_cu_show_one(dev, lp, i, p);
+
+      p += TXBLOCKZ;
+      if(p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+       p -= NTXBLOCKS * TXBLOCKZ;
+    }
+  printk("\n");
+}
+#endif /* DEBUG_I82586_SHOW */
 
-       printk(", IRQ %d", dev->irq);
-       if (enable_full_promiscuous)
-               printk(", promisc");
-       printk(", nwid 0x%02x%02x", psa.psa_nwid[0], psa.psa_nwid[1]);
+#ifdef DEBUG_DEVICE_SHOW
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver.
+ */
+static void
+wv_dev_show(device *   dev)
+{
+  printk(KERN_DEBUG "dev:");
+  printk(" start=%d,", dev->start);
+  printk(" tbusy=%ld,", dev->tbusy);
+  printk(" interrupt=%d,", dev->interrupt);
+  printk(" trans_start=%ld,", dev->trans_start);
+  printk(" flags=0x%x,", dev->flags);
+  printk("\n");
+} /* wv_dev_show */
+
+/*------------------------------------------------------------------*/
+/*
+ * Print the formatted status of the WaveLAN PCMCIA device driver's
+ * private information.
+ */
+static void
+wv_local_show(device * dev)
+{
+  net_local *lp;
+
+  lp = (net_local *)dev->priv;
+
+  printk(KERN_DEBUG "local:");
+  printk(" tx_n_in_use=%d,", lp->tx_n_in_use);
+  printk(" hacr=0x%x,", lp->hacr);
+  printk(" rx_head=0x%x,", lp->rx_head);
+  printk(" rx_last=0x%x,", lp->rx_last);
+  printk(" tx_first_free=0x%x,", lp->tx_first_free);
+  printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use);
+  printk("\n");
+} /* wv_local_show */
+#endif /* DEBUG_DEVICE_SHOW */
+
+#if defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO)
+/*------------------------------------------------------------------*/
+/*
+ * Dump packet header (and content if necessary) on the screen
+ */
+static inline void
+wv_packet_info(u_char *                p,              /* Packet to dump */
+              int              length,         /* Length of the packet */
+              char *           msg1,           /* Name of the device */
+              char *           msg2)           /* Name of the function */
+{
+#ifndef DEBUG_PACKET_DUMP
+  printk(KERN_DEBUG "%s: %s(): dest %02X:%02X:%02X:%02X:%02X:%02X, length %d\n",
+        msg1, msg2, p[0], p[1], p[2], p[3], p[4], p[5], length);
+  printk(KERN_DEBUG "%s: %s(): src %02X:%02X:%02X:%02X:%02X:%02X, type 0x%02X%02X\n",
+        msg1, msg2, p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13]);
+
+#else  /* DEBUG_PACKET_DUMP */
+  int          i;
+  int          maxi;
+
+  printk(KERN_DEBUG "%s: %s(): len=%d, data=\"", msg1, msg2, length);
+
+  if((maxi = length) > DEBUG_PACKET_DUMP)
+    maxi = DEBUG_PACKET_DUMP;
+  for(i = 0; i < maxi; i++)
+    if(p[i] >= ' ' && p[i] <= '~')
+      printk(" %c", p[i]);
+    else
+      printk("%02X", p[i]);
+  if(maxi < length)
+    printk("..");
+  printk("\"\n");
+  printk(KERN_DEBUG "\n");
+#endif /* DEBUG_PACKET_DUMP */
+}
+#endif /* defined(DEBUG_RX_INFO) || defined(DEBUG_TX_INFO) */
 
-       printk(", PC");
-       switch (psa.psa_comp_number)
+/*------------------------------------------------------------------*/
+/*
+ * This is the information which is displayed by the driver at startup
+ * There  is a lot of flag to configure it at your will...
+ */
+static inline void
+wv_init_info(device *  dev)
+{
+  short                ioaddr = dev->base_addr;
+  net_local *  lp = (net_local *)dev->priv;
+  psa_t                psa;
+  int          i;
+
+  /* Read the parameter storage area */
+  psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+#ifdef DEBUG_PSA_SHOW
+  wv_psa_show(&psa);
+#endif
+#ifdef DEBUG_MMC_SHOW
+  wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82586_SHOW
+  wv_cu_show(dev);
+#endif
+
+#ifdef DEBUG_BASIC_SHOW
+  /* Now, let's go for the basic stuff */
+  printk(KERN_NOTICE "%s: WaveLAN at %#x,", dev->name, ioaddr);
+  for(i = 0; i < WAVELAN_ADDR_SIZE; i++)
+    printk("%s%02X", (i == 0) ? " " : ":", dev->dev_addr[i]);
+  printk(", IRQ %d", dev->irq);
+
+  /* Print current network id */
+  if(psa.psa_nwid_select)
+    printk(", nwid 0x%02X-%02X", psa.psa_nwid[0], psa.psa_nwid[1]);
+  else
+    printk(", nwid off");
+
+  /* If 2.00 card */
+  if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+    {
+      unsigned short   freq;
+
+      /* Ask the EEprom to read the frequency from the first area */
+      fee_read(ioaddr, 0x00 /* 1st area - frequency... */,
+              &freq, 1);
+
+      /* Print frequency */
+      printk(", 2.00, %ld", (freq >> 6) + 2400L);
+
+      /* Hack !!! */
+      if(freq & 0x20)
+       printk(".5");
+    }
+  else
+    {
+      printk(", PC");
+      switch(psa.psa_comp_number)
        {
        case PSA_COMP_PC_AT_915:
        case PSA_COMP_PC_AT_2400:
-               printk("-AT");
-               break;
-
+         printk("-AT");
+         break;
        case PSA_COMP_PC_MC_915:
        case PSA_COMP_PC_MC_2400:
-               printk("-MC");
-               break;
-
+         printk("-MC");
+         break;
        case PSA_COMP_PCMCIA_915:
-               printk("MCIA");
-               break;
-
+         printk("MCIA");
+         break;
        default:
-               printk("???");
-               break;
+         printk("???");
        }
-
-       printk(", ");
-       switch (psa.psa_subband)
+      printk(", ");
+      switch (psa.psa_subband)
        {
        case PSA_SUBBAND_915:
-               printk("915");
-               break;
-
+         printk("915");
+         break;
        case PSA_SUBBAND_2425:
-               printk("2425");
-               break;
-
+         printk("2425");
+         break;
        case PSA_SUBBAND_2460:
-               printk("2460");
-               break;
-
+         printk("2460");
+         break;
        case PSA_SUBBAND_2484:
-               printk("2484");
-               break;
-
+         printk("2484");
+         break;
        case PSA_SUBBAND_2430_5:
-               printk("2430.5");
-               break;
-
+         printk("2430.5");
+         break;
        default:
-               printk("???");
-               break;
+         printk("???");
        }
-       printk(" MHz");
+    }
 
-       printk("\n");
+  printk(" MHz\n");
+#endif /* DEBUG_BASIC_SHOW */
 
-       if (wavelan_debug > 0)
-               printk(version);
+#ifdef DEBUG_VERSION_SHOW
+  /* Print version information */
+  printk(KERN_NOTICE "%s", version);
+#endif
+} /* wv_init_info */
 
-       dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL);
-       if (dev->priv == NULL)
-               return -ENOMEM;
-       memset(dev->priv, 0x00, sizeof(net_local));
-       lp = (net_local *)dev->priv;
-
-       if (first_wavelan == (net_local *)0)
-       {
-               first_wavelan = lp;
-               lp->prev = lp;
-               lp->next = lp;
-       }
-       else
-       {
-               lp->prev = first_wavelan->prev;
-               lp->next = first_wavelan;
-               first_wavelan->prev->next = lp;
-               first_wavelan->prev = lp;
-       }
-       lp->dev = dev;
+/********************* IOCTL, STATS & RECONFIG *********************/
+/*
+ * We found here routines that are called by Linux on differents
+ * occasions after the configuration and not for transmitting data
+ * These may be called when the user use ifconfig, /proc/net/dev
+ * or wireless extensions
+ */
 
-       lp->hacr = HACR_DEFAULT;
+/*------------------------------------------------------------------*/
+/*
+ * Get the current ethernet statistics. This may be called with the
+ * card open or closed.
+ * Used when the user read /proc/net/dev
+ */
+static en_stats        *
+wavelan_get_stats(device *     dev)
+{
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <>wavelan_get_stats()\n", dev->name);
+#endif
 
-       lp->full_promiscuous = enable_full_promiscuous;
-       lp->nwid[0] = psa.psa_nwid[0];
-       lp->nwid[1] = psa.psa_nwid[1];
+  return(&((net_local *) dev->priv)->stats);
+}
 
-       lp->watchdog.function = wavelan_watchdog;
-       lp->watchdog.data = (unsigned long)dev;
+/*------------------------------------------------------------------*/
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1     Promiscuous mode, receive all packets
+ * num_addrs == 0      Normal mode, clear multicast list
+ * num_addrs > 0       Multicast mode, receive normal and MC packets,
+ *                     and do best-effort filtering.
+ */
+static void
+wavelan_set_multicast_list(device *    dev)
+{
+  net_local *  lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_set_multicast_list()\n", dev->name);
+#endif
+
+#ifdef DEBUG_IOCTL_INFO
+  printk(KERN_DEBUG "%s: wavelan_set_multicast_list(): setting Rx mode %02X to %d addresses.\n",
+        dev->name, dev->flags, dev->mc_count);
+#endif
+
+  /* If we ask for promiscuous mode,
+   * or all multicast addresses (we don't have that !)
+   * or too much multicast addresses for the hardware filter */
+  if((dev->flags & IFF_PROMISC) ||
+     (dev->flags & IFF_ALLMULTI) ||
+     (dev->mc_count > I82586_MAX_MULTICAST_ADDRESSES))
+    {
+      /*
+       * Enable promiscuous mode: receive all packets.
+       */
+      if(!lp->promiscuous)
+       {
+         lp->promiscuous = 1;
+         lp->mc_count = 0;
 
-       dev->open = wavelan_open;
-       dev->stop = wavelan_close;
-       dev->hard_start_xmit = wavelan_send_packet;
-       dev->get_stats = wavelan_get_stats;
-       dev->set_multicast_list = &wavelan_set_multicast_list;
+         wv_82586_reconfig(dev);
 
+         /* Tell the kernel that we are doing a really bad job... */
+         dev->flags |= IFF_PROMISC;
+       }
+    }
+  else
+    /* If there is some multicast addresses to send */
+    if(dev->mc_list != (struct dev_mc_list *) NULL)
+      {
        /*
-        * Fill in the fields of the device structure
-        * with ethernet-generic values.
+        * Disable promiscuous mode, but receive all packets
+        * in multicast list
         */
-       ether_setup(dev);
-
-       dev->flags &= ~IFF_MULTICAST;           /* Not yet supported */
-       
-       dev->mtu = WAVELAN_MTU;
-
-       if (wavelan_debug > 0)
-               printk("%s: <-wavelan_probe1(): 0\n", dev->name);
-
-       return 0;
+#ifdef MULTICAST_AVOID
+       if(lp->promiscuous ||
+          (dev->mc_count != lp->mc_count))
+#endif
+         {
+           lp->promiscuous = 0;
+           lp->mc_count = dev->mc_count;
+
+           wv_82586_reconfig(dev);
+         }
+      }
+    else
+      {
+       /*
+        * Switch to normal mode: disable promiscuous mode and 
+        * clear the multicast list.
+        */
+       if(lp->promiscuous || lp->mc_count == 0)
+         {
+           lp->promiscuous = 0;
+           lp->mc_count = 0;
+
+           wv_82586_reconfig(dev);
+         }
+      }
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_set_multicast_list()\n", dev->name);
+#endif
 }
 
+/*------------------------------------------------------------------*/
 /*
- * Construct the fd and rbd structures.
- * Start the receive unit.
+ * This function doesn't exist...
  */
-static
-void
-wavelan_ru_start(device *dev)
+static int
+wavelan_set_mac_address(device *       dev,
+                       void *          addr)
 {
-       unsigned short  ioaddr;
-       net_local       *lp;
-       unsigned short  scb_cs;
-       fd_t            fd;
-       rbd_t           rbd;
-       unsigned short  rx;
-       unsigned short  rx_next;
-       int             i;
+  struct sockaddr *    mac = addr;
 
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
+  /* Copy the address */
+  memcpy(dev->dev_addr, mac->sa_data, WAVELAN_ADDR_SIZE);
 
-       obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs));
-       if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY)
-               return;
+  /* Reconfig the beast */
+  wv_82586_reconfig(dev);
 
-       lp->rx_head = OFFSET_RU;
+  return 0;
+}
+
+#ifdef WIRELESS_EXT    /* If wireless extension exist in the kernel */
 
-       for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next)
+/*------------------------------------------------------------------*/
+/*
+ * Frequency setting (for hardware able of it)
+ * It's a bit complicated and you don't really want to look into it...
+ * (called in wavelan_ioctl)
+ */
+static inline int
+wv_set_frequency(u_short       ioaddr, /* i/o port of the card */
+                iw_freq *      frequency)
+{
+  const int    BAND_NUM = 10;  /* Number of bands */
+  long         freq = 0L;      /* offset to 2.4 GHz in .5 MHz */
+#ifdef DEBUG_IOCTL_INFO
+  int          i;
+#endif
+
+  /* Setting by frequency */
+  /* Theoritically, you may set any frequency between
+   * the two limits with a 0.5 MHz precision. In practice,
+   * I don't want you to have trouble with local
+   * regulations... */
+  if((frequency->e == 1) &&
+     (frequency->m >= (int) 2.412e8) && (frequency->m <= (int) 2.487e8))
+    {
+      freq = ((frequency->m / 10000) - 24000L) / 5;
+    }
+
+  /* Setting by channel (same as wfreqsel) */
+  /* Warning : each channel is 11MHz wide, so some of the channels
+   * will interfere... */
+  if((frequency->e == 0) &&
+     (frequency->m >= 0) && (frequency->m < BAND_NUM))
+    {
+      /* frequency in 1/4 of MHz (as read in the offset register) */
+      short    bands[] = { 0x30, 0x58, 0x64, 0x7A, 0x80, 0xA8, 0xD0, 0xF0, 0xF8, 0x150 };
+
+      /* Get frequency offset */
+      freq = bands[frequency->m] >> 1;
+    }
+
+  /* Verify if the frequency is allowed */
+  if(freq != 0L)
+    {
+      u_short  table[10];      /* Authorized frequency table */
+
+      /* Read the frequency table */
+      fee_read(ioaddr, 0x71 /* frequency table */,
+              table, 10);
+
+#ifdef DEBUG_IOCTL_INFO
+      printk(KERN_DEBUG "Frequency table :");
+      for(i = 0; i < 10; i++)
+       {
+         printk(" %04X",
+                table[i]);
+       }
+      printk("\n");
+#endif
+
+      /* Look in the table if the frequency is allowed */
+      if(!(table[9 - ((freq - 24) / 16)] &
+          (1 << ((freq - 24) % 16))))
+       return -EINVAL;         /* not allowed */
+    }
+  else
+    return -EINVAL;
+
+  /* If we get a usable frequency */
+  if(freq != 0L)
+    {
+      unsigned short   area[16];
+      unsigned short   dac[2];
+      unsigned short   area_verify[16];
+      unsigned short   dac_verify[2];
+      /* Corresponding gain (in the power adjust value table)
+       * see AT&T Wavelan Data Manual, REF 407-024689/E, page 3-8
+       * & WCIN062D.DOC, page 6.2.9 */
+      unsigned short   power_limit[] = { 40, 80, 120, 160, 0 };
+      int              power_band = 0;         /* Selected band */
+      unsigned short   power_adjust;           /* Correct value */
+
+      /* Search for the gain */
+      power_band = 0;
+      while((freq > power_limit[power_band]) &&
+           (power_limit[++power_band] != 0))
+       ;
+
+      /* Read the first area */
+      fee_read(ioaddr, 0x00,
+              area, 16);
+
+      /* Read the DAC */
+      fee_read(ioaddr, 0x60,
+              dac, 2);
+
+      /* Read the new power adjust value */
+      fee_read(ioaddr, 0x6B - (power_band >> 1),
+              &power_adjust, 1);
+      if(power_band & 0x1)
+       power_adjust >>= 8;
+      else
+       power_adjust &= 0xFF;
+
+#ifdef DEBUG_IOCTL_INFO
+      printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
+      for(i = 0; i < 16; i++)
        {
-               rx_next = (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ;
-
-               fd.fd_status = 0;
-               fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0;
-               fd.fd_link_offset = rx_next;
-               fd.fd_rbd_offset = rx + sizeof(fd);
-               obram_write(ioaddr, rx, (unsigned char *)&fd, sizeof(fd));
-
-               rbd.rbd_status = 0;
-               rbd.rbd_next_rbd_offset = I82586NULL;
-               rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd);
-               rbd.rbd_bufh = 0;
-               rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ);
-               obram_write(ioaddr, rx + sizeof(fd), (unsigned char *)&rbd, sizeof(rbd));
-
-               lp->rx_last = rx;
+         printk(" %04X",
+                area[i]);
        }
+      printk("\n");
 
-       obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset), (unsigned char *)&lp->rx_head, sizeof(lp->rx_head));
+      printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
+            dac[0], dac[1]);
+#endif
 
-       scb_cs = SCB_CMD_RUC_GO;
-       obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+      /* Frequency offset (for info only...) */
+      area[0] = ((freq << 5) & 0xFFE0) | (area[0] & 0x1F);
 
-       set_chan_attn(ioaddr, lp->hacr);
+      /* Receiver Principle main divider coefficient */
+      area[3] = (freq >> 1) + 2400L - 352L;
+      area[2] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
 
-       for (i = 1000; i > 0; i--)
-       {
-               obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
-               if (scb_cs == 0)
-                       break;
+      /* Transmitter Main divider coefficient */
+      area[13] = (freq >> 1) + 2400L;
+      area[12] = ((freq & 0x1) << 4) | (area[2] & 0xFFEF);
 
-               udelay(1000);
-       }
+      /* Others part of the area are flags, bit streams or unused... */
 
-       if (i <= 0)
-               printk("%s: wavelan_ru_start(): board not accepting command.\n", dev->name);
-}
+      /* Set the value in the DAC */
+      dac[1] = ((power_adjust >> 1) & 0x7F) | (dac[1] & 0xFF80);
+      dac[0] = ((power_adjust & 0x1) << 4) | (dac[0] & 0xFFEF);
 
-/*
- * Initialise the transmit blocks.
- * Start the command unit executing the NOP
- * self-loop of the first transmit block.
- */
-static
-void
-wavelan_cu_start(device *dev)
-{
-       unsigned short  ioaddr;
-       net_local       *lp;
-       int             i;
-       unsigned short  txblock;
-       unsigned short  first_nop;
-       unsigned short  scb_cs;
+      /* Write the first area */
+      fee_write(ioaddr, 0x00,
+               area, 16);
 
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
+      /* Write the DAC */
+      fee_write(ioaddr, 0x60,
+               dac, 2);
 
-       lp->tx_first_free = OFFSET_CU;
-       lp->tx_first_in_use = I82586NULL;
+      /* We now should verify here that the EEprom writting was ok */
+
+      /* ReRead the first area */
+      fee_read(ioaddr, 0x00,
+              area_verify, 16);
 
-       for
-       (
-               i = 0, txblock = OFFSET_CU;
-               i < NTXBLOCKS;
-               i++, txblock += TXBLOCKZ
-       )
+      /* ReRead the DAC */
+      fee_read(ioaddr, 0x60,
+              dac_verify, 2);
+
+      /* Compare */
+      if(memcmp(area, area_verify, 16 * 2) ||
+        memcmp(dac, dac_verify, 2 * 2))
        {
-               ac_tx_t         tx;
-               ac_nop_t        nop;
-               tbd_t           tbd;
-               unsigned short  tx_addr;
-               unsigned short  nop_addr;
-               unsigned short  tbd_addr;
-               unsigned short  buf_addr;
-
-               tx_addr = txblock;
-               nop_addr = tx_addr + sizeof(tx);
-               tbd_addr = nop_addr + sizeof(nop);
-               buf_addr = tbd_addr + sizeof(tbd);
-
-               tx.tx_h.ac_status = 0;
-               tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I;
-               tx.tx_h.ac_link = nop_addr;
-               tx.tx_tbd_offset = tbd_addr;
-               obram_write(ioaddr, tx_addr, (unsigned char *)&tx, sizeof(tx));
-
-               nop.nop_h.ac_status = 0;
-               nop.nop_h.ac_command = acmd_nop;
-               nop.nop_h.ac_link = nop_addr;
-               obram_write(ioaddr, nop_addr, (unsigned char *)&nop, sizeof(nop));
-
-               tbd.tbd_status = TBD_STATUS_EOF;
-               tbd.tbd_next_bd_offset = I82586NULL;
-               tbd.tbd_bufl = buf_addr;
-               tbd.tbd_bufh = 0;
-               obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
+#ifdef DEBUG_IOCTL_ERROR
+         printk(KERN_INFO "Wavelan: wv_set_frequency : unable to write new frequency to EEprom (??)\n");
+#endif
+         return -EOPNOTSUPP;
        }
 
-       first_nop = OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t);
-       obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset), (unsigned char *)&first_nop, sizeof(first_nop));
+      /* We must download the frequency parameters to the
+       * synthetisers (from the EEprom - area 1)
+       * Note : as the EEprom is auto decremented, we set the end
+       * if the area... */
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x0F);
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+             MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
 
-       scb_cs = SCB_CMD_CUC_GO;
-       obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+      /* Wait until the download is finished */
+      fee_wait(ioaddr, 100, 100);
 
-       set_chan_attn(ioaddr, lp->hacr);
+      /* We must now download the power adjust value (gain) to
+       * the synthetisers (from the EEprom - area 7 - DAC) */
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_addr), 0x61);
+      mmc_out(ioaddr, mmwoff(0, mmw_fee_ctrl),
+             MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD);
 
-       for (i = 1000; i > 0; i--)
-       {
-               obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
-               if (scb_cs == 0)
-                       break;
+      /* Wait until the download is finished */
+      fee_wait(ioaddr, 100, 100);
+
+#ifdef DEBUG_IOCTL_INFO
+      /* Verification of what we have done... */
 
-               udelay(1000);
+      printk(KERN_DEBUG "Wavelan EEprom Area 1 :");
+      for(i = 0; i < 16; i++)
+       {
+         printk(" %04X",
+                area_verify[i]);
        }
+      printk("\n");
 
-       if (i <= 0)
-               printk("%s: wavelan_cu_start(): board not accepting command.\n", dev->name);
+      printk(KERN_DEBUG "Wavelan EEprom DAC : %04X %04X\n",
+            dac_verify[0], dac_verify[1]);
+#endif
 
-       lp->tx_n_in_use = 0;
-       dev->tbusy = 0;
+      return 0;
+    }
+  else
+    return -EINVAL;            /* Bah, never get there... */
 }
 
-static
-int
-wavelan_open(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * Give the list of available frequencies
+ */
+static inline int
+wv_frequency_list(u_short      ioaddr, /* i/o port of the card */
+                 iw_freq *     list,   /* List of frequency to fill */
+                 int           max)    /* Maximum number of frequencies */
 {
-       unsigned short  ioaddr;
-       net_local       *lp;
-       unsigned long   x;
-       int             r;
+  u_short      table[10];      /* Authorized frequency table */
+  long         freq = 0L;      /* offset to 2.4 GHz in .5 MHz + 12 MHz */
+  int          i;              /* index in the table */
+
+  /* Read the frequency table */
+  fee_read(ioaddr, 0x71 /* frequency table */,
+          table, 10);
+
+  /* Look all frequencies */
+  i = 0;
+  for(freq = 0; freq < 150; freq++)
+    /* Look in the table if the frequency is allowed */
+    if(table[9 - (freq / 16)] & (1 << (freq % 16)))
+      {
+       /* put in the list */
+       list[i].m = (((freq + 24) * 5) + 24000L) * 10000;
+       list[i++].e = 1;
+
+       /* Check number */
+       if(i >= max)
+         return(i);
+      }
+
+  return(i);
+}
 
-       if (wavelan_debug > 0)
-               printk("%s: ->wavelan_open(dev=0x%x)\n", dev->name, (unsigned int)dev);
+#ifdef WIRELESS_SPY
+/*------------------------------------------------------------------*/
+/*
+ * Gather wireless spy statistics : for each packet, compare the source
+ * address with out list, and if match, get the stats...
+ * Sorry, but this function really need wireless extensions...
+ */
+static inline void
+wl_spy_gather(device * dev,
+             u_char *  mac,            /* MAC address */
+             u_char *  stats)          /* Statistics to gather */
+{
+  net_local *  lp = (net_local *) dev->priv;
+  int          i;
+
+  /* Look all addresses */
+  for(i = 0; i < lp->spy_number; i++)
+    /* If match */
+    if(!memcmp(mac, lp->spy_address[i], WAVELAN_ADDR_SIZE))
+      {
+       /* Update statistics */
+       lp->spy_stat[i].qual = stats[2] & MMR_SGNL_QUAL;
+       lp->spy_stat[i].level = stats[0] & MMR_SIGNAL_LVL;
+       lp->spy_stat[i].noise = stats[1] & MMR_SILENCE_LVL;
+       lp->spy_stat[i].updated = 0x7;
+      }
+}
+#endif /* WIRELESS_SPY */
 
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
+#ifdef HISTOGRAM
+/*------------------------------------------------------------------*/
+/*
+ * This function calculate an histogram on the signal level.
+ * As the noise is quite constant, it's like doing it on the SNR.
+ * We have defined a set of interval (lp->his_range), and each time
+ * the level goes in that interval, we increment the count (lp->his_sum).
+ * With this histogram you may detect if one wavelan is really weak,
+ * or you may also calculate the mean and standard deviation of the level...
+ */
+static inline void
+wl_his_gather(device * dev,
+             u_char *  stats)          /* Statistics to gather */
+{
+  net_local *  lp = (net_local *) dev->priv;
+  u_char       level = stats[0] & MMR_SIGNAL_LVL;
+  int          i;
+
+  /* Find the correct interval */
+  i = 0;
+  while((i < (lp->his_number - 1)) && (level >= lp->his_range[i++]))
+    ;
 
-       if (dev->irq == 0)
+  /* Increment interval counter */
+  (lp->his_sum[i])++;
+}
+#endif /* HISTOGRAM */
+
+/*------------------------------------------------------------------*/
+/*
+ * Perform ioctl : config & info stuff
+ * This is here that are treated the wireless extensions (iwconfig)
+ */
+static int
+wavelan_ioctl(struct device *  dev,    /* Device on wich the ioctl apply */
+             struct ifreq *    rq,     /* Data passed */
+             int               cmd)    /* Ioctl number */
+{
+  unsigned short       ioaddr = dev->base_addr;
+  net_local *          lp = (net_local *)dev->priv;    /* lp is not unused */
+  struct iwreq *       wrq = (struct iwreq *) rq;
+  psa_t                        psa;
+  mm_t                 m;
+  unsigned long                x;
+  int                  ret = 0;
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_ioctl(cmd=0x%X)\n", dev->name, cmd);
+#endif
+
+  /* Disable interrupts & save flags */
+  x = wv_splhi();
+
+  /* Look what is the request */
+  switch(cmd)
+    {
+      /* --------------- WIRELESS EXTENSIONS --------------- */
+
+    case SIOCGIWNAME:
+      strcpy(wrq->u.name, "Wavelan");
+      break;
+
+    case SIOCSIWNWID:
+      /* Set NWID in wavelan */
+      if(wrq->u.nwid.on)
        {
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_open(): -ENXIO\n", dev->name);
-               return -ENXIO;
+         /* Set NWID in psa */
+         psa.psa_nwid[0] = (wrq->u.nwid.nwid & 0xFF00) >> 8;
+         psa.psa_nwid[1] = wrq->u.nwid.nwid & 0xFF;
+         psa.psa_nwid_select = 0x01;
+         psa_write(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa,
+                   (unsigned char *)psa.psa_nwid, 3);
+
+         /* Set NWID in mmc */
+         m.w.mmw_netw_id_l = wrq->u.nwid.nwid & 0xFF;
+         m.w.mmw_netw_id_h = (wrq->u.nwid.nwid & 0xFF00) >> 8;
+         mmc_write(ioaddr, (char *)&m.w.mmw_netw_id_l - (char *)&m,
+                   (unsigned char *)&m.w.mmw_netw_id_l, 2);
+         m.w.mmw_loopt_sel = 0x00;
+         mmc_write(ioaddr, (char *)&m.w.mmw_loopt_sel - (char *)&m,
+                   (unsigned char *)&m.w.mmw_loopt_sel, 1);
        }
-
-       if
-       (
-               irq2dev_map[dev->irq] != (device *)0
-               /* This is always true, but avoid the false IRQ. */
-               ||
-               (irq2dev_map[dev->irq] = dev) == (device *)0
-               ||
-               request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", NULL) != 0
-       )
+      else
        {
-               irq2dev_map[dev->irq] = (device *)0;
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_open(): -EAGAIN\n", dev->name);
-               return -EAGAIN;
+         /* Disable nwid in the psa */
+         psa.psa_nwid_select = 0x00;
+         psa_write(ioaddr, lp->hacr,
+                   (char *)&psa.psa_nwid_select - (char *)&psa,
+                   (unsigned char *)&psa.psa_nwid_select, 1);
+
+         /* Disable nwid in the mmc (no check) */
+         m.w.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
+         mmc_write(ioaddr, (char *)&m.w.mmw_loopt_sel - (char *)&m,
+                   (unsigned char *)&m.w.mmw_loopt_sel, 1);
        }
+      break;
+
+    case SIOCGIWNWID:
+      /* Read the NWID */
+      psa_read(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa,
+              (unsigned char *)psa.psa_nwid, 3);
+      wrq->u.nwid.nwid = (psa.psa_nwid[0] << 8) + psa.psa_nwid[1];
+      wrq->u.nwid.on = psa.psa_nwid_select;
+      break;
+
+    case SIOCSIWFREQ:
+      /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
+      if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+          (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+       ret = wv_set_frequency(ioaddr, &(wrq->u.freq));
+      else
+       ret = -EOPNOTSUPP;
+      break;
+
+    case SIOCGIWFREQ:
+      /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
+       * (does it work for everybody ??? - especially old cards...) */
+      if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+          (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+       {
+         unsigned short        freq;
 
-       x = wavelan_splhi();
-       if ((r = wavelan_hardware_reset(dev)) != -1)
+         /* Ask the EEprom to read the frequency from the first area */
+         fee_read(ioaddr, 0x00 /* 1st area - frequency... */,
+                  &freq, 1);
+         wrq->u.freq.m = ((freq >> 5) * 5 + 24000L) * 10000;
+         wrq->u.freq.e = 1;
+       }
+      else
        {
-               dev->interrupt = 0;
-               dev->start = 1;
+         int   bands[] = { 915e6, 2.425e8, 2.46e8, 2.484e8, 2.4305e8 };
+
+         psa_read(ioaddr, lp->hacr, (char *)&psa.psa_subband - (char *)&psa,
+                  (unsigned char *)&psa.psa_subband, 1);
+
+         if(psa.psa_subband <= 4)
+           {
+             wrq->u.freq.m = bands[psa.psa_subband];
+             wrq->u.freq.e = (psa.psa_subband != 0);
+           }
+         else
+           ret = -EOPNOTSUPP;
        }
-       wavelan_splx(x);
-
-       if (r == -1)
+      break;
+
+    case SIOCSIWSENS:
+      /* Set the level threshold */
+      if(!suser())
+       return -EPERM;
+      psa.psa_thr_pre_set = wrq->u.sensitivity & 0x3F;
+      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+              (unsigned char *) &psa.psa_thr_pre_set, 1);
+      mmc_out(ioaddr, mmwoff(0, mmw_thr_pre_set), psa.psa_thr_pre_set);
+      break;
+
+    case SIOCGIWSENS:
+      /* Read the level threshold */
+      psa_read(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+              (unsigned char *) &psa.psa_thr_pre_set, 1);
+      wrq->u.sensitivity = psa.psa_thr_pre_set & 0x3F;
+      break;
+
+    case SIOCGIWRANGE:
+      /* Basic checking... */
+      if(wrq->u.data.pointer != (caddr_t) 0)
        {
-               free_irq(dev->irq, NULL);
-               irq2dev_map[dev->irq] = (device *)0;
-               if (wavelan_debug > 0)
-                       printk("%s: <-wavelan_open(): -EAGAIN(2)\n", dev->name);
-               return -EAGAIN;
+         struct iw_range       range;
+
+         /* Verify the user buffer */
+         ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer,
+                           sizeof(struct iw_range));
+         if(ret)
+           break;
+
+         /* Set the length (useless : its constant...) */
+         wrq->u.data.length = sizeof(struct iw_range);
+
+         /* Set information in the range struct */
+         range.throughput = 1.6 * 1024 * 1024; /* don't argue on this ! */
+         range.min_nwid = 0x0000;
+         range.max_nwid = 0xFFFF;
+
+         /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable) */
+         if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+              (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+           {
+             range.num_channels = 10;
+             range.num_frequency = wv_frequency_list(ioaddr, range.freq,
+                                                     IW_MAX_FREQUENCIES);
+           }
+         else
+           range.num_channels = range.num_frequency = 0;
+
+         range.sensitivity = 0x3F;
+         range.max_qual.qual = MMR_SGNL_QUAL;
+         range.max_qual.level = MMR_SIGNAL_LVL;
+         range.max_qual.noise = MMR_SILENCE_LVL;
+
+         /* Copy structure to the user buffer */
+         copy_to_user(wrq->u.data.pointer, &range,
+                      sizeof(struct iw_range));
        }
+      break;
 
-       MOD_INC_USE_COUNT;
-
-       if (wavelan_debug > 0)
-               printk("%s: <-wavelan_open(): 0\n", dev->name);
-
-       return 0;
-}
-
-static
-void
-hardware_send_packet(device *dev, void *buf, short length)
-{
-       unsigned short  ioaddr;
-       net_local       *lp;
-       unsigned short  txblock;
-       unsigned short  txpred;
-       unsigned short  tx_addr;
-       unsigned short  nop_addr;
-       unsigned short  tbd_addr;
-       unsigned short  buf_addr;
-       ac_tx_t         tx;
-       ac_nop_t        nop;
-       tbd_t           tbd;
-       unsigned long   x;
-
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
-
-       x = wavelan_splhi();
-
-       txblock = lp->tx_first_free;
-       txpred = txblock - TXBLOCKZ;
-       if (txpred < OFFSET_CU)
-               txpred += NTXBLOCKS * TXBLOCKZ;
-       lp->tx_first_free += TXBLOCKZ;
-       if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
-               lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
-
-/*
-if (lp->tx_n_in_use > 0)
-       printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
-*/
-
-       lp->tx_n_in_use++;
-
-       tx_addr = txblock;
-       nop_addr = tx_addr + sizeof(tx);
-       tbd_addr = nop_addr + sizeof(nop);
-       buf_addr = tbd_addr + sizeof(tbd);
-
-       /*
-        * Transmit command.
-        */
-       tx.tx_h.ac_status = 0;
-       obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), (unsigned char *)&tx.tx_h.ac_status, sizeof(tx.tx_h.ac_status));
-
-       /*
-        * NOP command.
-        */
-       nop.nop_h.ac_status = 0;
-       obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), (unsigned char *)&nop.nop_h.ac_status, sizeof(nop.nop_h.ac_status));
-       nop.nop_h.ac_link = nop_addr;
-       obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), (unsigned char *)&nop.nop_h.ac_link, sizeof(nop.nop_h.ac_link));
-
-       /*
-        * Transmit buffer descriptor. 
-        */
-       tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & length);
-       tbd.tbd_next_bd_offset = I82586NULL;
-       tbd.tbd_bufl = buf_addr;
-       tbd.tbd_bufh = 0;
-       obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
-
-       /*
-        * Data.
-        */
-       obram_write(ioaddr, buf_addr, buf, length);
-
-       /*
-        * Overwrite the predecessor NOP link
-        * so that it points to this txblock.
-        */
-       nop_addr = txpred + sizeof(tx);
-       nop.nop_h.ac_status = 0;
-       obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), (unsigned char *)&nop.nop_h.ac_status, sizeof(nop.nop_h.ac_status));
-       nop.nop_h.ac_link = txblock;
-       obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), (unsigned char *)&nop.nop_h.ac_link, sizeof(nop.nop_h.ac_link));
-
-       if (lp->tx_first_in_use == I82586NULL)
-               lp->tx_first_in_use = txblock;
-
-       if (lp->tx_n_in_use < NTXBLOCKS - 1)
-               dev->tbusy = 0;
-
-       dev->trans_start = jiffies;
+    case SIOCGIWPRIV:
+      /* Basic checking... */
+      if(wrq->u.data.pointer != (caddr_t) 0)
+       {
+         struct iw_priv_args   priv[] =
+         {     /* cmd,         set_args,       get_args,       name */
+           { SIOCSIPQTHR, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, 0, "setqualthr" },
+           { SIOCGIPQTHR, 0, IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "getqualthr" },
+
+           { SIOCSIPHISTO, IW_PRIV_TYPE_BYTE | 16,     0, "sethisto" },
+           { SIOCGIPHISTO, 0,      IW_PRIV_TYPE_INT | 16, "gethisto" },
+         };
+
+         /* Verify the user buffer */
+         ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer,
+                           sizeof(priv));
+         if(ret)
+           break;
+
+         /* Set the number of ioctl available */
+         wrq->u.data.length = 4;
+
+         /* Copy structure to the user buffer */
+         copy_to_user(wrq->u.data.pointer, (u_char *) priv,
+                      sizeof(priv));
+       }
+      break;
 
-       if (lp->watchdog.next == (timer_list *)0)
-               wavelan_watchdog((unsigned long)dev);
+#ifdef WIRELESS_SPY
+    case SIOCSIWSPY:
+      /* Set the spy list */
 
-       wavelan_splx(x);
+      /* Check the number of addresses */
+      if(wrq->u.data.length > IW_MAX_SPY)
+       {
+         ret = -E2BIG;
+         break;
+       }
+      lp->spy_number = wrq->u.data.length;
 
-       if (wavelan_debug > 4)
+      /* If there is some addresses to copy */
+      if(lp->spy_number > 0)
        {
-               unsigned char   *a;
-
-               a = (unsigned char *)buf;
-
-               printk
-               (
-                       "%s: tx: dest %02x:%02x:%02x:%02x:%02x:%02x, length %d, tbd.tbd_bufl 0x%x.\n",
-                       dev->name,
-                       a[0], a[1], a[2], a[3], a[4], a[5],
-                       length,
-                       buf_addr
-               );
+         struct sockaddr       address[IW_MAX_SPY];
+         int                   i;
+
+         /* Verify where the user has set his addresses */
+         ret = verify_area(VERIFY_READ, wrq->u.data.pointer,
+                           sizeof(struct sockaddr) * lp->spy_number);
+         if(ret)
+           break;
+         /* Copy addresses to the driver */
+         copy_from_user(address, wrq->u.data.pointer,
+                        sizeof(struct sockaddr) * lp->spy_number);
+
+         /* Copy addresses to the lp structure */
+         for(i = 0; i < lp->spy_number; i++)
+           {
+             memcpy(lp->spy_address[i], address[i].sa_data,
+                    WAVELAN_ADDR_SIZE);
+           }
+
+         /* Reset structure... */
+         memset(lp->spy_stat, 0x00, sizeof(iw_qual) * IW_MAX_SPY);
+
+#ifdef DEBUG_IOCTL_INFO
+         printk(KERN_DEBUG "SetSpy - Set of new addresses is :\n");
+         for(i = 0; i < wrq->u.data.length; i++)
+           printk(KERN_DEBUG "%02X:%02X:%02X:%02X:%02X:%02X \n",
+                  lp->spy_address[i][0],
+                  lp->spy_address[i][1],
+                  lp->spy_address[i][2],
+                  lp->spy_address[i][3],
+                  lp->spy_address[i][4],
+                  lp->spy_address[i][5]);
+#endif /* DEBUG_IOCTL_INFO */
        }
-}
 
-static
-int
-wavelan_send_packet(struct sk_buff *skb, device *dev)
-{
-       unsigned short  ioaddr;
+      break;
 
-       ioaddr = dev->base_addr;
+    case SIOCGIWSPY:
+      /* Get the spy list and spy stats */
 
-       if (dev->tbusy)
-       {
-               /*
-                * If we get here, some higher level
-                * has decided we are broken.
-                */
-               int     tickssofar;
-
-               tickssofar = jiffies - dev->trans_start;
-
-               /*
-                * But for the moment, we will rely on wavelan_watchdog()
-                * instead as it allows finer control over exactly when we
-                * make the determination of failure.
-                *
-               if (tickssofar < 5)
-                */
-                       return 1;
-
-               wavelan_scb_show(ioaddr);
-               wavelan_ru_show(dev);
-               wavelan_cu_show(dev);
-               wavelan_dev_show(dev);
-               wavelan_local_show(dev);
-
-               printk("%s: transmit timed out -- resetting board.\n", dev->name);
-
-               (void)wavelan_hardware_reset(dev);
-       }
+      /* Set the number of addresses */
+      wrq->u.data.length = lp->spy_number;
 
-       /*
-        * If some higher layer thinks we've missed
-        * a tx-done interrupt we are passed NULL.
-        * Caution: dev_tint() handles the cli()/sti() itself.
-        */
-       if (skb == (struct sk_buff *)0)
+      /* If the user want to have the addresses back... */
+      if((lp->spy_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
        {
-               dev_tint(dev);
-               return 0;
+         struct sockaddr       address[IW_MAX_SPY];
+         int                   i;
+
+         /* Verify the user buffer */
+         ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer,
+                           (sizeof(iw_qual) + sizeof(struct sockaddr))
+                           * IW_MAX_SPY);
+         if(ret)
+           break;
+
+         /* Copy addresses from the lp structure */
+         for(i = 0; i < lp->spy_number; i++)
+           {
+             memcpy(address[i].sa_data, lp->spy_address[i],
+                    WAVELAN_ADDR_SIZE);
+             address[i].sa_family = AF_UNIX;
+           }
+
+         /* Copy addresses to the user buffer */
+         copy_to_user(wrq->u.data.pointer, address,
+                      sizeof(struct sockaddr) * lp->spy_number);
+
+         /* Copy stats to the user buffer (just after) */
+         copy_to_user(wrq->u.data.pointer +
+                      (sizeof(struct sockaddr) * lp->spy_number),
+                      lp->spy_stat, sizeof(iw_qual) * lp->spy_number);
+
+         /* Reset updated flags */
+         for(i = 0; i < lp->spy_number; i++)
+           lp->spy_stat[i].updated = 0x0;
+       }       /* if(pointer != NULL) */
+
+      break;
+#endif /* WIRELESS_SPY */
+
+      /* ------------------ PRIVATE IOCTL ------------------ */
+
+    case SIOCSIPQTHR:
+      if(!suser())
+       return -EPERM;
+      psa.psa_quality_thr = *(wrq->u.name) & 0x0F;
+      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa,
+              (unsigned char *)&psa.psa_quality_thr, 1);
+      mmc_out(ioaddr, mmwoff(0, mmw_quality_thr), psa.psa_quality_thr);
+      break;
+
+    case SIOCGIPQTHR:
+      psa_read(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa,
+              (unsigned char *)&psa.psa_quality_thr, 1);
+      *(wrq->u.name) = psa.psa_quality_thr & 0x0F;
+      break;
+
+#ifdef HISTOGRAM
+    case SIOCSIPHISTO:
+      /* Verif if the user is root */
+      if(!suser())
+       return -EPERM;
+
+      /* Check the number of intervals */
+      if(wrq->u.data.length > 16)
+       {
+         ret = -E2BIG;
+         break;
        }
+      lp->his_number = wrq->u.data.length;
 
-       /*
-        * Block a timer-based transmit from overlapping.
-        */
-       if (set_bit(0, (void *)&dev->tbusy) == 0)
+      /* If there is some addresses to copy */
+      if(lp->his_number > 0)
        {
-               short           length;
-               unsigned char   *buf;
-
-               length = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
-               buf = skb->data;
-
-               hardware_send_packet(dev, buf, length);
+         /* Verify where the user has set his addresses */
+         ret = verify_area(VERIFY_READ, wrq->u.data.pointer,
+                           sizeof(char) * lp->his_number);
+         if(ret)
+           break;
+         /* Copy interval ranges to the driver */
+         copy_from_user(lp->his_range, wrq->u.data.pointer,
+                        sizeof(char) * lp->his_number);
+
+         /* Reset structure... */
+         memset(lp->his_sum, 0x00, sizeof(long) * 16);
        }
-       else
-               printk("%s: Transmitter access conflict.\n", dev->name);
+      break;
 
-       dev_kfree_skb(skb, FREE_WRITE);
+    case SIOCGIPHISTO:
+      /* Set the number of intervals */
+      wrq->u.data.length = lp->his_number;
 
-       return 0;
+      /* Give back the distribution statistics */
+      if((lp->his_number > 0) && (wrq->u.data.pointer != (caddr_t) 0))
+       {
+         /* Verify the user buffer */
+         ret = verify_area(VERIFY_WRITE, wrq->u.data.pointer,
+                           sizeof(long) * 16);
+         if(ret)
+           break;
+
+         /* Copy data to the user buffer */
+         copy_to_user(wrq->u.data.pointer, lp->his_sum,
+                      sizeof(long) * lp->his_number);
+       }       /* if(pointer != NULL) */
+      break;
+#endif /* HISTOGRAM */
+
+      /* ------------------- OTHER IOCTL ------------------- */
+
+    default:
+      ret = -EOPNOTSUPP;
+    }
+
+  /* ReEnable interrupts & restore flags */
+  wv_splx(x);
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_ioctl()\n", dev->name);
+#endif
+  return ret;
 }
 
-#if    0
-static
-int
-addrcmp(unsigned char *a0, unsigned char *a1)
+/*------------------------------------------------------------------*/
+/*
+ * Get wireless statistics
+ * Called by /proc/net/wireless...
+ */
+static iw_stats *
+wavelan_get_wireless_stats(device *    dev)
 {
-       int     i;
+  unsigned short       ioaddr = dev->base_addr;
+  net_local *          lp = (net_local *) dev->priv;
+  mmr_t                        m;
+  iw_stats *           wstats;
+  unsigned long                x;
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_get_wireless_stats()\n", dev->name);
+#endif
+
+  /* Disable interrupts & save flags */
+  x = wv_splhi();
+
+  if(lp == (net_local *) NULL)
+    return (iw_stats *) NULL;
+  wstats = &lp->wstats;
+
+  /* Get data from the mmc */
+  mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+
+  mmc_read(ioaddr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, 1);
+  mmc_read(ioaddr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, 2);
+  mmc_read(ioaddr, mmroff(0, mmr_thr_pre_set), &m.mmr_thr_pre_set, 4);
+
+  mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+  /* Copy data to wireless stuff */
+  wstats->status = m.mmr_dce_status;
+  wstats->qual.qual = m.mmr_sgnl_qual & MMR_SGNL_QUAL;
+  wstats->qual.level = m.mmr_signal_lvl & MMR_SIGNAL_LVL;
+  wstats->qual.noise = m.mmr_silence_lvl & MMR_SILENCE_LVL;
+  wstats->qual.updated = (((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 7) |
+                         ((m.mmr_signal_lvl & MMR_SIGNAL_LVL_VALID) >> 6) |
+                         ((m.mmr_silence_lvl & MMR_SILENCE_LVL_VALID) >> 5));
+  wstats->discard.nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+  wstats->discard.code = 0L;
+  wstats->discard.misc = 0L;
+
+  /* ReEnable interrupts & restore flags */
+  wv_splx(x);
+
+#ifdef DEBUG_IOCTL_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_get_wireless_stats()\n", dev->name);
+#endif
+  return &lp->wstats;
+}
+#endif /* WIRELESS_EXT */
 
-       for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-       {
-               if (a0[i] != a1[i])
-                       return a0[i] - a1[i];
-       }
+/************************* PACKET RECEPTION *************************/
+/*
+ * This part deal with receiving the packets.
+ * The interrupt handler get an interrupt when a packet has been
+ * successfully received and called this part...
+ */
 
-       return 0;
+/*------------------------------------------------------------------*/
+/*
+ * This routine does the actual copy of data (including the ethernet
+ * header structure) from the WaveLAN card to an sk_buff chain that
+ * will be passed up to the network interface layer. NOTE: We
+ * currently don't handle trailer protocols (neither does the rest of
+ * the network interface), so if that is needed, it will (at least in
+ * part) be added here.  The contents of the receive ring buffer are
+ * copied to a message chain that is then passed to the kernel.
+ *
+ * Note: if any errors occur, the packet is "dropped on the floor"
+ * (called by wv_packet_rcv())
+ */
+static inline void
+wv_packet_read(device *                dev,
+              u_short          buf_off,
+              int              sksize)
+{
+  net_local *          lp = (net_local *) dev->priv;
+  u_short              ioaddr = dev->base_addr;
+  struct sk_buff *     skb;
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: ->wv_packet_read(0x%X, %d)\n",
+        dev->name, fd_p, sksize);
+#endif
+
+  /* Allocate buffer for the data */
+  if((skb = dev_alloc_skb(sksize)) == (struct sk_buff *) NULL)
+    {
+#ifdef DEBUG_RX_ERROR
+      printk(KERN_INFO "%s: wv_packet_read(): could not alloc_skb(%d, GFP_ATOMIC).\n",
+            dev->name, sksize);
+#endif
+      lp->stats.rx_dropped++;
+      return;
+    }
+
+  skb->dev = dev;
+
+  /* Copy the packet to the buffer */
+  obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize);
+  skb->protocol=eth_type_trans(skb, dev);
+
+#ifdef DEBUG_RX_INFO
+  wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read");
+#endif /* DEBUG_RX_INFO */
+
+  /* Statistics gathering & stuff associated.
+   * It seem a bit messy with all the define, but it's really simple... */
+#if defined(WIRELESS_SPY) || defined(HISTOGRAM)
+  if(
+#ifdef WIRELESS_SPY
+     (lp->spy_number > 0) ||
+#endif /* WIRELESS_SPY */
+#ifdef HISTOGRAM
+     (lp->his_number > 0) ||
+#endif /* HISTOGRAM */
+     0)
+    {
+      u_char   stats[3];       /* Signal level, Noise level, Signal quality */
+
+      /* read signal level, silence level and signal quality bytes */
+      /* Note : in the Pcmcia hardware, these are part of the frame. It seem
+       * that for the ISA hardware, it's nowhere to be found in the frame,
+       * so I'm oblige to do this (it has side effect on /proc/net/wireless)
+       * Any idea ? */
+      mmc_out(ioaddr, mmwoff(0, mmw_freeze), 1);
+      mmc_read(ioaddr, mmroff(0, mmr_signal_lvl), stats, 3);
+      mmc_out(ioaddr, mmwoff(0, mmw_freeze), 0);
+
+#ifdef DEBUG_RX_INFO
+      printk(KERN_DEBUG "%s: wv_packet_read(): Signal level %d/63, Silence level %d/63, signal quality %d/16\n",
+            dev->name, stats[0] & 0x3F, stats[1] & 0x3F, stats[2] & 0x0F);
+#endif
+
+      /* Spying stuff */
+#ifdef WIRELESS_SPY
+      wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats);
+#endif /* WIRELESS_SPY */
+#ifdef HISTOGRAM
+      wl_his_gather(dev, stats);
+#endif /* HISTOGRAM */
+    }
+#endif /* defined(WIRELESS_SPY) || defined(HISTOGRAM) */
+
+  /*
+   * Hand the packet to the Network Module
+   */
+  netif_rx(skb);
+
+  lp->stats.rx_packets++;
+
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: <-wv_packet_read()\n", dev->name);
+#endif
 }
-#endif /* 0 */
 
+/*------------------------------------------------------------------*/
 /*
  * Transfer as many packets as we can
  * from the device RAM.
  * Called by the interrupt handler.
  */
-static
-void
-wavelan_receive(device *dev)
+static inline void
+wv_receive(device *    dev)
 {
-       unsigned short  ioaddr;
-       net_local       *lp;
-       int             nreaped;
+  u_short      ioaddr = dev->base_addr;
+  net_local *  lp = (net_local *)dev->priv;
+  int          nreaped = 0;
 
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
-       nreaped = 0;
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: ->wv_receive()\n", dev->name);
+#endif
 
-       for (;;)
-       {
-               fd_t            fd;
-               rbd_t           rbd;
-               ushort          pkt_len;
-               int             sksize;
-               struct sk_buff  *skb;
-
-               obram_read(ioaddr, lp->rx_head, (unsigned char *)&fd, sizeof(fd));
-
-               if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C)
-                       break;
-
-               nreaped++;
-
-               if
-               (
-                       (fd.fd_status & (FD_STATUS_B | FD_STATUS_OK))
-                       !=
-                       (FD_STATUS_B | FD_STATUS_OK)
-               )
-               {
-                       /*
-                        * Not sure about this one -- it does not seem
-                        * to be an error so we will keep quiet about it.
-                       if ((fd.fd_status & FD_STATUS_B) != FD_STATUS_B)
-                               printk("%s: frame not consumed by RU.\n", dev->name);
-                        */
-
-                       if ((fd.fd_status & FD_STATUS_OK) != FD_STATUS_OK)
-                               printk("%s: frame not received successfully.\n", dev->name);
-               }
-
-               if ((fd.fd_status & (FD_STATUS_S6 | FD_STATUS_S7 | FD_STATUS_S8 | FD_STATUS_S9 | FD_STATUS_S10 | FD_STATUS_S11)) != 0)
-               {
-                       lp->stats.rx_errors++;
-
-                       if ((fd.fd_status & FD_STATUS_S6) != 0)
-                               printk("%s: no EOF flag.\n", dev->name);
-
-                       if ((fd.fd_status & FD_STATUS_S7) != 0)
-                       {
-                               lp->stats.rx_length_errors++;
-                               printk("%s: frame too short.\n", dev->name);
-                       }
-
-                       if ((fd.fd_status & FD_STATUS_S8) != 0)
-                       {
-                               lp->stats.rx_over_errors++;
-                               printk("%s: rx DMA overrun.\n", dev->name);
-                       }
-
-                       if ((fd.fd_status & FD_STATUS_S9) != 0)
-                       {
-                               lp->stats.rx_fifo_errors++;
-                               printk("%s: ran out of resources.\n", dev->name);
-                       }
-
-                       if ((fd.fd_status & FD_STATUS_S10) != 0)
-                       {
-                               lp->stats.rx_frame_errors++;
-                               printk("%s: alignment error.\n", dev->name);
-                       }
-
-                       if ((fd.fd_status & FD_STATUS_S11) != 0)
-                       {
-                               lp->stats.rx_crc_errors++;
-                               printk("%s: CRC error.\n", dev->name);
-                       }
-               }
-
-               if (fd.fd_rbd_offset == I82586NULL)
-                       printk("%s: frame has no data.\n", dev->name);
-               else
-               {
-                       obram_read(ioaddr, fd.fd_rbd_offset, (unsigned char *)&rbd, sizeof(rbd));
-
-                       if ((rbd.rbd_status & RBD_STATUS_EOF) != RBD_STATUS_EOF)
-                               printk("%s: missing EOF flag.\n", dev->name);
-
-                       if ((rbd.rbd_status & RBD_STATUS_F) != RBD_STATUS_F)
-                               printk("%s: missing F flag.\n", dev->name);
-
-                       pkt_len = rbd.rbd_status & RBD_STATUS_ACNT;
+  /* Loop on each received packet */
+  for(;;)
+    {
+      fd_t             fd;
+      rbd_t            rbd;
+      ushort           pkt_len;
 
-#if    0
-                       {
-                               unsigned char           addr[WAVELAN_ADDR_SIZE];
-                               int                     i;
-                               static unsigned char    toweraddr[WAVELAN_ADDR_SIZE]    =
-                               {
-                                       0x08, 0x00, 0x0e, 0x20, 0x3e, 0xd3,
-                               };
-
-                               obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr), &addr[0], sizeof(addr));
-                               if
-                               (
-                                       /*
-                                       addrcmp(&addr[0], &dev->dev_addr[0]) != 0
-                                       &&
-                                       */
-                                       addrcmp(&addr[0], toweraddr) != 0
-                               )
-                               {
-                                       printk("%s: foreign MAC source addr=", dev->name);
-                                       for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-                                               printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
-                                       printk("\n");
-                               }
-                       }
-#endif /* 0 */
+      obram_read(ioaddr, lp->rx_head, (unsigned char *) &fd, sizeof(fd));
 
-                       if (wavelan_debug > 5)
-                       {
-                               unsigned char   addr[WAVELAN_ADDR_SIZE];
-                               unsigned short  ltype;
-                               int             i;
+      /* If the current frame is not complete, we have reach the end... */
+      if((fd.fd_status & FD_STATUS_C) != FD_STATUS_C)
+       break;          /* This is how we exit the loop */
 
-#if    0
-                               printk("%s: fd_dest=", dev->name);
-                               for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-                                       printk("%s%02x", (i == 0) ? "" : ":", fd.fd_dest[i]);
-                               printk("\n");
-
-                               printk("%s: fd_src=", dev->name);
-                               for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-                                       printk("%s%02x", (i == 0) ? "" : ":", fd.fd_src[i]);
-                               printk("\n");
-                               printk("%s: fd_length=%d\n", dev->name, fd.fd_length);
-#endif /* 0 */
+      nreaped++;
 
-                               obram_read(ioaddr, rbd.rbd_bufl, &addr[0], sizeof(addr));
-                               printk("%s: dest=", dev->name);
-                               for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-                                       printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
-                               printk("\n");
-
-                               obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr), &addr[0], sizeof(addr));
-                               printk("%s: src=", dev->name);
-                               for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
-                                       printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
-                               printk("\n");
-
-                               obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr) * 2, (unsigned char *)&ltype, sizeof(ltype));
-                               printk("%s: ntohs(length/type)=0x%04x\n", dev->name, ntohs(ltype));
-                       }
-
-                       sksize = pkt_len;
-
-                       if ((skb = dev_alloc_skb(sksize)) == (struct sk_buff *)0)
-                       {
-                               printk("%s: could not alloc_skb(%d, GFP_ATOMIC).\n", dev->name, sksize);
-                               lp->stats.rx_dropped++;
-                       }
-                       else
-                       {
-                               skb->dev = dev;
-
-                               obram_read(ioaddr, rbd.rbd_bufl, skb_put(skb,pkt_len), pkt_len);
-
-                               if (wavelan_debug > 5)
-                               {
-                                       int     i;
-                                       int     maxi;
-
-                                       printk("%s: pkt_len=%d, data=\"", dev->name, pkt_len);
-
-                                       if ((maxi = pkt_len) > 16)
-                                               maxi = 16;
-                               
-                                       for (i = 0; i < maxi; i++)
-                                       {
-                                               unsigned char   c;
-
-                                               c = skb->data[i];
-                                               if (c >= ' ' && c <= '~')
-                                                       printk(" %c", skb->data[i]);
-                                               else
-                                                       printk("%02x", skb->data[i]);
-                                       }
-
-                                       if (maxi < pkt_len)
-                                               printk("..");
-                               
-                                       printk("\"\n\n");
-                               }
-                       
-                               skb->protocol=eth_type_trans(skb,dev);
-                               netif_rx(skb);
-
-                               lp->stats.rx_packets++;
-                       }
-               }
-
-               fd.fd_status = 0;
-               obram_write(ioaddr, fdoff(lp->rx_head, fd_status), (unsigned char *)&fd.fd_status, sizeof(fd.fd_status));
-
-               fd.fd_command = FD_COMMAND_EL;
-               obram_write(ioaddr, fdoff(lp->rx_head, fd_command), (unsigned char *)&fd.fd_command, sizeof(fd.fd_command));
-
-               fd.fd_command = 0;
-               obram_write(ioaddr, fdoff(lp->rx_last, fd_command), (unsigned char *)&fd.fd_command, sizeof(fd.fd_command));
-
-               lp->rx_last = lp->rx_head;
-               lp->rx_head = fd.fd_link_offset;
+      /* Check if frame correctly received */
+      if((fd.fd_status & (FD_STATUS_B | FD_STATUS_OK)) !=
+        (FD_STATUS_B | FD_STATUS_OK))
+       {
+         /*
+          * Not sure about this one -- it does not seem
+          * to be an error so we will keep quiet about it.
+          */
+#ifndef IGNORE_NORMAL_XMIT_ERRS
+#ifdef DEBUG_RX_ERROR
+         if((fd.fd_status & FD_STATUS_B) != FD_STATUS_B)
+           printk(KERN_INFO "%s: wv_receive(): frame not consumed by RU.\n",
+                  dev->name);
+#endif
+#endif /* IGNORE_NORMAL_XMIT_ERRS */
+
+#ifdef DEBUG_RX_ERROR
+         if((fd.fd_status & FD_STATUS_OK) != FD_STATUS_OK)
+           printk(KERN_INFO "%s: wv_receive(): frame not received successfully.\n",
+                  dev->name);
+#endif
        }
 
-/*
-       if (nreaped > 1)
-               printk("r%d", nreaped);
-*/
+      /* Check is there was problems in the frame processing */
+      if((fd.fd_status & (FD_STATUS_S6 | FD_STATUS_S7 | FD_STATUS_S8 |
+                         FD_STATUS_S9 | FD_STATUS_S10 | FD_STATUS_S11))
+        != 0)
+       {
+         lp->stats.rx_errors++;
+
+#ifdef DEBUG_RX_ERROR
+         if((fd.fd_status & FD_STATUS_S6) != 0)
+           printk(KERN_INFO "%s: wv_receive(): no EOF flag.\n", dev->name);
+#endif
+
+         if((fd.fd_status & FD_STATUS_S7) != 0)
+           {
+             lp->stats.rx_length_errors++;
+#ifdef DEBUG_RX_ERROR
+             printk(KERN_INFO "%s: wv_receive(): frame too short.\n",
+                    dev->name);
+#endif
+           }
+
+         if((fd.fd_status & FD_STATUS_S8) != 0)
+           {
+             lp->stats.rx_over_errors++;
+#ifdef DEBUG_RX_ERROR
+             printk(KERN_INFO "%s: wv_receive(): rx DMA overrun.\n",
+                    dev->name);
+#endif
+           }
+
+         if((fd.fd_status & FD_STATUS_S9) != 0)
+           {
+             lp->stats.rx_fifo_errors++;
+#ifdef DEBUG_RX_ERROR
+             printk(KERN_INFO "%s: wv_receive(): ran out of resources.\n",
+                    dev->name);
+#endif
+           }
+
+         if((fd.fd_status & FD_STATUS_S10) != 0)
+           {
+             lp->stats.rx_frame_errors++;
+#ifdef DEBUG_RX_ERROR
+             printk(KERN_INFO "%s: wv_receive(): alignment error.\n",
+                    dev->name);
+#endif
+           }
+
+         if((fd.fd_status & FD_STATUS_S11) != 0)
+           {
+             lp->stats.rx_crc_errors++;
+#ifdef DEBUG_RX_ERROR
+             printk(KERN_INFO "%s: wv_receive(): CRC error.\n", dev->name);
+#endif
+           }
+       }
+
+      /* Check if frame contain a pointer to the data */
+      if(fd.fd_rbd_offset == I82586NULL)
+#ifdef DEBUG_RX_ERROR
+       printk(KERN_INFO "%s: wv_receive(): frame has no data.\n", dev->name);
+#endif
+      else
+       {
+         obram_read(ioaddr, fd.fd_rbd_offset,
+                    (unsigned char *) &rbd, sizeof(rbd));
+
+#ifdef DEBUG_RX_ERROR
+         if((rbd.rbd_status & RBD_STATUS_EOF) != RBD_STATUS_EOF)
+           printk(KERN_INFO "%s: wv_receive(): missing EOF flag.\n",
+                  dev->name);
+
+         if((rbd.rbd_status & RBD_STATUS_F) != RBD_STATUS_F)
+           printk(KERN_INFO "%s: wv_receive(): missing F flag.\n",
+                  dev->name);
+#endif
+
+         pkt_len = rbd.rbd_status & RBD_STATUS_ACNT;
+
+         /* Read the packet and transmit to Linux */
+         wv_packet_read(dev, rbd.rbd_bufl, pkt_len);
+       }       /* if frame has data */
+
+      fd.fd_status = 0;
+      obram_write(ioaddr, fdoff(lp->rx_head, fd_status),
+                 (unsigned char *) &fd.fd_status, sizeof(fd.fd_status));
+
+      fd.fd_command = FD_COMMAND_EL;
+      obram_write(ioaddr, fdoff(lp->rx_head, fd_command),
+                 (unsigned char *) &fd.fd_command, sizeof(fd.fd_command));
+
+      fd.fd_command = 0;
+      obram_write(ioaddr, fdoff(lp->rx_last, fd_command),
+                 (unsigned char *) &fd.fd_command, sizeof(fd.fd_command));
+
+      lp->rx_last = lp->rx_head;
+      lp->rx_head = fd.fd_link_offset;
+    }  /* for(;;) -> loop on all frames */
+
+#ifdef DEBUG_RX_INFO
+  if(nreaped > 1)
+    printk(KERN_DEBUG "%s: wv_receive(): reaped %d\n", dev->name, nreaped);
+#endif
+#ifdef DEBUG_RX_TRACE
+  printk(KERN_DEBUG "%s: <-wv_receive()\n", dev->name);
+#endif
 }
 
+/*********************** PACKET TRANSMISSION ***********************/
 /*
- * Command completion interrupt.
- * Reclaim as many freed tx buffers as we can.
+ * This part deal with sending packet through the wavelan
+ *
  */
-static
-int
-wavelan_complete(device *dev, unsigned short ioaddr, net_local *lp)
-{
-       int     nreaped;
-
-       nreaped = 0;
 
-       for (;;)
-       {
-               unsigned short  tx_status;
-
-               if (lp->tx_first_in_use == I82586NULL)
-                       break;
-
-               obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), (unsigned char *)&tx_status, sizeof(tx_status));
-
-               if ((tx_status & AC_SFLD_C) == 0)
-                       break;
-
-               nreaped++;
-
-               --lp->tx_n_in_use;
+/*------------------------------------------------------------------*/
+/*
+ * This routine fills in the appropriate registers and memory
+ * locations on the WaveLAN card and starts the card off on
+ * the transmit.
+ *
+ * The principle :
+ * Each block contain a transmit command, a nop command,
+ * a transmit block descriptor and a buffer.
+ * The CU read the transmit block which point to the tbd,
+ * read the tbd and the the content of the buffer.
+ * When it has finish with it, it goes to the next command
+ * which in our case is the nop. The nop point on itself,
+ * so the CU stop here.
+ * When we add the next block, we modify the previous nop
+ * to make it point on the new tx command.
+ * Simple, isn't it ?
+ *
+ * (called in wavelan_packet_xmit())
+ */
+static inline void
+wv_packet_write(device *       dev,
+               void *  buf,
+               short   length)
+{
+  net_local *          lp = (net_local *) dev->priv;
+  u_short              ioaddr = dev->base_addr;
+  unsigned short       txblock;
+  unsigned short       txpred;
+  unsigned short       tx_addr;
+  unsigned short       nop_addr;
+  unsigned short       tbd_addr;
+  unsigned short       buf_addr;
+  ac_tx_t              tx;
+  ac_nop_t             nop;
+  tbd_t                        tbd;
+  int                  clen = length;
+  unsigned long                x;
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: ->wv_packet_write(%d)\n", dev->name, length);
+#endif
+
+  /* Check if we need some padding */
+  if(clen < ETH_ZLEN)
+    clen = ETH_ZLEN;
+
+  x = wv_splhi();
+
+  /* Calculate addresses of next block and previous block */
+  txblock = lp->tx_first_free;
+  txpred = txblock - TXBLOCKZ;
+  if(txpred < OFFSET_CU)
+    txpred += NTXBLOCKS * TXBLOCKZ;
+  lp->tx_first_free += TXBLOCKZ;
+  if(lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+    lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
 
 /*
 if (lp->tx_n_in_use > 0)
        printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
 */
 
-               if (lp->tx_n_in_use <= 0)
-                       lp->tx_first_in_use = I82586NULL;
-               else
-               {
-                       lp->tx_first_in_use += TXBLOCKZ;
-                       if (lp->tx_first_in_use >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
-                               lp->tx_first_in_use -= NTXBLOCKS * TXBLOCKZ;
-               }
-
-               if (tx_status & AC_SFLD_OK)
-               {
-                       int     ncollisions;
-
-                       lp->stats.tx_packets++;
-                       ncollisions = tx_status & AC_SFLD_MAXCOL;
-                       lp->stats.collisions += ncollisions;
-                       /*
-                       if (ncollisions > 0)
-                               printk("%s: tx completed after %d collisions.\n", dev->name, ncollisions);
-                       */
-               }
-               else
-               {
-                       lp->stats.tx_errors++;
-                       if (tx_status & AC_SFLD_S10)
-                       {
-                               lp->stats.tx_carrier_errors++;
-                               if (wavelan_debug > 0)
-                                       printk("%s:     tx error: no CS.\n", dev->name);
-                       }
-                       if (tx_status & AC_SFLD_S9)
-                       {
-                               lp->stats.tx_carrier_errors++;
-                               printk("%s:     tx error: lost CTS.\n", dev->name);
-                       }
-                       if (tx_status & AC_SFLD_S8)
-                       {
-                               lp->stats.tx_fifo_errors++;
-                               printk("%s:     tx error: slow DMA.\n", dev->name);
-                       }
-                       if (tx_status & AC_SFLD_S6)
-                       {
-                               lp->stats.tx_heartbeat_errors++;
-                               if (wavelan_debug > 0)
-                                       printk("%s:     tx error: heart beat.\n", dev->name);
-                       }
-                       if (tx_status & AC_SFLD_S5)
-                       {
-                               lp->stats.tx_aborted_errors++;
-                               if (wavelan_debug > 0)
-                                       printk("%s:     tx error: too many collisions.\n", dev->name);
-                       }
-               }
-
-               if (wavelan_debug > 5)
-                       printk("%s:     tx completed, tx_status 0x%04x.\n", dev->name, tx_status);
-       }
-
-/*
-       if (nreaped > 1)
-               printk("c%d", nreaped);
-*/
-
-       /*
-        * Inform upper layers.
-        */
-       if (lp->tx_n_in_use < NTXBLOCKS - 1)
-       {
-               dev->tbusy = 0;
-               mark_bh(NET_BH);
-       }
-
-       return nreaped;
+  lp->tx_n_in_use++;
+
+  /* Calculate addresses of the differents part of the block */
+  tx_addr = txblock;
+  nop_addr = tx_addr + sizeof(tx);
+  tbd_addr = nop_addr + sizeof(nop);
+  buf_addr = tbd_addr + sizeof(tbd);
+
+  /*
+   * Transmit command.
+   */
+  tx.tx_h.ac_status = 0;
+  obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
+             (unsigned char *) &tx.tx_h.ac_status,
+             sizeof(tx.tx_h.ac_status));
+
+  /*
+   * NOP command.
+   */
+  nop.nop_h.ac_status = 0;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+             (unsigned char *) &nop.nop_h.ac_status,
+             sizeof(nop.nop_h.ac_status));
+  nop.nop_h.ac_link = nop_addr;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+             (unsigned char *) &nop.nop_h.ac_link,
+             sizeof(nop.nop_h.ac_link));
+
+  /*
+   * Transmit buffer descriptor. 
+   */
+  tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & clen);
+  tbd.tbd_next_bd_offset = I82586NULL;
+  tbd.tbd_bufl = buf_addr;
+  tbd.tbd_bufh = 0;
+  obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
+
+  /*
+   * Data.
+   */
+  obram_write(ioaddr, buf_addr, buf, clen);
+
+  /*
+   * Overwrite the predecessor NOP link
+   * so that it points to this txblock.
+   */
+  nop_addr = txpred + sizeof(tx);
+  nop.nop_h.ac_status = 0;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+             (unsigned char *)&nop.nop_h.ac_status,
+             sizeof(nop.nop_h.ac_status));
+  nop.nop_h.ac_link = txblock;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+             (unsigned char *) &nop.nop_h.ac_link,
+             sizeof(nop.nop_h.ac_link));
+
+  /* If watchdog not already active, activate it... */
+  if(lp->watchdog.prev == (timer_list *) NULL)
+    {
+      /* set timer to expire in WATCHDOG_JIFFIES */
+      lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
+      add_timer(&lp->watchdog);
+    }
+
+  if(lp->tx_first_in_use == I82586NULL)
+    lp->tx_first_in_use = txblock;
+
+  if(lp->tx_n_in_use < NTXBLOCKS - 1)
+    dev->tbusy = 0;
+
+  wv_splx(x);
+
+#ifdef DEBUG_TX_INFO
+  wv_packet_info((u_char *) buf, length, dev->name, "wv_packet_write");
+#endif /* DEBUG_TX_INFO */
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: <-wv_packet_write()\n", dev->name);
+#endif
 }
 
-static
-void
-wavelan_watchdog(unsigned long a)
+/*------------------------------------------------------------------*/
+/*
+ * This routine is called when we want to send a packet (NET3 callback)
+ * In this routine, we check if the the harware is ready to accept
+ * the packet. We also prevent reentrance. Then, we call the function
+ * to send the packet...
+ */
+static int
+wavelan_packet_xmit(struct sk_buff *   skb,
+                   device *            dev)
 {
-       device          *dev;
-       net_local       *lp;
-       unsigned short  ioaddr;
-       unsigned long   x;
-       unsigned int    nreaped;
-
-       x = wavelan_splhi();
-
-       dev = (device *)a;
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
-
-       if (lp->tx_n_in_use <= 0)
+  net_local *  lp = (net_local *)dev->priv;
+
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_packet_xmit(0x%X)\n", dev->name,
+        (unsigned) skb);
+#endif
+
+  /* This flag indicate that the hardware can't perform a transmission.
+   * Theoritically, NET3 check it before sending a packet to the driver,
+   * but in fact it never do that and pool continuously.
+   * As the watchdog will abort too long transmissions, we are quite safe...
+   */
+  if(dev->tbusy)
+    return 1;
+
+  /*
+   * If some higher layer thinks we've missed
+   * a tx-done interrupt we are passed NULL.
+   * Caution: dev_tint() handles the cli()/sti() itself.
+   */
+  if(skb == (struct sk_buff *)0)
+    {
+#ifdef DEBUG_TX_ERROR
+      printk(KERN_INFO "%s: wavelan_packet_xmit(): skb == NULL\n", dev->name);
+#endif
+      dev_tint(dev);
+      return 0;
+    }
+
+  /*
+   * Block a timer-based transmit from overlapping.
+   * In other words, prevent reentering this routine.
+   */
+  if(set_bit(0, (void *)&dev->tbusy) != 0)
+#ifdef DEBUG_TX_ERROR
+    printk(KERN_INFO "%s: Transmitter access conflict.\n", dev->name);
+#endif
+  else
+    {
+      /* If somebody has asked to reconfigure the controler, we can do it now */
+      if(lp->reconfig_82586)
        {
-               wavelan_splx(x);
-               return;
+         wv_82586_config(dev);
+         if(dev->tbusy)
+           return 1;
        }
 
-       lp->watchdog.expires = jiffies+WATCHDOG_JIFFIES;
-       add_timer(&lp->watchdog);
+#ifdef DEBUG_TX_ERROR
+      if(skb->next)
+       printk(KERN_INFO "skb has next\n");
+#endif
 
-       if (jiffies - dev->trans_start < WATCHDOG_JIFFIES)
-       {
-               wavelan_splx(x);
-               return;
-       }
+      wv_packet_write(dev, skb->data, skb->len);
+    }
 
-       nreaped = wavelan_complete(dev, ioaddr, lp);
+  dev_kfree_skb(skb, FREE_WRITE);
 
-       printk("%s: warning: wavelan_watchdog(): %d reaped, %d remain.\n", dev->name, nreaped, lp->tx_n_in_use);
-       /*
-       wavelan_scb_show(ioaddr);
-       wavelan_ru_show(dev);
-       wavelan_cu_show(dev);
-       wavelan_dev_show(dev);
-       wavelan_local_show(dev);
-       */
-
-       wavelan_splx(x);
+#ifdef DEBUG_TX_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_packet_xmit()\n", dev->name);
+#endif
+  return 0;
 }
 
-static
-void
-wavelan_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-       device          *dev;
-       unsigned short  ioaddr;
-       net_local       *lp;
-       unsigned short  hasr;
-       unsigned short  status;
-       unsigned short  ack_cmd;
-
-       if ((dev = (device *)(irq2dev_map[irq])) == (device *)0)
-       {
-               printk("wavelan_interrupt(): irq %d for unknown device.\n", irq);
-               return;
-       }
-
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
-
-       dev->interrupt = 1;
-
-       if ((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR)
-       {
-               unsigned char   dce_status;
-
-               /*
-                * Interrupt from the modem management controller.
-                * This will clear it -- ignored for now.
-                */
-               mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, sizeof(dce_status));
-               if (wavelan_debug > 0)
-                       printk("%s: warning: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n", dev->name, dce_status);
-       }
-
-       if ((hasr & HASR_82586_INTR) == 0)
-       {
-               dev->interrupt = 0;
-               if (wavelan_debug > 0)
-                       printk("%s: warning: wavelan_interrupt() but (hasr & HASR_82586_INTR) == 0.\n", dev->name);
-               return;
-       }
-
-       obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&status, sizeof(status));
-
-       /*
-        * Acknowledge the interrupt(s).
-        */
-       ack_cmd = status & SCB_ST_INT;
-
-       obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&ack_cmd, sizeof(ack_cmd));
-
-       set_chan_attn(ioaddr, lp->hacr);
-
-       if (wavelan_debug > 5)
-               printk("%s: interrupt, status 0x%04x.\n", dev->name, status);
+/********************** HARDWARE CONFIGURATION **********************/
+/*
+ * This part do the real job of starting and configuring the hardware.
+ */
 
-       if ((status & SCB_ST_CX) == SCB_ST_CX)
-       {
-               /*
-                * Command completed.
-                */
-               if (wavelan_debug > 5)
-                       printk("%s: command completed.\n", dev->name);
-               (void)wavelan_complete(dev, ioaddr, lp);
-       }
+/*------------------------------------------------------------------*/
+/*
+ * Routine to initialize the Modem Management Controller.
+ * (called by wv_hw_reset())
+ */
+static inline int
+wv_mmc_init(device *   dev)
+{
+  u_short      ioaddr = dev->base_addr;
+  net_local *  lp = (net_local *)dev->priv;
+  psa_t                psa;
+  mmw_t                m;
+  int          configured;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_mmc_init()\n", dev->name);
+#endif
+
+  /* Read the parameter storage area */
+  psa_read(ioaddr, lp->hacr, 0, (unsigned char *) &psa, sizeof(psa));
+
+#ifdef USE_PSA_CONFIG
+  configured = psa.psa_conf_status & 1;
+#else
+  configured = 0;
+#endif
+
+  /* Is the PSA is not configured */
+  if(!configured)
+    {
+      /* User will be able to configure NWID after (with iwconfig) */
+      psa.psa_nwid[0] = 0;
+      psa.psa_nwid[1] = 0;
+
+      /* As NWID is not set : no NWID checking */
+      psa.psa_nwid_select = 0;
+
+      /* Set to standard values
+       * 0x04 for AT,
+       * 0x01 for MCA,
+       * 0x04 for PCMCIA and 2.00 card (AT&T 407-024689/E document)
+       */
+      if (psa.psa_comp_number & 1)
+       psa.psa_thr_pre_set = 0x01;
+      else
+       psa.psa_thr_pre_set = 0x04;
+      psa.psa_quality_thr = 0x03;
+
+      /* It is configured */
+      psa.psa_conf_status |= 1;
+
+#ifdef USE_PSA_CONFIG
+      /* Write the psa */
+      psa_write(ioaddr, lp->hacr, (char *)psa.psa_nwid - (char *)&psa,
+               (unsigned char *)psa.psa_nwid, 3);
+      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_thr_pre_set - (char *)&psa,
+               (unsigned char *)&psa.psa_thr_pre_set, 1);
+      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_quality_thr - (char *)&psa,
+               (unsigned char *)&psa.psa_quality_thr, 1);
+      psa_write(ioaddr, lp->hacr, (char *)&psa.psa_conf_status - (char *)&psa,
+               (unsigned char *)&psa.psa_conf_status, 1);
+#endif
+    }
+
+  /* Zero the mmc structure */
+  memset(&m, 0x00, sizeof(m));
+
+  /* Copy PSA info to the mmc */
+  m.mmw_netw_id_l = psa.psa_nwid[1];
+  m.mmw_netw_id_h = psa.psa_nwid[0];
+  
+  if(psa.psa_nwid_select & 1)
+    m.mmw_loopt_sel = 0x00;
+  else
+    m.mmw_loopt_sel = MMW_LOOPT_SEL_DIS_NWID;
+
+  m.mmw_thr_pre_set = psa.psa_thr_pre_set & 0x3F;
+  m.mmw_quality_thr = psa.psa_quality_thr & 0x0F;
+
+  /* Missing : encryption stuff... */
+
+  /*
+   * Set default modem control parameters.
+   * See NCR document 407-0024326 Rev. A.
+   */
+  m.mmw_jabber_enable = 0x01;
+  m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
+  m.mmw_ifs = 0x20;
+  m.mmw_mod_delay = 0x04;
+  m.mmw_jam_time = 0x38;
+
+  m.mmw_encr_enable = 0;
+  m.mmw_des_io_invert = 0;
+  m.mmw_freeze = 0;
+  m.mmw_decay_prm = 0;
+  m.mmw_decay_updat_prm = 0;
+
+  /* Write all info to mmc */
+  mmc_write(ioaddr, 0, (u_char *)&m, sizeof(m));
+
+  /* The following code start the modem of the 2.00 frequency
+   * selectable cards at power on. It's not strictly needed for the
+   * following boots...
+   * The original patch was by Joe Finney for the PCMCIA driver, but
+   * I've cleaned it a bit and add documentation.
+   * Thanks to Loeke Brederveld from Lucent for the info.
+   */
+
+  /* Attempt to recognise 2.00 cards (2.4 GHz frequency selectable)
+   * (does it work for everybody ??? - especially old cards...) */
+  /* Note : WFREQSEL verify that it is able to read from EEprom
+   * a sensible frequency (address 0x00) + that MMR_FEE_STATUS_ID
+   * is 0xA (Xilinx version) or 0xB (Ariadne version).
+   * My test is more crude but do work... */
+  if(!(mmc_in(ioaddr, mmroff(0, mmr_fee_status)) &
+       (MMR_FEE_STATUS_DWLD | MMR_FEE_STATUS_BUSY)))
+    {
+      /* We must download the frequency parameters to the
+       * synthetisers (from the EEprom - area 1)
+       * Note : as the EEprom is auto decremented, we set the end
+       * if the area... */
+      m.mmw_fee_addr = 0x0F;
+      m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+      mmc_write(ioaddr, (char *)&m.mmw_fee_ctrl - (char *)&m,
+               (unsigned char *)&m.mmw_fee_ctrl, 2);
+
+      /* Wait until the download is finished */
+      fee_wait(ioaddr, 100, 100);
+
+#ifdef DEBUG_CONFIG_INFO
+      /* The frequency was in the last word downloaded... */
+      mmc_read(ioaddr, (char *)&m.mmw_fee_data_l - (char *)&m,
+              (unsigned char *)&m.mmw_fee_data_l, 2);
+
+      /* Print some info for the user */
+      printk(KERN_DEBUG "%s: Wavelan 2.00 recognised (frequency select) : Current frequency = %ld\n",
+            dev->name,
+            ((m.mmw_fee_data_h << 4) |
+             (m.mmw_fee_data_l >> 4)) * 5 / 2 + 24000L);
+#endif
+
+      /* We must now download the power adjust value (gain) to
+       * the synthetisers (from the EEprom - area 7 - DAC) */
+      m.mmw_fee_addr = 0x61;
+      m.mmw_fee_ctrl = MMW_FEE_CTRL_READ | MMW_FEE_CTRL_DWLD;
+      mmc_write(ioaddr, (char *)&m.mmw_fee_ctrl - (char *)&m,
+               (unsigned char *)&m.mmw_fee_ctrl, 2);
+
+      /* Wait until the download is finished */
+    }  /* if 2.00 card */
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_mmc_init()\n", dev->name);
+#endif
+  return 0;
+}
 
-       if ((status & SCB_ST_FR) == SCB_ST_FR)
-       {
-               /*
-                * Frame received.
-                */
-               if (wavelan_debug > 5)
-                       printk("%s: received packet.\n", dev->name);
-               wavelan_receive(dev);
-       }
+/*------------------------------------------------------------------*/
+/*
+ * Construct the fd and rbd structures.
+ * Start the receive unit.
+ * (called by wv_hw_reset())
+ */
+static inline int
+wv_ru_start(device *   dev)
+{
+  net_local *  lp = (net_local *) dev->priv;
+  u_short      ioaddr = dev->base_addr;
+  u_short      scb_cs;
+  fd_t         fd;
+  rbd_t                rbd;
+  u_short      rx;
+  u_short      rx_next;
+  int          i;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_ru_start()\n", dev->name);
+#endif
+
+  obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs));
+  if((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY)
+    return 0;
+
+  lp->rx_head = OFFSET_RU;
+
+  for(i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next)
+    {
+      rx_next = (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ;
+
+      fd.fd_status = 0;
+      fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0;
+      fd.fd_link_offset = rx_next;
+      fd.fd_rbd_offset = rx + sizeof(fd);
+      obram_write(ioaddr, rx, (unsigned char *)&fd, sizeof(fd));
+
+      rbd.rbd_status = 0;
+      rbd.rbd_next_rbd_offset = I82586NULL;
+      rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd);
+      rbd.rbd_bufh = 0;
+      rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ);
+      obram_write(ioaddr, rx + sizeof(fd),
+                 (unsigned char *) &rbd, sizeof(rbd));
+
+      lp->rx_last = rx;
+    }
+
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset),
+             (unsigned char *) &lp->rx_head, sizeof(lp->rx_head));
+
+  scb_cs = SCB_CMD_RUC_GO;
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+             (unsigned char *) &scb_cs, sizeof(scb_cs));
+
+  set_chan_attn(ioaddr, lp->hacr);
+
+  for(i = 1000; i > 0; i--)
+    {
+      obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
+                (unsigned char *) &scb_cs, sizeof(scb_cs));
+      if (scb_cs == 0)
+       break;
+
+      udelay(10);
+    }
+
+  if(i <= 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wavelan_ru_start(): board not accepting command.\n",
+            dev->name);
+#endif
+      return -1;
+    }
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_ru_start()\n", dev->name);
+#endif
+  return 0;
+}
 
-       if
-       (
-               (status & SCB_ST_CNA) == SCB_ST_CNA
-               ||
-               (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && dev->start)
-       )
-       {
-               printk("%s: warning: CU inactive -- restarting.\n", dev->name);
+/*------------------------------------------------------------------*/
+/*
+ * Initialise the transmit blocks.
+ * Start the command unit executing the NOP
+ * self-loop of the first transmit block.
+ *
+ * Here, we create the list of send buffer used to transmit packets
+ * between the PC and the command unit. For each buffer, we create a
+ * buffer descriptor (pointing on the buffer), a transmit command
+ * (pointing to the buffer descriptor) and a nop command.
+ * The transmit command is linked to the nop, and the nop to itself.
+ * When we will have finish to execute the transmit command, we will
+ * then loop on the nop. By releasing the nop link to a new command,
+ * we may send another buffer.
+ *
+ * (called by wv_hw_reset())
+ */
+static inline int
+wv_cu_start(device *   dev)
+{
+  net_local *  lp = (net_local *) dev->priv;
+  u_short      ioaddr = dev->base_addr;
+  int          i;
+  u_short      txblock;
+  u_short      first_nop;
+  u_short      scb_cs;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_cu_start()\n", dev->name);
+#endif
+
+  lp->tx_first_free = OFFSET_CU;
+  lp->tx_first_in_use = I82586NULL;
+
+  for(i = 0, txblock = OFFSET_CU;
+      i < NTXBLOCKS;
+      i++, txblock += TXBLOCKZ)
+    {
+      ac_tx_t          tx;
+      ac_nop_t         nop;
+      tbd_t            tbd;
+      unsigned short   tx_addr;
+      unsigned short   nop_addr;
+      unsigned short   tbd_addr;
+      unsigned short   buf_addr;
+
+      tx_addr = txblock;
+      nop_addr = tx_addr + sizeof(tx);
+      tbd_addr = nop_addr + sizeof(nop);
+      buf_addr = tbd_addr + sizeof(tbd);
+
+      tx.tx_h.ac_status = 0;
+      tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I;
+      tx.tx_h.ac_link = nop_addr;
+      tx.tx_tbd_offset = tbd_addr;
+      obram_write(ioaddr, tx_addr, (unsigned char *) &tx, sizeof(tx));
+
+      nop.nop_h.ac_status = 0;
+      nop.nop_h.ac_command = acmd_nop;
+      nop.nop_h.ac_link = nop_addr;
+      obram_write(ioaddr, nop_addr, (unsigned char *) &nop, sizeof(nop));
+
+      tbd.tbd_status = TBD_STATUS_EOF;
+      tbd.tbd_next_bd_offset = I82586NULL;
+      tbd.tbd_bufl = buf_addr;
+      tbd.tbd_bufh = 0;
+      obram_write(ioaddr, tbd_addr, (unsigned char *) &tbd, sizeof(tbd));
+    }
+
+  first_nop = OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t);
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset),
+             (unsigned char *) &first_nop, sizeof(first_nop));
+
+  scb_cs = SCB_CMD_CUC_GO;
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+             (unsigned char *) &scb_cs, sizeof(scb_cs));
+
+  set_chan_attn(ioaddr, lp->hacr);
+
+  for(i = 1000; i > 0; i--)
+    {
+      obram_read(ioaddr, scboff(OFFSET_SCB, scb_command),
+                (unsigned char *) &scb_cs, sizeof(scb_cs));
+      if (scb_cs == 0)
+       break;
+
+      udelay(10);
+    }
+
+  if(i <= 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wavelan_cu_start(): board not accepting command.\n",
+            dev->name);
+#endif
+      return -1;
+    }
+
+  lp->tx_n_in_use = 0;
+  dev->tbusy = 0;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_cu_start()\n", dev->name);
+#endif
+  return 0;
+}
 
-               (void)wavelan_hardware_reset(dev);
-       }
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a standard config of the WaveLAN controler (i82586).
+ *
+ * It initialise the scp, iscp and scb structure
+ * The two first are only pointer to the next.
+ * The last one is used for basic configuration and for basic
+ * communication (interrupt status)
+ *
+ * (called by wv_hw_reset())
+ */
+static inline int
+wv_82586_start(device *        dev)
+{
+  net_local *  lp = (net_local *) dev->priv;
+  u_short      ioaddr = dev->base_addr;
+  scp_t                scp;            /* system configuration pointer */
+  iscp_t       iscp;           /* intermediate scp */
+  scb_t                scb;            /* system control block */
+  ach_t                cb;             /* Action command header */
+  u_char       zeroes[512];
+  int          i;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_82586_start()\n", dev->name);
+#endif
+
+  /*
+   * Clear the onboard RAM.
+   */
+  memset(&zeroes[0], 0x00, sizeof(zeroes));
+  for(i = 0; i < I82586_MEMZ; i += sizeof(zeroes))
+    obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes));
+
+  /*
+   * Construct the command unit structures:
+   * scp, iscp, scb, cb.
+   */
+  memset(&scp, 0x00, sizeof(scp));
+  scp.scp_sysbus = SCP_SY_16BBUS;
+  scp.scp_iscpl = OFFSET_ISCP;
+  obram_write(ioaddr, OFFSET_SCP, (unsigned char *)&scp, sizeof(scp));
+
+  memset(&iscp, 0x00, sizeof(iscp));
+  iscp.iscp_busy = 1;
+  iscp.iscp_offset = OFFSET_SCB;
+  obram_write(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
+
+  /* Our first command is to reset the i82586 */
+  memset(&scb, 0x00, sizeof(scb));
+  scb.scb_command = SCB_CMD_RESET;
+  scb.scb_cbl_offset = OFFSET_CU;
+  scb.scb_rfa_offset = OFFSET_RU;
+  obram_write(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+
+  set_chan_attn(ioaddr, lp->hacr);
+
+  /* Wait for command to finish */
+  for(i = 1000; i > 0; i--)
+    {
+      obram_read(ioaddr, OFFSET_ISCP, (unsigned char *) &iscp, sizeof(iscp));
+
+      if(iscp.iscp_busy == (unsigned short) 0)
+       break;
+
+      udelay(10);
+    }
+
+  if(i <= 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wv_82586_start(): iscp_busy timeout.\n",
+            dev->name);
+#endif
+      return -1;
+    }
+
+  /* Check command completion */
+  for(i = 15; i > 0; i--)
+    {
+      obram_read(ioaddr, OFFSET_SCB, (unsigned char *) &scb, sizeof(scb));
+
+      if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA))
+       break;
+
+      udelay(10);
+    }
+
+  if (i <= 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wv_82586_start(): status: expected 0x%02x, got 0x%02x.\n",
+            dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status);
+#endif
+      return -1;
+    }
+
+  wv_ack(dev);
+
+  /* Set the action command header */
+  memset(&cb, 0x00, sizeof(cb));
+  cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose);
+  cb.ac_link = OFFSET_CU;
+  obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+
+  if(wv_synchronous_cmd(dev, "diag()") == -1)
+    return -1;
+
+  obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+  if(cb.ac_status & AC_SFLD_FAIL)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wv_82586_start(): i82586 Self Test failed.\n",
+            dev->name);
+#endif
+      return -1;
+    }
+
+#ifdef DEBUG_I82586_SHOW
+  wv_scb_show(ioaddr);
+#endif
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_82586_start()\n", dev->name);
+#endif
+  return 0;
+}
 
-       if
-       (
-               (status & SCB_ST_RNR) == SCB_ST_RNR
-               ||
-               (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && dev->start)
-       )
-       {
-               printk("%s: warning: RU not ready -- restarting.\n", dev->name);
+/*------------------------------------------------------------------*/
+/*
+ * This routine does a standard config of the WaveLAN controler (i82586).
+ *
+ * This routine is a violent hack. We use the first free transmit block
+ * to make our configuration. In the buffer area, we create the three
+ * configure command (linked). We make the previous nop point to the
+ * beggining of the buffer instead of the tx command. After, we go as
+ * usual to the nop command...
+ * Note that only the last command (mc_set) will generate an interrupt...
+ *
+ * (called by wv_hw_reset(), wv_82586_reconfig())
+ */
+static void
+wv_82586_config(device *       dev)
+{
+  net_local *          lp = (net_local *) dev->priv;
+  u_short              ioaddr = dev->base_addr;
+  unsigned short       txblock;
+  unsigned short       txpred;
+  unsigned short       tx_addr;
+  unsigned short       nop_addr;
+  unsigned short       tbd_addr;
+  unsigned short       cfg_addr;
+  unsigned short       ias_addr;
+  unsigned short       mcs_addr;
+  ac_tx_t              tx;
+  ac_nop_t             nop;
+  ac_cfg_t             cfg;            /* Configure action */
+  ac_ias_t             ias;            /* IA-setup action */
+  ac_mcs_t             mcs;            /* Multicast setup */
+  struct dev_mc_list * dmi;
+  unsigned long                x;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_82586_config()\n", dev->name);
+#endif
+
+  x = wv_splhi();
+
+  /* Calculate addresses of next block and previous block */
+  txblock = lp->tx_first_free;
+  txpred = txblock - TXBLOCKZ;
+  if(txpred < OFFSET_CU)
+    txpred += NTXBLOCKS * TXBLOCKZ;
+  lp->tx_first_free += TXBLOCKZ;
+  if(lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+    lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
+
+  lp->tx_n_in_use++;
+
+  /* Calculate addresses of the differents part of the block */
+  tx_addr = txblock;
+  nop_addr = tx_addr + sizeof(tx);
+  tbd_addr = nop_addr + sizeof(nop);
+  cfg_addr = tbd_addr + sizeof(tbd_t); /* beggining of the buffer */
+  ias_addr = cfg_addr + sizeof(cfg);
+  mcs_addr = ias_addr + sizeof(ias);
+
+  /*
+   * Transmit command.
+   */
+  tx.tx_h.ac_status = 0xFFFF;  /* Fake completion value */
+  obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status),
+             (unsigned char *) &tx.tx_h.ac_status,
+             sizeof(tx.tx_h.ac_status));
+
+  /*
+   * NOP command.
+   */
+  nop.nop_h.ac_status = 0;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+             (unsigned char *) &nop.nop_h.ac_status,
+             sizeof(nop.nop_h.ac_status));
+  nop.nop_h.ac_link = nop_addr;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+             (unsigned char *) &nop.nop_h.ac_link,
+             sizeof(nop.nop_h.ac_link));
+
+  /* Create a configure action */
+  memset(&cfg, 0x00, sizeof(cfg));
 
-               (void)wavelan_hardware_reset(dev);
-       }
+#if    0
+  /*
+   * The default board configuration.
+   */
+  cfg.fifolim_bytecnt  = 0x080c;
+  cfg.addrlen_mode     = 0x2600;
+  cfg.linprio_interframe       = 0x7820;       /* IFS=120, ACS=2 */
+  cfg.slot_time        = 0xf00c;       /* slottime=12    */
+  cfg.hardware         = 0x0008;       /* tx even w/o CD */
+  cfg.min_frame_len    = 0x0040;
+#endif /* 0 */
 
-       dev->interrupt = 0;
+  /*
+   * For Linux we invert AC_CFG_ALOC(..) so as to conform
+   * to the way that net packets reach us from above.
+   * (See also ac_tx_t.)
+   */
+  cfg.cfg_byte_cnt = AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t));
+  cfg.cfg_fifolim = AC_CFG_FIFOLIM(8);
+  cfg.cfg_byte8 = AC_CFG_SAV_BF(0) |
+                 AC_CFG_SRDY(0);
+  cfg.cfg_byte9 = AC_CFG_ELPBCK(0) |
+                 AC_CFG_ILPBCK(0) |
+                 AC_CFG_PRELEN(AC_CFG_PLEN_2) |
+                 AC_CFG_ALOC(1) |
+                 AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE);
+  cfg.cfg_byte10 = AC_CFG_BOFMET(0) |
+                  AC_CFG_ACR(0) |
+                  AC_CFG_LINPRIO(0);
+  cfg.cfg_ifs = 32;
+  cfg.cfg_slotl = 0;
+  cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) |
+                  AC_CFG_SLTTMHI(2);
+  cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
+                  AC_CFG_BTSTF(0) |
+                  AC_CFG_CRC16(0) |
+                  AC_CFG_NCRC(0) |
+                  AC_CFG_TNCRS(1) |
+                  AC_CFG_MANCH(0) |
+                  AC_CFG_BCDIS(0) |
+                  AC_CFG_PRM(lp->promiscuous);
+  cfg.cfg_byte15 = AC_CFG_ICDS(0) |
+                  AC_CFG_CDTF(0) |
+                  AC_CFG_ICSS(0) |
+                  AC_CFG_CSTF(0);
+/*
+  cfg.cfg_min_frm_len = AC_CFG_MNFRM(64);
+*/
+  cfg.cfg_min_frm_len = AC_CFG_MNFRM(8);
+
+  cfg.cfg_h.ac_command = (AC_CFLD_CMD & acmd_configure);
+  cfg.cfg_h.ac_link = ias_addr;
+  obram_write(ioaddr, cfg_addr, (unsigned char *)&cfg, sizeof(cfg));
+
+  /* Setup the MAC address */
+  memset(&ias, 0x00, sizeof(ias));
+  ias.ias_h.ac_command = (AC_CFLD_CMD & acmd_ia_setup);
+  ias.ias_h.ac_link = mcs_addr;
+  memcpy(&ias.ias_addr[0], (unsigned char *)&dev->dev_addr[0], sizeof(ias.ias_addr));
+  obram_write(ioaddr, ias_addr, (unsigned char *)&ias, sizeof(ias));
+
+  /* Initialize adapter's ethernet multicast addresses */
+  memset(&mcs, 0x00, sizeof(mcs));
+  mcs.mcs_h.ac_command = AC_CFLD_I | (AC_CFLD_CMD & acmd_mc_setup);
+  mcs.mcs_h.ac_link = nop_addr;
+  mcs.mcs_cnt = WAVELAN_ADDR_SIZE * lp->mc_count;
+  obram_write(ioaddr, mcs_addr, (unsigned char *)&mcs, sizeof(mcs));
+
+  /* If any address to set */
+  if(lp->mc_count)
+    {
+      for(dmi=dev->mc_list; dmi; dmi=dmi->next)
+       outsw(PIOP1(ioaddr), (u_short *) dmi->dmi_addr,
+             WAVELAN_ADDR_SIZE >> 1);
+
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "%s: wv_82586_config(): set %d multicast addresses:\n",
+            dev->name, lp->mc_count);
+      for(dmi=dev->mc_list; dmi; dmi=dmi->next)
+       printk(KERN_DEBUG " %02x:%02x:%02x:%02x:%02x:%02x\n",
+              dmi->dmi_addr[0], dmi->dmi_addr[1], dmi->dmi_addr[2],
+              dmi->dmi_addr[3], dmi->dmi_addr[4], dmi->dmi_addr[5] );
+#endif
+    }
+
+  /*
+   * Overwrite the predecessor NOP link
+   * so that it points to the configure action.
+   */
+  nop_addr = txpred + sizeof(tx);
+  nop.nop_h.ac_status = 0;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status),
+             (unsigned char *)&nop.nop_h.ac_status,
+             sizeof(nop.nop_h.ac_status));
+  nop.nop_h.ac_link = cfg_addr;
+  obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link),
+             (unsigned char *) &nop.nop_h.ac_link,
+             sizeof(nop.nop_h.ac_link));
+
+  /* If watchdog not already active, activate it... */
+  if(lp->watchdog.prev == (timer_list *) NULL)
+    {
+      /* set timer to expire in WATCHDOG_JIFFIES */
+      lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
+      add_timer(&lp->watchdog);
+    }
+
+  lp->reconfig_82586 = 0;
+
+  if(lp->tx_first_in_use == I82586NULL)
+    lp->tx_first_in_use = txblock;
+
+  if(lp->tx_n_in_use < NTXBLOCKS - 1)
+    dev->tbusy = 0;
+
+  wv_splx(x);
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_82586_config()\n", dev->name);
+#endif
 }
 
-static
-int
-wavelan_close(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * This routine stop gracefully the WaveLAN controler (i82586).
+ * (called by wavelan_close())
+ */
+static inline void
+wv_82586_stop(device * dev)
 {
-       unsigned short  ioaddr;
-       net_local       *lp;
-       unsigned short  scb_cmd;
+  net_local *  lp = (net_local *) dev->priv;
+  u_short      ioaddr = dev->base_addr;
+  u_short      scb_cmd;
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_82586_stop()\n", dev->name);
+#endif
+
+  /* Suspend both command unit and receive unit */
+  scb_cmd = (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & SCB_CMD_RUC_SUS);
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+             (unsigned char *)&scb_cmd, sizeof(scb_cmd));
+  set_chan_attn(ioaddr, lp->hacr);
+
+  /* No more interrupts */
+  wv_ints_off(dev);
+
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_82586_stop()\n", dev->name);
+#endif
+}
 
-       if (wavelan_debug > 0)
-               printk("%s: ->wavelan_close(dev=0x%x)\n", dev->name, (unsigned int)dev);
+/*------------------------------------------------------------------*/
+/*
+ * Totally reset the wavelan and restart it.
+ * Performs the following actions:
+ *     1. A power reset (reset DMA)
+ *     2. Initialize the radio modem (using wv_mmc_init)
+ *     3. Reset & Configure LAN controller (using wv_82586_start)
+ *     4. Start the LAN controller's command unit
+ *     5. Start the LAN controller's receive unit
+ */
+static int
+wv_hw_reset(device *   dev)
+{
+  net_local *  lp = (net_local *)dev->priv;
+  u_short      ioaddr = dev->base_addr;
 
-       ioaddr = dev->base_addr;
-       lp = (net_local *)dev->priv;
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: ->wv_hw_reset(dev=0x%x)\n", dev->name,
+        (unsigned int)dev);
+#endif
 
-       dev->tbusy = 1;
-       dev->start = 0;
+  /* If watchdog was activated, kill it ! */
+  if(lp->watchdog.prev != (timer_list *) NULL)
+    del_timer(&lp->watchdog);
 
-       /*
-        * Flush the Tx and disable Rx.
-        */
-       scb_cmd = (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & SCB_CMD_RUC_SUS);
-       obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cmd, sizeof(scb_cmd));
-       set_chan_attn(ioaddr, lp->hacr);
+  /* Increase the number of resets done */
+  lp->nresets++;
 
-       wavelan_ints_off(dev);
+  wv_hacr_reset(ioaddr);
+  lp->hacr = HACR_DEFAULT;
 
-       free_irq(dev->irq, NULL);
-       irq2dev_map[dev->irq] = (device *)0;
+  if((wv_mmc_init(dev) < 0) ||
+     (wv_82586_start(dev) < 0))
+    return -1;
 
-       /*
-        * Release the ioport-region.
-        */
-       release_region(ioaddr, sizeof(ha_t));
+  /* Enable the card to send interrupts */
+  wv_ints_on(dev);
 
-       MOD_DEC_USE_COUNT;
+  /* Start card functions */
+  if((wv_ru_start(dev) < 0) ||
+     (wv_cu_start(dev) < 0))
+    return -1;
 
-       if (wavelan_debug > 0)
-               printk("%s: <-wavelan_close(): 0\n", dev->name);
+  /* Finish configuration */
+  wv_82586_config(dev);
 
-       return 0;
+#ifdef DEBUG_CONFIG_TRACE
+  printk(KERN_DEBUG "%s: <-wv_hw_reset()\n", dev->name);
+#endif
+  return 0;
 }
 
+/*------------------------------------------------------------------*/
 /*
- * Get the current statistics.
- * This may be called with the card open or closed.
+ * Check if there is a wavelan at the specific base address.
+ * As a side effect, it read the MAC address.
+ * (called in wavelan_probe() and init_module())
  */
-static
-en_stats       *
-wavelan_get_stats(device *dev)
+static int
+wv_check_ioaddr(u_short                ioaddr,
+               u_char *        mac)
 {
-       net_local       *lp;
+  int          i;              /* Loop counter */
+
+  /* Check if the base address if available */
+  if(check_region(ioaddr, sizeof(ha_t)))
+    return  EADDRINUSE;                /* ioaddr already used... */
+
+  /* Reset host interface */
+  wv_hacr_reset(ioaddr);
+
+  /* Read the MAC address from the parameter storage area */
+  psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_univ_mac_addr),
+          mac, 6);
+
+  /*
+   * Check the first three octets of the addr for the manufacturer's code.
+   * Note: If you can't find your wavelan card, you've got a
+   * non-NCR/AT&T/Lucent ISA cards, see wavelan.p.h for detail on
+   * how to configure your card...
+   */
+  for(i = 0; i < (sizeof(MAC_ADDRESSES) / sizeof(char) / 3); i++)
+    if((mac[0] == MAC_ADDRESSES[i][0]) &&
+       (mac[1] == MAC_ADDRESSES[i][1]) &&
+       (mac[2] == MAC_ADDRESSES[i][2]))
+      return 0;
+
+#ifdef DEBUG_CONFIG_INFO
+  printk(KERN_WARNING "Wavelan (0x%3X) : Your MAC address might be : %02X:%02X:%02X...\n",
+        ioaddr, mac[0], mac[1], mac[2]);
+#endif
+    return ENODEV;
+}
 
-       lp = (net_local *)dev->priv;
+/************************ INTERRUPT HANDLING ************************/
 
-       return &lp->stats;
+/*
+ * This function is the interrupt handler for the WaveLAN card. This
+ * routine will be called whenever: 
+ */
+static void
+wavelan_interrupt(int                  irq,
+                 void *                dev_id,
+                 struct pt_regs *      regs)
+{
+  device *     dev;
+  u_short      ioaddr;
+  net_local *  lp;
+  u_short      hasr;
+  u_short      status;
+  u_short      ack_cmd;
+
+  if((dev = (device *) (irq2dev_map[irq])) == (device *) NULL)
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_WARNING "wavelan_interrupt(): irq %d for unknown device.\n",
+            irq);
+#endif
+      return;
+    }
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_interrupt()\n", dev->name);
+#endif
+
+  lp = (net_local *) dev->priv;
+  ioaddr = dev->base_addr;
+
+  /* Prevent reentrance. What should we do here ? */
+#ifdef DEBUG_INTERRUPT_ERROR
+  if(dev->interrupt)
+    printk(KERN_INFO "%s: wavelan_interrupt(): Re-entering the interrupt handler.\n",
+          dev->name);
+#endif
+  dev->interrupt = 1;
+
+  if((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR)
+    {
+      u_char   dce_status;
+
+      /*
+       * Interrupt from the modem management controller.
+       * This will clear it -- ignored for now.
+       */
+      mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, sizeof(dce_status));
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "%s: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n",
+            dev->name, dce_status);
+#endif
+    }
+
+  if((hasr & HASR_82586_INTR) == 0)
+    {
+      dev->interrupt = 0;
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "%s: wavelan_interrupt(): interrupt not coming from i82586\n",
+            dev->name);
+#endif
+      return;
+    }
+
+  /* Read interrupt data */
+  obram_read(ioaddr, scboff(OFFSET_SCB, scb_status),
+            (unsigned char *) &status, sizeof(status));
+
+  /*
+   * Acknowledge the interrupt(s).
+   */
+  ack_cmd = status & SCB_ST_INT;
+  obram_write(ioaddr, scboff(OFFSET_SCB, scb_command),
+             (unsigned char *) &ack_cmd, sizeof(ack_cmd));
+  set_chan_attn(ioaddr, lp->hacr);
+
+#ifdef DEBUG_INTERRUPT_INFO
+  printk(KERN_DEBUG "%s: wavelan_interrupt(): status 0x%04x.\n",
+        dev->name, status);
+#endif
+
+  /* Command completed. */
+  if((status & SCB_ST_CX) == SCB_ST_CX)
+    {
+#ifdef DEBUG_INTERRUPT_INFO
+      printk(KERN_DEBUG "%s: wavelan_interrupt(): command completed.\n",
+            dev->name);
+#endif
+      wv_complete(dev, ioaddr, lp);
+
+      /* If watchdog was activated, kill it ! */
+      if(lp->watchdog.prev != (timer_list *) NULL)
+       del_timer(&lp->watchdog);
+      if(lp->tx_n_in_use > 0)
+       {
+         /* set timer to expire in WATCHDOG_JIFFIES */
+         lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
+         add_timer(&lp->watchdog);
+       }
+    }
+
+  /* Frame received. */
+  if((status & SCB_ST_FR) == SCB_ST_FR)
+    {
+#ifdef DEBUG_INTERRUPT_INFO
+      printk(KERN_DEBUG "%s: wavelan_interrupt(): received packet.\n",
+            dev->name);
+#endif
+      wv_receive(dev);
+    }
+
+  /* Check the state of the command unit */
+  if(((status & SCB_ST_CNA) == SCB_ST_CNA) ||
+     (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && dev->start))
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "%s: wavelan_interrupt(): CU inactive -- restarting\n",
+            dev->name);
+#endif
+      wv_hw_reset(dev);
+    }
+
+  /* Check the state of the command unit */
+  if(((status & SCB_ST_RNR) == SCB_ST_RNR) ||
+     (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && dev->start))
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "%s: wavelan_interrupt(): RU not ready -- restarting\n",
+            dev->name);
+#endif
+      wv_hw_reset(dev);
+    }
+
+  dev->interrupt = 0;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_interrupt()\n", dev->name);
+#endif
 }
 
-static
-void
-wavelan_set_multicast_list(device *dev)
+/*------------------------------------------------------------------*/
+/*
+ * Watchdog : when we start a transmission, we set a timer in the
+ * kernel.  If the transmission complete, this timer is disabled. If
+ * it expire, it try to unlock the hardware.
+ *
+ * Note : this watchdog doesn't work on the same principle as the
+ * watchdog in the previous version of the ISA driver. I make it this
+ * way because the overhead of add_timer() and del_timer() is nothing
+ * and that it avoid calling the watchdog, saving some CPU...
+ */
+static void
+wavelan_watchdog(u_long                a)
 {
-       net_local       *lp;
-       unsigned long   x;
-
-       if (wavelan_debug > 0)
-               printk("%s: ->wavelan_set_multicast_list(dev=%p)", dev->name, dev);
-
-       lp = (net_local *)dev->priv;
+  device *             dev;
+  net_local *          lp;
+  unsigned short       ioaddr;
+  unsigned long                x;
+  unsigned int         nreaped;
+
+  dev = (device *) a;
+  ioaddr = dev->base_addr;
+  lp = (net_local *) dev->priv;
+
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_watchdog()\n", dev->name);
+#endif
+
+#ifdef DEBUG_INTERRUPT_ERROR
+  printk(KERN_INFO "%s: wavelan_watchdog: watchdog timer expired\n",
+        dev->name);
+#endif
+
+  x = wv_splhi();
+
+  dev = (device *) a;
+  ioaddr = dev->base_addr;
+  lp = (net_local *) dev->priv;
+
+  if(lp->tx_n_in_use <= 0)
+    {
+      wv_splx(x);
+      return;
+    }
+
+  nreaped = wv_complete(dev, ioaddr, lp);
+
+#ifdef DEBUG_INTERRUPT_INFO
+  printk(KERN_DEBUG "%s: wavelan_watchdog(): %d reaped, %d remain.\n",
+        dev->name, nreaped, lp->tx_n_in_use);
+#endif
+
+#ifdef DEBUG_PSA_SHOW
+  {
+    psa_t              psa;
+    psa_read(dev, 0, (unsigned char *) &psa, sizeof(psa));
+    wv_psa_show(&psa);
+  }
+#endif
+#ifdef DEBUG_MMC_SHOW
+  wv_mmc_show(dev);
+#endif
+#ifdef DEBUG_I82586_SHOW
+  wv_cu_show(dev);
+#endif
+
+  /* If no buffer has been freed */
+  if(nreaped == 0)
+    {
+#ifdef DEBUG_INTERRUPT_ERROR
+      printk(KERN_INFO "%s: wavelan_watchdog(): cleanup failed, trying reset\n",
+            dev->name);
+#endif
+      wv_hw_reset(dev);
+    }
+  else
+    /* Re-set watchodog for next transmission */
+    if(lp->tx_n_in_use > 0)
+      {
+       /* set timer to expire in WATCHDOG_JIFFIES */
+       lp->watchdog.expires = jiffies + WATCHDOG_JIFFIES;
+       add_timer(&lp->watchdog);
+      }
 
-       if(dev->flags&IFF_PROMISC)
-       {
-               /*
-                * Promiscuous mode: receive all packets.
-                */
-               lp->promiscuous = 1;
-               x = wavelan_splhi();
-               (void)wavelan_hardware_reset(dev);
-               wavelan_splx(x);
-       }
-#if MULTICAST_IS_ADDED 
-       else if((dev->flags&IFF_ALLMULTI)||dev->mc_list)
-       {
-                       
-       
-       }
-#endif 
-       else    
-       {
-               /*
-                * Normal mode: disable promiscuous mode,
-                * clear multicast list.
-                */
-               lp->promiscuous = 0;
-               x = wavelan_splhi();
-               (void)wavelan_hardware_reset(dev);
-               wavelan_splx(x);
-       }
+  wv_splx(x);
 
-       if (wavelan_debug > 0)
-               printk("%s: <-wavelan_set_multicast_list()\n", dev->name);
+#ifdef DEBUG_INTERRUPT_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_watchdog()\n", dev->name);
+#endif
 }
 
+/********************* CONFIGURATION CALLBACKS *********************/
 /*
- * Extra WaveLAN-specific device data.
- * "cat /proc/net/wavelan" -- see fs/proc/net.c.
+ * Here are the functions called by the linux networking (NET3) for
+ * initialization, configuration and deinstallations of the Wavelan
+ * ISA Hardware.
  */
-static
-int
-sprintf_stats(char *buffer, device *dev)
-{
-       net_local       *lp;
-       unsigned char   v;
-       mmr_t           m;
-
-       lp = (net_local *)dev->priv;
-
-       if (lp == (net_local *)0)
-               return sprintf(buffer, "%6s: No statistics available.\n", dev->name);
-
-       v = (unsigned char)1;
-       mmc_write(dev->base_addr, mmwoff(0, mmw_freeze), &v, sizeof(v));
-
-       mmc_read(dev->base_addr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, sizeof(m.mmr_dce_status));
-       mmc_read(dev->base_addr, mmroff(0, mmr_correct_nwid_h), &m.mmr_correct_nwid_h, sizeof(m.mmr_correct_nwid_h));
-       mmc_read(dev->base_addr, mmroff(0, mmr_correct_nwid_l), &m.mmr_correct_nwid_l, sizeof(m.mmr_correct_nwid_l));
-       mmc_read(dev->base_addr, mmroff(0, mmr_wrong_nwid_h), &m.mmr_wrong_nwid_h, sizeof(m.mmr_wrong_nwid_h));
-       mmc_read(dev->base_addr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, sizeof(m.mmr_wrong_nwid_l));
-       mmc_read(dev->base_addr, mmroff(0, mmr_signal_lvl), &m.mmr_signal_lvl, sizeof(m.mmr_signal_lvl));
-       mmc_read(dev->base_addr, mmroff(0, mmr_silence_lvl), &m.mmr_silence_lvl, sizeof(m.mmr_silence_lvl));
-       mmc_read(dev->base_addr, mmroff(0, mmr_sgnl_qual), &m.mmr_sgnl_qual, sizeof(m.mmr_sgnl_qual));
-
-       v = (unsigned char)0;
-       mmc_write(dev->base_addr, mmwoff(0, mmw_freeze), &v, sizeof(v));
-
-       lp->correct_nwid += (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l;
-       lp->wrong_nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
-
-       return sprintf
-       (
-               buffer,
-               "%6s:   %02x %08x %08x   %02x   %02x   %02x   %02x    %u\n",
-               dev->name,
-               m.mmr_dce_status,
-               lp->correct_nwid,
-               lp->wrong_nwid,
-               m.mmr_signal_lvl,
-               m.mmr_silence_lvl,
-               m.mmr_sgnl_qual,
-               lp->tx_n_in_use,
-               lp->nresets
-       );
+
+/*------------------------------------------------------------------*/
+/*
+ * Configure and start up the WaveLAN PCMCIA adaptor.
+ * Called by NET3 when it "open" the device.
+ */
+static int
+wavelan_open(device *  dev)
+{
+  u_long       x;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_open(dev=0x%x)\n", dev->name,
+        (unsigned int) dev);
+#endif
+
+  /* Check irq */
+  if(dev->irq == 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "%s: wavelan_open(): no irq\n", dev->name);
+#endif
+      return -ENXIO;
+    }
+
+  if((irq2dev_map[dev->irq] != (device *) NULL) ||
+     /* This is always true, but avoid the false IRQ. */
+     ((irq2dev_map[dev->irq] = dev) == (device *) NULL) ||
+     (request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN", NULL) != 0))
+    {
+      irq2dev_map[dev->irq] = (device *) NULL;
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "%s: wavelan_open(): invalid irq\n", dev->name);
+#endif
+      return -EAGAIN;
+    }
+
+  x = wv_splhi();
+  if(wv_hw_reset(dev) != -1)
+    {
+      dev->interrupt = 0;
+      dev->start = 1;
+    }
+  else
+    {
+      free_irq(dev->irq, NULL);
+      irq2dev_map[dev->irq] = (device *) NULL;
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_INFO "%s: wavelan_open(): impossible to start the card\n",
+            dev->name);
+#endif
+      return -EAGAIN;
+    }
+  wv_splx(x);
+
+  MOD_INC_USE_COUNT;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_open()\n", dev->name);
+#endif
+  return 0;
 }
 
+/*------------------------------------------------------------------*/
+/*
+ * Shutdown the WaveLAN ISA card.
+ * Called by NET3 when it "close" the device.
+ */
 static int
-wavelan_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+wavelan_close(device * dev)
 {
-       int             len;
-       off_t           begin;
-       off_t           pos;
-       int             size;
-       unsigned long   x;
+  net_local *  lp = (net_local *)dev->priv;
 
-       len = 0;
-       begin = 0;
-       pos = 0;
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_close(dev=0x%x)\n", dev->name,
+        (unsigned int) dev);
+#endif
 
-       size = sprintf(buffer, "%s", "Iface |  dce    +nwid    -nwid  lvl slnc qual ntxq nrst\n");
+  /* Not do the job twice... */
+  if(dev->start == 0)
+    return 0;
 
-       pos += size;
-       len += size;
-       
-       x = wavelan_splhi();
+  dev->tbusy = 1;
+  dev->start = 0;
 
-       if (first_wavelan != (net_local *)0)
-       {
-               net_local       *lp;
-
-               lp = first_wavelan;
-               do
-               {
-                       size = sprintf_stats(buffer + len, lp->dev);
-
-                       len += size;
-                       pos = begin + len;
-                                       
-                       if (pos < offset)
-                       {
-                               len = 0;
-                               begin = pos;
-                       }
-
-                       if (pos > offset + length)
-                               break;
-               }
-                       while ((lp = lp->next) != first_wavelan);
-       }
+  /* If watchdog was activated, kill it ! */
+  if(lp->watchdog.prev != (timer_list *) NULL)
+    del_timer(&lp->watchdog);
 
-       wavelan_splx(x);
+  /*
+   * Flush the Tx and disable Rx.
+   */
+  wv_82586_stop(dev);
 
-       *start = buffer + (offset - begin);     /* Start of wanted data */
-       len -= (offset - begin);                /* Start slop */
-       if (len > length)
-               len = length;                   /* Ending slop */
+  free_irq(dev->irq, NULL);
+  irq2dev_map[dev->irq] = (device *) NULL;
 
-       return len;
-}
+  MOD_DEC_USE_COUNT;
 
-#if    defined(MODULE)
-static char            devicename[9]           = { 0, };
-static struct device   dev_wavelan             =
-{
-       devicename, /* device name is inserted by linux/drivers/net/net_init.c */
-       0, 0, 0, 0,
-       0, 0,
-       0, 0, 0, NULL, wavelan_probe
-};
-
-static int io = 0x390; /* Default from above.. */
-static int irq = 0;
-
-int
-init_module(void)
-{
-       dev_wavelan.base_addr = io;
-       dev_wavelan.irq       = irq;
-       if (register_netdev(&dev_wavelan) != 0)
-               return -EIO;
-
-       return 0;
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_close()\n", dev->name);
+#endif
+  return 0;
 }
 
-void
-cleanup_module(void)
+/*------------------------------------------------------------------*/
+/*
+ * Probe an i/o address, and if the wavelan is there configure the
+ * device structure
+ * (called by wavelan_probe() & via init_module())
+ */
+static int
+wavelan_config(device *        dev)
 {
-       proc_net_unregister(PROC_NET_WAVELAN);
-       unregister_netdev(&dev_wavelan);
-       kfree_s(dev_wavelan.priv, sizeof(struct net_local));
-       dev_wavelan.priv = NULL;
+  u_short      ioaddr = dev->base_addr;
+  u_char       irq_mask;
+  int          irq;
+  net_local *  lp;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_config(dev=0x%x, ioaddr=0x%x)\n", dev->name,
+        (unsigned int)dev, ioaddr);
+#endif
+
+  /* Check irq arg on command line */
+  if(dev->irq != 0)
+    {
+      irq_mask = wv_irq_to_psa(dev->irq);
+
+      if(irq_mask == 0)
+       {
+#ifdef DEBUG_CONFIG_ERROR
+         printk(KERN_WARNING "%s: wavelan_config(): invalid irq %d -- ignored.\n",
+                dev->name, dev->irq);
+#endif
+         dev->irq = 0;
+       }
+      else
+       {
+#ifdef DEBUG_CONFIG_INFO
+         printk(KERN_DEBUG "%s: wavelan_config(): changing irq to %d\n",
+                dev->name, dev->irq);
+#endif
+         psa_write(ioaddr, HACR_DEFAULT,
+                   psaoff(0, psa_int_req_no), &irq_mask, 1);
+         wv_hacr_reset(ioaddr);
+       }
+    }
+
+  psa_read(ioaddr, HACR_DEFAULT, psaoff(0, psa_int_req_no), &irq_mask, 1);
+  if((irq = wv_psa_to_irq(irq_mask)) == -1)
+    {
+#ifdef DEBUG_CONFIG_ERROR
+      printk(KERN_INFO "%s: wavelan_config(): could not wavelan_map_irq(%d).\n",
+            dev->name, irq_mask);
+#endif
+      return EAGAIN;
+    }
+
+  dev->irq = irq;
+
+  request_region(ioaddr, sizeof(ha_t), "wavelan");
+
+  dev->mem_start = 0x0000;
+  dev->mem_end = 0x0000;
+  dev->if_port = 0;
+
+  /* Initialize device structures */
+  dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL);
+  if(dev->priv == NULL)
+    return -ENOMEM;
+  memset(dev->priv, 0x00, sizeof(net_local));
+  lp = (net_local *)dev->priv;
+
+  /* Back link to the device structure */
+  lp->dev = dev;
+  /* Add the device at the beggining of the linked list */
+  lp->next = wavelan_list;
+  wavelan_list = lp;
+
+  lp->hacr = HACR_DEFAULT;
+
+  lp->watchdog.function = wavelan_watchdog;
+  lp->watchdog.data = (unsigned long) dev;
+  lp->promiscuous = 0;
+  lp->mc_count = 0;
+
+  /*
+   * Fill in the fields of the device structure
+   * with ethernet-generic values.
+   */
+  ether_setup(dev);
+
+  dev->open = wavelan_open;
+  dev->stop = wavelan_close;
+  dev->hard_start_xmit = wavelan_packet_xmit;
+  dev->get_stats = wavelan_get_stats;
+  dev->set_multicast_list = &wavelan_set_multicast_list;
+  dev->set_mac_address = &wavelan_set_mac_address;
+
+#ifdef WIRELESS_EXT    /* If wireless extension exist in the kernel */
+  dev->do_ioctl = wavelan_ioctl;
+  dev->get_wireless_stats = wavelan_get_wireless_stats;
+#endif
+
+  dev->mtu = WAVELAN_MTU;
+
+  /* Display nice info */
+  wv_init_info(dev);
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: <-wavelan_config()\n", dev->name);
+#endif
+  return 0;
 }
-#endif /* defined(MODULE) */
-
-static
-void
-wavelan_cu_show_one(device *dev, net_local *lp, int i, unsigned short p)
-{
-       unsigned short  ioaddr;
-       ac_tx_t         actx;
-
-       ioaddr = dev->base_addr;
-
-       printk("%d: 0x%x:", i, p);
-
-       obram_read(ioaddr, p, (unsigned char *)&actx, sizeof(actx));
-       printk(" status=0x%x,", actx.tx_h.ac_status);
-       printk(" command=0x%x,", actx.tx_h.ac_command);
 
+/*------------------------------------------------------------------*/
 /*
+ * Check for a network adaptor of this type.
+ * Return '0' iff one exists.
+ * (There seem to be different interpretations of
+ * the initial value of dev->base_addr.
+ * We follow the example in drivers/net/ne.c.)
+ * (called in "Space.c")
+ * As this function is called outside the wavelan module, it should be
+ * declared extern, but it seem to cause troubles...
+ */
+/* extern */ int
+wavelan_probe(device * dev)
+{
+  short                base_addr;
+  mac_addr     mac;            /* Mac address (check wavelan existence) */
+  int          i;
+  int          r;
+
+#ifdef DEBUG_CALLBACK_TRACE
+  printk(KERN_DEBUG "%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n",
+        dev->name, (unsigned int)dev, (unsigned int)dev->base_addr);
+#endif
+
+#ifdef STRUCT_CHECK
+  if (wv_struct_check() != (char *) NULL)
+    {
+      printk(KERN_WARNING "%s: wavelan_probe(): structure/compiler botch: \"%s\"\n",
+            dev->name, wv_struct_check());
+      return ENODEV;
+    }
+#endif /* STRUCT_CHECK */
+
+  /* Check the value of the command line parameter for base address */
+  base_addr = dev->base_addr;
+
+  /* Don't probe at all. */
+  if(base_addr < 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "%s: wavelan_probe(): invalid base address\n",
+            dev->name);
+#endif
+      return ENXIO;
+    }
+
+  /* Check a single specified location. */
+  if(base_addr > 0x100)
+    {
+      /* Check if the is something at this base address */
+      if((r = wv_check_ioaddr(base_addr, mac)) == 0)
        {
-               tbd_t   tbd;
+         memcpy(dev->dev_addr, mac, 6);        /* Copy mac address */
+         r = wavelan_config(dev);
+       }
 
-               obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd));
-               printk(" tbd_status=0x%x,", tbd.tbd_status);
+#ifdef DEBUG_CONFIG_INFO
+      if(r != 0)
+       printk(KERN_DEBUG "%s: wavelan_probe(): no device at specified base address (0x%X) or address already in use\n",
+              dev->name, base_addr);
+#endif
+
+#ifdef DEBUG_CALLBACK_TRACE
+      printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name);
+#endif
+      return r;
+    }
+
+  /* Scan all possible address of the wavelan hardware */
+  for(i = 0; i < NELS(iobase); i++)
+    {
+      /* Check if the is something at this base address */
+      if(wv_check_ioaddr(iobase[i], mac) == 0)
+       {
+         dev->base_addr = iobase[i];           /* Copy base address */
+         memcpy(dev->dev_addr, mac, 6);        /* Copy mac address */
+         if(wavelan_config(dev) == 0)
+           {
+#ifdef DEBUG_CALLBACK_TRACE
+             printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name);
+#endif
+             return 0;
+           }
        }
-*/
+    }
 
-       printk("|");
-}
+  /* We may have touch base_addr : another driver may not like it... */
+  dev->base_addr = base_addr;
 
-#if    0
-static
-void
-wavelan_psa_show(psa_t *p)
-{
-       printk("psa:");
-
-       printk("psa_io_base_addr_1: 0x%02x,", p->psa_io_base_addr_1);
-       printk("psa_io_base_addr_2: 0x%02x,", p->psa_io_base_addr_2);
-       printk("psa_io_base_addr_3: 0x%02x,", p->psa_io_base_addr_3);
-       printk("psa_io_base_addr_4: 0x%02x,", p->psa_io_base_addr_4);
-       printk("psa_rem_boot_addr_1: 0x%02x,", p->psa_rem_boot_addr_1);
-       printk("psa_rem_boot_addr_2: 0x%02x,", p->psa_rem_boot_addr_2);
-       printk("psa_rem_boot_addr_3: 0x%02x,", p->psa_rem_boot_addr_3);
-       printk("psa_holi_params: 0x%02x,", p->psa_holi_params);
-       printk("psa_int_req_no: %d,", p->psa_int_req_no);
-       printk
-       (
-               "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x,",
-               p->psa_univ_mac_addr[0],
-               p->psa_univ_mac_addr[1],
-               p->psa_univ_mac_addr[2],
-               p->psa_univ_mac_addr[3],
-               p->psa_univ_mac_addr[4],
-               p->psa_univ_mac_addr[5]
-       );
-       printk
-       (
-               "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x,",
-               p->psa_local_mac_addr[0],
-               p->psa_local_mac_addr[1],
-               p->psa_local_mac_addr[2],
-               p->psa_local_mac_addr[3],
-               p->psa_local_mac_addr[4],
-               p->psa_local_mac_addr[5]
-       );
-       printk("psa_univ_local_sel: %d,", p->psa_univ_local_sel);
-       printk("psa_comp_number: %d,", p->psa_comp_number);
-       printk("psa_thr_pre_set: 0x%02x,", p->psa_thr_pre_set);
-       printk("psa_feature_select/decay_prm: 0x%02x,", p->psa_feature_select);
-       printk("psa_subband/decay_update_prm: %d,", p->psa_subband);
-       printk("psa_quality_thr: 0x%02x,", p->psa_quality_thr);
-       printk("psa_mod_delay: 0x%02x,", p->psa_mod_delay);
-       printk("psa_nwid: 0x%02x%02x,", p->psa_nwid[0], p->psa_nwid[1]);
-       printk("psa_undefined: %d,", p->psa_undefined);
-       printk("psa_encryption_select: %d,", p->psa_encryption_select);
-       printk
-       (
-               "psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,",
-               p->psa_encryption_key[0],
-               p->psa_encryption_key[1],
-               p->psa_encryption_key[2],
-               p->psa_encryption_key[3],
-               p->psa_encryption_key[4],
-               p->psa_encryption_key[5],
-               p->psa_encryption_key[6],
-               p->psa_encryption_key[7]
-       );
-       printk("psa_databus_width: %d,", p->psa_databus_width);
-       printk("psa_call_code/auto_squelch: 0x%02x,", p->psa_call_code);
-       printk("psa_no_of_retries: %d,", p->psa_no_of_retries);
-       printk("psa_acr: %d,", p->psa_acr);
-       printk("psa_dump_count: %d,", p->psa_dump_count);
-       printk("psa_nwid_prefix: 0x%02x,", p->psa_nwid_prefix);
-       printk("psa_conf_status: %d,", p->psa_conf_status);
-       printk("psa_crc: 0x%02x%02x,", p->psa_crc[0], p->psa_crc[1]);
-       printk("psa_crc_status: 0x%02x,", p->psa_crc_status);
-
-       printk("\n");
-}
+#ifdef DEBUG_CONFIG_INFO
+  printk(KERN_DEBUG "%s: wavelan_probe(): no device found\n",
+        dev->name);
+#endif
 
-static
-void
-wavelan_mmc_show(unsigned short ioaddr)
-{
-       mmr_t   m;
-
-       mmc_read(ioaddr, 0, (unsigned char *)&m, sizeof(m));
-
-       printk("mmr:");
-       printk(" des_status: 0x%x", m.mmr_des_status);
-       printk(" des_avail: 0x%x", m.mmr_des_avail);
-       printk(" des_io_invert: 0x%x", m.mmr_des_io_invert);
-       printk
-       (
-               " dce_status: 0x%x[%s%s%s%s]",
-               m.mmr_dce_status & 0x0F,
-               (m.mmr_dce_status & MMR_DCE_STATUS_ENERG_DET) ? "energy detected," : "",
-               (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ? "loop test indicated," : "",
-               (m.mmr_dce_status & MMR_DCE_STATUS_XMTITR_IND) ? "transmitter on," : "",
-               (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ? "jabber timer expired," : ""
-       );
-       printk(" correct_nwid: %d", m.mmr_correct_nwid_h << 8 | m.mmr_correct_nwid_l);
-       printk(" wrong_nwid: %d", (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
-       printk(" thr_pre_set: 0x%x", m.mmr_thr_pre_set);
-       printk(" signal_lvl: %d", m.mmr_signal_lvl);
-       printk(" silence_lvl: %d", m.mmr_silence_lvl);
-       printk(" sgnl_qual: 0x%x", m.mmr_sgnl_qual);
-       printk(" netw_id_l: %x", m.mmr_netw_id_l);
-
-       printk("\n");
+  return ENODEV;
 }
-#endif /* 0 */
 
-static
-void
-wavelan_scb_show(unsigned short ioaddr)
-{
-       scb_t   scb;
-
-       obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));   
-
-       printk("scb:");
-
-       printk(" status:");
-       printk
-       (
-               " stat 0x%x[%s%s%s%s]",
-               (scb.scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA | SCB_ST_RNR)) >> 12,
-               (scb.scb_status & SCB_ST_CX) ? "cmd completion interrupt," : "",
-               (scb.scb_status & SCB_ST_FR) ? "frame received," : "",
-               (scb.scb_status & SCB_ST_CNA) ? "cmd unit not active," : "",
-               (scb.scb_status & SCB_ST_RNR) ? "rcv unit not ready," : ""
-       );
-       printk
-       (
-               " cus 0x%x[%s%s%s]",
-               (scb.scb_status & SCB_ST_CUS) >> 8,
-               ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_IDLE) ? "idle" : "",
-               ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_SUSP) ? "suspended" : "",
-               ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_ACTV) ? "active" : ""
-       );
-       printk
-       (
-               " rus 0x%x[%s%s%s%s]",
-               (scb.scb_status & SCB_ST_RUS) >> 4,
-               ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_IDLE) ? "idle" : "",
-               ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_SUSP) ? "suspended" : "",
-               ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_NRES) ? "no resources" : "",
-               ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_RDY) ? "ready" : ""
-       );
-
-       printk(" command:");
-       printk
-       (
-               " ack 0x%x[%s%s%s%s]",
-               (scb.scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR | SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12,
-               (scb.scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "",
-               (scb.scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "",
-               (scb.scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "",
-               (scb.scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : ""
-       );
-       printk
-       (
-               " cuc 0x%x[%s%s%s%s%s]",
-               (scb.scb_command & SCB_CMD_CUC) >> 8,
-               ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_NOP) ? "nop" : "",
-               ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_GO) ? "start cbl_offset" : "",
-               ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_RES) ? "resume execution" : "",
-               ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_SUS) ? "suspend execution" : "",
-               ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_ABT) ? "abort execution" : ""
-       );
-       printk
-       (
-               " ruc 0x%x[%s%s%s%s%s]",
-               (scb.scb_command & SCB_CMD_RUC) >> 4,
-               ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_NOP) ? "nop" : "",
-               ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_GO) ? "start rfa_offset" : "",
-               ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_RES) ? "resume reception" : "",
-               ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_SUS) ? "suspend reception" : "",
-               ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_ABT) ? "abort reception" : ""
-       );
-
-       printk(" cbl_offset 0x%x", scb.scb_cbl_offset);
-       printk(" rfa_offset 0x%x", scb.scb_rfa_offset);
-
-       printk(" crcerrs %d", scb.scb_crcerrs);
-       printk(" alnerrs %d", scb.scb_alnerrs);
-       printk(" rscerrs %d", scb.scb_rscerrs);
-       printk(" ovrnerrs %d", scb.scb_ovrnerrs);
-
-       printk("\n");
-}
+/****************************** MODULE ******************************/
+/*
+ * Module entry point : insertion & removal
+ */
 
-static
-void
-wavelan_ru_show(device *dev)
+#ifdef MODULE
+/*------------------------------------------------------------------*/
+/*
+ * Insertion of the module...
+ * I'm now quite proud of the multi-device support...
+ */
+int
+init_module(void)
 {
-       net_local       *lp;
-
-       lp = (net_local *)dev->priv;
-
-       printk("ru:");
-       /*
-        * Not implemented yet...
-        */
-       printk("\n");
+  mac_addr     mac;            /* Mac address (check wavelan existence) */
+  int          ret = 0;
+  int          i;
+
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "-> init_module()\n");
+#endif
+
+  /* If probing is asked */
+  if(io[0] == 0)
+    {
+#ifdef DEBUG_CONFIG_ERRORS
+      printk(KERN_WARNING "wavelan init_module(): doing device probing (bad !)\n");
+      printk(KERN_WARNING "Specify base addresses while loading module to correct the problem\n");
+#endif
+
+      /* Copy the basic set of address to be probed */
+      for(i = 0; i < NELS(iobase); i++)
+       io[i] = iobase[i];
+    }
+
+
+  /* Loop on all possible base addresses */
+  i = -1;
+  while((io[++i] != 0) && (i < NELS(io)))
+    {
+      /* Check if the is something at this base address */
+      if(wv_check_ioaddr(io[i], mac) == 0)
+       {
+         device *      dev;
+
+         /* Create device and set basics args */
+         dev = kmalloc(sizeof(struct device), GFP_KERNEL);
+         memset(dev, 0x00, sizeof(struct device));
+         dev->name = name[i];
+         dev->base_addr = io[i];
+         dev->irq = irq[i];
+         dev->init = &wavelan_config;
+         memcpy(dev->dev_addr, mac, 6);        /* Copy mac address */
+
+         /* Try to create the device */
+         if(register_netdev(dev) != 0)
+           {
+             /* DeAllocate everything */
+             /* Note : if dev->priv is mallocated, there is no way to fail */
+             kfree_s(dev, sizeof(struct device));
+             ret = -EIO;
+           }
+       }       /* If there is something at the address */
+    }          /* Loop on all addresses */
+
+#ifdef DEBUG_CONFIG_ERRORS
+  if(wavelan_list == (net_local *) NULL)
+    printk(KERN_WARNING "wavelan init_module(): No device found\n");
+#endif
+
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "<- init_module()\n");
+#endif
+  return ret;
 }
 
-static
+/*------------------------------------------------------------------*/
+/*
+ * Removal of the module
+ */
 void
-wavelan_cu_show(device *dev)
+cleanup_module(void)
 {
-       net_local       *lp;
-       unsigned int    i;
-       unsigned short  p;
-
-       lp = (net_local *)dev->priv;
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "-> cleanup_module()\n");
+#endif
 
-       printk("cu:");
-       printk("\n");
+  /* Loop on all devices and release them */
+  while(wavelan_list != (net_local *) NULL)
+    {
+      device * dev = wavelan_list->dev;
 
-       for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++)
-       {
-               wavelan_cu_show_one(dev, lp, i, p);
+#ifdef DEBUG_CONFIG_INFO
+      printk(KERN_DEBUG "%s: cleanup_module(): removing device at 0x%x\n",
+            dev->name, (unsigned int) dev);
+#endif
 
-               p += TXBLOCKZ;
-               if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
-                       p -= NTXBLOCKS * TXBLOCKZ;
-       }
-}
+      /* Release the ioport-region. */
+      release_region(dev->base_addr, sizeof(ha_t));
 
-static
-void
-wavelan_dev_show(device *dev)
-{
-       printk("dev:");
-       printk(" start=%d,", dev->start);
-       printk(" tbusy=%ld,", dev->tbusy);
-       printk(" interrupt=%d,", dev->interrupt);
-       printk(" trans_start=%ld,", dev->trans_start);
-       printk(" flags=0x%x,", dev->flags);
-       printk("\n");
-}
+      /* Remove definitely the device */
+      unregister_netdev(dev);
 
-static
-void
-wavelan_local_show(device *dev)
-{
-       net_local       *lp;
+      /* Unlink the device */
+      wavelan_list = wavelan_list->next;
 
-       lp = (net_local *)dev->priv;
+      /* Free pieces */
+      kfree_s(dev->priv, sizeof(struct net_local));
+      kfree_s(dev, sizeof(struct device));
+    }
 
-       printk("local:");
-       printk(" tx_n_in_use=%d,", lp->tx_n_in_use);
-       printk(" hacr=0x%x,", lp->hacr);
-       printk(" rx_head=0x%x,", lp->rx_head);
-       printk(" rx_last=0x%x,", lp->rx_last);
-       printk(" tx_first_free=0x%x,", lp->tx_first_free);
-       printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use);
-       printk("\n");
+#ifdef DEBUG_MODULE_TRACE
+  printk(KERN_DEBUG "<- cleanup_module()\n");
+#endif
 }
+#endif /* MODULE */
 
 /*
  * This software may only be used and distributed
index ddad1c9fab067d0730efcddd42601502c3bc85d2..2e92c798eaa8add14701be3c25aea42713e8dc60 100644 (file)
-#define WAVELAN_ADDR_SIZE      6       /* Size of a MAC address */
-#define SA_ADDR0               0x08    /* First octet of WaveLAN MAC addresses */
-#define SA_ADDR1               0x00    /* Second octet of WaveLAN MAC addresses */
-#define SA_ADDR2               0x0E    /* Third octet of WaveLAN MAC addresses */
-#define SA_ALT_ADDR2           0x6A    /* Alternate third octet of WaveLAN MAC addresses */
-#define WAVELAN_MTU            1500    /* Maximum size of WaveLAN packet */
-
 /*
- * Parameter Storage Area (PSA).
+ *     Wavelan ISA driver
+ *
+ *             Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ * Original copyrigth follow. See wavelan.p.h for details.
+ *
+ * This file contain the declarations of the Wavelan hardware. Note that
+ * the Wavelan ISA include a i82586 controler (see definitions in
+ * file i82586.h).
+ *
+ * The main difference between the ISA hardware and the pcmcia one is
+ * the Ethernet Controler (i82586 instead of i82593).
+ * The i82586 allow multiple transmit buffers. The PSA need to be accessed
+ * through the host interface.
  */
-typedef struct psa_t   psa_t;
-struct psa_t
-{
-       unsigned char   psa_io_base_addr_1;     /* Base address 1 ??? */
-       unsigned char   psa_io_base_addr_2;     /* Base address 2 */
-       unsigned char   psa_io_base_addr_3;     /* Base address 3 */
-       unsigned char   psa_io_base_addr_4;     /* Base address 4 */
-       unsigned char   psa_rem_boot_addr_1;    /* Remote Boot Address 1 */
-       unsigned char   psa_rem_boot_addr_2;    /* Remote Boot Address 2 */
-       unsigned char   psa_rem_boot_addr_3;    /* Remote Boot Address 3 */
-       unsigned char   psa_holi_params;        /* HOst Lan Interface (HOLI) Parameters */
-       unsigned char   psa_int_req_no;         /* Interrupt Request Line */
-       unsigned char   psa_unused0[7];         /* unused */
-       unsigned char   psa_univ_mac_addr[WAVELAN_ADDR_SIZE];   /* Universal (factory) MAC Address */
-       unsigned char   psa_local_mac_addr[WAVELAN_ADDR_SIZE];  /* Local MAC Address */
-       unsigned char   psa_univ_local_sel;     /* Universal Local Selection */
-#define                PSA_UNIVERSAL   0               /* Universal (factory) */
-#define                PSA_LOCAL       1               /* Local */
-       unsigned char   psa_comp_number;        /* Compatibility Number: */
-#define                PSA_COMP_PC_AT_915      0       /* PC-AT 915 MHz        */
-#define                PSA_COMP_PC_MC_915      1       /* PC-MC 915 MHz        */
-#define                PSA_COMP_PC_AT_2400     2       /* PC-AT 2.4 GHz        */
-#define                PSA_COMP_PC_MC_2400     3       /* PC-MC 2.4 GHz        */
-#define                PSA_COMP_PCMCIA_915     4       /* PCMCIA 915 MHz       */
-       unsigned char   psa_thr_pre_set;        /* Modem Threshold Preset */
-       unsigned char   psa_feature_select;     /* ??? */
-#if    0
-       <alias for above>
-       unsigned char   psa_decay_prm;          /* Modem Decay */
-#endif /* 0 */
-       unsigned char   psa_subband;            /* Subband      */
-#define                PSA_SUBBAND_915         0       /* 915 MHz      */
-#define                PSA_SUBBAND_2425        1       /* 2425 MHz     */
-#define                PSA_SUBBAND_2460        2       /* 2460 MHz     */
-#define                PSA_SUBBAND_2484        3       /* 2484 MHz     */
-#define                PSA_SUBBAND_2430_5      4       /* 2430.5 MHz   */
-#if    0
-       <alias for above>
-       unsigned char   psa_decay_updat_prm;    /* Modem Decay Update ??? */
-#endif /* 0 */
-       unsigned char   psa_quality_thr;        /* Modem Quality Threshold */
-       unsigned char   psa_mod_delay;          /* Modem Delay ??? */
-       unsigned char   psa_nwid[2];            /* Network ID */
-       unsigned char   psa_undefined;          /* undefined */
-       unsigned char   psa_encryption_select;  /* Encryption On Off */
-       unsigned char   psa_encryption_key[8];  /* Encryption Key */
-       unsigned char   psa_databus_width;      /* 8/16 bit bus width */
-       unsigned char   psa_call_code;          /* ??? */
-#if    0
-       <alias for above>
-       unsigned char   psa_auto_squelch;       /* Automatic Squelch level On off ??? */
-#endif /* 0 */
-       unsigned char   psa_no_of_retries;      /* LAN Cont. No of retries */
-       unsigned char   psa_acr;                /* LAN Cont. ACR */
-       unsigned char   psa_dump_count;         /* number of Dump Commands in TFB */
-       unsigned char   psa_unused1[4];         /* unused */
-       unsigned char   psa_nwid_prefix;        /* ??? */
-       unsigned char   psa_unused2[3];         /* unused */
-       unsigned char   psa_conf_status;        /* Card Configuration Status */
-       unsigned char   psa_crc[2];             /* CRC over PSA */
-       unsigned char   psa_crc_status;         /* CRC Valid Flag */
-};
-#if    STRUCT_CHECK == 1
-#define        PSA_SIZE        64
-#endif /* STRUCT_CHECK == 1 */
 
-/*
- * Modem Management Controller (MMC) write structure.
+#ifndef _WAVELAN_H
+#define        _WAVELAN_H
+
+/* The detection of the wavelan card is made by reading the MAC
+ * address from the card and checking it. If you have a non AT&T
+ * product (OEM, like DEC RoamAbout, or Digital Ocean, Epson, ...),
+ * you might need to modify this part to accomodate your hardware...
  */
-typedef struct mmw_t   mmw_t;
-struct mmw_t
+const char     MAC_ADDRESSES[][3] =
 {
-       unsigned char   mmw_encr_key[8];        /* encryption key */
-       unsigned char   mmw_encr_enable;        /* enable/disable encryption */
-       unsigned char   mmw_unused0[1];         /* unused */
-       unsigned char   mmw_des_io_invert;      /* ??? */
-       unsigned char   mmw_unused1[5];         /* unused */
-       unsigned char   mmw_loopt_sel;          /* looptest selection */
-#define                MMW_LOOPT_SEL_UNDEFINED 0x40    /* undefined */
-#define                MMW_LOOPT_SEL_INT       0x20    /* activate Attention Request */
-#define                MMW_LOOPT_SEL_LS        0x10    /* looptest without collision avoidance */
-#define                MMW_LOOPT_SEL_LT3A      0x08    /* looptest 3a */
-#define                MMW_LOOPT_SEL_LT3B      0x04    /* looptest 3b */
-#define                MMW_LOOPT_SEL_LT3C      0x02    /* looptest 3c */
-#define                MMW_LOOPT_SEL_LT3D      0x01    /* looptest 3d */
-       unsigned char   mmw_jabber_enable;      /* jabber timer enable */
-       unsigned char   mmw_freeze;             /* freeze / unfreeze signal level */
-       unsigned char   mmw_anten_sel;          /* antenna selection */
-#define                MMW_ANTEN_SEL_SEL       0x01    /* direct antenna selection */
-#define                MMW_ANTEN_SEL_ALG_EN    0x02    /* antenna selection algorithm enable */
-       unsigned char   mmw_ifs;                /* inter frame spacing */
-       unsigned char   mmw_mod_delay;          /* modem delay */
-       unsigned char   mmw_jam_time;           /* jamming time */
-       unsigned char   mmw_unused2[1];         /* unused */
-       unsigned char   mmw_thr_pre_set;        /* level threshold preset */
-       unsigned char   mmw_decay_prm;          /* decay parameters */
-       unsigned char   mmw_decay_updat_prm;    /* decay update parameters */
-       unsigned char   mmw_quality_thr;        /* quality (z-quotient) threshold */
-       unsigned char   mmw_netw_id_l;          /* NWID low order byte */
-       unsigned char   mmw_netw_id_h;          /* NWID high order byte */
+  { 0x08, 0x00, 0x0E },                /* AT&T Wavelan (standard) & DEC RoamAbout */
+  { 0x08, 0x00, 0x6A },                /* AT&T Wavelan (alternate) */
+  /* Add your card here and send me the patch ! */
 };
-#if    STRUCT_CHECK == 1
-#define        MMW_SIZE        30
-#endif /* STRUCT_CHECK == 1 */
 
-#define        mmwoff(p,f)     (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
+#define WAVELAN_ADDR_SIZE      6       /* Size of a MAC address */
 
-/*
- * Modem Management Controller (MMC) read structure.
- */
-typedef struct mmr_t   mmr_t;
-struct mmr_t
-{
-       unsigned char   mmr_unused0[8];         /* unused */
-       unsigned char   mmr_des_status;         /* encryption status */
-       unsigned char   mmr_des_avail;          /* encryption available (0x55 read) */
-       unsigned char   mmr_des_io_invert;      /* des I/O invert register */
-       unsigned char   mmr_unused1[5];         /* unused */
-       unsigned char   mmr_dce_status;         /* DCE status */
-#define                MMR_DCE_STATUS_ENERG_DET        0x01    /* energy detected */
-#define                MMR_DCE_STATUS_LOOPT_IND        0x02    /* loop test indicated */
-#define                MMR_DCE_STATUS_XMTITR_IND       0x04    /* transmitter on */
-#define                MMR_DCE_STATUS_JBR_EXPIRED      0x08    /* jabber timer expired */
-       unsigned char   mmr_unused2[3];         /* unused */
-       unsigned char   mmr_correct_nwid_l;     /* no. of correct NWID's rxd (low) */
-       unsigned char   mmr_correct_nwid_h;     /* no. of correct NWID's rxd (high) */
-       unsigned char   mmr_wrong_nwid_l;       /* count of wrong NWID's received (low) */
-       unsigned char   mmr_wrong_nwid_h;       /* count of wrong NWID's received (high) */
-       unsigned char   mmr_thr_pre_set;        /* level threshold preset */
-       unsigned char   mmr_signal_lvl;         /* signal level */
-       unsigned char   mmr_silence_lvl;        /* silence level */
-       unsigned char   mmr_sgnl_qual;          /* signal quality */
-#define                MMR_SGNL_QUAL_0         0x01    /* signal quality 0 */
-#define                MMR_SGNL_QUAL_1         0x02    /* signal quality 1 */
-#define                MMR_SGNL_QUAL_2         0x04    /* signal quality 2 */
-#define                MMR_SGNL_QUAL_3         0x08    /* signal quality 3 */
-#define                MMR_SGNL_QUAL_S_A       0x80    /* currently selected antenna */
-       unsigned char   mmr_netw_id_l;          /* NWID low order byte ??? */
-       unsigned char   mmr_unused3[1];         /* unused */
-};
-#if    STRUCT_CHECK == 1
-#define        MMR_SIZE        30
-#endif /* STRUCT_CHECK == 1 */
+#define WAVELAN_MTU            1500    /* Maximum size of WaveLAN packet */
 
-#define        MMR_LEVEL_MASK  0x3F
+#define        MAXDATAZ                (WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU)
 
-#define        mmroff(p,f)     (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+/*************************** PC INTERFACE ****************************/
 
 /*
  * Host Adaptor structure.
@@ -195,9 +78,8 @@ struct ha_t
        unsigned short  ha_pior2;       /* Program I/O Address Register Port 2 */
        unsigned short  ha_piop2;       /* Program I/O Port 2 */
 };
-#if    STRUCT_CHECK == 1
+
 #define HA_SIZE                16
-#endif /* STRUCT_CHECK == 1 */
 
 #define        hoff(p,f)       (unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0)
 #define        HACR(p)         hoff(p, ha_command)
@@ -227,14 +109,14 @@ struct ha_t
 #define        HACR_DEFAULT            (HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2))
 #define        HACR_INTRON             (HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE)
 
-#define        MAXDATAZ                (WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU)
+/************************** MEMORY LAYOUT **************************/
 
 /*
  * Onboard 64k RAM layout.
  * (Offsets from 0x0000.)
  */
-#define OFFSET_RU              0x0000
-#define OFFSET_CU              0x8000
+#define OFFSET_RU              0x0000          /* 75 % memory */
+#define OFFSET_CU              0xC000          /* 25 % memory */
 #define OFFSET_SCB             (OFFSET_ISCP - sizeof(scb_t))
 #define OFFSET_ISCP            (OFFSET_SCP - sizeof(iscp_t))
 #define OFFSET_SCP             I82586_SCP_ADDR
@@ -245,6 +127,217 @@ struct ha_t
 #define        NRXBLOCKS               ((OFFSET_CU - OFFSET_RU) / RXBLOCKZ)
 #define        NTXBLOCKS               ((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ)
 
+/********************** PARAMETER STORAGE AREA **********************/
+
+/*
+ * Parameter Storage Area (PSA).
+ */
+typedef struct psa_t   psa_t;
+struct psa_t
+{
+  unsigned char        psa_io_base_addr_1;     /* [0x00] Base address 1 ??? */
+  unsigned char        psa_io_base_addr_2;     /* [0x01] Base address 2 */
+  unsigned char        psa_io_base_addr_3;     /* [0x02] Base address 3 */
+  unsigned char        psa_io_base_addr_4;     /* [0x03] Base address 4 */
+  unsigned char        psa_rem_boot_addr_1;    /* [0x04] Remote Boot Address 1 */
+  unsigned char        psa_rem_boot_addr_2;    /* [0x05] Remote Boot Address 2 */
+  unsigned char        psa_rem_boot_addr_3;    /* [0x06] Remote Boot Address 3 */
+  unsigned char        psa_holi_params;        /* [0x07] HOst Lan Interface (HOLI) Parameters */
+  unsigned char        psa_int_req_no;         /* [0x08] Interrupt Request Line */
+  unsigned char        psa_unused0[7];         /* [0x09-0x0F] unused */
+
+  unsigned char        psa_univ_mac_addr[WAVELAN_ADDR_SIZE];   /* [0x10-0x15] Universal (factory) MAC Address */
+  unsigned char        psa_local_mac_addr[WAVELAN_ADDR_SIZE];  /* [0x16-1B] Local MAC Address */
+  unsigned char        psa_univ_local_sel;     /* [0x1C] Universal Local Selection */
+#define                PSA_UNIVERSAL   0               /* Universal (factory) */
+#define                PSA_LOCAL       1               /* Local */
+  unsigned char        psa_comp_number;        /* [0x1D] Compatability Number: */
+#define                PSA_COMP_PC_AT_915      0       /* PC-AT 915 MHz        */
+#define                PSA_COMP_PC_MC_915      1       /* PC-MC 915 MHz        */
+#define                PSA_COMP_PC_AT_2400     2       /* PC-AT 2.4 GHz        */
+#define                PSA_COMP_PC_MC_2400     3       /* PC-MC 2.4 GHz        */
+#define                PSA_COMP_PCMCIA_915     4       /* PCMCIA 915 MHz or 2.0 */
+  unsigned char        psa_thr_pre_set;        /* [0x1E] Modem Threshold Preset */
+  unsigned char        psa_feature_select;     /* [0x1F] Call code required (1=on) */
+#define                PSA_FEATURE_CALL_CODE   0x01    /* Call code required (Japan) */
+  unsigned char        psa_subband;            /* [0x20] Subband       */
+#define                PSA_SUBBAND_915         0       /* 915 MHz or 2.0 */
+#define                PSA_SUBBAND_2425        1       /* 2425 MHz     */
+#define                PSA_SUBBAND_2460        2       /* 2460 MHz     */
+#define                PSA_SUBBAND_2484        3       /* 2484 MHz     */
+#define                PSA_SUBBAND_2430_5      4       /* 2430.5 MHz   */
+  unsigned char        psa_quality_thr;        /* [0x21] Modem Quality Threshold */
+  unsigned char        psa_mod_delay;          /* [0x22] Modem Delay ??? (reserved) */
+  unsigned char        psa_nwid[2];            /* [0x23-0x24] Network ID */
+  unsigned char        psa_nwid_select;        /* [0x25] Network ID Select On Off */
+  unsigned char        psa_encryption_select;  /* [0x26] Encryption On Off */
+  unsigned char        psa_encryption_key[8];  /* [0x27-0x2E] Encryption Key */
+  unsigned char        psa_databus_width;      /* [0x2F] AT bus width select 8/16 */
+  unsigned char        psa_call_code[8];       /* [0x30-0x37] (Japan) Call Code */
+  unsigned char        psa_nwid_prefix[2];     /* [0x38-0x39] Roaming domain */
+  unsigned char        psa_reserved[2];        /* [0x3A-0x3B] Reserved - fixed 00 */
+  unsigned char        psa_conf_status;        /* [0x3C] Conf Status, bit 0=1:config*/
+  unsigned char        psa_crc[2];             /* [0x3D] CRC-16 over PSA */
+  unsigned char        psa_crc_status;         /* [0x3F] CRC Valid Flag */
+};
+
+#define        PSA_SIZE        64
+
+/* Calculate offset of a field in the above structure
+ * Warning : only even addresses are used */
+#define        psaoff(p,f)     ((unsigned short) ((void *)(&((psa_t *) ((void *) NULL + (p)))->f) - (void *) NULL))
+
+/******************** MODEM MANAGEMENT INTERFACE ********************/
+
+/*
+ * Modem Management Controller (MMC) write structure.
+ */
+typedef struct mmw_t   mmw_t;
+struct mmw_t
+{
+  unsigned char        mmw_encr_key[8];        /* encryption key */
+  unsigned char        mmw_encr_enable;        /* enable/disable encryption */
+#define        MMW_ENCR_ENABLE_MODE    0x02    /* Mode of security option */
+#define        MMW_ENCR_ENABLE_EN      0x01    /* Enable security option */
+  unsigned char        mmw_unused0[1];         /* unused */
+  unsigned char        mmw_des_io_invert;      /* Encryption option */
+#define        MMW_DES_IO_INVERT_RES   0x0F    /* Reserved */
+#define        MMW_DES_IO_INVERT_CTRL  0xF0    /* Control ??? (set to 0) */
+  unsigned char        mmw_unused1[5];         /* unused */
+  unsigned char        mmw_loopt_sel;          /* looptest selection */
+#define        MMW_LOOPT_SEL_DIS_NWID  0x40    /* disable NWID filtering */
+#define        MMW_LOOPT_SEL_INT       0x20    /* activate Attention Request */
+#define        MMW_LOOPT_SEL_LS        0x10    /* looptest w/o collision avoidance */
+#define MMW_LOOPT_SEL_LT3A     0x08    /* looptest 3a */
+#define        MMW_LOOPT_SEL_LT3B      0x04    /* looptest 3b */
+#define        MMW_LOOPT_SEL_LT3C      0x02    /* looptest 3c */
+#define        MMW_LOOPT_SEL_LT3D      0x01    /* looptest 3d */
+  unsigned char        mmw_jabber_enable;      /* jabber timer enable */
+  /* Abort transmissions > 200 ms */
+  unsigned char        mmw_freeze;             /* freeze / unfreeeze signal level */
+  /* 0 : signal level & qual updated for every new message, 1 : frozen */
+  unsigned char        mmw_anten_sel;          /* antenna selection */
+#define MMW_ANTEN_SEL_SEL      0x01    /* direct antenna selection */
+#define        MMW_ANTEN_SEL_ALG_EN    0x02    /* antenna selection algo. enable */
+  unsigned char        mmw_ifs;                /* inter frame spacing */
+  /* min time between transmission in bit periods (.5 us) - bit 0 ignored */
+  unsigned char        mmw_mod_delay;          /* modem delay (synchro) */
+  unsigned char        mmw_jam_time;           /* jamming time (after collision) */
+  unsigned char        mmw_unused2[1];         /* unused */
+  unsigned char        mmw_thr_pre_set;        /* level threshold preset */
+  /* Discard all packet with signal < this value (4) */
+  unsigned char        mmw_decay_prm;          /* decay parameters */
+  unsigned char        mmw_decay_updat_prm;    /* decay update parameterz */
+  unsigned char        mmw_quality_thr;        /* quality (z-quotient) threshold */
+  /* Discard all packet with quality < this value (3) */
+  unsigned char        mmw_netw_id_l;          /* NWID low order byte */
+  unsigned char        mmw_netw_id_h;          /* NWID high order byte */
+  /* Network ID or Domain : create virtual net on the air */
+
+  /* 2.0 Hardware extension - frequency selection support */
+  unsigned char        mmw_mode_select;        /* for analog tests (set to 0) */
+  unsigned char        mmw_unused3[1];         /* unused */
+  unsigned char        mmw_fee_ctrl;           /* frequency eeprom control */
+#define        MMW_FEE_CTRL_PRE        0x10    /* Enable protected instructions */
+#define        MMW_FEE_CTRL_DWLD       0x08    /* Download eeprom to mmc */
+#define        MMW_FEE_CTRL_CMD        0x07    /* EEprom commands : */
+#define        MMW_FEE_CTRL_READ       0x06    /* Read */
+#define        MMW_FEE_CTRL_WREN       0x04    /* Write enable */
+#define        MMW_FEE_CTRL_WRITE      0x05    /* Write data to address */
+#define        MMW_FEE_CTRL_WRALL      0x04    /* Write data to all addresses */
+#define        MMW_FEE_CTRL_WDS        0x04    /* Write disable */
+#define        MMW_FEE_CTRL_PRREAD     0x16    /* Read addr from protect register */
+#define        MMW_FEE_CTRL_PREN       0x14    /* Protect register enable */
+#define        MMW_FEE_CTRL_PRCLEAR    0x17    /* Unprotect all registers */
+#define        MMW_FEE_CTRL_PRWRITE    0x15    /* Write addr in protect register */
+#define        MMW_FEE_CTRL_PRDS       0x14    /* Protect register disable */
+  /* Never issue this command (PRDS) : it's irreversible !!! */
+
+  unsigned char        mmw_fee_addr;           /* EEprom address */
+#define        MMW_FEE_ADDR_CHANNEL    0xF0    /* Select the channel */
+#define        MMW_FEE_ADDR_OFFSET     0x0F    /* Offset in channel data */
+#define        MMW_FEE_ADDR_EN         0xC0    /* FEE_CTRL enable operations */
+#define        MMW_FEE_ADDR_DS         0x00    /* FEE_CTRL disable operations */
+#define        MMW_FEE_ADDR_ALL        0x40    /* FEE_CTRL all operations */
+#define        MMW_FEE_ADDR_CLEAR      0xFF    /* FEE_CTRL clear operations */
+
+  unsigned char        mmw_fee_data_l;         /* Write data to EEprom */
+  unsigned char        mmw_fee_data_h;         /* high octet */
+  unsigned char        mmw_ext_ant;            /* Setting for external antenna */
+#define        MMW_EXT_ANT_EXTANT      0x01    /* Select external antenna */
+#define        MMW_EXT_ANT_POL         0x02    /* Polarity of the antenna */
+#define        MMW_EXT_ANT_INTERNAL    0x00    /* Internal antenna */
+#define        MMW_EXT_ANT_EXTERNAL    0x03    /* External antenna */
+#define        MMW_EXT_ANT_IQ_TEST     0x1C    /* IQ test pattern (set to 0) */
+};
+
+#define        MMW_SIZE        37
+
+#define        mmwoff(p,f)     (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * Modem Management Controller (MMC) read structure.
+ */
+typedef struct mmr_t   mmr_t;
+struct mmr_t
+{
+  unsigned char        mmr_unused0[8];         /* unused */
+  unsigned char        mmr_des_status;         /* encryption status */
+  unsigned char        mmr_des_avail;          /* encryption available (0x55 read) */
+#define        MMR_DES_AVAIL_DES       0x55            /* DES available */
+#define        MMR_DES_AVAIL_AES       0x33            /* AES (AT&T) available */
+  unsigned char        mmr_des_io_invert;      /* des I/O invert register */
+  unsigned char        mmr_unused1[5];         /* unused */
+  unsigned char        mmr_dce_status;         /* DCE status */
+#define        MMR_DCE_STATUS_RX_BUSY          0x01    /* receiver busy */
+#define        MMR_DCE_STATUS_LOOPT_IND        0x02    /* loop test indicated */
+#define        MMR_DCE_STATUS_TX_BUSY          0x04    /* transmitter on */
+#define        MMR_DCE_STATUS_JBR_EXPIRED      0x08    /* jabber timer expired */
+  unsigned char        mmr_dsp_id;             /* DSP id (AA = Daedalus rev A) */
+  unsigned char        mmr_unused2[2];         /* unused */
+  unsigned char        mmr_correct_nwid_l;     /* # of correct NWID's rxd (low) */
+  unsigned char        mmr_correct_nwid_h;     /* # of correct NWID's rxd (high) */
+  /* Warning : Read high order octet first !!! */
+  unsigned char        mmr_wrong_nwid_l;       /* # of wrong NWID's rxd (low) */
+  unsigned char        mmr_wrong_nwid_h;       /* # of wrong NWID's rxd (high) */
+  unsigned char        mmr_thr_pre_set;        /* level threshold preset */
+#define        MMR_THR_PRE_SET         0x3F            /* level threshold preset */
+#define        MMR_THR_PRE_SET_CUR     0x80            /* Current signal above it */
+  unsigned char        mmr_signal_lvl;         /* signal level */
+#define        MMR_SIGNAL_LVL          0x3F            /* signal level */
+#define        MMR_SIGNAL_LVL_VALID    0x80            /* Updated since last read */
+  unsigned char        mmr_silence_lvl;        /* silence level (noise) */
+#define        MMR_SILENCE_LVL         0x3F            /* silence level */
+#define        MMR_SILENCE_LVL_VALID   0x80            /* Updated since last read */
+  unsigned char        mmr_sgnl_qual;          /* signal quality */
+#define        MMR_SGNL_QUAL           0x0F            /* signal quality */
+#define        MMR_SGNL_QUAL_ANT       0x80            /* current antenna used */
+  unsigned char        mmr_netw_id_l;          /* NWID low order byte ??? */
+  unsigned char        mmr_unused3[3];         /* unused */
+
+  /* 2.0 Hardware extension - frequency selection support */
+  unsigned char        mmr_fee_status;         /* Status of frequency eeprom */
+#define        MMR_FEE_STATUS_ID       0xF0            /* Modem revision id */
+#define        MMR_FEE_STATUS_DWLD     0x08            /* Download in progress */
+#define        MMR_FEE_STATUS_BUSY     0x04            /* EEprom busy */
+  unsigned char        mmr_unused4[1];         /* unused */
+  unsigned char        mmr_fee_data_l;         /* Read data from eeprom (low) */
+  unsigned char        mmr_fee_data_h;         /* Read data from eeprom (high) */
+};
+
+#define        MMR_SIZE        36
+
+#define        mmroff(p,f)     (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/* Make the two above structures one */
+typedef union mm_t
+{
+  struct mmw_t w;      /* Write to the mmc */
+  struct mmr_t r;      /* Read from the mmc */
+} mm_t;
+
+#endif /* _WAVELAN_H */
+
 /*
  * This software may only be used and distributed
  * according to the terms of the GNU Public License.
diff --git a/drivers/net/wavelan.p.h b/drivers/net/wavelan.p.h
new file mode 100644 (file)
index 0000000..dcfc70e
--- /dev/null
@@ -0,0 +1,629 @@
+/*
+ *     Wavelan ISA driver
+ *
+ *             Jean II - HPLB '96
+ *
+ * Reorganisation and extension of the driver.
+ *
+ * This file contain all definition and declarations necessary for the
+ * wavelan isa driver. This file is a private header, so it should
+ * be included only on wavelan.c !!!
+ */
+
+#ifndef WAVELAN_P_H
+#define WAVELAN_P_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * This driver provide a Linux interface to the Wavelan ISA hardware
+ * The Wavelan is a product of Lucent ("http://wavelan.netland.nl/").
+ * This division was formerly part of NCR and then AT&T.
+ * Wavelan are also distributed by DEC (RoamAbout), Digital Ocean and
+ * Aironet (Arlan). If you have one of those product, you will need to
+ * make some changes below...
+ *
+ * This driver is still a beta software. A lot of bugs have been corrected,
+ * a lot of functionalities are implemented, the whole appear pretty stable,
+ * but there is still some area of improvement (encryption, performance...).
+ *
+ * To know how to use this driver, read the NET3 HOWTO.
+ * If you want to exploit the many other fonctionalities, look comments
+ * in the code...
+ *
+ * This driver is the result of the effort of many peoples (see below).
+ */
+
+/* ------------------------ SPECIFIC NOTES ------------------------ */
+/*
+ * MAC address and hardware detection :
+ * ----------------------------------
+ *     The detection code of the wavelan chech that the first 3
+ *     octets of the MAC address fit the company code. This type of
+ *     detection work well for AT&T cards (because the AT&T code is
+ *     hardcoded in wavelan.h), but of course will fail for other
+ *     manufacturer.
+ *
+ *     If you are sure that your card is derived from the wavelan,
+ *     here is the way to configure it :
+ *     1) Get your MAC address
+ *             a) With your card utilities (wfreqsel, instconf, ...)
+ *             b) With the driver :
+ *                     o compile the kernel with DEBUG_CONFIG_INFO enabled
+ *                     o Boot and look the card messages
+ *     2) Set your MAC code (3 octets) in MAC_ADDRESSES[][3] (wavelan.h)
+ *     3) Compile & verify
+ *     4) Send me the MAC code - I will include it in the next version...
+ *
+ * "CU Inactive" message at boot up :
+ * -----------------------------------
+ *     It seem that there is some weird timings problems with the
+ *     Intel microcontroler. In fact, this message is triggered by a
+ *     bad reading of the on board ram the first time we read the
+ *     control block. If you ignore this message, all is ok (but in
+ *     fact, currently, it reset the wavelan hardware).
+ *
+ *     To get rid of that problem, there is two solution. The first
+ *     is to add a dummy read of the scb at the end of
+ *     wv_82586_config. The second is to add the timers
+ *     wv_synchronous_cmd and wv_ack (the udelay just after the
+ *     waiting loops - seem that the controler is not totally ready
+ *     when it say it is !).
+ *
+ *     In the current code, I use the second solution (to be
+ *     consistent with the original solution of Bruce Janson).
+ */
+
+/* --------------------- WIRELESS EXTENSIONS --------------------- */
+/*
+ * This driver is the first one to support "wireless extensions".
+ * This set of extensions provide you some way to control the wireless
+ * caracteristics of the hardware in a standard way and support for
+ * applications for taking advantage of it (like Mobile IP).
+ *
+ * By default, these wireless extensions are disabled, because they
+ * need a patch to the Linux Kernel. This simple patch may be found
+ * with the driver + some utilities to access those wireless
+ * extensions (iwconfig...). Hopefully, those wireless extensions will
+ * make their way in the kernel someday.
+ *
+ * You also will need to enable the CONFIG_NET_RADIO in the kernel
+ * configuration to enable the wireless extensions.
+ */
+
+/* ---------------------------- FILES ---------------------------- */
+/*
+ * wavelan.c :         The actual code for the driver - C functions
+ *
+ * wavelan.p.h :       Private header : local types / vars for the driver
+ *
+ * wavelan.h :         Description of the hardware interface & structs
+ *
+ * i82586.h :          Description if the Ethernet controler
+ */
+
+/* --------------------------- HISTORY --------------------------- */
+/*
+ * (Made with information in drivers headers. It may not be accurate,
+ * and I garantee nothing except my best effort...)
+ *
+ * The history of the Wavelan drivers is as complicated as history of
+ * the Wavelan itself (NCR -> AT&T -> Lucent).
+ *
+ * All started with Anders Klemets <klemets@paul.rutgers.edu>,
+ * writting a Wavelan ISA driver for the MACH microkernel. Girish
+ * Welling <welling@paul.rutgers.edu> had also worked on it.
+ * Keith Moore modify this for the Pcmcia hardware.
+ * 
+ * Robert Morris <rtm@das.harvard.edu> port these two drivers to BSDI
+ * and add specific Pcmcia support (there is currently no equivalent
+ * of the PCMCIA package under BSD...).
+ *
+ * Jim Binkley <jrb@cs.pdx.edu> port both BSDI drivers to freeBSD.
+ *
+ * Bruce Janson <bruce@cs.usyd.edu.au> port the BSDI ISA driver to Linux.
+ *
+ * Anthony D. Joseph <adj@lcs.mit.edu> started modify Bruce driver
+ * (with help of the BSDI PCMCIA driver) for PCMCIA.
+ * Yunzhou Li <yunzhou@strat.iol.unh.edu> finished is work.
+ * Joe Finney <joe@comp.lancs.ac.uk> patched the driver to start
+ * correctly 2.00 cards (2.4 GHz with frequency selection).
+ * David Hinds <dhinds@hyper.stanford.edu> integrated the whole in his
+ * Pcmcia package (+ bug corrections).
+ *
+ * I (Jean Tourrilhes - jt@hplb.hpl.hp.com) then started to make some
+ * patchs to the Pcmcia driver. After, I added code in the ISA driver
+ * for Wireless Extensions and full support of frequency selection
+ * cards. Then, I've done the same to the Pcmcia driver + some
+ * reorganisation. Finally, I came back to the ISA driver to
+ * upgrade it at the same level as the Pcmcia one and reorganise
+ * the code
+ * Loeke Brederveld <lbrederv@wavelan.com> from Lucent has given me
+ * much needed informations on the Wavelan hardware.
+ */
+
+/* The original copyrights and litteratures mention others names and
+ * credits. I don't know what there part in this development was...
+ */
+
+/* By the way : for the copyright & legal stuff :
+ * Almost everybody wrote code under GNU or BSD license (or alike),
+ * and want that their original copyright remain somewhere in the
+ * code (for myself, I go with the GPL).
+ * Nobody want to take responsibility for anything, except the fame...
+ */
+
+/* --------------------------- CREDITS --------------------------- */
+/*
+ * This software was developed as a component of the
+ * Linux operating system.
+ * It is based on other device drivers and information
+ * either written or supplied by:
+ *     Ajay Bakre (bakre@paul.rutgers.edu),
+ *     Donald Becker (becker@cesdis.gsfc.nasa.gov),
+ *     Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com),
+ *     Anders Klemets (klemets@it.kth.se),
+ *     Vladimir V. Kolpakov (w@stier.koenig.ru),
+ *     Marc Meertens (Marc.Meertens@Utrecht.NCR.com),
+ *     Pauline Middelink (middelin@polyware.iaf.nl),
+ *     Robert Morris (rtm@das.harvard.edu),
+ *     Jean Tourrilhes (jt@hplb.hpl.hp.com),
+ *     Girish Welling (welling@paul.rutgers.edu),
+ *     Clark Woodworth <clark@hiway1.exit109.com>
+ *     Yongguang Zhang <ygz@isl.hrl.hac.com>...
+ *
+ * Thanks go also to:
+ *     James Ashton (jaa101@syseng.anu.edu.au),
+ *     Alan Cox (iialan@iiit.swan.ac.uk),
+ *     Allan Creighton (allanc@cs.usyd.edu.au),
+ *     Matthew Geier (matthew@cs.usyd.edu.au),
+ *     Remo di Giovanni (remo@cs.usyd.edu.au),
+ *     Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de),
+ *     Vipul Gupta (vgupta@cs.binghamton.edu),
+ *     Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
+ *     Tim Nicholson (tim@cs.usyd.edu.au),
+ *     Ian Parkin (ian@cs.usyd.edu.au),
+ *     John Rosenberg (johnr@cs.usyd.edu.au),
+ *     George Rossi (george@phm.gov.au),
+ *     Arthur Scott (arthur@cs.usyd.edu.au),
+ *     Peter Storey,
+ * for their assistance and advice.
+ *
+ * Additional Credits:
+ *
+ * My developpement has been done under Linux 2.0.x (Debian 1.1) with
+ *     an HP Vectra XP/60.
+ *
+ */
+
+/* ------------------------- IMPROVEMENTS ------------------------- */
+/*
+ * I proudly present :
+ *
+ * Changes mades in first pre-release :
+ * ----------------------------------
+ *     - Reorganisation of the code, function name change
+ *     - Creation of private header (wavelan.p.h)
+ *     - Reorganised debug messages
+ *     - More comments, history, ...
+ *     - mmc_init : configure the PSA if not done
+ *     - mmc_init : correct default value of level threshold for pcmcia
+ *     - mmc_init : 2.00 detection better code for 2.00 init
+ *     - better info at startup
+ *     - irq setting (note : this setting is permanent...)
+ *     - Watchdog : change strategy (+ solve module removal problems)
+ *     - add wireless extensions (ioctl & get_wireless_stats)
+ *       get/set nwid/frequency on fly, info for /proc/net/wireless
+ *     - More wireless extension : SETSPY and GETSPY
+ *     - Make wireless extensions optional
+ *     - Private ioctl to set/get quality & level threshold, histogram
+ *     - Remove /proc/net/wavelan
+ *     - Supress useless stuff from lp (net_local)
+ *     - kernel 2.1 support (copy_to/from_user instead of memcpy_to/fromfs)
+ *     - Add message level (debug stuff in /var/adm/debug & errors not
+ *       displayed at console and still in /var/adm/messages)
+ *     - multi device support
+ *     - Start fixing the probe (init code)
+ *     - More inlines
+ *     - man page
+ *     - Lot of others minor details & cleanups
+ *
+ * Changes made in second pre-release :
+ * ----------------------------------
+ *     - Cleanup init code (probe & module init)
+ *     - Better multi device support (module)
+ *     - name assignement (module)
+ *
+ * Changes made in third pre-release :
+ * ---------------------------------
+ *     - Be more conservative on timers
+ *     - Preliminary support for multicast (I still lack some details...)
+ *
+ * Changes made in fourth pre-release :
+ * ----------------------------------
+ *     - multicast (revisited and finished)
+ *     - Avoid reset in set_multicast_list (a really big hack)
+ *       if somebody could apply this code for other i82586 based driver...
+ *     - Share on board memory 75% RU / 25% CU (instead of 50/50)
+ *
+ * Changes made for release in 2.1.15 :
+ * ----------------------------------
+ *     - Change the detection code for multi manufacturer code support
+ *
+ * Changes made for release in 2.1.17 :
+ * ----------------------------------
+ *     - Update to wireless extensions changes
+ *     - Silly bug in card initial configuration (psa_conf_status)
+ *
+ * Changes made for release in 2.1.27 & 2.0.30 :
+ * -------------------------------------------
+ *     - Small bug in debug code (probably not the last one...)
+ *     - Remove extern kerword for wavelan_probe()
+ *     - Level threshold is now a standard wireless extension (version 4 !)
+ *
+ * Wishes & dreams :
+ * ---------------
+ *     - Encryption stuff
+ *     - Roaming
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include       <linux/module.h>
+
+#include       <linux/kernel.h>
+#include       <linux/sched.h>
+#include       <linux/types.h>
+#include       <linux/fcntl.h>
+#include       <linux/interrupt.h>
+#include       <linux/stat.h>
+#include       <linux/ptrace.h>
+#include       <linux/ioport.h>
+#include       <linux/in.h>
+#include       <linux/string.h>
+#include       <linux/delay.h>
+#include       <asm/system.h>
+#include       <asm/bitops.h>
+#include       <asm/io.h>
+#include       <asm/dma.h>
+#include       <linux/errno.h>
+#include       <linux/netdevice.h>
+#include       <linux/etherdevice.h>
+#include       <linux/skbuff.h>
+#include       <linux/malloc.h>
+#include       <linux/timer.h>
+
+#include <linux/wireless.h>            /* Wireless extensions */
+
+/* Wavelan declarations */
+#include       "i82586.h"
+#include       "wavelan.h"
+
+/****************************** DEBUG ******************************/
+
+#undef DEBUG_MODULE_TRACE      /* Module insertion/removal */
+#undef DEBUG_CALLBACK_TRACE    /* Calls made by Linux */
+#undef DEBUG_INTERRUPT_TRACE   /* Calls to handler */
+#undef DEBUG_INTERRUPT_INFO    /* type of interrupt & so on */
+#define DEBUG_INTERRUPT_ERROR  /* problems */
+#undef DEBUG_CONFIG_TRACE      /* Trace the config functions */
+#undef DEBUG_CONFIG_INFO       /* What's going on... */
+#define DEBUG_CONFIG_ERRORS    /* Errors on configuration */
+#undef DEBUG_TX_TRACE          /* Transmission calls */
+#undef DEBUG_TX_INFO           /* Header of the transmited packet */
+#define DEBUG_TX_ERROR         /* unexpected conditions */
+#undef DEBUG_RX_TRACE          /* Transmission calls */
+#undef DEBUG_RX_INFO           /* Header of the transmited packet */
+#define DEBUG_RX_ERROR         /* unexpected conditions */
+#undef DEBUG_PACKET_DUMP       16      /* Dump packet on the screen */
+#undef DEBUG_IOCTL_TRACE       /* Misc call by Linux */
+#undef DEBUG_IOCTL_INFO                /* Various debug info */
+#define DEBUG_IOCTL_ERROR      /* What's going wrong */
+#define DEBUG_BASIC_SHOW       /* Show basic startup info */
+#undef DEBUG_VERSION_SHOW      /* Print version info */
+#undef DEBUG_PSA_SHOW          /* Dump psa to screen */
+#undef DEBUG_MMC_SHOW          /* Dump mmc to screen */
+#undef DEBUG_SHOW_UNUSED       /* Show also unused fields */
+#undef DEBUG_I82586_SHOW       /* Show i82586 status */
+#undef DEBUG_DEVICE_SHOW       /* Show device parameters */
+
+/* Options : */
+#define USE_PSA_CONFIG         /* Use info from the PSA */
+#define IGNORE_NORMAL_XMIT_ERRS        /* Don't bother with normal conditions */
+#undef STRUCT_CHECK            /* Verify padding of structures */
+#undef PSA_CRC                 /* Check CRC in PSA */
+#undef OLDIES                  /* Old code (to redo) */
+#undef RECORD_SNR              /* To redo */
+#undef EEPROM_IS_PROTECTED     /* Doesn't seem to be necessary */
+#define MULTICAST_AVOID                /* Avoid extra multicast (I'm sceptical) */
+
+#ifdef WIRELESS_EXT    /* If wireless extension exist in the kernel */
+/* Warning : these stuff will slow down the driver... */
+#define WIRELESS_SPY           /* Enable spying addresses */
+#undef HISTOGRAM               /* Enable histogram of sig level... */
+#endif
+
+/************************ CONSTANTS & MACROS ************************/
+
+#ifdef DEBUG_VERSION_SHOW
+static const char      *version        = "wavelan.c : v15 (wireless extensions) 12/2/97\n";
+#endif
+
+/* Watchdog temporisation */
+#define        WATCHDOG_JIFFIES        32      /* TODO: express in HZ. */
+
+/* Macro to get the number of elements in an array */
+#define        NELS(a)                         (sizeof(a) / sizeof(a[0]))
+
+/* ------------------------ PRIVATE IOCTL ------------------------ */
+
+#define SIOCSIPQTHR    SIOCDEVPRIVATE          /* Set quality threshold */
+#define SIOCGIPQTHR    SIOCDEVPRIVATE + 1      /* Get quality threshold */
+#define SIOCSIPLTHR    SIOCDEVPRIVATE + 2      /* Set level threshold */
+#define SIOCGIPLTHR    SIOCDEVPRIVATE + 3      /* Get level threshold */
+
+#define SIOCSIPHISTO   SIOCDEVPRIVATE + 6      /* Set histogram ranges */
+#define SIOCGIPHISTO   SIOCDEVPRIVATE + 7      /* Get histogram values */
+
+/* ----------------------- VERSION SUPPORT ----------------------- */
+
+/* This ugly patch is needed to cope with old version of the kernel */
+#ifndef copy_from_user
+#define copy_from_user memcpy_fromfs
+#define copy_to_user   memcpy_tofs
+#endif
+
+/****************************** TYPES ******************************/
+
+/* Shortcuts */
+typedef struct device          device;
+typedef struct enet_statistics en_stats;
+typedef struct iw_statistics   iw_stats;
+typedef struct iw_quality      iw_qual;
+typedef struct iw_freq         iw_freq;
+typedef struct net_local       net_local;
+typedef struct timer_list      timer_list;
+
+/* Basic types */
+typedef u_char         mac_addr[WAVELAN_ADDR_SIZE];    /* Hardware address */
+
+/*
+ * Static specific data for the interface.
+ *
+ * For each network interface, Linux keep data in two structure. "device"
+ * keep the generic data (same format for everybody) and "net_local" keep
+ * the additional specific data.
+ * Note that some of this specific data is in fact generic (en_stats, for
+ * example).
+ */
+struct net_local
+{
+  net_local *  next;           /* Linked list of the devices */
+  device *     dev;            /* Reverse link... */
+  en_stats     stats;          /* Ethernet interface statistics */
+  int          nresets;        /* Number of hw resets */
+  u_char       reconfig_82586; /* Need to reconfigure the controler */
+  u_char       promiscuous;    /* Promiscuous mode */
+  int          mc_count;       /* Number of multicast addresses */
+  timer_list   watchdog;       /* To avoid blocking state */
+  u_short      hacr;           /* Current host interface state */
+
+  int          tx_n_in_use;
+  u_short      rx_head;
+  u_short      rx_last;
+  u_short      tx_first_free;
+  u_short      tx_first_in_use;
+
+#ifdef WIRELESS_EXT
+  iw_stats     wstats;         /* Wireless specific stats */
+#endif
+
+#ifdef WIRELESS_SPY
+  int          spy_number;             /* Number of addresses to spy */
+  mac_addr     spy_address[IW_MAX_SPY];        /* The addresses to spy */
+  iw_qual      spy_stat[IW_MAX_SPY];           /* Statistics gathered */
+#endif /* WIRELESS_SPY */
+#ifdef HISTOGRAM
+  int          his_number;             /* Number of intervals */
+  u_char       his_range[16];          /* Boundaries of interval ]n-1; n] */
+  u_long       his_sum[16];            /* Sum in interval */
+#endif /* HISTOGRAM */
+};
+
+/**************************** PROTOTYPES ****************************/
+
+/* ----------------------- MISC SUBROUTINES ------------------------ */
+static inline unsigned long    /* flags */
+       wv_splhi(void);         /* Disable interrupts */
+static inline void
+       wv_splx(unsigned long); /* ReEnable interrupts : flags */
+static u_char
+       wv_irq_to_psa(int);
+static int
+       wv_psa_to_irq(u_char);
+/* ------------------- HOST ADAPTER SUBROUTINES ------------------- */
+static inline u_short          /* data */
+       hasr_read(u_short);     /* Read the host interface : base address */
+static inline void
+       hacr_write(u_short,     /* Write to host interface : base address */
+                  u_short),    /* data */
+       hacr_write_slow(u_short,
+                  u_short),
+       set_chan_attn(u_short,  /* ioaddr */
+                     u_short), /* hacr */
+       wv_hacr_reset(u_short), /* ioaddr */
+       wv_16_off(u_short,      /* ioaddr */
+                 u_short),     /* hacr */
+       wv_16_on(u_short,       /* ioaddr */
+                u_short),      /* hacr */
+       wv_ints_off(device *),
+       wv_ints_on(device *);
+/* ----------------- MODEM MANAGEMENT SUBROUTINES ----------------- */
+static void
+       psa_read(u_short,       /* Read the Parameter Storage Area */
+                u_short,       /* hacr */
+                int,           /* offset in PSA */
+                u_char *,      /* buffer to fill */
+                int),          /* size to read */
+       psa_write(u_short,      /* Write to the PSA */
+                 u_short,      /* hacr */
+                 int,          /* Offset in psa */
+                 u_char *,     /* Buffer in memory */
+                 int);         /* Length of buffer */
+static inline void
+       mmc_out(u_short,        /* Write 1 byte to the Modem Manag Control */
+               u_short,
+               u_char),
+       mmc_write(u_short,      /* Write n bytes to the MMC */
+                 u_char,
+                 u_char *,
+                 int);
+static inline u_char           /* Read 1 byte from the MMC */
+       mmc_in(u_short,
+              u_short);
+static inline void
+       mmc_read(u_short,       /* Read n bytes from the MMC */
+                u_char,
+                u_char *,
+                int),
+       fee_wait(u_short,       /* Wait for frequency EEprom : base address */
+                int,           /* Base delay to wait for */
+                int);          /* Number of time to wait */
+static void
+       fee_read(u_short,       /* Read the frequency EEprom : base address */
+                u_short,       /* destination offset */
+                u_short *,     /* data buffer */
+                int),          /* number of registers */
+       fee_write(u_short,      /* Write to frequency EEprom : base address */
+                 u_short,      /* destination offset */
+                 u_short *,    /* data buffer */
+                 int);         /* number of registers */
+/* ---------------------- I82586 SUBROUTINES ----------------------- */
+static /*inline*/ void
+       obram_read(u_short,     /* ioaddr */
+                  u_short,     /* o */
+                  u_char *,    /* b */
+                  int);        /* n */
+static inline void
+       obram_write(u_short,    /* ioaddr */
+                   u_short,    /* o */
+                   u_char *,   /* b */
+                   int);       /* n */
+static void
+       wv_ack(device *);
+static inline int
+       wv_synchronous_cmd(device *,
+                          const char *),
+       wv_config_complete(device *,
+                          u_short,
+                          net_local *);
+static int
+       wv_complete(device *,
+                   u_short,
+                   net_local *);
+static inline void
+       wv_82586_reconfig(device *);
+/* ------------------- DEBUG & INFO SUBROUTINES ------------------- */
+#ifdef DEBUG_I82586_SHOW
+static void
+       wv_scb_show(unsigned short);
+#endif
+static inline void
+       wv_init_info(device *); /* display startup info */
+/* ------------------- IOCTL, STATS & RECONFIG ------------------- */
+static en_stats        *
+       wavelan_get_stats(device *);    /* Give stats /proc/net/dev */
+static void
+       wavelan_set_multicast_list(device *);
+/* ----------------------- PACKET RECEPTION ----------------------- */
+static inline void
+       wv_packet_read(device *,        /* Read a packet from a frame */
+                      u_short,
+                      int),
+       wv_receive(device *);   /* Read all packets waiting */
+/* --------------------- PACKET TRANSMISSION --------------------- */
+static inline void
+       wv_packet_write(device *,       /* Write a packet to the Tx buffer */
+                       void *,
+                       short);
+static int
+       wavelan_packet_xmit(struct sk_buff *,   /* Send a packet */
+                           device *);
+/* -------------------- HARDWARE CONFIGURATION -------------------- */
+static inline int
+       wv_mmc_init(device *),          /* Initialize the modem */
+       wv_ru_start(device *),          /* Start the i82586 receiver unit */
+       wv_cu_start(device *),          /* Start the i82586 command unit */
+       wv_82586_start(device *);       /* Start the i82586 */
+static void
+       wv_82586_config(device *);      /* Configure the i82586 */
+static inline void
+       wv_82586_stop(device *);
+static int
+       wv_hw_reset(device *),          /* Reset the wavelan hardware */
+       wv_check_ioaddr(u_short,        /* ioaddr */
+                       u_char *);      /* mac address (read) */
+/* ---------------------- INTERRUPT HANDLING ---------------------- */
+static void
+       wavelan_interrupt(int,          /* Interrupt handler */
+                         void *,
+                         struct pt_regs *);
+static void
+       wavelan_watchdog(u_long);       /* Transmission watchdog */
+/* ------------------- CONFIGURATION CALLBACKS ------------------- */
+static int
+       wavelan_open(device *),         /* Open the device */
+       wavelan_close(device *),        /* Close the device */
+       wavelan_config(device *);       /* Configure one device */
+extern int
+       wavelan_probe(device *);        /* See Space.c */
+
+/**************************** VARIABLES ****************************/
+
+/*
+ * This is the root of the linked list of wavelan drivers
+ * It is use to verify that we don't reuse the same base address
+ * for two differents drivers and to make the cleanup when
+ * removing the module.
+ */
+static net_local *     wavelan_list    = (net_local *) NULL;
+
+/*
+ * This table is used to translate the psa value to irq number
+ * and vice versa...
+ */
+static u_char  irqvals[]       =
+{
+          0,    0,    0, 0x01,
+       0x02, 0x04,    0, 0x08,
+          0,    0, 0x10, 0x20,
+       0x40,    0,    0, 0x80,
+};
+
+/*
+ * Table of the available i/o address (base address) for wavelan
+ */
+static unsigned short  iobase[]        =
+{
+#if    0
+  /* Leave out 0x3C0 for now -- seems to clash with some video
+   * controllers.
+   * Leave out the others too -- we will always use 0x390 and leave
+   * 0x300 for the Ethernet device.
+   * Jean II : 0x3E0 is really fine as well...
+   */
+  0x300, 0x390, 0x3E0, 0x3C0
+#endif /* 0 */
+  0x390, 0x3E0
+};
+
+#ifdef MODULE
+/* Name of the devices (memory allocation) */
+static char    devname[4][IFNAMSIZ] = { "", "", "", "" };
+
+/* Parameters set by insmod */
+static int     io[4]   = { 0, 0, 0, 0 };
+static int     irq[4]  = { 0, 0, 0, 0 };
+static char *  name[4] = { devname[0], devname[1], devname[2], devname[3] };
+#endif /* MODULE */
+
+#endif /* WAVELAN_P_H */
index 824fcb1561a79586365c79b21c27ddef680edd24..5530b3694ce934f88c154db84d8f29403cdfb8a4 100644 (file)
@@ -1,4 +1,4 @@
-/* $Id: wic.c,v 1.0 1995/02/11 10:26:05 hayes Exp $ */
+/* $Id: wic.c,v 1.3.2.1 1997/03/09 02:14:40 davem Exp $ */
 /* WIC: A parallel port "network" driver for Linux. */
 /* based on the plip network driver */
 /* Modified for Linux 1.3.x by Alan Cox <Alan.Cox@linux.org> */
@@ -964,6 +964,7 @@ wic_tx_packet(struct sk_buff *skb, struct device *dev)
        if (skb->len > dev->mtu) {
                printk("%s: packet too big, %d.\n", dev->name, (int)skb->len);
                dev->tbusy = 0;
+               dev_kfree_skb(skb, FREE_WRITE);
                return 0;
        }
 
index ceebda47d7a93539eb69b8bb7917b2c026617d50..80bf86bddcb2788ed6b918ea72b63b9a6a417b5e 100644 (file)
@@ -87,7 +87,8 @@ struct pci_dev_info dev_info[] = {
        DEVICE( TRIDENT,        TRIDENT_9660,   "TG 9660"),
        DEVICE( AI,             AI_M1435,       "M1435"),
        DEVICE( MATROX,         MATROX_MGA_2,   "Atlas PX2085"),
-       DEVICE( MATROX,         MATROX_MIL     ,"Millennium"),
+       DEVICE( MATROX,         MATROX_MIL,     "Millennium"),
+       DEVICE( MATROX,         MATROX_MYS,     "Mystique"),
        DEVICE( MATROX,         MATROX_MGA_IMP, "MGA Impression"),
        DEVICE( CT,             CT_65545,       "65545"),
        DEVICE( CT,             CT_65548,       "65548"),
@@ -895,29 +896,47 @@ static unsigned int scan_bus(struct pci_bus *bus, unsigned long *mem_startp)
                        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);
                }
index 09d944e01967f2b4d0746027304ecba6cfafaa88..fc779ada4a7d88f65baf3a8d99fefc89288e95a9 100644 (file)
@@ -686,6 +686,7 @@ AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id);
 AM53C974_write_8(CMDREG, CMDREG_RBUS);     /* reset SCSI bus */
 udelay(10);
 AM53C974_config_after_reset(instance);
+udelay(500000);
 return(1);
 }
 
index 7cc3acb64e11621de19cce5a1ead5bde67995d83..e69229b0afc291eac54beb4031b34a9e8fc69f2e 100644 (file)
@@ -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>
 
   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.9"
+#define BusLogic_DriverDate            "29 March 1997"
 
 
 #include <linux/module.h>
@@ -69,61 +72,49 @@ static BusLogic_CommandLineEntry_T
 
 
 /*
-  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 } };
 
 
 /*
@@ -136,41 +127,50 @@ static char
   *BusLogic_CommandFailureReason;
 
 
+/*
+  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 @@ const char *BusLogic_DriverInfo(SCSI_Host_T *Host)
 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_RegisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
 
 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 void BusLogic_UnregisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
 
 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 @@ static boolean BusLogic_CreateMailboxes(BusLogic_HostAdapter_T *HostAdapter)
                        : 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,49 @@ static void BusLogic_DestroyMailboxes(BusLogic_HostAdapter_T *HostAdapter)
 
 
 /*
-  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;
+  HostAdapter->AllocatedCCBs++;
+  return true;
+}
+
+
+/*
+  BusLogic_CreateInitialCCBs allocates the initial CCBs for Host Adapter.
+*/
+
+static boolean BusLogic_CreateInitialCCBs(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,49 +336,59 @@ static void BusLogic_DestroyCCBs(BusLogic_HostAdapter_T *HostAdapter)
 
 
 /*
-  BusLogic_AllocateCCB allocates a CCB from the Host Adapter's free list,
+  BusLogic_CreateAdditionalCCBs allocates Additional CCBs for Host Adapter.  If
+  allocation fails and there are no remaining CCBs available, the Driver Queue
+  Depth is decreased to a known safe value to avoid potential deadlocks when
+  multiple host adapters share the same IRQ Channel.
+*/
+
+static void BusLogic_CreateAdditionalCCBs(BusLogic_HostAdapter_T *HostAdapter,
+                                         int AdditionalCCBs,
+                                         boolean SuccessMessageP)
+{
+  int Allocated;
+  if (AdditionalCCBs <= 0) return;
+  for (Allocated = 0; Allocated < AdditionalCCBs; Allocated++)
+    if (!BusLogic_CreateCCB(HostAdapter)) break;
+  if (Allocated > 0 && SuccessMessageP)
+    BusLogic_Notice("Allocated %d additional CCBs (total now %d)\n",
+                   HostAdapter, Allocated, HostAdapter->AllocatedCCBs);
+  if (Allocated > 0) return;
+  BusLogic_Notice("Failed to allocate additional CCBs\n", HostAdapter);
+  HostAdapter->DriverQueueDepth =
+    HostAdapter->AllocatedCCBs - (HostAdapter->MaxTargetDevices - 1);
+  HostAdapter->SCSI_Host->can_queue = HostAdapter->DriverQueueDepth;
+}
+
+
+/*
+  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;
-  int Allocated;
   CCB = HostAdapter->Free_CCBs;
   if (CCB != NULL)
     {
       CCB->SerialNumber = ++SerialNumber;
       HostAdapter->Free_CCBs = CCB->Next;
       CCB->Next = NULL;
+      if (HostAdapter->Free_CCBs == NULL)
+       BusLogic_CreateAdditionalCCBs(HostAdapter,
+                                     HostAdapter->IncrementalCCBs,
+                                     true);
       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;
-    }
+  BusLogic_CreateAdditionalCCBs(HostAdapter,
+                               HostAdapter->IncrementalCCBs,
+                               true);
   CCB = HostAdapter->Free_CCBs;
-  if (CCB == NULL)
-    {
-      printk("scsi%d: Failed to allocate additional CCBs\n",
-            HostAdapter->HostNumber);
-      return NULL;
-    }
-  printk("scsi%d: Allocated %d additional CCBs\n",
-        HostAdapter->HostNumber, Allocated);
+  if (CCB == NULL) return NULL;
   CCB->SerialNumber = ++SerialNumber;
   HostAdapter->Free_CCBs = CCB->Next;
   CCB->Next = NULL;
@@ -363,7 +398,7 @@ static BusLogic_CCB_T *BusLogic_AllocateCCB(BusLogic_HostAdapter_T *HostAdapter)
 
 /*
   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.
 */
 
@@ -377,6 +412,47 @@ static void BusLogic_DeallocateCCB(BusLogic_CCB_T *CCB)
 }
 
 
+/*
+  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
@@ -387,12 +463,12 @@ static void BusLogic_DeallocateCCB(BusLogic_CCB_T *CCB)
   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,13 +480,27 @@ static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
 {
   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.
@@ -418,13 +508,17 @@ static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
   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 +543,40 @@ static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
        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 +601,23 @@ static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
   */
   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 +630,27 @@ static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
   /*
     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 +661,545 @@ static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
        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;
   /*
-    Interrogate PCI Configuration Space for any BusLogic SCSI Host Adapters.
-  */
-  if (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)
+    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_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++;
+           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 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))
+       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);
+      }
+  /*
+    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_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)
+         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 FlashPoint Host Adapters first unless the BIOS primary disk is
+  controlled by the first PCI MultiMaster Host Adapter, in which case
+  MultiMaster 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 (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())
+    {
+      if (BusLogic_ProbeOptions.Bits.ProbeMultiMasterFirst)
+       {
+         BusLogic_InitializeMultiMasterProbeInfo();
+         BusLogic_InitializeFlashPointProbeInfo();
+       }
+      else if (BusLogic_ProbeOptions.Bits.ProbeFlashPointFirst)
+       {
+         BusLogic_InitializeFlashPointProbeInfo();
+         BusLogic_InitializeMultiMasterProbeInfo();
+       }
+      else
+       {
+         int FlashPointCount = BusLogic_InitializeFlashPointProbeInfo();
+         int PCIMultiMasterCount = BusLogic_InitializeMultiMasterProbeInfo();
+         if (FlashPointCount > 0 && PCIMultiMasterCount > 0)
            {
+             BusLogic_ProbeInfo_T *ProbeInfo =
+               &BusLogic_ProbeInfoList[FlashPointCount];
+             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 controlled by this PCI MultiMaster Host Adapter, then
+               reverse the probe order so that MultiMaster Host Adapters are
+               probed before FlashPoint 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,
+                        BusLogic_ProbeInfoList,
+                        sizeof(BusLogic_ProbeInfoList));
+                 memcpy(&BusLogic_ProbeInfoList[0],
+                        &SavedProbeInfo[FlashPointCount],
+                        MultiMasterCount * sizeof(BusLogic_ProbeInfo_T));
+                 memcpy(&BusLogic_ProbeInfoList[MultiMasterCount],
+                        &SavedProbeInfo[0],
+                        FlashPointCount * 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 +1207,19 @@ static void BusLogic_InitializeAddressProbeList(void)
 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 +1230,81 @@ static boolean BusLogic_Failure(BusLogic_HostAdapter_T *HostAdapter,
 
 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;
   /*
-    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.
+    FlashPoint Host Adapters are Probed by the FlashPoint SCCB Manager.
   */
-  StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
-  if (TraceProbe)
-    printk("BusLogic_Probe(0x%X): Status 0x%02X\n",
-          HostAdapter->IO_Address, StatusRegister);
-  if (StatusRegister == 0xFF) return false;
+  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));
+         BusLogic_Error("BusLogic: FlashPoint Host Adapter detected at "
+                        "PCI Bus %d Device %d\n", HostAdapter,
+                        HostAdapter->Bus, HostAdapter->Device);
+         BusLogic_Error("BusLogic: I/O Address 0x%X PCI Address 0x%X, "
+                        "but FlashPoint\n", HostAdapter,
+                        HostAdapter->IO_Address, HostAdapter->PCI_Address);
+         BusLogic_Error("BusLogic: Probe Function failed to validate it.\n",
+                        HostAdapter);
+         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, 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 +1320,39 @@ static boolean BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
 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 +1365,13 @@ static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
   */
   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 +1379,34 @@ static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
   */
   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 +1419,70 @@ static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
 
 /*
   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 +1492,65 @@ static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
   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 = 16;
+      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 +1584,47 @@ static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
       != 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 +1638,19 @@ static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
     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 +1670,39 @@ static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
                           NULL, 0, &FirmwareVersionLetter,
                           sizeof(FirmwareVersionLetter))
          != sizeof(FirmwareVersionLetter))
-       return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER");
+       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;
-    }
   /*
     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,112 +1711,242 @@ static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
     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 > 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 > 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);
-  /*
-    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.
+  HostAdapter->MaxLogicalUnits = (HostAdapter->ExtendedLUNSupport ? 32 : 8);
+  /*
+    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;
       HostAdapter->MailboxCount = 255;
       HostAdapter->InitialCCBs = 64;
-      HostAdapter->IncrementalCCBs = 32;
+      HostAdapter->IncrementalCCBs = 16;
     }
   else
     {
       HostAdapter->StrictRoundRobinModeSupport = false;
       HostAdapter->MailboxCount = 32;
       HostAdapter->InitialCCBs = 32;
-      HostAdapter->IncrementalCCBs = 4;
+      HostAdapter->IncrementalCCBs = 8;
     }
-  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.
@@ -1292,34 +1972,11 @@ static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
              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.
@@ -1328,78 +1985,190 @@ static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
     HostAdapter->TaggedQueuingPermitted =
       (HostAdapter->CommandLineEntry->TaggedQueuingPermitted &
        HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask) |
-      (TaggedQueuingPermittedDefault &
+      (HostAdapter->TaggedQueuingPermitted &
        ~HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask);
-  else HostAdapter->TaggedQueuingPermitted = TaggedQueuingPermittedDefault;
-  /*
-    Announce the Host Adapter Configuration.
-  */
-  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;
+  /*
+    Indicate reading the Host Adapter Configuration completed successfully.
+  */
+  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] !=
@@ -1408,17 +2177,44 @@ static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
        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;
 }
@@ -1431,47 +2227,33 @@ static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
 
 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;
   /*
@@ -1480,10 +2262,10 @@ static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter)
   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);
@@ -1504,11 +2286,13 @@ static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter)
 
 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.
@@ -1531,34 +2315,30 @@ static boolean BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter)
 {
   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.
   */
@@ -1578,11 +2358,10 @@ static boolean BusLogic_InitializeHostAdapter(BusLogic_HostAdapter_T
   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++)
@@ -1591,10 +2370,14 @@ static boolean BusLogic_InitializeHostAdapter(BusLogic_HostAdapter_T
         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.
   */
@@ -1633,42 +2416,27 @@ static boolean BusLogic_InitializeHostAdapter(BusLogic_HostAdapter_T
        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.
   */
@@ -1696,19 +2464,22 @@ static boolean BusLogic_TargetDeviceInquiry(BusLogic_HostAdapter_T
     confused if they receive SCSI Commands too soon after a SCSI Bus Reset.
   */
   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
@@ -1788,31 +2559,31 @@ static boolean BusLogic_TargetDeviceInquiry(BusLogic_HostAdapter_T
            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.
   */
@@ -1838,7 +2609,7 @@ static void BusLogic_InitializeHostStructure(BusLogic_HostAdapter_T
   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;
@@ -1859,6 +2630,7 @@ static void BusLogic_SelectQueueDepths(SCSI_Host_T *Host,
   int TaggedQueueDepth = HostAdapter->TaggedQueueDepth;
   int UntaggedQueueDepth = HostAdapter->UntaggedQueueDepth;
   int TaggedDeviceCount = 0, UntaggedDeviceCount = 0;
+  int DesiredCCBs = HostAdapter->MaxTargetDevices - 1;
   SCSI_Device_T *Device;
   for (Device = DeviceList; Device != NULL; Device = Device->next)
     if (Device->host == Host)
@@ -1871,7 +2643,7 @@ static void BusLogic_SelectQueueDepths(SCSI_Host_T *Host,
   if (TaggedQueueDepth == 0 && TaggedDeviceCount > 0)
     {
       TaggedQueueDepth =
-       1 + ((HostAdapter->TotalQueueDepth
+       1 + ((HostAdapter->HostAdapterQueueDepth
              - UntaggedDeviceCount * UntaggedQueueDepth)
             / TaggedDeviceCount);
       if (TaggedQueueDepth > BusLogic_PreferredTaggedQueueDepth)
@@ -1884,10 +2656,12 @@ static void BusLogic_SelectQueueDepths(SCSI_Host_T *Host,
            (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;
+       DesiredCCBs += Device->queue_depth;
       }
+  BusLogic_CreateAdditionalCCBs(HostAdapter,
+                               DesiredCCBs - HostAdapter->AllocatedCCBs,
+                               false);
 }
 
 
@@ -1901,31 +2675,49 @@ static void BusLogic_SelectQueueDepths(SCSI_Host_T *Host,
 
 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.
@@ -1952,7 +2744,7 @@ int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
        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,
@@ -1961,12 +2753,11 @@ int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
        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,
@@ -1978,23 +2769,23 @@ int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
        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, Initial 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_CreateInitialCCBs(HostAdapter) &&
+         BusLogic_CreateTargetDeviceStatistics(HostAdapter) &&
          BusLogic_InitializeHostAdapter(HostAdapter) &&
          BusLogic_TargetDeviceInquiry(HostAdapter))
        {
@@ -2004,9 +2795,12 @@ int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
            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++;
        }
@@ -2014,12 +2808,14 @@ int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
        {
          /*
            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);
@@ -2027,6 +2823,8 @@ int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
          scsi_unregister(Host);
        }
     }
+  if (MessageBuffer != NULL)
+    scsi_init_free(MessageBuffer, BusLogic_MessageBufferSize);
   return BusLogicHostAdapterCount;
 }
 
@@ -2041,6 +2839,16 @@ int BusLogic_ReleaseHostAdapter(SCSI_Host_T *Host)
 {
   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.
@@ -2051,7 +2859,7 @@ int BusLogic_ReleaseHostAdapter(SCSI_Host_T *Host)
   /*
     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.
   */
@@ -2060,12 +2868,35 @@ int BusLogic_ReleaseHostAdapter(SCSI_Host_T *Host)
 }
 
 
+/*
+  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)
@@ -2084,9 +2915,10 @@ static int BusLogic_ComputeResultCode(BusLogic_HostAdapterStatus_T
     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:
@@ -2108,8 +2940,8 @@ static int BusLogic_ComputeResultCode(BusLogic_HostAdapterStatus_T
       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;
     }
@@ -2118,158 +2950,99 @@ static int BusLogic_ComputeResultCode(BusLogic_HostAdapterStatus_T
 
 
 /*
-  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.
+    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.
   */
-  for (HostAdapter = BusLogic_RegisteredHostAdapters;
-       HostAdapter != NULL;
-       HostAdapter = HostAdapter->Next)
+  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;
+         HostAdapter->LastResetCompleted[TargetID] = jiffies;
          /*
            Place CCB back on the Host Adapter's free list.
          */
@@ -2298,7 +3071,7 @@ static void BusLogic_InterruptHandler(int IRQ_Channel,
              {
                Command = CCB->Command;
                BusLogic_DeallocateCCB(CCB);
-               HostAdapter->ActiveCommandCount[TargetID]--;
+               HostAdapter->ActiveCommands[TargetID]--;
                Command->result = DID_RESET << 16;
                Command->scsi_done(Command);
              }
@@ -2307,57 +3080,151 @@ static void BusLogic_InterruptHandler(int IRQ_Channel,
       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->CompletionCode)
+           {
+           case BusLogic_IncomingMailboxFree:
+           case BusLogic_AbortedCommandNotFound:
+           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:
+             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(HostAdapter,
+                                          CCB->HostAdapterStatus,
+                                          CCB->TargetDeviceStatus);
+             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;
+           }
+         /*
+           Place CCB back on the Host Adapter's free list.
+         */
+         BusLogic_DeallocateCCB(CCB);
+         /*
+           Call the SCSI Command Completion Routine.
+         */
+         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.
          */
-         switch (CCB->MailboxCompletionCode)
+         InterruptRegister.All = BusLogic_ReadInterruptRegister(HostAdapter);
+         if (InterruptRegister.Bits.InterruptValid)
            {
-           case BusLogic_IncomingMailboxFree:
-           case BusLogic_AbortedCommandNotFound:
-             printk("scsi%d: CCB #%ld to Target %d Impossible State\n",
-                    HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
-             break;
-           case BusLogic_CommandCompletedWithoutError:
-             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);
-             Command->result = DID_ABORT << 16;
-             break;
-           case BusLogic_CommandCompletedWithError:
-             Command->result =
-               BusLogic_ComputeResultCode(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");
-                 }
-             break;
+             /*
+               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
+       {
          /*
-           Place CCB back on the Host Adapter's free list.
-         */
-         BusLogic_DeallocateCCB(CCB);
-         /*
-           Call the SCSI Command Completion Routine.
+           Check if there is a pending interrupt for this Host Adapter.
          */
-         Command->scsi_done(Command);
+         if (FlashPoint_InterruptPending(HostAdapter->CardHandle))
+           if (FlashPoint_HandleInterrupt(HostAdapter->CardHandle)
+               == FlashPoint_ExternalBusReset)
+             {
+               HostAdapter->HostAdapterResetRequested = true;
+               HostAdapterResetRequested = true;
+             }
        }
       /*
        Release exclusive access to Host Adapter.
@@ -2365,25 +3232,31 @@ static void BusLogic_InterruptHandler(int IRQ_Channel,
       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
@@ -2408,7 +3281,12 @@ static boolean BusLogic_WriteOutgoingMailbox(BusLogic_HostAdapter_T
        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;
@@ -2425,6 +3303,8 @@ int BusLogic_QueueCommand(SCSI_Command_T *Command,
 {
   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;
@@ -2452,8 +3332,8 @@ int BusLogic_QueueCommand(SCSI_Command_T *Command,
   /*
     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)
@@ -2482,7 +3362,13 @@ int BusLogic_QueueCommand(SCSI_Command_T *Command,
       int Segment;
       CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather;
       CCB->DataLength = SegmentCount * sizeof(BusLogic_ScatterGatherSegment_T);
+#ifndef CONFIG_SCSI_OMIT_FLASHPOINT
+      if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
+       CCB->DataPointer = Virtual_to_Bus(CCB->ScatterGatherList);
+      else CCB->DataPointer = (BusLogic_BusAddress_T) CCB->ScatterGatherList;
+#else
       CCB->DataPointer = Virtual_to_Bus(CCB->ScatterGatherList);
+#endif
       for (Segment = 0; Segment < SegmentCount; Segment++)
        {
          CCB->ScatterGatherList[Segment].SegmentByteCount =
@@ -2496,10 +3382,22 @@ int BusLogic_QueueCommand(SCSI_Command_T *Command,
     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;
@@ -2511,17 +3409,8 @@ int BusLogic_QueueCommand(SCSI_Command_T *Command,
   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
@@ -2536,16 +3425,18 @@ int BusLogic_QueueCommand(SCSI_Command_T *Command,
     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])
     {
@@ -2557,58 +3448,77 @@ int BusLogic_QueueCommand(SCSI_Command_T *Command,
        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.
   */
@@ -2630,6 +3540,8 @@ int BusLogic_AbortCommand(SCSI_Command_T *Command)
   BusLogic_Lock_T Lock;
   BusLogic_CCB_T *CCB;
   int Result;
+  BusLogic_IncrementErrorCounter(
+    &HostAdapter->TargetDeviceStatistics[TargetID].CommandAbortsRequested);
   /*
     Acquire exclusive access to Host Adapter.
   */
@@ -2639,8 +3551,8 @@ int BusLogic_AbortCommand(SCSI_Command_T *Command)
   */
   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;
     }
@@ -2652,56 +3564,84 @@ int BusLogic_AbortCommand(SCSI_Command_T *Command)
     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.
@@ -2724,6 +3664,11 @@ static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *HostAdapter,
   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.
   */
@@ -2737,9 +3682,9 @@ static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *HostAdapter,
       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;
       }
@@ -2747,44 +3692,53 @@ static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *HostAdapter,
        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.
   */
@@ -2826,7 +3780,10 @@ static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *HostAdapter,
          }
       }
   for (TargetID = 0; TargetID < HostAdapter->MaxTargetDevices; TargetID++)
-    HostAdapter->LastResetTime[TargetID] = jiffies;
+    {
+      HostAdapter->LastResetAttempted[TargetID] = jiffies;
+      HostAdapter->LastResetCompleted[TargetID] = jiffies;
+    }
   Result = SCSI_RESET_SUCCESS | SCSI_RESET_HOST_RESET;
   /*
     Release exclusive access to Host Adapter.
@@ -2847,9 +3804,11 @@ static int BusLogic_SendBusDeviceReset(BusLogic_HostAdapter_T *HostAdapter,
                                       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.
   */
@@ -2862,8 +3821,8 @@ static int BusLogic_SendBusDeviceReset(BusLogic_HostAdapter_T *HostAdapter,
     {
       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;
        }
@@ -2871,29 +3830,29 @@ static int BusLogic_SendBusDeviceReset(BusLogic_HostAdapter_T *HostAdapter,
        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;
        }
     }
@@ -2908,23 +3867,26 @@ static int BusLogic_SendBusDeviceReset(BusLogic_HostAdapter_T *HostAdapter,
       {
        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
@@ -2932,8 +3894,8 @@ static int BusLogic_SendBusDeviceReset(BusLogic_HostAdapter_T *HostAdapter,
   */
   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;
   /*
@@ -2945,18 +3907,30 @@ static int BusLogic_SendBusDeviceReset(BusLogic_HostAdapter_T *HostAdapter,
       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)))
+  if (BusLogic_MultiMasterHostAdapterP(HostAdapter))
     {
-      printk("scsi%d: cannot write Outgoing Mailbox for Bus Device Reset\n",
-            HostAdapter->HostNumber);
-      BusLogic_DeallocateCCB(CCB);
-      goto Done;
+      /*
+       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
@@ -2974,12 +3948,25 @@ static int BusLogic_SendBusDeviceReset(BusLogic_HostAdapter_T *HostAdapter,
     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;
+  HostAdapter->LastResetAttempted[TargetID] = jiffies;
+  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.
@@ -3004,30 +3991,29 @@ int BusLogic_ResetCommand(SCSI_Command_T *Command, unsigned int ResetFlags)
   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
     the system was initialized if no prior resets have occurred.
   */
   if (HostAdapter->TaggedQueuingActive[TargetID] &&
-      jiffies - HostAdapter->LastResetTime[TargetID] < 10*60*HZ)
+      jiffies - HostAdapter->LastResetCompleted[TargetID] < 10*60*HZ)
     {
       HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID);
       HostAdapter->TaggedQueuingActive[TargetID] = false;
-      printk("scsi%d: Tagged Queuing now disabled for Target %d\n",
-            HostAdapter->HostNumber, 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;
+      BusLogic_Warning("Tagged Queuing now disabled for Target %d\n",
+                      HostAdapter, TargetID);
+    }
   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
@@ -3038,15 +4024,19 @@ int BusLogic_ResetCommand(SCSI_Command_T *Command, unsigned int ResetFlags)
        clear the error condition.
       */
       if (HostAdapter->CommandSuccessfulFlag[TargetID] ||
-         jiffies - HostAdapter->LastResetTime[TargetID] < HZ/10)
+         jiffies - HostAdapter->LastResetAttempted[TargetID] < HZ/10)
        {
          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;
 }
 
@@ -3057,10 +4047,11 @@ int BusLogic_ResetCommand(SCSI_Command_T *Command, unsigned int ResetFlags)
   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
@@ -3074,7 +4065,7 @@ int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, KernelDevice_T Device,
     (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 */)
       {
@@ -3133,22 +4124,233 @@ int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, KernelDevice_T Device,
       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;
 }
 
 
+/*
+  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\
+Current Driver Queue Depth:    %d\n\
+Currently Allocated CCBs:      %d\n",
+                   HostAdapter->DriverQueueDepth,
+                   HostAdapter->AllocatedCCBs);
+  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.
@@ -3168,9 +4370,9 @@ int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, KernelDevice_T Device,
   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
@@ -3259,56 +4461,85 @@ int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, KernelDevice_T Device,
                        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
+                       FlashPoint Host Adapters first unless the BIOS primary
+                       disk is controlled by the first PCI MultiMaster Host
+                       Adapter, in which case MultiMaster 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
+                       FlashPoint Host Adapters first unless the BIOS primary
+                       disk is controlled by the first PCI MultiMaster Host
+                       Adapter, in which case MultiMaster 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;
     }
@@ -3317,8 +4548,9 @@ void BusLogic_Setup(char *Strings, int *Integers)
       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;
@@ -3326,131 +4558,154 @@ void BusLogic_Setup(char *Strings, int *Integers)
   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;
+      }
 }
 
 
index be29070c5e60b93f6656d5ad4814b22282be346a..7cd6ba4361baee63d4a0363bdeb8ee944e8594ef 100644 (file)
@@ -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>
 
   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_device SCSI_Device_T;
 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 @@ int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *);
 */
 
 #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 @@ int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *);
 
 
 /*
-  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 @@ int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *);
 /*
   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 @@ int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *);
 
 #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 @@ int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *);
 
 
 /*
-  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             9900
 
 
 /*
-  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 the possible Host Adapter BIOS Disk Geometry Translations.
+*/
+
+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 Boolean data type.
+*/
+
+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;
 
 
 /*
-  Define a boolean data type.
+  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 false 0
-#define true  1
-typedef unsigned char boolean;
+#define BusLogic_ISA_StandardAddressesCount    6
+
+static BusLogic_IO_Address_T
+  BusLogic_ISA_StandardAddresses[BusLogic_ISA_StandardAddressesCount] =
+    { 0x330, 0x334, 0x230, 0x234, 0x130, 0x134 };
 
 
 /*
-  Define a 32 bit bus address data type.
+  Define the Probe Options.
 */
 
-typedef unsigned int bus_address_t;
+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 @@ typedef enum
   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 @@ typedef unsigned char BusLogic_InstalledDevices8_T[8];
   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 BusLogic_SynchronousValue_T
 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 @@ BusLogic_SetupInformation_T;
 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 @@ typedef unsigned char BusLogic_FirmwareVersionLetter_T;
 
 
 /*
-  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
+{
+  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
 {
-  unsigned char ISACompatibleIOPort;                   /* Byte 0 */
+  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 @@ typedef struct BusLogic_GenericIOPortInformation
   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 @@ typedef struct BusLogic_ExtendedSetupInformation
   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 @@ BusLogic_ExtendedSetupInformation_T;
   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 @@ BusLogic_FetchHostAdapterLocalRAMRequest_T;
 
 
 /*
-  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
@@ -550,6 +850,22 @@ typedef struct BusLogic_AutoSCSIByte45
 BusLogic_AutoSCSIByte45_T;
 
 
+/*
+  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
@@ -557,33 +873,26 @@ BusLogic_AutoSCSIByte45_T;
   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 @@ typedef unsigned char BusLogic_RequestedReplyLength_T;
 
 
 /*
-  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 unsigned long BusLogic_Lock_T;
 
 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 @@ typedef enum
   BusLogic_InitiatorCCB_ScatterGatherResidual =        0x04,
   BusLogic_BusDeviceReset =                    0x81
 }
+__attribute__ ((packed))
 BusLogic_CCB_Opcode_T;
 
 
@@ -650,16 +965,17 @@ BusLogic_CCB_Opcode_T;
 
 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 @@ typedef enum
   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 @@ typedef enum
   BusLogic_HostAdapterHardwareTimeoutError =   0x30,
   BusLogic_SCSIParityErrorDetected =           0x34
 }
+__attribute__ ((packed))
 BusLogic_HostAdapterStatus_T;
 
 
@@ -702,6 +1020,7 @@ typedef enum
   BusLogic_CheckCondition =                    0x02,
   BusLogic_DeviceBusy =                                0x08
 }
+__attribute__ ((packed))
 BusLogic_TargetDeviceStatus_T;
 
 
@@ -711,10 +1030,10 @@ BusLogic_TargetDeviceStatus_T;
 
 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 @@ typedef unsigned char SCSI_CDB_T[BusLogic_CDB_MaxLength];
 
 
 /*
-  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 */
   /*
-    BusLogic Linux Driver Portion.
+    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 Defined Portion.
   */
   struct BusLogic_HostAdapter *HostAdapter;
   SCSI_Command_T *Command;
@@ -789,13 +1124,13 @@ typedef struct BusLogic_CCB
         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 @@ BusLogic_CCB_T;
 
 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,50 +1153,112 @@ BusLogic_OutgoingMailbox_T;
 
 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.
+  Define the Linux BusLogic Driver Command Line Entry structure.
 */
 
-typedef enum
+typedef struct BusLogic_CommandLineEntry
 {
-  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_IO_Address_T IO_Address;
+  unsigned short TaggedQueueDepth;
+  unsigned short BusSettleTime;
+  unsigned short TaggedQueuingPermitted;
+  unsigned short TaggedQueuingPermittedMask;
+  BusLogic_LocalOptions_T LocalOptions;
+  BusLogic_ErrorRecoveryStrategy_T
+    ErrorRecoveryStrategy[BusLogic_MaxTargetDevices];
 }
-BusLogic_BusType_T;
+BusLogic_CommandLineEntry_T;
 
-static char
-  *BusLogic_BusNames[] =
-    { "Unknown", "ISA", "MCA", "EISA", "VESA", "PCI" };
+
+/*
+  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 Linux BusLogic Driver Command Line Entry structure.
+  Define the FlashPoint Card Handle data type.
 */
 
-typedef struct BusLogic_CommandLineEntry
+#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
 {
-  unsigned int IO_Address;
-  unsigned short TaggedQueueDepth;
-  unsigned short BusSettleTime;
-  unsigned short LocalOptions;
-  unsigned short TaggedQueuingPermitted;
-  unsigned short TaggedQueuingPermittedMask;
-  unsigned char ErrorRecoveryStrategy[BusLogic_MaxTargetDevices];
+  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 */
 }
-BusLogic_CommandLineEntry_T;
+FlashPoint_Info_T;
 
 
 /*
@@ -871,32 +1268,40 @@ BusLogic_CommandLineEntry_T;
 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,35 +1311,51 @@ typedef struct BusLogic_HostAdapter
   unsigned short MailboxCount;
   unsigned short InitialCCBs;
   unsigned short IncrementalCCBs;
-  unsigned short TotalQueueDepth;
+  unsigned short AllocatedCCBs;
+  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];
+  unsigned long LastResetAttempted[BusLogic_MaxTargetDevices];
+  unsigned long LastResetCompleted[BusLogic_MaxTargetDevices];
   BusLogic_OutgoingMailbox_T *FirstOutgoingMailbox;
   BusLogic_OutgoingMailbox_T *LastOutgoingMailbox;
   BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
   BusLogic_IncomingMailbox_T *FirstIncomingMailbox;
   BusLogic_IncomingMailbox_T *LastIncomingMailbox;
   BusLogic_IncomingMailbox_T *NextIncomingMailbox;
+  BusLogic_TargetDeviceStatistics_T *TargetDeviceStatistics;
 }
 BusLogic_HostAdapter_T;
 
@@ -1007,42 +1428,78 @@ void BusLogic_ReleaseHostAdapterLockID(BusLogic_HostAdapter_T *HostAdapter,
 */
 
 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 +1536,178 @@ static inline void BusLogic_Delay(int Seconds)
   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
+
+
+/*
+  FlashPoint support is only available for the Intel x86 Architecture.
+*/
+
+#ifndef __i386__
+
+#undef CONFIG_SCSI_OMIT_FLASHPOINT
+#define CONFIG_SCSI_OMIT_FLASHPOINT
+
+#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 */
index 007bd6ce7022793a200709993c5ed82d56302442..90186fc7d197829435bae485bde347b29c7497b4 100644 (file)
@@ -23,6 +23,9 @@ dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
 dep_tristate 'Always IN2000 SCSI support' CONFIG_SCSI_IN2000 $CONFIG_SCSI
 dep_tristate 'AM53/79C974 PCI SCSI support' CONFIG_SCSI_AM53C974 $CONFIG_SCSI
 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 --git a/drivers/scsi/FlashPoint.c b/drivers/scsi/FlashPoint.c
new file mode 100644 (file)
index 0000000..7c74868
--- /dev/null
@@ -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 --git a/drivers/scsi/LICENSE.FlashPoint b/drivers/scsi/LICENSE.FlashPoint
new file mode 100644 (file)
index 0000000..ffd0fe2
--- /dev/null
@@ -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.
index 301e674c470457fef7aecb8523cfa773c47ba2cf..b251fc3f1c9ba6150abb6625a747a1993fa20378 100644 (file)
@@ -350,6 +350,12 @@ endif
 
 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 
 
index ecf35ce1a322ab9b1343e0eadf9a6c30f5096764..6a66e4df67428db80ece8c50d6c3e2b9699a89c3 100644 (file)
@@ -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.9 for Linux 2.0
 
-                                17 July 1996
+                             PRODUCTION RELEASE
+
+                                29 March 1997
 
                               Leonard N. Zubkoff
                               Dandelion Digital
@@ -18,10 +19,15 @@ BusLogic, Inc. designs and manufactures a variety of high performance SCSI host
 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
@@ -31,11 +37,10 @@ the major performance and error recovery features can be configured from the
 Linux kernel command line, allowing individual installations to tune driver
 performance and error recovery to their particular needs.
 
-The most recent versions of this driver will always be available from my Linux
-Home Page at URL "http://www.dandelion.com/Linux/" and by anonymous FTP from
-ftp.dandelion.com.  While only limited FTP directory listings are permitted,
-the introductory banner displayed on anonymous FTP login will provide a list of
-the driver versions and any other files that are available for retrieval.
+The latest information on Linux support for BusLogic SCSI Host Adapters, as
+well as the most recent release of this driver and the latest firmware for the
+BT-948/958/958D, will always be available from my Linux Home Page at URL
+"http://www.dandelion.com/Linux/".
 
 Bug reports should be sent via electronic mail to "lnz@dandelion.com".  Please
 include with the bug report the complete configuration messages reported by the
@@ -112,26 +117,23 @@ o Performance Features
 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 +157,16 @@ o PCI Configuration Support
   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 +186,50 @@ the date of this document.  It is recommended that anyone purchasing a BusLogic
 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 SCSI-2
+FlashPoint DL (BT-932) Dual Channel Ultra SCSI-2
+FlashPoint LW (BT-950) Wide Ultra SCSI-2
+FlashPoint DW (BT-952) Dual Channel Wide Ultra SCSI-2
 
-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 "W" Series Host Adapters:
 
-"C" Series Host Adapters:
+BT-948     PCI         Ultra SCSI-2
+BT-958     PCI         Wide Ultra SCSI-2
+BT-958D            PCI         Wide Differential Ultra SCSI-2
 
-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 "C" Series Host Adapters:
 
-"S" Series Host Adapters:
+BT-946C            PCI         Fast SCSI-2
+BT-956C            PCI         Wide Fast SCSI-2
+BT-956CD    PCI                Wide Differential Fast SCSI-2
+BT-445C            VLB         Fast SCSI-2
+BT-747C            EISA        Fast SCSI-2
+BT-757C            EISA        Wide Fast SCSI-2
+BT-757CD    EISA       Wide Differential Fast SCSI-2
+BT-545C            ISA         Fast SCSI-2
+BT-540CF    ISA                Fast SCSI-2
 
-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 "S" Series Host Adapters:
 
-"A" Series Host Adapters:
+BT-445S            VLB         Fast SCSI-2
+BT-747S            EISA        Fast SCSI-2
+BT-747D            EISA        Differential Fast SCSI-2
+BT-757S            EISA        Wide Fast SCSI-2
+BT-757D            EISA        Wide Differential Fast SCSI-2
+BT-545S            ISA         Fast SCSI-2
+BT-542D            ISA         Differential Fast SCSI-2
+BT-742A            EISA        SCSI-2 (742A revision H)
+BT-542B            ISA         SCSI-2 (542B revision H)
 
-BT-742A            EISA    Single-ended SCSI-2 (742A revisions A - G)
-BT-542B            ISA     Single-ended SCSI-2 (542B revisions A - G)
+MultiMaster "A" Series Host Adapters:
 
-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        SCSI-2 (742A revisions A - G)
+BT-542B            ISA         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 +292,45 @@ kernel command line options.  A full description of the command line options
 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 FlashPoint Host Adapters first unless
+    the BIOS primary disk is controlled by the first PCI MultiMaster Host
+    Adapter, in which case MultiMaster 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 FlashPoint Host Adapters first unless
+    the BIOS primary disk is controlled by the first PCI MultiMaster Host
+    Adapter, in which case MultiMaster 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 +341,11 @@ file "BusLogic.c".  The following examples may be useful as a starting point:
     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 +372,16 @@ substantially impact performance.
 
                                 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.
+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 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):
+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.9.tar.gz
+  mv README.* LICENSE.* BusLogic.[ch] FlashPoint.c linux/drivers/scsi
+  patch -p < BusLogic.patch
   cd linux
   make config
   make depend
index 174d897fb38d8a81104ceea5df9c044bd5007f03..d5acaa300a4676f600e2e5364efbbe5cee6c7621 100644 (file)
@@ -1,4 +1,66 @@
+The BusLogic FlashPoint SCSI Host Adapters are now fully supported on Linux.
+The upgrade program described below has been officially terminated effective
+31 March 1997 since it is no longer needed.
 
+
+
+         MYLEX INTRODUCES LINUX OPERATING SYSTEM SUPPORT FOR ITS
+             BUSLOGIC FLASHPOINT LINE OF SCSI HOST ADAPTERS
+
+
+FREMONT, CA, -- October 8, 1996 -- Mylex Corporation has expanded Linux
+operating system support to its BusLogic brand of FlashPoint Ultra SCSI
+host adapters.  All of BusLogic's other SCSI host adapters, including the
+MultiMaster line, currently support the Linux operating system.  Linux
+drivers and information will be available on October 15th at
+http://www.dandelion.com/Linux/.
+
+"Mylex is committed to supporting the Linux community," says Peter Shambora,
+vice president of marketing for Mylex.  "We have supported Linux driver
+development and provided technical support for our host adapters for several
+years, and are pleased to now make our FlashPoint products available to this
+user base."
+
+The Linux Operating System
+
+Linux is a freely-distributed implementation of UNIX for Intel x86, Sun
+SPARC, SGI MIPS, Motorola 68k, Digital Alpha AXP and Motorola PowerPC
+machines.  It supports a wide range of software, including the X Window
+System, Emacs, and TCP/IP networking.  Further information is available at
+http://www.linux.org and http://www.ssc.com/linux.
+
+FlashPoint Host Adapters
+
+The FlashPoint family of Ultra SCSI host adapters, designed for workstation
+and file server environments, are available in narrow, wide, dual channel,
+and dual channel wide versions.  These adapters feature SeqEngine
+automation technology, which minimizes SCSI command overhead and reduces
+the number of interrupts generated to the CPU.
+
+About Mylex
+
+Mylex Corporation (NASDAQ/NM SYMBOL: MYLX), founded in 1983, is a leading
+producer of RAID technology and network management products.  The company
+produces high performance disk array (RAID) controllers, and complementary
+computer products for network servers, mass storage systems, workstations
+and system boards.  Through its wide range of RAID controllers and its
+BusLogic line of Ultra SCSI host adapter products, Mylex provides enabling
+intelligent I/O technologies that increase network management control,
+enhance CPU utilization, optimize I/O performance, and ensure data security
+and availability.  Products are sold globally through a network of OEMs,
+major distributors, VARs, and system integrators.  Mylex Corporation is
+headquartered at 34551 Ardenwood Blvd., Fremont, CA.
+
+                                  ####
+
+Contact:
+
+Peter Shambora
+Vice President of Marketing
+Mylex Corp.
+510/796-6100
+peters@mylex.com
+\f
                               ANNOUNCEMENT
               BusLogic FlashPoint LT/BT-948 Upgrade Program
                              1 February 1996
index 2054a9cd9029215cc1092cdc8543cc88232b0671..8fac822db87392c2389c35ee154172b71436f31e 100644 (file)
@@ -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>
@@ -805,7 +808,9 @@ static int aha1542_mbenable(int base)
      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);
   };
index 11de4bde37d1551f2a1cd2772ed2109930309387..802dd3fba2701b504338c0f69358dd5c9b0c833d 100644 (file)
  *  for proper handling of multiple devices courteously
  *  provided by Michael Weller, March, 1993
  *
+ *  Multiple adapter support, extended translation detection,
+ *  update to current scsi subsystem changes, proc fs support,
+ *  working (!) module support based on patches from Andreas Arens,
+ *  by Andreas Degert <ad@papyrus.hamburg.com>, 2/1997
+ *
  * aha1740_makecode may still need even more work
  * if it doesn't work for your devices, take a look.
  */
@@ -59,12 +64,52 @@ struct proc_dir_entry proc_scsi_aha1740 = {
 static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/aha1740.c,v 1.1 1992/07/24 06:27:38 root Exp root $";
 */
 
-static unsigned int slot, base;
-static unsigned char irq_level;
+struct aha1740_hostdata {
+    unsigned int slot;
+    unsigned int translation;
+    unsigned int last_ecb_used;
+    struct ecb ecb[AHA1740_ECBS];
+};
+
+#define HOSTDATA(host) ((struct aha1740_hostdata *) &host->hostdata)
 
-static struct ecb ecb[AHA1740_ECBS];   /* One for each queued operation */
+/* One for each IRQ level (9-15) */
+static struct Scsi_Host * aha_host[8] = {NULL, };
+
+int aha1740_proc_info(char *buffer, char **start, off_t offset,
+                     int length, int hostno, int inout)
+{
+    int len;
+    struct Scsi_Host * shpnt;
+    struct aha1740_hostdata *host;
+
+    if (inout)
+       return(-ENOSYS);
+
+    for (len = 0; len < 8; len++) {
+       shpnt = aha_host[len];
+       if (shpnt && shpnt->host_no == hostno)
+           break;
+    }
+    host = HOSTDATA(shpnt);
+
+    len = sprintf(buffer, "aha174x at IO:%x, IRQ %d, SLOT %d.\n"
+                 "Extended translation %sabled.\n",
+                 shpnt->io_port, shpnt->irq, host->slot,
+                 host->translation ? "en" : "dis");
+
+    if (offset > len) {
+       *start = buffer;
+       return 0;
+    }
+
+    *start = buffer + offset;
+    len -= offset;
+    if (len > length)
+       len = length;
+    return len;
+}
 
-static int aha1740_last_ecb_used  = 0; /* optimization */
 
 int aha1740_makecode(unchar *sense, unchar *status)
 {
@@ -88,8 +133,9 @@ int aha1740_makecode(unchar *sense, unchar *status)
 
     status_word = * (struct statusword *) status;
 #ifdef DEBUG
-printk("makecode from %x,%x,%x,%x %x,%x,%x,%x",status[0],status[1],status[2],status[3],
-sense[0],sense[1],sense[2],sense[3]);
+    printk("makecode from %x,%x,%x,%x %x,%x,%x,%x",
+          status[0], status[1], status[2], status[3],
+          sense[0], sense[1], sense[2], sense[3]);
 #endif
     if (!status_word.don) /* Anything abnormal was detected */
     {
@@ -113,8 +159,8 @@ sense[0],sense[1],sense[2],sense[3]);
                break;
            case 0x04:
            case 0x05:
-               retval=DID_ABORT; /* Either by this driver or the AHA1740
-                                        itself */
+               retval=DID_ABORT;
+               /* Either by this driver or the AHA1740 itself */
                break;
            default:
                retval=DID_ERROR; /* No further diagnostics possible */
@@ -141,35 +187,35 @@ sense[0],sense[1],sense[2],sense[3]);
     return status[3] | retval << 16;
 }
 
-int aha1740_test_port(void)
+int aha1740_test_port(unsigned int base)
 {
-    char    name[4],tmp;
+    char name[4], tmp;
 
     /* Okay, look for the EISA ID's */
-    name[0]= 'A' -1 + ((tmp = inb(HID0)) >> 2); /* First character */
+    name[0]= 'A' -1 + ((tmp = inb(HID0(base))) >> 2); /* First character */
     name[1]= 'A' -1 + ((tmp & 3) << 3);
-    name[1]+= ((tmp = inb(HID1)) >> 5)&0x7;    /* Second Character */
+    name[1]+= ((tmp = inb(HID1(base))) >> 5)&0x7;      /* Second Character */
     name[2]= 'A' -1 + (tmp & 0x1f);            /* Third Character */
     name[3]=0;
-    tmp = inb(HID2);
-    if ( strcmp ( name, HID_MFG ) || inb(HID2) != HID_PRD )
+    tmp = inb(HID2(base));
+    if ( strcmp ( name, HID_MFG ) || inb(HID2(base)) != HID_PRD )
        return 0;   /* Not an Adaptec 174x */
 
-/*  if ( inb(HID3) != HID_REV )
-       printk("aha1740: Warning; board revision of %d; expected %d\n",
-           inb(HID3),HID_REV); */
+/*  if ( inb(HID3(base)) != HID_REV )
+       printk("aha174x: Warning; board revision of %d; expected %d\n",
+           inb(HID3(base)),HID_REV); */
 
-    if ( inb(EBCNTRL) != EBCNTRL_VALUE )
+    if ( inb(EBCNTRL(base)) != EBCNTRL_VALUE )
     {
-       printk("aha1740: Board detected, but EBCNTRL = %x, so disabled it.\n",
-           inb(EBCNTRL));
+       printk("aha174x: Board detected, but EBCNTRL = %x, so disabled it.\n",
+           inb(EBCNTRL(base)));
        return 0;
     }
 
-    if ( inb(PORTADR) & PORTADDR_ENH )
+    if ( inb(PORTADR(base)) & PORTADDR_ENH )
        return 1;   /* Okay, we're all set */
        
-    printk("aha1740: Board detected, but not in enhanced mode, so disabled it.\n");
+    printk("aha174x: Board detected, but not in enhanced mode, so disabled it.\n");
     return 0;
 }
 
@@ -181,49 +227,58 @@ void aha1740_intr_handle(int irq, void *dev_id, struct pt_regs * regs)
     int number_serviced;
     struct ecb *ecbptr;
     Scsi_Cmnd *SCtmp;
+    unsigned int base;
 
+    if (!aha_host[irq - 9])
+       panic("aha1740.c: Irq from unknown host!\n");
+    base = aha_host[irq - 9]->io_port;
     number_serviced = 0;
 
-    while(inb(G2STAT) & G2STAT_INTPEND)
+    while(inb(G2STAT(base)) & G2STAT_INTPEND)
     {
        DEB(printk("aha1740_intr top of loop.\n"));
-       adapstat = inb(G2INTST);
-       ecbptr = (struct ecb *) bus_to_virt(inl(MBOXIN0));
-       outb(G2CNTRL_IRST,G2CNTRL); /* interrupt reset */
+       adapstat = inb(G2INTST(base));
+       ecbptr = (struct ecb *) bus_to_virt(inl(MBOXIN0(base)));
+       outb(G2CNTRL_IRST,G2CNTRL(base)); /* interrupt reset */
       
        switch ( adapstat & G2INTST_MASK )
        {
        case    G2INTST_CCBRETRY:
        case    G2INTST_CCBERROR:
        case    G2INTST_CCBGOOD:
-           outb(G2CNTRL_HRDY,G2CNTRL); /* Host Ready -> Mailbox in complete */
+           /* Host Ready -> Mailbox in complete */
+           outb(G2CNTRL_HRDY,G2CNTRL(base));
            if (!ecbptr)
            {
                printk("Aha1740 null ecbptr in interrupt (%x,%x,%x,%d)\n",
-                       inb(G2STAT),adapstat,inb(G2INTST),number_serviced++);
+                      inb(G2STAT(base)),adapstat,
+                      inb(G2INTST(base)), number_serviced++);
                continue;
            }
            SCtmp = ecbptr->SCpnt;
            if (!SCtmp)
            {
                printk("Aha1740 null SCtmp in interrupt (%x,%x,%x,%d)\n",
-                       inb(G2STAT),adapstat,inb(G2INTST),number_serviced++);
+                      inb(G2STAT(base)),adapstat,
+                      inb(G2INTST(base)), number_serviced++);
                continue;
            }
            if (SCtmp->host_scribble)
                scsi_free(SCtmp->host_scribble, 512);
-         /* Fetch the sense data, and tuck it away, in the required slot.  The
-            Adaptec automatically fetches it, and there is no guarantee that
-            we will still have it in the cdb when we come back */
+           /* Fetch the sense data, and tuck it away, in the required slot.
+              The Adaptec automatically fetches it, and there is no
+              guarantee that we will still have it in the cdb when we come
+              back */
            if ( (adapstat & G2INTST_MASK) == G2INTST_CCBERROR )
-             {
+           {
                memcpy(SCtmp->sense_buffer, ecbptr->sense, 
                       sizeof(SCtmp->sense_buffer));
                errstatus = aha1740_makecode(ecbptr->sense,ecbptr->status);
-             }
+           }
            else
                errstatus = 0;
-           DEB(if (errstatus) printk("aha1740_intr_handle: returning %6x\n", errstatus));
+           DEB(if (errstatus) printk("aha1740_intr_handle: returning %6x\n",
+                                     errstatus));
            SCtmp->result = errstatus;
            my_done = ecbptr->done;
            memset(ecbptr,0,sizeof(struct ecb)); 
@@ -231,12 +286,14 @@ void aha1740_intr_handle(int irq, void *dev_id, struct pt_regs * regs)
                my_done(SCtmp);
            break;
        case    G2INTST_HARDFAIL:
-           printk("aha1740 hardware failure!\n");
+           printk(KERN_ALERT "aha1740 hardware failure!\n");
            panic("aha1740.c"); /* Goodbye */
        case    G2INTST_ASNEVENT:
-           printk("aha1740 asynchronous event: %02x %02x %02x %02x %02x\n",adapstat,
-               inb(MBOXIN0),inb(MBOXIN1),inb(MBOXIN2),inb(MBOXIN3)); /* Say What? */
-           outb(G2CNTRL_HRDY,G2CNTRL); /* Host Ready -> Mailbox in complete */
+           printk("aha1740 asynchronous event: %02x %02x %02x %02x %02x\n",
+                  adapstat, inb(MBOXIN0(base)), inb(MBOXIN1(base)),
+                  inb(MBOXIN2(base)), inb(MBOXIN3(base))); /* Say What? */
+           /* Host Ready -> Mailbox in complete */
+           outb(G2CNTRL_HRDY,G2CNTRL(base));
            break;
        case    G2INTST_CMDGOOD:
            /* set immediate command success flag here: */
@@ -245,7 +302,7 @@ void aha1740_intr_handle(int irq, void *dev_id, struct pt_regs * regs)
            /* Set immediate command failure flag here: */
            break;
        }
-      number_serviced++;
+       number_serviced++;
     }
 }
 
@@ -254,18 +311,19 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
     unchar direction;
     unchar *cmd = (unchar *) SCpnt->cmnd;
     unchar target = SCpnt->target;
+    struct aha1740_hostdata *host = HOSTDATA(SCpnt->host);
     unsigned long flags;
     void *buff = SCpnt->request_buffer;
     int bufflen = SCpnt->request_bufflen;
     int ecbno;
     DEB(int i);
 
-    
     if(*cmd == REQUEST_SENSE)
     {
        if (bufflen != sizeof(SCpnt->sense_buffer))
        {
-           printk("Wrong buffer length supplied for request sense (%d)\n",bufflen);
+           printk("Wrong buffer length supplied for request sense (%d)\n",
+                  bufflen);
        }
        SCpnt->result = 0;
        done(SCpnt); 
@@ -279,39 +337,41 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
        i = scsi2int(cmd+2);
     else
        i = -1;
-    printk("aha1740_queuecommand: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
+    printk("aha1740_queuecommand: dev %d cmd %02x pos %d len %d ",
+          target, *cmd, i, bufflen);
     printk("scsi cmd:");
     for (i = 0; i < SCpnt->cmd_len; i++) printk("%02x ", cmd[i]);
     printk("\n");
 #endif
 
     /* locate an available ecb */
-
     save_flags(flags);
     cli();
-    ecbno = aha1740_last_ecb_used + 1;         /* An optimization */
-    if (ecbno >= AHA1740_ECBS) ecbno = 0;
-
-    do{
-      if( ! ecb[ecbno].cmdw )
-       break;
-      ecbno++;
-      if (ecbno >= AHA1740_ECBS ) ecbno = 0;
-    } while (ecbno != aha1740_last_ecb_used);
+    ecbno = host->last_ecb_used + 1;           /* An optimization */
+    if (ecbno >= AHA1740_ECBS)
+       ecbno = 0;
+    do {
+       if (!host->ecb[ecbno].cmdw)
+           break;
+       ecbno++;
+       if (ecbno >= AHA1740_ECBS)
+           ecbno = 0;
+    } while (ecbno != host->last_ecb_used);
 
-    if( ecb[ecbno].cmdw )
-      panic("Unable to find empty ecb for aha1740.\n");
+    if (host->ecb[ecbno].cmdw)
+       panic("Unable to find empty ecb for aha1740.\n");
 
-    ecb[ecbno].cmdw = AHA1740CMD_INIT; /* SCSI Initiator Command doubles as reserved flag */
+    host->ecb[ecbno].cmdw = AHA1740CMD_INIT;   /* SCSI Initiator Command
+                                                  doubles as reserved flag */
 
-    aha1740_last_ecb_used = ecbno;    
+    host->last_ecb_used = ecbno;    
     restore_flags(flags);
 
 #ifdef DEBUG
-    printk("Sending command (%d %x)...",ecbno, done);
+    printk("Sending command (%d %x)...", ecbno, done);
 #endif
 
-    ecb[ecbno].cdblen = SCpnt->cmd_len;        /* SCSI Command Descriptor Block Length */
+    host->ecb[ecbno].cdblen = SCpnt->cmd_len;  /* SCSI Command Descriptor Block Length */
 
     direction = 0;
     if (*cmd == READ_10 || *cmd == READ_6)
@@ -319,28 +379,27 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
     else if (*cmd == WRITE_10 || *cmd == WRITE_6)
        direction = 0;
 
-    memcpy(ecb[ecbno].cdb, cmd, ecb[ecbno].cdblen);
+    memcpy(host->ecb[ecbno].cdb, cmd, SCpnt->cmd_len);
 
     if (SCpnt->use_sg)
     {
        struct scatterlist * sgpnt;
        struct aha1740_chain * cptr;
        int i;
-#ifdef DEBUG
-       unsigned char * ptr;
-#endif
-       ecb[ecbno].sg = 1;        /* SCSI Initiator Command  w/scatter-gather*/
+       DEB(unsigned char * ptr);
+
+       host->ecb[ecbno].sg = 1;  /* SCSI Initiator Command  w/scatter-gather*/
        SCpnt->host_scribble = (unsigned char *) scsi_malloc(512);
        sgpnt = (struct scatterlist *) SCpnt->request_buffer;
        cptr = (struct aha1740_chain *) SCpnt->host_scribble; 
        if (cptr == NULL) panic("aha1740.c: unable to allocate DMA memory\n");
        for(i=0; i<SCpnt->use_sg; i++)
        {
-           cptr[i].dataptr = (long) sgpnt[i].address;
            cptr[i].datalen = sgpnt[i].length;
+           cptr[i].dataptr = virt_to_bus(sgpnt[i].address);
        }
-       ecb[ecbno].datalen = SCpnt->use_sg * sizeof(struct aha1740_chain);
-       ecb[ecbno].dataptr = (long) cptr;
+       host->ecb[ecbno].datalen = SCpnt->use_sg * sizeof(struct aha1740_chain);
+       host->ecb[ecbno].dataptr = virt_to_bus(cptr);
 #ifdef DEBUG
        printk("cptr %x: ",cptr);
        ptr = (unsigned char *) cptr;
@@ -350,144 +409,161 @@ int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
     else
     {
        SCpnt->host_scribble = NULL;
-       ecb[ecbno].datalen = bufflen;
-       ecb[ecbno].dataptr = (long) buff;
+       host->ecb[ecbno].datalen = bufflen;
+       host->ecb[ecbno].dataptr = virt_to_bus(buff);
     }
-    ecb[ecbno].lun = SCpnt->lun;
-    ecb[ecbno].ses = 1;        /* Suppress underrun errors */
-    ecb[ecbno].dir= direction;
-    ecb[ecbno].ars=1;  /* Yes, get the sense on an error */
-    ecb[ecbno].senselen = 12;
-    ecb[ecbno].senseptr = (long) ecb[ecbno].sense;
-    ecb[ecbno].statusptr = (long) ecb[ecbno].status;
-    ecb[ecbno].done = done;
-    ecb[ecbno].SCpnt = SCpnt;
+    host->ecb[ecbno].lun = SCpnt->lun;
+    host->ecb[ecbno].ses = 1;  /* Suppress underrun errors */
+    host->ecb[ecbno].dir = direction;
+    host->ecb[ecbno].ars = 1;  /* Yes, get the sense on an error */
+    host->ecb[ecbno].senselen = 12;
+    host->ecb[ecbno].senseptr = virt_to_bus(host->ecb[ecbno].sense);
+    host->ecb[ecbno].statusptr = virt_to_bus(host->ecb[ecbno].status);
+    host->ecb[ecbno].done = done;
+    host->ecb[ecbno].SCpnt = SCpnt;
 #ifdef DEBUG
     {
        int i;
        printk("aha1740_command: sending.. ");
-       for (i = 0; i < sizeof(ecb[ecbno])-10; i++)
-           printk("%02x ", ((unchar *)&ecb[ecbno])[i]);
+       for (i = 0; i < sizeof(host->ecb[ecbno]) - 10; i++)
+           printk("%02x ", ((unchar *)&host->ecb[ecbno])[i]);
     }
     printk("\n");
 #endif
     if (done)
-    { /*  You may question the code below, which contains potentially
-         non-terminating while loops with interrupts disabled.  So did
-         I when I wrote it, but the Adaptec Spec says the card is so fast,
-         that this problem virtually never occurs so I've kept it.  We
-         do printk a warning first, so that you'll know if it happens.
-         In practice the only time we've seen this message is when some-
-         thing else is in the driver was broken, like _makecode(), or
-         when a scsi device hung the scsi bus.  Even under these conditions,
-         The loop actually only cycled < 3 times (we instrumented it). */
-
+    { /*  The Adaptec Spec says the card is so fast that the loops will
+         only be executed once in the code below. Even if this was true
+         with the fastest processors when the spec was written, it doesn't
+         seem to be true with todays fast processors. We print a warning
+         if the code is executed more often than LOOPCNT_WARN. If this
+         happens, it should be investigated. If the count reaches
+         LOOPCNT_MAX, we assume something is broken; since there is no
+         way to return an error (the return value is ignored by the
+         mid-level scsi layer) we have to panic (and maybe that's the
+         best thing we can do then anyhow). */
+
+#define LOOPCNT_WARN 10                /* excessive mbxout wait -> syslog-msg */
+#define LOOPCNT_MAX 1000000    /* mbxout deadlock -> panic() after ~ 2 sec. */
+       int loopcnt;
+       unsigned int base = SCpnt->host->io_port;
        DEB(printk("aha1740[%d] critical section\n",ecbno));
        save_flags(flags);
        cli();
-       if ( ! (inb(G2STAT) & G2STAT_MBXOUT) )
-       {
-           printk("aha1740[%d]_mbxout wait!\n",ecbno);
-           cli(); /* printk may have done a sti()! */
+       for (loopcnt = 0; ; loopcnt++) {
+           if (inb(G2STAT(base)) & G2STAT_MBXOUT) break;
+           if (loopcnt == LOOPCNT_WARN) {
+               printk("aha1740[%d]_mbxout wait!\n",ecbno);
+               cli(); /* printk may have done a sti()! */
+           }
+           if (loopcnt == LOOPCNT_MAX)
+               panic("aha1740.c: mbxout busy!\n");
        }
-       mb();
-       while ( ! (inb(G2STAT) & G2STAT_MBXOUT) );      /* Oh Well. */
-       outl(virt_to_bus(ecb+ecbno), MBOXOUT0);
-       if ( inb(G2STAT) & G2STAT_BUSY )
-       {
-           printk("aha1740[%d]_attn wait!\n",ecbno);
-           cli();
+       outl(virt_to_bus(host->ecb + ecbno), MBOXOUT0(base));
+       for (loopcnt = 0; ; loopcnt++) {
+           if (! (inb(G2STAT(base)) & G2STAT_BUSY)) break;
+           if (loopcnt == LOOPCNT_WARN) {
+               printk("aha1740[%d]_attn wait!\n",ecbno);
+               cli();
+           }
+           if (loopcnt == LOOPCNT_MAX)
+               panic("aha1740.c: attn wait failed!\n");
        }
-       while ( inb(G2STAT) & G2STAT_BUSY );            /* And Again! */
-       outb(ATTN_START | (target & 7), ATTN);  /* Start it up */
+       outb(ATTN_START | (target & 7), ATTN(base)); /* Start it up */
        restore_flags(flags);
        DEB(printk("aha1740[%d] request queued.\n",ecbno));
     }
     else
-      printk("aha1740_queuecommand: done can't be NULL\n");
-    
+       printk(KERN_ALERT "aha1740_queuecommand: done can't be NULL\n");
     return 0;
 }
 
-static volatile int internal_done_flag = 0;
-static volatile int internal_done_errcode = 0;
-
 static void internal_done(Scsi_Cmnd * SCpnt)
 {
-    internal_done_errcode = SCpnt->result;
-    ++internal_done_flag;
+    SCpnt->SCp.Status++;
 }
 
 int aha1740_command(Scsi_Cmnd * SCpnt)
 {
     aha1740_queuecommand(SCpnt, internal_done);
-
-    while (!internal_done_flag);
-    internal_done_flag = 0;
-    return internal_done_errcode;
+    SCpnt->SCp.Status = 0;
+    while (!SCpnt->SCp.Status)
+       barrier();
+    return SCpnt->result;
 }
 
 /* Query the board for its irq_level.  Nothing else matters
    in enhanced mode on an EISA bus. */
 
-void aha1740_getconfig(void)
+void aha1740_getconfig(unsigned int base, unsigned int *irq_level,
+                      unsigned int *translation)
 {
-  static int intab[] = { 9,10,11,12,0,14,15,0 };
+    static int intab[] = { 9, 10, 11, 12, 0, 14, 15, 0 };
 
-  irq_level = intab [ inb(INTDEF)&0x7 ];
-  outb(inb(INTDEF) | 0x10, INTDEF);
+    *irq_level = intab[inb(INTDEF(base)) & 0x7];
+    *translation = inb(RESV1(base)) & 0x1;
+    outb(inb(INTDEF(base)) | 0x10, INTDEF(base));
 }
 
 int aha1740_detect(Scsi_Host_Template * tpnt)
 {
-    tpnt->proc_dir = &proc_scsi_aha1740;
+    int count = 0, slot;
 
-    memset(&ecb, 0, sizeof(struct ecb));
     DEB(printk("aha1740_detect: \n"));
-    
+
     for ( slot=MINEISA; slot <= MAXEISA; slot++ )
     {
-       base = SLOTBASE(slot);
+       int slotbase;
+       unsigned int irq_level, translation;
+       struct Scsi_Host *shpnt;
+       struct aha1740_hostdata *host;
+       slotbase = SLOTBASE(slot);
        /*
         * The ioports for eisa boards are generally beyond that used in the
         * check/allocate region code, but this may change at some point,
         * so we go through the motions.
         */
-       if(check_region(base, 0x5c)) continue;  /* See if in use */
-       if ( aha1740_test_port())  break;
-    }
-    if ( slot > MAXEISA )
-       return 0;
-
-    aha1740_getconfig();
-
-    if ( (inb(G2STAT) & (G2STAT_MBXOUT | G2STAT_BUSY) ) != G2STAT_MBXOUT )
-    {  /* If the card isn't ready, hard reset it */
-       outb(G2CNTRL_HRST,G2CNTRL);
-       outb(0,G2CNTRL);    
-    }
-
-    printk("Configuring Adaptec at IO:%x, IRQ %d\n",base,
-          irq_level);
-
-    DEB(printk("aha1740_detect: enable interrupt channel %d\n", irq_level));
-
-    if (request_irq(irq_level,aha1740_intr_handle, 0, "aha1740", NULL))
-    {
-       printk("Unable to allocate IRQ for adaptec controller.\n");
-       return 0;
+       if (check_region(slotbase, SLOTSIZE))  /* See if in use */
+           continue;
+       if (!aha1740_test_port(slotbase))
+           continue;
+       aha1740_getconfig(slotbase,&irq_level,&translation);
+       if ((inb(G2STAT(slotbase)) &
+            (G2STAT_MBXOUT|G2STAT_BUSY)) != G2STAT_MBXOUT)
+       {       /* If the card isn't ready, hard reset it */
+           outb(G2CNTRL_HRST, G2CNTRL(slotbase));
+           outb(0, G2CNTRL(slotbase));
+       }
+       printk("Configuring aha174x at IO:%x, IRQ %d\n", slotbase, irq_level);
+       printk("aha174x: Extended translation %sabled.\n",
+              translation ? "en" : "dis");
+       DEB(printk("aha1740_detect: enable interrupt channel %d\n",irq_level));
+       if (request_irq(irq_level,aha1740_intr_handle,0,"aha1740",NULL)) {
+           printk("Unable to allocate IRQ for adaptec controller.\n");
+           continue;
+       }
+       shpnt = scsi_register(tpnt, sizeof(struct aha1740_hostdata));
+       request_region(slotbase, SLOTSIZE, "aha1740");
+       shpnt->base = 0;
+       shpnt->io_port = slotbase;
+       shpnt->n_io_port = SLOTSIZE;
+       shpnt->irq = irq_level;
+       shpnt->dma_channel = 0xff;
+       host = HOSTDATA(shpnt);
+       host->slot = slot;
+       host->translation = translation;
+       aha_host[irq_level - 9] = shpnt;
+       count++;
     }
-    request_region(base, 0x5c,"aha1740");  /* Reserve the space that we need to use */
-    return 1;
+    return count;
 }
 
 /* Note:  They following two functions do not apply very well to the Adaptec,
-which basically manages its own affairs quite well without our interference,
-so I haven't put anything into them.  I can faintly imagine someone with a
-*very* badly behaved SCSI target (perhaps an old tape?) wanting the abort(),
-but it hasn't happened yet, and doing aborts brings the Adaptec to its
-knees.  I cannot (at this moment in time) think of any reason to reset the
-card once it's running.  So there. */
+   which basically manages its own affairs quite well without our interference,
+   so I haven't put anything into them.  I can faintly imagine someone with a
+   *very* badly behaved SCSI target (perhaps an old tape?) wanting the abort(),
+   but it hasn't happened yet, and doing aborts brings the Adaptec to its
+   knees.  I cannot (at this moment in time) think of any reason to reset the
+   card once it's running.  So there. */
 
 int aha1740_abort(Scsi_Cmnd * SCpnt)
 {
@@ -499,7 +575,7 @@ int aha1740_abort(Scsi_Cmnd * SCpnt)
    that it will get some kind of response for the command in SCpnt.  We must
    oblige, or the command will hang the scsi system */
 
-int aha1740_reset(Scsi_Cmnd * SCpnt)
+int aha1740_reset(Scsi_Cmnd * SCpnt, unsigned int ignored)
 {
     DEB(printk("aha1740_reset called\n"));
     return SCSI_RESET_PUNT;
@@ -507,13 +583,23 @@ int aha1740_reset(Scsi_Cmnd * SCpnt)
 
 int aha1740_biosparam(Disk * disk, kdev_t dev, int* ip)
 {
-  int size = disk->capacity;
-DEB(printk("aha1740_biosparam\n"));
-  ip[0] = 64;
-  ip[1] = 32;
-  ip[2] = size >> 11;
-/*  if (ip[2] >= 1024) ip[2] = 1024; */
-  return 0;
+    int size = disk->capacity;
+    int extended = HOSTDATA(disk->device->host)->translation;
+
+    DEB(printk("aha1740_biosparam\n"));
+    if (extended && (ip[2] > 1024))
+    {
+       ip[0] = 255;
+       ip[1] = 63;
+       ip[2] = size / (255 * 63);
+    }
+    else
+    {
+       ip[0] = 64;
+       ip[1] = 32;
+       ip[2] = size >> 11;
+    }
+    return 0;
 }
 
 #ifdef MODULE
index 0c5de11a223f50060877b3136d5541bf5ccc66ad..cd38a894b3c905b41e242361787e79295873075d 100644 (file)
 #define MINEISA 1   /* I don't have an EISA Spec to know these ranges, so I */
 #define MAXEISA 8   /* Just took my machine's specifications.  Adjust to fit.*/
                    /* I just saw an ad, and bumped this from 6 to 8 */
-#define        SLOTBASE(x)     ((x << 12)+ 0xc80 )
-#define        BASE            (base)
+#define        SLOTBASE(x)     ((x << 12) + 0xc80)
+#define SLOTSIZE       0x5c
 
 /* EISA configuration registers & values */
-#define        HID0    (base + 0x0)
-#define        HID1    (base + 0x1)
-#define HID2   (base + 0x2)
-#define        HID3    (base + 0x3)
-#define        EBCNTRL (base + 0x4)
-#define        PORTADR (base + 0x40)
-#define BIOSADR (base + 0x41)
-#define INTDEF (base + 0x42)
-#define SCSIDEF (base + 0x43)
-#define BUSDEF (base + 0x44)
-#define        RESV0   (base + 0x45)
-#define RESV1  (base + 0x46)
-#define        RESV2   (base + 0x47)
+#define        HID0(base)      (base + 0x0)
+#define        HID1(base)      (base + 0x1)
+#define HID2(base)     (base + 0x2)
+#define        HID3(base)      (base + 0x3)
+#define        EBCNTRL(base)   (base + 0x4)
+#define        PORTADR(base)   (base + 0x40)
+#define BIOSADR(base)  (base + 0x41)
+#define INTDEF(base)   (base + 0x42)
+#define SCSIDEF(base)  (base + 0x43)
+#define BUSDEF(base)   (base + 0x44)
+#define        RESV0(base)     (base + 0x45)
+#define RESV1(base)    (base + 0x46)
+#define        RESV2(base)     (base + 0x47)
 
 #define        HID_MFG "ADP"
 #define        HID_PRD 0
 #define EBCNTRL_VALUE 1
 #define PORTADDR_ENH 0x80
 /* READ */
-#define        G2INTST (BASE + 0x56)
-#define G2STAT (BASE + 0x57)
-#define        MBOXIN0 (BASE + 0x58)
-#define        MBOXIN1 (BASE + 0x59)
-#define        MBOXIN2 (BASE + 0x5a)
-#define        MBOXIN3 (BASE + 0x5b)
-#define G2STAT2        (BASE + 0x5c)
+#define        G2INTST(base)   (base + 0x56)
+#define G2STAT(base)   (base + 0x57)
+#define        MBOXIN0(base)   (base + 0x58)
+#define        MBOXIN1(base)   (base + 0x59)
+#define        MBOXIN2(base)   (base + 0x5a)
+#define        MBOXIN3(base)   (base + 0x5b)
+#define G2STAT2(base)  (base + 0x5c)
 
 #define G2INTST_MASK           0xf0    /* isolate the status */
 #define        G2INTST_CCBGOOD         0x10    /* CCB Completed */
 #define G2STAT2_READY  0       /* Host Ready Bit */
 
 /* WRITE (and ReadBack) */
-#define        MBOXOUT0        (BASE + 0x50)
-#define        MBOXOUT1        (BASE + 0x51)
-#define        MBOXOUT2        (BASE + 0x52)
-#define        MBOXOUT3        (BASE + 0x53)
-#define        ATTN            (BASE + 0x54)
-#define G2CNTRL                (BASE + 0x55)
+#define        MBOXOUT0(base)  (base + 0x50)
+#define        MBOXOUT1(base)  (base + 0x51)
+#define        MBOXOUT2(base)  (base + 0x52)
+#define        MBOXOUT3(base)  (base + 0x53)
+#define        ATTN(base)      (base + 0x54)
+#define G2CNTRL(base)  (base + 0x55)
 
 #define        ATTN_IMMED      0x10    /* Immediate Command */
 #define        ATTN_START      0x40    /* Start CCB */
@@ -157,20 +157,24 @@ int aha1740_detect(Scsi_Host_Template *);
 int aha1740_command(Scsi_Cmnd *);
 int aha1740_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int aha1740_abort(Scsi_Cmnd *);
-int aha1740_reset(Scsi_Cmnd *);
+int aha1740_reset(Scsi_Cmnd *, unsigned int);
 int aha1740_biosparam(Disk *, kdev_t, int*);
+int aha1740_proc_info(char *buffer, char **start, off_t offset,
+                               int length, int hostno, int inout);
 
 #define AHA1740_ECBS 32
 #define AHA1740_SCATTER 16
+#define AHA1740_CMDLUN 1
 
 #ifndef NULL
-#define NULL 0
+       #define NULL 0
 #endif
 
+extern struct proc_dir_entry proc_scsi_aha1740;
 
 #define AHA1740 {NULL, NULL,                           \
-                   NULL,                                \
-                  NULL,                                \
+                   &proc_scsi_aha1740,                 \
+                  aha1740_proc_info,                   \
                   "Adaptec 174x (EISA)",               \
                   aha1740_detect,                      \
                   NULL,                                \
@@ -184,10 +188,9 @@ int aha1740_biosparam(Disk *, kdev_t, int*);
                   AHA1740_ECBS,                        \
                   7,                                   \
                   AHA1740_SCATTER,                     \
-                  1,                                   \
+                  AHA1740_CMDLUN,                      \
                   0,                                   \
                   0,                                   \
                   ENABLE_CLUSTERING}
 
 #endif
-
index f0982dc8a6ad7c539fdc3f7c5acaa1c4eb96ded9..f3982aa91258c1705d4082b850650d1557c5b04c 100644 (file)
@@ -874,7 +874,7 @@ debug_config(struct aic7xxx_host_config *p)
         "        scsi id %d\n"
         "        scsi selection timeout %d ms\n"
         "        scsi bus reset at power-on %sabled\n",
-        scsi_conf & 0x07,
+        (p->bus_type & AIC_WIDE) ? (scsi_conf & 0x0f) : (scsi_conf & 0x07),
         SST[(scsi_conf >> 3) & 0x03],
         (scsi_conf & 0x40) ? "en" : "dis");
 
index 9290c38a5206e180dcc2ecb36ecb68c7135def04..fb82d349af7bd09466255bc22cc0bc42bd8c9cf2 100644 (file)
@@ -1,6 +1,25 @@
 /*
  *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
  *
+ *      24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
+ *          When loading as a module, parameter passing is now supported
+ *          both in 2.0 and in 2.1 style.
+ *          Fixed data transfer direction for some SCSI opcodes.
+ *          Immediate acknowledge to request sense commands.
+ *          Linked commands to each disk device are now reordered by elevator
+ *          sorting. Rare cases in which reordering of write requests could 
+ *          cause wrong results are managed.
+ *          Fixed spurious timeouts caused by long simple queue tag sequences.
+ *          New command line option (tm:[0-3]) to choose the type of tags:
+ *          0 -> mixed (default); 1 -> simple; 2 -> head; 3 -> ordered.
+ *
+ *      18 Jan 1997 rev. 2.60 for linux 2.1.21 and 2.0.28
+ *          Added command line options to enable/disable linked commands
+ *          (lc:[y|n]), tagged commands (tc:[y|n]) and to set the max queue
+ *          depth (mq:xx). Default is "eata=lc:n,tc:n,mq:16".
+ *          Improved command linking.
+ *          Documented how to setup RAID-0 with DPT SmartRAID boards.
+ *
  *       8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27
  *          Added linked command support.
  *          Improved detection of PCI boards using ISA base addresses.
@@ -12,7 +31,7 @@
  *          When CONFIG_PCI is defined, BIOS32 is used to include in the
  *          list of i/o ports to be probed all the PCI SCSI controllers.
  *          The list of i/o ports to be probed can be overwritten by the
- *          "eata=port0, port1,...." boot command line option.
+ *          "eata=port0,port1,...." boot command line option.
  *          Scatter/gather lists are now allocated by a number of kmalloc
  *          calls, in order to avoid the previous size limit of 64Kb.
  *
  *  v003.D0, firmware v07G.0).
  *
  *  DPT SmartRAID boards support "Hardware Array" - a group of disk drives
- *  which are all members of the same RAID-1 or RAID-5 array implemented
+ *  which are all members of the same RAID-0, RAID-1 or RAID-5 array implemented
  *  in host adapter hardware. Hardware Arrays are fully compatible with this
  *  driver, since they look to it as a single disk drive.
- *  By contrast RAID-0 are implemented as "Array Group", which does not
- *  seem to be supported correctly by the actual SCSI subsystem.
- *  To get RAID-0 functionality, the linux MD driver must be used instead of
- *  the DPT "Array Group" feature.
+ *
+ *  WARNING: to create a RAID-0 "Hardware Array" you must select "Other Unix"
+ *  as the current OS in the DPTMGR "Initial System Installation" menu.
+ *  Otherwise RAID-0 is generated as an "Array Group" (i.e. software RAID-0), 
+ *  which is not supported by the actual SCSI subsystem.
+ *  To get the "Array Group" functionality, the Linux MD driver must be used
+ *  instead of the DPT "Array Group" feature.
  *
  *  Multiple ISA, EISA and PCI boards can be configured in the same system.
  *  It is suggested to put all the EISA boards on the same IRQ level, all
  *  - ISA  0x170, 0x230, 0x330.
  * 
  *  The above list of detection probes can be totally replaced by the
- *  boot command line option: "eata=port0, port1, port2,...", where the
+ *  boot command line option: "eata=port0,port1,port2,...", where the
  *  port0, port1... arguments are ISA/EISA/PCI addresses to be probed.
- *  For example using "eata=0x7410, 0x7450, 0x230", the driver probes
+ *  For example using "eata=0x7410,0x7450,0x230", the driver probes
  *  only the two PCI addresses 0x7410 and 0x7450 and the ISA address 0x230,
  *  in this order; "eata=0" totally disables this driver.
  *
+ *  After the optional list of detection probes, other possible command line
+ *  options are:
+ *
+ *  lc:y  enables linked commands;
+ *  lc:n  disables linked commands;
+ *  tc:y  enables tagged commands;
+ *  tc:n  disables tagged commands;
+ *  tm:0  use head/simple/ordered queue tag sequences for reads and ordered
+ *        queue tags for writes;
+ *  tm:1  use only simple queue tags;
+ *  tm:2  use only head of queue tags;
+ *  tm:3  use only ordered queue tags;
+ *  mq:xx set the max queue depth to the value xx (2 <= xx <= 32).
+ *
+ *  The default value is: "eata=lc:n,tc:n,mq:16,tm:0". An example using
+ *  the list of detection probes could be: "eata=0x7410,0x230,lc:y,tc:n,mq:4".
+ *
+ *  When loading as a module, parameters can be specified as well.
+ *  The above example would be (use 1 in place of y and 0 in place of n):
+ *
+ *  modprobe eata io_port=0x7410,0x230 linked_comm=1 tagged_comm=0 \
+ *                max_queue_depth=4 tag_mode=0
+ *
+ *  ----------------------------------------------------------------------------
+ *  In this implementation, linked commands are designed to work with any DISK
+ *  or CD-ROM, since this linking has only the intent of clustering (time-wise)
+ *  and reordering by elevator sorting commands directed to each device,
+ *  without any relation with the actual SCSI protocol between the controller
+ *  and the device.
+ *  If Q is the queue depth reported at boot time for each device (also named
+ *  cmds/lun) and Q > 2, whenever there is already an active command to the
+ *  device all other commands to the same device  (up to Q-1) are kept waiting
+ *  in the elevator sorting queue. When the active command completes, the
+ *  commands in this queue are sorted by sector address. The sort is chosen
+ *  between increasing or decreasing by minimizing the seek distance between
+ *  the sector of the commands just completed and the sector of the first 
+ *  command in the list to be sorted. 
+ *  Trivial math assures that if there are (Q-1) outstanding request for
+ *  random seeks over S sectors, the unsorted average seek distance is S/2,
+ *  while the sorted average seek distance is S/(Q-1). The seek distance is
+ *  hence divided by a factor (Q-1)/2.
+ *  The above pure geometric remarks are valid in all cases and the 
+ *  driver effectively reduces the seek distance by the predicted factor
+ *  when there are Q concurrent read i/o operations on the device, but this
+ *  does not necessarily results in a noticeable performance improvement:
+ *  your mileage may vary....
+ *
+ *  Note: command reordering inside a batch of queued commands could cause
+ *        wrong results only if there is at least one write request and the
+ *        intersection (sector-wise) of all requests is not empty. 
+ *        When the driver detects a batch including overlapping requests
+ *        (a really rare event) strict serial (pid) order is enforced.
+ *  ----------------------------------------------------------------------------
+ *
  *  The boards are named EATA0, EATA1,... according to the detection order.
  *
  *  In order to support multiple ISA boards in a reliable way,
  *  the driver sets host->wish_block = TRUE for all ISA boards.
  */
 
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#define MAX_INT_PARAM 10
 #if defined(MODULE)
 #include <linux/module.h>
 #include <linux/version.h>
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,26)
+MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i");
+MODULE_PARM(linked_comm, "i");
+MODULE_PARM(tagged_comm, "i");
+MODULE_PARM(link_statistics, "i");
+MODULE_PARM(max_queue_depth, "i");
+MODULE_PARM(tag_mode, "i");
+MODULE_AUTHOR("Dario Ballabio");
+#endif
 #endif
 
 #include <linux/string.h>
@@ -235,9 +322,9 @@ struct proc_dir_entry proc_scsi_eata2x = {
 #define MAX_LARGE_SGLIST 252
 #define MAX_INTERNAL_RETRIES 64
 #define MAX_CMD_PER_LUN 2
-#define MAX_TAGGED_CMD_PER_LUN 16
+#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
 
-#define SKIP 1
+#define SKIP UINT_MAX
 #define FALSE 0
 #define TRUE 1
 #define FREE 0
@@ -249,6 +336,10 @@ struct proc_dir_entry proc_scsi_eata2x = {
 #define ABORTING 6
 #define NO_DMA  0xff
 #define MAXLOOP 200000
+#define TAG_MIXED    0
+#define TAG_SIMPLE   1
+#define TAG_HEAD     2
+#define TAG_ORDERED  3
 
 #define REG_CMD         7
 #define REG_STATUS      7
@@ -281,6 +372,8 @@ struct proc_dir_entry proc_scsi_eata2x = {
 #define ASST              0x01
 
 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+#define YESNO(a) ((a) ? 'y' : 'n')
+#define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM)
 
 /* "EATA", in Big Endian format */
 #define EATA_SIGNATURE 0x41544145
@@ -298,7 +391,8 @@ struct eata_info {
          version:4;    /* EATA version, should be 0x1 */
    unchar  ocsena:1,    /* Overlap Command Support Enabled */
           tarsup:1,    /* Target Mode Supported */
-                :2,
+           trnxfr:1,    /* Truncate Transfer Cmd NOT Necessary */
+           morsup:1,    /* More Supported */
           dmasup:1,    /* DMA Supported */
           drqvld:1,    /* DRQ Index (DRQX) is valid */
              ata:1,    /* This is an ATA device */
@@ -327,10 +421,13 @@ struct eata_info {
 
    /* Structure extension defined in EATA 2.0C */
    unchar   max_lun;    /* Max SCSI LUN number */
-   unchar        :6,
+   unchar        :4,
+               m1:1,    /* This is a PCI with an M1 chip installed */
+          idquest:1,    /* RAIDNUM returned is questionable */
               pci:1,    /* This board is PCI */
              eisa:1;    /* This board is EISA */
-   unchar   notused[2];
+   unchar   raidnum;    /* Uniquely identifies this HBA in a system */
+   unchar   notused;
 
    ushort ipad[247];
    };
@@ -373,9 +470,13 @@ struct mscp {
             dout:1,     /* Direction of Transfer is Out (Host to Target) */
              din:1;     /* Direction of Transfer is In (Target to Host) */
    unchar sense_len;     /* Request Sense Length */
-   unchar unused[4];
+   unchar unused[3];
+   unchar  fwnest:1,     /* Send command to a component of an Array Group */
+                 :7;
    unchar phsunit:1,     /* Send to Target Physical Unit (bypass RAID) */
-         notused:7;
+              iat:1,     /* Inhibit Address Translation */
+            hbaci:1,     /* Inhibit HBA Caching for this command */
+                 :5;
    unchar  target:5,     /* SCSI target ID */
           channel:3;     /* SCSI channel number */
    unchar     lun:5,     /* SCSI logical unit number */
@@ -390,7 +491,6 @@ struct mscp {
    ulong  sp_addr;       /* Address where sp is DMA'ed when cp completes */
    ulong  sense_addr;    /* Address where Sense Data is DMA'ed on error */
    unsigned int index;   /* cp index */
-   unsigned int link_id; /* reference cp for linked commands */
    struct sg_list *sglist;
    };
 
@@ -417,14 +517,18 @@ static struct Scsi_Host *sh[MAX_BOARDS + 1];
 static const char *driver_name = "EATA";
 static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
 
-static unsigned int io_port[MAX_BOARDS + 1] = { 
+static unsigned int io_port[] = { 
+
+   /* Space for MAX_INT_PARAM ports usable while loading as a module */
+   SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
+   SKIP,    SKIP,
 
    /* First ISA */
    0x1f0,
 
    /* Space for MAX_PCI ports possibly reported by PCI_BIOS */
-    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
-    SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
+   SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
+   SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
 
    /* MAX_EISA ports */
    0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
@@ -446,19 +550,22 @@ static unsigned int io_port[MAX_BOARDS + 1] = {
 #define DEV2V(addr) ((addr) ? DEV2H(bus_to_virt((unsigned long)addr)) : 0)
 
 static void eata2x_interrupt_handler(int, void *, struct pt_regs *);
+static void flush_dev(Scsi_Device *, unsigned long, unsigned int, unsigned int);
 static int do_trace = FALSE;
 static int setup_done = FALSE;
+static int link_statistics = 0;
+static int tag_mode = TAG_MIXED;
 
 #if defined (CONFIG_SCSI_EATA_TAGGED_QUEUE)
-static int tagged_commands = TRUE;
+static int tagged_comm = TRUE;
 #else
-static int tagged_commands = FALSE;
+static int tagged_comm = FALSE;
 #endif
 
 #if defined (CONFIG_SCSI_EATA_LINKED_COMMANDS)
-static int linked_commands = TRUE;
+static int linked_comm = TRUE;
 #else
-static int linked_commands = FALSE;
+static int linked_comm = FALSE;
 #endif
 
 #if defined CONFIG_SCSI_EATA_MAX_TAGS
@@ -481,37 +588,50 @@ static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
 
       if (dev->host != host) continue;
 
-      if (dev->tagged_supported) ntag++;
-      else                       nuntag++;
+      if (TLDEV(dev->type) && (dev->tagged_supported || linked_comm))
+         ntag++;
+      else
+         nuntag++;
       }
 
    utqd = MAX_CMD_PER_LUN;
 
-   tqd = (host->can_queue - utqd * nuntag) / (ntag + 1);
+   tqd = (host->can_queue - utqd * nuntag) / (ntag ? ntag : 1);
 
    if (tqd > max_queue_depth) tqd = max_queue_depth;
 
    if (tqd < MAX_CMD_PER_LUN) tqd = MAX_CMD_PER_LUN;
 
    for(dev = devlist; dev; dev = dev->next) {
-      char *tag_suffix = "";
+      char *tag_suffix = "", *link_suffix = "";
 
       if (dev->host != host) continue;
 
-      if (dev->tagged_supported) dev->queue_depth = tqd;
-      else                       dev->queue_depth = utqd;
+      if (TLDEV(dev->type) && (dev->tagged_supported || linked_comm))
+         dev->queue_depth = tqd;
+      else
+         dev->queue_depth = utqd;
+
+      if (TLDEV(dev->type)) {
+         if (linked_comm && dev->queue_depth > 2)
+            link_suffix = ", linked";
+         else
+            link_suffix = ", unlinked";
+         }
 
-      if (tagged_commands && dev->tagged_supported) {
+      if (tagged_comm && dev->tagged_supported && TLDEV(dev->type)) {
          dev->tagged_queue = 1;
          dev->current_tag = 1;
          }
 
-      if (dev->tagged_supported && dev->tagged_queue) tag_suffix = ", tagged";
-      else if (dev->tagged_supported) tag_suffix = ", untagged";
+      if (dev->tagged_supported && TLDEV(dev->type) && dev->tagged_queue)
+         tag_suffix = ", tagged";
+      else if (dev->tagged_supported && TLDEV(dev->type))
+         tag_suffix = ", untagged";
 
-      printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s.\n",
+      printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s%s.\n",
              BN(j), host->host_no, dev->channel, dev->id, dev->lun,
-             dev->queue_depth, tag_suffix);
+             dev->queue_depth, link_suffix, tag_suffix);
       }
 
    restore_flags(flags);
@@ -563,7 +683,7 @@ static inline int port_detect(unsigned int port_base, unsigned int j,
    unsigned char irq, dma_channel, subversion, i;
    unsigned char protocol_rev;
    struct eata_info info;
-   char *bus_type, dma_name[16];
+   char *bus_type, dma_name[16], tag_type;
 
    /* Allowed DMA channels for ISA (0 indicates reserved) */
    unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
@@ -779,10 +899,18 @@ static inline int port_detect(unsigned int port_base, unsigned int j,
 
    if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
 
-   printk("%s: 2.0%c, %s 0x%03x, IRQ %u, %s, SG %d, MB %d, TC %d, LC %d, "\
-          "MQ %d.\n", BN(j), HD(j)->protocol_rev, bus_type, sh[j]->io_port,
+   if (tagged_comm) {
+      if      (tag_mode == TAG_SIMPLE)  tag_type = '1';
+      else if (tag_mode == TAG_HEAD)    tag_type = '2';
+      else if (tag_mode == TAG_ORDERED) tag_type = '3';
+      else                              tag_type = 'y';
+      }
+   else                                 tag_type = 'n';
+
+   printk("%s: 2.0%c, %s 0x%03x, IRQ %u, %s, SG %d, MB %d, tc:%c, lc:%c, "\
+          "mq:%d.\n", BN(j), HD(j)->protocol_rev, bus_type, sh[j]->io_port,
           sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue,
-          tagged_commands, linked_commands, max_queue_depth);
+          tag_type, YESNO(linked_comm), max_queue_depth);
 
    if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
       printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
@@ -793,10 +921,11 @@ static inline int port_detect(unsigned int port_base, unsigned int j,
              BN(j), i, info.host_addr[3 - i]);
 
 #if defined (DEBUG_DETECT)
-   printk("%s: Vers. 0x%x, ocs %u, tar %u, SYNC 0x%x, sec. %u, "\
-          "infol %ld, cpl %ld spl %ld.\n", name, info.version,
-          info.ocsena, info.tarsup, info.sync, info.second,
-          DEV2H(info.data_len), DEV2H(info.cp_len), DEV2H(info.sp_len));
+   printk("%s: Vers. 0x%x, ocs %u, tar %u, trnxfr %u, more %u, SYNC 0x%x, "\
+          "sec. %u, infol %ld, cpl %ld spl %ld.\n", name, info.version,
+          info.ocsena, info.tarsup, info.trnxfr, info.morsup, info.sync,
+          info.second, DEV2H(info.data_len), DEV2H(info.cp_len),
+          DEV2H(info.sp_len));
 
    if (protocol_rev == 'B' || protocol_rev == 'C')
       printk("%s: isaena %u, forcaddr %u, max_id %u, max_chan %u, "\
@@ -804,8 +933,9 @@ static inline int port_detect(unsigned int port_base, unsigned int j,
              info.max_id, info.max_chan, info.large_sg, info.res1);
 
    if (protocol_rev == 'C')
-      printk("%s: max_lun %u, pci %u, eisa %u.\n", name, 
-             info.max_lun, info.pci, info.eisa);
+      printk("%s: max_lun %u, m1 %u, idquest %u, pci %u, eisa %u, "\
+             "raidnum %u.\n", name, info.max_lun, info.m1, info.idquest, 
+             info.pci, info.eisa, info.raidnum);
 #endif
 
    return TRUE;
@@ -813,15 +943,34 @@ static inline int port_detect(unsigned int port_base, unsigned int j,
 
 void eata2x_setup(char *str, int *ints) {
    int i, argc = ints[0];
+   char *cur = str, *pc;
 
-   if (argc <= 0) return;
+   if (argc > 0) {
 
-   if (argc > MAX_BOARDS) argc = MAX_BOARDS;
+      if (argc > MAX_INT_PARAM) argc = MAX_INT_PARAM;
 
-   for (i = 0; i < argc; i++) io_port[i] = ints[i + 1]; 
+      for (i = 0; i < argc; i++) io_port[i] = ints[i + 1]; 
    
-   io_port[i] = 0;
-   setup_done = TRUE;
+      io_port[i] = 0;
+      setup_done = TRUE;
+      }
+
+   while (cur && (pc = strchr(cur, ':'))) {
+      int val = 0, c = *++pc;
+
+      if (c == 'n' || c == 'N') val = FALSE;
+      else if (c == 'y' || c == 'Y') val = TRUE;
+      else val = (int) simple_strtoul(pc, NULL, 0);
+
+      if (!strncmp(cur, "lc:", 3)) linked_comm = val;
+      else if (!strncmp(cur, "tc:", 3)) tagged_comm = val;
+      else if (!strncmp(cur, "tm:", 3)) tag_mode = val;
+      else if (!strncmp(cur, "mq:", 3))  max_queue_depth = val;
+      else if (!strncmp(cur, "ls:", 3))  link_statistics = val;
+
+      if ((cur = strchr(cur, ','))) ++cur;
+      }
+
    return;
 }
 
@@ -852,7 +1001,7 @@ static void add_pci_ports(void) {
              continue;
 
       /* Reverse the returned address order */
-      io_port[MAX_PCI - k] = 
+      io_port[MAX_INT_PARAM + MAX_PCI - k] = 
              (addr & PCI_BASE_ADDRESS_IO_MASK) + PCI_BASE_ADDRESS_0;
       }
 #endif
@@ -869,6 +1018,14 @@ int eata2x_detect(Scsi_Host_Template *tpnt) {
    save_flags(flags);
    cli();
 
+#if defined(MODULE)
+   /* io_port could have been modified when loading as a module */
+   if(io_port[0] != SKIP) {
+      setup_done = TRUE;
+      io_port[MAX_INT_PARAM] = 0;
+      }
+#endif
+
    for (k = 0; k < MAX_IRQ; k++) {
       irqlist[k] = 0;
       calls[k] = 0;
@@ -914,9 +1071,15 @@ int eata2x_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
    struct mssp *spp;
 
    static const unsigned char data_out_cmds[] = {
-      0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x0b, 0x10, 0x16, 0x18, 0x1d, 
-      0x24, 0x2b, 0x2e, 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 
-      0x3f, 0x40, 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea
+      0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e,
+      0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40, 
+      0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b
+      };
+
+   static const unsigned char data_none_cmds[] = {
+      0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e,
+      0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47,
+      0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5
       };
 
    save_flags(flags);
@@ -926,6 +1089,16 @@ int eata2x_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
 
    if (!done) panic("%s: qcomm, pid %ld, null done.\n", BN(j), SCpnt->pid);
 
+   if (SCpnt->cmnd[0] == REQUEST_SENSE && SCpnt->sense_buffer[0]) {
+      SCpnt->result = DID_OK << 16; 
+      SCpnt->host_scribble = NULL;
+      printk("%s: qcomm, target %d.%d:%d, pid %ld, request sense ignored.\n",
+             BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid);
+      restore_flags(flags);
+      done(SCpnt);    
+      return 0;
+      }
+
    /* i is the mailbox number, look for the first free mailbox 
       starting from last_cp_used */
    i = HD(j)->last_cp_used + 1;
@@ -984,7 +1157,13 @@ int eata2x_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
        break;
        }
 
-   cpp->din = !cpp->dout;
+   if ((cpp->din = !cpp->dout))
+      for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
+        if (SCpnt->cmnd[0] == data_none_cmds[k]) {
+          cpp->din = FALSE;
+          break;
+          }
+
    cpp->reqsen = TRUE;
    cpp->dispri = TRUE;
    cpp->one = TRUE;
@@ -997,11 +1176,20 @@ int eata2x_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
    
    if (SCpnt->device->tagged_queue) {
 
-      if (HD(j)->target_redo[SCpnt->target][SCpnt->channel] || 
-                            HD(j)->target_to[SCpnt->target][SCpnt->channel])
+      if (HD(j)->target_redo[SCpnt->target][SCpnt->channel] ||
+            HD(j)->target_to[SCpnt->target][SCpnt->channel])
          cpp->mess[0] = ORDERED_QUEUE_TAG;
-      else
+      else if (tag_mode == TAG_SIMPLE)  cpp->mess[0] = SIMPLE_QUEUE_TAG;
+      else if (tag_mode == TAG_HEAD)    cpp->mess[0] = HEAD_OF_QUEUE_TAG;
+      else if (tag_mode == TAG_ORDERED) cpp->mess[0] = ORDERED_QUEUE_TAG;
+      else if ((SCpnt->device->current_tag % SCpnt->device->queue_depth) == 0)
+         cpp->mess[0] = ORDERED_QUEUE_TAG;
+      else if ((SCpnt->device->current_tag % SCpnt->device->queue_depth) == 1)
+         cpp->mess[0] = HEAD_OF_QUEUE_TAG;
+      else if (cpp->din)
          cpp->mess[0] = SIMPLE_QUEUE_TAG;
+      else
+         cpp->mess[0] = ORDERED_QUEUE_TAG;
 
       cpp->mess[1] = SCpnt->device->current_tag++;
       }
@@ -1017,27 +1205,13 @@ int eata2x_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
 
    memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len);
 
-   if (linked_commands && SCpnt->device->tagged_supported
-            && !HD(j)->target_to[SCpnt->target][SCpnt->channel]
-           && !HD(j)->target_redo[SCpnt->target][SCpnt->channel])
-   
-      for (k = 0; k < sh[j]->can_queue; k++) {
-         if (HD(j)->cp_stat[k] != IN_USE) continue;
-
-         if ((&HD(j)->cp[k])->SCpnt->device != SCpnt->device) continue;
-
-         cpp->link_id = k;
-         HD(j)->cp_stat[i] = READY;
-
-#if defined (DEBUG_LINKED_COMMANDS)
-         printk("%s: qcomm, target %d.%d:%d, pid %ld, Mbox %d ready.\n", 
-                BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun,
-                SCpnt->pid, i);
-#endif
-         restore_flags(flags);
-         return 0;
-         }
+   if (linked_comm && SCpnt->device->queue_depth > 2
+                                     && TLDEV(SCpnt->device->type)) {
+      HD(j)->cp_stat[i] = READY;
+      flush_dev(SCpnt->device, 0, j, FALSE);
+      restore_flags(flags);
+      return 0;
+      }
 
    /* Send control packet to the board */
    if (do_dma(sh[j]->io_port, (unsigned int) cpp, SEND_CP_DMA)) {
@@ -1273,38 +1447,152 @@ int eata2x_reset(Scsi_Cmnd *SCarg, unsigned int reset_flags) {
       }
 }
 
-static inline void process_ready_list(unsigned int i, unsigned int j) {
+static void sort(unsigned long sk[], unsigned int da[], unsigned int n,
+                 unsigned int rev) {
+   unsigned int i, j, k, y;
+   unsigned long x;
+
+   for (i = 0; i < n - 1; i++) {
+      k = i;
+
+      for (j = k + 1; j < n; j++) 
+         if (rev) {
+            if (sk[j] > sk[k]) k = j;
+            }
+         else {
+            if (sk[j] < sk[k]) k = j;
+            }
+
+      if (k != i) {
+         x = sk[k]; sk[k] = sk[i]; sk[i] = x;
+         y = da[k]; da[k] = da[i]; da[i] = y;
+         }
+      }
+
+   return;
+   }
+
+static inline void reorder(unsigned int j, unsigned long cursec,
+                 unsigned int ihdlr, unsigned int il[], unsigned int n_ready) {
    Scsi_Cmnd *SCpnt;
-   unsigned int k, n_ready = 0;
    struct mscp *cpp;
+   unsigned int k, n;
+   unsigned int rev = FALSE, s = TRUE, r = TRUE;
+   unsigned int input_only = TRUE, overlap = FALSE;
+   unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
+   unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0;
+
+   static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
+   static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
+   static unsigned int readysorted = 0, revcount = 0;
+   static unsigned long seeksorted = 0, seeknosort = 0;
+
+   if (link_statistics && !(++flushcount % link_statistics))
+      printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\
+             " av %ldK as %ldK.\n", flushcount, batchcount, inputcount,
+             ovlcount, readycount, readysorted, sortcount, revcount,
+             seeknosort / (readycount - batchcount + 1), 
+             seeksorted / (readycount - batchcount + 1));
+
+   if (n_ready <= 1) return;
+
+   for (n = 0; n < n_ready; n++) {
+      k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+      if (!cpp->din) input_only = FALSE;
+
+      if (SCpnt->request.sector < minsec) minsec = SCpnt->request.sector;
+      if (SCpnt->request.sector > maxsec) maxsec = SCpnt->request.sector;
+
+      sl[n] = SCpnt->request.sector;
+
+      if (!n) continue;
+
+      if (sl[n] < sl[n - 1]) s = FALSE;
+      if (sl[n] > sl[n - 1]) r = FALSE;
+   
+      if (link_statistics) {
+         if (sl[n] > sl[n - 1])
+            seek += sl[n] - sl[n - 1];
+         else
+            seek += sl[n - 1] - sl[n];
+         }
+
+      }
+
+   if (cursec > ((maxsec + minsec) / 2)) rev = TRUE;
+
+   if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev);
+
+   if (!input_only) for (n = 0; n < n_ready; n++) {
+      k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+      ll[n] = SCpnt->request.nr_sectors; pl[n] = SCpnt->pid;
+
+      if (!n) continue;
+      if ((sl[n] == sl[n - 1]) || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n]))
+          || (rev && ((sl[n] + ll[n]) > sl[n - 1]))) overlap = TRUE;
+      }
+
+   if (overlap) sort(pl, il, n_ready, FALSE);
+
+   if (link_statistics) {
+      batchcount++; readycount += n_ready, seeknosort += seek / 1024; 
+      if (input_only) inputcount++;
+      if (overlap) { ovlcount++; seeksorted += seek / 1024; }
+      else seeksorted += (maxsec - minsec) / 1024;
+      if (rev && !r)     {  revcount++; readysorted += n_ready; }
+      if (!rev && !s)    { sortcount++; readysorted += n_ready; }
+      }
+
+#if defined (DEBUG_LINKED_COMMANDS)
+   if (link_statistics && (overlap || !(flushcount % link_statistics)))
+      for (n = 0; n < n_ready; n++) {
+         k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+         printk("%s %d.%d:%d pid %ld mb %d fc %d nr %d sec %ld ns %ld"\
+                " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
+                (ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target,
+                SCpnt->lun, SCpnt->pid, k, flushcount, n_ready,
+                SCpnt->request.sector, SCpnt->request.nr_sectors, cursec,
+                YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only), 
+                YESNO(overlap), cpp->din);
+         }
+#endif
+}
+
+static void flush_dev(Scsi_Device *dev, unsigned long cursec, unsigned int j,
+                      unsigned int ihdlr) {
+   Scsi_Cmnd *SCpnt;
+   struct mscp *cpp;
+   unsigned int k, n, n_ready = 0, il[MAX_MAILBOXES];
 
    for (k = 0; k < sh[j]->can_queue; k++) {
 
-      if (HD(j)->cp_stat[k] != READY) continue;
+      if (HD(j)->cp_stat[k] != READY && HD(j)->cp_stat[k] != IN_USE) continue;
+
+      cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
 
-      cpp = &HD(j)->cp[k];
+      if (SCpnt->device != dev) continue;
 
-      if (cpp->link_id != i) continue;
+      if (HD(j)->cp_stat[k] == IN_USE) return;
 
-      SCpnt = cpp->SCpnt;
-      n_ready++;
+      il[n_ready++] = k;
+      }
+
+   reorder(j, cursec, ihdlr, il, n_ready);
+  
+   for (n = 0; n < n_ready; n++) {
+      k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
 
       if (do_dma(sh[j]->io_port, (unsigned int) cpp, SEND_CP_DMA)) {
-         printk("%s: ihdlr, target %d.%d:%d, pid %ld, Mbox %d, link_id %d, "\
-                "n_ready %d, adapter busy, will abort.\n", BN(j),
-                SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
-                k, i, n_ready);
+         printk("%s: %s, target %d.%d:%d, pid %ld, Mbox %d, adapter"\
+                " busy, will abort.\n", BN(j), (ihdlr ? "ihdlr" : "qcomm"),
+                SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid, k);
          HD(j)->cp_stat[k] = ABORTING;
          continue;
          }
 
       HD(j)->cp_stat[k] = IN_USE;
-
-#if defined (DEBUG_LINKED_COMMANDS)
-      printk("%s: ihdlr, target %d.%d:%d, pid %ld, Mbox %d in use, link_id %d,"
-             " n_ready %d.\n", BN(j), SCpnt->channel, SCpnt->target, 
-             SCpnt->lun, SCpnt->pid, k, i, n_ready);
-#endif
       }
 
 }
@@ -1313,7 +1601,7 @@ static void eata2x_interrupt_handler(int irq, void *dev_id,
                                      struct pt_regs *regs) {
    Scsi_Cmnd *SCpnt;
    unsigned long flags;
-   unsigned int i, j, k, c, status, tstatus, loops, total_loops = 0;
+   unsigned int i, j, k, c, status, tstatus, loops, total_loops = 0, reg;
    struct mssp *spp;
    struct mscp *cpp;
 
@@ -1345,7 +1633,7 @@ static void eata2x_interrupt_handler(int irq, void *dev_id,
                              BN(j), HD(j)->iocount);
    
         /* Read the status register to clear the interrupt indication */
-        inb(sh[j]->io_port + REG_STATUS);
+        reg = inb(sh[j]->io_port + REG_STATUS);
    
         /* Service all mailboxes of this board */
         for (i = 0; i < sh[j]->can_queue; i++) {
@@ -1356,8 +1644,6 @@ static void eata2x_interrupt_handler(int irq, void *dev_id,
    
            spp->eoc = FALSE;
    
-            if (linked_commands) process_ready_list(i, j);
-
            if (HD(j)->cp_stat[i] == IGNORE) {
               HD(j)->cp_stat[i] = FREE;
               continue;
@@ -1398,6 +1684,10 @@ static void eata2x_interrupt_handler(int irq, void *dev_id,
                     " irq %d.\n", BN(j), i, SCpnt->pid, 
                     *(unsigned int *)SCpnt->host_scribble, irq);
    
+         if (linked_comm && SCpnt->device->queue_depth > 2
+                                           && TLDEV(SCpnt->device->type))
+            flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
+
            tstatus = status_byte(spp->target_status);
    
            switch (spp->adapter_status) {
@@ -1466,14 +1756,14 @@ static void eata2x_interrupt_handler(int irq, void *dev_id,
                     status = DID_ERROR << 16;
 
                  break;
-              case 0x07:     /* Bus Parity Error */
-              case 0x0c:     /* Controller Ram Parity */
               case 0x05:     /* Unexpected Bus Phase */
               case 0x06:     /* Unexpected Bus Free */
+              case 0x07:     /* Bus Parity Error */
               case 0x08:     /* SCSI Hung */
               case 0x09:     /* Unexpected Message Reject */
               case 0x0a:     /* SCSI Bus Reset Stuck */
               case 0x0b:     /* Auto Request-Sense Failed */
+              case 0x0c:     /* Controller Ram Parity Error */
               default:
                  status = DID_ERROR << 16;
                  break;
@@ -1493,10 +1783,10 @@ static void eata2x_interrupt_handler(int irq, void *dev_id,
                do_trace || msg_byte(spp->target_status))
 #endif
               printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
-                     " target %d.%d:%d, pid %ld, count %d.\n",
+                     " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
                      BN(j), i, spp->adapter_status, spp->target_status,
                      SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
-                      HD(j)->iocount);
+                      reg, HD(j)->iocount);
    
            /* Set the command state to inactive */
            SCpnt->host_scribble = NULL;
index beec9596d579e9c603c65e9e42b20347f27d4681..82791396abdadd5099f0499af3ce8d11e8a2332a 100644 (file)
@@ -12,7 +12,7 @@ int eata2x_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int eata2x_abort(Scsi_Cmnd *);
 int eata2x_reset(Scsi_Cmnd *, unsigned int);
 
-#define EATA_VERSION "2.50.00"
+#define EATA_VERSION "3.00.09"
 
 
 #define EATA {                                                 \
index 2be3dc3df74088b6b82a0b0ca250f0d30a6e8724..41ffec21e9c47ab3b70f980041d28f166e73934a 100644 (file)
@@ -280,11 +280,15 @@ static struct dev_info device_list[] =
 {"INSITE","Floptical   F*8I","*", BLIST_KEY},
 {"INSITE","I325VM","*", BLIST_KEY},
 {"NRC","MBR-7","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"NRC","MBR-7.4","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"NAKAMICH","MJ-4.8S","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"PIONEER","CD-ROM DRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"PIONEER","CD-ROM DRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
 {"EMULEX","MD21/S2     ESDI","*", BLIST_SINGLELUN},
 {"CANON","IPUBJD","*", BLIST_SPARSELUN},
 {"MATSHITA","PD","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"YAMAHA","CDR100","1.00", BLIST_NOLUN},       /* Locks up if polled for lun != 0 */
+{"YAMAHA","CDR102","1.00", BLIST_NOLUN},       /* Locks up if polled for lun != 0 */
 /*
  * Must be at end of list...
  */
@@ -2920,7 +2924,9 @@ static void resize_dma_pool(void)
            if (SDpnt->type == TYPE_WORM || SDpnt->type == TYPE_ROM)
                new_dma_sectors += (2048 >> 9) * SDpnt->queue_depth;
        }
-       else if (SDpnt->type == TYPE_SCANNER || SDpnt->type == TYPE_PROCESSOR) {
+       else if (SDpnt->type == TYPE_SCANNER ||
+                SDpnt->type == TYPE_PROCESSOR ||
+                SDpnt->type == TYPE_MEDIUM_CHANGER) {
            new_dma_sectors += (4096 >> 9) * SDpnt->queue_depth;
        }
        else {
index 6b327def4eb51a998d7295371f2821d91e8e5abd..01e8881abdbd12f873e41034252b723ce2eb6325 100644 (file)
@@ -247,10 +247,14 @@ static int ioctl_command(Scsi_Device *dev, void *buffer)
        retries = 1;
        break;
       case START_STOP:
-      case MOVE_MEDIUM:
        timeout =  60 * HZ;     /* 60 seconds */
        retries = 1;
        break;
+      case MOVE_MEDIUM:
+      case READ_ELEMENT_STATUS:
+       timeout =  5 * 60 * HZ; /* 5 minutes */
+       retries = 1;
+       break;
       default:
        timeout = MAX_TIMEOUT;
        retries = MAX_RETRIES;
index 5bc6731ef6c7423e140a1b46fa6789afc65691c7..e972b2bc170abef7b8cdf93e0dac814b82bd6195 100644 (file)
@@ -1206,6 +1206,7 @@ static int sd_init_onedisk(int i)
                printk ("scsi : deleting disk entry.\n");
                rscsi_disks[i].device = NULL;
                sd_template.nr_dev--;
+               sd_gendisk.nr_real--;
                return i;
            }
        }
index 4c561845a64fb1e481262f28c6b49e1066102fa2..307b2e6121cf617fa99c2833221fc4e5c3d6f2ac 100644 (file)
@@ -483,7 +483,7 @@ void sr_photocd(struct inode *inode)
                    no_multi = 1;
                }
            } else
-               printk(KERN_WARNING"sr_photocd: ioctl error (TOSHIBA #1): 0x%x\n",rc);
+               printk(KERN_INFO"sr_photocd: ioctl error (TOSHIBA #1): 0x%x\n",rc);
            break; /* if the first ioctl fails, we don't call the second one */
        }
        is_xa  = (rec[0] == 0x20);
index 92e2776b9bc99c2819338bde7529c056e32a3ec8..3d7b8522ac4afa7acbb99da1cf84e08f79d88f03 100644 (file)
@@ -614,7 +614,6 @@ scsi_tape_open(struct inode * inode, struct file * filp)
       if ((SCpnt->sense_buffer[0] & 0x70) == 0x70 &&
          (SCpnt->sense_buffer[2] & 0x0f) == NO_TAPE) {
        (STp->mt_status)->mt_fileno = STp->drv_block = 0 ;
-       printk(KERN_NOTICE "st%d: No tape.\n", dev);
        STp->ready = ST_NO_TAPE;
       } else {
        (STp->mt_status)->mt_fileno = STp->drv_block = (-1);
index a6095cdb7181dc1051b8bf8a52856c4ccd00128f..d89a90a7b533ac1c6e2474f7e682afb62c8281ec 100644 (file)
@@ -1,6 +1,21 @@
 /*
  *      u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
  *
+ *      24 Feb 1997 rev. 3.00 for linux 2.0.29 and 2.1.26
+ *          When loading as a module, parameter passing is now supported
+ *          both in 2.0 and in 2.1 style.
+ *          Fixed data transfer direction for some SCSI opcodes.
+ *          Immediate acknowledge to request sense commands.
+ *          Linked commands to each disk device are now reordered by elevator
+ *          sorting. Rare cases in which reordering of write requests could 
+ *          cause wrong results are managed.
+ *
+ *      18 Jan 1997 rev. 2.60 for linux 2.1.21 and 2.0.28
+ *          Added command line options to enable/disable linked commands
+ *          (lc:[y|n]), old firmware support (of:[y|n]) and to set the max
+ *          queue depth (mq:xx). Default is "u14-34f=lc:n,of:n,mq:8".
+ *          Improved command linking.
+ *
  *       8 Jan 1997 rev. 2.50 for linux 2.1.20 and 2.0.27
  *          Added linked command support.
  *
@@ -9,7 +24,7 @@
  *
  *      22 Nov 1996 rev. 2.30 for linux 2.1.12 and 2.0.26
  *          The list of i/o ports to be probed can be overwritten by the
- *          "u14-34f=port0, port1,...." boot command line option.
+ *          "u14-34f=port0,port1,...." boot command line option.
  *          Scatter/gather lists are now allocated by a number of kmalloc
  *          calls, in order to avoid the previous size limit of 64Kb.
  *
  *
  *  Here a sample configuration using two U14F boards:
  *
- U14F0: ISA 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 32, MB 16, UC 1, LC 1, MQ 8.
- U14F1: ISA 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 32, MB 16, UC 1, LC 1, MQ 8.
+ U14F0: ISA 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 32, MB 16, of:n, lc:y, mq:8.
+ U14F1: ISA 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 32, MB 16, of:n, lc:y, mq:8.
  *
  *  The boot controller must have its BIOS enabled, while other boards can
  *  have their BIOS disabled, or enabled to an higher address.
  *  problems when using more then 16 scatter/gather lists.
  *
  *  The list of i/o ports to be probed can be totally replaced by the
- *  boot command line option: "u14-34f=port0, port1, port2,...", where the
+ *  boot command line option: "u14-34f=port0,port1,port2,...", where the
  *  port0, port1... arguments are ISA/VESA addresses to be probed.
- *  For example using "u14-34f=0x230, 0x340", the driver probes only the two
+ *  For example using "u14-34f=0x230,0x340", the driver probes only the two
  *  addresses 0x230 and 0x340 in this order; "u14-34f=0" totally disables
  *  this driver.
  *
+ *  After the optional list of detection probes, other possible command line
+ *  options are:
+ *
+ *  lc:y  enables linked commands;
+ *  lc:n  disables linked commands;
+ *  of:y  enables old firmware support;
+ *  of:n  disables old firmware support;
+ *  mq:xx set the max queue depth to the value xx (2 <= xx <= 8).
+ *
+ *  The default value is: "u14-34f=lc:n,of:n,mq:8". An example using the list
+ *  of detection probes could be: "u14-34f=0x230,0x340,lc:y,of:n,mq:4".
+ *
+ *  When loading as a module, parameters can be specified as well.
+ *  The above example would be (use 1 in place of y and 0 in place of n):
+ *
+ *  modprobe u14-34f io_port=0x230,0x340 linked_comm=1 have_old_firmware=0 \
+ *                max_queue_depth=4
+ *
+ *  ----------------------------------------------------------------------------
+ *  In this implementation, linked commands are designed to work with any DISK
+ *  or CD-ROM, since this linking has only the intent of clustering (time-wise)
+ *  and reordering by elevator sorting commands directed to each device,
+ *  without any relation with the actual SCSI protocol between the controller
+ *  and the device.
+ *  If Q is the queue depth reported at boot time for each device (also named
+ *  cmds/lun) and Q > 2, whenever there is already an active command to the
+ *  device all other commands to the same device  (up to Q-1) are kept waiting
+ *  in the elevator sorting queue. When the active command completes, the
+ *  commands in this queue are sorted by sector address. The sort is chosen
+ *  between increasing or decreasing by minimizing the seek distance between
+ *  the sector of the commands just completed and the sector of the first 
+ *  command in the list to be sorted. 
+ *  Trivial math assures that if there are (Q-1) outstanding request for
+ *  random seeks over S sectors, the unsorted average seek distance is S/2,
+ *  while the sorted average seek distance is S/(Q-1). The seek distance is
+ *  hence divided by a factor (Q-1)/2.
+ *  The above pure geometric remarks are valid in all cases and the 
+ *  driver effectively reduces the seek distance by the predicted factor
+ *  when there are Q concurrent read i/o operations on the device, but this
+ *  does not necessarily results in a noticeable performance improvement:
+ *  your mileage may vary....
+ *
+ *  Note: command reordering inside a batch of queued commands could cause
+ *        wrong results only if there is at least one write request and the
+ *        intersection (sector-wise) of all requests is not empty. 
+ *        When the driver detects a batch including overlapping requests
+ *        (a really rare event) strict serial (pid) order is enforced.
+ *  ----------------------------------------------------------------------------
+ *
  *  The boards are named Ux4F0, Ux4F1,... according to the detection order.
  *
  *  In order to support multiple ISA boards in a reliable way,
  *  the driver sets host->wish_block = TRUE for all ISA boards.
  */
 
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+#define MAX_INT_PARAM 10
 #if defined(MODULE)
 #include <linux/module.h>
 #include <linux/version.h>
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,26)
+MODULE_PARM(io_port, "1-" __MODULE_STRING(MAX_INT_PARAM) "i");
+MODULE_PARM(linked_comm, "i");
+MODULE_PARM(have_old_firmware, "i");
+MODULE_PARM(link_statistics, "i");
+MODULE_PARM(max_queue_depth, "i");
+MODULE_AUTHOR("Dario Ballabio");
+#endif
 #endif
 
 #include <linux/string.h>
@@ -247,8 +321,9 @@ struct proc_dir_entry proc_scsi_u14_34f = {
 #define MAX_SAFE_SGLIST 16
 #define MAX_INTERNAL_RETRIES 64
 #define MAX_CMD_PER_LUN 2
-#define MAX_TAGGED_CMD_PER_LUN 8
+#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
 
+#define SKIP UINT_MAX
 #define FALSE 0
 #define TRUE 1
 #define FREE 0
@@ -282,6 +357,8 @@ struct proc_dir_entry proc_scsi_u14_34f = {
 #define ASST              0x91
 
 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+#define YESNO(a) ((a) ? 'y' : 'n')
+#define TLDEV(type) ((type) == TYPE_DISK || (type) == TYPE_ROM)
 
 #define PACKED          __attribute__((packed))
 
@@ -313,7 +390,6 @@ struct mscp {
    unsigned int sense_addr PACKED;
    Scsi_Cmnd *SCpnt;
    unsigned int index;                  /* cp index */
-   unsigned int link_id;                /* reference cp for linked commands */
    struct sg_list *sglist;
    };
 
@@ -343,12 +419,18 @@ static struct Scsi_Host *sh[MAX_BOARDS + 1];
 static const char *driver_name = "Ux4F";
 static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
 
-static unsigned int io_port[] = {
-      0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140,
+static unsigned int io_port[] = { 
 
-      /* End of list */
-      0x0
-      };
+   /* Space for MAX_INT_PARAM ports usable while loading as a module */
+   SKIP,    SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,   SKIP,
+   SKIP,    SKIP,
+
+   /* Possible ISA/VESA ports */
+   0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140,
+
+   /* End of list */
+   0x0
+   };
 
 #define HD(board) ((struct hostdata *) &sh[board]->hostdata)
 #define BN(board) (HD(board)->board_name)
@@ -370,13 +452,21 @@ static unsigned int io_port[] = {
 #define DEV2V(addr) ((addr) ? DEV2H(bus_to_virt((unsigned long)addr)) : 0)
 
 static void u14_34f_interrupt_handler(int, void *, struct pt_regs *);
+static void flush_dev(Scsi_Device *, unsigned long, unsigned int, unsigned int);
 static int do_trace = FALSE;
 static int setup_done = FALSE;
+static int link_statistics = 0;
+
+#if defined (HAVE_OLD_UX4F_FIRMWARE)
+static int have_old_firmware = TRUE;
+#else
+static int have_old_firmware = FALSE;
+#endif
 
 #if defined (CONFIG_SCSI_U14_34F_LINKED_COMMANDS)
-static int linked_commands = TRUE;
+static int linked_comm = TRUE;
 #else
-static int linked_commands = FALSE;
+static int linked_comm = FALSE;
 #endif
 
 #if defined CONFIG_SCSI_U14_34F_MAX_TAGS
@@ -399,32 +489,45 @@ static void select_queue_depths(struct Scsi_Host *host, Scsi_Device *devlist) {
 
       if (dev->host != host) continue;
 
-      if (dev->tagged_supported) ntag++;
-      else                       nuntag++;
+      if (TLDEV(dev->type) && (dev->tagged_supported || linked_comm))
+         ntag++;
+      else
+         nuntag++;
       }
 
    utqd = MAX_CMD_PER_LUN;
 
-   tqd = (host->can_queue - utqd * nuntag) / (ntag + 1);
+   tqd = (host->can_queue - utqd * nuntag) / (ntag ? ntag : 1);
 
    if (tqd > max_queue_depth) tqd = max_queue_depth;
 
    if (tqd < MAX_CMD_PER_LUN) tqd = MAX_CMD_PER_LUN;
 
    for(dev = devlist; dev; dev = dev->next) {
-      char *tag_suffix = "";
+      char *tag_suffix = "", *link_suffix = "";
 
       if (dev->host != host) continue;
 
-      if (dev->tagged_supported) dev->queue_depth = tqd;
-      else                       dev->queue_depth = utqd;
+      if (TLDEV(dev->type) && (dev->tagged_supported || linked_comm))
+         dev->queue_depth = tqd;
+      else
+         dev->queue_depth = utqd;
+
+      if (TLDEV(dev->type)) {
+         if (linked_comm && dev->queue_depth > 2)
+            link_suffix = ", linked";
+         else
+            link_suffix = ", unlinked";
+         }
 
-      if (dev->tagged_supported && dev->tagged_queue) tag_suffix = ", tagged";
-      else if (dev->tagged_supported) tag_suffix = ", untagged";
+      if (dev->tagged_supported && TLDEV(dev->type) && dev->tagged_queue)
+         tag_suffix = ", tagged";
+      else if (dev->tagged_supported && TLDEV(dev->type))
+         tag_suffix = ", untagged";
 
-      printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s.\n",
+      printk("%s: scsi%d, channel %d, id %d, lun %d, cmds/lun %d%s%s.\n",
              BN(j), host->host_no, dev->channel, dev->id, dev->lun,
-             dev->queue_depth, tag_suffix);
+             dev->queue_depth, link_suffix, tag_suffix);
       }
 
    restore_flags(flags);
@@ -606,32 +709,26 @@ static inline int port_detect(unsigned int port_base, unsigned int j,
    HD(j)->board_number = j;
    irqlist[irq]++;
 
-   if (HD(j)->subversion == ESA) {
+   if (have_old_firmware) sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
 
-#if defined (HAVE_OLD_UX4F_FIRMWARE)
-      sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
-#endif
-
-      sh[j]->dma_channel = NO_DMA;
+   if (HD(j)->subversion == ESA) {
       sh[j]->unchecked_isa_dma = FALSE;
+      sh[j]->dma_channel = NO_DMA;
       sprintf(BN(j), "U34F%d", j);
       bus_type = "VESA";
       }
    else {
       sh[j]->wish_block = TRUE;
-
-#if defined (HAVE_OLD_UX4F_FIRMWARE)
-      sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
-      sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
-#endif
-
-      sh[j]->dma_channel = dma_channel;
       sh[j]->unchecked_isa_dma = TRUE;
-      sprintf(BN(j), "U14F%d", j);
       disable_dma(dma_channel);
       clear_dma_ff(dma_channel);
       set_dma_mode(dma_channel, DMA_MODE_CASCADE);
       enable_dma(dma_channel);
+
+      if (have_old_firmware) sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
+
+      sh[j]->dma_channel = dma_channel;
+      sprintf(BN(j), "U14F%d", j);
       bus_type = "ISA";
       }
 
@@ -668,10 +765,10 @@ static inline int port_detect(unsigned int port_base, unsigned int j,
 
    if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
 
-   printk("%s: %s 0x%03x, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d, UC %d, "\
-          "LC %d, MQ %d.\n", BN(j), bus_type, sh[j]->io_port, (int)sh[j]->base,
+   printk("%s: %s 0x%03x, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d, of:%c, "\
+          "lc:%c, mq:%d.\n", BN(j), bus_type, sh[j]->io_port, (int)sh[j]->base,
           sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue,
-          sh[j]->hostt->use_clustering, linked_commands, max_queue_depth);
+          YESNO(have_old_firmware), YESNO(linked_comm), max_queue_depth);
 
    if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
       printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
@@ -686,15 +783,33 @@ static inline int port_detect(unsigned int port_base, unsigned int j,
 
 void u14_34f_setup(char *str, int *ints) {
    int i, argc = ints[0];
+   char *cur = str, *pc;
 
-   if (argc <= 0) return;
+   if (argc > 0) {
 
-   if (argc > MAX_BOARDS) argc = MAX_BOARDS;
+      if (argc > MAX_INT_PARAM) argc = MAX_INT_PARAM;
 
-   for (i = 0; i < argc; i++) io_port[i] = ints[i + 1]; 
+      for (i = 0; i < argc; i++) io_port[i] = ints[i + 1]; 
    
-   io_port[i] = 0;
-   setup_done = TRUE;
+      io_port[i] = 0;
+      setup_done = TRUE;
+      }
+
+   while (cur && (pc = strchr(cur, ':'))) {
+      int val = 0, c = *++pc;
+
+      if (c == 'n' || c == 'N') val = FALSE;
+      else if (c == 'y' || c == 'Y') val = TRUE;
+      else val = (int) simple_strtoul(pc, NULL, 0);
+
+      if (!strncmp(cur, "lc:", 3)) linked_comm = val;
+      else if (!strncmp(cur, "of:", 3)) have_old_firmware = val;
+      else if (!strncmp(cur, "mq:", 3))  max_queue_depth = val;
+      else if (!strncmp(cur, "ls:", 3))  link_statistics = val;
+
+      if ((cur = strchr(cur, ','))) ++cur;
+      }
+
    return;
 }
 
@@ -707,6 +822,14 @@ int u14_34f_detect(Scsi_Host_Template *tpnt) {
    save_flags(flags);
    cli();
 
+#if defined(MODULE)
+   /* io_port could have been modified when loading as a module */
+   if(io_port[0] != SKIP) {
+      setup_done = TRUE;
+      io_port[MAX_INT_PARAM] = 0;
+      }
+#endif
+
    for (k = 0; k < MAX_IRQ; k++) {
       irqlist[k] = 0;
       calls[k] = 0;
@@ -714,8 +837,12 @@ int u14_34f_detect(Scsi_Host_Template *tpnt) {
 
    for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL;
 
-   for (k = 0; io_port[k]; k++)
+   for (k = 0; io_port[k]; k++) {
+
+      if (io_port[k] == SKIP) continue;
+
       if (j < MAX_BOARDS && port_detect(io_port[k], j, tpnt)) j++;
+      }
 
    if (j > 0) 
       printk("UltraStor 14F/34F: Copyright (C) 1994-1997 Dario Ballabio.\n");
@@ -747,9 +874,15 @@ int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
    struct mscp *cpp;
 
    static const unsigned char data_out_cmds[] = {
-      0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x0b, 0x10, 0x16, 0x18, 0x1d, 
-      0x24, 0x2b, 0x2e, 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 
-      0x3f, 0x40, 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea
+      0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x18, 0x1d, 0x24, 0x2e,
+      0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d, 0x3f, 0x40, 
+      0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea, 0x1b
+      };
+
+   static const unsigned char data_none_cmds[] = {
+      0x01, 0x0b, 0x10, 0x11, 0x13, 0x16, 0x17, 0x19, 0x2b, 0x1e,
+      0x2c, 0xac, 0x2f, 0xaf, 0x33, 0xb3, 0x35, 0x36, 0x45, 0x47,
+      0x48, 0x49, 0xa9, 0x4b, 0xa5, 0xa6, 0xb5
       };
 
    save_flags(flags);
@@ -759,6 +892,16 @@ int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
 
    if (!done) panic("%s: qcomm, pid %ld, null done.\n", BN(j), SCpnt->pid);
 
+   if (SCpnt->cmnd[0] == REQUEST_SENSE && SCpnt->sense_buffer[0]) {
+      SCpnt->result = DID_OK << 16; 
+      SCpnt->host_scribble = NULL;
+      printk("%s: qcomm, target %d.%d:%d, pid %ld, request sense ignored.\n",
+             BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid);
+      restore_flags(flags);
+      done(SCpnt);    
+      return 0;
+      }
+
    /* i is the mailbox number, look for the first free mailbox 
       starting from last_cp_used */
    i = HD(j)->last_cp_used + 1;
@@ -805,10 +948,17 @@ int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
    cpp->xdir = DTD_IN;
 
    for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
-     if (SCpnt->cmnd[0] == data_out_cmds[k]) {
-       cpp->xdir = DTD_OUT;
-       break;
-       }
+      if (SCpnt->cmnd[0] == data_out_cmds[k]) {
+        cpp->xdir = DTD_OUT;
+        break;
+        }
+
+   if (cpp->xdir == DTD_IN)
+      for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
+         if (SCpnt->cmnd[0] == data_none_cmds[k]) {
+            cpp->xdir = DTD_NONE;
+            break;
+            }
 
    cpp->opcode = OP_SCSI;
    cpp->channel = SCpnt->channel;
@@ -830,27 +980,13 @@ int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
    cpp->scsi_cdbs_len = SCpnt->cmd_len;
    memcpy(cpp->scsi_cdbs, SCpnt->cmnd, cpp->scsi_cdbs_len);
 
-   if (linked_commands && SCpnt->device->tagged_supported
-            && !HD(j)->target_to[SCpnt->target][SCpnt->channel]
-           && !HD(j)->target_redo[SCpnt->target][SCpnt->channel])
-   
-      for (k = 0; k < sh[j]->can_queue; k++) {
-         if (HD(j)->cp_stat[k] != IN_USE) continue;
-
-         if ((&HD(j)->cp[k])->SCpnt->device != SCpnt->device) continue;
-
-         cpp->link_id = k;
-         HD(j)->cp_stat[i] = READY;
-
-#if defined (DEBUG_LINKED_COMMANDS)
-         printk("%s: qcomm, target %d.%d:%d, pid %ld, Mbox %d ready.\n", 
-                BN(j), SCpnt->channel, SCpnt->target, SCpnt->lun,
-                SCpnt->pid, i);
-#endif
-         restore_flags(flags);
-         return 0;
-         }
+   if (linked_comm && SCpnt->device->queue_depth > 2
+                                     && TLDEV(SCpnt->device->type)) {
+      HD(j)->cp_stat[i] = READY;
+      flush_dev(SCpnt->device, 0, j, FALSE);
+      restore_flags(flags);
+      return 0;
+      }
 
    if (wait_on_busy(sh[j]->io_port)) {
       SCpnt->result = DID_ERROR << 16;
@@ -1102,27 +1238,147 @@ int u14_34f_biosparam(Disk *disk, kdev_t dev, int *dkinfo) {
    return FALSE;
 }
 
-static inline void process_ready_list(unsigned int i, unsigned int j) {
+static void sort(unsigned long sk[], unsigned int da[], unsigned int n,
+                 unsigned int rev) {
+   unsigned int i, j, k, y;
+   unsigned long x;
+
+   for (i = 0; i < n - 1; i++) {
+      k = i;
+
+      for (j = k + 1; j < n; j++) 
+         if (rev) {
+            if (sk[j] > sk[k]) k = j;
+            }
+         else {
+            if (sk[j] < sk[k]) k = j;
+            }
+
+      if (k != i) {
+         x = sk[k]; sk[k] = sk[i]; sk[i] = x;
+         y = da[k]; da[k] = da[i]; da[i] = y;
+         }
+      }
+
+   return;
+   }
+
+static inline void reorder(unsigned int j, unsigned long cursec,
+                 unsigned int ihdlr, unsigned int il[], unsigned int n_ready) {
+   Scsi_Cmnd *SCpnt;
+   struct mscp *cpp;
+   unsigned int k, n;
+   unsigned int rev = FALSE, s = TRUE, r = TRUE;
+   unsigned int input_only = TRUE, overlap = FALSE;
+   unsigned long sl[n_ready], pl[n_ready], ll[n_ready];
+   unsigned long maxsec = 0, minsec = ULONG_MAX, seek = 0;
+
+   static unsigned int flushcount = 0, batchcount = 0, sortcount = 0;
+   static unsigned int readycount = 0, ovlcount = 0, inputcount = 0;
+   static unsigned int readysorted = 0, revcount = 0;
+   static unsigned long seeksorted = 0, seeknosort = 0;
+
+   if (link_statistics && !(++flushcount % link_statistics))
+      printk("fc %d bc %d ic %d oc %d rc %d rs %d sc %d re %d"\
+             " av %ldK as %ldK.\n", flushcount, batchcount, inputcount,
+             ovlcount, readycount, readysorted, sortcount, revcount,
+             seeknosort / (readycount - batchcount + 1), 
+             seeksorted / (readycount - batchcount + 1));
+
+   if (n_ready <= 1) return;
+
+   for (n = 0; n < n_ready; n++) {
+      k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+
+      if (!(cpp->xdir == DTD_IN)) input_only = FALSE;
+
+      if (SCpnt->request.sector < minsec) minsec = SCpnt->request.sector;
+      if (SCpnt->request.sector > maxsec) maxsec = SCpnt->request.sector;
+
+      sl[n] = SCpnt->request.sector;
+
+      if (!n) continue;
+
+      if (sl[n] < sl[n - 1]) s = FALSE;
+      if (sl[n] > sl[n - 1]) r = FALSE;
+   
+      if (link_statistics) {
+         if (sl[n] > sl[n - 1])
+            seek += sl[n] - sl[n - 1];
+         else
+            seek += sl[n - 1] - sl[n];
+         }
+
+      }
+
+   if (cursec > ((maxsec + minsec) / 2)) rev = TRUE;
+
+   if (!((rev && r) || (!rev && s))) sort(sl, il, n_ready, rev);
+
+   if (!input_only) for (n = 0; n < n_ready; n++) {
+      k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+      ll[n] = SCpnt->request.nr_sectors; pl[n] = SCpnt->pid;
+
+      if (!n) continue;
+      if ((sl[n] == sl[n - 1]) || (!rev && ((sl[n - 1] + ll[n - 1]) > sl[n]))
+          || (rev && ((sl[n] + ll[n]) > sl[n - 1]))) overlap = TRUE;
+      }
+
+   if (overlap) sort(pl, il, n_ready, FALSE);
+
+   if (link_statistics) {
+      batchcount++; readycount += n_ready, seeknosort += seek / 1024; 
+      if (input_only) inputcount++;
+      if (overlap) { ovlcount++; seeksorted += seek / 1024; }
+      else seeksorted += (maxsec - minsec) / 1024;
+      if (rev && !r)     {  revcount++; readysorted += n_ready; }
+      if (!rev && !s)    { sortcount++; readysorted += n_ready; }
+      }
+
+#if defined (DEBUG_LINKED_COMMANDS)
+   if (link_statistics && (overlap || !(flushcount % link_statistics)))
+      for (n = 0; n < n_ready; n++) {
+         k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
+         printk("%s %d.%d:%d pid %ld mb %d fc %d nr %d sec %ld ns %ld"\
+                " cur %ld s:%c r:%c rev:%c in:%c ov:%c xd %d.\n",
+                (ihdlr ? "ihdlr" : "qcomm"), SCpnt->channel, SCpnt->target,
+                SCpnt->lun, SCpnt->pid, k, flushcount, n_ready,
+                SCpnt->request.sector, SCpnt->request.nr_sectors, cursec,
+                YESNO(s), YESNO(r), YESNO(rev), YESNO(input_only), 
+                YESNO(overlap), cpp->xdir);
+         }
+#endif
+}
+
+static void flush_dev(Scsi_Device *dev, unsigned long cursec, unsigned int j,
+                      unsigned int ihdlr) {
    Scsi_Cmnd *SCpnt;
-   unsigned int k, n_ready = 0;
    struct mscp *cpp;
+   unsigned int k, n, n_ready = 0, il[MAX_MAILBOXES];
 
    for (k = 0; k < sh[j]->can_queue; k++) {
 
-      if (HD(j)->cp_stat[k] != READY) continue;
+      if (HD(j)->cp_stat[k] != READY && HD(j)->cp_stat[k] != IN_USE) continue;
 
-      cpp = &HD(j)->cp[k];
+      cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
 
-      if (cpp->link_id != i) continue;
+      if (SCpnt->device != dev) continue;
 
-      SCpnt = cpp->SCpnt;
-      n_ready++;
+      if (HD(j)->cp_stat[k] == IN_USE) return;
+
+      il[n_ready++] = k;
+      }
+
+   reorder(j, cursec, ihdlr, il, n_ready);
+  
+   for (n = 0; n < n_ready; n++) {
+      k = il[n]; cpp = &HD(j)->cp[k]; SCpnt = cpp->SCpnt;
 
       if (wait_on_busy(sh[j]->io_port)) {
-         printk("%s: ihdlr, target %d.%d:%d, pid %ld, Mbox %d, link_id %d, "\
-                "n_ready %d, adapter busy, will abort.\n", BN(j),
-                SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
-                k, i, n_ready);
+         printk("%s: %s, target %d.%d:%d, pid %ld, Mbox %d, adapter"\
+                " busy, will abort.\n", BN(j), (ihdlr ? "ihdlr" : "qcomm"),
+                SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid, k);
          HD(j)->cp_stat[k] = ABORTING;
          continue;
          }
@@ -1130,21 +1386,14 @@ static inline void process_ready_list(unsigned int i, unsigned int j) {
       outl(V2DEV(cpp), sh[j]->io_port + REG_OGM);
       outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
       HD(j)->cp_stat[k] = IN_USE;
-
-#if defined (DEBUG_LINKED_COMMANDS)
-      printk("%s: ihdlr, target %d.%d:%d, pid %ld, Mbox %d in use, link_id %d,"
-             " n_ready %d.\n", BN(j), SCpnt->channel, SCpnt->target, 
-             SCpnt->lun, SCpnt->pid, k, i, n_ready);
-#endif
       }
-
 }
 
 static void u14_34f_interrupt_handler(int irq, void *dev_id,
                                       struct pt_regs *regs) {
    Scsi_Cmnd *SCpnt;
    unsigned long flags;
-   unsigned int i, j, k, c, status, tstatus, loops, total_loops = 0, ret;
+   unsigned int i, j, k, c, status, tstatus, loops, total_loops = 0, reg, ret;
    struct mscp *spp;
 
    save_flags(flags);
@@ -1167,7 +1416,7 @@ static void u14_34f_interrupt_handler(int irq, void *dev_id,
       loops = 0;
 
       /* Loop until all interrupts for a board are serviced */
-      while (inb(sh[j]->io_port + REG_SYS_INTR) & IRQ_ASSERTED) {
+      while ((reg = inb(sh[j]->io_port + REG_SYS_INTR)) & IRQ_ASSERTED) {
         total_loops++;
         loops++;
 
@@ -1186,8 +1435,6 @@ static void u14_34f_interrupt_handler(int irq, void *dev_id,
            panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n",
                   BN(j), (void *)ret, HD(j)->cp);
 
-         if (linked_commands) process_ready_list(i, j);
-
         if (HD(j)->cp_stat[i] == IGNORE) {
            HD(j)->cp_stat[i] = FREE;
            continue;
@@ -1223,6 +1470,10 @@ static void u14_34f_interrupt_handler(int irq, void *dev_id,
                  " irq %d.\n", BN(j), i, SCpnt->pid, 
                  *(unsigned int *)SCpnt->host_scribble, irq);
 
+         if (linked_comm && SCpnt->device->queue_depth > 2
+                                           && TLDEV(SCpnt->device->type))
+            flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
+
         tstatus = status_byte(spp->target_status);
 
         switch (spp->adapter_status) {
@@ -1321,10 +1572,10 @@ static void u14_34f_interrupt_handler(int irq, void *dev_id,
               do_trace || msg_byte(spp->target_status))
 #endif
            printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
-                  " target %d.%d:%d, pid %ld, count %d.\n",
+                  " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
                   BN(j), i, spp->adapter_status, spp->target_status,
                   SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
-                   HD(j)->iocount);
+                   reg, HD(j)->iocount);
 
         /* Set the command state to inactive */
         SCpnt->host_scribble = NULL;
index 044e853881120508d74cfee39e778791056040ec..588259e101462c6848a740618a49c2e1146c2073 100644 (file)
@@ -11,7 +11,7 @@ int u14_34f_abort(Scsi_Cmnd *);
 int u14_34f_reset(Scsi_Cmnd *, unsigned int);
 int u14_34f_biosparam(Disk *, kdev_t, int *);
 
-#define U14_34F_VERSION "2.50.00"
+#define U14_34F_VERSION "3.00.09"
 
 #define ULTRASTOR_14_34F {                                            \
                NULL, /* Ptr for modules */                           \
index 0ac60bcf719156615d885020ce4b781255c5ace2..337236a304931073ffad9291ab46623dfe2e1235 100644 (file)
@@ -20,21 +20,19 @@ extern int *blksize_size[];
 #define MAX_BUF_PER_PAGE (PAGE_SIZE / 512)
 #define NBUF 64
 
-int block_write(struct inode * inode, struct file * filp, const char * buf, int count)
+int block_write(struct inode * inode, struct file * filp,
+       const char * buf, int count)
 {
-       int blocksize, blocksize_bits, i, j, buffercount,write_error;
+       int blocksize, blocksize_bits, i, buffercount,write_error;
        int block, blocks;
        loff_t offset;
        int chars;
        int written = 0;
-       int cluster_list[MAX_BUF_PER_PAGE];
        struct buffer_head * bhlist[NBUF];
-       int blocks_per_cluster;
        unsigned int size;
        kdev_t dev;
        struct buffer_head * bh, *bufferlist[NBUF];
        register char * p;
-       int excess;
 
        write_error = buffercount = 0;
        dev = inode->i_rdev;
@@ -51,8 +49,6 @@ int block_write(struct inode * inode, struct file * filp, const char * buf, int
                i >>= 1;
        }
 
-       blocks_per_cluster = PAGE_SIZE / blocksize;
-
        block = filp->f_pos >> blocksize_bits;
        offset = filp->f_pos & (blocksize-1);
 
@@ -68,15 +64,14 @@ int block_write(struct inode * inode, struct file * filp, const char * buf, int
                        chars=count;
 
 #if 0
-               if (chars == blocksize)
-                       bh = getblk(dev, block, blocksize);
-               else
-                       bh = breada(dev,block,block+1,block+2,-1);
-
+               /* get the buffer head */
+               {
+                       struct buffer_head * (*fn)(kdev_t, int, int) = getblk;
+                       if (chars != blocksize)
+                               fn = bread;
+                       bh = fn(dev, block, blocksize);
+               }
 #else
-               for(i=0; i<blocks_per_cluster; i++) cluster_list[i] = block+i;
-               if((block % blocks_per_cluster) == 0)
-                 generate_cluster(dev, cluster_list, blocksize);
                bh = getblk(dev, block, blocksize);
 
                if (chars != blocksize && !buffer_uptodate(bh)) {
@@ -90,15 +85,8 @@ int block_write(struct inode * inode, struct file * filp, const char * buf, int
                    blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9) / 2;
                    if (block + blocks > size) blocks = size - block;
                    if (blocks > NBUF) blocks=NBUF;
-                   excess = (block + blocks) % blocks_per_cluster;
-                   if ( blocks > excess )
-                       blocks -= excess;
                    bhlist[0] = bh;
                    for(i=1; i<blocks; i++){
-                     if(((i+block) % blocks_per_cluster) == 0) {
-                       for(j=0; j<blocks_per_cluster; j++) cluster_list[j] = block+i+j;
-                       generate_cluster(dev, cluster_list, blocksize);
-                     };
                      bhlist[i] = getblk (dev, block+i, blocksize);
                      if(!bhlist[i]){
                        while(i >= 0) brelse(bhlist[i--]);
@@ -157,7 +145,8 @@ int block_write(struct inode * inode, struct file * filp, const char * buf, int
        return written;
 }
 
-int block_read(struct inode * inode, struct file * filp, char * buf, int count)
+int block_read(struct inode * inode, struct file * filp,
+       char * buf, int count)
 {
        unsigned int block;
        loff_t offset;
@@ -165,8 +154,6 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
        int blocksize_bits, i;
        unsigned int blocks, rblocks, left;
        int bhrequest, uptodate;
-       int cluster_list[MAX_BUF_PER_PAGE];
-       int blocks_per_cluster;
        struct buffer_head ** bhb, ** bhe;
        struct buffer_head * buflist[NBUF];
        struct buffer_head * bhreq[NBUF];
@@ -174,7 +161,6 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
        loff_t size;
        kdev_t dev;
        int read;
-       int excess;
 
        dev = inode->i_rdev;
        blocksize = BLOCK_SIZE;
@@ -193,8 +179,6 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
        else
                size = INT_MAX;
 
-       blocks_per_cluster = PAGE_SIZE / blocksize;
-
        if (offset > size)
                left = 0;
        /* size - offset might not fit into left, so check explicitly. */
@@ -215,9 +199,6 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
        if (filp->f_reada) {
                if (blocks < read_ahead[MAJOR(dev)] / (blocksize >> 9))
                        blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9);
-               excess = (block + blocks) % blocks_per_cluster;
-               if ( blocks > excess )
-                       blocks -= excess;               
                if (rblocks > blocks)
                        blocks = rblocks;
                
@@ -240,12 +221,6 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
                uptodate = 1;
                while (blocks) {
                        --blocks;
-#if 1
-                       if((block % blocks_per_cluster) == 0) {
-                         for(i=0; i<blocks_per_cluster; i++) cluster_list[i] = block+i;
-                         generate_cluster(dev, cluster_list, blocksize);
-                       }
-#endif
                        *bhb = getblk(dev, block++, blocksize);
                        if (*bhb && !buffer_uptodate(*bhb)) {
                                uptodate = 0;
@@ -266,7 +241,6 @@ int block_read(struct inode * inode, struct file * filp, char * buf, int count)
                /* Now request them all */
                if (bhrequest) {
                        ll_rw_block(READ, bhrequest, bhreq);
-                       refill_freelist(blocksize);
                }
 
                do { /* Finish off all I/O that has actually completed */
index 771b4abc2ec7dd8d2eb30252bc8726a3d05f3d21..9dfa0a22301e610f2badbe18daae0e42f712afa7 100644 (file)
  * data, of course), but instead letting the caller do it.
  */
 
-/*
- * NOTE! There is one discordant note here: checking floppies for
- * disk change. This is where it fits best, I think, as it should
- * invalidate changed floppy-disk-caches.
- */
 /* Some bdflush() changes for the dynamic ramdisk - Paul Gortmaker, 12/94 */
+/* Start bdflush() with kernel_thread not syscall - Paul Gortmaker, 12/95 */
+
+/* Removed a lot of unnecessary code and simplified things now that
+   the buffer cache isn't our primary cache - Andrew Tridgell 12/96 */
 
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <asm/system.h>
 #include <asm/segment.h>
 #include <asm/io.h>
+#include <asm/bitops.h>
 
 #define NR_SIZES 5
 static char buffersize_index[17] =
 {-1,  0,  1, -1,  2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4};
-static short int bufferindex_size[NR_SIZES] = {512, 1024, 2048, 4096, 8192};
 
 #define BUFSIZE_INDEX(X) ((int) buffersize_index[(X)>>9])
 #define MAX_BUF_PER_PAGE (PAGE_SIZE / 512)
+#define MAX_UNUSED_BUFFERS 30 /* don't ever have more than this number of 
+                                unused buffer heads */
+#define HASH_PAGES         4  /* number of pages to use for the hash table */
+#define NR_HASH (HASH_PAGES*PAGE_SIZE/sizeof(struct buffer_head *))
+#define HASH_MASK (NR_HASH-1)
 
 static int grow_buffers(int pri, int size);
-static int shrink_specific_buffers(unsigned int priority, int size);
-static int maybe_shrink_lav_buffers(int);
 
-static int nr_hash = 0;  /* Size of hash table */
 static struct buffer_head ** hash_table;
 static struct buffer_head * lru_list[NR_LIST] = {NULL, };
-/* next_to_age is an array of pointers into the lru lists, used to
-   cycle through the buffers aging their contents when deciding which
-   buffers to discard when more memory is needed */
-static struct buffer_head * next_to_age[NR_LIST] = {NULL, };
 static struct buffer_head * free_list[NR_SIZES] = {NULL, };
 
 static struct buffer_head * unused_list = NULL;
-struct buffer_head * reuse_list = NULL;
+static struct buffer_head * reuse_list = NULL;
 static struct wait_queue * buffer_wait = NULL;
 
-int nr_buffers = 0;
-int nr_buffers_type[NR_LIST] = {0,};
-int nr_buffers_size[NR_SIZES] = {0,};
-int nr_buffers_st[NR_SIZES][NR_LIST] = {{0,},};
-int buffer_usage[NR_SIZES] = {0,};  /* Usage counts used to determine load average */
-int buffers_lav[NR_SIZES] = {0,};  /* Load average of buffer usage */
-int nr_free[NR_SIZES] = {0,};
+static int nr_buffers = 0;
+static int nr_buffers_type[NR_LIST] = {0,};
+static int nr_buffer_heads = 0;
+static int nr_unused_buffer_heads = 0;
+static int refilled = 0;       /* Set NZ when a buffer freelist is refilled 
+                                 this is used by the loop device */
+
+/* this is used by some architectures to estimate available memory */
 int buffermem = 0;
-int nr_buffer_heads = 0;
-int refilled = 0;       /* Set NZ when a buffer freelist is refilled */
-extern int *blksize_size[];
 
 /* Here is the parameter block for the bdflush process. If you add or
  * remove any of the parameters, make sure to update kernel/sysctl.c.
@@ -79,8 +73,9 @@ extern int *blksize_size[];
 static void wakeup_bdflush(int);
 
 #define N_PARAM 9
-#define LAV
 
+/* the dummy values in this structure are left in there for compatibility
+   with old programs that play with the /proc entries */
 union bdflush_param{
        struct {
                int nfract;  /* Percentage of buffer cache dirty to 
@@ -91,26 +86,17 @@ union bdflush_param{
                                each time we call refill */
                int nref_dirt; /* Dirty buffer threshold for activating bdflush
                                  when trying to refill buffers. */
-               int clu_nfract;  /* Percentage of buffer cache to scan to 
-                                   search for free clusters */
+               int dummy1;    /* unused */
                int age_buffer;  /* Time for normal buffer to age before 
                                    we flush it */
                int age_super;  /* Time for superblock to age before we 
                                   flush it */
-               int lav_const;  /* Constant used for load average (time
-                                  constant */
-               int lav_ratio;  /* Used to determine how low a lav for a
-                                  particular size can go before we start to
-                                  trim back the buffers */
+               int dummy2;    /* unused */
+               int dummy3;    /* unused */
        } b_un;
        unsigned int data[N_PARAM];
 } bdf_prm = {{60, 500, 64, 256, 15, 30*HZ, 5*HZ, 1884, 2}};
 
-/* The lav constant is set for 1 minute, as long as the update process runs
-   every 5 seconds.  If you change the frequency of update, the time
-   constant will also change. */
-
-
 /* These are the min and max parameter values that we will allow to be assigned */
 int bdflush_min[N_PARAM] = {  0,  10,    5,   25,  0,   100,   100, 1, 1};
 int bdflush_max[N_PARAM] = {100,5000, 2000, 2000,100, 60000, 60000, 2047, 5};
@@ -155,77 +141,90 @@ repeat:
 static int sync_buffers(kdev_t dev, int wait)
 {
        int i, retry, pass = 0, err = 0;
-       int nlist, ncount;
        struct buffer_head * bh, *next;
 
        /* One pass for no-wait, three for wait:
           0) write out all dirty, unlocked buffers;
           1) write out all dirty buffers, waiting if locked;
           2) wait for completion by waiting for all buffers to unlock. */
- repeat:
-       retry = 0;
- repeat2:
-       ncount = 0;
+       do {
+               retry = 0;
+repeat:
        /* We search all lists as a failsafe mechanism, not because we expect
           there to be dirty buffers on any of the other lists. */
-       for(nlist = 0; nlist < NR_LIST; nlist++)
-        {
-        repeat1:
-                bh = lru_list[nlist];
-                if(!bh) continue;
-                for (i = nr_buffers_type[nlist]*2 ; i-- > 0 ; bh = next) {
-                        if(bh->b_list != nlist) goto repeat1;
-                        next = bh->b_next_free;
-                        if(!lru_list[nlist]) break;
-                        if (dev && bh->b_dev != dev)
-                                 continue;
-                        if (buffer_locked(bh))
-                         {
-                                 /* Buffer is locked; skip it unless wait is
-                                    requested AND pass > 0. */
-                                 if (!wait || !pass) {
-                                         retry = 1;
-                                         continue;
-                                 }
-                                 wait_on_buffer (bh);
-                                 goto repeat2;
-                         }
-                        /* If an unlocked buffer is not uptodate, there has
-                            been an IO error. Skip it. */
-                        if (wait && buffer_req(bh) && !buffer_locked(bh) &&
-                            !buffer_dirty(bh) && !buffer_uptodate(bh)) {
-                                 err = 1;
-                                 continue;
-                         }
-                        /* Don't write clean buffers.  Don't write ANY buffers
-                           on the third pass. */
-                        if (!buffer_dirty(bh) || pass>=2)
-                                 continue;
-                        /* don't bother about locked buffers */
-                        if (buffer_locked(bh))
-                                continue;
-                        bh->b_count++;
-                        bh->b_flushtime = 0;
-                        ll_rw_block(WRITE, 1, &bh);
-
-                        if(nlist != BUF_DIRTY) { 
-                                printk("[%d %s %ld] ", nlist,
-                                       kdevname(bh->b_dev), bh->b_blocknr);
-                                ncount++;
-                        }
-                        bh->b_count--;
-                        retry = 1;
-                }
-        }
-       if (ncount)
-         printk("sys_sync: %d dirty buffers not on dirty list\n", ncount);
-       
+               bh = lru_list[BUF_DIRTY];
+               if (!bh)
+                       goto repeat2;
+               for (i = nr_buffers_type[BUF_DIRTY]*2 ; i-- > 0 ; bh = next) {
+                       if (bh->b_list != BUF_DIRTY)
+                               goto repeat;
+                       next = bh->b_next_free;
+                       if (!lru_list[BUF_DIRTY])
+                               break;
+                       if (dev && bh->b_dev != dev)
+                               continue;
+                       if (buffer_locked(bh)) {
+                               /* Buffer is locked; skip it unless wait is
+                                  requested AND pass > 0. */
+                               if (!wait || !pass) {
+                                       retry = 1;
+                                       continue;
+                               }
+                               wait_on_buffer (bh);
+                               goto repeat;
+                       }
+                       /* If an unlocked buffer is not uptodate, there has
+                           been an IO error. Skip it. */
+                       if (wait && buffer_req(bh) && !buffer_locked(bh) &&
+                           !buffer_dirty(bh) && !buffer_uptodate(bh)) {
+                               err = 1;
+                               continue;
+                       }
+                       /* Don't write clean buffers.  Don't write ANY buffers
+                          on the third pass. */
+                       if (!buffer_dirty(bh) || pass >= 2)
+                               continue;
+                       /* don't bother about locked buffers */
+                       if (buffer_locked(bh))
+                               continue;
+                       bh->b_count++;
+                       next->b_count++;
+                       bh->b_flushtime = 0;
+                       ll_rw_block(WRITE, 1, &bh);
+                       bh->b_count--;
+                       next->b_count--;
+                       retry = 1;
+               }
+
+    repeat2:
+               bh = lru_list[BUF_LOCKED];
+               if (!bh)
+                       break;
+               for (i = nr_buffers_type[BUF_LOCKED]*2 ; i-- > 0 ; bh = next) {
+                       if (bh->b_list != BUF_LOCKED)
+                               goto repeat2;
+                       next = bh->b_next_free;
+                       if (!lru_list[BUF_LOCKED])
+                               break;
+                       if (dev && bh->b_dev != dev)
+                               continue;
+                       if (buffer_locked(bh)) {
+                               /* Buffer is locked; skip it unless wait is
+                                  requested AND pass > 0. */
+                               if (!wait || !pass) {
+                                       retry = 1;
+                                       continue;
+                               }
+                               wait_on_buffer (bh);
+                               goto repeat2;
+                       }
+               }
+
        /* If we are waiting for the sync to succeed, and if any dirty
           blocks were written, then repeat; on the second pass, only
           wait for buffers being written (do not pass to write any
           more buffers on the second pass). */
-       if (wait && retry && ++pass<=2)
-                goto repeat;
+       } while (wait && retry && ++pass<=2);
        return err;
 }
 
@@ -312,7 +311,7 @@ void invalidate_buffers(kdev_t dev)
        }
 }
 
-#define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block))%nr_hash)
+#define _hashfn(dev,block) (((unsigned)(HASHDEV(dev)^block))&HASH_MASK)
 #define hash(dev,block) hash_table[_hashfn(dev,block)]
 
 static inline void remove_from_hash_queue(struct buffer_head * bh)
@@ -339,11 +338,6 @@ static inline void remove_from_lru_list(struct buffer_head * bh)
                 lru_list[bh->b_list] = bh->b_next_free;
        if (lru_list[bh->b_list] == bh)
                 lru_list[bh->b_list] = NULL;
-       if (next_to_age[bh->b_list] == bh)
-               next_to_age[bh->b_list] = bh->b_next_free;
-       if (next_to_age[bh->b_list] == bh)
-               next_to_age[bh->b_list] = NULL;
-
        bh->b_next_free = bh->b_prev_free = NULL;
 }
 
@@ -356,7 +350,6 @@ static inline void remove_from_free_list(struct buffer_head * bh)
                panic("Free list corrupted");
        if(!free_list[isize])
                panic("Free list empty");
-       nr_free[isize]--;
        if(bh->b_next_free == bh)
                 free_list[isize] = NULL;
        else {
@@ -376,7 +369,6 @@ static inline void remove_from_queues(struct buffer_head * bh)
                return;
        }
        nr_buffers_type[bh->b_list]--;
-       nr_buffers_st[BUFSIZE_INDEX(bh->b_size)][bh->b_list]--;
        remove_from_hash_queue(bh);
        remove_from_lru_list(bh);
 }
@@ -387,8 +379,6 @@ static inline void put_last_lru(struct buffer_head * bh)
                return;
        if (bh == lru_list[bh->b_list]) {
                lru_list[bh->b_list] = bh->b_next_free;
-               if (next_to_age[bh->b_list] == bh)
-                       next_to_age[bh->b_list] = bh->b_next_free;
                return;
        }
        if(bh->b_dev == B_FREE)
@@ -400,8 +390,6 @@ static inline void put_last_lru(struct buffer_head * bh)
                lru_list[bh->b_list] = bh;
                lru_list[bh->b_list]->b_prev_free = bh;
        }
-       if (!next_to_age[bh->b_list])
-               next_to_age[bh->b_list] = bh;
 
        bh->b_next_free = lru_list[bh->b_list];
        bh->b_prev_free = lru_list[bh->b_list]->b_prev_free;
@@ -423,7 +411,6 @@ static inline void put_last_free(struct buffer_head * bh)
                bh->b_prev_free = bh;
        }
 
-       nr_free[isize]++;
        bh->b_next_free = free_list[isize];
        bh->b_prev_free = free_list[isize]->b_prev_free;
        free_list[isize]->b_prev_free->b_next_free = bh;
@@ -441,15 +428,13 @@ static inline void insert_into_queues(struct buffer_head * bh)
                lru_list[bh->b_list] = bh;
                bh->b_prev_free = bh;
        }
-       if (!next_to_age[bh->b_list])
-               next_to_age[bh->b_list] = bh;
+
        if (bh->b_next_free) panic("VFS: buffer LRU pointers corrupted");
        bh->b_next_free = lru_list[bh->b_list];
        bh->b_prev_free = lru_list[bh->b_list]->b_prev_free;
        lru_list[bh->b_list]->b_prev_free->b_next_free = bh;
        lru_list[bh->b_list]->b_prev_free = bh;
        nr_buffers_type[bh->b_list]++;
-       nr_buffers_st[BUFSIZE_INDEX(bh->b_size)][bh->b_list]++;
 /* put the buffer in new hash-queue if it has a device */
        bh->b_prev = NULL;
        bh->b_next = NULL;
@@ -502,6 +487,7 @@ struct buffer_head * get_hash_table(kdev_t dev, int block, int size)
 
 void set_blocksize(kdev_t dev, int size)
 {
+       extern int *blksize_size[];
        int i, nlist;
        struct buffer_head * bh, *bhnext;
 
@@ -550,26 +536,69 @@ void set_blocksize(kdev_t dev, int size)
        }
 }
 
-#define BADNESS(bh) (buffer_dirty(bh) || buffer_locked(bh))
 
-void refill_freelist(int size)
+/* check if a buffer is OK to be reclaimed */
+static inline int can_reclaim(struct buffer_head *bh, int size)
+{
+       if (bh->b_count || 
+           buffer_protected(bh) || buffer_locked(bh))
+               return 0;
+                        
+       if (mem_map[MAP_NR((unsigned long) bh->b_data)].count != 1 ||
+           buffer_dirty(bh)) {
+               refile_buffer(bh);
+               return 0;
+       }
+
+       if (bh->b_size != size)
+               return 0;
+
+       return 1;
+}
+
+/* find a candidate buffer to be reclaimed */
+static struct buffer_head *find_candidate(struct buffer_head *list,int *list_len,int size)
+{
+       struct buffer_head *bh;
+       
+       for (bh = list; 
+            bh && (*list_len) > 0; 
+            bh = bh->b_next_free, (*list_len)--) {
+               if (size != bh->b_size) {
+                       /* this provides a mechanism for freeing blocks
+                          of other sizes, this is necessary now that we
+                          no longer have the lav code. */
+                       try_to_free_buffer(bh,&bh,1);
+                       continue;
+               }
+
+               if (buffer_locked(bh) && 
+                   (bh->b_list == BUF_LOCKED || bh->b_list == BUF_LOCKED1)) {
+                       /* Buffers are written in the order they are placed 
+                          on the locked list. If we encounter a locked
+                          buffer here, this means that the rest of them
+                          are also locked */
+                       (*list_len) = 0;
+                       return NULL;
+               }
+
+               if (can_reclaim(bh,size))
+                   return bh;
+       }
+
+       return NULL;
+}
+
+static void refill_freelist(int size)
 {
-       struct buffer_head * bh, * tmp;
-       struct buffer_head * candidate[NR_LIST];
+       struct buffer_head * bh;
+       struct buffer_head * candidate[BUF_DIRTY];
        unsigned int best_time, winner;
-       int isize = BUFSIZE_INDEX(size);
-       int buffers[NR_LIST];
+       int buffers[BUF_DIRTY];
        int i;
        int needed;
 
-       /* First see if we even need this.  Sometimes it is advantageous
-        to request some blocks in a filesystem that we know that we will
-        be needing ahead of time. */
-
-       if (nr_free[isize] > 100)
-               return;
-
-       ++refilled;
+       refilled = 1;
        /* If there are too many dirty buffers, we wake up the update process
           now so as to ensure that there are still clean buffers available
           for user processes to use (and dirty) */
@@ -582,72 +611,24 @@ void refill_freelist(int size)
                needed -= PAGE_SIZE;
        }
 
-       if(needed <= 0) return;
-
-       /* See if there are too many buffers of a different size.
-          If so, victimize them */
-
-       while(maybe_shrink_lav_buffers(size))
-        {
-                if(!grow_buffers(GFP_BUFFER, size)) break;
-                needed -= PAGE_SIZE;
-                if(needed <= 0) return;
-        };
-
+repeat:
        /* OK, we cannot grow the buffer cache, now try to get some
           from the lru list */
 
        /* First set the candidate pointers to usable buffers.  This
           should be quick nearly all of the time. */
 
-repeat0:
-       for(i=0; i<NR_LIST; i++){
-               if(i == BUF_DIRTY || i == BUF_SHARED || 
-                  nr_buffers_type[i] == 0) {
-                       candidate[i] = NULL;
-                       buffers[i] = 0;
-                       continue;
-               }
+       if(needed <= 0) return;
+
+       for(i=0; i<BUF_DIRTY; i++){
                buffers[i] = nr_buffers_type[i];
-               for (bh = lru_list[i]; buffers[i] > 0; bh = tmp, buffers[i]--)
-                {
-                        if(buffers[i] < 0) panic("Here is the problem");
-                        tmp = bh->b_next_free;
-                        if (!bh) break;
-                        
-                        if (mem_map[MAP_NR((unsigned long) bh->b_data)].count != 1 ||
-                            buffer_dirty(bh)) {
-                                refile_buffer(bh);
-                                continue;
-                        }
-                        
-                        if (bh->b_count || buffer_protected(bh) || bh->b_size != size)
-                                 continue;
-                        
-                        /* Buffers are written in the order they are placed 
-                           on the locked list. If we encounter a locked
-                           buffer here, this means that the rest of them
-                           are also locked */
-                        if (buffer_locked(bh) && (i == BUF_LOCKED || i == BUF_LOCKED1)) {
-                                buffers[i] = 0;
-                                break;
-                        }
-                        
-                        if (BADNESS(bh)) continue;
-                        break;
-                };
-               if(!buffers[i]) candidate[i] = NULL; /* Nothing on this list */
-               else candidate[i] = bh;
-               if(candidate[i] && candidate[i]->b_count) panic("Here is the problem");
+               candidate[i] = find_candidate(lru_list[i], &buffers[i], size);
        }
        
- repeat:
-       if(needed <= 0) return;
-       
        /* Now see which candidate wins the election */
        
        winner = best_time = UINT_MAX;  
-       for(i=0; i<NR_LIST; i++){
+       for(i=0; i<BUF_DIRTY; i++){
                if(!candidate[i]) continue;
                if(candidate[i]->b_lru_time < best_time){
                        best_time = candidate[i]->b_lru_time;
@@ -658,65 +639,21 @@ repeat0:
        /* If we have a winner, use it, and then get a new candidate from that list */
        if(winner != UINT_MAX) {
                i = winner;
-               bh = candidate[i];
-               candidate[i] = bh->b_next_free;
-               if(candidate[i] == bh) candidate[i] = NULL;  /* Got last one */
-               if (bh->b_count || bh->b_size != size)
-                        panic("Busy buffer in candidate list\n");
-               if (mem_map[MAP_NR((unsigned long) bh->b_data)].count != 1)
-                        panic("Shared buffer in candidate list\n");
-               if (buffer_protected(bh))
-                       panic("Protected buffer in candidate list\n");
-               if (BADNESS(bh)) panic("Buffer in candidate list with BADNESS != 0\n");
-               
-               if(bh->b_dev == B_FREE)
-                       panic("Wrong list");
-               remove_from_queues(bh);
-               bh->b_dev = B_FREE;
-               put_last_free(bh);
-               needed -= bh->b_size;
-               buffers[i]--;
-               if(buffers[i] < 0) panic("Here is the problem");
-               
-               if(buffers[i] == 0) candidate[i] = NULL;
+               while (needed>0 && (bh=candidate[i])) {
+                       candidate[i] = bh->b_next_free;
+                       if(candidate[i] == bh) candidate[i] = NULL;  /* Got last one */
+                       remove_from_queues(bh);
+                       bh->b_dev = B_FREE;
+                       put_last_free(bh);
+                       needed -= bh->b_size;
+                       buffers[i]--;
+                       if(buffers[i] == 0) candidate[i] = NULL;
                
-               /* Now all we need to do is advance the candidate pointer
-                  from the winner list to the next usable buffer */
-               if(candidate[i] && buffers[i] > 0){
-                       if(buffers[i] <= 0) panic("Here is another problem");
-                       for (bh = candidate[i]; buffers[i] > 0; bh = tmp, buffers[i]--) {
-                               if(buffers[i] < 0) panic("Here is the problem");
-                               tmp = bh->b_next_free;
-                               if (!bh) break;
-                               
-                               if (mem_map[MAP_NR((unsigned long) bh->b_data)].count != 1 ||
-                                   buffer_dirty(bh)) {
-                                       refile_buffer(bh);
-                                       continue;
-                               };
-                               
-                               if (bh->b_count || buffer_protected(bh) || bh->b_size != size)
-                                        continue;
-                               
-                               /* Buffers are written in the order they are
-                                  placed on the locked list.  If we encounter
-                                  a locked buffer here, this means that the
-                                  rest of them are also locked */
-                               if (buffer_locked(bh) && (i == BUF_LOCKED || i == BUF_LOCKED1)) {
-                                       buffers[i] = 0;
-                                       break;
-                               }
-             
-                               if (BADNESS(bh)) continue;
-                               break;
-                       };
-                       if(!buffers[i]) candidate[i] = NULL; /* Nothing here */
-                       else candidate[i] = bh;
-                       if(candidate[i] && candidate[i]->b_count) 
-                                panic("Here is the problem");
+                       if (candidate[i] && !can_reclaim(candidate[i],size))
+                               candidate[i] = find_candidate(candidate[i],&buffers[i], size);
                }
-               
-               goto repeat;
+               if (needed >= 0)
+                       goto repeat;
        }
        
        if(needed <= 0) return;
@@ -726,15 +663,15 @@ repeat0:
        if (nr_free_pages > min_free_pages + 5) {
                if (grow_buffers(GFP_BUFFER, size)) {
                        needed -= PAGE_SIZE;
-                       goto repeat0;
+                       goto repeat;
                };
        }
-       
+
        /* and repeat until we find something good */
        if (!grow_buffers(GFP_ATOMIC, size))
                wakeup_bdflush(1);
        needed -= PAGE_SIZE;
-       goto repeat0;
+       goto repeat;
 }
 
 /*
@@ -752,9 +689,6 @@ struct buffer_head * getblk(kdev_t dev, int block, int size)
        struct buffer_head * bh;
        int isize = BUFSIZE_INDEX(size);
 
-       /* Update this for the buffer size lav. */
-       buffer_usage[isize]++;
-
        /* If there are too many dirty buffers, we wake up the update process
           now so as to ensure that there are still clean buffers available
           for user processes to use (and dirty) */
@@ -770,7 +704,9 @@ repeat:
                return bh;
        }
 
-       while(!free_list[isize]) refill_freelist(size);
+       while(!free_list[isize]) {
+               refill_freelist(size);
+       }
        
        if (find_buffer(dev,block,size))
                 goto repeat;
@@ -812,7 +748,6 @@ void set_writetime(struct buffer_head * buf, int flag)
 void refile_buffer(struct buffer_head * buf)
 {
        int dispose;
-       int isize;
 
        if(buf->b_dev == B_FREE) {
                printk("Attempt to refile free buffer\n");
@@ -820,17 +755,13 @@ void refile_buffer(struct buffer_head * buf)
        }
        if (buffer_dirty(buf))
                dispose = BUF_DIRTY;
-       else if ((mem_map[MAP_NR((unsigned long) buf->b_data)].count > 1) || buffer_protected(buf))
-               dispose = BUF_SHARED;
        else if (buffer_locked(buf))
                dispose = BUF_LOCKED;
-       else if (buf->b_list == BUF_SHARED)
-               dispose = BUF_UNSHARED;
        else
                dispose = BUF_CLEAN;
        if(dispose == BUF_CLEAN) buf->b_lru_time = jiffies;
        if(dispose != buf->b_list)  {
-               if(dispose == BUF_DIRTY || dispose == BUF_UNSHARED)
+               if(dispose == BUF_DIRTY)
                         buf->b_lru_time = jiffies;
                if(dispose == BUF_LOCKED && 
                   (buf->b_flushtime - buf->b_lru_time) <= bdf_prm.b_un.age_super)
@@ -841,16 +772,13 @@ void refile_buffer(struct buffer_head * buf)
                if (dispose == BUF_DIRTY) {
                /* This buffer is dirty, maybe we need to start flushing. */
                /* If too high a percentage of the buffers are dirty... */
-               if (nr_buffers_type[BUF_DIRTY] > 
-                  (nr_buffers - nr_buffers_type[BUF_SHARED]) *
-                  bdf_prm.b_un.nfract/100)
+               if (nr_buffers_type[BUF_DIRTY] > nr_buffers * bdf_prm.b_un.nfract/100)
                         wakeup_bdflush(0);
                /* If this is a loop device, and
-                * more than half of the buffers of this size are dirty... */ 
+                * more than half of the buffers are dirty... */ 
                /* (Prevents no-free-buffers deadlock with loop device.) */
-               isize = BUFSIZE_INDEX(buf->b_size);
                if (MAJOR(buf->b_dev) == LOOP_MAJOR &&
-                   nr_buffers_st[isize][BUF_DIRTY]*2>nr_buffers_size[isize])
+                   nr_buffers_type[BUF_DIRTY]*2>nr_buffers)
                        wakeup_bdflush(1);
                }
        }
@@ -977,11 +905,15 @@ struct buffer_head * breada(kdev_t dev, int block, int bufsize,
        return NULL;
 }
 
-/*
- * See fs/inode.c for the weird use of volatile..
- */
 static void put_unused_buffer_head(struct buffer_head * bh)
 {
+       if (nr_unused_buffer_heads >= MAX_UNUSED_BUFFERS) {
+               nr_buffer_heads--;
+               kfree(bh);
+               return;
+       }
+       memset(bh,0,sizeof(*bh));
+       nr_unused_buffer_heads++;
        bh->b_next_free = unused_list;
        unused_list = bh;
        wake_up(&buffer_wait);
@@ -989,21 +921,23 @@ static void put_unused_buffer_head(struct buffer_head * bh)
 
 static void get_more_buffer_heads(void)
 {
-       int i;
        struct buffer_head * bh;
 
-       for (;;) {
-               if (unused_list)
-                       return;
-
+       while (!unused_list) {
                /*
                 * This is critical.  We can't swap out pages to get
                 * more buffer heads, because the swap-out may need
                 * more buffer-heads itself.  Thus GFP_ATOMIC.
                 */
-               bh = (struct buffer_head *) get_free_page(GFP_ATOMIC);
-               if (bh)
-                       break;
+               /* we now use kmalloc() here instead of gfp as we want
+                   to be able to easily release buffer heads - they
+                   took up quite a bit of memory (tridge) */
+               bh = (struct buffer_head *) kmalloc(sizeof(*bh),GFP_ATOMIC);
+               if (bh) {
+                       put_unused_buffer_head(bh);
+                       nr_buffer_heads++;
+                       return;
+               }
 
                /*
                 * Uhhuh. We're _really_ low on memory. Now we just
@@ -1014,10 +948,6 @@ static void get_more_buffer_heads(void)
                sleep_on(&buffer_wait);
        }
 
-       for (nr_buffer_heads+=i=PAGE_SIZE/sizeof*bh ; i>0; i--) {
-               bh->b_next_free = unused_list;  /* only make link */
-               unused_list = bh++;
-       }
 }
 
 /* 
@@ -1036,17 +966,15 @@ static void get_more_buffer_heads(void)
 static inline void recover_reusable_buffer_heads(void)
 {
        if (reuse_list) {
-               struct buffer_head *bh;
-               unsigned long flags;
+               struct buffer_head *head;
+
+               head = xchg(&reuse_list, NULL);
        
-               save_flags(flags);
                do {
-                       cli();
-                       bh = reuse_list;
-                       reuse_list = bh->b_next_free;
-                       restore_flags(flags);
+                       struct buffer_head *bh = head;
+                       head = head->b_next_free;
                        put_unused_buffer_head(bh);
-               } while (reuse_list);
+               } while (head);
        }
 }
 
@@ -1060,6 +988,7 @@ static struct buffer_head * get_unused_buffer_head(void)
                return NULL;
        bh = unused_list;
        unused_list = bh->b_next_free;
+       nr_unused_buffer_heads--;
        return bh;
 }
 
@@ -1214,7 +1143,7 @@ int brw_page(int rw, struct page *page, kdev_t dev, int b[], int size, int bmap)
                arr[nr++] = next;
        } while (prev = next, (next = next->b_this_page) != NULL);
        prev->b_this_page = bh;
-       
+
        if (nr) {
                ll_rw_block(rw, nr, arr);
                /* The rest of the work is done in mark_buffer_uptodate()
@@ -1339,7 +1268,7 @@ int generic_readpage(struct inode * inode, struct page * page)
        page->count++;
        set_bit(PG_locked, &page->flags);
        set_bit(PG_free_after, &page->flags);
-       
+
        i = PAGE_SIZE >> inode->i_sb->s_blocksize_bits;
        block = page->offset >> inode->i_sb->s_blocksize_bits;
        p = nr;
@@ -1385,7 +1314,6 @@ static int grow_buffers(int pri, int size)
 
        tmp = bh;
        while (1) {
-               nr_free[isize]++;
                if (insert_point) {
                        tmp->b_next_free = insert_point->b_next_free;
                        tmp->b_prev_free = insert_point;
@@ -1397,7 +1325,6 @@ static int grow_buffers(int pri, int size)
                }
                insert_point = tmp;
                ++nr_buffers;
-               ++nr_buffers_size[isize];
                if (tmp->b_this_page)
                        tmp = tmp->b_this_page;
                else
@@ -1427,7 +1354,6 @@ int try_to_free_buffer(struct buffer_head * bh, struct buffer_head ** bhp,
 {
        unsigned long page;
        struct buffer_head * tmp, * p;
-       int isize = BUFSIZE_INDEX(bh->b_size);
 
        *bhp = bh;
        page = (unsigned long) bh->b_data;
@@ -1449,7 +1375,6 @@ int try_to_free_buffer(struct buffer_head * bh, struct buffer_head ** bhp,
                p = tmp;
                tmp = tmp->b_this_page;
                nr_buffers--;
-               nr_buffers_size[isize]--;
                if (p == *bhp)
                  {
                    *bhp = p->b_prev_free;
@@ -1465,177 +1390,6 @@ int try_to_free_buffer(struct buffer_head * bh, struct buffer_head ** bhp,
        return !mem_map[MAP_NR(page)].count;
 }
 
-/* Age buffers on a given page, according to whether they have been
-   visited recently or not. */
-static inline void age_buffer(struct buffer_head *bh)
-{
-       struct buffer_head *tmp = bh;
-       int touched = 0;
-
-       /*
-        * When we age a page, we mark all other buffers in the page
-        * with the "has_aged" flag.  Then, when these aliased buffers
-        * come up for aging, we skip them until next pass.  This
-        * ensures that a page full of multiple buffers only gets aged
-        * once per pass through the lru lists. 
-        */
-       if (clear_bit(BH_Has_aged, &bh->b_state))
-               return;
-       
-       do {
-               touched |= clear_bit(BH_Touched, &tmp->b_state);
-               tmp = tmp->b_this_page;
-               set_bit(BH_Has_aged, &tmp->b_state);
-       } while (tmp != bh);
-       clear_bit(BH_Has_aged, &bh->b_state);
-
-       if (touched) 
-               touch_page(mem_map + MAP_NR((unsigned long) bh->b_data));
-       else
-               age_page(mem_map + MAP_NR((unsigned long) bh->b_data));
-}
-
-/*
- * Consult the load average for buffers and decide whether or not
- * we should shrink the buffers of one size or not.  If we decide yes,
- * do it and return 1.  Else return 0.  Do not attempt to shrink size
- * that is specified.
- *
- * I would prefer not to use a load average, but the way things are now it
- * seems unavoidable.  The way to get rid of it would be to force clustering
- * universally, so that when we reclaim buffers we always reclaim an entire
- * page.  Doing this would mean that we all need to move towards QMAGIC.
- */
-
-static int maybe_shrink_lav_buffers(int size)
-{         
-       int nlist;
-       int isize;
-       int total_lav, total_n_buffers, n_sizes;
-       
-       /* Do not consider the shared buffers since they would not tend
-          to have getblk called very often, and this would throw off
-          the lav.  They are not easily reclaimable anyway (let the swapper
-          make the first move). */
-  
-       total_lav = total_n_buffers = n_sizes = 0;
-       for(nlist = 0; nlist < NR_SIZES; nlist++)
-        {
-                total_lav += buffers_lav[nlist];
-                if(nr_buffers_size[nlist]) n_sizes++;
-                total_n_buffers += nr_buffers_size[nlist];
-                total_n_buffers -= nr_buffers_st[nlist][BUF_SHARED]; 
-        }
-       
-       /* See if we have an excessive number of buffers of a particular
-          size - if so, victimize that bunch. */
-  
-       isize = (size ? BUFSIZE_INDEX(size) : -1);
-       
-       if (n_sizes > 1)
-                for(nlist = 0; nlist < NR_SIZES; nlist++)
-                 {
-                         if(nlist == isize) continue;
-                         if(nr_buffers_size[nlist] &&
-                            bdf_prm.b_un.lav_const * buffers_lav[nlist]*total_n_buffers < 
-                            total_lav * (nr_buffers_size[nlist] - nr_buffers_st[nlist][BUF_SHARED]))
-                                  if(shrink_specific_buffers(6, bufferindex_size[nlist])) 
-                                           return 1;
-                 }
-       return 0;
-}
-
-/*
- * Try to free up some pages by shrinking the buffer-cache
- *
- * Priority tells the routine how hard to try to shrink the
- * buffers: 6 means "don't bother too much", while a value
- * of 0 means "we'd better get some free pages now".
- *
- * "limit" is meant to limit the shrink-action only to pages
- * that are in the 0 - limit address range, for DMA re-allocations.
- * We ignore that right now.
- */
-
-static int shrink_specific_buffers(unsigned int priority, int size)
-{
-       struct buffer_head *bh;
-       int nlist;
-       int i, isize, isize1;
-
-#ifdef DEBUG
-       if(size) printk("Shrinking buffers of size %d\n", size);
-#endif
-       /* First try the free lists, and see if we can get a complete page
-          from here */
-       isize1 = (size ? BUFSIZE_INDEX(size) : -1);
-
-       for(isize = 0; isize<NR_SIZES; isize++){
-               if(isize1 != -1 && isize1 != isize) continue;
-               bh = free_list[isize];
-               if(!bh) continue;
-               for (i=0 ; !i || bh != free_list[isize]; bh = bh->b_next_free, i++) {
-                       if (bh->b_count || buffer_protected(bh) ||
-                           !bh->b_this_page)
-                                continue;
-                       if (!age_of((unsigned long) bh->b_data) &&
-                           try_to_free_buffer(bh, &bh, 6))
-                                return 1;
-                       if(!bh) break;
-                       /* Some interrupt must have used it after we
-                          freed the page.  No big deal - keep looking */
-               }
-       }
-       
-       /* Not enough in the free lists, now try the lru list */
-       
-       for(nlist = 0; nlist < NR_LIST; nlist++) {
-       repeat1:
-               if(priority > 2 && nlist == BUF_SHARED) continue;
-               i = nr_buffers_type[nlist];
-               i = ((BUFFEROUT_WEIGHT * i) >> 10) >> priority;
-               for ( ; i > 0; i-- ) {
-                       bh = next_to_age[nlist];
-                       if (!bh)
-                               break;
-                       next_to_age[nlist] = bh->b_next_free;
-
-                       /* First, age the buffer. */
-                       age_buffer(bh);
-                       /* We may have stalled while waiting for I/O
-                          to complete. */
-                       if(bh->b_list != nlist) goto repeat1;
-                       if (bh->b_count || buffer_protected(bh) ||
-                           !bh->b_this_page)
-                                continue;
-                       if(size && bh->b_size != size) continue;
-                       if (buffer_locked(bh))
-                                if (priority)
-                                         continue;
-                                else
-                                         wait_on_buffer(bh);
-                       if (buffer_dirty(bh)) {
-                               bh->b_count++;
-                               bh->b_flushtime = 0;
-                               ll_rw_block(WRITEA, 1, &bh);
-                               bh->b_count--;
-                               continue;
-                       }
-                       /* At priority 6, only consider really old
-                          (age==0) buffers for reclaiming.  At
-                          priority 0, consider any buffers. */
-                       if ((age_of((unsigned long) bh->b_data) >>
-                            (6-priority)) > 0)
-                               continue;                               
-                       if (try_to_free_buffer(bh, &bh, 0))
-                                return 1;
-                       if(!bh) break;
-               }
-       }
-       return 0;
-}
-
-
 /* ================== Debugging =================== */
 
 void show_buffers(void)
@@ -1643,17 +1397,18 @@ void show_buffers(void)
        struct buffer_head * bh;
        int found = 0, locked = 0, dirty = 0, used = 0, lastused = 0;
        int protected = 0;
-       int shared;
-       int nlist, isize;
+       int nlist;
+       static char *buf_types[NR_LIST] = {"CLEAN","LOCKED","LOCKED1","DIRTY"};
 
        printk("Buffer memory:   %6dkB\n",buffermem>>10);
        printk("Buffer heads:    %6d\n",nr_buffer_heads);
        printk("Buffer blocks:   %6d\n",nr_buffers);
 
        for(nlist = 0; nlist < NR_LIST; nlist++) {
-         shared = found = locked = dirty = used = lastused = protected = 0;
+         found = locked = dirty = used = lastused = protected = 0;
          bh = lru_list[nlist];
          if(!bh) continue;
+
          do {
                found++;
                if (buffer_locked(bh))
@@ -1662,234 +1417,31 @@ void show_buffers(void)
                        protected++;
                if (buffer_dirty(bh))
                        dirty++;
-               if (mem_map[MAP_NR(((unsigned long) bh->b_data))].count != 1)
-                       shared++;
                if (bh->b_count)
                        used++, lastused = found;
                bh = bh->b_next_free;
          } while (bh != lru_list[nlist]);
-         printk("Buffer[%d] mem: %d buffers, %d used (last=%d), "
-                "%d locked, %d protected, %d dirty %d shrd\n",
-                nlist, found, used, lastused,
-                locked, protected, dirty, shared);
-       };
-       printk("Size    [LAV]     Free  Clean  Unshar     Lck    Lck1   Dirty  Shared \n");
-       for(isize = 0; isize<NR_SIZES; isize++){
-               printk("%5d [%5d]: %7d ", bufferindex_size[isize],
-                      buffers_lav[isize], nr_free[isize]);
-               for(nlist = 0; nlist < NR_LIST; nlist++)
-                        printk("%7d ", nr_buffers_st[isize][nlist]);
-               printk("\n");
-       }
-}
-
-
-/* ====================== Cluster patches for ext2 ==================== */
-
-/*
- * try_to_reassign() checks if all the buffers on this particular page
- * are unused, and reassign to a new cluster them if this is true.
- */
-static inline int try_to_reassign(struct buffer_head * bh, struct buffer_head ** bhp,
-                          kdev_t dev, unsigned int starting_block)
-{
-       unsigned long page;
-       struct buffer_head * tmp, * p;
-
-       *bhp = bh;
-       page = (unsigned long) bh->b_data;
-       page &= PAGE_MASK;
-       if(mem_map[MAP_NR(page)].count != 1) return 0;
-       tmp = bh;
-       do {
-               if (!tmp)
-                        return 0;
-               
-               if (tmp->b_count || buffer_protected(tmp) ||
-                   buffer_dirty(tmp) || buffer_locked(tmp))
-                        return 0;
-               tmp = tmp->b_this_page;
-       } while (tmp != bh);
-       tmp = bh;
-       
-       while((unsigned long) tmp->b_data & (PAGE_SIZE - 1)) 
-                tmp = tmp->b_this_page;
-       
-       /* This is the buffer at the head of the page */
-       bh = tmp;
-       do {
-               p = tmp;
-               tmp = tmp->b_this_page;
-               remove_from_queues(p);
-               p->b_dev = dev;
-               mark_buffer_uptodate(p, 0);
-               clear_bit(BH_Req, &p->b_state);
-               p->b_blocknr = starting_block++;
-               insert_into_queues(p);
-       } while (tmp != bh);
-       return 1;
-}
-
-/*
- * Try to find a free cluster by locating a page where
- * all of the buffers are unused.  We would like this function
- * to be atomic, so we do not call anything that might cause
- * the process to sleep.  The priority is somewhat similar to
- * the priority used in shrink_buffers.
- * 
- * My thinking is that the kernel should end up using whole
- * pages for the buffer cache as much of the time as possible.
- * This way the other buffers on a particular page are likely
- * to be very near each other on the free list, and we will not
- * be expiring data prematurely.  For now we only cannibalize buffers
- * of the same size to keep the code simpler.
- */
-static int reassign_cluster(kdev_t dev, 
-                    unsigned int starting_block, int size)
-{
-       struct buffer_head *bh;
-       int isize = BUFSIZE_INDEX(size);
-       int i;
-
-       /* We want to give ourselves a really good shot at generating
-          a cluster, and since we only take buffers from the free
-          list, we "overfill" it a little. */
-
-       while(nr_free[isize] < 32) refill_freelist(size);
-
-       bh = free_list[isize];
-       if(bh)
-                for (i=0 ; !i || bh != free_list[isize] ; bh = bh->b_next_free, i++) {
-                        if (!bh->b_this_page)  continue;
-                        if (try_to_reassign(bh, &bh, dev, starting_block))
-                                return 4;
-                }
-       return 0;
-}
-
-/* This function tries to generate a new cluster of buffers
- * from a new page in memory.  We should only do this if we have
- * not expanded the buffer cache to the maximum size that we allow.
- */
-static unsigned long try_to_generate_cluster(kdev_t dev, int block, int size)
-{
-       struct buffer_head * bh, * tmp, * arr[MAX_BUF_PER_PAGE];
-       int isize = BUFSIZE_INDEX(size);
-       unsigned long offset;
-       unsigned long page;
-       int nblock;
-
-       page = get_free_page(GFP_NOBUFFER);
-       if(!page) return 0;
-
-       bh = create_buffers(page, size);
-       if (!bh) {
-               free_page(page);
-               return 0;
-       };
-       nblock = block;
-       for (offset = 0 ; offset < PAGE_SIZE ; offset += size) {
-               if (find_buffer(dev, nblock++, size))
-                        goto not_aligned;
-       }
-       tmp = bh;
-       nblock = 0;
-       while (1) {
-               arr[nblock++] = bh;
-               bh->b_count = 1;
-               bh->b_flushtime = 0;
-               bh->b_state = 0;
-               bh->b_dev = dev;
-               bh->b_list = BUF_CLEAN;
-               bh->b_blocknr = block++;
-               nr_buffers++;
-               nr_buffers_size[isize]++;
-               insert_into_queues(bh);
-               if (bh->b_this_page)
-                       bh = bh->b_this_page;
-               else
-                       break;
-       }
-       buffermem += PAGE_SIZE;
-       mem_map[MAP_NR(page)].buffers = bh;
-       bh->b_this_page = tmp;
-       while (nblock-- > 0)
-               brelse(arr[nblock]);
-       return 4; /* ?? */
-not_aligned:
-       while ((tmp = bh) != NULL) {
-               bh = bh->b_this_page;
-               put_unused_buffer_head(tmp);
-       }
-       free_page(page);
-       return 0;
-}
-
-unsigned long generate_cluster(kdev_t dev, int b[], int size)
-{
-       int i, offset;
-       
-       for (i = 0, offset = 0 ; offset < PAGE_SIZE ; i++, offset += size) {
-               if(i && b[i]-1 != b[i-1]) return 0;  /* No need to cluster */
-               if(find_buffer(dev, b[i], size)) return 0;
+         printk("%8s: %d buffers, %d used (last=%d), "
+                "%d locked, %d protected, %d dirty\n",
+                buf_types[nlist], found, used, lastused,
+                locked, protected, dirty);
        };
-
-       /* OK, we have a candidate for a new cluster */
-       
-       /* See if one size of buffer is over-represented in the buffer cache,
-          if so reduce the numbers of buffers */
-       if(maybe_shrink_lav_buffers(size))
-        {
-                int retval;
-                retval = try_to_generate_cluster(dev, b[0], size);
-                if(retval) return retval;
-        };
-       
-       if (nr_free_pages > min_free_pages*2) 
-                return try_to_generate_cluster(dev, b[0], size);
-       else
-                return reassign_cluster(dev, b[0], size);
 }
 
-
 /* ===================== Init ======================= */
 
 /*
- * This initializes the initial buffer free list.  nr_buffers_type is set
- * to one less the actual number of buffers, as a sop to backwards
- * compatibility --- the old code did this (I think unintentionally,
- * but I'm not sure), and programs in the ps package expect it.
- *                                     - TYT 8/30/92
+ * allocate the hash table and init the free list
  */
 void buffer_init(void)
 {
-       int i;
-       int isize = BUFSIZE_INDEX(BLOCK_SIZE);
-       long memsize = MAP_NR(high_memory) << PAGE_SHIFT;
-
-       if (memsize >= 64*1024*1024)
-               nr_hash = 65521;
-       else if (memsize >= 32*1024*1024)
-               nr_hash = 32749;
-       else if (memsize >= 16*1024*1024)
-               nr_hash = 16381;
-       else if (memsize >= 8*1024*1024)
-               nr_hash = 8191;
-       else if (memsize >= 4*1024*1024)
-               nr_hash = 4093;
-       else nr_hash = 997;
-       
-       hash_table = (struct buffer_head **) vmalloc(nr_hash * 
-                                                    sizeof(struct buffer_head *));
+       hash_table = (struct buffer_head **)vmalloc(NR_HASH*sizeof(struct buffer_head *));
+       if (!hash_table)
+               panic("Failed to allocate buffer hash table\n");
+       memset(hash_table,0,NR_HASH*sizeof(struct buffer_head *));
 
-
-       for (i = 0 ; i < nr_hash ; i++)
-               hash_table[i] = NULL;
        lru_list[BUF_CLEAN] = 0;
        grow_buffers(GFP_KERNEL, BLOCK_SIZE);
-       if (!free_list[isize])
-               panic("VFS: Unable to initialize buffer free list!");
-       return;
 }
 
 
@@ -1925,7 +1477,7 @@ static void wakeup_bdflush(int wait)
 
 asmlinkage int sync_old_buffers(void)
 {
-       int i, isize;
+       int i;
        int ndirty, nwritten;
        int nlist;
        int ncount;
@@ -1944,6 +1496,7 @@ asmlinkage int sync_old_buffers(void)
                ndirty = 0;
                nwritten = 0;
        repeat:
+
                bh = lru_list[nlist];
                if(bh) 
                         for (i = nr_buffers_type[nlist]; i-- > 0; bh = next) {
@@ -1981,13 +1534,6 @@ asmlinkage int sync_old_buffers(void)
        printk("Wrote %d/%d buffers\n", nwritten, ndirty);
 #endif
        
-       /* We assume that we only come through here on a regular
-          schedule, like every 5 seconds.  Now update load averages.  
-          Shift usage counts to prevent overflow. */
-       for(isize = 0; isize<NR_SIZES; isize++){
-               CALC_LOAD(buffers_lav[isize], bdf_prm.b_un.lav_const, buffer_usage[isize]);
-               buffer_usage[isize] = 0;
-       }
        return 0;
 }
 
@@ -1999,8 +1545,6 @@ asmlinkage int sync_old_buffers(void)
 
 asmlinkage int sys_bdflush(int func, long data)
 {
-       int i, error;
-
        if (!suser())
                return -EPERM;
 
@@ -2009,26 +1553,24 @@ asmlinkage int sys_bdflush(int func, long data)
 
        /* Basically func 1 means read param 1, 2 means write param 1, etc */
        if (func >= 2) {
-               i = (func-2) >> 1;
+               int i = (func-2) >> 1;
                if (i < 0 || i >= N_PARAM)
                        return -EINVAL;
                if((func & 1) == 0) {
-                       error = verify_area(VERIFY_WRITE, (void *) data, sizeof(int));
-                       if (error)
-                               return error;
-                       put_user(bdf_prm.data[i], (int*)data);
-                       return 0;
-               };
+                       int error = verify_area(VERIFY_WRITE, (int*)data, 4);
+                       if (!error)
+                               put_user(bdf_prm.data[i], (int*)data);
+                       return error;
+               }
                if (data < bdflush_min[i] || data > bdflush_max[i])
                        return -EINVAL;
                bdf_prm.data[i] = data;
-               return 0;
-       };
+       }
 
        /* Having func 0 used to launch the actual bdflush and then never
-       return (unless explicitly killed). We return zero here to 
-       remain semi-compatible with present update(8) programs. */
-
+        * return (unless explicitly killed). We return zero here to 
+        * remain semi-compatible with present update(8) programs.
+        */
        return 0;
 }
 
@@ -2070,12 +1612,12 @@ int bdflush(void * unused)
         *      and other internals and thus be subject to the SMP locking
         *      rules. (On a uniprocessor box this does nothing).
         */
-        
+
+
 #ifdef __SMP__
        lock_kernel();
        syscall_count++;
 #endif
-                
        for (;;) {
 #ifdef DEBUG
                printk("bdflush() activated...");
@@ -2091,6 +1633,7 @@ int bdflush(void * unused)
                         ndirty = 0;
                         refilled = 0;
                 repeat:
+
                         bh = lru_list[nlist];
                         if(bh) 
                                  for (i = nr_buffers_type[nlist]; i-- > 0 && ndirty < bdf_prm.b_un.ndirty; 
@@ -2151,9 +1694,7 @@ int bdflush(void * unused)
                
                /* 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 - nr_buffers_type[BUF_SHARED]) * 
-                  bdf_prm.b_un.nfract/100) {
+               if(nr_buffers_type[BUF_DIRTY] <= nr_buffers * bdf_prm.b_un.nfract/100) {
                        current->signal = 0;
                        interruptible_sleep_on(&bdflush_wait);
                }
index 6d36f11c1b46ca056edd7cbd490d3dc422633c5e..7c574d37290231a7b4f956ac9b8de389edfb748b 100644 (file)
@@ -327,42 +327,6 @@ repeat:
        return result;
 }
 
-static int block_getcluster (struct inode * inode, struct buffer_head * bh,
-                                         int nr,
-                                         int blocksize)
-{
-       u32 * p;
-       int firstblock = 0;
-       int result = 0;
-       int i;
-
-       /* Check to see if clustering possible here. */
-
-       if(!bh) return 0;
-
-       if((nr & ((PAGE_SIZE >> EXT2_BLOCK_SIZE_BITS(inode->i_sb)) - 1)) != 0)
-               goto out;
-       if(nr + 3 > EXT2_ADDR_PER_BLOCK(inode->i_sb)) goto out;
-
-       for(i=0; i< (PAGE_SIZE >> EXT2_BLOCK_SIZE_BITS(inode->i_sb)); i++) {
-         p = (u32 *) bh->b_data + nr + i;
-         
-         /* All blocks in cluster must already be allocated */
-         if(*p == 0) goto out;
-         
-         /* See if aligned correctly */
-         if(i==0) firstblock = *p;
-         else if(*p != firstblock + i) goto out;
-       }
-       
-       p = (u32 *) bh->b_data + nr;
-       result = generate_cluster(bh->b_dev, (int *) p, blocksize);
-
-      out:
-       brelse(bh);
-       return result;
-}
-
 struct buffer_head * ext2_getblk (struct inode * inode, long block,
                                  int create, int * err)
 {
@@ -425,56 +389,6 @@ struct buffer_head * ext2_getblk (struct inode * inode, long block,
                             inode->i_sb->s_blocksize, b, err);
 }
 
-int ext2_getcluster (struct inode * inode, long block)
-{
-       struct buffer_head * bh;
-       int err, create;
-       unsigned long b;
-       unsigned long addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
-       int addr_per_block_bits = EXT2_ADDR_PER_BLOCK_BITS(inode->i_sb);
-
-       create = 0;
-       err = -EIO;
-       if (block < 0) {
-               ext2_warning (inode->i_sb, "ext2_getblk", "block < 0");
-               return 0;
-       }
-       if (block > EXT2_NDIR_BLOCKS + addr_per_block +
-               (1 << (addr_per_block_bits * 2)) +
-               ((1 << (addr_per_block_bits * 2)) << addr_per_block_bits)) {
-               ext2_warning (inode->i_sb, "ext2_getblk", "block > big");
-               return 0;
-       }
-
-       err = -ENOSPC;
-       b = block;
-       if (block < EXT2_NDIR_BLOCKS) return 0;
-
-       block -= EXT2_NDIR_BLOCKS;
-
-       if (block < addr_per_block) {
-               bh = inode_getblk (inode, EXT2_IND_BLOCK, create, b, &err);
-               return block_getcluster (inode, bh, block, 
-                                        inode->i_sb->s_blocksize);
-       }
-       block -= addr_per_block;
-       if (block < (1 << (addr_per_block_bits * 2))) {
-               bh = inode_getblk (inode, EXT2_DIND_BLOCK, create, b, &err);
-               bh = block_getblk (inode, bh, block >> addr_per_block_bits,
-                                  create, inode->i_sb->s_blocksize, b, &err);
-               return block_getcluster (inode, bh, block & (addr_per_block - 1),
-                                    inode->i_sb->s_blocksize);
-       }
-       block -= (1 << (addr_per_block_bits * 2));
-       bh = inode_getblk (inode, EXT2_TIND_BLOCK, create, b, &err);
-       bh = block_getblk (inode, bh, block >> (addr_per_block_bits * 2),
-                          create, inode->i_sb->s_blocksize, b, &err);
-       bh = block_getblk (inode, bh, (block >> addr_per_block_bits) & (addr_per_block - 1),
-                          create, inode->i_sb->s_blocksize, b, &err);
-       return block_getcluster (inode, bh, block & (addr_per_block - 1),
-                                inode->i_sb->s_blocksize);
-}
-
 struct buffer_head * ext2_bread (struct inode * inode, int block, 
                                 int create, int *err)
 {
index 127c3d681b241fe3520ebab27f6f38c63d09d19b..5007f186e0ffa9ec574fd8209818be31e6b082ef 100644 (file)
@@ -173,6 +173,7 @@ void clear_inode(struct inode * inode)
 {
        struct wait_queue * wait;
 
+       inode->i_count++;
        truncate_inode_pages(inode, 0);
        wait_on_inode(inode);
        if (IS_WRITABLE(inode)) {
@@ -182,7 +183,7 @@ void clear_inode(struct inode * inode)
        remove_inode_hash(inode);
        remove_inode_free(inode);
        wait = ((volatile struct inode *) inode)->i_wait;
-       if (inode->i_count)
+       if (--inode->i_count)
                nr_free_inodes++;
        memset(inode,0,sizeof(*inode));
        ((volatile struct inode *) inode)->i_wait = wait;
index cb02d2c4fe734a9bab839bc34d745ee2d7811740..d2714ee56ee37a210b23f99c9e80e32d9148b49d 100644 (file)
@@ -639,7 +639,10 @@ static int do_rmdir(const char * name)
        }
        if (dir->i_sb && dir->i_sb->dq_op)
                dir->i_sb->dq_op->initialize(dir, -1);
-       return dir->i_op->rmdir(dir,basename,namelen);
+       down(&dir->i_sem);
+       error = dir->i_op->rmdir(dir,basename,namelen);
+       up(&dir->i_sem);
+       return error;
 }
 
 asmlinkage int sys_rmdir(const char * pathname)
@@ -690,7 +693,10 @@ static int do_unlink(const char * name)
        }
        if (dir->i_sb && dir->i_sb->dq_op)
                dir->i_sb->dq_op->initialize(dir, -1);
-       return dir->i_op->unlink(dir,basename,namelen);
+       down(&dir->i_sem);
+       error = dir->i_op->unlink(dir,basename,namelen);
+       up(&dir->i_sem);
+       return error;
 }
 
 asmlinkage int sys_unlink(const char * pathname)
index d2f32f94c4a462f3141824b8f85daeef10ae5a34..4c9f8e779fe48aa46ee9b11cb16de22ec77f8dd0 100644 (file)
@@ -633,6 +633,7 @@ static int nfs_link(struct inode *oldinode, struct inode *dir,
                NFS_FH(dir), name);
 
        nfs_lookup_cache_remove(dir, oldinode, NULL);
+       NFS_CACHEINV(oldinode);
        iput(oldinode);
        iput(dir);
        return error;
index 7a538e24081251428f48377e1fc733bef27c0778..be7d3dcd9003c86a44f19912aebee8c6154de131 100644 (file)
@@ -19,7 +19,6 @@ static struct symbol_table procfs_syms = {
        X(proc_unregister),
        X(proc_root),
        X(in_group_p),
-       X(generate_cluster),
        X(proc_net_inode_operations),
        X(proc_net),
 
index 5123f5b7d9922491922aded4ab7ca8f2269abdc0..fec92faecf68ac53892ba74ece7929fe1b217473 100644 (file)
@@ -553,6 +553,7 @@ kdev_t get_unnamed_dev(void)
                if (!set_bit(i,unnamed_dev_in_use))
                        return MKDEV(UNNAMED_MAJOR, i);
        }
+       printk("VFS: Sorry, out of unnamed devices\n");
        return 0;
 }
 
@@ -910,6 +911,8 @@ asmlinkage int sys_mount(char * dev_name, char * dir_name, char * type,
        }
        retval = do_mount(dev,dev_name,dir_name,t,flags,(void *) page);
        free_page(page);
+       if (retval && !fstype->requires_dev) 
+               put_unnamed_dev(dev);
        if (retval && fops && fops->release)
                fops->release(inode, NULL);
        iput(inode);
index 210c2a5b3501dbf69403fb5d517b6b787e4e0ccb..042a96966e23c36ac2636e623fb62e7bc4e1165f 100644 (file)
@@ -8,17 +8,20 @@
  */
 
 #include <asm/atomic.h>
+#include <asm/system.h>
 
 struct semaphore {
        atomic_t count;
-       atomic_t waiting;
+       atomic_t waking;
+       int lock;                       /* to make waking testing atomic */
        struct wait_queue * wait;
 };
 
-#define MUTEX ((struct semaphore) { 1, 0, NULL })
-#define MUTEX_LOCKED ((struct semaphore) { 0, 0, NULL })
+#define MUTEX ((struct semaphore) { 1, 0, 0, NULL })
+#define MUTEX_LOCKED ((struct semaphore) { 0, 0, 0, NULL })
 
 extern void __down(struct semaphore * sem);
+extern int  __down_interruptible(struct semaphore * sem);
 extern void __up(struct semaphore * sem);
 
 /*
@@ -27,11 +30,33 @@ extern void __up(struct semaphore * sem);
  */
 extern inline void down(struct semaphore * sem)
 {
-       for (;;) {
-               if (atomic_dec_return(&sem->count) >= 0)
-                       break;
+       if (atomic_dec_return(&sem->count) < 0)
                __down(sem);
-       }
+}
+
+/*
+ * Primitives to spin on a lock.  Needed only for SMP version.
+ */
+extern inline void get_buzz_lock(int *lock_ptr)
+{
+#ifdef __SMP__
+        while (xchg(lock_ptr,1) != 0) ;
+#endif
+} /* get_buzz_lock */
+
+extern inline void give_buzz_lock(int *lock_ptr)
+{
+#ifdef __SMP__
+        *lock_ptr = 0 ;
+#endif
+} /* give_buzz_lock */
+
+extern inline int down_interruptible(struct semaphore * sem)
+{
+       int ret = 0;
+       if (atomic_dec_return(&sem->count) < 0)
+               ret = __down_interruptible(sem);
+       return ret;
 }
 
 extern inline void up(struct semaphore * sem)
index 03f74548188d673d57facd431b31ba165a7c6aa8..46c4b0ab4ccc3af2aabb51ac88c70ce144f41974 100644 (file)
@@ -214,7 +214,7 @@ type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4)           \
 type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5)      \
 {                                                                       \
        extern long syscall (int, ...);                                  \
-       return syscall(__NR_##name, arg1, arg2, arg3, arg4);             \
+       return syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5);       \
 }
 
 #endif /* __LIBRARY__ && __GNUC__ */
index 40ebe5fc470cdac22682c2b794b18a937fbcd3b4..1486d1c1920252894e93cc1b37c92eb7639013d8 100644 (file)
@@ -2,21 +2,33 @@
 #define _I386_SEMAPHORE_H
 
 #include <linux/linkage.h>
+#include <asm/system.h>
 
 /*
  * SMP- and interrupt-safe semaphores..
  *
  * (C) Copyright 1996 Linus Torvalds
+ *
+ * Modified 1996-12-23 by Dave Grothe <dave@gcom.com> to fix bugs in
+ *                     the original code and to make semaphore waits
+ *                     interruptible so that processes waiting on
+ *                     semaphores can be killed.
+ *
+ * If you would like to see an analysis of this implementation, please
+ * ftp to gcom.com and download the file
+ * /pub/linux/src/semaphore/semaphore-2.0.24.tar.gz.
+ *
  */
 
 struct semaphore {
        int count;
-       int waiting;
+       int waking;
+       int lock ;                      /* to make waking testing atomic */
        struct wait_queue * wait;
 };
 
-#define MUTEX ((struct semaphore) { 1, 0, NULL })
-#define MUTEX_LOCKED ((struct semaphore) { 0, 0, NULL })
+#define MUTEX ((struct semaphore) { 1, 0, 0, NULL })
+#define MUTEX_LOCKED ((struct semaphore) { 0, 0, 0, NULL })
 
 asmlinkage void down_failed(void /* special register calling convention */);
 asmlinkage void up_wakeup(void /* special register calling convention */);
@@ -32,19 +44,64 @@ extern void __up(struct semaphore * sem);
 extern inline void down(struct semaphore * sem)
 {
        __asm__ __volatile__(
-               "# atomic down operation\n"
-               "1:\n\t"
-               "movl $1b,%%eax\n\t"
+               "# atomic down operation\n\t"
+               "movl $1f,%%eax\n\t"
 #ifdef __SMP__
                "lock ; "
 #endif
                "decl 0(%0)\n\t"
-               "js " SYMBOL_NAME_STR(down_failed)
+               "js " SYMBOL_NAME_STR(down_failed) "\n"
+               "1:\n"
                :/* no outputs */
                :"c" (sem)
                :"ax","dx","memory");
 }
 
+/*
+ * Primitives to spin on a lock.  Needed only for SMP version.
+ */
+extern inline void get_buzz_lock(int *lock_ptr)
+{
+#ifdef __SMP__
+        while (xchg(lock_ptr,1) != 0) ;
+#endif
+} /* get_buzz_lock */
+
+extern inline void give_buzz_lock(int *lock_ptr)
+{
+#ifdef __SMP__
+        *lock_ptr = 0 ;
+#endif
+} /* give_buzz_lock */
+
+asmlinkage int down_failed_interruptible(void);  /* params in registers */
+
+/*
+ * This version waits in interruptible state so that the waiting
+ * process can be killed.  The down_failed_interruptible routine
+ * returns negative for signalled and zero for semaphore acquired.
+ */
+extern inline int down_interruptible(struct semaphore * sem)
+{
+       int     ret ;
+
+        __asm__ __volatile__(
+                "# atomic interruptible down operation\n\t"
+                "movl $2f,%%eax\n\t"
+#ifdef __SMP__
+                "lock ; "
+#endif
+                "decl 0(%1)\n\t"
+                "js " SYMBOL_NAME_STR(down_failed_interruptible) "\n\t"
+                "xorl %%eax,%%eax\n"
+                "2:\n"
+                :"=a" (ret)
+                :"c" (sem)
+                :"ax","dx","memory");
+
+       return(ret) ;
+}
+
 /*
  * Note! This is subtle. We jump to wake people up only if
  * the semaphore was negative (== somebody was waiting on it).
index 992bfca60bb6a3bf13269cbd74b272cbd6d16d0a..1a337ba74123f6d5aeb11c3c6d89ee77a90bb4bb 100644 (file)
@@ -23,11 +23,11 @@ register char *tmp= (char *)dest;
 register char dummy;
 __asm__ __volatile__(
        "\n1:\t"
-       "movb (%0),%2\n\t"
+       "movb (%0),%b2\n\t"
        "incl %0\n\t"
-       "movb %2,(%1)\n\t"
+       "movb %b2,(%1)\n\t"
        "incl %1\n\t"
-       "testb %2,%2\n\t"
+       "testb %b2,%b2\n\t"
        "jne 1b"
        :"=r" (src), "=r" (tmp), "=q" (dummy)
        :"0" (src), "1" (tmp)
@@ -43,15 +43,15 @@ register char dummy;
 if (count) {
 __asm__ __volatile__(
        "\n1:\t"
-       "movb (%0),%2\n\t"
+       "movb (%0),%b2\n\t"
        "incl %0\n\t"
-       "movb %2,(%1)\n\t"
+       "movb %b2,(%1)\n\t"
        "incl %1\n\t"
        "decl %3\n\t"
        "je 3f\n\t"
-       "testb %2,%2\n\t"
+       "testb %b2,%b2\n\t"
        "jne 1b\n\t"
-       "2:\tmovb %2,(%1)\n\t"
+       "2:\tmovb %b2,(%1)\n\t"
        "incl %1\n\t"
        "decl %3\n\t"
        "jne 2b\n\t"
@@ -101,7 +101,7 @@ __asm__ __volatile__(
        "incl %1\n\t"
        "testb %b0,%b0\n\t"
        "jne 2b\n"
-       "3:\txorl %0,%0\n\t"
+       "3:\txorb %b0,%b0\n\t"
        "movb %b0,(%1)\n\t"
        :"=q" (dummy), "=r" (tmp), "=r" (src), "=r" (count)
        :"1"  (tmp), "2"  (src), "3"  (count)
@@ -121,11 +121,11 @@ __asm__ __volatile__(
        "incl %2\n\t"
        "testb %b0,%b0\n\t"
        "jne 1b\n\t"
-       "xorl %0,%0\n\t"
+       "xorl %k0,%k0\n\t"
        "jmp 3f\n"
-       "2:\tmovl $1,%0\n\t"
+       "2:\tmovl $1,%k0\n\t"
        "jb 3f\n\t"
-       "negl %0\n"
+       "negl %k0\n"
        "3:"
        :"=q" (__res), "=r" (cs), "=r" (ct)
        :"1" (cs), "2" (ct)
@@ -147,11 +147,11 @@ __asm__ __volatile__(
        "incl %2\n\t"
        "testb %b0,%b0\n\t"
        "jne 1b\n"
-       "2:\txorl %0,%0\n\t"
+       "2:\txorl %k0,%k0\n\t"
        "jmp 4f\n"
-       "3:\tmovl $1,%0\n\t"
+       "3:\tmovl $1,%k0\n\t"
        "jb 4f\n\t"
-       "negl %0\n"
+       "negl %k0\n"
        "4:"
        :"=q" (__res), "=r" (cs), "=r" (ct), "=r" (count)
        :"1"  (cs), "2"  (ct),  "3" (count));
@@ -339,7 +339,9 @@ __asm__ __volatile__(
        "cmpl $-1,%2\n\t"
        "jne 1b\n"
        "3:\tsubl %1,%0"
-       :"=a" (__res):"c" (s),"d" (count));
+       :"=a" (__res)
+       :"c" (s),"d" (count)
+       :"dx");
 return __res;
 }
 /* end of additional stuff */
@@ -414,9 +416,11 @@ return __res;
 
 #define __HAVE_ARCH_MEMCPY
 #define memcpy(d,s,count) \
-(__builtin_constant_p(count) ? \
- __memcpy_c((d),(s),(count)) : \
- __memcpy_g((d),(s),(count)))
+(count == 0 \
+ ? d \
+ : __builtin_constant_p(count) \
+   ? __memcpy_c((d),(s),(count)) \
+   : __memcpy_g((d),(s),(count)))
 
 /*
  *     These ought to get tweaked to do some cache priming.
@@ -436,7 +440,7 @@ __asm__ __volatile__ (
        :"=r" (dummy1), "=r" (tmp), "=r" (from), "=r" (dummy2) 
        :"1" (tmp), "2" (from), "3" (n/4)
        :"memory");
-return (to);
+return to;
 }
 
 extern inline void * __memcpy_by2(void * to, const void * from, size_t n)
@@ -457,7 +461,7 @@ __asm__ __volatile__ (
        :"=r" (dummy1), "=r" (tmp), "=r" (from), "=r" (dummy2) 
        :"1" (tmp), "2" (from), "3" (n/2)
        :"memory");
-return (to);
+return to;
 }
 
 extern inline void * __memcpy_g(void * to, const void * from, size_t n)
@@ -476,7 +480,7 @@ __asm__ __volatile__ (
        : /* no output */
        :"c" (n),"D" ((long) tmp),"S" ((long) from)
        :"cx","di","si","memory");
-return (to);
+return to;
 }
 
 
@@ -497,6 +501,7 @@ __asm__ __volatile__ (
        "std\n\t"
        "rep\n\t"
        "movsb\n\t"
+       "cld\n\t"
        : /* no output */
        :"c" (n), "S" (n-1+(const char *)src), "D" (n-1+(char *)tmp)
        :"cx","si","di","memory");
@@ -553,13 +558,15 @@ return __res;
 
 #define __HAVE_ARCH_MEMSET
 #define memset(s,c,count) \
-(__builtin_constant_p(c) ? \
- (__builtin_constant_p(count) ? \
-  __memset_cc((s),(c),(count)) : \
-  __memset_cg((s),(c),(count))) : \
- (__builtin_constant_p(count) ? \
-  __memset_gc((s),(c),(count)) : \
-  __memset_gg((s),(c),(count))))
+(count == 0 \
+ ? s \
+ : __builtin_constant_p(c) \
+   ? __builtin_constant_p(count) \
+     ? __memset_cc((s),(c),(count)) \
+     : __memset_cg((s),(c),(count)) \
+   : __builtin_constant_p(count) \
+     ? __memset_gc((s),(c),(count)) \
+     : __memset_gg((s),(c),(count)))
 
 extern inline void * __memset_cc_by4(void * s, char c, size_t count)
 {
@@ -574,7 +581,7 @@ __asm__ __volatile__ (
        "decl %1\n\t"
        "jnz 1b"
        :"=r" (tmp), "=r" (dummy)
-       :"q" (0x01010101UL * (unsigned char) c), "0" (tmp), "1" (count/4)
+       :"r" (0x01010101UL * (unsigned char) c), "0" (tmp), "1" (count/4)
        :"memory");
 return s;
 }
@@ -592,7 +599,7 @@ __asm__ __volatile__ (
        "jnz 1b\n"
        "2:\tmovw %w2,(%0)"
        :"=r" (tmp), "=r" (dummy)
-       :"q" (0x01010101UL * (unsigned char) c), "0" (tmp), "1" (count/2)
+       :"r" (0x01010101UL * (unsigned char) c), "0" (tmp), "1" (count/2)
        :"memory");
 return s;
 }
@@ -604,9 +611,9 @@ register int dummy;
 __asm__ __volatile__ (
        "movb %b0,%h0\n"
        "pushw %w0\n\t"
-       "shll $16,%0\n\t"
+       "shll $16,%k0\n\t"
        "popw %w0\n"
-       "1:\tmovl %0,(%1)\n\t"
+       "1:\tmovl %k0,(%1)\n\t"
        "addl $4,%1\n\t"
        "decl %2\n\t"
        "jnz 1b\n"
@@ -625,9 +632,9 @@ __asm__ __volatile__ (
        "shrl $1,%2\n\t"          /* may be divisible also by 4 */
        "jz 2f\n\t"
        "pushw %w0\n\t"
-       "shll $16,%0\n\t"
+       "shll $16,%k0\n\t"
        "popw %w0\n"
-       "1:\tmovl %0,(%1)\n\t"
+       "1:\tmovl %k0,(%1)\n\t"
        "addl $4,%1\n\t"
        "decl %2\n\t"
        "jnz 1b\n"
@@ -643,6 +650,7 @@ extern inline void * __memset_cg(void * s, char c, size_t count)
 register void *tmp = (void *)s;
 __asm__ __volatile__ (
        "shrl $1,%%ecx\n\t"
+       "cld\n\t"
        "rep\n\t"
        "stosw\n\t"
        "jnc 1f\n\t"
@@ -660,6 +668,7 @@ register void *tmp = (void *)s;
 __asm__ __volatile__ (
        "movb %%al,%%ah\n\t"
        "shrl $1,%%ecx\n\t"
+       "cld\n\t"
        "rep\n\t"
        "stosw\n\t"
        "jnc 1f\n\t"
index a7611c735ebb780ee68243a7d88f79367ebc9c42..19293539713fffac803cf16a66477a4e9bc3c92d 100644 (file)
@@ -546,7 +546,9 @@ __asm__ __volatile__(
        "cmpl $-1,%2\n\t"
        "jne 1b\n"
        "3:\tsubl %1,%0"
-       :"=a" (__res):"c" (s),"d" (count));
+       :"=a" (__res)
+       :"c" (s),"d" (count)
+       :"dx");
 return __res;
 }
 /* end of additional stuff */
index c70d0577b698ffb73f35df42f379fcfef29c9b31..98daa117a5fade05f0d0aa2c54fd44591aa1ac0f 100644 (file)
@@ -72,11 +72,15 @@ struct atalk_sock
 
 #ifdef __KERNEL__
 
+#include <asm/byteorder.h>
+
 struct ddpehdr
 {
-       /* FIXME for bigendians */
-       /*__u16 deh_pad:2,deh_hops:4,deh_len:10;*/
-       __u16   deh_len:10,deh_hops:4,deh_pad:2;
+#ifdef __LITTLE_ENDIAN_BITFIELD
+       __u16   deh_len:10, deh_hops:4, deh_pad:2;
+#else
+       __u16   deh_pad:2, deh_hops:4, deh_len:10;
+#endif
        __u16   deh_sum;
        __u16   deh_dnet;
        __u16   deh_snet;
@@ -93,8 +97,11 @@ struct ddpehdr
  
 struct ddpshdr
 {
-       /* FIXME for bigendians */
+#ifdef __LITTLE_ENDIAN_BITFIELD
        __u16   dsh_len:10, dsh_pad:6;
+#else
+       __u16   dsh_pad:6, dsh_len:10;
+#endif
        __u8    dsh_dport;
        __u8    dsh_sport;
        /* And netatalk apps expect to stick the type in themselves */
index 4eb1df02b89aed0155dfbb002c92e03a0dd7691c..4c5a25b69b7032d47b86c16b6a0b03e2c0205d87 100644 (file)
@@ -382,15 +382,19 @@ static void end_request(int uptodate) {
        struct request *req = CURRENT;
 #endif /* IDE_DRIVER */
        struct buffer_head * bh;
+       int nsect;
 
        req->errors = 0;
        if (!uptodate) {
                printk("end_request: I/O error, dev %s, sector %lu\n",
                        kdevname(req->rq_dev), req->sector);
-               req->nr_sectors--;
-               req->nr_sectors &= ~SECTOR_MASK;
-               req->sector += (BLOCK_SIZE / 512);
-               req->sector &= ~SECTOR_MASK;            
+               if ((bh = req->bh) != NULL) {
+                       nsect = bh->b_size >> 9;
+                       req->nr_sectors--;
+                       req->nr_sectors &= ~(nsect - 1);
+                       req->sector += nsect;
+                       req->sector &= ~(nsect - 1);
+               }
        }
 
        if ((bh = req->bh) != NULL) {
index bd87ebcf563f96718cc99df1343379b495e33a82..15f8cfb0a20f448281950c62a90b3e922c77f96a 100644 (file)
@@ -296,6 +296,7 @@ struct ext2_inode {
 #define EXT2_MOUNT_ERRORS_RO           0x0020  /* Remount fs ro on errors */
 #define EXT2_MOUNT_ERRORS_PANIC                0x0040  /* Panic on errors */
 #define EXT2_MOUNT_MINIX_DF            0x0080  /* Mimics the Minix statfs */
+#define EXT2_MOUNT_NO_ATIME            0x0100  /* Don't update the atime */
 
 #define clear_opt(o, opt)              o &= ~EXT2_MOUNT_##opt
 #define set_opt(o, opt)                        o |= EXT2_MOUNT_##opt
index 7b19fba30ffc105d59ee4975806e867016de4d85..34eb63c073567c43be922e3a2c14beddd9576eb7 100644 (file)
@@ -564,20 +564,17 @@ extern struct super_block super_blocks[NR_SUPER];
 
 extern void refile_buffer(struct buffer_head * buf);
 extern void set_writetime(struct buffer_head * buf, int flag);
-extern void refill_freelist(int size);
 extern int try_to_free_buffer(struct buffer_head*, struct buffer_head**, int);
 
 extern int nr_buffers;
 extern int buffermem;
 extern int nr_buffer_heads;
 
-#define BUF_CLEAN 0
-#define BUF_UNSHARED 1 /* Buffers that were shared but are not any more */
-#define BUF_LOCKED 2   /* Buffers scheduled for write */
-#define BUF_LOCKED1 3  /* Supers, inodes */
-#define BUF_DIRTY 4    /* Dirty buffers, not yet scheduled for write */
-#define BUF_SHARED 5   /* Buffers shared */
-#define NR_LIST 6
+#define BUF_CLEAN      0
+#define BUF_LOCKED     1       /* Buffers scheduled for write */
+#define BUF_LOCKED1    2       /* Supers, inodes */
+#define BUF_DIRTY      3       /* Dirty buffers, not yet scheduled for write */
+#define NR_LIST                4
 
 void mark_buffer_uptodate(struct buffer_head * bh, int on);
 
index 3a33555f7c93540e7aeb8434e623253c5fb2c7ff..9ac441707cac940efbb6cf9da3ba7c45d72e9443 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/ip.h>
 #include <linux/tcp.h>
 #include <linux/udp.h>
+#include <linux/config.h>
 
 struct ip_fw 
 {
@@ -127,6 +128,9 @@ struct ip_fw
 #define IP_FW_OUT              2
 #define IP_FW_ACCT             3
 #define IP_FW_CHAINS           4       /* total number of ip_fw chains */
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+#define IP_FW_AUTOFW           5
+#endif
 
 #define IP_FW_INSERT           (IP_FW_BASE_CTL)
 #define IP_FW_APPEND           (IP_FW_BASE_CTL+1)
@@ -167,6 +171,12 @@ struct ip_fw
 #define IP_ACCT_FLUSH          (IP_FW_FLUSH  | (IP_FW_ACCT << IP_FW_SHIFT))
 #define IP_ACCT_ZERO           (IP_FW_ZERO   | (IP_FW_ACCT << IP_FW_SHIFT))
 
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+#define IP_AUTOFW_ADD          (IP_FW_APPEND | (IP_FW_AUTOFW << IP_FW_SHIFT))
+#define IP_AUTOFW_DEL          (IP_FW_DELETE | (IP_FW_AUTOFW << IP_FW_SHIFT))
+#define IP_AUTOFW_FLUSH        (IP_FW_FLUSH  | (IP_FW_AUTOFW << IP_FW_SHIFT))
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+
 struct ip_fwpkt
 {
        struct iphdr fwp_iph;                   /* IP header */
@@ -197,7 +207,6 @@ struct ip_fw_masq;
 #define IP_FW_MODE_ACCT_OUT    0x02    /* accounting (outgoing) */
 #define IP_FW_MODE_CHK         0x04    /* check requested by user */
 
-#include <linux/config.h>
 #ifdef CONFIG_IP_FIREWALL
 extern struct ip_fw *ip_fw_in_chain;
 extern struct ip_fw *ip_fw_out_chain;
@@ -207,6 +216,9 @@ extern int ip_fw_out_policy;
 extern int ip_fw_fwd_policy;
 extern int ip_fw_ctl(int, void *, int);
 #endif
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+extern int ip_autofw_ctl(int, void *, int);
+#endif
 #ifdef CONFIG_IP_ACCT
 extern struct ip_fw *ip_acct_chain;
 extern int ip_acct_ctl(int, void *, int);
@@ -216,4 +228,29 @@ extern int ip_fw_chk(struct iphdr *, struct device *, __u16 *, struct ip_fw *, i
 extern void ip_fw_init(void);
 #endif /* KERNEL */
 
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+#define IP_FWD_RANGE           1
+#define IP_FWD_PORT            2
+#define IP_FWD_DIRECT          3
+
+#define IP_AUTOFW_ACTIVE       1
+#define IP_AUTOFW_USETIME      2
+#define IP_AUTOFW_SECURE       4
+
+struct ip_autofw {
+       struct ip_autofw * next;
+       __u16 type;
+       __u16 low;
+       __u16 hidden;
+       __u16 high;
+       __u16 visible;
+       __u16 protocol;
+       __u32 lastcontact;
+       __u32 where;
+       __u16 ctlproto;
+       __u16 ctlport;
+       __u16 flags;
+       struct timer_list timer;
+};
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
 #endif /* _IP_FW_H */
index 64b89aa251305215e4ee851913e74fa0569f9620..8bc1e2e115af32aa5ca7497088383253dec6f95b 100644 (file)
@@ -193,7 +193,9 @@ struct device
   void                   (*header_cache_bind)(struct hh_cache **hhp, struct device *dev, unsigned short htype, __u32 daddr);
   void                   (*header_cache_update)(struct hh_cache *hh, struct device *dev, unsigned char *  haddr);
 #define HAVE_CHANGE_MTU
-  int                    (*change_mtu)(struct device *dev, int new_mtu);            
+  int                    (*change_mtu)(struct device *dev, int new_mtu);
+
+  struct iw_statistics*          (*get_wireless_stats)(struct device *dev);
 };
 
 
index 60f52076df892b7455ab0d668f9cbf4ecfc291ec..6c4e19dfb2ec3b1868d9549db9bf54eab47d4117 100644 (file)
 #define PCI_VENDOR_ID_MATROX           0x102B
 #define PCI_DEVICE_ID_MATROX_MGA_2     0x0518
 #define PCI_DEVICE_ID_MATROX_MIL       0x0519
+#define PCI_DEVICE_ID_MATROX_MYS       0x051A
 #define PCI_DEVICE_ID_MATROX_MGA_IMP   0x0d10
 
 #define PCI_VENDOR_ID_CT               0x102c
index 4a0f7322cfe7e5b685d2d666cd0afc9e99521d78..fb017b7d1e80b76c9df7d66648479ea6bdc390ca 100644 (file)
@@ -83,7 +83,7 @@ enum net_directory_inos {
        PROC_NET_IPFWOUT,
        PROC_NET_IPACCT,
        PROC_NET_IPMSQHST,
-       PROC_NET_WAVELAN,
+       PROC_NET_WIRELESS,
        PROC_NET_IPX_INTERFACE,
        PROC_NET_IPX_ROUTE,
        PROC_NET_IPX,
@@ -104,6 +104,7 @@ enum net_directory_inos {
        PROC_NET_IP_MASQ_APP,
        PROC_NET_STRIP_STATUS,
        PROC_NET_STRIP_TRACE,
+       PROC_NET_IPAUTOFW,
        PROC_NET_LAST
 };
 
index b2706ce031b7a98f9f353831f17c79468bf5dc70..b47367ac32063f6a6e3d4767ba6ff935435d53c3 100644 (file)
@@ -55,6 +55,11 @@ extern void get_random_bytes(void *buf, int nbytes);
 
 extern __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr,
                                        __u16 sport, __u16 dport);
+__u32 secure_tcp_probe_number(__u32 saddr, __u32 daddr,
+                 __u16 sport, __u16 dport, __u32 sseq, int validate);
+
+__u32 secure_tcp_syn_cookie(__u32 saddr, __u32 daddr,
+                 __u16 sport, __u16 dport, __u32 sseq, __u32 count);
 
 #ifndef MODULE
 extern struct file_operations random_fops, urandom_fops;
index d63a9046d9efdd5153994a0523fdb3e9ac238ca0..9a0fa860a26a94c56cd4972eb5416e3776975ab3 100644 (file)
@@ -60,7 +60,9 @@ struct msghdr
 #define AF_BRIDGE      7       /* Multiprotocol bridge         */
 #define AF_AAL5                8       /* Reserved for Werner's ATM    */
 #define AF_X25         9       /* Reserved for X.25 project    */
+#ifdef LINUX_2_1_X
 #define AF_INET6       10      /* IP version 6                 */
+#endif
 #define AF_MAX         12      /* For now.. */
 
 /* Protocol families, same as address families. */
@@ -74,8 +76,9 @@ struct msghdr
 #define PF_BRIDGE      AF_BRIDGE
 #define PF_AAL5                AF_AAL5
 #define PF_X25         AF_X25
+#ifdef LINUX_2_1_X
 #define PF_INET6       AF_INET6
-
+#endif
 #define PF_MAX         AF_MAX
 
 /* Maximum queue length specifiable by listen.  */
index 70b8093c08bc29f9d6c416f626978d34a23c0650..6629027a6ad5a2822ec71eb169068fb6faa27ef0 100644 (file)
@@ -97,6 +97,7 @@ struct __sysctl_args {
 #define NET_IPV4_ARP_CHECK_INTERVAL     5
 #define NET_IPV4_ARP_CONFIRM_INTERVAL   6
 #define NET_IPV4_ARP_CONFIRM_TIMEOUT   7
+#define NET_IPV4_FORWARD                8
 
 /* /proc/sys/net/ipx */
 
diff --git a/include/linux/wireless.h b/include/linux/wireless.h
new file mode 100644 (file)
index 0000000..0bc29b7
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * This file define a set of standard wireless extensions
+ *
+ * Version :   4       12.2.97
+ *
+ * Authors :   Jean Tourrilhes - HPLB - <jt@hplb.hpl.hp.com>
+ */
+
+#ifndef _LINUX_WIRELESS_H
+#define _LINUX_WIRELESS_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * Basically, the wireless extensions are for now a set of standard ioctl
+ * call + /proc/net/wireless
+ *
+ * The entry /proc/net/wireless give statistics and information on the
+ * driver.
+ * This is better than having each driver having its entry because
+ * its centralised and we may remove the driver module safely.
+ *
+ * Ioctl are used to configure the driver and issue commands.  This is
+ * better than command line options of insmod because we may want to
+ * change dynamically (while the driver is running) some parameters.
+ *
+ * The ioctl mechanimsm are copied from standard devices ioctl.
+ * We have the list of command plus a structure descibing the
+ * data exchanged...
+ * Note that to add these ioctl, I was obliged to modify :
+ *     net/core/dev.c (two place + add include)
+ *     net/ipv4/af_inet.c (one place + add include)
+ *
+ * /proc/net/wireless is a copy of /proc/net/dev.
+ * We have a structure for data passed from the driver to /proc/net/wireless
+ * Too add this, I've modified :
+ *     net/core/dev.c (two other places)
+ *     include/linux/netdevice.h (one place)
+ *     include/linux/proc_fs.h (one place)
+ *
+ * Do not add here things that are redundant with other mechanisms
+ * (drivers init, ifconfig, /proc/net/dev, ...) and with are not
+ * wireless specific.
+ *
+ * These wireless extensions are not magic : each driver has to provide
+ * support for them...
+ *
+ * IMPORTANT NOTE : As everything in the kernel, this is very much a
+ * work in progress. Contact me if you have ideas of improvements...
+ */
+
+/***************************** INCLUDES *****************************/
+
+#include <linux/types.h>               /* for "caddr_t" et al          */
+#include <linux/socket.h>              /* for "struct sockaddr" et al  */
+#include <linux/if.h>                  /* for IFNAMSIZ and co... */
+
+/**************************** CONSTANTS ****************************/
+
+/* --------------------------- VERSION --------------------------- */
+/*
+ * This constant is used to know the availability of the wireless
+ * extensions and to know which version of wireless extensions it is
+ * (there is some stuff that will be added in the future...)
+ * I just plan to increment with each new version.
+ */
+#define WIRELESS_EXT   4
+
+/*
+ * Changes :
+ *
+ * V2 to V3
+ * --------
+ *     Alan Cox start some imcompatibles changes. I've integrated a bit more.
+ *     - Encryption renamed to Encode to avoid US regulation problems
+ *     - Frequency changed from float to struct to avoid problems on old 386
+ *
+ * V3 to V4
+ * --------
+ *     - Add sensitivity
+ */
+
+/* -------------------------- IOCTL LIST -------------------------- */
+
+/* Basic operations */
+#define SIOCSIWNAME    0x8B00          /* Unused ??? */
+#define SIOCGIWNAME    0x8B01          /* get name */
+#define SIOCSIWNWID    0x8B02          /* set network id */
+#define SIOCGIWNWID    0x8B03          /* get network id */
+#define SIOCSIWFREQ    0x8B04          /* set channel/frequency */
+#define SIOCGIWFREQ    0x8B05          /* get channel/frequency */
+#define SIOCSIWENCODE  0x8B06          /* set encoding info */
+#define SIOCGIWENCODE  0x8B07          /* get encoding info */
+#define SIOCSIWSENS    0x8B08          /* set sensitivity */
+#define SIOCGIWSENS    0x8B09          /* get sensitivity */
+
+/* Informative stuff */
+#define SIOCSIWRANGE   0x8B0A          /* Unused ??? */
+#define SIOCGIWRANGE   0x8B0B          /* Get range of parameters */
+#define SIOCSIWPRIV    0x8B0C          /* Unused ??? */
+#define SIOCGIWPRIV    0x8B0D          /* get private ioctl interface info */
+
+/* Mobile IP support */
+#define SIOCSIWSPY     0x8B10          /* set spy addresses */
+#define SIOCGIWSPY     0x8B11          /* get spy info (quality of link) */
+
+/* ------------------------- IOCTL STUFF ------------------------- */
+
+/* The first and the last (range) */
+#define SIOCIWFIRST    0x8B00
+#define SIOCIWLAST     0x8B13
+
+/* Even : get (world access), odd : set (root access) */
+#define IW_IS_SET(cmd) (!((cmd) & 0x1))
+#define IW_IS_GET(cmd) ((cmd) & 0x1)
+
+/* ------------------------- PRIVATE INFO ------------------------- */
+/*
+ * The following is used with SIOCGIWPRIV. It allow a driver to define
+ * the interface (name, type of data) for its private ioctl.
+ * Privates ioctl are SIOCDEVPRIVATE -> SIOCDEVPRIVATE + 0xF
+ */
+
+#define IW_PRIV_TYPE_MASK      0x7000  /* Type of arguments */
+#define IW_PRIV_TYPE_NONE      0x0000
+#define IW_PRIV_TYPE_BYTE      0x1000  /* Char as number */
+#define IW_PRIV_TYPE_CHAR      0x2000  /* Char as character */
+#define IW_PRIV_TYPE_INT       0x4000  /* 32 bits int */
+#define IW_PRIV_TYPE_FLOAT     0x5000
+
+#define IW_PRIV_SIZE_FIXED     0x0800  /* Variable or fixed nuber of args */
+
+#define IW_PRIV_SIZE_MASK      0x07FF  /* Max number of those args */
+
+/*
+ * Note : if the number of args is fixed and the size < 16 octets,
+ * instead of passing a pointer we will put args in the iwreq struct...
+ */
+
+/* ----------------------- OTHER CONSTANTS ----------------------- */
+
+/* Maximum frequencies in the range struct */
+#define IW_MAX_FREQUENCIES     16
+/* Note : if you have something like 80 frequencies,
+ * don't increase this constant and don't fill the frequency list.
+ * The user will be able to set by channel anyway... */
+
+/* Maximum of address that you may set with SPY */
+#define IW_MAX_SPY             8
+
+/****************************** TYPES ******************************/
+
+/* --------------------------- SUBTYPES --------------------------- */
+/*
+ *     A frequency
+ *     For numbers lower than 10^9, we encode the number in 'mant' and
+ *     set 'exp' to 0
+ *     For number greater than 10^9, we divide it by a power of 10.
+ *     The power of 10 is in 'exp', the result is in 'mant'.
+ */
+struct iw_freq
+{
+       __u32           m;              /* Mantissa */
+       __u16           e;              /* Exponent */
+};
+
+/*
+ *     Quality of the link
+ */
+struct iw_quality
+{
+       __u8            qual;           /* link quality (SNR or better...) */
+       __u8            level;          /* signal level */
+       __u8            noise;          /* noise level */
+       __u8            updated;        /* Flags to know if updated */
+};
+
+/*
+ *     Packet discarded in the wireless adapter due to
+ *     "wireless" specific problems...
+ */
+struct iw_discarded
+{
+       __u32           nwid;           /* Wrong nwid */
+       __u32           code;           /* Unable to code/decode */
+       __u32           misc;           /* Others cases */
+};
+
+/* ------------------------ WIRELESS STATS ------------------------ */
+/*
+ * Wireless statistics (used for /proc/net/wireless)
+ */
+struct iw_statistics
+{
+       __u8            status;         /* Status
+                                        * - device dependant for now */
+
+       struct iw_quality       qual;           /* Quality of the link
+                                                * (instant/mean/max) */
+       struct iw_discarded     discard;        /* Packet discarded counts */
+};
+
+/* ------------------------ IOCTL REQUEST ------------------------ */
+/*
+ * The structure to exchange data for ioctl.
+ * This structure is the same as 'struct ifreq', but (re)defined for
+ * convenience...
+ *
+ * Note that it should fit on the same memory footprint !
+ * You should check this when increasing the above structures (16 octets)
+ * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
+ */
+struct iwreq 
+{
+       union
+       {
+               char    ifrn_name[IFNAMSIZ];    /* if name, e.g. "en0" */
+       } ifr_ifrn;
+
+       /* Data part */
+       union
+       {
+               /* Config - generic */
+               char    name[IFNAMSIZ];
+               /* Name : used to verify the presence of  wireless extensions.
+                * Name of the protocol/provider... */
+
+               struct          /* network id (or domain) : used to to */
+               {               /* create logical channels on the air */
+                       __u32   nwid;           /* value */
+                       __u8    on;             /* active/unactive nwid */
+               }       nwid;
+
+               struct iw_freq  freq;   /* frequency or channel :
+                                        * 0-1000 = channel
+                                        * > 1000 = frequency in Hz */
+
+               struct          /* Encoding stuff */
+               {
+                       __u8    method;         /* Algorithm number / off */
+                       __u64   code;           /* Data used for algorithm */
+               }       encoding;
+
+               __u32   sensitivity;    /* signal level threshold */
+
+               struct          /* For all data bigger than 16 octets */
+               {
+                       caddr_t pointer;        /* Pointer to the data
+                                                * (in user space) */
+                       __u16   length;         /* fields or byte size */
+                       __u16   flags;          /* Unused */
+               }       data;
+       }       u;
+};
+
+/* -------------------------- IOCTL DATA -------------------------- */
+/*
+ *     For those ioctl which want to exchange mode data that what could
+ *     fit in the above structure...
+ */
+
+/*
+ *     Range of parameters
+ */
+
+struct iw_range
+{
+       /* Informative stuff (to choose between different interface) */
+       __u32           throughput;     /* To give an idea... */
+
+       /* NWID (or domain id) */
+       __u32           min_nwid;       /* Minimal NWID we are able to set */
+       __u32           max_nwid;       /* Maximal NWID we are able to set */
+
+       /* Frequency */
+       __u16           num_channels;   /* Number of channels [0; num - 1] */
+       __u8            num_frequency;  /* Number of entry in the list */
+       struct iw_freq  freq[IW_MAX_FREQUENCIES];       /* list */
+
+       /* Note : this frequency list doesn't need to fit channel numbers */
+
+       /* Encoder stuff */
+
+       /* signal level threshold range */
+       __u32   sensitivity;
+
+       /* Quality of link & SNR stuff */
+       struct iw_quality       max_qual;       /* Quality of the link */
+};
+
+/*
+ * Private ioctl interface information
+ */
+struct iw_priv_args
+{
+       __u32           cmd;            /* Number of the ioctl to issue */
+       __u16           set_args;       /* Type and number of args */
+       __u16           get_args;       /* Type and number of args */
+       char            name[IFNAMSIZ]; /* Name of the extension */
+};
+
+#endif /* _LINUX_WIRELESS_H */
index c7bd9987971e5c79b251eda864713d292f904083..c1a88c7cd4f43ed9b97b5006d11f957f4f3afa0c 100644 (file)
@@ -134,6 +134,8 @@ void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int i
  */
  
 extern int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, __u32 target_addr);
+extern int sysctl_ip_forward; 
+
  
 /*
  *     Functions provided by ip_options.c
index 8b29eeb153fa429a53f469a6611f32887347b71d..c67d10a56c1d7f8a9b422611c8751c5ef7c25d6d 100644 (file)
@@ -8,18 +8,37 @@
 #include <linux/types.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
+#include <linux/config.h>
+
+/*
+ * This define affects the number of ports that can be handled
+ * by each of the protocol helper modules.
+ */
+#define MAX_MASQ_APP_PORTS 12
 
 /*
  *     Linux ports don't normally get allocated above 32K.
  *     I used an extra 4K port-space
  */
+
 #define PORT_MASQ_BEGIN        61000
 #define PORT_MASQ_END  (PORT_MASQ_BEGIN+4096)
 
+/*
+ * Default timeouts for masquerade functions The control channels now
+ * expire the same as TCP channels (other than being updated by
+ * packets on their associated data channels.
+ */
 #define MASQUERADE_EXPIRE_TCP     15*60*HZ
 #define MASQUERADE_EXPIRE_TCP_FIN  2*60*HZ
 #define MASQUERADE_EXPIRE_UDP      5*60*HZ
+/* 
+ * ICMP can no longer be modified on the fly using an ioctl - this
+ * define is the only way to change the timeouts 
+ */
+#define MASQUERADE_EXPIRE_ICMP      125*HZ
+
+#define IP_AUTOFW_EXPIRE            15*HZ
 
 #define IP_MASQ_F_OUT_SEQ                      0x01    /* must do output seq adjust */
 #define IP_MASQ_F_IN_SEQ               0x02    /* must do input seq adjust */
@@ -32,6 +51,9 @@
 #define IP_MASQ_F_SAW_FIN              (IP_MASQ_F_SAW_FIN_IN | \
                                         IP_MASQ_F_SAW_FIN_OUT)
                                                /* tcp fin pkts seen */
+#define IP_MASQ_F_CONTROL              0x100   /* this is a control channel */
+#define IP_MASQ_F_NO_SPORT             0x200   /* no sport set yet */
+#define IP_MASQ_F_FTP_PASV             0x400   /* ftp PASV command just issued */
 
 #ifdef __KERNEL__
 
@@ -59,6 +81,7 @@ struct ip_masq {
        struct ip_masq_app *app;        /* bound ip_masq_app object */
        void            *app_data;      /* Application private data */
        unsigned  flags;                /* status flags */
+       struct ip_masq  *control;       /* Corresponding control connection */
 };
 
 /*
@@ -76,9 +99,10 @@ extern struct ip_fw_masq *ip_masq_expire;
 /*
  *     [0]: UDP free_ports
  *     [1]: TCP free_ports
+ *     [2]: ICMP free ids
  */
 
-extern int ip_masq_free_ports[2];
+extern int ip_masq_free_ports[3];
 
 /*
  *     ip_masq initializer (registers symbols and /proc/net entries)
@@ -98,6 +122,9 @@ extern int ip_fw_demasquerade(struct sk_buff **, struct device *);
 extern struct ip_masq *ip_masq_new(struct device *dev, int proto, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned flags);
 extern void ip_masq_set_expire(struct ip_masq *ms, unsigned long tout);
 
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+extern void ip_autofw_expire(unsigned long data);
+#endif
 
 /*
  *     
@@ -167,6 +194,10 @@ extern int ip_masq_app_getinfo(char *buffer, char **start, off_t offset, int len
  */
 extern struct sk_buff * ip_masq_skb_replace(struct sk_buff *skb, int pri, char *o_buf, int o_len, char *n_buf, int n_len);
 
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+extern struct ip_autofw * ip_autofw_hosts;
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+
 #endif /* __KERNEL__ */
 
 #endif /* _IP_MASQ_H */
index 4b4248790d228dc62968cff32423d3e16a955f22..5b2f97f6c899812c6e2bdbedde1dc923bc9d2d6c 100644 (file)
@@ -31,4 +31,14 @@ extern int   raw_read(struct sock *sk, unsigned char *buff,
 extern int     raw_rcv(struct sock *, struct sk_buff *, struct device *, 
                        __u32, __u32);
 
+/* 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 */
index 686b2e7898f80a769f4916712401e81d965a4628..a85fcf346d446d8663adb2407d25ac4e5a8a3fd6 100644 (file)
@@ -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 for ISS.
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
 
 #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
  */
@@ -155,6 +152,10 @@ struct tcp_opt
  */
 struct sock 
 {
+       /* This must be first. */
+       struct sock             *sklist_next;
+       struct sock             *sklist_prev;
+
        struct options          *opt;
        atomic_t                wmem_alloc;
        atomic_t                rmem_alloc;
@@ -193,9 +194,14 @@ struct sock
                                bsdism;
        unsigned long           lingertime;
        int                     proc;
+
        struct sock             *next;
-       struct sock             *prev; /* Doubly linked chain.. */
+       struct sock             **pprev;
+       struct sock             *bind_next;
+       struct sock             **bind_pprev;
        struct sock             *pair;
+       int                     hashent;
+       struct sock             *prev;
        struct sk_buff          * volatile send_head;
        struct sk_buff          * volatile send_next;
        struct sk_buff          * volatile send_tail;
@@ -337,6 +343,10 @@ struct sock
  
 struct proto 
 {
+       /* These must be first. */
+       struct sock             *sklist_next;
+       struct sock             *sklist_prev;
+
        void                    (*close)(struct sock *sk, unsigned long timeout);
        int                     (*build_header)(struct sk_buff *skb,
                                        __u32 saddr,
@@ -372,11 +382,18 @@ struct proto
        int                     (*recvmsg)(struct sock *sk, struct msghdr *msg, int len,
                                        int noblock, int flags, int *addr_len);
        int                     (*bind)(struct sock *sk, struct sockaddr *uaddr, int addr_len);
+
+       /* 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[SOCK_ARRAY_SIZE];
 };
 
 #define TIME_WRITE     1
@@ -402,6 +419,46 @@ struct proto
 #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
@@ -449,18 +506,6 @@ here:
 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                    put_sock(unsigned short, struct sock *); 
-extern struct sock             *get_sock(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,
index 36bb61270a9f853cf1a2f401dc45b9ecf7aada41..0bcbc997a8a435698987fbb82a57eebdd64c859c 100644 (file)
 #include <linux/tcp.h>
 #include <net/checksum.h>
 
+/* This is for all connections with a full identity, no wildcards. */
+#define TCP_HTABLE_SIZE                256
+
+/* This is for listening sockets, thus all sockets which possess wildcards. */
+#define TCP_LHTABLE_SIZE       32      /* 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);
+}
+
+/* This is IPv4 specific. */
+static __inline__ int tcp_hashfn(__u32 laddr, __u16 lport,
+                                __u32 faddr, __u16 fport)
+{
+       return ((laddr ^ lport) ^ (faddr ^ fport)) & (TCP_HTABLE_SIZE - 1);
+}
+
+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);
+}
+
+/* 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);
+       struct sock **htable = &tcp_bound_hash[hashent];
+
+       if((sk->bind_next = *htable) != NULL)
+               (*htable)->bind_pprev = &sk->bind_next;
+       *htable = sk;
+       sk->bind_pprev = htable;
+}
+
+static __inline__ void tcp_sk_unbindify(struct sock *sk)
+{
+       if(sk->bind_next)
+               sk->bind_next->bind_pprev = sk->bind_pprev;
+       *(sk->bind_pprev) = sk->bind_next;
+}
+
 /*
  * 40 is maximal IP options size
  * 4  is TCP option size (MSS)
 #define TCP_WRITE_TIME (30*HZ) /* initial time to wait for an ACK,
                                 * after last transmit                  */
 #define TCP_TIMEOUT_INIT (3*HZ)        /* RFC 1122 initial timeout value       */
-#define TCP_SYN_RETRIES         10     /* number of times to retry opening a
+#define TCP_SYN_RETRIES              /* number of times to retry opening a
                                 * connection   (TCP_RETR2-....)        */
 #define TCP_PROBEWAIT_LEN (1*HZ)/* time to wait between probes when
                                 * I've got something to write and
@@ -128,6 +209,8 @@ static __inline__ int max(unsigned int a, unsigned int b)
 extern struct proto tcp_prot;
 extern struct tcp_mib tcp_statistics;
 
+extern unsigned short          tcp_good_socknum(void);
+
 extern void    tcp_err(int type, int code, unsigned char *header, __u32 daddr,
                        __u32, struct inet_protocol *protocol, int len);
 extern void    tcp_shutdown (struct sock *sk, int how);
@@ -152,7 +235,7 @@ extern void tcp_send_probe0(struct sock *);
 extern void tcp_send_partial(struct sock *);
 extern void tcp_write_wakeup(struct sock *);
 extern void tcp_send_fin(struct sock *sk);
-extern void tcp_send_synack(struct sock *, struct sock *, struct sk_buff *);
+extern void tcp_send_synack(struct sock *, struct sock *, struct sk_buff *, int);
 extern void tcp_send_skb(struct sock *, struct sk_buff *);
 extern void tcp_send_ack(struct sock *sk);
 extern void tcp_send_delayed_ack(struct sock *sk, int max_timeout, unsigned long timeout);
@@ -163,9 +246,6 @@ extern void tcp_enqueue_partial(struct sk_buff *, struct sock *);
 extern struct sk_buff * tcp_dequeue_partial(struct sock *);
 extern void tcp_shrink_skb(struct sock *,struct sk_buff *,u32);
 
-/* tcp_input.c */
-extern void tcp_cache_zap(void);
-
 /* CONFIG_IP_TRANSPARENT_PROXY */
 extern int tcp_chkaddr(struct sk_buff *);
 
@@ -262,7 +342,6 @@ static __inline__ void tcp_set_state(struct sock *sk, int state)
                break;
 
        case TCP_CLOSE:
-               tcp_cache_zap();
                /* Should be about 2 rtt's */
                reset_timer(sk, TIME_DONE, min(sk->rtt * 2, TCP_DONE_TIME));
                /* fall through */
index 404f64a956b34e729135c3ff1df12888091e9c81..d2c74769471032ba34105c3a1d632ba3295f5d6f 100644 (file)
 
 #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
 
@@ -47,7 +56,6 @@ extern int    udp_rcv(struct sk_buff *skb, struct device *dev,
                        unsigned short len, __u32 saddr, int redo,
                        struct inet_protocol *protocol);
 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);
index 1020a33b9168decc9a3a87e826617cd52a3338a1..f48f02c2e7e4ddc0c639bcd803aea880d7c9b33d 100644 (file)
 #define TYPE_SCANNER        0x06
 #define TYPE_MOD            0x07    /* Magneto-optical disk - 
                                     * - treated as TYPE_DISK */
+#define TYPE_MEDIUM_CHANGER 0x08
 #define TYPE_NO_LUN         0x7f
 
 
index 3ef52cdef884e1efa20baf882162fa16f8629e8a..b9ab9cc223783395519e81b18ff6611ffe1b29ac 100644 (file)
@@ -282,7 +282,7 @@ int do_fork(unsigned long clone_flags, unsigned long usp, struct pt_regs *regs)
        /* ok, now we should be set up.. */
        p->swappable = 1;
        p->exit_signal = clone_flags & CSIGNAL;
-       p->counter = current->counter >> 1;
+       p->counter = (current->counter >>= 1);
        wake_up_process(p);                     /* do this last, just in case */
        ++total_forks;
        return p->pid;
index 164a6d9da607f7e994c0c8671592c6e397af986b..453bdfcc422c880dcee45554ddb783e9fa3d4e2e 100644 (file)
@@ -323,7 +323,6 @@ struct symbol_table symbol_table = {
        X(___strtok),
        X(init_fifo),
        X(super_blocks),
-       X(reuse_list),
        X(fifo_inode_operations),
        X(chrdev_inode_operations),
        X(blkdev_inode_operations),
index 48184bfcf677a98c058be188e98a8cc355e3bc13..7d7a4ad639e81633bac95a9594115605ca4fa13e 100644 (file)
@@ -13,7 +13,7 @@
 #include <linux/types.h>
 #include <linux/ioport.h>
 
-#define IOTABLE_SIZE 64
+#define IOTABLE_SIZE 128
 
 typedef struct resource_entry_t {
        u_long from, num;
index 675d6bb934cbca0a138986d5cc8d48ebd62736ec..0832ca2e938b4998346cadc356ab1e65fa86259f 100644 (file)
@@ -4,6 +4,9 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  *
  *  1996-04-21 Modified by Ulrich Windl to make NTP work
+ *  1996-12-23  Modified by Dave Grothe to fix bugs in semaphores and
+ *              make semaphores SMP safe
+ *  1997-01-28  Modified by Finn Arne Gangstad to make timers scale better.
  */
 
 /*
@@ -482,32 +485,47 @@ bad:
        printk("       *q = %p\n",*q);
 }
 
+
 /*
  * Semaphores are implemented using a two-way counter:
  * The "count" variable is decremented for each process
- * that tries to sleep, while the "waiting" variable is
- * incremented _while_ the process is sleeping on that
- * semaphore. 
+ * that tries to sleep, while the "waking" variable is
+ * incremented when the "up()" code goes to wake up waiting
+ * processes.
  *
  * Notably, the inline "up()" and "down()" functions can
  * efficiently test if they need to do any extra work (up
  * needs to do something only if count was negative before
  * the increment operation.
+ *
+ * This routine must execute atomically.
  */
-static inline void normalize_semaphore(struct semaphore *sem)
+static inline int waking_non_zero(struct semaphore *sem)
 {
-       atomic_add(xchg(&sem->waiting,0), &sem->count);
+       int     ret ;
+       long    flags ;
+
+       get_buzz_lock(&sem->lock) ;
+       save_flags(flags) ;
+       cli() ;
+
+       if ((ret = (sem->waking > 0)))
+               sem->waking-- ;
+
+       restore_flags(flags) ;
+       give_buzz_lock(&sem->lock) ;
+       return(ret) ;
 }
 
 /*
  * When __up() is called, the count was negative before
- * incrementing it, and we need to wake up somebody. In
- * most cases "waiting" will be positive, and the normalization
- * will allow things to continue. However, if somebody has
- * /just/ done a down(), it may be that count was negative
- * without waiting being positive (or in the generic case
- * "count is more negative than waiting is positive"), and
- * the waiter needs to check this itself (see __down).
+ * incrementing it, and we need to wake up somebody.
+ *
+ * This routine adds one to the count of processes that need to
+ * wake up and exit.  ALL waiting processes actually wake up but
+ * only the one that gets to the "waking" field first will gate
+ * through and acquire the semaphore.  The others will go back
+ * to sleep.
  *
  * Note that these functions are only called when there is
  * contention on the lock, and as such all this is the
@@ -517,55 +535,86 @@ static inline void normalize_semaphore(struct semaphore *sem)
  */
 void __up(struct semaphore *sem)
 {
-       normalize_semaphore(sem);
+       atomic_inc(&sem->waking) ;
        wake_up(&sem->wait);
 }
 
-void __down(struct semaphore * sem)
+/*
+ * Perform the "down" function.  Return zero for semaphore acquired,
+ * return negative for signalled out of the function.
+ *
+ * If called from __down, the return is ignored and the wait loop is
+ * not interruptible.  This means that a task waiting on a semaphore
+ * using "down()" cannot be killed until someone does an "up()" on
+ * the semaphore.
+ *
+ * If called from __down_interruptible, the return value gets checked
+ * upon return.  If the return value is negative then the task continues
+ * with the negative value in the return register (it can be tested by
+ * the caller).
+ *
+ * Either form may be used in conjunction with "up()".
+ *
+ */
+int __do_down(struct semaphore * sem, int task_state)
 {
        struct task_struct *tsk = current;
        struct wait_queue wait = { tsk, NULL };
+       int               ret = 0 ;
 
-       /*
-        * The order here is important. We add ourselves to the
-        * wait queues and mark ourselves sleeping _first_. That
-        * way, if a "up()" comes in here, we'll either get
-        * woken up (up happens after the wait queues are set up)
-        * OR we'll have "waiting > 0".
-        */
-       tsk->state = TASK_UNINTERRUPTIBLE;
+       tsk->state = task_state;
        add_wait_queue(&sem->wait, &wait);
-       atomic_inc(&sem->waiting);
 
        /*
-        * Ok, we're set up. The only race here is really that
-        * an "up()" might have incremented count before we got
-        * here, so we check "count+waiting". If that is larger
-        * than zero, we shouldn't sleep, but re-try the lock.
+        * Ok, we're set up.  sem->count is known to be less than zero
+        * so we must wait.
+        *
+        * We can let go the lock for purposes of waiting.
+        * We re-acquire it after awaking so as to protect
+        * all semaphore operations.
+        *
+        * If "up()" is called before we call waking_non_zero() then
+        * we will catch it right away.  If it is called later then
+        * we will have to go through a wakeup cycle to catch it.
+        *
+        * Multiple waiters contend for the semaphore lock to see
+        * who gets to gate through and who has to wait some more.
         */
-       if (sem->count+sem->waiting <= 0) {
-               /*
-                * If "count+waiting" <= 0, we have to wait
-                * for a up(), which will normalize the count.
-                * Remember, at this point we have decremented
-                * count, and incremented up, so if count is
-                * zero or positive we need to return to re-try
-                * the lock.  It _may_ be that both count and
-                * waiting is zero and that it is still locked,
-                * but we still want to re-try the lock in that
-                * case to make count go negative again so that
-                * the optimized "up()" wake_up sequence works.
-                */
-               do {
-                       schedule();
-                       tsk->state = TASK_UNINTERRUPTIBLE;
-               } while (sem->count < 0);
+       for (;;)
+       {
+               if (waking_non_zero(sem))       /* are we waking up?  */
+                   break ;                     /* yes, exit loop */
+
+               if (   task_state == TASK_INTERRUPTIBLE
+                   && (tsk->signal & ~tsk->blocked)    /* signalled */
+                  )
+               {
+                   ret = -EINTR ;              /* interrupted */
+                   atomic_inc(&sem->count) ;   /* give up on down operation */
+                   break ;
+               }
+
+               schedule();
+               tsk->state = task_state;
        }
+
        tsk->state = TASK_RUNNING;
        remove_wait_queue(&sem->wait, &wait);
-       normalize_semaphore(sem);
+       return(ret) ;
+
+} /* __do_down */
+
+void __down(struct semaphore * sem)
+{
+       __do_down(sem,TASK_UNINTERRUPTIBLE) ; 
+}
+
+int __down_interruptible(struct semaphore * sem)
+{
+       return(__do_down(sem,TASK_INTERRUPTIBLE)) ; 
 }
 
+
 static inline void __sleep_on(struct wait_queue **p, int state)
 {
        unsigned long flags;
@@ -596,70 +645,170 @@ void sleep_on(struct wait_queue **p)
        __sleep_on(p,TASK_UNINTERRUPTIBLE);
 }
 
-/*
- * The head for the timer-list has a "expires" field of MAX_UINT,
- * and the sorting routine counts on this..
- */
-static struct timer_list timer_head = { &timer_head, &timer_head, ~0, 0, NULL };
+#define TVN_BITS 6
+#define TVR_BITS 8
+#define TVN_SIZE (1 << TVN_BITS)
+#define TVR_SIZE (1 << TVR_BITS)
+#define TVN_MASK (TVN_SIZE - 1)
+#define TVR_MASK (TVR_SIZE - 1)
+
 #define SLOW_BUT_DEBUGGING_TIMERS 0
 
-void add_timer(struct timer_list * timer)
+struct timer_vec {
+        int index;
+        struct timer_list *vec[TVN_SIZE];
+};
+
+struct timer_vec_root {
+        int index;
+        struct timer_list *vec[TVR_SIZE];
+};
+
+static struct timer_vec tv5 = { 0 };
+static struct timer_vec tv4 = { 0 };
+static struct timer_vec tv3 = { 0 };
+static struct timer_vec tv2 = { 0 };
+static struct timer_vec_root tv1 = { 0 };
+
+static struct timer_vec * const tvecs[] = {
+       (struct timer_vec *)&tv1, &tv2, &tv3, &tv4, &tv5
+};
+
+#define NOOF_TVECS (sizeof(tvecs) / sizeof(tvecs[0]))
+
+static unsigned long timer_jiffies = 0;
+
+static inline void insert_timer(struct timer_list *timer,
+                               struct timer_list **vec, int idx)
 {
-       unsigned long flags;
-       struct timer_list *p;
+       if ((timer->next = vec[idx]))
+               vec[idx]->prev = timer;
+       vec[idx] = timer;
+       timer->prev = (struct timer_list *)&vec[idx];
+}
 
-#if SLOW_BUT_DEBUGGING_TIMERS
-       if (timer->next || timer->prev) {
-               printk("add_timer() called with non-zero list from %p\n",
-                       __builtin_return_address(0));
-               return;
+static inline void internal_add_timer(struct timer_list *timer)
+{
+       /*
+        * must be cli-ed when calling this
+        */
+       unsigned long expires = timer->expires;
+       unsigned long idx = expires - timer_jiffies;
+
+       if (idx < TVR_SIZE) {
+               int i = expires & TVR_MASK;
+               insert_timer(timer, tv1.vec, i);
+       } else if (idx < 1 << (TVR_BITS + TVN_BITS)) {
+               int i = (expires >> TVR_BITS) & TVN_MASK;
+               insert_timer(timer, tv2.vec, i);
+       } else if (idx < 1 << (TVR_BITS + 2 * TVN_BITS)) {
+               int i = (expires >> (TVR_BITS + TVN_BITS)) & TVN_MASK;
+               insert_timer(timer, tv3.vec, i);
+       } else if (idx < 1 << (TVR_BITS + 3 * TVN_BITS)) {
+               int i = (expires >> (TVR_BITS + 2 * TVN_BITS)) & TVN_MASK;
+               insert_timer(timer, tv4.vec, i);
+       } else if (expires < timer_jiffies) {
+               /* can happen if you add a timer with expires == jiffies,
+                * or you set a timer to go off in the past
+                */
+               insert_timer(timer, tv1.vec, tv1.index);
+       } else if (idx < 0xffffffffUL) {
+               int i = (expires >> (TVR_BITS + 3 * TVN_BITS)) & TVN_MASK;
+               insert_timer(timer, tv5.vec, i);
+       } else {
+               /* Can only get here on architectures with 64-bit jiffies */
+               timer->next = timer->prev = timer;
        }
-#endif
-       p = &timer_head;
+}
+
+void add_timer(struct timer_list *timer)
+{
+       unsigned long flags;
        save_flags(flags);
        cli();
-       do {
-               p = p->next;
-       } while (timer->expires > p->expires);
-       timer->next = p;
-       timer->prev = p->prev;
-       p->prev = timer;
-       timer->prev->next = timer;
+#if SLOW_BUT_DEBUGGING_TIMERS
+        if (timer->next || timer->prev) {
+                printk("add_timer() called with non-zero list from %p\n",
+                      __builtin_return_address(0));
+               goto out;
+        }
+#endif
+       internal_add_timer(timer);
+#if SLOW_BUT_DEBUGGING_TIMERS
+out:
+#endif
        restore_flags(flags);
 }
 
-int del_timer(struct timer_list * timer)
+static inline int detach_timer(struct timer_list *timer)
 {
        int ret = 0;
-       if (timer->next) {
-               unsigned long flags;
-               struct timer_list * next;
-               save_flags(flags);
-               cli();
-               if ((next = timer->next) != NULL) {
-                       (next->prev = timer->prev)->next = next;
-                       timer->next = timer->prev = NULL;
-                       ret = 1;
-               }
-               restore_flags(flags);
+       struct timer_list *next, *prev;
+       next = timer->next;
+       prev = timer->prev;
+       if (next) {
+               next->prev = prev;
+       }
+       if (prev) {
+               ret = 1;
+               prev->next = next;
        }
        return ret;
 }
 
-static inline void run_timer_list(void)
+
+int del_timer(struct timer_list * timer)
 {
-       struct timer_list * timer;
+       int ret;
+       unsigned long flags;
+       save_flags(flags);
+       cli();
+       ret = detach_timer(timer);
+       timer->next = timer->prev = 0;
+       restore_flags(flags);
+       return ret;
+}
 
+static inline void cascade_timers(struct timer_vec *tv)
+{
+        /* cascade all the timers from tv up one level */
+        struct timer_list *timer;
+        timer = tv->vec[tv->index];
+        /*
+         * We are removing _all_ timers from the list, so we don't  have to
+         * detach them individually, just clear the list afterwards.
+         */
+        while (timer) {
+                struct timer_list *tmp = timer;
+                timer = timer->next;
+                internal_add_timer(tmp);
+        }
+        tv->vec[tv->index] = NULL;
+        tv->index = (tv->index + 1) & TVN_MASK;
+}
+
+static inline void run_timer_list(void)
+{
        cli();
-       while ((timer = timer_head.next) != &timer_head && timer->expires <= jiffies) {
-               void (*fn)(unsigned long) = timer->function;
-               unsigned long data = timer->data;
-               timer->next->prev = timer->prev;
-               timer->prev->next = timer->next;
-               timer->next = timer->prev = NULL;
-               sti();
-               fn(data);
-               cli();
+       while ((long)(jiffies - timer_jiffies) >= 0) {
+               struct timer_list *timer;
+               if (!tv1.index) {
+                       int n = 1;
+                       do {
+                               cascade_timers(tvecs[n]);
+                       } while (tvecs[n]->index == 1 && ++n < NOOF_TVECS);
+               }
+               while ((timer = tv1.vec[tv1.index])) {
+                       void (*fn)(unsigned long) = timer->function;
+                       unsigned long data = timer->data;
+                       detach_timer(timer);
+                       timer->next = timer->prev = NULL;
+                       sti();
+                       fn(data);
+                       cli();
+               }
+               ++timer_jiffies; 
+               tv1.index = (tv1.index + 1) & TVR_MASK;
        }
        sti();
 }
@@ -1255,8 +1404,7 @@ static int setscheduler(pid_t pid, int policy,
        if (p->next_run)
                move_last_runqueue(p);
        sti();
-       schedule();
-
+       need_resched = 1;
        return 0;
 }
 
@@ -1313,6 +1461,7 @@ asmlinkage int sys_sched_yield(void)
        cli();
        move_last_runqueue(current);
        sti();
+       need_resched = 1;
        return 0;
 }
 
index e9f7564cf87b72e73c94303cb05e752b2b0f47b7..377fcc649ddf8de65412d1c035e42301e8e121e2 100644 (file)
@@ -854,6 +854,8 @@ asmlinkage int sys_setrlimit(unsigned int resource, struct rlimit *rlim)
        if (err)
                return err;
        memcpy_fromfs(&new_rlim, rlim, sizeof(*rlim));
+       if (new_rlim.rlim_cur < 0 || new_rlim.rlim_max < 0)
+               return -EINVAL;
        old_rlim = current->rlim + resource;
        if (((new_rlim.rlim_cur > old_rlim->rlim_max) ||
             (new_rlim.rlim_max > old_rlim->rlim_max)) &&
index 3365a6b3792dbc2565728f635bc6f88066bd2677..e26c023704a423a4a3d6d33c79dd18475b9c1d52 100644 (file)
@@ -228,7 +228,7 @@ static int in_egroup_p(gid_t grp)
 {
        int     i;
 
-       if (grp == current->euid)
+       if (grp == current->egid)
                return 1;
 
        for (i = 0; i < NGROUPS; i++) {
index dd897b3d3ead75cbcc9fd6c7067a26b571249a0e..70d8703bf73163c01b0d9816bf1cff818a7d723b 100644 (file)
@@ -519,7 +519,8 @@ asmlinkage int sys_swapon(const char * specialfile, int swap_flags)
        p->flags = SWP_WRITEOK;
        p->pages = j;
        nr_swap_pages += j;
-       printk("Adding Swap: %dk swap-space\n",j<<(PAGE_SHIFT-10));
+       printk("Adding Swap: %dk swap-space (priority %d)\n",
+              j<<(PAGE_SHIFT-10), p->prio);
 
        /* insert swap space into swap_list: */
        prev = -1;
index 6616243d09af0152ffefc23fa6a8589178588e17..f9981fb0db1a2b9bec1ad69fcc9a72fd5669d5e6 100644 (file)
@@ -87,6 +87,9 @@
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>
+#endif /* CONFIG_NET_RADIO */
 
 /*
  *     The list of packet types we will receive (as opposed to discard)
@@ -657,13 +660,14 @@ void net_bh(void)
                pt_prev = NULL;
                for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next)
                {
-                       if(pt_prev)
-                       {
-                               struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
-                               if(skb2)
-                                       pt_prev->func(skb2,skb->dev, pt_prev);
+                       if(!ptype->dev || ptype->dev == skb->dev) {
+                               if(pt_prev) {
+                                       struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
+                                       if(skb2)
+                                               pt_prev->func(skb2,skb->dev, pt_prev);
+                               }
+                               pt_prev=ptype;
                        }
-                       pt_prev=ptype;
                }
                
                for (ptype = ptype_base[ntohs(type)&15]; ptype != NULL; ptype = ptype->next) 
@@ -940,6 +944,95 @@ int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy
 #endif /* CONFIG_PROC_FS */
 
 
+#ifdef CONFIG_NET_RADIO
+#ifdef CONFIG_PROC_FS
+
+/*
+ * Print one entry of /proc/net/wireless
+ * This is a clone of /proc/net/dev (just above)
+ */
+static int
+sprintf_wireless_stats(char *          buffer,
+                      struct device *  dev)
+{
+       /* Get stats from the driver */
+       struct iw_statistics *stats = (dev->get_wireless_stats ?
+                                      dev->get_wireless_stats(dev) :
+                                      (struct iw_statistics *) NULL);
+       int size;
+       
+       if(stats != (struct iw_statistics *) NULL)
+               size = sprintf(buffer,
+                              "%6s: %02x  %3d%c %3d%c  %3d%c %5d %5d %5d\n",
+                              dev->name,
+                              stats->status,
+                              stats->qual.qual,
+                              stats->qual.updated & 1 ? '.' : ' ',
+                              stats->qual.level,
+                              stats->qual.updated & 2 ? '.' : ' ',
+                              stats->qual.noise,
+                              stats->qual.updated & 3 ? '.' : ' ',
+                              stats->discard.nwid,
+                              stats->discard.code,
+                              stats->discard.misc);
+       else
+               size = 0;
+
+       return size;
+}
+
+/*
+ * Print info for /proc/net/wireless (print all entries)
+ * This is a clone of /proc/net/dev (just above)
+ */
+int
+dev_get_wireless_info(char *   buffer,
+                     char **   start,
+                     off_t     offset,
+                     int       length,
+                     int       dummy)
+{
+       int             len = 0;
+       off_t           begin = 0;
+       off_t           pos = 0;
+       int             size;
+
+       struct device * dev;
+
+       size = sprintf(buffer,
+                      "Inter-|sta|  Quality       |  Discarded packets\n"
+                      " face |tus|link level noise| nwid crypt  misc\n");
+       
+       pos+=size;
+       len+=size;
+
+
+       for(dev = dev_base; dev != NULL; dev = dev->next) 
+       {
+               size = sprintf_wireless_stats(buffer+len, dev);
+               len+=size;
+               pos=begin+len;
+
+               if(pos < offset)
+               {
+                       len=0;
+                       begin=pos;
+               }
+               if(pos > offset + length)
+                       break;
+       }
+
+       *start = buffer + (offset - begin);     /* Start of wanted data */
+       len -= (offset - begin);                /* Start slop */
+       if(len > length)
+               len = length;           /* Ending slop */
+
+       return len;
+}
+#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_NET_RADIO */
+
+
 /*
  *     This checks bitmasks for the ioctl calls for devices.
  */
@@ -1279,7 +1372,23 @@ static int dev_ifsioc(void *arg, unsigned int getset)
                                memcpy_tofs(arg,&ifr,sizeof(struct ifreq));
                                break;
                        }
-                       
+
+#ifdef CONFIG_NET_RADIO
+                       if((getset >= SIOCIWFIRST) &&
+                          (getset <= SIOCIWLAST))
+                       {
+                               if(dev->do_ioctl==NULL)
+                                       return -EOPNOTSUPP;
+                               /* Perform the ioctl */
+                               ret=dev->do_ioctl(dev, &ifr, getset);
+                               /* If return args... */
+                               if(IW_IS_GET(getset))
+                                       memcpy_tofs(arg, &ifr,
+                                                   sizeof(struct ifreq));
+                               break;
+                       }
+#endif /* CONFIG_NET_RADIO */
+
                        ret = -EINVAL;
        }
        return(ret);
@@ -1355,6 +1464,15 @@ int dev_ioctl(unsigned int cmd, void *arg)
                           (cmd <= (SIOCDEVPRIVATE + 15))) {
                                return dev_ifsioc(arg, cmd);
                        }
+#ifdef CONFIG_NET_RADIO
+                       if((cmd >= SIOCIWFIRST) &&
+                          (cmd <= SIOCIWLAST))
+                       {
+                               if((IW_IS_SET(cmd)) && (!suser()))
+                                       return -EPERM;
+                               return dev_ifsioc(arg, cmd);
+                       }
+#endif /* CONFIG_NET_RADIO */
                        return -EINVAL;
        }
 }
@@ -1456,6 +1574,17 @@ int net_dev_init(void)
        });
 #endif
 
+#ifdef CONFIG_NET_RADIO
+#ifdef CONFIG_PROC_FS
+       proc_net_register(&(struct proc_dir_entry) {
+               PROC_NET_WIRELESS, 8, "wireless",
+               S_IFREG | S_IRUGO, 1, 0, 0,
+               0, &proc_net_inode_operations,
+               dev_get_wireless_info
+       });
+#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_NET_RADIO */
+
        /*      
         *      Initialise net_alias engine 
         *
index 6af7c270a298a8afcf5c33825c102946da9928d7..8a40ca1e30c3774efc0100f4a8f90f4533506a62 100644 (file)
@@ -584,12 +584,23 @@ void kfree_skb(struct sk_buff *skb, int rw)
 #if CONFIG_SKB_CHECK
        IS_SKB(skb);
 #endif
-       if (skb->lock)
-       {
-               skb->free = 3;    /* Free when unlocked */
-               net_free_locked++;
-               return;
+       /* Check it twice, this is such a rare event and only occurs under
+        * extremely high load, normal code path should not suffer from the
+        * overhead of the cli.
+        */
+       if (skb->lock) {
+               unsigned long flags;
+
+               save_flags(flags); cli();
+               if(skb->lock) {
+                       skb->free = 3;    /* Free when unlocked */
+                       net_free_locked++;
+                       restore_flags(flags);
+                       return;
+               }
+               restore_flags(flags);
        }
+
        if (skb->free == 2)
                printk(KERN_WARNING "Warning: kfree_skb passed an skb that nobody set the free flag on! (from %p)\n",
                        __builtin_return_address(0));
@@ -831,21 +842,29 @@ struct sk_buff *skb_copy(struct sk_buff *skb, int priority)
 
 void skb_device_lock(struct sk_buff *skb)
 {
+       unsigned long flags;
+
+       save_flags(flags); cli();
        if(skb->lock)
                printk("double lock on device queue, lock=%d caller=%p\n",
                        skb->lock, (&skb)[-1]);
        else
                net_locked++;
        skb->lock++;
+       restore_flags(flags);
 }
 
 void skb_device_unlock(struct sk_buff *skb)
 {
+       unsigned long flags;
+
+       save_flags(flags); cli();
        if(skb->lock==0)
                printk("double unlock on device queue!\n");
        skb->lock--;
        if(skb->lock==0)
                net_locked--;
+       restore_flags(flags);
 }
 
 void dev_kfree_skb(struct sk_buff *skb, int mode)
index 8e655058690a2ae7b3cc89afcc60c93cbbd8d762..e228a1801df7c40e22d587ea2e4feca538e0234b 100644 (file)
@@ -3,22 +3,27 @@
 #
 bool 'IP: forwarding/gatewaying' CONFIG_IP_FORWARD
 bool 'IP: multicasting' CONFIG_IP_MULTICAST
+bool 'IP: syn cookies' CONFIG_SYN_COOKIES
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+  bool 'IP: rst cookies' CONFIG_RST_COOKIES
+fi
 if [ "$CONFIG_FIREWALL" = "y" ]; then
   bool 'IP: firewalling' CONFIG_IP_FIREWALL
   if [ "$CONFIG_IP_FIREWALL" = "y" ]; then
     bool 'IP: firewall packet logging' CONFIG_IP_FIREWALL_VERBOSE
-    if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_IP_FORWARD" = "y" ]; then
-      bool 'IP: masquerading (EXPERIMENTAL)' CONFIG_IP_MASQUERADE
+      bool 'IP: masquerading' CONFIG_IP_MASQUERADE
       if [ "$CONFIG_IP_MASQUERADE" != "n" ]; then
         comment 'Protocol-specific masquerading support will be built as modules.'
+        bool 'IP: ipautofw masq support' CONFIG_IP_MASQUERADE_IPAUTOFW
+        bool 'IP: ICMP masquerading' CONFIG_IP_MASQUERADE_ICMP
       fi
-      bool 'IP: transparent proxy support (EXPERIMENTAL)' CONFIG_IP_TRANSPARENT_PROXY
-      bool 'IP: always defragment' CONFIG_IP_ALWAYS_DEFRAG
-    fi
+      if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then        
+        bool 'IP: transparent proxy support (EXPERIMENTAL)' CONFIG_IP_TRANSPARENT_PROXY
+      fi       
+     bool 'IP: always defragment' CONFIG_IP_ALWAYS_DEFRAG
   fi
 fi
 bool 'IP: accounting' CONFIG_IP_ACCT
-if [ "$CONFIG_IP_FORWARD" = "y" ]; then
   bool 'IP: optimize as router not host' CONFIG_IP_ROUTER
   tristate 'IP: tunneling' CONFIG_NET_IPIP
   if [ "$CONFIG_IP_MULTICAST" = "y" ]; then
@@ -26,7 +31,6 @@ if [ "$CONFIG_IP_FORWARD" = "y" ]; then
         bool 'IP: multicast routing (EXPERIMENTAL)' CONFIG_IP_MROUTE
       fi               
   fi
-fi
 if [ "$CONFIG_NET_ALIAS" = "y" ]; then
        tristate 'IP: aliasing support' CONFIG_IP_ALIAS
 fi
index d4f7655ea02b64cf87891a6a98506c8f0f18d098..1b17a0a1eeec40f0824547a37b9e9190bb37f765 100644 (file)
@@ -40,7 +40,8 @@ endif
 
 ifeq ($(CONFIG_IP_MASQUERADE),y)
 IPV4_OBJS += ip_masq.o ip_masq_app.o
-M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o
+M_OBJS += ip_masq_ftp.o ip_masq_irc.o ip_masq_raudio.o ip_masq_cuseeme.o 
+M_OBJS += ip_masq_vdolive.o ip_masq_quake.o
 endif
 
 ifeq ($(CONFIG_IP_ALIAS),y)
index cdf78b14a6a3aa83e5f1c650d3e7eaef3755ea51..b3c673e5ba7430f21aef49071e6bb72f6c6e494c 100644 (file)
@@ -50,6 +50,7 @@
  *             Alan Cox        :       Loosened bind a little.
  *             Mike McLagan    :       ADD/DEL DLCI Ioctls
  *     Willy Konynenberg       :       Transparent proxying support.
+ *             David S. Miller :       New socket lookup architecture for ISS.
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
 #ifdef CONFIG_KERNELD
 #include <linux/kerneld.h>
 #endif
+#ifdef CONFIG_NET_RADIO
+#include <linux/wireless.h>
+#endif /* CONFIG_NET_RADIO */
 
 #define min(a,b)       ((a)<(b)?(a):(b))
 
@@ -124,237 +128,28 @@ int (*dlci_ioctl_hook)(unsigned int, void *) = NULL;
 
 int (*rarp_ioctl_hook)(unsigned int,void*) = NULL;
 
-/*
- *     See if a socket number is in use.
- */
-static int sk_inuse(struct proto *prot, int num)
-{
-       struct sock *sk;
-
-       for(sk = prot->sock_array[num & (SOCK_ARRAY_SIZE -1 )];
-               sk != NULL;  sk=sk->next) 
-       {
-               if (sk->num == num) 
-                       return(1);
-       }
-       return(0);
-}
-
-
-/*
- *     Pick a new socket number
- */
-
-unsigned short get_new_socknum(struct proto *prot, unsigned short base)
-{
-       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;
-
-       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;
-               }
-       }
-
-       /* 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);
-}
-
-/*
- *     Add a socket into the socket tables by number.
- */
-
-void put_sock(unsigned short num, 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);
-
-       /* 
-        *      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[]..
-        */
-       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.
- */
-
-static void 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;
-       }
-
-       /* 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);
-}
-
 /*
  *     Destroy an AF_INET socket
  */
  
-void destroy_sock(struct sock *sk)
+static __inline__ void kill_sk_queues(struct sock *sk)
 {
        struct sk_buff *skb;
 
-       lock_sock(sk);                  /* just to be safe. */
-
-       remove_sock(sk);
-  
-       /*
-        *      Now we can no longer get new packets or once the
-        *      timers are killed, send them.
-        */
-        
-       delete_timer(sk);
-       del_timer(&sk->delack_timer);
-       del_timer(&sk->retransmit_timer);
-       
-       /*
-        *      Drain any partial frames
-        */
-        
-       while ((skb = tcp_dequeue_partial(sk)) != NULL) 
-       {
-               IS_SKB(skb);
+       while((skb = tcp_dequeue_partial(sk)) != NULL)
                kfree_skb(skb, FREE_WRITE);
-       }
 
-       /*
-        *      Cleanup up the write buffer. 
-        */
-        
-       while((skb = skb_dequeue(&sk->write_queue)) != NULL) {
-               IS_SKB(skb);
+       /* Next, the write queue. */
+       while((skb = skb_dequeue(&sk->write_queue)) != NULL)
                kfree_skb(skb, FREE_WRITE);
-       }
-       
-       /*
-        *      Clean up the read buffer.
-        */
 
-       while((skb=skb_dequeue(&sk->receive_queue))!=NULL) 
-       {
-               /*
-                * This will take care of closing sockets that were
+       /* Then, the receive queue. */
+       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);
+               if (skb->sk != NULL && skb->sk != sk)
                        skb->sk->prot->close(skb->sk, 0);
-               }
-               IS_SKB(skb);
                kfree_skb(skb, FREE_READ);
        }
 
@@ -386,16 +181,61 @@ void destroy_sock(struct sock *sk)
        sk->send_next = NULL;
        sti();
 
+       /* Finally, the backlog. */
+       while((skb=skb_dequeue(&sk->back_log)) != NULL) {
+               /* skb->sk = NULL; */
+               kfree_skb(skb, FREE_READ);
+       }
+}
+
+static __inline__ void kill_sk_now(struct sock *sk)
+{
+       /* No longer exists. */
+       del_from_prot_sklist(sk);
+
+       /* This is gross, but needed for SOCK_PACKET -DaveM */
+       if(sk->prot->unhash)
+               sk->prot->unhash(sk);
+
+       if(sk->opt)
+               kfree(sk->opt);
+       ip_rt_put(sk->ip_route_cache);
+       sk_free(sk);
+}
+
+static __inline__ void kill_sk_later(struct sock *sk)
+{
+       /* 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]
+        */
+                 
+       NETDEBUG(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);
+       reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
+}
+
+void destroy_sock(struct sock *sk)
+{
+       lock_sock(sk);                  /* just to be safe. */
+
        /*
-        *      Now the backlog. 
+        *      Now we can no longer get new packets or once the
+        *      timers are killed, send them.
         */
         
-       while((skb=skb_dequeue(&sk->back_log))!=NULL) 
-       {
-               /* this should [almost] never happen. */
-               skb->sk = NULL;
-               kfree_skb(skb, FREE_READ);
-       }
+       delete_timer(sk);
+       del_timer(&sk->delack_timer);
+       del_timer(&sk->retransmit_timer);
+       
+       kill_sk_queues(sk);
 
        /*
         *      Now if it has a half accepted/ closed socket. 
@@ -414,28 +254,9 @@ void destroy_sock(struct sock *sk)
         */
 
        if (sk->rmem_alloc == 0 && sk->wmem_alloc == 0) 
-       {
-               if(sk->opt)
-                       kfree(sk->opt);
-               ip_rt_put(sk->ip_route_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. */
-               NETDEBUG(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);
-               reset_timer(sk, TIME_DESTROY, SOCK_DESTROY_TIME);
-       }
+               kill_sk_now(sk);
+       else
+               kill_sk_later(sk);
 }
 
 /*
@@ -508,15 +329,13 @@ static int inet_getsockopt(struct socket *sock, int level, int optname,
 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();
-               put_sock(sk->num, sk);
                sk->dummy_th.source = ntohs(sk->num);
+               sk->prot->rehash(sk);
+               add_to_prot_sklist(sk);
        }
        return 0;
 }
@@ -529,7 +348,7 @@ static int inet_listen(struct socket *sock, int backlog)
 {
        struct sock *sk = (struct sock *) sock->data;
 
-       if(inet_autobind(sk)!=0)
+       if(inet_autobind(sk) != 0)
                return -EAGAIN;
 
        /* We might as well re use these. */ 
@@ -538,16 +357,17 @@ static int inet_listen(struct socket *sock, int backlog)
         * somewhere. We might as well truncate it to what everybody
         * else does..
         * Now truncate to 128 not 5. 
+        *
+        * This was wrong, truncate both cases to SOMAXCONN. -DaveM
         */
-       if ((unsigned) backlog == 0)    /* BSDism */
-               backlog = 1;
-       if ((unsigned) backlog > SOMAXCONN)
+       if (((unsigned) backlog == 0) || ((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);
        }
        return(0);
 }
@@ -592,76 +412,41 @@ static int inet_create(struct socket *sock, int protocol)
 {
        struct sock *sk;
        struct proto *prot;
-       int err;
 
        sk = sk_alloc(GFP_KERNEL);
        if (sk == NULL) 
-               return(-ENOBUFS);
+               goto do_oom;
+#if 0 /* sk_alloc() does this for us. -DaveM */
        memset(sk,0,sizeof(*sk));       /* Efficient way to set most fields to zero */
+#endif
        /*
         *      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;
-                       prot = &tcp_prot;
-                       break;
-
-               case SOCK_DGRAM:
-                       if (protocol && protocol != IPPROTO_UDP) 
-                       {
-                               sk_free(sk);
-                               return(-EPROTONOSUPPORT);
-                       }
-                       protocol = IPPROTO_UDP;
-                       sk->no_check = UDP_NO_CHECK;
-                       prot=&udp_prot;
-                       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->num = protocol;
-                       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->num = protocol;
-                       break;
-
-               default:
-                       sk_free(sk);
-                       return(-ESOCKTNOSUPPORT);
+       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 = &tcp_prot;
+       } 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=&udp_prot;
+       } else if(sock->type == SOCK_RAW || sock->type == SOCK_PACKET) {
+               if (!suser()) 
+                       goto free_and_badperm;
+               if (!protocol) 
+                       goto free_and_noproto;
+               prot = &raw_prot;
+               prot = (sock->type == SOCK_RAW) ? &raw_prot : &packet_prot;
+               sk->reuse = 1;
+               sk->num = protocol;
+       } else {
+               goto free_and_badtype;
        }
+
        sk->socket = sock;
 #ifdef CONFIG_TCP_NAGLE_OFF
        sk->nonagle = 1;
@@ -717,28 +502,43 @@ static int inet_create(struct socket *sock, int protocol)
        sk->write_space = def_callback3;
        sk->error_report = def_callback1;
 
-       if (sk->num) 
-       {
-       /*
-        * It assumes that any protocol which allows
-        * the user to assign a number at socket
-        * creation time automatically
-        * shares.
-        */
-               put_sock(sk->num, sk);
+       if (sk->num) {
+               /* It assumes that any protocol which allows
+                * the user to assign a number at socket
+                * creation time automatically
+                * shares.
+                */
                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;
 }
 
 
@@ -759,191 +559,103 @@ static int inet_dup(struct socket *newsock, struct socket *oldsock)
  
 static int inet_release(struct socket *sock, struct socket *peer)
 {
-       unsigned long timeout;
        struct sock *sk = (struct sock *) sock->data;
 
-       if (sk == NULL) 
-               return(0);
+       if (sk) {
+               unsigned long timeout;
 
-       sk->state_change(sk);
+               sk->state_change(sk);
 
-       /* Start closing the connection.  This may take a while. */
+               /* Start closing the connection.  This may take a while. */
 
 #ifdef CONFIG_IP_MULTICAST
-       /* Applications forget to leave groups before exiting */
-       ip_mc_drop_socket(sk);
+               /* Applications forget to leave groups before exiting */
+               ip_mc_drop_socket(sk);
 #endif
-       /*
-        * 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)
+               /*
+                * 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)) {
+                       if (sk->lingertime)
+                               timeout = jiffies + HZ*sk->lingertime;
+               }
 
-       sock->data = NULL;
-       sk->socket = NULL;
+               sock->data = NULL;
+               sk->socket = NULL;
 
-       sk->prot->close(sk, timeout);
+               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=(struct sock *)sock->data, *sk2;
-       unsigned short snum = 0 /* Stoopid compiler.. this IS ok */;
+       struct sock *sk=(struct sock *)sock->data;
+       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)
+               /* Superuser may bind to any address to allow transparent proxying. */
+               if(!suser())
 #endif
-               return(-EADDRNOTAVAIL); /* Source address MUST be ours! */
+                       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.
+        *      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.
         */
-       if (chk_addr_ret || addr->sin_addr.s_addr == 0)
-       {
-#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
-       }
-#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);
-                       }
+       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 */
 
-                       /*
-                        *      Two binds match ?
-                        */
+       /* Make sure we are allowed to bind here. */
+       if(sk->prot->verify_bind(sk, snum))
+               return -EADDRINUSE;
 
-                       if (sk2->rcv_saddr != sk->rcv_saddr) 
-                               continue;
-                       /*
-                        *      Reusable port ?
-                        */
+       sk->num = snum;
+       sk->dummy_th.source = ntohs(sk->num);
+       sk->daddr = 0;
+       sk->dummy_th.dest = 0;
+       sk->prot->rehash(sk);
+       add_to_prot_sklist(sk);
 
-                       if (!sk->reuse)
-                       {
-                               sti();
-                               return(-EADDRINUSE);
-                       }
-                       
-                       /*
-                        *      Reuse ?
-                        */
-                        
-                       if (!sk2->reuse || sk2->state==TCP_LISTEN)
-                       {
-                               sti();
-                               return(-EADDRINUSE);
-                       }
-               }
-               sti();
-
-               remove_sock(sk);
-               if(sock->type==SOCK_DGRAM)
-                       udp_cache_zap();
-               if(sock->type==SOCK_STREAM)
-                       tcp_cache_zap();
-               put_sock(snum, sk);
-               sk->dummy_th.source = ntohs(sk->num);
-               sk->daddr = 0;
-               sk->dummy_th.dest = 0;
-       }
        ip_rt_put(sk->ip_route_cache);
        sk->ip_route_cache=NULL;
        return(0);
@@ -961,23 +673,21 @@ static int inet_connect(struct socket *sock, struct sockaddr * uaddr,
        int err;
        sock->conn = NULL;
 
-       if (sock->state == SS_CONNECTING && tcp_connected(sk->state))
-       {
+       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 */
        }
 
-       if (sock->state == SS_CONNECTING && sk->protocol == IPPROTO_TCP && (flags & O_NONBLOCK))
-       {
+       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) {
                /* 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);
@@ -987,8 +697,7 @@ static int inet_connect(struct socket *sock, struct sockaddr * uaddr,
                sock->state = SS_CONNECTING;
        }
        
-       if (sk->state > TCP_FIN_WAIT2 && sock->state==SS_CONNECTING)
-       {
+       if (sk->state > TCP_FIN_WAIT2 && sock->state==SS_CONNECTING) {
                sock->state=SS_UNCONNECTED;
                return sock_error(sk);
        }
@@ -997,18 +706,15 @@ static int inet_connect(struct socket *sock, struct sockaddr * uaddr,
                return(-EINPROGRESS);
 
        cli(); /* avoid the race condition */
-       while(sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) 
-       {
+       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() */
@@ -1017,8 +723,7 @@ static int inet_connect(struct socket *sock, struct sockaddr * uaddr,
        sti();
        sock->state = SS_CONNECTED;
 
-       if (sk->state != TCP_ESTABLISHED && sk->err) 
-       {
+       if (sk->state != TCP_ESTABLISHED && sk->err) {
                sock->state = SS_UNCONNECTED;
                return sock_error(sk);
        }
@@ -1049,8 +754,7 @@ static int inet_accept(struct socket *sock, struct socket *newsock, int flags)
         *      its own when it accepts one.
         */
         
-       if (newsock->data)
-       {
+       if (newsock->data) {
                struct sock *sk=(struct sock *)newsock->data;
                newsock->data=NULL;
                destroy_sock(sk);
@@ -1063,18 +767,13 @@ static int inet_accept(struct socket *sock, struct socket *newsock, int flags)
         *      Restore the state if we have been interrupted, and then returned. 
         */
         
-       if (sk1->pair != NULL ) 
-       {
+       if (sk1->pair != NULL) {
                sk2 = sk1->pair;
                sk1->pair = NULL;
-       } 
-       else
-       {
+       } else {
                sk2 = sk1->prot->accept(sk1,flags);
-               if (sk2 == NULL) 
-               {
+               if (sk2 == NULL)
                        return sock_error(sk1);
-               }
        }
        newsock->data = (void *)sk2;
        sk2->sleep = newsock->wait;
@@ -1084,11 +783,9 @@ static int inet_accept(struct socket *sock, struct socket *newsock, int flags)
                return(0);
 
        cli(); /* avoid the race. */
-       while(sk2->state == TCP_SYN_RECV) 
-       {
+       while(sk2->state == TCP_SYN_RECV) {
                interruptible_sleep_on(sk2->sleep);
-               if (current->signal & ~current->blocked) 
-               {
+               if (current->signal & ~current->blocked) {
                        sti();
                        sk1->pair = sk2;
                        sk2->sleep = NULL;
@@ -1099,15 +796,14 @@ static int inet_accept(struct socket *sock, struct socket *newsock, int flags)
        }
        sti();
 
-       if (sk2->state != TCP_ESTABLISHED && sk2->err > 0) 
-       {
+       if (sk2->state != TCP_ESTABLISHED && sk2->err > 0) {
                err = sock_error(sk2);
                destroy_sock(sk2);
                newsock->data = NULL;
                return err;
        }
-       if (sk2->state == TCP_CLOSE)
-       {
+
+       if (sk2->state == TCP_CLOSE) {
                destroy_sock(sk2);
                newsock->data=NULL;
                return -ECONNABORTED;
@@ -1129,15 +825,12 @@ static int inet_getname(struct socket *sock, struct sockaddr *uaddr,
   
        sin->sin_family = AF_INET;
        sk = (struct sock *) sock->data;
-       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;
@@ -1162,9 +855,11 @@ static int inet_recvmsg(struct socket *sock, struct msghdr *ubuf, int size, int
                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->recvmsg(sk, ubuf, size, noblock, flags,addr_len));
 }
 
@@ -1173,8 +868,7 @@ static int inet_sendmsg(struct socket *sock, struct msghdr *msg, int size, int n
           int flags)
 {
        struct sock *sk = (struct sock *) sock->data;
-       if (sk->shutdown & SEND_SHUTDOWN) 
-       {
+       if (sk->shutdown & SEND_SHUTDOWN) {
                send_sig(SIGPIPE, current, 1);
                return(-EPIPE);
        }
@@ -1182,9 +876,11 @@ static int inet_sendmsg(struct socket *sock, struct msghdr *msg, int size, int n
                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, noblock, flags));
                           
 }
@@ -1218,9 +914,8 @@ static int inet_select(struct socket *sock, int sel_type, select_table *wait )
 {
        struct sock *sk=(struct sock *) sock->data;
        if (sk->prot->select == NULL) 
-       {
                return(0);
-       }
+
        return(sk->prot->select(sk, sel_type, wait));
 }
 
@@ -1346,6 +1041,12 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
                           (cmd <= (SIOCDEVPRIVATE + 15)))
                                return(dev_ioctl(cmd,(void *) arg));
 
+#ifdef CONFIG_NET_RADIO
+                       if((cmd >= SIOCIWFIRST) &&
+                          (cmd <= SIOCIWLAST))
+                               return(dev_ioctl(cmd,(void *) arg));
+#endif /* CONFIG_NET_RADIO */
+
                        if (sk->prot->ioctl==NULL) 
                                return(-EINVAL);
                        return(sk->prot->ioctl(sk, cmd, arg));
@@ -1354,222 +1055,6 @@ static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
        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 (hpnum && 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);
-}
-#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,
-                               unsigned long paddr, unsigned short pnum)
-{
-       struct sock *s = 0;
-       struct sock *result = NULL;
-       int badness = -1;
-       unsigned short hnum;
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-       unsigned short hpnum;
-       int firstpass = 1;
-#endif 
-
-       hnum = ntohs(num);
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-       hpnum = ntohs(pnum);
-#endif 
-
-       /*
-        * 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.
-        */
-
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-       for(s = get_sock_loop_init(hnum, hpnum, s, &firstpass, prot);
-               s != NULL;
-               s = get_sock_loop_next(hnum, hpnum, s, &firstpass, prot))
-#else
-       for(s = prot->sock_array[hnum & (SOCK_ARRAY_SIZE - 1)];
-                       s != NULL; s = s->next) 
-#endif
-       {
-               int score = 0;
-
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-               /* accept the addressed port or the redirect (proxy) port */
-               if (s->num != hnum && (hpnum == 0 || s->num != hpnum))
-#else
-               if (s->num != hnum) 
-#endif
-                       continue;
-
-               if(s->dead && (s->state == TCP_CLOSE))
-                       continue;
-               /* local address matches? */
-               if (s->rcv_saddr) {
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-                       /*
-                        * 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 (hpnum
-                           ? ((s->num != hpnum || s->rcv_saddr != paddr)
-                             && (s->num != hnum || s->rcv_saddr != laddr))
-                           : (s->rcv_saddr != laddr))
-#else
-                       if (s->rcv_saddr != laddr)
-#endif
-                               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? */
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-               if (score == 3 && s->num == hnum)
-#else
-               if (score == 3)
-#endif
-                       return s;
-               /* no, check if this is the best so far.. */
-               if (score <= badness)
-                       continue;
-#ifdef CONFIG_IP_TRANSPARENT_PROXY
-               /* 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 (hpnum && s->num != hpnum && !s->rcv_saddr)
-                       continue;
-#endif
-               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);
-}
-
-#ifdef CONFIG_IP_MULTICAST
-/*
- *     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);
-}
-
-#endif
-
 static struct proto_ops inet_proto_ops = {
        AF_INET,
 
@@ -1601,8 +1086,6 @@ extern unsigned long seq_offset;
 void inet_proto_init(struct net_proto *pro)
 {
        struct inet_protocol *p;
-       int i;
-
 
        printk("Swansea University Computer Society TCP/IP for NET3.034\n");
 
@@ -1618,19 +1101,6 @@ void inet_proto_init(struct net_proto *pro)
         *      Add all the protocols. 
         */
         
-       for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
-       {
-               tcp_prot.sock_array[i] = NULL;
-               udp_prot.sock_array[i] = NULL;
-               raw_prot.sock_array[i] = NULL;
-       }
-       tcp_prot.inuse = 0;
-       tcp_prot.highestinuse = 0;
-       udp_prot.inuse = 0;
-       udp_prot.highestinuse = 0;
-       raw_prot.inuse = 0;
-       raw_prot.highestinuse = 0;
-
        printk("IP Protocols: ");
        for(p = inet_protocol_base; p != NULL;) 
        {
index 27751c1d270671f28d584a14e9e4d3d32b68975c..008adecc3b2731829201a1ecd39427cefce8f365 100644 (file)
@@ -61,6 +61,7 @@
  *             Stuart Cheshire :       Metricom and grat arp fixes
  *                                     *** FOR 2.1 clean this up ***
  *             Lawrence V. Stefani: (08/12/96) Added FDDI support.
+ *             David S. Miller :       Fix skb leakage in arp_find.
  */
 
 /* RFC1122 Status:
@@ -1430,6 +1431,7 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
                        else
                        {
                                icmp_send(skb, ICMP_DEST_UNREACH, ICMP_HOST_UNREACH, 0, dev);
+                               skb_device_unlock(skb); /* else it is lost forever */
                                dev_kfree_skb(skb, FREE_WRITE);
                        }
                }
@@ -1439,8 +1441,10 @@ int arp_find(unsigned char *haddr, u32 paddr, struct device *dev,
 
        entry = arp_new_entry(paddr, dev, NULL, skb);
 
-       if (skb != NULL && !entry)
+       if (skb != NULL && !entry) {
+               skb_device_unlock(skb); /* else it is lost forever */
                dev_kfree_skb(skb, FREE_WRITE);
+       }
 
        arp_unlock();
        return 1;
@@ -1804,7 +1808,18 @@ int arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
        }
 #else
        if (arp->ar_hln != dev->addr_len    || 
+#if CONFIG_AP1000
+           /*
+            * ARP from cafe-f was found to use ARPHDR_IEEE802 instead of
+            * the expected ARPHDR_ETHER.
+            */
+           (strcmp(dev->name,"fddi") == 0 && 
+            arp->ar_hrd != ARPHRD_ETHER && arp->ar_hrd != ARPHRD_IEEE802) ||
+           (strcmp(dev->name,"fddi") != 0 &&
+            dev->type != ntohs(arp->ar_hrd)) ||
+#else
                dev->type != ntohs(arp->ar_hrd) ||
+#endif
                dev->flags & IFF_NOARP          ||
                arp->ar_pln != 4)
        {
index 7aa28f4e3bdb2952be386f469a2c780388398d91..dbc52af01fa59ab7f5a8b38cb50a4191a4433fd3 100644 (file)
@@ -826,10 +826,12 @@ static void icmp_redirect(struct icmphdr *icmph, struct sk_buff *skb, struct dev
         *      When using no routing protocol, we MAY follow redirects. (RFC 1812, 5.2.7.2)
         */
 
-#if defined(CONFIG_IP_FORWARD) && !defined(CONFIG_IP_DUMB_ROUTER)
+#if !defined(CONFIG_IP_DUMB_ROUTER)
+       if (sysctl_ip_forward) {
        NETDEBUG(printk(KERN_INFO "icmp: ICMP redirect ignored. dest = %lX, "
               "orig gw = %lX, \"new\" gw = %lX, device = %s.\n", ntohl(ip),
                ntohl(source), ntohl(icmph->un.gateway), dev->name));
+       }
 #else  
        switch(icmph->code & 7) 
        {
@@ -989,6 +991,10 @@ static void icmp_discard(struct icmphdr *icmph, struct sk_buff *skb, struct devi
  *     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->h.raw + skb->h.iph->ihl*4);
@@ -1003,8 +1009,7 @@ int icmp_chkaddr(struct sk_buff *skb)
                        {
                        struct tcphdr *th = (struct tcphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
-                       sk = get_sock(&tcp_prot, th->source, iph->daddr,
-                                               th->dest, iph->saddr, 0, 0);
+                       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;
@@ -1018,8 +1023,7 @@ int icmp_chkaddr(struct sk_buff *skb)
                        {
                        struct udphdr *uh = (struct udphdr *)(((unsigned char *)iph)+(iph->ihl<<2));
 
-                       sk = get_sock(&udp_prot, uh->source, iph->daddr,
-                                               uh->dest, iph->saddr, 0, 0);
+                       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;
index 318108d088578eec472644bf04703f12126e5e18..da1d427def61806d449a3d286acfe0b8f3f0c629 100644 (file)
 #include <linux/route.h>
 #include <net/route.h>
 
-#ifdef CONFIG_IP_FORWARD
+#ifdef CONFIG_IP_FORWARD /* set the default */
+int sysctl_ip_forward = 1; 
+#else
+int sysctl_ip_forward = 0; 
+#endif
+
 #ifdef CONFIG_IP_MROUTE
 
 /*
@@ -264,6 +269,13 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
                 */
                if (iph->protocol == IPPROTO_ICMP)
                {
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+#define icmph ((struct icmphdr *)((char *)iph + (iph->ihl<<2)))
+                     if ((icmph->type==ICMP_DEST_UNREACH)||
+                         (icmph->type==ICMP_SOURCE_QUENCH)||
+                         (icmph->type==ICMP_TIME_EXCEEDED))
+                       {
+#endif
                        if ((fw_res = ip_fw_masq_icmp(&skb, dev2)) < 0)
                        {
                                if (rt)
@@ -275,6 +287,9 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
                        if (fw_res)
                                /* ICMP matched - skip firewall */
                                goto skip_call_fw_firewall;
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+                       }
+#endif                         
                }
 #endif
                fw_res=call_fw_firewall(PF_INET, dev2, iph, NULL);
@@ -561,7 +576,6 @@ int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag,
 }
 
 
-#endif
 
 
 
index 37092157fa2a29e511d7d84ee3504cd3c89dd943..ba9575baf1dd251d45b1776dc3233bf22daa6e47 100644 (file)
@@ -37,6 +37,8 @@
  *             Willy Konynenberg <willy@xos.nl> 10/5/96.
  *     Make separate accounting on incoming and outgoing packets possible.
  *             Jos Vos <jos@xos.nl> 18/5/1996.
+ *     Add timeout reprieve for idle control channels.
+ *             Keith Owens <kaos@audio.apana.org.au> 05/07/1996.
  *
  *
  * Masquerading functionality
@@ -886,6 +888,87 @@ int ip_acct_ctl(int stage, void *m, int len)
 }
 #endif
 
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+
+int ip_autofw_add(struct ip_autofw * af)
+{
+       struct ip_autofw * newaf;
+       init_timer(&af->timer);
+       newaf = kmalloc( sizeof(struct ip_autofw), GFP_ATOMIC );
+       memcpy(newaf, af, sizeof(struct ip_autofw));
+       newaf->timer.data = (unsigned long) newaf;
+       newaf->timer.function = ip_autofw_expire;
+       newaf->timer.expires = 0;
+       newaf->lastcontact=0;
+       newaf->next=ip_autofw_hosts;
+       ip_autofw_hosts=newaf;
+       return(0);
+}
+
+int ip_autofw_del(struct ip_autofw * af)
+{
+       struct ip_autofw * prev, * curr;
+       prev=NULL;
+       curr=ip_autofw_hosts;
+       while (curr)
+       {
+               if (af->type     == curr->type &&
+                   af->low      == curr->low &&
+                   af->high     == curr->high &&
+                   af->hidden   == curr->hidden &&
+                   af->visible  == curr->visible &&
+                   af->protocol == curr->protocol &&
+                   af->where    == curr->where &&
+                   af->ctlproto == curr->ctlproto &&
+                   af->ctlport  == curr->ctlport)
+               {
+                       if (prev)
+                       {
+                               prev->next=curr->next;
+                               kfree_s(curr,sizeof(struct ip_autofw));
+                               return(0);
+                       }
+                       else
+                       {
+                               kfree_s(ip_autofw_hosts,sizeof(struct ip_autofw));
+                               ip_autofw_hosts=curr->next;
+                               return(0);
+                       }
+               }
+               prev=curr;
+               curr=curr->next;
+       }
+       return(EINVAL);
+}
+
+int ip_autofw_flush(void)
+{
+       struct ip_autofw * af;
+       while (ip_autofw_hosts)
+       {
+               af=ip_autofw_hosts;
+               ip_autofw_hosts=ip_autofw_hosts->next;
+               kfree_s(af,sizeof(struct ip_autofw));
+       }
+       return(0);
+}
+
+int ip_autofw_ctl(int stage, void *m, int len)
+{
+       if (stage == IP_AUTOFW_ADD)
+               return (ip_autofw_add((struct ip_autofw *) m));
+
+       if (stage == IP_AUTOFW_DEL)
+               return (ip_autofw_del((struct ip_autofw *) m));
+       
+       if (stage == IP_AUTOFW_FLUSH)
+               return (ip_autofw_flush());
+       
+       return(EINVAL);
+}
+
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+
 #ifdef CONFIG_IP_FIREWALL
 int ip_fw_ctl(int stage, void *m, int len)
 {
index e099b774495900713c34935b4cb88b3a4842b1ef..0d31eb3fc55376b07d08465767a0b970ae22f592 100644 (file)
@@ -482,13 +482,13 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
                        {
                                opt->srr_is_hit = 1;
                                opt->is_changed = 1;
-#ifdef CONFIG_IP_FORWARD
-                               if (ip_forward(skb, dev, is_frag, nexthop))
+                               if (sysctl_ip_forward) {
+                                       if (ip_forward(skb, dev, is_frag, nexthop))
+                                               kfree_skb(skb, FREE_WRITE);
+                               } else {
+                                       ip_statistics.IpInAddrErrors++;
                                        kfree_skb(skb, FREE_WRITE);
-#else
-                               ip_statistics.IpInAddrErrors++;
-                               kfree_skb(skb, FREE_WRITE);
-#endif
+                               }
                                return 0;
                        }
                }
@@ -575,32 +575,34 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
                 *      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, dev, iph->saddr,daddr);
-                                       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 
@@ -616,7 +618,6 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
                 *      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;
@@ -718,21 +719,21 @@ int ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
         *      The packet is for another target. Forward the frame
         */
 
-#ifdef CONFIG_IP_FORWARD
-       if (opt && opt->is_strictroute) 
-       {
-             icmp_send(skb, ICMP_PARAMETERPROB, 0, 16, skb->dev);
-             kfree_skb(skb, FREE_WRITE);
-             return -1;
-       }
-       if (ip_forward(skb, dev, is_frag, iph->daddr))
-               kfree_skb(skb, FREE_WRITE);
-#else
+       if (sysctl_ip_forward) {
+               if (opt && opt->is_strictroute) 
+               {
+                       icmp_send(skb, ICMP_PARAMETERPROB, 0, 16, skb->dev);
+                       kfree_skb(skb, FREE_WRITE);
+                       return -1;
+               }
+               if (ip_forward(skb, dev, is_frag, iph->daddr))
+                       kfree_skb(skb, FREE_WRITE);
+       } else {
 /*     printk("Machine %lx tried to use us as a forwarder to %lx but we have forwarding disabled!\n",
                        iph->saddr,iph->daddr);*/
-       ip_statistics.IpInAddrErrors++;
-       kfree_skb(skb, FREE_WRITE);
-#endif
+               ip_statistics.IpInAddrErrors++;
+               kfree_skb(skb, FREE_WRITE);
+       }
        return(0);
 }
        
index 5505a6fd5951743e2ff827c5906ed6d39efff698..69b784ee5d36d1611c89854fce60eb918f59bdd0 100644 (file)
  *     Juan Jose Ciarlante     :       Added hashed lookup by proto,maddr,mport and proto,saddr,sport
  *     Juan Jose Ciarlante     :       Fixed deadlock if free ports get exhausted
  *     Juan Jose Ciarlante     :       Added NO_ADDR status flag.
+ *     Richard Lynch           :       Added IP Autoforward
  *     Nigel Metheringham      :       Added ICMP handling for demasquerade
  *     Nigel Metheringham      :       Checksum checking of masqueraded data
  *     Nigel Metheringham      :       Better handling of timeouts of TCP conns
+ *     Keith Owens             :       Keep control channels alive if any related data entries.
+ *     Delian Delchev          :       Added support for ICMP requests and replys
+ *     Nigel Metheringham      :       ICMP in ICMP handling, tidy ups, bug fixes, made ICMP optional
  *
  *     
  */
@@ -37,6 +41,7 @@
 #include <net/udp.h>
 #include <net/checksum.h>
 #include <net/ip_masq.h>
+#include <linux/ip_fw.h>
 
 #define IP_MASQ_TAB_SIZE 256    /* must be power of 2 */
 
  *     Implement IP packet masquerading
  */
 
-static const char *strProt[] = {"UDP","TCP"};
+static const char *strProt[] = {"UDP","TCP","ICMP"};
 
-static __inline__ const char * masq_proto_name(unsigned proto)
+/*
+ * masq_proto_num returns 0 for UDP, 1 for TCP, 2 for ICMP
+ */
+
+static int masq_proto_num(unsigned proto)
+{
+   switch (proto)
+   {
+      case IPPROTO_UDP:  return (0); break;
+      case IPPROTO_TCP:  return (1); break;
+      case IPPROTO_ICMP: return (2); break;
+      default:           return (-1); break;
+   }
+}
+
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+/*
+ * Converts an ICMP reply code into the equivalent request code
+ */
+static __inline__ const __u8 icmp_type_request(__u8 type)
+{
+   switch (type)
+   {
+      case ICMP_ECHOREPLY: return ICMP_ECHO; break;
+      case ICMP_TIMESTAMPREPLY: return ICMP_TIMESTAMP; break;
+      case ICMP_INFO_REPLY: return ICMP_INFO_REQUEST; break;
+      case ICMP_ADDRESSREPLY: return ICMP_ADDRESS; break;
+      default: return (255); break;
+   }
+}
+
+/*
+ * Helper macros - attempt to make code clearer! 
+ */
+
+/* ID used in ICMP lookups */
+#define icmp_id(icmph)         ((icmph->un).echo.id)
+/* (port) hash value using in ICMP lookups for requests */
+#define icmp_hv_req(icmph)     ((__u16)(icmph->code+(__u16)(icmph->type<<8)))
+/* (port) hash value using in ICMP lookups for replies */
+#define icmp_hv_rep(icmph)     ((__u16)(icmph->code+(__u16)(icmp_type_request(icmph->type)<<8)))
+#endif
+
+static __inline__ const char *masq_proto_name(unsigned proto)
 {
-        return strProt[proto==IPPROTO_TCP];
+        return strProt[masq_proto_num(proto)];
 }
 
 /*
@@ -69,9 +117,10 @@ static __u16 masq_port = PORT_MASQ_BEGIN;
  *     
  */
 
-int ip_masq_free_ports[2] = {
+int ip_masq_free_ports[3] = {
         PORT_MASQ_END - PORT_MASQ_BEGIN,       /* UDP */
-        PORT_MASQ_END - PORT_MASQ_BEGIN        /* TCP */
+        PORT_MASQ_END - PORT_MASQ_BEGIN,       /* TCP */
+        PORT_MASQ_END - PORT_MASQ_BEGIN                /* ICMP */
 };
 
 static struct symbol_table ip_masq_syms = {
@@ -103,12 +152,108 @@ static struct ip_fw_masq ip_masq_dummy = {
 
 struct ip_fw_masq *ip_masq_expire = &ip_masq_dummy;
 
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+/*
+ *     Auto-forwarding table
+ */
+
+struct ip_autofw * ip_autofw_hosts = NULL;
+
+/*
+ *     Check if a masq entry should be created for a packet
+ */
+
+struct ip_autofw * ip_autofw_check_range (__u32 where, __u16 port, __u16 protocol, int reqact)
+{
+       struct ip_autofw *af;
+       af=ip_autofw_hosts;
+       port=ntohs(port);
+       while (af)
+       {
+               if (af->type==IP_FWD_RANGE && 
+                    port>=af->low && 
+                    port<=af->high && 
+                    protocol==af->protocol && 
+                    /* it's ok to create masq entries after the timeout if we're in insecure mode */
+                    (af->flags & IP_AUTOFW_ACTIVE || !reqact || !(af->flags & IP_AUTOFW_SECURE)) &&  
+                    (!(af->flags & IP_AUTOFW_SECURE) || af->lastcontact==where || !reqact))
+                       return(af);
+               af=af->next;
+       }
+       return(NULL);
+}
+
+struct ip_autofw * ip_autofw_check_port (__u16 port, __u16 protocol)
+{
+       struct ip_autofw *af;
+       af=ip_autofw_hosts;
+       port=ntohs(port);
+       while (af)
+       {
+               if (af->type==IP_FWD_PORT && port==af->visible && protocol==af->protocol)
+                       return(af);
+               af=af->next;
+       }
+       return(NULL);
+}
+
+struct ip_autofw * ip_autofw_check_direct (__u16 port, __u16 protocol)
+{
+       struct ip_autofw *af;
+       af=ip_autofw_hosts;
+       port=ntohs(port);
+       while (af)
+       {
+               if (af->type==IP_FWD_DIRECT && af->low<=port && af->high>=port)
+                       return(af);
+               af=af->next;
+       }
+       return(NULL);
+}
+
+void ip_autofw_update_out (__u32 who, __u32 where, __u16 port, __u16 protocol)
+{
+       struct ip_autofw *af;
+       af=ip_autofw_hosts;
+       port=ntohs(port);
+       while (af)
+       {
+               if (af->type==IP_FWD_RANGE && af->ctlport==port && af->ctlproto==protocol)
+               {
+                       if (af->flags & IP_AUTOFW_USETIME)
+                       {
+                               if (af->timer.expires)
+                                       del_timer(&af->timer);
+                               af->timer.expires=jiffies+IP_AUTOFW_EXPIRE;
+                               add_timer(&af->timer);
+                       }
+                       af->flags|=IP_AUTOFW_ACTIVE;
+                       af->lastcontact=where;
+                       af->where=who;
+               }
+               af=af->next;
+       }
+}
+
+void ip_autofw_update_in (__u32 where, __u16 port, __u16 protocol)
+{
+/*     struct ip_autofw *af;
+       af=ip_autofw_check_range(where, port,protocol);
+       if (af)
+       {
+               del_timer(&af->timer);
+               af->timer.expires=jiffies+IP_AUTOFW_EXPIRE;
+               add_timer(&af->timer);
+       }*/
+}
+
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+
 /*
  *     Returns hash value
  */
 
 static __inline__ unsigned
-
 ip_masq_hash_key(unsigned proto, __u32 addr, __u16 port)
 {
         return (proto^ntohl(addr)^ntohs(port)) & (IP_MASQ_TAB_SIZE-1);
@@ -219,7 +364,7 @@ ip_masq_in_get(struct iphdr *iph)
  *     broken out of the ip/tcp headers or directly supplied for those
  *     pathological protocols with address/port in the data stream
  *     (ftp, irc).  addresses and ports are in network order.
- *     called for pkts coming from INside-to-outside the firewall.
+ *     called for pkts coming from outside-to-INside the firewall.
  *
  *     NB. Cannot check destination address, just for the incoming port.
  *     reason: archie.doc.ac.uk has 6 interfaces, you send to
@@ -236,12 +381,33 @@ ip_masq_in_get_2(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16 d
 
         hash = ip_masq_hash_key(protocol, d_addr, d_port);
         for(ms = ip_masq_m_tab[hash]; ms ; ms = ms->m_link) {
-               if ( protocol==ms->protocol &&
-                   (s_addr==ms->daddr || ms->flags & IP_MASQ_F_NO_DADDR) &&
-                    (s_port==ms->dport || ms->flags & IP_MASQ_F_NO_DPORT) &&
-                    (d_addr==ms->maddr && d_port==ms->mport))
+               if (protocol==ms->protocol &&
+                   ((s_addr==ms->daddr || ms->flags & IP_MASQ_F_NO_DADDR)
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+                    || (ms->dport==htons(1558))
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+                    ) &&
+                   (s_port==ms->dport || ms->flags & IP_MASQ_F_NO_DPORT) &&
+                   (d_addr==ms->maddr && d_port==ms->mport)) {
+#ifdef DEBUG_IP_MASQUERADE_VERBOSE
+                       printk("MASQ: look/in %d %08X:%04hX->%08X:%04hX OK\n",
+                              protocol,
+                              s_addr,
+                              s_port,
+                              d_addr,
+                              d_port);
+#endif
                         return ms;
+               }
         }
+#ifdef DEBUG_IP_MASQUERADE_VERBOSE
+       printk("MASQ: look/in %d %08X:%04hX->%08X:%04hX fail\n",
+              protocol,
+              s_addr,
+              s_port,
+              d_addr,
+              d_port);
+#endif
         return NULL;
 }
 
@@ -274,6 +440,12 @@ ip_masq_out_get(struct iphdr *iph)
  *     pathological protocols with address/port in the data stream
  *     (ftp, irc).  addresses and ports are in network order.
  *     called for pkts coming from inside-to-OUTside the firewall.
+ *
+ *     Normally we know the source address and port but for some protocols
+ *     (e.g. ftp PASV) we do not know the source port initially.  Alas the
+ *     hash is keyed on source port so if the first lookup fails then try again
+ *     with a zero port, this time only looking at entries marked "no source
+ *     port".
  */
 
 struct ip_masq *
@@ -282,14 +454,48 @@ ip_masq_out_get_2(int protocol, __u32 s_addr, __u16 s_port, __u32 d_addr, __u16
         unsigned hash;
         struct ip_masq *ms;
 
+
         hash = ip_masq_hash_key(protocol, s_addr, s_port);
         for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) {
                if (protocol == ms->protocol &&
                    s_addr == ms->saddr && s_port == ms->sport &&
-                    d_addr == ms->daddr && d_port == ms->dport )
+                    d_addr == ms->daddr && d_port == ms->dport ) {
+#ifdef DEBUG_IP_MASQUERADE_VERBOSE
+                       printk("MASQ: lk/out1 %d %08X:%04hX->%08X:%04hX OK\n",
+                              protocol,
+                              s_addr,
+                              s_port,
+                              d_addr,
+                              d_port);
+#endif
                         return ms;
+               }
         }
-
+        hash = ip_masq_hash_key(protocol, s_addr, 0);
+        for(ms = ip_masq_s_tab[hash]; ms ; ms = ms->s_link) {
+               if (ms->flags & IP_MASQ_F_NO_SPORT &&
+                   protocol == ms->protocol &&
+                   s_addr == ms->saddr && 
+                    d_addr == ms->daddr && d_port == ms->dport ) {
+#ifdef DEBUG_IP_MASQUERADE_VERBOSE
+                       printk("MASQ: lk/out2 %d %08X:%04hX->%08X:%04hX OK\n",
+                              protocol,
+                              s_addr,
+                              s_port,
+                              d_addr,
+                              d_port);
+#endif
+                        return ms;
+               }
+        }
+#ifdef DEBUG_IP_MASQUERADE_VERBOSE
+       printk("MASQ: lk/out1 %d %08X:%04hX->%08X:%04hX fail\n",
+              protocol,
+              s_addr,
+              s_port,
+              d_addr,
+              d_port);
+#endif
         return NULL;
 }
 
@@ -315,41 +521,90 @@ ip_masq_getbym(int protocol, __u32 m_addr, __u16 m_port)
 
 static void masq_expire(unsigned long data)
 {
-       struct ip_masq *ms = (struct ip_masq *)data;
+       struct ip_masq *ms = (struct ip_masq *)data, *ms_data;
        unsigned long flags;
 
+       if (ms->flags & IP_MASQ_F_CONTROL) {
+               /* a control channel is about to expire */
+               int idx = 0, reprieve = 0;
 #ifdef DEBUG_CONFIG_IP_MASQUERADE
-       printk("Masqueraded %s %lX:%X expired\n",
-                       masq_proto_name(ms->protocol),
-                       ntohl(ms->saddr),ntohs(ms->sport));
+               printk("Masquerade control %s %lX:%X about to expire\n",
+                               masq_proto_name(ms->protocol),
+                               ntohl(ms->saddr),ntohs(ms->sport));
+#endif
+               save_flags(flags);
+               cli();
+
+               /*
+                * If any other masquerade entry claims that the expiring entry
+                * is its control channel then keep the control entry alive.
+                * Useful for long running data channels with inactive control
+                * links which we don't want to lose, e.g. ftp.
+                * Assumption: loops such as a->b->a or a->a will never occur.
+                */
+               for (idx = 0; idx < IP_MASQ_TAB_SIZE && !reprieve; idx++) {
+                       for (ms_data = ip_masq_m_tab[idx]; ms_data ; ms_data = ms_data->m_link) {
+                               if (ms_data->control == ms) {
+                                       reprieve = 1;   /* this control connection can live a bit longer */
+                                       ip_masq_set_expire(ms, ip_masq_expire->tcp_timeout);
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
+                                       printk("Masquerade control %s %lX:%X expiry reprieved\n",
+                                                       masq_proto_name(ms->protocol),
+                                                       ntohl(ms->saddr),ntohs(ms->sport));
+#endif
+                                       break;
+                               }
+                       }
+               }
+               restore_flags(flags);
+               if (reprieve)
+                       return;
+       }
+
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
+       printk("Masqueraded %s %lX:%X expired\n",masq_proto_name(ms->protocol),ntohl(ms->saddr),ntohs(ms->sport));
 #endif
        
        save_flags(flags);
        cli();
 
         if (ip_masq_unhash(ms)) {
-                ip_masq_free_ports[ms->protocol==IPPROTO_TCP]++;
-                ip_masq_unbind_app(ms);
+                ip_masq_free_ports[masq_proto_num(ms->protocol)]++;
+                if (ms->protocol != IPPROTO_ICMP)
+                             ip_masq_unbind_app(ms);
                 kfree_s(ms,sizeof(*ms));
         }
 
        restore_flags(flags);
 }
 
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+void ip_autofw_expire(unsigned long data)
+{
+       struct ip_autofw * af;
+       af=(struct ip_autofw *) data;
+       af->flags&=0xFFFF ^ IP_AUTOFW_ACTIVE;
+       af->timer.expires=0;
+       af->lastcontact=0;
+       if (af->flags & IP_AUTOFW_SECURE)
+               af->where=0;
+}
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+
 /*
  *     Create a new masquerade list entry, also allocate an
  *     unused mport, keeping the portnumber between the
  *     given boundaries MASQ_BEGIN and MASQ_END.
  */
 
-struct ip_masq * ip_masq_new(struct device *dev, int proto, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags)
+struct ip_masq * ip_masq_new_enh(struct device *dev, int proto, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags, __u16 matchport)
 {
         struct ip_masq *ms, *mst;
         int ports_tried, *free_ports_p;
        unsigned long flags;
         static int n_fails = 0;
 
-        free_ports_p = &ip_masq_free_ports[proto==IPPROTO_TCP];
+        free_ports_p = &ip_masq_free_ports[masq_proto_num(proto)];
 
         if (*free_ports_p == 0) {
                 if (++n_fails < 5)
@@ -375,6 +630,7 @@ struct ip_masq * ip_masq_new(struct device *dev, int proto, __u32 saddr, __u16 s
         ms->dport         = dport;
         ms->flags         = mflags;
         ms->app_data      = NULL;
+       ms->control        = NULL;
 
         if (proto == IPPROTO_UDP)
                 ms->flags |= IP_MASQ_F_NO_DADDR;
@@ -389,8 +645,11 @@ struct ip_masq * ip_masq_new(struct device *dev, int proto, __u32 saddr, __u16 s
                /*
                  *     Try the next available port number
                  */
-                
-               ms->mport = htons(masq_port++);
+                if (!matchport || ports_tried)
+                       ms->mport = htons(masq_port++);
+               else
+                       ms->mport = matchport;
+                       
                if (masq_port==PORT_MASQ_END) masq_port = PORT_MASQ_BEGIN;
                 
                 restore_flags(flags);
@@ -413,7 +672,8 @@ struct ip_masq * ip_masq_new(struct device *dev, int proto, __u32 saddr, __u16 s
                         
                         restore_flags(flags);
                         
-                        ip_masq_bind_app(ms);
+                        if (proto != IPPROTO_ICMP)
+                              ip_masq_bind_app(ms);
                         n_fails = 0;
                         return ms;
                 }
@@ -426,6 +686,11 @@ struct ip_masq * ip_masq_new(struct device *dev, int proto, __u32 saddr, __u16 s
         return NULL;
 }
 
+struct ip_masq * ip_masq_new(struct device *dev, int proto, __u32 saddr, __u16 sport, __u32 daddr, __u16 dport, unsigned mflags)
+{
+       return (ip_masq_new_enh(dev, proto, saddr, sport, daddr, dport, mflags, 0) );
+}
+
 /*
  *     Set masq expiration (deletion) and adds timer,
  *     if timeout==0 cancel expiration.
@@ -467,6 +732,8 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
         * We may need to consider masq-ing some ICMP related to masq-ed protocols
         */
 
+        if (iph->protocol==IPPROTO_ICMP) 
+            return (ip_fw_masq_icmp(skb_ptr,dev));
        if (iph->protocol!=IPPROTO_UDP && iph->protocol!=IPPROTO_TCP)
                return -1;
 
@@ -483,19 +750,59 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
 #endif
 
         ms = ip_masq_out_get(iph);
-        if (ms!=NULL)
+       if (ms!=NULL) {
                 ip_masq_set_expire(ms,0);
 
+               /*
+                *      Set sport if not defined yet (e.g. ftp PASV).  Because
+                *      masq entries are hashed on sport, unhash with old value
+                *      and hash with new.
+                */
+
+               if ( ms->flags & IP_MASQ_F_NO_SPORT && ms->protocol == IPPROTO_TCP ) {
+                       unsigned long flags;
+                       ms->flags &= ~IP_MASQ_F_NO_SPORT;
+                       save_flags(flags);
+                       cli();
+                       ip_masq_unhash(ms);
+                       ms->sport = portptr[0];
+                       ip_masq_hash(ms);       /* hash on new sport */
+                       restore_flags(flags);
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
+                       printk("ip_fw_masquerade(): filled sport=%d\n",
+                              ntohs(ms->sport));
+#endif
+               }
+       }
+
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+       /* update any ipautofw entries .. */
+       ip_autofw_update_out(iph->saddr, iph->daddr, portptr[1], 
+                            iph->protocol);
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+
        /*
         *      Nope, not found, create a new entry for it
         */
        
        if (ms==NULL)
        {
-                ms = ip_masq_new(dev, iph->protocol,
-                                 iph->saddr, portptr[0],
-                                 iph->daddr, portptr[1],
-                                 0);
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+               /* if the source port is supposed to match the masq port, then
+                  make it so */
+               if (ip_autofw_check_direct(portptr[1],iph->protocol))
+                       ms = ip_masq_new_enh(dev, iph->protocol,
+                                        iph->saddr, portptr[0],
+                                        iph->daddr, portptr[1],
+                                        0,
+                                        portptr[0]);
+                else
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+                       ms = ip_masq_new_enh(dev, iph->protocol,
+                                        iph->saddr, portptr[0],
+                                        iph->daddr, portptr[1],
+                                        0,
+                                        0);
                 if (ms == NULL)
                        return -1;
        }
@@ -530,7 +837,7 @@ int ip_fw_masquerade(struct sk_buff **skb_ptr, struct device *dev)
         *      Adjust packet accordingly to protocol
         */
        
-       if (iph->protocol==IPPROTO_UDP)
+       if (masq_proto_num(iph->protocol)==0)
        {
                 timeout = ip_masq_expire->udp_timeout;
                recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,size);
@@ -596,12 +903,63 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev)
        struct ip_masq  *ms;
        unsigned short   len   = ntohs(iph->tot_len) - (iph->ihl * 4);
 
-#ifdef DEBUG_CONFIG_IP_MASQUERADE
-       printk("Incoming forward ICMP (%d) %lX -> %lX\n",
-               icmph->type,
+#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP
+       printk("Incoming forward ICMP (%d,%d) %lX -> %lX\n",
+               icmph->type, ntohs(icmp_id(icmph)),
                ntohl(iph->saddr), ntohl(iph->daddr));
 #endif
 
+#ifdef CONFIG_IP_MASQUERADE_ICMP               
+       if ((icmph->type == ICMP_ECHO ) ||
+           (icmph->type == ICMP_TIMESTAMP ) ||
+           (icmph->type == ICMP_INFO_REQUEST ) ||
+           (icmph->type == ICMP_ADDRESS )) {
+#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP
+               printk("MASQ: icmp request rcv %lX->%lX id %d type %d\n",
+                      ntohl(iph->saddr),
+                      ntohl(iph->daddr),
+                      ntohs(icmp_id(icmph)),
+                      icmph->type);
+#endif
+               ms = ip_masq_out_get_2(iph->protocol,
+                                      iph->saddr,
+                                      icmp_id(icmph),
+                                      iph->daddr,
+                                      icmp_hv_req(icmph));
+               if (ms == NULL) {
+                       ms = ip_masq_new(dev,
+                                        iph->protocol,
+                                        iph->saddr,
+                                        icmp_id(icmph),
+                                        iph->daddr,
+                                        icmp_hv_req(icmph),
+                                        0);
+                       if (ms == NULL)
+                               return (-1);
+#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP
+                       printk("MASQ: Create new icmp entry\n");
+#endif               
+               }
+               ip_masq_set_expire(ms, 0);
+               /* Rewrite source address */
+               iph->saddr = ms->maddr;
+               ip_send_check(iph);
+               /* Rewrite port (id) */
+               (icmph->un).echo.id = ms->mport;
+               icmph->checksum = 0;
+               icmph->checksum = ip_compute_csum((unsigned char *)icmph, len);
+               ip_masq_set_expire(ms, MASQUERADE_EXPIRE_ICMP);
+#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP
+               printk("MASQ: icmp request rwt %lX->%lX id %d type %d\n",
+                      ntohl(iph->saddr),
+                      ntohl(iph->daddr),
+                      ntohs(icmp_id(icmph)),
+                      icmph->type);
+#endif
+               return (1);
+       }
+#endif
+
        /* 
         * Work through seeing if this is for us.
         * These checks are supposed to be in an order that
@@ -618,6 +976,57 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev)
        /* Now find the contained IP header */
        ciph = (struct iphdr *) (icmph + 1);
 
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+       if (ciph->protocol == IPPROTO_ICMP) {
+               /*
+                * This section handles ICMP errors for ICMP packets
+                */
+               struct icmphdr  *cicmph = (struct icmphdr *)((char *)ciph + 
+                                                            (ciph->ihl<<2));
+
+#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP
+               printk("MASQ: fw icmp/icmp rcv %lX->%lX id %d type %d\n",
+                      ntohl(ciph->saddr),
+                      ntohl(ciph->daddr),
+                      ntohs(icmp_id(cicmph)),
+                      cicmph->type);
+#endif
+               ms = ip_masq_out_get_2(ciph->protocol, 
+                                     ciph->daddr,
+                                     icmp_id(cicmph),
+                                     ciph->saddr,
+                                     icmp_hv_rep(cicmph));
+
+               if (ms == NULL)
+                       return 0;
+
+               /* Now we do real damage to this packet...! */
+               /* First change the source IP address, and recalc checksum */
+               iph->saddr = ms->maddr;
+               ip_send_check(iph);
+       
+               /* Now change the *dest* address in the contained IP */
+               ciph->daddr = ms->maddr;
+               ip_send_check(ciph);
+
+               /* Change the ID to the masqed one! */
+               (cicmph->un).echo.id = ms->mport;
+       
+               /* And finally the ICMP checksum */
+               icmph->checksum = 0;
+               icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
+
+#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP
+               printk("MASQ: fw icmp/icmp rwt %lX->%lX id %d type %d\n",
+                      ntohl(ciph->saddr),
+                      ntohl(ciph->daddr),
+                      ntohs(icmp_id(cicmph)),
+                      cicmph->type);
+#endif
+               return 1;
+       }
+#endif /* CONFIG_IP_MASQUERADE_ICMP */
+
        /* We are only interested ICMPs generated from TCP or UDP packets */
        if ((ciph->protocol != IPPROTO_UDP) && (ciph->protocol != IPPROTO_TCP))
                return 0;
@@ -628,9 +1037,6 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev)
         * (but reversed relative to outer IP header!)
         */
        pptr = (__u16 *)&(((char *)ciph)[ciph->ihl*4]);
-       if (ntohs(pptr[1]) < PORT_MASQ_BEGIN ||
-           ntohs(pptr[1]) > PORT_MASQ_END)
-               return 0;
 
        /* Ensure the checksum is correct */
        if (ip_compute_csum((unsigned char *) icmph, len)) 
@@ -642,13 +1048,17 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev)
        }
 
 #ifdef DEBUG_CONFIG_IP_MASQUERADE
-       printk("Handling forward ICMP for %lX:%X -> %lX:%X\n",
+       printk("Handling forward ICMP for %lX:%X -> %lX:%X\n",
               ntohl(ciph->saddr), ntohs(pptr[0]),
               ntohl(ciph->daddr), ntohs(pptr[1]));
 #endif
 
-       /* This is pretty much what ip_masq_in_get() does */
-       ms = ip_masq_in_get_2(ciph->protocol, ciph->saddr, pptr[0], ciph->daddr, pptr[1]);
+       /* This is pretty much what ip_masq_out_get() does */
+       ms = ip_masq_out_get_2(ciph->protocol, 
+                              ciph->daddr, 
+                              pptr[1], 
+                              ciph->saddr, 
+                              pptr[0]);
 
        if (ms == NULL)
                return 0;
@@ -670,7 +1080,7 @@ int ip_fw_masq_icmp(struct sk_buff **skb_p, struct device *dev)
        icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
 
 #ifdef DEBUG_CONFIG_IP_MASQUERADE
-       printk("Rewrote forward ICMP to %lX:%X -> %lX:%X\n",
+       printk("Rewrote forward ICMP to %lX:%X -> %lX:%X\n",
               ntohl(ciph->saddr), ntohs(pptr[0]),
               ntohl(ciph->daddr), ntohs(pptr[1]));
 #endif
@@ -695,22 +1105,124 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p, struct device *dev)
        struct ip_masq  *ms;
        unsigned short   len   = ntohs(iph->tot_len) - (iph->ihl * 4);
 
-#ifdef DEBUG_CONFIG_IP_MASQUERADE
-       printk("Incoming reverse ICMP (%d) %lX -> %lX\n",
-               icmph->type,
+#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP
+       printk("MASQ: icmp in/rev (%d,%d) %lX -> %lX\n",
+               icmph->type, ntohs(icmp_id(icmph)),
                ntohl(iph->saddr), ntohl(iph->daddr));
 #endif
 
-       if ((icmph->type != ICMP_DEST_UNREACH) &&
-           (icmph->type != ICMP_SOURCE_QUENCH) &&
-           (icmph->type != ICMP_TIME_EXCEEDED))
-               return 0;
+#ifdef CONFIG_IP_MASQUERADE_ICMP               
+       if ((icmph->type == ICMP_ECHOREPLY) ||
+           (icmph->type == ICMP_TIMESTAMPREPLY) ||
+           (icmph->type == ICMP_INFO_REPLY) ||
+           (icmph->type == ICMP_ADDRESSREPLY)) {
+#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP
+               printk("MASQ: icmp reply rcv %lX->%lX id %d type %d, req %d\n",
+                      ntohl(iph->saddr),
+                      ntohl(iph->daddr),
+                      ntohs(icmp_id(icmph)),
+                      icmph->type,
+                      icmp_type_request(icmph->type));
+#endif
+               ms = ip_masq_in_get_2(iph->protocol,
+                                     iph->saddr,
+                                     icmp_hv_rep(icmph),
+                                     iph->daddr,
+                                     icmp_id(icmph));
+               if (ms == NULL)
+                       return 0;
 
-       /* Now find the contained IP header */
+               ip_masq_set_expire(ms,0);
+
+               /* Reset source address */
+               iph->daddr = ms->saddr;
+               /* Redo IP header checksum */
+               ip_send_check(iph);
+               /* Set ID to fake port number */
+               (icmph->un).echo.id = ms->sport;
+               /* Reset ICMP checksum and set expiry */
+               icmph->checksum=0;
+               icmph->checksum=ip_compute_csum((unsigned char *)icmph,len);
+               ip_masq_set_expire(ms, MASQUERADE_EXPIRE_ICMP);
+#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP
+               printk("MASQ: icmp reply rwt %lX->%lX id %d type %d\n",
+                      ntohl(iph->saddr),
+                      ntohl(iph->daddr),
+                      ntohs(icmp_id(icmph)),
+                      icmph->type);
+#endif
+               return 1;
+       } else {
+#endif
+               if ((icmph->type != ICMP_DEST_UNREACH) &&
+                   (icmph->type != ICMP_SOURCE_QUENCH) &&
+                   (icmph->type != ICMP_TIME_EXCEEDED))
+                       return 0;
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+       }
+#endif
+       /*
+        * If we get here we have an ICMP error of one of the above 3 types
+        * Now find the contained IP header
+        */
        ciph = (struct iphdr *) (icmph + 1);
 
+#ifdef CONFIG_IP_MASQUERADE_ICMP
+       if (ciph->protocol == IPPROTO_ICMP) {
+               /*
+                * This section handles ICMP errors for ICMP packets
+                *
+                * First get a new ICMP header structure out of the IP packet
+                */
+               struct icmphdr  *cicmph = (struct icmphdr *)((char *)ciph + 
+                                                            (ciph->ihl<<2));
+
+#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP
+               printk("MASQ: rv icmp/icmp rcv %lX->%lX id %d type %d\n",
+                      ntohl(ciph->saddr),
+                      ntohl(ciph->daddr),
+                      ntohs(icmp_id(cicmph)),
+                      cicmph->type);
+#endif
+               ms = ip_masq_in_get_2(ciph->protocol, 
+                                     ciph->daddr, 
+                                     icmp_hv_req(cicmph),
+                                     ciph->saddr, 
+                                     icmp_id(cicmph));
+
+               if (ms == NULL)
+                       return 0;
+
+               /* Now we do real damage to this packet...! */
+               /* First change the dest IP address, and recalc checksum */
+               iph->daddr = ms->saddr;
+               ip_send_check(iph);
+       
+               /* Now change the *source* address in the contained IP */
+               ciph->saddr = ms->saddr;
+               ip_send_check(ciph);
+
+               /* Change the ID to the original one! */
+               (cicmph->un).echo.id = ms->sport;
+
+               /* And finally the ICMP checksum */
+               icmph->checksum = 0;
+               icmph->checksum = ip_compute_csum((unsigned char *) icmph, len);
+
+#ifdef DEBUG_CONFIG_IP_MASQUERADE_ICMP
+               printk("MASQ: rv icmp/icmp rwt %lX->%lX id %d type %d\n",
+                      ntohl(ciph->saddr),
+                      ntohl(ciph->daddr),
+                      ntohs(icmp_id(cicmph)),
+                      cicmph->type);
+#endif
+               return 1;
+       }
+#endif /* CONFIG_IP_MASQUERADE_ICMP */
+
        /* We are only interested ICMPs generated from TCP or UDP packets */
-       if ((ciph->protocol != IPPROTO_UDP) && (ciph->protocol != IPPROTO_TCP))
+       if ((ciph->protocol != IPPROTO_UDP) && 
+           (ciph->protocol != IPPROTO_TCP))
                return 0;
 
        /* 
@@ -738,7 +1250,11 @@ int ip_fw_demasq_icmp(struct sk_buff **skb_p, struct device *dev)
 #endif
 
        /* This is pretty much what ip_masq_in_get() does, except params are wrong way round */
-       ms = ip_masq_in_get_2(ciph->protocol, ciph->daddr, pptr[1], ciph->saddr, pptr[0]);
+       ms = ip_masq_in_get_2(ciph->protocol,
+                             ciph->daddr,
+                             pptr[1],
+                             ciph->saddr,
+                             pptr[0]);
 
        if (ms == NULL)
                return 0;
@@ -786,6 +1302,9 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
        struct ip_masq  *ms;
        unsigned short len;
        unsigned long   timeout;
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW 
+       struct ip_autofw *af;
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
 
        switch (iph->protocol) {
        case IPPROTO_ICMP:
@@ -794,9 +1313,17 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
        case IPPROTO_UDP:
                /* Make sure packet is in the masq range */
                portptr = (__u16 *)&(((char *)iph)[iph->ihl*4]);
-               if (ntohs(portptr[1]) < PORT_MASQ_BEGIN ||
-                   ntohs(portptr[1]) > PORT_MASQ_END)
+               if ((ntohs(portptr[1]) < PORT_MASQ_BEGIN ||
+                    ntohs(portptr[1]) > PORT_MASQ_END)
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW 
+                   && !ip_autofw_check_range(iph->saddr, portptr[1], 
+                                             iph->protocol, 0)
+                   && !ip_autofw_check_direct(portptr[1], iph->protocol)
+                   && !ip_autofw_check_port(portptr[1], iph->protocol)
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+                       )
                        return 0;
+
                /* Check that the checksum is OK */
                len = ntohs(iph->tot_len) - (iph->ihl * 4);
                if ((iph->protocol == IPPROTO_UDP) && (portptr[3] == 0))
@@ -836,8 +1363,31 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
 
         ms = ip_masq_in_get(iph);
 
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW 
+        if (ms == NULL && (af=ip_autofw_check_range(iph->saddr, portptr[1],
+                                                   iph->protocol, 0))) {
+               ms = ip_masq_new_enh(dev, iph->protocol,
+                                    af->where, portptr[1],
+                                    iph->saddr, portptr[0],
+                                    0,
+                                    portptr[1]);
+        }
+        if ( ms == NULL && (af=ip_autofw_check_port(portptr[1], 
+                                                   iph->protocol)) ) {
+               ms = ip_masq_new_enh(dev, iph->protocol,
+                                    af->where, htons(af->hidden),
+                                    iph->saddr, portptr[0],
+                                    0,
+                                    htons(af->visible));
+        }
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+
         if (ms != NULL)
         {
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW 
+               ip_autofw_update_in(iph->saddr, portptr[1], iph->protocol);
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+               
                /* Stop the timer ticking.... */
                ip_masq_set_expire(ms,0);
 
@@ -848,7 +1398,7 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
                 if ( ms->flags & IP_MASQ_F_NO_DPORT && ms->protocol == IPPROTO_TCP ) {
                         ms->flags &= ~IP_MASQ_F_NO_DPORT;
                         ms->dport = portptr[0];
-#if DEBUG_CONFIG_IP_MASQUERADE
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
                         printk("ip_fw_demasquerade(): filled dport=%d\n",
                                ntohs(ms->dport));
 #endif
@@ -856,7 +1406,7 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
                 if (ms->flags & IP_MASQ_F_NO_DADDR && ms->protocol == IPPROTO_TCP)  {
                         ms->flags &= ~IP_MASQ_F_NO_DADDR;
                         ms->daddr = iph->saddr;
-#if DEBUG_CONFIG_IP_MASQUERADE
+#ifdef DEBUG_CONFIG_IP_MASQUERADE
                         printk("ip_fw_demasquerade(): filled daddr=%X\n",
                                ntohs(ms->daddr));
 #endif
@@ -886,7 +1436,7 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
                 * timeouts.
                 * If a TCP RST is seen collapse the tunnel (by using short timeout)!
                  */
-                if (iph->protocol==IPPROTO_UDP)
+                if (masq_proto_num(iph->protocol)==0)
                {
                         recalc_check((struct udphdr *)portptr,iph->saddr,iph->daddr,len);
                        timeout = ip_masq_expire->udp_timeout;
@@ -933,9 +1483,54 @@ int ip_fw_demasquerade(struct sk_buff **skb_p, struct device *dev)
 }
 
 /*
- *     /proc/net entry
+ *     /proc/net entries
  */
 
+
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+
+static int ip_autofw_procinfo(char *buffer, char **start, off_t offset,
+                             int length, int unused)
+{
+       off_t pos=0, begin=0;
+       struct ip_autofw * af;
+       int len=0;
+       
+       len=sprintf(buffer,"Type Prot Low  High Vis  Hid  Where    Last     CPto CPrt Timer Flags\n"); 
+        
+        for(af = ip_autofw_hosts; af ; af = af->next)
+       {
+               len+=sprintf(buffer+len,"%4X %4X %04X-%04X/%04X %04X %08lX %08lX %04X %04X %6lu %4X\n",
+                                       af->type,
+                                       af->protocol,
+                                       af->low,
+                                       af->high,
+                                       af->visible,
+                                       af->hidden,
+                                       ntohl(af->where),
+                                       ntohl(af->lastcontact),
+                                       af->ctlproto,
+                                       af->ctlport,
+                                       (af->timer.expires<jiffies ? 0 : af->timer.expires-jiffies), 
+                                       af->flags);
+
+               pos=begin+len;
+               if(pos<offset) 
+               {
+                       len=0;
+                       begin=pos;
+               }
+               if(pos>offset+length)
+                       break;
+        }
+       *start=buffer+(offset-begin);
+       len-=(offset-begin);
+       if(len>length)
+               len=length;
+       return len;
+}
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
+
 static int ip_msqhst_procinfo(char *buffer, char **start, off_t offset,
                              int length, int unused)
 {
@@ -1007,6 +1602,14 @@ int ip_masq_init(void)
                0, &proc_net_inode_operations,
                ip_msqhst_procinfo
        });
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+       proc_net_register(&(struct proc_dir_entry) {
+               PROC_NET_IPAUTOFW, 9, "ip_autofw",
+               S_IFREG | S_IRUGO, 1, 0, 0,
+               0, &proc_net_inode_operations,
+               ip_autofw_procinfo
+       });
+#endif /* CONFIG_IP_MASQUERADE_IPAUTOFW */
 #endif 
         ip_masq_app_init();
 
diff --git a/net/ipv4/ip_masq_cuseeme.c b/net/ipv4/ip_masq_cuseeme.c
new file mode 100644 (file)
index 0000000..3566fbd
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ *             IP_MASQ_FTP CUSeeMe masquerading module
+ *
+ *
+ * Version:    @(#)ip_masq_cuseeme.c 0.02   07/23/96
+ *
+ * Author:     Richard Lynch
+ *             
+ *
+ * Fixes:
+ *     Richard Lynch           :       Updated patch to conform to new module
+ *                                     specifications
+ *     Nigel Metheringham      :       Multiple port support
+ *     
+ *
+ *
+ *     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.
+ *     
+ * Multiple Port Support
+ *     The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12)
+ *     with the port numbers being defined at module load time.  The module
+ *     uses the symbol "ports" to define a list of monitored ports, which can
+ *     be specified on the insmod command line as
+ *             ports=x1,x2,x3...
+ *     where x[n] are integer port numbers.  This option can be put into
+ *     /etc/conf.modules (or /etc/modules.conf depending on your config)
+ *     where modload will pick it up should you use modload to load your
+ *     modules.
+ *     
+ */
+
+#include <linux/module.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <net/protocol.h>
+#include <net/udp.h>
+#include <net/ip_masq.h>
+
+#ifndef DEBUG_CONFIG_IP_MASQ_CUSEEME
+#define DEBUG_CONFIG_IP_MASQ_CUSEEME 0
+#endif
+
+/* 
+ * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
+ * First port is set to the default port.
+ */
+int ports[MAX_MASQ_APP_PORTS] = {7648}; /* I rely on the trailing items being set to zero */
+struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
+
+static int
+masq_cuseeme_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+        MOD_INC_USE_COUNT;
+        return 0;
+}
+
+static int
+masq_cuseeme_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+        MOD_DEC_USE_COUNT;
+        return 0;
+}
+
+int
+masq_cuseeme_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev)
+{
+       struct sk_buff *skb = *skb_p;
+       struct iphdr *iph = skb->h.iph;
+       struct udphdr *uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+       struct cu_header {
+               char dest[8];
+               short family;
+               u_short port;
+               u_long addr;
+       } *cu_head;
+       char *data=(char *)&uh[1];
+       
+       if (skb->len - ((unsigned char *) data - skb->h.raw) > 16)
+       {
+               cu_head         = (struct cu_header *) data;
+/*             printk("CUSeeMe orig: %lX:%X\n",ntohl(cu_head->addr),ntohs(cu_head->port));*/
+               cu_head->port   = ms->mport;
+               cu_head->addr = (u_long) dev->pa_addr;
+       }
+       return 0;
+}
+
+struct ip_masq_app ip_masq_cuseeme = {
+        NULL,                  /* next */
+        "cuseeme",
+        0,                      /* type */
+        0,                      /* n_attach */
+        masq_cuseeme_init_1,   /* ip_masq_init_1 */
+        masq_cuseeme_done_1,   /* ip_masq_done_1 */
+        masq_cuseeme_out,      /* pkt_out */
+        NULL                    /* pkt_in */
+};
+
+
+/*
+ *     ip_masq_cuseeme initialization
+ */
+
+int ip_masq_cuseeme_init(void)
+{
+       int i, j;
+
+       for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+               if (ports[i]) {
+                       if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
+                                                           GFP_KERNEL)) == NULL)
+                               return -ENOMEM;
+                       memcpy(masq_incarnations[i], &ip_masq_cuseeme, sizeof(struct ip_masq_app));
+                       if ((j = register_ip_masq_app(masq_incarnations[i], 
+                                                     IPPROTO_TCP, 
+                                                     ports[i]))) {
+                               return j;
+                       }
+#if DEBUG_CONFIG_IP_MASQ_CUSEEME
+                       printk("CuSeeMe: loaded support on port[%d] = %d\n",
+                              i, ports[i]);
+#endif
+               } else {
+                       /* To be safe, force the incarnation table entry to NULL */
+                       masq_incarnations[i] = NULL;
+               }
+       }
+       return 0;
+}
+
+/*
+ *     ip_masq_cuseeme fin.
+ */
+
+int ip_masq_cuseeme_done(void)
+{
+       int i, j, k;
+
+       k=0;
+       for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+               if (masq_incarnations[i]) {
+                       if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
+                               k = j;
+                       } else {
+                               kfree(masq_incarnations[i]);
+                               masq_incarnations[i] = NULL;
+#if DEBUG_CONFIG_IP_MASQ_CUSEEME
+                               printk("CuSeeMe: unloaded support on port[%d] = %d\n",
+                                      i, ports[i]);
+#endif
+                       }
+               }
+       }
+       return k;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+        if (ip_masq_cuseeme_init() != 0)
+                return -EIO;
+        register_symtab(0);
+        return 0;
+}
+
+void cleanup_module(void)
+{
+        if (ip_masq_cuseeme_done() != 0)
+                printk("ip_masq_cuseeme: can't remove module");
+}
+
+#endif /* MODULE */
index 75fbb01a7ce556bb7fab8586bf8767f711065332..87a019da209e1272c0f6d858c4dfee02408cb6ba 100644 (file)
@@ -10,6 +10,8 @@
  * Fixes:
  *     Wouter Gadeyne          :       Fixed masquerading support of ftp PORT commands
  *     Juan Jose Ciarlante     :       Code moved and adapted from ip_fw.c
+ *     Keith Owens             :       Add keep alive for ftp control channel
+ *     Nigel Metheringham      :       Added multiple port support
  *     
  *
  *
  *     as published by the Free Software Foundation; either version
  *     2 of the License, or (at your option) any later version.
  *     
+ * Multiple Port Support
+ *     The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12)
+ *     with the port numbers being defined at module load time.  The module
+ *     uses the symbol "ports" to define a list of monitored ports, which can
+ *     be specified on the insmod command line as
+ *             ports=x1,x2,x3...
+ *     where x[n] are integer port numbers.  This option can be put into
+ *     /etc/conf.modules (or /etc/modules.conf depending on your config)
+ *     where modload will pick it up should you use modload to load your
+ *     modules.
+ *     
  */
 
 #include <linux/module.h>
 #include <net/tcp.h>
 #include <net/ip_masq.h>
 
+#ifndef DEBUG_CONFIG_IP_MASQ_FTP
 #define DEBUG_CONFIG_IP_MASQ_FTP 0
+#endif
+
+/* 
+ * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
+ * First port is set to the default port.
+ */
+int ports[MAX_MASQ_APP_PORTS] = {21}; /* I rely on the trailing items being set to zero */
+struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
 
 static int
 masq_ftp_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
@@ -66,8 +88,9 @@ masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
        iph = skb->h.iph;
         th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
         data = (char *)&th[1];
-
         data_limit = skb->h.raw + skb->len - 18;
+        if (skb->len >= 6 && (memcmp(data, "PASV\r\n", 6) == 0 || memcmp(data, "pasv\r\n", 6) == 0))
+               ms->flags |= IP_MASQ_F_FTP_PASV;
 
        while (data < data_limit)
        {
@@ -123,6 +146,8 @@ masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
                                        
                        if (n_ms==NULL)
                                return 0;
+                       n_ms->control = ms;             /* keepalive from data to the control channel */
+                       ms->flags |= IP_MASQ_F_CONTROL; /* this is a control channel */
                }
 
                 /*
@@ -171,6 +196,108 @@ masq_ftp_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb
 
 }
 
+/*
+ * Look at incoming ftp packets to catch the response to a PASV command.  When
+ * we see one we build a masquerading entry for the client address, client port
+ * 0 (unknown at the moment), the server address and the server port.  Mark the
+ * current masquerade entry as a control channel and point the new entry at the
+ * control entry.  All this work just for ftp keepalive across masquerading.
+ *
+ * The incoming packet should be something like
+ * "227 Entering Passive Mode (xxx,xxx,xxx,xxx,ppp,ppp)".
+ * xxx,xxx,xxx,xxx is the server address, ppp,ppp is the server port number.
+ * ncftp 2.3.0 cheats by skipping the leading number then going 22 bytes into
+ * the data so we do the same.  If it's good enough for ncftp then it's good
+ * enough for me.
+ *
+ * In this case, the client is the source machine being masqueraded, the server
+ * is the destination for ftp requests.  It all depends on your point of view ...
+ */
+
+int
+masq_ftp_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev)
+{
+       struct sk_buff *skb;
+       struct iphdr *iph;
+       struct tcphdr *th;
+       char *data, *data_limit;
+       unsigned char p1,p2,p3,p4,p5,p6;
+       __u32 to;
+       __u16 port;
+       struct ip_masq *n_ms;
+
+       if (! ms->flags & IP_MASQ_F_FTP_PASV)
+               return 0;       /* quick exit if no outstanding PASV */
+
+       skb = *skb_p;
+       iph = skb->h.iph;
+       th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+       data = (char *)&th[1];
+       data_limit = skb->h.raw + skb->len;
+
+       while (data < data_limit && *data != ' ')
+               ++data; 
+       while (data < data_limit && *data == ' ')
+               ++data; 
+       data += 22;
+       if (data >= data_limit || *data != '(')
+               return 0;
+       p1 = simple_strtoul(data+1, &data, 10);
+       if (data >= data_limit || *data != ',')
+               return 0;
+       p2 = simple_strtoul(data+1, &data, 10);
+       if (data >= data_limit || *data != ',')
+               return 0;
+       p3 = simple_strtoul(data+1, &data, 10);
+       if (data >= data_limit || *data != ',')
+               return 0;
+       p4 = simple_strtoul(data+1, &data, 10);
+       if (data >= data_limit || *data != ',')
+               return 0;
+       p5 = simple_strtoul(data+1, &data, 10);
+       if (data >= data_limit || *data != ',')
+               return 0;
+       p6 = simple_strtoul(data+1, &data, 10);
+       if (data >= data_limit || *data != ')')
+               return 0;
+
+       to = (p1<<24) | (p2<<16) | (p3<<8) | p4;
+       port = (p5<<8) | p6;
+
+       /*
+        * Now update or create an masquerade entry for it
+        */
+#if DEBUG_CONFIG_IP_MASQ_FTP
+       printk("PASV response %lX:%X %X:%X detected\n", ntohl(ms->saddr), 0, to, port);
+#endif 
+       n_ms = ip_masq_out_get_2(iph->protocol,
+                                ms->saddr, 0,
+                                htonl(to), htons(port));
+       if (n_ms) {
+               /* existing masquerade, clear timer */
+               ip_masq_set_expire(n_ms,0);
+       }
+       else {
+               n_ms = ip_masq_new(dev, IPPROTO_TCP,
+                                  ms->saddr, 0,
+                                  htonl(to), htons(port),
+                                  IP_MASQ_F_NO_SPORT);
+
+               if (n_ms==NULL)
+                       return 0;
+               n_ms->control = ms;             /* keepalive from data to the control channel */
+               ms->flags |= IP_MASQ_F_CONTROL; /* this is a control channel */
+       }
+
+       /*
+        * keep for a bit longer than tcp_fin, client may not issue open
+        * to server port before tcp_fin_timeout.
+        */
+       ip_masq_set_expire(n_ms, ip_masq_expire->tcp_fin_timeout*3);
+       ms->flags &= ~IP_MASQ_F_FTP_PASV;
+       return 0;       /* no diff required for incoming packets, thank goodness */
+}
+
 struct ip_masq_app ip_masq_ftp = {
         NULL,                  /* next */
        "ftp",                  /* name */
@@ -179,7 +306,7 @@ struct ip_masq_app ip_masq_ftp = {
         masq_ftp_init_1,        /* ip_masq_init_1 */
         masq_ftp_done_1,        /* ip_masq_done_1 */
         masq_ftp_out,           /* pkt_out */
-        NULL                    /* pkt_in */
+        masq_ftp_in,            /* pkt_in */
 };
 
 /*
@@ -188,7 +315,29 @@ struct ip_masq_app ip_masq_ftp = {
 
 int ip_masq_ftp_init(void)
 {
-        return register_ip_masq_app(&ip_masq_ftp, IPPROTO_TCP, 21);
+       int i, j;
+
+       for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+               if (ports[i]) {
+                       if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
+                                                           GFP_KERNEL)) == NULL)
+                               return -ENOMEM;
+                       memcpy(masq_incarnations[i], &ip_masq_ftp, sizeof(struct ip_masq_app));
+                       if ((j = register_ip_masq_app(masq_incarnations[i], 
+                                                     IPPROTO_TCP, 
+                                                     ports[i]))) {
+                               return j;
+                       }
+#if DEBUG_CONFIG_IP_MASQ_FTP
+                       printk("Ftp: loaded support on port[%d] = %d\n",
+                              i, ports[i]);
+#endif
+               } else {
+                       /* To be safe, force the incarnation table entry to NULL */
+                       masq_incarnations[i] = NULL;
+               }
+       }
+       return 0;
 }
 
 /*
@@ -197,7 +346,24 @@ int ip_masq_ftp_init(void)
 
 int ip_masq_ftp_done(void)
 {
-        return unregister_ip_masq_app(&ip_masq_ftp);
+       int i, j, k;
+
+       k=0;
+       for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+               if (masq_incarnations[i]) {
+                       if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
+                               k = j;
+                       } else {
+                               kfree(masq_incarnations[i]);
+                               masq_incarnations[i] = NULL;
+#if DEBUG_CONFIG_IP_MASQ_FTP
+                               printk("Ftp: unloaded support on port[%d] = %d\n",
+                                      i, ports[i]);
+#endif
+                       }
+               }
+       }
+       return k;
 }
 
 #ifdef MODULE
index 4bb93e5d13e80fc1c76253506c7cd7a8279093a0..90544283eaa9f5c66d5448aefc6c02e347b98bfd 100644 (file)
@@ -9,6 +9,7 @@
  *
  * Fixes:
  *     - set NO_DADDR flag in ip_masq_new().
+ *     - Added multiple port support (Nigel Metheringham)
  *
  * FIXME:
  *     - detect also previous "PRIVMSG" string ?.
  *     as published by the Free Software Foundation; either version
  *     2 of the License, or (at your option) any later version.
  *     
+ * Multiple Port Support
+ *     The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12)
+ *     with the port numbers being defined at module load time.  The module
+ *     uses the symbol "ports" to define a list of monitored ports, which can
+ *     be specified on the insmod command line as
+ *             ports=x1,x2,x3...
+ *     where x[n] are integer port numbers.  This option can be put into
+ *     /etc/conf.modules (or /etc/modules.conf depending on your config)
+ *     where modload will pick it up should you use modload to load your
+ *     modules.
+ *     
  */
 
 #include <linux/module.h>
 #include <net/tcp.h>
 #include <net/ip_masq.h>
 
+#ifndef DEBUG_CONFIG_IP_MASQ_IRC
 #define DEBUG_CONFIG_IP_MASQ_IRC 0
+#endif
+
+/* 
+ * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
+ * First port is set to the default port.
+ */
+int ports[MAX_MASQ_APP_PORTS] = {6667}; /* I rely on the trailing items being set to zero */
+struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
 
 static int
 masq_irc_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
@@ -239,7 +260,29 @@ struct ip_masq_app ip_masq_irc = {
 
 int ip_masq_irc_init(void)
 {
-        return register_ip_masq_app(&ip_masq_irc, IPPROTO_TCP, 6667);
+       int i, j;
+
+       for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+               if (ports[i]) {
+                       if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
+                                                           GFP_KERNEL)) == NULL)
+                               return -ENOMEM;
+                       memcpy(masq_incarnations[i], &ip_masq_irc, sizeof(struct ip_masq_app));
+                       if ((j = register_ip_masq_app(masq_incarnations[i], 
+                                                     IPPROTO_TCP, 
+                                                     ports[i]))) {
+                               return j;
+                       }
+#if DEBUG_CONFIG_IP_MASQ_IRC
+                       printk("Irc: loaded support on port[%d] = %d\n",
+                              i, ports[i]);
+#endif
+               } else {
+                       /* To be safe, force the incarnation table entry to NULL */
+                       masq_incarnations[i] = NULL;
+               }
+       }
+       return 0;
 }
 
 /*
@@ -248,9 +291,27 @@ int ip_masq_irc_init(void)
 
 int ip_masq_irc_done(void)
 {
-        return unregister_ip_masq_app(&ip_masq_irc);
+       int i, j, k;
+
+       k=0;
+       for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+               if (masq_incarnations[i]) {
+                       if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
+                               k = j;
+                       } else {
+                               kfree(masq_incarnations[i]);
+                               masq_incarnations[i] = NULL;
+#if DEBUG_CONFIG_IP_MASQ_IRC
+                               printk("Irc: unloaded support on port[%d] = %d\n",
+                                      i, ports[i]);
+#endif
+                       }
+               }
+       }
+       return k;
 }
 
+
 #ifdef MODULE
 
 int init_module(void)
diff --git a/net/ipv4/ip_masq_quake.c b/net/ipv4/ip_masq_quake.c
new file mode 100644 (file)
index 0000000..2ce3846
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ *             IP_MASQ_QUAKE quake masquerading module
+ *
+ *
+ * Version:    @(#)ip_masq_quake.c 1.00   22/02/97
+ *
+ * Author:     Harald Hoyer mailto:HarryH@Royal.Net
+ *             
+ *
+ * Fixes: 
+ *      Harald Hoyer            :       Unofficial Quake Specs found at 
+ *                                 http://www.gamers.org/dEngine/quake/spec/ 
+ *      Harald Hoyer            :       Check for QUAKE-STRING
+ *
+ *     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.
+ *  
+ *  
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <net/protocol.h>
+#include <net/udp.h>
+#include <net/ip_masq.h>
+
+#ifndef DEBUG_CONFIG_IP_MASQ_QUAKE
+#define DEBUG_CONFIG_IP_MASQ_QUAKE 0
+#endif
+
+/* 
+ * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
+ * First ports are set to the default port.
+ */
+int ports[MAX_MASQ_APP_PORTS] = { 26000,   /* I rely on the trailing items */
+                                 27000 }; /* being set to zero */
+struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
+
+
+typedef struct
+{ 
+        __u16 type;     // (Little Endian) Type of message.
+       __u16 length;   // (Little Endian) Length of message, header included. 
+       char  message[0];  // The contents of the message.
+} QUAKEHEADER;
+
+struct quake_priv_data {
+       /* Have we seen a client connect message */
+       char    cl_connect;
+};
+
+static int
+masq_quake_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+        MOD_INC_USE_COUNT;
+       if ((ms->app_data = kmalloc(sizeof(struct quake_priv_data),
+                                   GFP_ATOMIC)) == NULL) 
+               printk(KERN_INFO "Quake: No memory for application data\n");
+       else 
+       {
+               struct quake_priv_data *priv = 
+                       (struct quake_priv_data *)ms->app_data;
+               priv->cl_connect = 0;
+       }
+        return 0;
+}
+
+static int
+masq_quake_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+       MOD_DEC_USE_COUNT;
+       if (ms->app_data)
+               kfree_s(ms->app_data, sizeof(struct quake_priv_data));
+       return 0;
+}
+
+int
+masq_quake_in (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev)
+{
+       struct sk_buff *skb;
+       struct iphdr *iph;
+       struct udphdr *uh;
+       QUAKEHEADER *qh;
+       __u16 udp_port;
+       char *data;
+       unsigned char code;
+       struct quake_priv_data *priv = (struct quake_priv_data *)ms->app_data;
+        
+       if(priv->cl_connect == -1)
+         return 0;
+
+       skb = *skb_p;
+       iph = skb->h.iph;
+/*     iph = skb->nh.iph; */
+
+       uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+
+       /* Check for lenght */
+       if(ntohs(uh->len) < 5)
+         return 0;
+       
+       qh = (QUAKEHEADER *)&uh[1];
+
+       if(qh->type != 0x0080)
+         return 0;
+
+       
+       code = qh->message[0];
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+         printk("Quake_in: code = %d \n", (int)code);
+#endif
+
+       switch(code) {
+       case 0x01:
+         /* Connection Request */
+
+         if(ntohs(qh->length) < 0x0c) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+           printk("Quake_in: length < 0xc \n");
+#endif
+           return 0;
+         }
+
+         data = &qh->message[1];
+
+         /* Check for stomping string */
+         if(memcmp(data,"QUAKE\0\3",7)) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+           printk("Quake_out: memcmp failed \n");
+#endif
+           return 0;
+         }
+         else {
+           priv->cl_connect = 1;
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+           printk("Quake_out: memcmp ok \n");
+#endif
+         }
+         break;
+
+       case 0x81:
+         /* Accept Connection */
+         if((ntohs(qh->length) < 0x09) || (priv->cl_connect == 0))
+           return 0;
+         data = &qh->message[1];
+
+         memcpy(&udp_port, data, 2);
+
+         ms->dport = htons(udp_port);
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+         printk("Quake_in: in_rewrote UDP port %d \n", udp_port);
+#endif
+         priv->cl_connect = -1;
+
+         break;
+       }
+        
+       return 0;
+}
+
+int
+masq_quake_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev)
+{
+       struct sk_buff *skb;
+       struct iphdr *iph;
+       struct udphdr *uh;
+       QUAKEHEADER *qh;
+       __u16 udp_port;
+       char *data;
+       unsigned char code;
+       struct ip_masq *n_ms;
+       struct quake_priv_data *priv = (struct quake_priv_data *)ms->app_data;
+
+       if(priv->cl_connect == -1)
+         return 0;
+        
+       skb = *skb_p;
+       iph = skb->h.iph;
+/*     iph = skb->nh.iph; */
+
+       uh = (struct udphdr *)&(((char *)iph)[iph->ihl*4]);
+
+       /* Check for lenght */
+       if(ntohs(uh->len) < 5)
+         return 0;
+       
+       qh = (QUAKEHEADER *)&uh[1];
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+         printk("Quake_out: qh->type = %d \n", (int)qh->type);
+#endif
+
+       if(qh->type != 0x0080)
+         return 0;
+       
+       code = qh->message[0];
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+         printk("Quake_out: code = %d \n", (int)code);
+#endif
+
+       switch(code) {
+       case 0x01:
+         /* Connection Request */
+
+         if(ntohs(qh->length) < 0x0c) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+           printk("Quake_out: length < 0xc \n");
+#endif
+           return 0;
+         }
+
+         data = &qh->message[1];
+
+         /* Check for stomping string */
+         if(memcmp(data,"QUAKE\0\3",7)) {
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+           printk("Quake_out: memcmp failed \n");
+#endif
+           return 0;
+         }
+         else {
+           priv->cl_connect = 1;
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+           printk("Quake_out: memcmp ok \n");
+#endif
+         }
+         break;
+
+       case 0x81:
+         /* Maybe a redirection of a quake-server at the inner side works in
+             the future? */
+
+         /* Accept Connection */
+         if((ntohs(qh->length) < 0x09) || (priv->cl_connect == 0))
+           return 0;
+
+         data = &qh->message[1];
+
+         memcpy(&udp_port, data, 2);
+         
+         n_ms = ip_masq_new(dev, IPPROTO_UDP,
+                            ms->saddr, htons(udp_port),
+                            ms->daddr, ms->dport,
+                            0);
+
+         if (n_ms==NULL)
+           return 0;
+
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+         printk("Quake_out: out_rewrote UDP port %d -> %d\n",
+                udp_port, ntohs(n_ms->mport));
+#endif
+         udp_port = ntohs(n_ms->mport);
+         memcpy(data, &udp_port, 2);
+
+         break;
+       }
+        
+       return 0;
+}
+
+struct ip_masq_app ip_masq_quake = {
+        NULL,                  /* next */
+       "Quake",                /* name */
+        0,                      /* type */
+        0,                      /* n_attach */
+        masq_quake_init_1,      /* ip_masq_init_1 */
+        masq_quake_done_1,      /* ip_masq_done_1 */
+        masq_quake_out,         /* pkt_out */
+        masq_quake_in           /* pkt_in */
+};
+
+/*
+ *     ip_masq_quake initialization
+ */
+
+int ip_masq_quake_init(void)
+{
+       int i, j;
+
+       for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+               if (ports[i]) {
+                       if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
+                                                           GFP_KERNEL)) == NULL)
+                               return -ENOMEM;
+                       memcpy(masq_incarnations[i], &ip_masq_quake, sizeof(struct ip_masq_app));
+                       if ((j = register_ip_masq_app(masq_incarnations[i], 
+                                                     IPPROTO_UDP, 
+                                                     ports[i]))) {
+                               return j;
+                       }
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+                       printk("Quake: loaded support on port[%d] = %d\n",
+                              i, ports[i]);
+#endif
+               } else {
+                       /* To be safe, force the incarnation table entry to NULL */
+                       masq_incarnations[i] = NULL;
+               }
+       }
+       return 0;
+}
+
+/*
+ *     ip_masq_quake fin.
+ */
+
+int ip_masq_quake_done(void)
+{
+       int i, j, k;
+
+       k=0;
+       for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+               if (masq_incarnations[i]) {
+                       if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
+                               k = j;
+                       } else {
+                               kfree(masq_incarnations[i]);
+                               masq_incarnations[i] = NULL;
+#if DEBUG_CONFIG_IP_MASQ_QUAKE
+                               printk("Quake: unloaded support on port[%d] = %d\n",
+                                      i, ports[i]);
+#endif
+                       }
+               }
+       }
+       return k;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+        if (ip_masq_quake_init() != 0)
+                return -EIO;
+        register_symtab(0);
+        return 0;
+}
+
+void cleanup_module(void)
+{
+        if (ip_masq_quake_done() != 0)
+                printk("ip_masq_quake: can't remove module");
+}
+
+#endif /* MODULE */
index 9fe4e8f155a9e36a930eefd44be4795e25482a5b..22dbe608222f696a4114cf05631db912671dcd80 100644 (file)
@@ -2,12 +2,12 @@
  *             IP_MASQ_RAUDIO  - Real Audio masquerading module
  *
  *
- * Version:    @(#)$Id: ip_masq_raudio.c,v 1.3 1996/05/20 13:24:26 nigel Exp $
+ * Version:    @(#)$Id: ip_masq_raudio.c,v 1.1.2.1 1997/03/04 12:04:53 davem Exp $
  *
  * Author:     Nigel Metheringham
  *             [strongly based on ftp module by Juan Jose Ciarlante & Wouter Gadeyne]
  *             [Real Audio information taken from Progressive Networks firewall docs]
- *
+ *             [Kudos to Progressive Networks for making the protocol specs available]
  *
  *
  *
  *     When the link is up there appears to be enough control data 
  *     crossing the control link to keep it open even if a long audio
  *     piece is playing.
+ *
+ *     The Robust UDP support added in RealAudio 3.0 is supported, but due
+ *     to servers/clients not making great use of this has not been greatly
+ *     tested.  RealVideo (as used in the Real client version 4.0beta1) is
+ *     supported but again is not greatly tested (bandwidth requirements
+ *     appear to exceed that available at the sites supporting the protocol).
+ *
+ * Multiple Port Support
+ *     The helper can be made to handle up to MAX_MASQ_APP_PORTS (normally 12)
+ *     with the port numbers being defined at module load time.  The module
+ *     uses the symbol "ports" to define a list of monitored ports, which can
+ *     be specified on the insmod command line as
+ *             ports=x1,x2,x3...
+ *     where x[n] are integer port numbers.  This option can be put into
+ *     /etc/conf.modules (or /etc/modules.conf depending on your config)
+ *     where modload will pick it up should you use modload to load your
+ *     modules.
  *     
  */
 
 struct raudio_priv_data {
        /* Associated data connection - setup but not used at present */
        struct  ip_masq *data_conn;
+       /* UDP Error correction connection - setup but not used at present */
+       struct  ip_masq *error_conn;
        /* Have we seen and performed setup */
        short   seen_start;
 };
 
+/* 
+ * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
+ * First port is set to the default port.
+ */
+int ports[MAX_MASQ_APP_PORTS] = {7070}; /* I rely on the trailing items being set to zero */
+struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
+
 static int
 masq_raudio_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
 {
@@ -72,6 +98,7 @@ masq_raudio_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
                        (struct raudio_priv_data *)ms->app_data;
                priv->seen_start = 0;
                priv->data_conn = NULL;
+               priv->error_conn = NULL;
        }
         return 0;
 }
@@ -106,7 +133,7 @@ masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **
         th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
         data = (char *)&th[1];
 
-        data_limit = skb->h.raw + skb->len - 18;
+        data_limit = skb->h.raw + skb->len;
 
        /* Check to see if this is the first packet with protocol ID */
        if (memcmp(data, "PNA", 3)) {
@@ -133,15 +160,26 @@ masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **
        }
 
        data += 2;
-       while (data < data_limit) {
+       while (data+4 < data_limit) {
                memcpy(&msg_id, data, 2);
                data += 2;
                memcpy(&msg_len, data, 2);
                data += 2;
+               if (ntohs(msg_id) == 0) {
+                       /* The zero tag indicates the end of options */
+#if DEBUG_CONFIG_IP_MASQ_RAUDIO
+                       printk("RealAudio: packet end tag seen\n");
+#endif
+                       return 0;
+               }
 #if DEBUG_CONFIG_IP_MASQ_RAUDIO
                printk("RealAudio: msg %d - %d byte\n",
                       ntohs(msg_id), ntohs(msg_len));
 #endif
+               if (ntohs(msg_id) == 0) {
+                       /* The zero tag indicates the end of options */
+                       return 0;
+               }
                p = data;
                data += ntohs(msg_len);
                if (data > data_limit)
@@ -149,9 +187,26 @@ masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **
                        printk(KERN_INFO "RealAudio: Packet too short for data\n");
                        return 0;
                }
-               if (ntohs(msg_id) == 1) {
-                       /* This is a message detailing the UDP port to be used */
+               if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
+                       /* 
+                        * MsgId == 1
+                        * Audio UDP data port on client
+                        *
+                        * MsgId == 7
+                        * Robust UDP error correction port number on client
+                        *
+                        * Since these messages are treated just the same, they
+                        * are bundled together here....
+                        */
                        memcpy(&udp_port, p, 2);
+
+                       /* 
+                        * Sometimes a server sends a message 7 with a zero UDP port
+                        * Rather than do anything with this, just ignore it!
+                        */
+                       if (udp_port == 0)
+                               continue;
+
                        n_ms = ip_masq_new(dev, IPPROTO_UDP,
                                           ms->saddr, udp_port,
                                           ms->daddr, 0,
@@ -162,24 +217,19 @@ masq_raudio_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **
 
                        memcpy(p, &(n_ms->mport), 2);
 #if DEBUG_CONFIG_IP_MASQ_RAUDIO
-                       printk("RealAudio: rewrote UDP port %d -> %d\n",
-                              ntohs(udp_port), ntohs(n_ms->mport));
+                       printk("RealAudio: rewrote UDP port %d -> %d in msg %d\n",
+                              ntohs(udp_port), ntohs(n_ms->mport), ntohs(msg_id));
 #endif
                        ip_masq_set_expire(n_ms, ip_masq_expire->udp_timeout);
 
                        /* Make ref in application data to data connection */
-                       if (priv)
-                               priv->data_conn = n_ms;
-
-                       /* 
-                        * There is nothing else useful we can do
-                        * Maybe a development could do more, but for now
-                        * we exit gracefully!
-                        */
-                       return 0;
-
-               } else if (ntohs(msg_id) == 0)
-                       return 0;
+                       if (priv) {
+                               if (ntohs(msg_id) == 1)
+                                       priv->data_conn = n_ms;
+                               else
+                                       priv->error_conn = n_ms;
+                       }
+               }
        }
        return 0;
 }
@@ -201,7 +251,29 @@ struct ip_masq_app ip_masq_raudio = {
 
 int ip_masq_raudio_init(void)
 {
-        return register_ip_masq_app(&ip_masq_raudio, IPPROTO_TCP, 7070);
+       int i, j;
+
+       for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+               if (ports[i]) {
+                       if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
+                                                           GFP_KERNEL)) == NULL)
+                               return -ENOMEM;
+                       memcpy(masq_incarnations[i], &ip_masq_raudio, sizeof(struct ip_masq_app));
+                       if ((j = register_ip_masq_app(masq_incarnations[i], 
+                                                     IPPROTO_TCP, 
+                                                     ports[i]))) {
+                               return j;
+                       }
+#if DEBUG_CONFIG_IP_MASQ_RAUDIO
+                       printk("RealAudio: loaded support on port[%d] = %d\n",
+                              i, ports[i]);
+#endif
+               } else {
+                       /* To be safe, force the incarnation table entry to NULL */
+                       masq_incarnations[i] = NULL;
+               }
+       }
+       return 0;
 }
 
 /*
@@ -210,7 +282,24 @@ int ip_masq_raudio_init(void)
 
 int ip_masq_raudio_done(void)
 {
-        return unregister_ip_masq_app(&ip_masq_raudio);
+       int i, j, k;
+
+       k=0;
+       for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+               if (masq_incarnations[i]) {
+                       if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
+                               k = j;
+                       } else {
+                               kfree(masq_incarnations[i]);
+                               masq_incarnations[i] = NULL;
+#if DEBUG_CONFIG_IP_MASQ_RAUDIO
+                               printk("RealAudio: unloaded support on port[%d] = %d\n",
+                                      i, ports[i]);
+#endif
+                       }
+               }
+       }
+       return k;
 }
 
 #ifdef MODULE
diff --git a/net/ipv4/ip_masq_vdolive.c b/net/ipv4/ip_masq_vdolive.c
new file mode 100644 (file)
index 0000000..0d1dca2
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ *             IP_MASQ_VDOLIVE  - VDO Live masquerading module
+ *
+ *
+ * Version:    @(#)$Id: ip_masq_vdolive.c,v 1.1.2.1 1997/03/04 12:04:47 davem Exp $
+ *
+ * Author:     Nigel Metheringham <Nigel.Metheringham@ThePLAnet.net>
+ *             PLAnet Online Ltd
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ *
+ * Thanks:
+ *     Thank you to VDOnet Corporation for allowing me access to
+ *     a protocol description without an NDA.  This means that
+ *     this module can be distributed as source - a great help!
+ *     
+ */
+
+#include <linux/module.h>
+#include <asm/system.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <net/protocol.h>
+#include <net/tcp.h>
+#include <net/ip_masq.h>
+
+#ifndef DEBUG_CONFIG_IP_MASQ_VDOLIVE
+#define DEBUG_CONFIG_IP_MASQ_VDOLIVE 0
+#endif
+
+struct vdolive_priv_data {
+       /* Ports used */
+       unsigned short  origport;
+       unsigned short  masqport;
+       /* State of decode */
+       unsigned short  state;
+};
+
+/* 
+ * List of ports (up to MAX_MASQ_APP_PORTS) to be handled by helper
+ * First port is set to the default port.
+ */
+int ports[MAX_MASQ_APP_PORTS] = {7000}; /* I rely on the trailing items being set to zero */
+struct ip_masq_app *masq_incarnations[MAX_MASQ_APP_PORTS];
+
+static int
+masq_vdolive_init_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+        MOD_INC_USE_COUNT;
+       if ((ms->app_data = kmalloc(sizeof(struct vdolive_priv_data),
+                                   GFP_ATOMIC)) == NULL) 
+               printk(KERN_INFO "VDOlive: No memory for application data\n");
+       else 
+       {
+               struct vdolive_priv_data *priv = 
+                       (struct vdolive_priv_data *)ms->app_data;
+               priv->origport = 0;
+               priv->masqport = 0;
+               priv->state = 0;
+       }
+        return 0;
+}
+
+static int
+masq_vdolive_done_1 (struct ip_masq_app *mapp, struct ip_masq *ms)
+{
+        MOD_DEC_USE_COUNT;
+       if (ms->app_data)
+               kfree_s(ms->app_data, sizeof(struct vdolive_priv_data));
+        return 0;
+}
+
+int
+masq_vdolive_out (struct ip_masq_app *mapp, struct ip_masq *ms, struct sk_buff **skb_p, struct device *dev)
+{
+        struct sk_buff *skb;
+       struct iphdr *iph;
+       struct tcphdr *th;
+       char *data, *data_limit;
+       unsigned int tagval;    /* This should be a 32 bit quantity */
+       struct ip_masq *n_ms;
+       struct vdolive_priv_data *priv = 
+               (struct vdolive_priv_data *)ms->app_data;
+
+       /* This doesn't work at all if no priv data was allocated on startup */
+       if (!priv)
+               return 0;
+
+       /* Everything running correctly already */
+       if (priv->state == 3)
+               return 0;
+
+        skb = *skb_p;
+       iph = skb->h.iph;
+        th = (struct tcphdr *)&(((char *)iph)[iph->ihl*4]);
+        data = (char *)&th[1];
+
+        data_limit = skb->h.raw + skb->len;
+
+       if (data+8 > data_limit) {
+#if DEBUG_CONFIG_IP_MASQ_VDOLIVE
+               printk("VDOlive: packet too short for ID %lx %lx\n",
+                      data, data_limit);
+#endif
+               return 0;
+       }
+       memcpy(&tagval, data+4, 4);
+#if DEBUG_CONFIG_IP_MASQ_VDOLIVE
+       printk("VDOlive: packet seen, tag %ld, in initial state %d\n",
+              ntohl(tagval), priv->state);
+#endif
+
+       /* Check for leading packet ID */
+       if ((ntohl(tagval) != 6) && (ntohl(tagval) != 1)) {
+#if DEBUG_CONFIG_IP_MASQ_VDOLIVE
+               printk("VDOlive: unrecognised tag %ld, in initial state %d\n",
+                      ntohl(tagval), priv->state);
+#endif
+               return 0;
+       }
+               
+
+       /* Check packet is long enough for data - ignore if not */
+       if ((ntohl(tagval) == 6) && (data+36 > data_limit)) {
+#if DEBUG_CONFIG_IP_MASQ_VDOLIVE
+               printk("VDOlive: initial packet too short %lx %lx\n",
+                      data, data_limit);
+#endif
+               return 0;
+       } else if ((ntohl(tagval) == 1) && (data+20 > data_limit)) {
+#if DEBUG_CONFIG_IP_MASQ_VDOLIVE
+               printk("VDOlive: secondary packet too short %lx %lx\n",
+                      data, data_limit);
+#endif
+               return 0;
+       }
+
+       /* Adjust data pointers */
+       /*
+        * I could check the complete protocol version tag 
+        * in here however I am just going to look for the
+        * "VDO Live" tag in the hope that this part will
+        * remain constant even if the version changes
+        */
+       if (ntohl(tagval) == 6) {
+               data += 24;
+#if DEBUG_CONFIG_IP_MASQ_VDOLIVE
+               printk("VDOlive: initial packet found\n");
+#endif
+       } else {
+               data += 8;
+#if DEBUG_CONFIG_IP_MASQ_VDOLIVE
+               printk("VDOlive: secondary packet found\n");
+#endif
+       }
+
+       if (memcmp(data, "VDO Live", 8) != 0) {
+#if DEBUG_CONFIG_IP_MASQ_VDOLIVE
+               printk("VDOlive: did not find tag\n");
+#endif
+               return 0;
+       }
+       /* 
+        * The port number is the next word after the tag.
+        * VDOlive encodes all of these values
+        * in 32 bit words, so in this case I am
+        * skipping the first 2 bytes of the next
+        * word to get to the relevant 16 bits
+        */
+       data += 10;
+
+       /*
+        * If we have not seen the port already,
+        * set the masquerading tunnel up
+        */
+       if (!priv->origport) {
+               memcpy(&priv->origport, data, 2);
+
+#if DEBUG_CONFIG_IP_MASQ_VDOLIVE
+               printk("VDOlive: found port %d\n",
+                      ntohs(priv->origport));
+#endif
+
+               /* Open up a tunnel */
+               n_ms = ip_masq_new(dev, IPPROTO_UDP,
+                                  ms->saddr, priv->origport,
+                                  ms->daddr, 0,
+                                  IP_MASQ_F_NO_DPORT);
+                                       
+               if (n_ms==NULL) {
+                       printk("VDOlive: unable to build UDP tunnel for %x:%x\n",
+                              ms->saddr, priv->origport);
+                       /* Leave state as unset */
+                       priv->origport = 0;
+                       return 0;
+               }
+
+               ip_masq_set_expire(n_ms, ip_masq_expire->udp_timeout);
+               priv->masqport = n_ms->mport;
+       } else if (memcmp(data, &(priv->origport), 2)) {
+               printk("VDOlive: ports do not match\n");
+               /* Write the port in anyhow!!! */
+       }
+
+       /*
+        * Write masq port into packet
+        */
+       memcpy(data, &(priv->masqport), 2);
+#if DEBUG_CONFIG_IP_MASQ_VDOLIVE
+       printk("VDOlive: rewrote port %d to %d, server %s\n",
+              ntohs(priv->origport), ntohs(priv->masqport), in_ntoa(ms->saddr));
+#endif
+
+       /*
+        * Set state bit to make which bit has been done
+        */
+
+       priv->state |= (ntohl(tagval) == 6) ? 1 : 2;
+
+       return 0;
+}
+
+
+struct ip_masq_app ip_masq_vdolive = {
+        NULL,                  /* next */
+       "VDOlive",              /* name */
+        0,                      /* type */
+        0,                      /* n_attach */
+        masq_vdolive_init_1,   /* ip_masq_init_1 */
+        masq_vdolive_done_1,   /* ip_masq_done_1 */
+        masq_vdolive_out,      /* pkt_out */
+        NULL                    /* pkt_in */
+};
+
+/*
+ *     ip_masq_vdolive initialization
+ */
+
+int ip_masq_vdolive_init(void)
+{
+       int i, j;
+
+       for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+               if (ports[i]) {
+                       if ((masq_incarnations[i] = kmalloc(sizeof(struct ip_masq_app),
+                                                           GFP_KERNEL)) == NULL)
+                               return -ENOMEM;
+                       memcpy(masq_incarnations[i], &ip_masq_vdolive, sizeof(struct ip_masq_app));
+                       if ((j = register_ip_masq_app(masq_incarnations[i], 
+                                                     IPPROTO_TCP, 
+                                                     ports[i]))) {
+                               return j;
+                       }
+#if DEBUG_CONFIG_IP_MASQ_VDOLIVE
+                       printk("RealAudio: loaded support on port[%d] = %d\n",
+                              i, ports[i]);
+#endif
+               } else {
+                       /* To be safe, force the incarnation table entry to NULL */
+                       masq_incarnations[i] = NULL;
+               }
+       }
+       return 0;
+}
+
+/*
+ *     ip_masq_vdolive fin.
+ */
+
+int ip_masq_vdolive_done(void)
+{
+       int i, j, k;
+
+       k=0;
+       for (i=0; (i<MAX_MASQ_APP_PORTS); i++) {
+               if (masq_incarnations[i]) {
+                       if ((j = unregister_ip_masq_app(masq_incarnations[i]))) {
+                               k = j;
+                       } else {
+                               kfree(masq_incarnations[i]);
+                               masq_incarnations[i] = NULL;
+#if DEBUG_CONFIG_IP_MASQ_VDOLIVE
+                               printk("VDOlive: unloaded support on port[%d] = %d\n",
+                                      i, ports[i]);
+#endif
+                       }
+               }
+       }
+       return k;
+}
+
+
+#ifdef MODULE
+
+int init_module(void)
+{
+        if (ip_masq_vdolive_init() != 0)
+                return -EIO;
+        register_symtab(0);
+        return 0;
+}
+
+void cleanup_module(void)
+{
+        if (ip_masq_vdolive_done() != 0)
+                printk("ip_masq_vdolive: can't remove module");
+}
+
+#endif /* MODULE */
index 4b1c3b1e2f9b53dc31eebedccb21b5c8660236f1..7e90749aa6b930f3f12b40481de5bd093d4e061d 100644 (file)
@@ -406,6 +406,22 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
                        err=ip_fw_ctl(optname, &tmp_fw,optlen);
                        return -err;    /* -0 is 0 after all */
                        
+#endif
+#ifdef CONFIG_IP_MASQUERADE_IPAUTOFW
+               case IP_AUTOFW_ADD:
+               case IP_AUTOFW_DEL:
+               case IP_AUTOFW_FLUSH:
+                       if(!suser())
+                               return -EPERM;
+                       if(optlen>sizeof(tmp_fw) || optlen<1)
+                               return -EINVAL;
+                       err=verify_area(VERIFY_READ,optval,optlen);
+                       if(err)
+                               return err;
+                       memcpy_fromfs(&tmp_fw,optval,optlen);
+                       err=ip_autofw_ctl(optname, &tmp_fw,optlen);
+                       return -err;    /* -0 is 0 after all */
+                       
 #endif
 #ifdef CONFIG_IP_ACCT
                case IP_ACCT_INSERT:
index de9844c26f51ab9c9aba1d47a66d42c4f6a119b3..1c8635696c1365830f82099cb8f21ca0ff9573a3 100644 (file)
@@ -479,28 +479,34 @@ int packet_recvmsg(struct sock *sk, struct msghdr *msg, int len,
  
 struct proto packet_prot = 
 {
-       packet_close,
-       ip_build_header,        /* Not actually used */
-       NULL,
-       NULL,
-       ip_queue_xmit,          /* These two are not actually used */
-       NULL,
-       NULL,
-       NULL,
-       NULL, 
-       datagram_select,
-       NULL,                   /* No ioctl */
-       packet_init,
-       NULL,
-       NULL,                   /* No set/get socket options */
-       NULL,
-       packet_sendmsg,         /* Sendmsg */
-       packet_recvmsg,         /* Recvmsg */
-       packet_bind,            /* Bind */
-       128,
-       0,
-       "PACKET",
-       0, 0
+       (struct sock *)&packet_prot,    /* sklist_next */
+       (struct sock *)&packet_prot,    /* sklist_prev */
+       packet_close,                   /* close */
+       ip_build_header,                /* build_header, Not actually used */
+       NULL,                           /* connect */
+       NULL,                           /* accept */
+       ip_queue_xmit,                  /* queue_xmit, These two are not actually used */
+       NULL,                           /* retransmit */
+       NULL,                           /* write_wakeup */
+       NULL,                           /* read_wakeup */
+       NULL,                           /* rcv */
+       datagram_select,                /* select */
+       NULL,                           /* ioctl, No ioctl */
+       packet_init,                    /* init */
+       NULL,                           /* shutdown */
+       NULL,                           /* setsockopt, No set/get socket options */
+       NULL,                           /* getsockopt */
+       packet_sendmsg,                 /* sendmsg */
+       packet_recvmsg,                 /* recvmsg */
+       packet_bind,                    /* bind */
+       NULL,                           /* hash */
+       NULL,                           /* unhash */
+       NULL,                           /* rehash */
+       NULL,                           /* good_socknum */
+       NULL,                           /* verify_bind */
+       128,                            /* max_header */
+       0,                              /* retransmits */
+       "PACKET",                       /* name */
+       0,                              /* inuse */
+       0                               /* highestinuse */
 };
-
-       
index 5b9fc3c7e6e24729c9cfcf99e25d0462f9ab7d8d..2badb59eefb4679fd74549e489474d488de6e405 100644 (file)
 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;
-       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 "
@@ -83,71 +78,62 @@ get__netinfo(struct proto *pro, char *buffer, int format, char **start, off_t of
  *     a memory timer destroy. Instead of playing with timers we just
  *     concede defeat and cli().
  */
-       for(i = 0; i < SOCK_ARRAY_SIZE; i++) 
-       {
-               cli();
-               sp = s_array[i];
-               while(sp != NULL) 
-               {
-                       pos += 128;
-                       if (pos < offset)
-                       {
-                               sp = sp->next;
-                               continue;
-                       }
-                       dest  = sp->daddr;
-                       src   = sp->saddr;
-                       destp = sp->dummy_th.dest;
-                       srcp  = sp->dummy_th.source;
+       start_bh_atomic();
+       sp = pro->sklist_next;
+       while(sp != (struct sock *)pro) {
+               pos += 128;
+               if (pos < offset)
+                       goto next;
+
+               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-sp->rcv_ack_seq:sp->wmem_alloc, 
-                               format==0?sp->acked_seq-sp->copied_seq:sp->rmem_alloc,
-                               timer_active, timer_expires-jiffies, (unsigned) sp->retransmits,
-                               (sp->socket&&SOCK_INODE(sp->socket))?SOCK_INODE(sp->socket)->i_uid:0,
-                               timer_active?sp->timeout:0,
-                               sp->socket && SOCK_INODE(sp->socket) ?
-                               SOCK_INODE(sp->socket)->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;
+               /* 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;
                }
-               sti();  /* We only turn interrupts back on for a moment,
-                          but because the interrupt queues anything built
-                          up before this will clear before we jump back
-                          and cli(), so it's not as bad as it looks */
-               if(len>= length)
+               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-sp->rcv_ack_seq:sp->wmem_alloc, 
+                       format==0?sp->acked_seq-sp->copied_seq:sp->rmem_alloc,
+                       timer_active, timer_expires-jiffies, (unsigned) sp->retransmits,
+                       (sp->socket&&SOCK_INODE(sp->socket))?SOCK_INODE(sp->socket)->i_uid:0,
+                       timer_active?sp->timeout:0,
+                       sp->socket && SOCK_INODE(sp->socket) ?
+                       SOCK_INODE(sp->socket)->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 are kept in the protocols sklist, so we
+                * follow the 'next' link to get them all.
+                */
+               if(len >= length)
                        break;
+       next:
+               sp = sp->sklist_next;
+               i++;
        }
+       end_bh_atomic();
+
        begin = len - (pos - offset);
        *start = buffer + begin;
        len -= begin;
@@ -186,7 +172,6 @@ int afinet_get_info(char *buffer, char **start, off_t offset, int length, int du
 
        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",
index 1d515383b2df4c08c83a3e2fc3a3f56deb39e525..8cccdbb0e9d84038fba1c912fd590673f2180b20 100644 (file)
@@ -46,7 +46,6 @@
 #include <linux/igmp.h>
 
 
-#ifdef CONFIG_IP_FORWARD
 #ifdef CONFIG_NET_IPIP
 
 static struct inet_protocol ipip_protocol = 
@@ -61,14 +60,13 @@ static struct inet_protocol ipip_protocol =
 };
 
 
-#endif
 #endif
 
 static struct inet_protocol tcp_protocol = 
 {
        tcp_rcv,                /* TCP handler          */
        tcp_err,                /* TCP error control    */  
-#if defined(CONFIG_NET_IPIP) && defined(CONFIG_IP_FORWARD)
+#if defined(CONFIG_NET_IPIP)
        &ipip_protocol,
 #else  
        NULL,                   /* next                 */
index 402237f7f9a2ac852a83926a327caf0bfd3c2afc..24438b5b2dd66c4cfdd75a0ade42e5daca310182 100644 (file)
@@ -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 for ISS.
  *
  *             This program is free software; you can redistribute it and/or
  *             modify it under the terms of the GNU General Public License
 #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;
+}
+
 static inline unsigned long min(unsigned long a, unsigned long b)
 {
        if (a < b) 
@@ -137,6 +215,31 @@ static int raw_rcv_redo(struct sk_buff *skb, struct device *dev, struct options
        return 0;
 }
 
+/* This gets rid of all the nasties in af_inet. -DaveM */
+static int raw_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+{
+       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 */
+       ip_rt_put(sk->ip_route_cache);
+       sk->ip_route_cache = NULL;
+       return 0;
+}
+
 /*
  *     This should be the easiest of all, all we do is
  *     copy it into a buffer. All demultiplexing is done
@@ -375,31 +478,38 @@ int raw_recvmsg(struct sock *sk, struct msghdr *msg, int len,
 
 
 struct proto raw_prot = {
-       raw_close,
-       ip_build_header,
-       udp_connect,
-       NULL,
-       ip_queue_xmit,
-       NULL,
-       NULL,
-       NULL,
-       raw_rcv_redo,
-       datagram_select,
+       (struct sock *)&raw_prot,       /* sklist_next */
+       (struct sock *)&raw_prot,       /* sklist_prev */
+       raw_close,                      /* close */
+       ip_build_header,                /* build_header */
+       udp_connect,                    /* connect */
+       NULL,                           /* accept */
+       ip_queue_xmit,                  /* queue_xmit */
+       NULL,                           /* retransmit */
+       NULL,                           /* write_wakeup */
+       NULL,                           /* read_wakeup */
+       raw_rcv_redo,                   /* rcv */
+       datagram_select,                /* select */
 #ifdef CONFIG_IP_MROUTE        
-       ipmr_ioctl,
+       ipmr_ioctl,                     /* ioctl */
 #else
-       NULL,
+       NULL,                           /* ioctl */
 #endif         
-       raw_init,
-       NULL,
-       ip_setsockopt,
-       ip_getsockopt,
-       raw_sendmsg,
-       raw_recvmsg,
-       NULL,           /* No special bind */
-       128,
-       0,
-       "RAW",
-       0, 0,
-       {NULL,}
+       raw_init,                       /* init */
+       NULL,                           /* shutdown */
+       ip_setsockopt,                  /* setsockopt */
+       ip_getsockopt,                  /* getsockopt */
+       raw_sendmsg,                    /* sendmsg */
+       raw_recvmsg,                    /* recvmsg */
+       raw_bind,                       /* bind */
+       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 */
 };
index 1278259ad69a4526cd3a0610c950cde8bc7ecbd9..53fe93b2ba3dd416c94401b9549364ce55faa9f1 100644 (file)
@@ -564,11 +564,14 @@ static __inline__ void fib_add_1(short flags, __u32 dst, __u32 mask,
                        f1 = fz->fz_list;
                        while (f1)
                        {
-                               struct fib_node * next;
+                               struct fib_node * next, **end;
                                unsigned hash = fz_hash_code(f1->fib_dst, logmask);
                                next = f1->fib_next;
-                               f1->fib_next = ht[hash];
-                               ht[hash] = f1;
+                               f1->fib_next = NULL;
+                               end = &ht[hash];
+                               while(*end != NULL)
+                                       end = &(*end)->fib_next;
+                               *end = f1;
                                f1 = next;
                        }
                        fz->fz_list = NULL;
index 21d7cd3be8524748ee03b3321381ac5fa65bec40..83129afaa820263286048acba23f072596ee526e 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/mm.h>
 #include <linux/sysctl.h>
+#include <net/ip.h>
 
 /* From arp.c */
 extern int sysctl_arp_res_time;
@@ -17,6 +18,27 @@ extern int sysctl_arp_check_interval;
 extern int sysctl_arp_confirm_interval;
 extern int sysctl_arp_confirm_timeout;
 
+extern int sysctl_ip_forward; 
+static int proc_doipforward(ctl_table *ctl, int write, struct file *filp,
+                           void *buffer, size_t *lenp) 
+{
+    int val = sysctl_ip_forward;    
+    int retv;
+
+    retv = proc_dointvec(ctl, write, filp, buffer, lenp);
+    if (write) {
+       if (sysctl_ip_forward && !val) {
+           printk(KERN_INFO "sysctl: ip forwarding enabled\n");
+           ip_statistics.IpForwarding = 1; 
+       }
+       if (!sysctl_ip_forward && val) {
+           printk(KERN_INFO "sysctl: ip forwarding off\n");
+           ip_statistics.IpForwarding = 2;
+       }
+    }
+    return retv; 
+}
+
 ctl_table ipv4_table[] = {
         {NET_IPV4_ARP_RES_TIME, "arp_res_time",
          &sysctl_arp_res_time, sizeof(int), 0644, NULL, &proc_dointvec},
@@ -34,5 +56,7 @@ ctl_table ipv4_table[] = {
         {NET_IPV4_ARP_CONFIRM_TIMEOUT, "arp_confirm_timeout",
          &sysctl_arp_confirm_timeout, sizeof(int), 0644, NULL,
          &proc_dointvec},
+       {NET_IPV4_FORWARD, "ip_forward", &sysctl_ip_forward, sizeof(int),
+        0644, NULL, &proc_doipforward },
        {0}
 };
index bb328f5466845bf856b2834b8b1c2a9487b3ec5b..bcac7a79e5b864a421191100e87c98de5dad9fe2 100644 (file)
  *     Stefan Magdalinski      :       adjusted tcp_readable() to fix FIONREAD
  *     Willy Konynenberg       :       Transparent proxying support.
  *             Theodore Ts'o   :       Do secure TCP sequence numbers.
+ *             David S. Miller :       New socket lookup architecture for ISS.
+ *                                     This code is dedicated to John Dyson.
  *                                     
  * To Fix:
  *             Fast path the code. Two things here - fix the window calculation
 unsigned long seq_offset;
 struct tcp_mib tcp_statistics;
 
+/* 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
+ */
+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];
+
+/* 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];
+
+extern struct sock *tcp_v4_lookup(u32 saddr, u16 sport, u32 daddr, u16 dport);
+
+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->bind_next) {
+               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->bind_next) {
+               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->bind_next; } while(++j < size && sk);
+                       if(j < size) {
+                               best = (start + i);
+                               size = j;
+                               if(bc && size <= bc) {
+                                       start = best + 1;
+                                       goto verify;
+                               }
+                       }
+               }
+       } while(++i != end);
+
+       /* 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;
+}
+
+void tcp_v4_hash(struct sock *sk)
+{
+       unsigned char state;
+
+       SOCKHASH_LOCK();
+       state = sk->state;
+       if(state != TCP_CLOSE || !sk->dead) {
+               struct sock **skp;
+
+               if(state == TCP_LISTEN)
+                       skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+               else
+                       skp = &tcp_established_hash[tcp_sk_hashfn(sk)];
+
+               if((sk->next = *skp) != NULL)
+                       (*skp)->pprev = &sk->next;
+               *skp = sk;
+               sk->pprev = skp;
+               tcp_sk_bindify(sk);
+       }
+       SOCKHASH_UNLOCK();
+}
+
+void tcp_v4_unhash(struct sock *sk)
+{
+       SOCKHASH_LOCK();
+       if(sk->pprev) {
+               if(sk->next)
+                       sk->next->pprev = sk->pprev;
+               *sk->pprev = sk->next;
+               sk->pprev = NULL;
+               tcp_sk_unbindify(sk);
+       }
+       SOCKHASH_UNLOCK();
+}
+
+void tcp_v4_rehash(struct sock *sk)
+{
+       unsigned char state;
+
+       SOCKHASH_LOCK();
+       state = sk->state;
+       if(sk->pprev) {
+               if(sk->next)
+                       sk->next->pprev = sk->pprev;
+               *sk->pprev = sk->next;
+               sk->pprev = NULL;
+               tcp_sk_unbindify(sk);
+       }
+       if(state != TCP_CLOSE || !sk->dead) {
+               struct sock **skp;
+
+               if(state == TCP_LISTEN)
+                       skp = &tcp_listening_hash[tcp_sk_listen_hashfn(sk)];
+               else
+                       skp = &tcp_established_hash[tcp_sk_hashfn(sk)];
+
+               if((sk->next = *skp) != NULL)
+                       (*skp)->pprev = &sk->next;
+               *skp = sk;
+               sk->pprev = skp;
+               tcp_sk_bindify(sk);
+       }
+       SOCKHASH_UNLOCK();
+}
+
 static void tcp_close(struct sock *sk, unsigned long timeout);
 
 /*
@@ -518,8 +717,7 @@ void tcp_err(int type, int code, unsigned char *header, __u32 daddr,
        if (len < 8)    /* NOT sizeof(struct tcphdr) */
                return;
                
-       sk = get_sock(&tcp_prot, th->source, daddr, th->dest, saddr, 0, 0);
-
+       sk = tcp_v4_lookup(daddr, th->dest, saddr, th->source);
        if (sk == NULL)
                return;
 
@@ -1640,6 +1838,7 @@ static int tcp_close_state(struct sock *sk, int dead)
                case TCP_CLOSE:
                case TCP_LISTEN:
                        break;
+               case TCP_LAST_ACK:      /* Could have shutdown() then close()!*/
                case TCP_CLOSE_WAIT:    /* They have FIN'd us. We send our FIN and
                                           wait only for the ACK */
                        ns=TCP_LAST_ACK;
@@ -1753,7 +1952,6 @@ static void tcp_close(struct sock *sk, unsigned long timeout)
 
        lock_sock(sk);
 
-       tcp_cache_zap();
        if(sk->state == TCP_LISTEN)
        {
                /* Special case */
@@ -1761,6 +1959,7 @@ static void tcp_close(struct sock *sk, unsigned long timeout)
                tcp_close_pending(sk);
                release_sock(sk);
                sk->dead = 1;
+               tcp_v4_unhash(sk);
                return;
        }
 
@@ -1813,12 +2012,6 @@ static void tcp_close(struct sock *sk, unsigned long timeout)
                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.
          */
@@ -1833,6 +2026,9 @@ static void tcp_close(struct sock *sk, unsigned long timeout)
 
        release_sock(sk);
        sk->dead = 1;
+
+       if(sk->state == TCP_CLOSE)
+               tcp_v4_unhash(sk);
 }
 
 
@@ -1916,27 +2112,24 @@ no_listen:
  */
 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;
 }
 
@@ -2077,12 +2270,13 @@ static int tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
        tcp_send_check(t1, sk->saddr, sk->daddr,
                  sizeof(struct tcphdr) + 4, buff);
 
-       /*
-        *      This must go first otherwise a really quick response will get reset.
+       tcp_set_state(sk,TCP_SYN_SENT);
+
+       /* Socket identity change complete, no longer
+        * in TCP_CLOSE, so rehash.
         */
+       tcp_v4_rehash(sk);
 
-       tcp_cache_zap();
-       tcp_set_state(sk,TCP_SYN_SENT);
        if(rt&&rt->rt_flags&RTF_IRTT)
                sk->rto = rt->rt_irtt;
        else
@@ -2174,27 +2368,34 @@ int tcp_getsockopt(struct sock *sk, int level, int optname, char *optval, int *o
 
 
 struct proto tcp_prot = {
-       tcp_close,
-       ip_build_header,
-       tcp_connect,
-       tcp_accept,
-       ip_queue_xmit,
-       tcp_retransmit,
-       tcp_write_wakeup,
-       tcp_read_wakeup,
-       tcp_rcv,
-       tcp_select,
-       tcp_ioctl,
-       NULL,
-       tcp_shutdown,
-       tcp_setsockopt,
-       tcp_getsockopt,
-       tcp_sendmsg,
-       tcp_recvmsg,
-       NULL,           /* No special bind() */
-       128,
-       0,
-       "TCP",
-       0, 0,
-       {NULL,}
+       (struct sock *)&tcp_prot,       /* sklist_next */
+       (struct sock *)&tcp_prot,       /* sklist_prev */
+       tcp_close,                      /* close */
+       ip_build_header,                /* build_header */
+       tcp_connect,                    /* connect */
+       tcp_accept,                     /* accept */
+       ip_queue_xmit,                  /* queue_xmit */
+       tcp_retransmit,                 /* retransmit */
+       tcp_write_wakeup,               /* write_wakeup */
+       tcp_read_wakeup,                /* read_wakeup */
+       tcp_rcv,                        /* rcv */
+       tcp_select,                     /* select */
+       tcp_ioctl,                      /* ioctl */
+       NULL,                           /* init */
+       tcp_shutdown,                   /* shutdown */
+       tcp_setsockopt,                 /* setsockopt */
+       tcp_getsockopt,                 /* getsockopt */
+       tcp_sendmsg,                    /* sendmsg */
+       tcp_recvmsg,                    /* recvmsg */
+       NULL,                           /* bind */
+       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 */
 };
index 831a303c556e6747efaee945ca9058de69ce26c0..e23180563f263e521322d7fb9b39078e57caf927 100644 (file)
  *             Eric Schenk     :       Delayed ACK bug fixes.
  *             Eric Schenk     :       Floyd style fast retrans war avoidance.
  *             Eric Schenk     :       Skip fast retransmit on small windows.
- *             Eric schenk     :       Fixes to retransmission code to
+ *             Eric Schenk     :       Fixes to retransmission code to
  *                             :       avoid extra retransmission.
  *             Theodore Ts'o   :       Do secure TCP sequence numbers.
+ *             Eric Schenk     :       SYN and RST cookies for dealing
+ *                             :       with SYN flooding attacks.
+ *             David S. Miller :       New socket lookup architecture for ISS.
+ *                                     This code is dedicated to John Dyson.
  */
 
 #include <linux/config.h>
@@ -146,42 +150,200 @@ extern __inline__ void tcp_rtt_estimator(struct sock *sk, struct sk_buff *oskb)
        sk->backoff = 0;
 }
 
+#if defined(CONFIG_RST_COOKIES)
+
 /*
- *     Cached last hit socket
+ * This code needs to be a bit more clever.
+ * Does 2 minute timeouts now. Still just a circular buffer.
+ * At most 32 validations stored. New validations are ignored
+ * if all 32 validations are currently valid. To do otherwise
+ * allows a situation in which clearances are forgotten before
+ * they can be used (provided valid traffic is coming fast enough).
+ * The buffer should really be as long as the number of valid
+ * connections we want to accept in an 2 minute period.
+ * 32 is maybe to small. On the other hand, the validation check
+ * algorithm has to walk the whole table, which is also stupid.
+ * It would be better to have a combined hash/circular buffer.
+ * The hash could be used with chaining for fast lookup.
+ * Really this is probably an argument against using RST cookies
+ * at all, since they take up space for the clearances.
  */
-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;
 
-void tcp_cache_zap(void)
+static struct {
+       u32 saddr;
+       unsigned long tstamp;
+} clearances[32] = {
+{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
+{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
+{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},
+{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}};
+
+static next_clearance = 0;
+/* Does the address saddr have an active security clearance? */
+int tcp_clearance(__u32 saddr)
+{
+       int i;
+       for (i = 0; i < 32; i++)
+               if (clearances[i].saddr == saddr
+               && clearances[i].tstamp > jiffies-HZ*120)
+                       return 1;
+       return 0;
+}
+
+void add_clearance(__u32 saddr)
 {
-       th_cache_sk=NULL;
+       /*
+        * If expired then we can add a new entry.
+        */
+       if (clearances[next_clearance].tstamp <= jiffies-HZ*300) {
+               clearances[next_clearance].saddr = saddr;
+               clearances[next_clearance].tstamp = jiffies;
+               next_clearance = (next_clearance+1)%32;
+       }
 }
 
+#endif
+
+#ifdef CONFIG_SYN_COOKIES
 /*
- *     Find the socket, using the last hit cache if applicable. The cache is not quite
- *     right...
+ *     MTU values we can represent in fall back mode.
+ *     FIXME. I sort of picked these out of a hat. I should
+ *     probably look around for docs on what common values are.
  */
+static __u32 cookie_mtu[8] = { 64, 128, 256, 296, 512, 576, 1024, 1500 };
+#endif
+
+extern void tcp_v4_hash(struct sock *sk);
+extern void tcp_v4_unhash(struct sock *sk);
+extern void tcp_v4_rehash(struct sock *sk);
 
-static inline struct sock * get_tcp_sock(u32 saddr, u16 sport, u32 daddr, u16 dport, u32 paddr, u16 pport)
+/* 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;
-
-       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, paddr, pport);
-               if (sk) {
-                       th_cache_saddr=saddr;
-                       th_cache_daddr=daddr;
-                       th_cache_dport=dport;
-                       th_cache_sport=sport;
-                       th_cache_sk=sk;
+       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)->bind_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
 
 /*
  * React to a out-of-window TCP sequence number in an incoming packet
@@ -363,7 +525,10 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
        struct sock *newsk;
        struct tcphdr *th;
        struct rtable *rt;
-  
+#ifdef CONFIG_SYN_COOKIES
+       int send_cookie = 0;
+#endif
+
        th = skb->h.th;
 
        /* If the socket is dead, don't accept the connection. */
@@ -387,13 +552,65 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
         *
         *      BSD does some funnies here and allows 3/2 times the
         *      set backlog as a fudge factor. That's just too gross.
+        *
+         *     Well, now I'm making things even grosser for dealing
+        *      with SYNACK flooding.
         */
 
-       if (sk->ack_backlog >= sk->max_ack_backlog) 
+       if (sk->ack_backlog >= sk->max_ack_backlog)
        {
+#if defined(CONFIG_RST_COOKIES) || defined(CONFIG_SYN_COOKIES)
+               static unsigned long warning_time = 0;
+
+               /* We may be experiencing SYNACK flooding.
+                * We now must decide if we should accept this connection.
+                * If we have a security clearance for the incoming
+                * packet, i.e. it is from a location we where talking
+                * to succesfully recently, or that has responded to
+                * a security probe, then we go ahead and deal normally,
+                * accepting up to 2*max in the backlog.
+                * Otherwise, we send out either an RST security probe
+                * or a SYN cookie, or both. (depending on configuration).
+                * Note that we send out a cookie even if the backlog
+                * is full up to 2*max, since the backlog may clear
+                * by the time we get a response.
+                * WARNING: This code changes the semantics of the backlog
+                * a bit. I'm not entirely sure this is the right thing
+                * to do here.
+                */
+               extern void tcp_send_synack_probe(unsigned long saddr,
+                                                 unsigned long daddr, struct tcphdr *th,
+                                                 struct proto *prot,
+                                                 struct options *opt,
+                                                 struct device *dev, int tos, int ttl);
+
+#ifdef CONFIG_RST_COOKIES
+               if (!tcp_clearance(saddr)) {
+#endif
+                       /* Only let this warning get printed once a minute. */
+                       if (jiffies - warning_time > HZ) {
+                               warning_time = jiffies;
+                               printk(KERN_INFO "Warning: possible SYN flooding. Sending cookies.\n");
+                       }
+#ifdef CONFIG_RST_COOKIES
+                       tcp_send_synack_probe(daddr, saddr, th, &tcp_prot,
+                               opt, dev, skb->ip_hdr->tos, 255);
+#endif
+#ifdef CONFIG_SYN_COOKIES
+                       send_cookie = 1;
+#endif
+#ifdef CONFIG_RST_COOKIES
+               } else if (sk->ack_backlog >= 2*sk->max_ack_backlog) {
+                       tcp_statistics.TcpAttemptFails++;
+                       kfree_skb(skb, FREE_READ);
+                       return;
+               }
+#endif
+#else
                tcp_statistics.TcpAttemptFails++;
                kfree_skb(skb, FREE_READ);
                return;
+#endif
        }
 
        /*
@@ -414,6 +631,10 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
        }
 
        memcpy(newsk, sk, sizeof(*newsk));
+
+       /* Or else we die! -DaveM */
+       newsk->sklist_next = NULL;
+
        newsk->opt = NULL;
        newsk->ip_route_cache  = NULL;
        if (opt && opt->optlen) 
@@ -483,9 +704,6 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
        newsk->state = TCP_SYN_RECV;
        newsk->timeout = 0;
        newsk->ip_xmit_timeout = 0;
-       newsk->write_seq = seq; 
-       newsk->window_seq = newsk->write_seq;
-       newsk->rcv_ack_seq = newsk->write_seq;
        newsk->urg_data = 0;
        newsk->retransmits = 0;
        newsk->linger=0;
@@ -518,8 +736,9 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
        newsk->daddr = saddr;
        newsk->saddr = daddr;
        newsk->rcv_saddr = daddr;
+       tcp_v4_hash(newsk);
+       add_to_prot_sklist(newsk);
 
-       put_sock(newsk->num,newsk);
        newsk->acked_seq = skb->seq + 1;
        newsk->copied_seq = skb->seq + 1;
        newsk->socket = NULL;
@@ -579,12 +798,245 @@ static void tcp_conn_request(struct sock *sk, struct sk_buff *skb,
         */
 
        tcp_options(newsk,skb->h.th);
-       
-       tcp_cache_zap();
-       tcp_send_synack(newsk, sk, skb);
+
+#ifdef CONFIG_SYN_COOKIES
+       if (send_cookie) {
+               int mtu_index = 0;
+               /* Pick the largest MTU smaller than sk->mtu that we
+                * can represent in a cookies bottom 3 bits.
+                */
+               while (newsk->mtu > cookie_mtu[mtu_index+1] && mtu_index < 7)
+                       mtu_index++;
+               newsk->mtu = cookie_mtu[mtu_index];
+               /*
+                * Choose a cookie.
+                */
+               seq = secure_tcp_syn_cookie(daddr,saddr,
+                       ntohs(th->source),ntohs(th->dest),ntohl(th->seq),jiffies/(60*HZ));
+               seq |= mtu_index;
+       }
+#endif
+
+       /* Set up the right sequence numbers */
+       newsk->write_seq = seq; 
+       newsk->window_seq = newsk->write_seq;
+       newsk->rcv_ack_seq = newsk->write_seq;
+
+#ifdef CONFIG_SYN_COOKIES
+       tcp_send_synack(newsk, sk, skb, send_cookie);
+#else
+       tcp_send_synack(newsk, sk, skb, 0);
+#endif
 }
 
 
+#ifdef CONFIG_SYN_COOKIES
+/*
+ *     This routine handles a faked connection request as a result
+ *     of a valid SYN cookie being seen. This sets up a socket in the
+ *     SYN_SENT state.
+ */
+static int tcp_conn_request_fake(struct sock *sk, struct sk_buff *skb,
+                u32 daddr, u32 saddr, struct options *opt, struct device *dev, u32 seq, u32 mtu)
+{
+       struct sock *newsk;
+       struct sk_buff *newskb;
+       struct rtable *rt;
+
+       /* If the socket is dead, don't accept the connection. */
+       if (!sk->dead) 
+       {
+               sk->data_ready(sk,0);
+       }
+       else 
+       {
+               if(sk->debug)
+                       printk("Reset on %p: Connect on dead socket.\n",sk);
+               tcp_statistics.TcpAttemptFails++;
+               return 0;
+       }
+
+       /*
+        * We need to build a new sock struct.
+        * It is sort of bad to have a socket without an inode attached
+        * to it, but the wake_up's will just wake up the listening socket,
+        * and if the listening socket is destroyed before this is taken
+        * off of the queue, this will take care of it.
+        */
+
+       newsk = (struct sock *) kmalloc(sizeof(struct sock), GFP_ATOMIC);
+       if (newsk == NULL) 
+       {
+               /* Bad juju. If we ignore things now the remote side
+                * will be frozen. Really we should retrans the cookie,
+                * but that's a no go also, since we don't have enough
+                * memory to receive it either. So, we're stuck with
+                * this bad case, and a few others further down.
+                * We just have to hope it is a low probability event.
+                * Also, to avoid a loop we must not go down into
+                * the recursive call to tcp_rcv in the caller to this
+                * routine, so we should let them know we failed.
+                */
+               tcp_statistics.TcpAttemptFails++;
+               return 0;
+       }
+
+       memcpy(newsk, sk, sizeof(*newsk));
+
+       /* Or else we die! -DaveM */
+       newsk->sklist_next = NULL;
+
+       newsk->opt = NULL;
+       newsk->ip_route_cache  = NULL;
+       if (opt && opt->optlen) 
+       {
+               sk->opt = (struct options*)kmalloc(sizeof(struct options)+opt->optlen, GFP_ATOMIC);
+               if (!sk->opt) 
+               {
+                       /* More bad juju. */
+                       kfree_s(newsk, sizeof(struct sock));
+                       tcp_statistics.TcpAttemptFails++;
+                       return 0;
+               }
+               if (ip_options_echo(sk->opt, opt, daddr, saddr, skb)) 
+               {
+                       /* More bad juju. */
+                       kfree_s(sk->opt, sizeof(struct options)+opt->optlen);
+                       kfree_s(newsk, sizeof(struct sock));
+                       tcp_statistics.TcpAttemptFails++;
+                       return 0;
+               }
+       }
+       
+       skb_queue_head_init(&newsk->write_queue);
+       skb_queue_head_init(&newsk->receive_queue);
+       newsk->send_head = NULL;
+       newsk->send_tail = NULL;
+       newsk->send_next = NULL;
+       skb_queue_head_init(&newsk->back_log);
+       newsk->rtt = 0;
+       newsk->rto = TCP_TIMEOUT_INIT;
+       newsk->mdev = TCP_TIMEOUT_INIT;
+       newsk->max_window = 0;
+       /*
+        * See draft-stevens-tcpca-spec-01 for discussion of the
+        * initialization of these values.
+        */
+       newsk->cong_window = 1;
+       newsk->cong_count = 0;
+       newsk->ssthresh = 0x7fffffff;
+
+       newsk->lrcvtime = 0;
+       newsk->idletime = 0;
+       newsk->high_seq = 0;
+       newsk->backoff = 0;
+       newsk->blog = 0;
+       newsk->intr = 0;
+       newsk->proc = 0;
+       newsk->done = 0;
+       newsk->partial = NULL;
+       newsk->pair = NULL;
+       newsk->wmem_alloc = 0;
+       newsk->rmem_alloc = 0;
+       newsk->localroute = sk->localroute;
+
+       newsk->max_unacked = MAX_WINDOW - TCP_WINDOW_DIFF;
+
+       newsk->err = 0;
+       newsk->shutdown = 0;
+       newsk->ack_backlog = 0;
+       newsk->acked_seq = skb->seq;
+       newsk->lastwin_seq = skb->seq;
+       newsk->delay_acks = 1;
+       newsk->copied_seq = skb->seq;
+       newsk->fin_seq = skb->seq-1;
+       newsk->syn_seq = skb->seq-1;
+       newsk->state = TCP_SYN_RECV;
+       newsk->timeout = 0;
+       newsk->ip_xmit_timeout = 0;
+       newsk->urg_data = 0;
+       newsk->retransmits = 0;
+       newsk->linger=0;
+       newsk->destroy = 0;
+       init_timer(&newsk->timer);
+       newsk->timer.data = (unsigned long)newsk;
+       newsk->timer.function = &net_timer;
+       init_timer(&newsk->delack_timer);
+       newsk->delack_timer.data = (unsigned long)newsk;
+       newsk->delack_timer.function = tcp_delack_timer;
+       init_timer(&newsk->retransmit_timer);
+       newsk->retransmit_timer.data = (unsigned long)newsk;
+       newsk->retransmit_timer.function = tcp_retransmit_timer;
+       newsk->dummy_th.source = skb->h.th->dest;
+       newsk->dummy_th.dest = skb->h.th->source;
+       newsk->users=0;
+       
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+       /* 
+        *      Deal with possibly redirected traffic by setting num to
+        *      the intended destination port of the received packet.
+        */
+       newsk->num = ntohs(skb->h.th->dest);
+
+#endif
+       /*
+        *      Swap these two, they are from our point of view. 
+        */
+        
+       newsk->daddr = saddr;
+       newsk->saddr = daddr;
+       newsk->rcv_saddr = daddr;
+       tcp_v4_hash(newsk);
+       add_to_prot_sklist(newsk);
+
+       newsk->acked_seq = skb->seq;
+       newsk->copied_seq = skb->seq;
+       newsk->socket = NULL;
+
+       /*
+        *      Grab the ttl and tos values and use them 
+        */
+
+       newsk->ip_ttl=sk->ip_ttl;
+       newsk->ip_tos=skb->ip_hdr->tos;
+
+       rt = ip_rt_route(newsk->opt && newsk->opt->srr ? newsk->opt->faddr : saddr, 0);
+       newsk->ip_route_cache = rt;
+       
+       if (rt!=NULL && (rt->rt_flags&RTF_WINDOW))
+               newsk->window_clamp = rt->rt_window;
+       else
+               newsk->window_clamp = 0;
+
+       newsk->mtu = mtu;
+
+       /* Set up the right sequence numbers.
+        * Note that we have to make sure write_seq is correct for having
+        * sent off the handshake!
+        */
+       newsk->write_seq = seq+1; 
+       newsk->sent_seq = seq+1;
+       newsk->window_seq = seq;
+       newsk->rcv_ack_seq = seq;
+       newsk->max_unacked = 2 * newsk->mss;
+
+       tcp_select_window(newsk);
+
+       /* We need to get something into the receive queue to enable an
+        * accept. Possibly we should be faking up a SYN packet, but
+        * as far as I can tell the contents of this skb don't matter,
+        * so long as it points to our new socket.
+        */
+       newskb = skb_clone(skb,GFP_ATOMIC);
+       newskb->sk = newsk;
+       atomic_add(skb->truesize, &newsk->rmem_alloc);
+       sk->ack_backlog++;
+       skb_queue_tail(&sk->receive_queue,newskb);
+       return 1;
+}
+#endif
+
 /*
  * Handle a TCP window that shrunk on us. It shouldn't happen,
  * but..
@@ -787,7 +1239,7 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack, int len)
 
        if (sk->rcv_ack_seq == ack
                && sk->window_seq == window_seq
-               && len != th->doff*4
+               && len == th->doff*4
                && before(ack, sk->sent_seq)
                && after(ack, sk->high_seq))
        {
@@ -830,7 +1282,8 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack, int len)
        else
        {
                if (sk->rcv_ack_cnt > MAX_DUP_ACKS) {
-                       sk->cong_window = sk->ssthresh;
+                       /* Don't allow congestion window to drop to zero. */
+                       sk->cong_window = max(sk->ssthresh, 1);
                }
                sk->window_seq = window_seq;
                sk->rcv_ack_seq = ack;
@@ -1166,7 +1619,12 @@ static int tcp_ack(struct sock *sk, struct tcphdr *th, u32 ack, int len)
        {
                tcp_set_state(sk, TCP_ESTABLISHED);
                tcp_options(sk,th);
+
+#if 0
                sk->dummy_th.dest=th->source;
+               tcp_v4_rehash(sk);
+#endif
+
                sk->copied_seq = sk->acked_seq;
                if(!sk->dead)
                        sk->state_change(sk);
@@ -1738,11 +2196,12 @@ int tcp_chkaddr(struct sk_buff *skb)
        struct tcphdr *th = (struct tcphdr *)(skb->h.raw + iph->ihl*4);
        struct sock *sk;
 
-       sk = get_sock(&tcp_prot, th->dest, iph->saddr, th->source, iph->daddr, 0, 0);
-
-       if (!sk) return 0;
+       sk = tcp_v4_lookup(iph->saddr, th->source, iph->daddr, th->dest);
+       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
@@ -1771,6 +2230,11 @@ int tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
         */
        th = skb->h.th;
        sk = skb->sk;
+#ifdef CONFIG_RST_COOKIES
+       if (th->rst && secure_tcp_probe_number(saddr,daddr,ntohs(th->source),ntohs(th->dest),ntohl(th->seq),1)) {
+               add_clearance(saddr);
+       }
+#endif
        if (!redo) {
                tcp_statistics.TcpInSegs++;
                if (skb->pkt_type!=PACKET_HOST)
@@ -1795,7 +2259,10 @@ int tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
                        default:
                                /* CHECKSUM_UNNECESSARY */
                }
-               sk = get_tcp_sock(saddr, th->source, daddr, th->dest, dev->pa_addr, skb->redirport);
+#ifdef CONFIG_SYN_COOKIES
+retry_search:
+#endif
+               sk = __tcp_v4_lookup(th, saddr, th->source, daddr, th->dest);
                if (!sk)
                        goto no_tcp_socket;
                skb->sk = sk;
@@ -1828,8 +2295,9 @@ int tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
         *      exist so should cause resets as if the port was unreachable.
         */
 
-       if (sk->zapped || sk->state==TCP_CLOSE)
+       if (sk->zapped || sk->state==TCP_CLOSE) {
                goto no_tcp_socket;
+       }
 
        if (!sk->prot) 
        {
@@ -1870,8 +2338,42 @@ int tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
         
                if(sk->state==TCP_LISTEN)
                {
-                       if(th->ack)     /* These use the socket TOS.. might want to be the received TOS */
+                       if (th->ack) {  /* These use the socket TOS.. might want to be the received TOS */
+#ifdef CONFIG_SYN_COOKIES
+                               if (!th->syn && !th->rst) {
+                                       __u32 acked_seq = ntohl(th->ack_seq)-1;
+                                       int mtu_index = (acked_seq&0x7); /* extract MTU */
+                                       __u32 count = jiffies/(60*HZ);
+
+                                       acked_seq = acked_seq&0xfffffff8;
+
+                                       /* Any time in the last 2 minutes is OK */
+                                       if (acked_seq == secure_tcp_syn_cookie(daddr,
+                                           saddr,ntohs(th->source),ntohs(th->dest),
+                                           ntohl(th->seq)-1,count)
+                                       || acked_seq == secure_tcp_syn_cookie(daddr,
+                                           saddr,ntohs(th->source),ntohs(th->dest),
+                                           ntohl(th->seq)-1,count-1)
+                                       || acked_seq == secure_tcp_syn_cookie(daddr,
+                                           saddr,ntohs(th->source),ntohs(th->dest),
+                                           ntohl(th->seq)-1,count-2)) {
+                                               /* If this passes, we need to fake up the
+                                               * new socket in TCP_SYN_SENT state and
+                                               * call ourselves recursively to handle
+                                               * the move to ESTABLISHED using the
+                                               * current packet. Nasty, but a cleaner
+                                               * solution would require major rewrites.
+                                               */
+                                               if (tcp_conn_request_fake(sk, skb, daddr, saddr, opt,
+                                                                         dev, (acked_seq | mtu_index), cookie_mtu[mtu_index])) {
+
+                                                       goto retry_search;
+                                               }
+                                       }
+                               }
+#endif
                                tcp_send_reset(daddr,saddr,th,sk->prot,opt,dev,0, 255);
+                       }
 
                        /*
                         *      We don't care for RST, and non SYN are absorbed (old segments)
@@ -1987,7 +2489,12 @@ int tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
                                tcp_send_ack(sk);
                                tcp_set_state(sk, TCP_ESTABLISHED);
                                tcp_options(sk,th);
+
+#if 0
                                sk->dummy_th.dest=th->source;
+                               tcp_v4_rehash(sk);
+#endif
+
                                sk->copied_seq = sk->acked_seq;
                                if(!sk->dead)
                                {
@@ -2063,12 +2570,22 @@ int tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
                        sk->err=ECONNRESET;
                        tcp_set_state(sk, TCP_CLOSE);
                        sk->shutdown = SHUTDOWN_MASK;
-                       sk=get_sock(&tcp_prot, th->dest, saddr, th->source, daddr, dev->pa_addr, skb->redirport);
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+                       sk = tcp_v4_proxy_lookup(th->dest, saddr, th->source, daddr,
+                                                dev->pa_addr, skb->redirport);
+#else
+                       sk = NULL;
+#endif
                        /* this is not really correct: we should check sk->users */
                        if (sk && sk->state==TCP_LISTEN)
                        {
                                skb->sk = sk;
                                atomic_add(skb->truesize, &sk->rmem_alloc);
+                               /* FIXME: Is the sequence number addition
+                                * of 128000 here enough for fast networks?
+                                * Also, does this reduce the security of
+                                * our tcp sequence numbers?
+                                */
                                tcp_conn_request(sk, skb, daddr, saddr,opt, dev,seq+128000);
                                return 0;
                        }
@@ -2170,7 +2687,7 @@ rfc_step6:                /* I'll clean this up later */
 
 no_tcp_socket:
        /*
-        *      No such TCB. If th->rst is 0 send a reset (checked in tcp_send_reset)
+        * No such TCB. If th->rst is 0 send a reset (checked in tcp_send_reset)
         */
        tcp_send_reset(daddr, saddr, th, &tcp_prot, opt,dev,0,255);
 
index 9ca4ab140e9d4d525016616665d633402a1858f4..3e770cf0abac2d91dfd7ca38d4b86179a4c3e09c 100644 (file)
  *
  * Fixes:      Eric Schenk     : avoid multiple retransmissions in one
  *                             : round trip timeout.
+ *             Eric Schenk     : tcp rst and syn cookies to deal
+ *                             : with synflooding attacks.
+ *             Eric Schenk     : If a simultaneous close occurred, and the
+ *                               connection was over an assymetric route, we
+ *                               would lose badly if we dropped our outgoing
+ *                               FIN because the route disappeared on us.
+ *                               We now handle this case correctly.
+ *             Eric Schenk     : Handle the case where a route changes, and
+ *                               thus the outgoing device does as well, when
+ *                               skb's are on the retransmit queue which still
+ *                               refer to the old obsolete destination.
  */
 
 #include <linux/config.h>
@@ -28,7 +39,9 @@
 #include <linux/ip_fw.h>
 #include <linux/firewall.h>
 #include <linux/interrupt.h>
-
+#ifdef CONFIG_RST_COOKIES
+#include <linux/random.h>
+#endif
 
 /*
  * RFC 1122 says:
@@ -277,9 +290,12 @@ void tcp_enqueue_partial(struct sk_buff * skb, struct sock * sk)
        sk->partial = skb;
        init_timer(&sk->partial_timer);
        /*
-        *      Wait up to 1 second for the buffer to fill.
+        *      Wait up to 30 second for the buffer to fill.
+        *      ( I have no idea why this timer is here!
+        *        It seems to be sillyness for interactive response. Linus?
+        *      )
         */
-       sk->partial_timer.expires = jiffies+HZ/10;
+       sk->partial_timer.expires = jiffies+30*HZ;
        sk->partial_timer.function = (void (*)(unsigned long)) tcp_send_partial;
        sk->partial_timer.data = (unsigned long) sk;
        add_timer(&sk->partial_timer);
@@ -383,8 +399,11 @@ void tcp_write_xmit(struct sock *sk)
                        /*
                         *      IP manages our queue for some crazy reason
                         */
-                        
+#ifndef NO_DAVEM_FIX                    
+                       sk->prot->queue_xmit(sk, skb->dev, skb, 0);
+#else
                        sk->prot->queue_xmit(sk, skb->dev, skb, skb->free);
+#endif
 
                        clear_delayed_acks(sk);
 
@@ -498,6 +517,45 @@ void tcp_do_retransmit(struct sock *sk, int all)
                else
                {
                        dev=rt->rt_dev;
+                       if (skb->dev != dev && skb->link3 == 0
+                       && !skb_queue_empty(&sk->write_queue)) {
+                               /* THIS IS UGLY. DON'T SHOW THIS TO YOUR MOTHER. --erics
+                                * Route shifted devices.
+                                * If this is the last packet in the
+                                * retransmit queue, then we should walk
+                                * the chain of packets in the write_queue
+                                * that have the same device and
+                                * fix routing on these packets as well.
+                                * If we fail to do this, then every packet
+                                * in the transmit queue will incurr a 
+                                * retransmit with the backed off retransmit
+                                * timeout. This is very bad.
+                                */
+                               struct sk_buff *skb2 = sk->write_queue.next;
+                               while (skb2 && skb2->dev == skb->dev) {
+                                       skb2->raddr=rt->rt_gateway;
+                                       skb2->dev = dev;
+                                       skb2->arp=1;
+                                       if (rt->rt_hh)
+                                       {
+                                               memcpy(skb_push(skb2,dev->hard_header_len),rt->rt_hh->hh_data,dev->hard_header_len);
+                                               if (!rt->rt_hh->hh_uptodate)
+                                               {
+                                                       skb2->arp = 0;
+#if RT_CACHE_DEBUG >= 2
+                                                       printk("tcp_do_retransmit(1): hh miss %08x via %08x\n", iph->daddr, rt->rt_gateway);
+#endif
+                                               }
+                                       }
+                                       else if (dev->hard_header)
+                                       {
+                                               if(dev->hard_header(skb2, dev, ETH_P_IP, NULL, NULL, skb2->len)<0)
+                                                       skb2->arp=0;
+                                       }
+
+                                       skb2 = skb2->next;
+                               }
+                       }
                        skb->raddr=rt->rt_gateway;
                        skb->dev=dev;
                        skb->arp=1;
@@ -519,7 +577,7 @@ void tcp_do_retransmit(struct sock *sk, int all)
                                {
                                        skb->arp = 0;
 #if RT_CACHE_DEBUG >= 2
-                                       printk("tcp_do_retransmit: hh miss %08x via %08x\n", iph->daddr, rt->rt_gateway);
+                                       printk("tcp_do_retransmit(2): hh miss %08x via %08x\n", iph->daddr, rt->rt_gateway);
 #endif
                                }
                        }
@@ -685,6 +743,73 @@ void tcp_send_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th,
        tcp_statistics.TcpOutSegs++;
 }
 
+#ifdef CONFIG_RST_COOKIES
+/*
+ *     This routine will send a bad SYNACK to the remote tcp
+ *     containing a secure sequence number.
+ *     This should evoke a reset with a cookie, so we can verify
+ *     the existence of the remote machine.
+ */
+void tcp_send_synack_probe(unsigned long saddr, unsigned long daddr, struct tcphdr *th,
+         struct proto *prot, struct options *opt, struct device *dev, int tos, int ttl)
+{
+       struct sk_buff *buff;
+       struct tcphdr *t1;
+       int tmp;
+       struct device *ndev=NULL;
+
+       /*
+        * We need to grab some memory, and put together a SYNACK,
+        * and then put it into the queue to be sent.
+        */
+
+       buff = alloc_skb(MAX_SYN_SIZE, GFP_ATOMIC);
+       if (buff == NULL) 
+               return;
+
+       buff->sk = NULL;
+       buff->dev = dev;
+       buff->localroute = 0;
+       buff->csum = 0;
+
+       /*
+        *      Put in the IP header and routing stuff. 
+        */
+
+       tmp = prot->build_header(buff, saddr, daddr, &ndev, IPPROTO_TCP, opt,
+                          sizeof(struct tcphdr),tos,ttl,NULL);
+       if (tmp < 0) 
+       {
+               buff->free = 1;
+               sock_wfree(NULL, buff);
+               return;
+       }
+
+       t1 = (struct tcphdr *)skb_put(buff,sizeof(struct tcphdr));
+
+       memcpy(t1, th, sizeof(*t1));
+       /*
+        *      Swap the send and the receive. 
+        */
+       t1->dest = th->source;
+       t1->source = th->dest;
+       t1->ack_seq = t1->seq = htonl(secure_tcp_probe_number(daddr,saddr,
+               ntohs(th->source),ntohs(th->dest),ntohl(th->seq),0));
+       t1->window = htons(1024);       /* make up a window here. */
+       t1->syn = 1;
+       t1->ack = 1;
+       t1->urg = 0;
+       t1->rst = 0;
+       t1->psh = 0;
+       t1->doff = sizeof(*t1)/4;
+
+       tcp_send_check(t1, saddr, daddr, sizeof(*t1), buff);
+       prot->queue_xmit(NULL, ndev, buff, 1);
+       tcp_statistics.TcpOutSegs++;
+}
+#endif
+
 /*
  *     Send a fin.
  */
@@ -724,21 +849,31 @@ void tcp_send_fin(struct sock *sk)
                           sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
        if (tmp < 0) 
        {
-               int t;
-               /*
-                *      Finish anyway, treat this as a send that got lost. 
-                *      (Not good).
-                */
-                
-               buff->free = 1;
-               sock_wfree(sk,buff);
-               sk->write_seq++;
-               t=del_timer(&sk->timer);
-               if(t)
-                       add_timer(&sk->timer);
-               else
-                       tcp_reset_msl_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
-               return;
+               /* Oh oh. We couldn't route the packet, and we can't afford
+                * to drop it from the queue, since we will fail to retransmit
+                * then, and we never try to initiate a close again.
+                * Drop it onto the loopback device. The worst thing that
+                * happens is that the send gets droped when it comes out the
+                * the other side. If we get lucky it might even get forward
+                * to its real destination.
+                * WARNING: there are a few subtle points here.
+                * 1) We assume that if we build the header using the
+                *    loopback we can not fail. The only way this can happen
+                *    right now is if someone marks the loopback as
+                *    a gateway. This should never happen. Be careful
+                *    not to change that without taking this case into account.
+                * 2) If we fail to queue up the FIN packet here we get
+                *    bitten later when we receive a simultaneous FIN.
+                *    See the comments in tcp_fin().
+                */
+               dev = &loopback_dev;
+               tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev,
+                          IPPROTO_TCP, sk->opt,
+                          sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl,&sk->ip_route_cache);
+               if (tmp < 0) {
+                       printk(KERN_CRIT "tcp_send_fin: Impossible loopback failure");
+                       return;
+               }
        }
        
        /*
@@ -782,7 +917,7 @@ void tcp_send_fin(struct sock *sk)
 }
 
 
-void tcp_send_synack(struct sock * newsk, struct sock * sk, struct sk_buff * skb)
+void tcp_send_synack(struct sock * newsk, struct sock * sk, struct sk_buff * skb, int destroy)
 {
        struct tcphdr *t1;
        unsigned char *ptr;
@@ -853,19 +988,33 @@ void tcp_send_synack(struct sock * newsk, struct sock * sk, struct sk_buff * skb
        ptr[3] =(newsk->mtu) & 0xff;
        buff->csum = csum_partial(ptr, 4, 0);
        tcp_send_check(t1, newsk->saddr, newsk->daddr, sizeof(*t1)+4, buff);
-       newsk->prot->queue_xmit(newsk, ndev, buff, 0);
-       tcp_reset_xmit_timer(newsk, TIME_WRITE , TCP_TIMEOUT_INIT);
-       skb->sk = newsk;
+       newsk->prot->queue_xmit(newsk, ndev, buff, destroy);
 
-       /*
-        *      Charge the sock_buff to newsk. 
-        */
-        
-       atomic_sub(skb->truesize, &sk->rmem_alloc);
-       atomic_add(skb->truesize, &newsk->rmem_alloc);
+
+#ifdef CONFIG_SYN_COOKIES
+       if (destroy) {
+               /*
+                * Get rid of the newsk structure if this was a cookie.
+                */
+               destroy_sock(newsk);
+               skb->sk = sk;
+               kfree_skb(skb, FREE_READ);
+       } else {
+#endif
+               tcp_reset_xmit_timer(newsk, TIME_WRITE , TCP_TIMEOUT_INIT);
+               skb->sk = newsk;
+
+               /*
+                *      Charge the sock_buff to newsk. 
+                */
+               atomic_sub(skb->truesize, &sk->rmem_alloc);
+               atomic_add(skb->truesize, &newsk->rmem_alloc);
        
-       skb_queue_tail(&sk->receive_queue,skb);
-       sk->ack_backlog++;
+               skb_queue_tail(&sk->receive_queue,skb);
+               sk->ack_backlog++;
+#ifdef CONFIG_SYN_COOKIES
+       }
+#endif
        tcp_statistics.TcpOutSegs++;
 }
 
@@ -976,10 +1125,9 @@ void tcp_send_ack(struct sock *sk)
                sock_wfree(sk, buff);
                return;
        }
-#if 0  /* why does this result in problems? */
+
 #ifndef CONFIG_NO_PATH_MTU_DISCOVERY
        buff->ip_hdr->frag_off |= htons(IP_DF);
-#endif
 #endif
 
        t1 =(struct tcphdr *)skb_put(buff,sizeof(struct tcphdr));
@@ -1048,17 +1196,21 @@ void tcp_write_wakeup(struct sock *sk)
 #endif
        
                /*
-                *      How many bytes can we send ?
+                *      Recover the buffer pointers
                 */
                 
-               win_size = sk->window_seq - sk->sent_seq;
+               iph = (struct iphdr *)skb->ip_hdr;
+               th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2));
 
                /*
-                *      Recover the buffer pointers
+                *      How many bytes can we send ?
                 */
                 
-               iph = (struct iphdr *)skb->ip_hdr;
-               th = (struct tcphdr *)(((char *)iph) +(iph->ihl << 2));
+               /* During window probes, don't try to send more than is
+                * actually in the skb we've taken off the send queue here.
+                */
+               win_size = skb->len - (((unsigned char *) th) - skb->data);
+               win_size -= th->doff * 4;
 
                /*
                 *      Grab the data for a temporary frame
index 35de9fe8a125c4bd6839d446bda77c6aa38aea59..4e9c6d2b5baf4cd0b3f588b834c02f03e3e8cb7e 100644 (file)
@@ -116,6 +116,7 @@ static void tcp_retransmit_time(struct sock *sk, int all)
        if (sk->send_head)
                tcp_reset_xmit_timer(sk, TIME_WRITE, sk->rto);
        else
+               /* This should never happen! */
                printk(KERN_ERR "send_head NULL in tcp_retransmit_time\n");
 }
 
@@ -167,9 +168,15 @@ static int tcp_write_timeout(struct sock *sk)
        
        /*
         *      Have we tried to SYN too many times (repent repent 8))
+        *      NOTE: we must be careful to do this test for both
+        *      the SYN_SENT and SYN_RECV states, otherwise we take
+        *      23 minutes to timeout on the SYN_RECV state, which
+        *      leaves us (more) open to denial of service attacks
+        *      than we would like.
         */
         
-       if(sk->retransmits > TCP_SYN_RETRIES && sk->state==TCP_SYN_SENT)
+       if (sk->retransmits > TCP_SYN_RETRIES
+       && (sk->state==TCP_SYN_SENT || sk->state==TCP_SYN_RECV))
        {
                if(sk->err_soft)
                        sk->err=sk->err_soft;
index 432bb10f82d4509af8c1589300f53e60025e0276..e58d9342f0d8664bb2efbf079aae66c97f0e0de7 100644 (file)
@@ -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 for ISS.
+ *                                     Last socket cache retained as it
+ *                                     does have a high hit rate.
  *
  *
  *             This program is free software; you can redistribute it and/or
 
 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, 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)) {
+                       int sk2_reuse = sk2->reuse;
+
+                       if(!sk2->rcv_saddr || !sk->rcv_saddr) {
+                               if((!sk2_reuse) || (!sk_reuse)) {
+                                       retval = 1;
+                                       break;
+                               }
+                       } else if(sk2->rcv_saddr == sk->rcv_saddr) {
+                               if((!sk_reuse) || (!sk2_reuse)) {
+                                       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;
+}
+
+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);
+       }
+       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;
+       SOCKHASH_UNLOCK();
+}
+
+/* UDP is nearly always wildcards out the wazoo, it makes no sense to try
+ * harder than this. -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;
+__inline__ struct sock *udp_v4_lookup(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;
+}
 
-void udp_cache_zap(void)
+#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)
 {
-       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 *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))
@@ -165,8 +408,7 @@ void udp_err(int type, int code, unsigned char *header, __u32 daddr,
        
        uh = (struct udphdr *)header;  
    
-       sk = get_sock(&udp_prot, uh->source, daddr, uh->dest, saddr, 0, 0);
-
+       sk = udp_v4_lookup(daddr, uh->dest, saddr, uh->source);
        if (sk == NULL) 
                return; /* No socket for error */
        
@@ -618,7 +860,6 @@ int udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
        sk->daddr = usin->sin_addr.s_addr;
        sk->dummy_th.dest = usin->sin_port;
        sk->state = TCP_ESTABLISHED;
-       udp_cache_zap();
        sk->ip_route_cache = rt;
        return(0);
 }
@@ -628,8 +869,6 @@ static void udp_close(struct sock *sk, unsigned long timeout)
 {
        lock_sock(sk);
        sk->state = TCP_CLOSE;
-       if(uh_cache_sk==sk)
-               udp_cache_zap();
        release_sock(sk);
        sk->dead = 1;
        destroy_sock(sk);
@@ -679,15 +918,53 @@ int udp_chkaddr(struct sk_buff *skb)
        struct udphdr *uh = (struct udphdr *)(skb->h.raw + iph->ihl*4);
        struct sock *sk;
 
-       sk = get_sock(&udp_prot, uh->dest, iph->saddr, uh->source, iph->daddr, 0, 0);
-
-       if (!sk) return 0;
+       sk = udp_v4_lookup(iph->saddr, uh->source, iph->daddr, uh->dest);
+       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
 
+#ifdef CONFIG_IP_MULTICAST
+/*
+ *     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;
+}
+#endif
+
 /*
  *     All we need to do is get the socket, and then do a checksum. 
  */
@@ -784,46 +1061,15 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
 
 #ifdef CONFIG_IP_MULTICAST
        if (addr_type==IS_BROADCAST || addr_type==IS_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;
-       }       
+               return udp_v4_mcast_deliver(skb, uh, saddr, daddr);
 #endif
-       if(saddr==uh_cache_saddr && daddr==uh_cache_daddr && uh->dest==uh_cache_dport && uh->source==uh_cache_sport)
-               sk=(struct sock *)uh_cache_sk;
+#ifdef CONFIG_IP_TRANSPARENT_PROXY
+       if(skb->redirport)
+               sk = udp_v4_proxy_lookup(uh->dest, saddr, uh->source,
+                                        daddr, dev->pa_addr, skb->redirport);
        else
-       {
-               sk = get_sock(&udp_prot, uh->dest, saddr, uh->source, daddr, dev->pa_addr, skb->redirport);
-               uh_cache_saddr=saddr;
-               uh_cache_daddr=daddr;
-               uh_cache_dport=uh->dest;
-               uh_cache_sport=uh->source;
-               uh_cache_sk=sk;
-       }
+#endif
+       sk = udp_v4_lookup(saddr, uh->source, daddr, uh->dest);
        
        if (sk == NULL) 
        {
@@ -845,27 +1091,34 @@ int udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
 }
 
 struct proto udp_prot = {
-       udp_close,
-       ip_build_header,
-       udp_connect,
-       NULL,
-       ip_queue_xmit,
-       NULL,
-       NULL,
-       NULL,
-       udp_rcv,
-       datagram_select,
-       udp_ioctl,
-       NULL,
-       NULL,
-       ip_setsockopt,
-       ip_getsockopt,
-       udp_sendmsg,
-       udp_recvmsg,
-       NULL,           /* No special bind function */
-       128,
-       0,
-       "UDP",
-       0, 0,
-       {NULL,}
+       (struct sock *)&udp_prot,       /* sklist_next */
+       (struct sock *)&udp_prot,       /* sklist_prev */
+       udp_close,                      /* close */
+       ip_build_header,                /* build_header */
+       udp_connect,                    /* connect */
+       NULL,                           /* accept */
+       ip_queue_xmit,                  /* queue_xmit */
+       NULL,                           /* retransmit */
+       NULL,                           /* write_wakeup */
+       NULL,                           /* read_wakeup */
+       udp_rcv,                        /* rcv */
+       datagram_select,                /* select */
+       udp_ioctl,                      /* ioctl */
+       NULL,                           /* init */
+       NULL,                           /* shutdown */
+       ip_setsockopt,                  /* setsockopt */
+       ip_getsockopt,                  /* getsockopt */
+       udp_sendmsg,                    /* sendmsg */
+       udp_recvmsg,                    /* recvmsg */
+       NULL,                           /* bind */
+       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 */
 };
index fe3c062a298c50efb533f4af8cd8fe959a044dc5..1ab7839f665698b0541bba3a43a8da081c7fced4 100644 (file)
@@ -105,9 +105,8 @@ static struct symbol_table net_syms = {
        X(arp_send),
        X(ip_id_count),
        X(ip_send_check),
-#ifdef CONFIG_IP_FORWARD
        X(ip_forward),
-#endif
+       X(sysctl_ip_forward),
 
 #if    defined(CONFIG_ULTRA)   ||      defined(CONFIG_WD80x3)          || \
        defined(CONFIG_EL2)     ||      defined(CONFIG_NE2000)          || \
index 9bea73153f3add70bce80763e36b3046651ae6f8..77d6cf111d7acd7087b39f6100ebaced25dfe79d 100644 (file)
@@ -42,6 +42,9 @@
 #
 # 200396 Tom Dyas (tdyas@eden.rutgers.edu) - when the module option is
 # chosen for an item, define the macro <option_name>_MODULE
+#
+# 090397 Axel Boldt (boldt@math.ucsb.edu) - avoid ? and + in regular 
+# expressions for GNU expr since version 1.15 and up use \? and \+.
 
 #
 # Make sure we're really running bash.
@@ -288,7 +291,7 @@ function int () {
        def=${old:-$3}
        while :; do
          readln "$1 ($2) [$def] " "$def" "$old"
-         if expr "$ans" : '0$\|-?[1-9][0-9]*$' > /dev/null; then
+         if expr "$ans" : '0$\|\(-[1-9]\|[1-9]\)[0-9]*$' > /dev/null; then
            define_int "$2" "$ans"
            break
          else
@@ -319,12 +322,12 @@ function hex () {
        while :; do
          readln "$1 ($2) [$def] " "$def" "$old"
          ans=${ans#*[x,X]}
-        if expr "$ans" : '[0-9a-fA-F]+$' > /dev/null; then
-          define_hex "$2" "$ans"
-          break
-        else
+         if expr "$ans" : '[0-9a-fA-F][0-9a-fA-F]*$' > /dev/null; then
+           define_hex "$2" "$ans"
+           break
+         else
            help "$2"
-        fi
+         fi
        done
 }
 
@@ -422,6 +425,7 @@ echo "#" >> $CONFIG
 echo "/*" > $CONFIG_H
 echo " * Automatically generated C config: don't edit" >> $CONFIG_H
 echo " */" >> $CONFIG_H
+echo "#define AUTOCONF_INCLUDED" >> $CONFIG_H
 
 DEFAULT=""
 if [ "$1" = "-d" ] ; then